remove ecpair dependency

This commit is contained in:
Alex Bosworth 2026-07-03 16:56:16 -07:00
parent 662cd189a8
commit 4239ba093d
No known key found for this signature in database
GPG key ID: E80D2F3F311FD87E
6 changed files with 62 additions and 36 deletions

View file

@ -1,6 +1,6 @@
# Versions
## 22.1.6
## 22.1.7
- Add support for LND 0.21.1

View file

@ -2,7 +2,6 @@ const asyncAuto = require('async/auto');
const {bech32} = require('bech32');
const {returnResult} = require('asyncjs-util');
const {signMessage} = require('ln-service');
const tinysecp = require('tiny-secp256k1');
const signAuthChallenge = require('./sign_auth_challenge');
@ -35,9 +34,6 @@ const wordsAsUtf8 = n => Buffer.from(bech32.fromWords(n)).toString('utf8');
module.exports = (args, cbk) => {
return new Promise((resolve, reject) => {
return asyncAuto({
// Import the ECPair library
ecp: async () => (await import('ecpair')).ECPairFactory(tinysecp),
// Check arguments
validate: cbk => {
if (!args.ask) {
@ -112,18 +108,21 @@ module.exports = (args, cbk) => {
}],
// Derive keys and get signatures
sign: ['ecp', 'parse', 'seed', ({ecp, parse, seed}, cbk) => {
const sign = signAuthChallenge({
ecp,
hostname: parse.hostname,
k1: parse.k1,
seed: seed.signature,
});
sign: ['parse', 'seed', ({parse, seed}, cbk) => {
try {
const sign = signAuthChallenge({
hostname: parse.hostname,
k1: parse.k1,
seed: seed.signature,
});
return cbk(null, {
public_key: sign.public_key,
signature: sign.signature,
});
return cbk(null, {
public_key: sign.public_key,
signature: sign.signature,
});
} catch (err) {
return cbk([500, 'UnexpectedErrorSigningAuthChallenge', {err}]);
}
}],
// Display confirmation dialog with domain name and action

View file

@ -1,46 +1,59 @@
const {createHash} = require('crypto');
const {createHmac} = require('crypto');
const tinysecp256k1 = require('tiny-secp256k1');
const derEncodeSignature = require('./der_encode_signature');
const asDer = n => (n[0]&128)?Buffer.concat([Buffer.alloc(1),n],1+n.length):n;
const bufferAsHex = buffer => buffer.toString('hex');
const {from} = Buffer;
const bufferAsHex = buffer => Buffer.from(buffer).toString('hex');
const derivePublicKey = key => tinysecp256k1.pointFromScalar(key, true);
const hexAsBuffer = hex => Buffer.from(hex, 'hex');
const hmacSha256 = (pk, url) => createHmac('sha256', pk).update(url).digest();
const {isPrivate} = tinysecp256k1;
const sha256 = n => createHash('sha256').update(n).digest();
const {sign} = tinysecp256k1;
const utf8AsBuffer = utf8 => Buffer.from(utf8, 'utf8');
/** Sign an authentication challenge for LNURL Auth
{
ecp: <ECPair Object>
hostname: <Domain for Authentication Challenge String>
k1: <Challenge Nonce String>
seed: <Seed Signature String>
}
@throws
<Error>
@returns
{
public_key: <Signing Identity Public Key Hex String>
signature: <Signature For Authentication Challenge Hex String>
}
*/
module.exports = ({ecp, hostname, k1, seed}) => {
module.exports = ({hostname, k1, seed}) => {
// LUD-13: LN wallet defines hashingKey as sha256(signature)
const hashingKey = sha256(utf8AsBuffer(seed));
// LUD-13: linkingPrivKey is defined as hmacSha256(hashingKey, domain)
const linkingPrivKey = hmacSha256(hashingKey, utf8AsBuffer(hostname));
// Instantiate the key pair from this derived private key
const linkingKey = ecp.fromPrivateKey(linkingPrivKey);
// Validate the private key
if (!isPrivate(linkingPrivKey)) {
throw new Error('ExpectedValidLinkingPrivateKey');
}
const publicKey = derivePublicKey(linkingPrivKey);
if (!publicKey) {
throw new Error('ExpectedPublicKeyFromLinkingPrivateKey');
}
// Using the host-specific linking key, sign the challenge k1 value
const signature = bufferAsHex(from(linkingKey.sign(hexAsBuffer(k1))));
const signature = bufferAsHex(sign(hexAsBuffer(k1), linkingPrivKey));
return {
public_key: bufferAsHex(linkingKey.publicKey),
public_key: bufferAsHex(publicKey),
signature: derEncodeSignature({signature}).encoded,
};
};

View file

@ -1,6 +1,7 @@
const {randomBytes} = require('crypto');
const asyncAuto = require('async/auto');
const asyncRetry = require('async/retry');
const {createSignedRequest} = require('ln-service');
const {createUnsignedRequest} = require('ln-service');
const {decode} = require('bip66');
@ -8,13 +9,15 @@ const {returnResult} = require('asyncjs-util');
const {signBytes} = require('ln-service');
const tinysecp256k1 = require('tiny-secp256k1');
const bufferAsHex = buffer => buffer.toString('hex');
const bufferAsHex = buffer => Buffer.from(buffer).toString('hex');
const {concat} = Buffer;
const defaultBaseFee = '1000';
const defaultCltvDelta = 144;
const defaultFeeRate = '1';
const derivePublicKey = key => tinysecp256k1.pointFromScalar(key, true);
const hexAsBuffer = hex => Buffer.from(hex, 'hex');
const {isArray} = Array;
const {isPrivate} = tinysecp256k1;
const keyFamilyIdentity = 6;
const keyIndexIdentity = 0;
const makePrivateKey = () => randomBytes(32);
@ -103,20 +106,33 @@ module.exports = (args, cbk) => {
return cbk();
},
// Create a key pair for a virtual channel invoice
getKeyPair: ['validate', async ({}) => {
// Generate key pair
getKeyPair: ['validate', ({}, cbk) => {
// Exit early when not using a virtual channel
if (!args.is_virtual) {
return;
return cbk();
}
const ecp = (await import('ecpair')).ECPairFactory(tinysecp256k1);
return asyncRetry({}, cbk => {
const privateKey = makePrivateKey();
const key = ecp.fromPrivateKey(makePrivateKey());
// Very rarely random bytes are not a valid private key
if (!isPrivate(privateKey)) {
return cbk([503, 'ExpectedValidPrivateKeyFromRandomBytes']);
}
const publicKey = unit8AsHex(key.publicKey);
const publicKey = derivePublicKey(privateKey);
return {private_key: key.privateKey, public_key: publicKey};
if (!publicKey) {
return cbk([503, 'ExpectedDerivedPublicKeyFromPrivateKey']);
}
return cbk(null, {
private_key: privateKey,
public_key: bufferAsHex(publicKey),
});
},
cbk);
}],
// Assemble the hop hints from the chosen hint channels

1
package-lock.json generated
View file

@ -28,7 +28,6 @@
"colorette": "2.0.20",
"crypto-js": "4.2.0",
"csv-parse": "7.0.1",
"ecpair": "2.1.0",
"goldengate": "16.0.3",
"grammy": "1.44.0",
"hot-formula-parser": "4.0.0",

View file

@ -29,7 +29,6 @@
"colorette": "2.0.20",
"crypto-js": "4.2.0",
"csv-parse": "7.0.1",
"ecpair": "2.1.0",
"goldengate": "16.0.3",
"grammy": "1.44.0",
"hot-formula-parser": "4.0.0",
@ -81,5 +80,5 @@
"postpublish": "docker buildx build --platform linux/amd64,linux/arm64 -t alexbosworth/balanceofsatoshis -t alexbosworth/balanceofsatoshis:$npm_package_version --push .",
"test": "npx nyc@17.1.0 node --experimental-test-coverage --test test/arrays/*.js test/balances/*.js test/chain/*.js test/display/*.js test/encryption/*.js test/lnd/*.js test/network/*.js test/nodes/*.js test/peers/*.js test/responses/*.js test/routing/*.js test/services/*.js test/swaps/*.js test/tags/*.js test/telegram/*.js test/wallets/*.js"
},
"version": "22.1.6"
"version": "22.1.7"
}