Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BREAKING CHANGE(fix): deriveKeypair ignoring a manual algorithm being specified #2376

Merged
merged 9 commits into from
Aug 8, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/ripple-keypairs/HISTORY.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
10 changes: 8 additions & 2 deletions packages/ripple-keypairs/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.')
Expand Down
3 changes: 3 additions & 0 deletions packages/xrpl/HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
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)

### Added
* Add `BurnedNFTokens`, `FirstNFTSequence`, `MintedNFTokens`,
`NFTokenMinter`, and `WalletLocator` to `AccountRoot`.
Expand Down
2 changes: 1 addition & 1 deletion packages/xrpl/src/Wallet/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 })
}

/**
Expand Down
2 changes: 2 additions & 0 deletions packages/xrpl/test/integration/regularKey.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
Wallet,
AccountSetAsfFlags,
OfferCreate,
ECDSA,
} from '../../src'
import { convertStringToHex } from '../../src/utils'
import { multisign } from '../../src/Wallet/signer'
Expand Down Expand Up @@ -41,6 +42,7 @@ async function generateFundedWalletWithRegularKey(

const regularKeyWallet = Wallet.fromSeed(regularKeyInfo.seed, {
masterAddress: masterWallet.address,
algorithm: ECDSA.secp256k1,
})

const setRegularTx: SetRegularKey = {
Expand Down
3 changes: 2 additions & 1 deletion packages/xrpl/test/integration/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
TimeoutError,
NotConnectedError,
unixTimeToRippleTime,
ECDSA,
} from '../../src'
import { Payment, Transaction } from '../../src/models/transactions'
import { hashSignedTx } from '../../src/utils/hashes'
Expand Down Expand Up @@ -175,7 +176,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,
Expand Down
118 changes: 70 additions & 48 deletions packages/xrpl/test/wallet/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -97,33 +107,33 @@ describe('Wallet', function () {
})

describe('fromSeed', function () {
const seed = 'ssL9dv2W5RK8L3tuzQxYY6EaZhSxW'
const publicKey =
'030E58CDD076E798C84755590AAF6237CA8FAE821070A59F648B517A30DC6F589D'
const privateKey =
'00141BA006D3363D2FB2785E8DF4E44D3A49908780CB4FB51F6D217C08C021429F'
// 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 () {
Expand Down Expand Up @@ -197,7 +207,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)
Expand All @@ -206,33 +219,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 () {
Expand All @@ -245,7 +252,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)
Expand All @@ -262,15 +272,20 @@ 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)
})

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)
Expand All @@ -289,6 +304,7 @@ describe('Wallet', function () {
const wallet = Wallet.fromMnemonic(regularKeyPair.mnemonic, {
masterAddress,
mnemonicEncoding: 'rfc1751',
algorithm: ECDSA.secp256k1,
})

assert.equal(wallet.publicKey, regularKeyPair.publicKey)
Expand All @@ -299,13 +315,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 () {
Expand All @@ -316,32 +332,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)
})
})
Expand All @@ -351,7 +369,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 () {
Expand Down Expand Up @@ -386,7 +406,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',
Expand Down
6 changes: 4 additions & 2 deletions packages/xrpl/test/wallet/signer.test.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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'
Expand Down