From 3250f583ab63a4019f0ec43548a95bdb62a61053 Mon Sep 17 00:00:00 2001 From: Jackson Mills Date: Tue, 8 Aug 2023 15:19:44 -0700 Subject: [PATCH] BREAKING CHANGE(fix): `deriveKeypair` ignoring a manual `algorithm` being specified (#2376) --- packages/ripple-keypairs/HISTORY.md | 1 + packages/ripple-keypairs/src/index.ts | 10 +- packages/xrpl/HISTORY.md | 4 + packages/xrpl/src/Wallet/index.ts | 2 +- .../xrpl/test/integration/regularKey.test.ts | 2 + packages/xrpl/test/integration/utils.ts | 3 +- packages/xrpl/test/wallet/index.test.ts | 114 ++++++++++-------- packages/xrpl/test/wallet/signer.test.ts | 6 +- 8 files changed, 87 insertions(+), 55 deletions(-) diff --git a/packages/ripple-keypairs/HISTORY.md b/packages/ripple-keypairs/HISTORY.md index 066988b19a..5a0c0638be 100644 --- a/packages/ripple-keypairs/HISTORY.md +++ b/packages/ripple-keypairs/HISTORY.md @@ -1,6 +1,7 @@ # ripple-keypairs Release History ## Unreleased +* Fix `deriveKeypair` ignoring manual decoding algorithm. (Specifying algorithm=`ed25519` in `opts` now works on secrets like `sNa1...`) ## 1.3.0 (2023-06-13) ### Added diff --git a/packages/ripple-keypairs/src/index.ts b/packages/ripple-keypairs/src/index.ts index 93da6565b6..75ec7cb670 100644 --- a/packages/ripple-keypairs/src/index.ts +++ b/packages/ripple-keypairs/src/index.ts @@ -104,13 +104,19 @@ function select(algorithm): any { function deriveKeypair( seed: string, - options?: object, + options?: { + algorithm?: 'ed25519' | 'ecdsa-secp256k1' + validator?: boolean + accountIndex?: number + }, ): { publicKey: string privateKey: string } { const decoded = addressCodec.decodeSeed(seed) - const algorithm = decoded.type === 'ed25519' ? 'ed25519' : 'ecdsa-secp256k1' + const proposedAlgorithm = options?.algorithm ?? decoded.type + const algorithm = + proposedAlgorithm === 'ed25519' ? 'ed25519' : 'ecdsa-secp256k1' const method = select(algorithm) const keypair = method.deriveKeypair(decoded.bytes, options) const messageToVerify = hash('This test message should verify.') diff --git a/packages/xrpl/HISTORY.md b/packages/xrpl/HISTORY.md index 7ec340a10f..a286633f0e 100644 --- a/packages/xrpl/HISTORY.md +++ b/packages/xrpl/HISTORY.md @@ -3,6 +3,10 @@ Subscribe to [the **xrpl-announce** mailing list](https://groups.google.com/g/xrpl-announce) for release announcements. We recommend that xrpl.js (ripple-lib) users stay up-to-date with the latest stable release. ## Unreleased +### Fixed +* Fixed Wallet.generate() ignoring the `algorithm` parameter (Only a problem once binary-codec fix for `derive_keypair` is added) +* Fixed Wallet.fromSeed() ignoring the `algorithm` parameter + ## 2.10.0 (2023-08-07) ### Added diff --git a/packages/xrpl/src/Wallet/index.ts b/packages/xrpl/src/Wallet/index.ts index 709f1bf1a9..1c059a0fb0 100644 --- a/packages/xrpl/src/Wallet/index.ts +++ b/packages/xrpl/src/Wallet/index.ts @@ -137,7 +137,7 @@ export class Wallet { throw new ValidationError('Invalid cryptographic signing algorithm') } const seed = generateSeed({ algorithm }) - return Wallet.fromSeed(seed) + return Wallet.fromSeed(seed, { algorithm }) } /** diff --git a/packages/xrpl/test/integration/regularKey.test.ts b/packages/xrpl/test/integration/regularKey.test.ts index 85a94a4dd8..fbad77fb4e 100644 --- a/packages/xrpl/test/integration/regularKey.test.ts +++ b/packages/xrpl/test/integration/regularKey.test.ts @@ -8,6 +8,7 @@ import { Wallet, AccountSetAsfFlags, OfferCreate, + ECDSA, } from '../../src' import { convertStringToHex } from '../../src/utils' import { multisign } from '../../src/Wallet/signer' @@ -41,6 +42,7 @@ async function generateFundedWalletWithRegularKey( const regularKeyWallet = Wallet.fromSeed(regularKeyInfo.seed, { masterAddress: masterWallet.address, + algorithm: ECDSA.secp256k1, }) const setRegularTx: SetRegularKey = { diff --git a/packages/xrpl/test/integration/utils.ts b/packages/xrpl/test/integration/utils.ts index 6b28a96447..fd4d1ef022 100644 --- a/packages/xrpl/test/integration/utils.ts +++ b/packages/xrpl/test/integration/utils.ts @@ -10,6 +10,7 @@ import { type SubmitResponse, TimeoutError, NotConnectedError, + ECDSA, } from '../../src' import { Payment, Transaction } from '../../src/models/transactions' import { hashSignedTx } from '../../src/utils/hashes' @@ -146,7 +147,7 @@ export async function fundAccount( // 2 times the amount needed for a new account (20 XRP) Amount: '400000000', } - const wal = Wallet.fromSeed(masterSecret) + const wal = Wallet.fromSeed(masterSecret, { algorithm: ECDSA.secp256k1 }) const response = await submitTransaction({ client, wallet: wal, diff --git a/packages/xrpl/test/wallet/index.test.ts b/packages/xrpl/test/wallet/index.test.ts index f7497d55ff..8ba3005e46 100644 --- a/packages/xrpl/test/wallet/index.test.ts +++ b/packages/xrpl/test/wallet/index.test.ts @@ -16,6 +16,16 @@ const { sign: RESPONSE_FIXTURES } = responses * Provides tests for Wallet class. */ describe('Wallet', function () { + const knownSecret = 'sh1HiK7SwjS1VxFdXi7qeMHRedrYX' + const publicKeySecp256k1 = + '03BFC2F7AE242C3493187FA0B72BE97B2DF71194FB772E507FF9DEA0AD13CA1625' + const privateKeySecp256k1 = + '00B6FE8507D977E46E988A8A94DB3B8B35E404B60F8B11AC5213FA8B5ABC8A8D19' + const publicKeyED25519 = + 'ED8079E575450E256C496578480020A33E19B579D58A2DB8FF13FC6B05B9229DE3' + const privateKeyED25519 = + 'EDD2AF6288A903DED9860FC62E778600A985BDF804E40BD8266505553E3222C3DA' + describe('constructor', function () { it('initializes a wallet using a Regular Key Pair', function () { const masterAddress = 'rUAi7pipxGpYfPNg3LtPcf2ApiS8aw9A93' @@ -97,33 +107,27 @@ describe('Wallet', function () { }) describe('fromSeed', function () { - const seed = 'ssL9dv2W5RK8L3tuzQxYY6EaZhSxW' - const publicKey = - '030E58CDD076E798C84755590AAF6237CA8FAE821070A59F648B517A30DC6F589D' - const privateKey = - '00141BA006D3363D2FB2785E8DF4E44D3A49908780CB4FB51F6D217C08C021429F' - it('derives a wallet using default algorithm', function () { - const wallet = Wallet.fromSeed(seed) + const wallet = Wallet.fromSeed(knownSecret) - assert.equal(wallet.publicKey, publicKey) - assert.equal(wallet.privateKey, privateKey) + assert.equal(wallet.publicKey, publicKeyED25519) + assert.equal(wallet.privateKey, privateKeyED25519) }) it('derives a wallet using algorithm ecdsa-secp256k1', function () { const algorithm = ECDSA.secp256k1 - const wallet = Wallet.fromSeed(seed, { algorithm }) + const wallet = Wallet.fromSeed(knownSecret, { algorithm }) - assert.equal(wallet.publicKey, publicKey) - assert.equal(wallet.privateKey, privateKey) + assert.equal(wallet.publicKey, publicKeySecp256k1) + assert.equal(wallet.privateKey, privateKeySecp256k1) }) it('derives a wallet using algorithm ed25519', function () { const algorithm = ECDSA.ed25519 - const wallet = Wallet.fromSeed(seed, { algorithm }) + const wallet = Wallet.fromSeed(knownSecret, { algorithm }) - assert.equal(wallet.publicKey, publicKey) - assert.equal(wallet.privateKey, privateKey) + assert.equal(wallet.publicKey, publicKeyED25519) + assert.equal(wallet.privateKey, privateKeyED25519) }) it('derives a wallet using rfc1751 mnemonic with secp256k1 key', function () { @@ -197,7 +201,10 @@ describe('Wallet', function () { '004265A28F3E18340A490421D47B2EB8DBC2C0BF2C24CEFEA971B61CED2CABD233', } - const wallet = Wallet.fromSeed(regularKeyPair.seed, { masterAddress }) + const wallet = Wallet.fromSeed(regularKeyPair.seed, { + masterAddress, + algorithm: ECDSA.secp256k1, + }) assert.equal(wallet.publicKey, regularKeyPair.publicKey) assert.equal(wallet.privateKey, regularKeyPair.privateKey) @@ -206,33 +213,27 @@ describe('Wallet', function () { }) describe('fromSecret', function () { - const seed = 'ssL9dv2W5RK8L3tuzQxYY6EaZhSxW' - const publicKey = - '030E58CDD076E798C84755590AAF6237CA8FAE821070A59F648B517A30DC6F589D' - const privateKey = - '00141BA006D3363D2FB2785E8DF4E44D3A49908780CB4FB51F6D217C08C021429F' - it('derives a wallet using default algorithm', function () { - const wallet = Wallet.fromSecret(seed) + const wallet = Wallet.fromSecret(knownSecret) - assert.equal(wallet.publicKey, publicKey) - assert.equal(wallet.privateKey, privateKey) + assert.equal(wallet.publicKey, publicKeyED25519) + assert.equal(wallet.privateKey, privateKeyED25519) }) it('derives a wallet using algorithm ecdsa-secp256k1', function () { const algorithm = ECDSA.secp256k1 - const wallet = Wallet.fromSecret(seed, { algorithm }) + const wallet = Wallet.fromSecret(knownSecret, { algorithm }) - assert.equal(wallet.publicKey, publicKey) - assert.equal(wallet.privateKey, privateKey) + assert.equal(wallet.publicKey, publicKeySecp256k1) + assert.equal(wallet.privateKey, privateKeySecp256k1) }) it('derives a wallet using algorithm ed25519', function () { const algorithm = ECDSA.ed25519 - const wallet = Wallet.fromSecret(seed, { algorithm }) + const wallet = Wallet.fromSecret(knownSecret, { algorithm }) - assert.equal(wallet.publicKey, publicKey) - assert.equal(wallet.privateKey, privateKey) + assert.equal(wallet.publicKey, publicKeyED25519) + assert.equal(wallet.privateKey, privateKeyED25519) }) it('derives a wallet using a Regular Key Pair', function () { @@ -245,7 +246,10 @@ describe('Wallet', function () { '004265A28F3E18340A490421D47B2EB8DBC2C0BF2C24CEFEA971B61CED2CABD233', } - const wallet = Wallet.fromSecret(regularKeyPair.seed, { masterAddress }) + const wallet = Wallet.fromSecret(regularKeyPair.seed, { + masterAddress, + algorithm: ECDSA.secp256k1, + }) assert.equal(wallet.publicKey, regularKeyPair.publicKey) assert.equal(wallet.privateKey, regularKeyPair.privateKey) @@ -262,7 +266,9 @@ describe('Wallet', function () { '0013FC461CA5799F1357C8130AF703CBA7E9C28E072C6CA8F7DEF8601CDE98F394' it('derives a wallet using default derivation path', function () { - const wallet = Wallet.fromMnemonic(mnemonic) + const wallet = Wallet.fromMnemonic(mnemonic, { + algorithm: ECDSA.secp256k1, + }) assert.equal(wallet.publicKey, publicKey) assert.equal(wallet.privateKey, privateKey) @@ -270,7 +276,10 @@ describe('Wallet', function () { it('derives a wallet using an input derivation path', function () { const derivationPath = "m/44'/144'/0'/0/0" - const wallet = Wallet.fromMnemonic(mnemonic, { derivationPath }) + const wallet = Wallet.fromMnemonic(mnemonic, { + derivationPath, + algorithm: ECDSA.secp256k1, + }) assert.equal(wallet.publicKey, publicKey) assert.equal(wallet.privateKey, privateKey) @@ -289,6 +298,7 @@ describe('Wallet', function () { const wallet = Wallet.fromMnemonic(regularKeyPair.mnemonic, { masterAddress, mnemonicEncoding: 'rfc1751', + algorithm: ECDSA.secp256k1, }) assert.equal(wallet.publicKey, regularKeyPair.publicKey) @@ -299,13 +309,13 @@ describe('Wallet', function () { describe('fromEntropy', function () { let entropy: number[] - const publicKey = + const entropyPublicKeySecp256k1 = '0390A196799EE412284A5D80BF78C3E84CBB80E1437A0AECD9ADF94D7FEAAFA284' - const privateKey = + const entropyPrivateKeySecp256k1 = '002512BBDFDBB77510883B7DCCBEF270B86DEAC8B64AC762873D75A1BEE6298665' - const publicKeyED25519 = + const entropyPublicKeyED25519 = 'ED1A7C082846CFF58FF9A892BA4BA2593151CCF1DBA59F37714CC9ED39824AF85F' - const privateKeyED25519 = + const entropyPrivateKeyED25519 = 'ED0B6CBAC838DFE7F47EA1BD0DF00EC282FDF45510C92161072CCFB84035390C4D' beforeEach(function () { @@ -316,32 +326,34 @@ describe('Wallet', function () { it('derives a wallet using entropy', function () { const wallet = Wallet.fromEntropy(entropy) - assert.equal(wallet.publicKey, publicKeyED25519) - assert.equal(wallet.privateKey, privateKeyED25519) + assert.equal(wallet.publicKey, entropyPublicKeyED25519) + assert.equal(wallet.privateKey, entropyPrivateKeyED25519) }) it('derives a wallet using algorithm ecdsa-secp256k1', function () { const algorithm = ECDSA.secp256k1 const wallet = Wallet.fromEntropy(entropy, { algorithm }) - assert.equal(wallet.publicKey, publicKey) - assert.equal(wallet.privateKey, privateKey) + assert.equal(wallet.publicKey, entropyPublicKeySecp256k1) + assert.equal(wallet.privateKey, entropyPrivateKeySecp256k1) }) it('derives a wallet using algorithm ed25519', function () { const algorithm = ECDSA.ed25519 const wallet = Wallet.fromEntropy(entropy, { algorithm }) - assert.equal(wallet.publicKey, publicKeyED25519) - assert.equal(wallet.privateKey, privateKeyED25519) + assert.equal(wallet.publicKey, entropyPublicKeyED25519) + assert.equal(wallet.privateKey, entropyPrivateKeyED25519) }) it('derives a wallet using a regular key pair', function () { const masterAddress = 'rUAi7pipxGpYfPNg3LtPcf2ApiS8aw9A93' - const wallet = Wallet.fromEntropy(entropy, { masterAddress }) + const wallet = Wallet.fromEntropy(entropy, { + masterAddress, + }) - assert.equal(wallet.publicKey, publicKeyED25519) - assert.equal(wallet.privateKey, privateKeyED25519) + assert.equal(wallet.publicKey, entropyPublicKeyED25519) + assert.equal(wallet.privateKey, entropyPrivateKeyED25519) assert.equal(wallet.classicAddress, masterAddress) }) }) @@ -351,7 +363,9 @@ describe('Wallet', function () { let wallet: Wallet beforeEach(function () { - wallet = Wallet.fromSeed('ss1x3KLrSvfg7irFc1D929WXZ7z9H') + wallet = Wallet.fromSeed('ss1x3KLrSvfg7irFc1D929WXZ7z9H', { + algorithm: ECDSA.secp256k1, + }) }) it('sign successfully', async function () { @@ -386,7 +400,9 @@ describe('Wallet', function () { ], } - const result = Wallet.fromSeed(secret).sign(lowercaseMemoTx) + const result = Wallet.fromSeed(secret, { + algorithm: ECDSA.secp256k1, + }).sign(lowercaseMemoTx) assert.deepEqual(result, { tx_blob: '120000228000000023000022B8240000000C2E0000270F201B00D5A36761400000000098968068400000000000000C73210305E09ED602D40AB1AF65646A4007C2DAC17CB6CDACDE301E74FB2D728EA057CF744730450221009C00E8439E017CA622A5A1EE7643E26B4DE9C808DE2ABE45D33479D49A4CEC66022062175BE8733442FA2A4D9A35F85A57D58252AE7B19A66401FE238B36FA28E5A081146C1856D0E36019EA75C56D7E8CBA6E35F9B3F71583147FB49CD110A1C46838788CD12764E3B0F837E0DDF9EA7C1F687474703A2F2F6578616D706C652E636F6D2F6D656D6F2F67656E657269637D0472656E74E1F1', diff --git a/packages/xrpl/test/wallet/signer.test.ts b/packages/xrpl/test/wallet/signer.test.ts index 80cca97633..d241937265 100644 --- a/packages/xrpl/test/wallet/signer.test.ts +++ b/packages/xrpl/test/wallet/signer.test.ts @@ -1,7 +1,7 @@ import { assert } from 'chai' import { decode, encode } from 'ripple-binary-codec' -import { Transaction, ValidationError } from '../../src' +import { ECDSA, Transaction, ValidationError } from '../../src' import { Wallet } from '../../src/Wallet' import { authorizeChannel, @@ -188,7 +188,9 @@ describe('Signer', function () { }) it('authorizeChannel succeeds with secp256k1 seed', function () { - const secpWallet = Wallet.fromSeed('snGHNrPbHrdUcszeuDEigMdC1Lyyd') + const secpWallet = Wallet.fromSeed('snGHNrPbHrdUcszeuDEigMdC1Lyyd', { + algorithm: ECDSA.secp256k1, + }) const channelId = '5DB01B7FFED6B67E6B0414DED11E051D2EE2B7619CE0EAA6286D67A3A4D5BDB3' const amount = '1000000'