-
Notifications
You must be signed in to change notification settings - Fork 148
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: custom derivation paths in Menmonic ECDSA private key derivation
Signed-off-by: Brendan Graetz <[email protected]>
- Loading branch information
Showing
2 changed files
with
134 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,6 +25,8 @@ import CACHE from "./Cache.js"; | |
* @typedef {import("./PrivateKey.js").default} PrivateKey | ||
*/ | ||
|
||
const HARDENED_BIT = 0x80000000; | ||
|
||
/** | ||
* Multi-word mnemonic phrase (BIP-39). | ||
* | ||
|
@@ -136,6 +138,57 @@ export default class Mnemonic { | |
); | ||
} | ||
|
||
/** | ||
* Converts a derivation path from string to an array of integers. | ||
* Note that this expects precisely 5 components in the derivation path, | ||
* as per BIP-44: | ||
* `m / purpose' / coin_type' / account' / change / address_index` | ||
* Takes into account `'` for hardening as per BIP-32, | ||
* and does not prescribe which components should be hardened. | ||
* | ||
* @param {string} derivationPath the derivation path in BIP-44 format, | ||
* e.g. "m/44'/60'/0'/0/0" | ||
* @returns {Array<number>} to be used with PrivateKey#derive | ||
*/ | ||
calculateDerivationPathValues(derivationPath) { | ||
// Parse the derivation path from string into values | ||
const pattern = /m\/(\d+'?)\/(\d+'?)\/(\d+'?)\/(\d+'?)\/(\d+'?)/; | ||
const matches = pattern.exec(derivationPath); | ||
const values = new Array(5); // as Array<Number>; | ||
if (matches) { | ||
// Extract numbers and use apostrophe to select if is hardened | ||
for (let i = 1; i <= 5; i++) { | ||
let value = matches[i]; | ||
if (value.endsWith("'")) { | ||
value = value.substring(0, value.length - 1); | ||
values[i - 1] = parseInt(value, 10) | HARDENED_BIT; | ||
} else { | ||
values[i - 1] = parseInt(value, 10); | ||
} | ||
} | ||
} | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return | ||
return values; | ||
} | ||
|
||
/** | ||
* Common implementation for both `toStandardECDSAsecp256k1PrivateKey` | ||
* functions. | ||
* | ||
* @param {string} passphrase the passphrase used to protect the | ||
* mnemonic, use "" for none | ||
* @param {Array<number>} derivationPathValues derivation path as an | ||
* integer array, | ||
* see: `calculateDerivationPathValues` | ||
* @returns a private key | ||
Check warning on line 183 in src/Mnemonic.js
|
||
*/ | ||
async toStandardECDSAsecp256k1PrivateKeyImpl( | ||
passphrase, | ||
derivationPathValues, | ||
) { | ||
return this.toEcdsaPrivateKey(passphrase, derivationPathValues); | ||
} | ||
|
||
/** | ||
* Recover an ECDSA private key from this mnemonic phrase, with an | ||
* optional passphrase. | ||
|
@@ -153,6 +206,28 @@ export default class Mnemonic { | |
); | ||
} | ||
|
||
/** | ||
* Recover an ECDSAsecp256k1 private key from this mnemonic phrase and | ||
* derivation path, with an optional passphrase | ||
* | ||
* @param {string} passphrase the passphrase used to protect the mnemonic, | ||
* use "" for none | ||
* @param {string} derivationPath the derivation path in BIP-44 format, | ||
* e.g. "m/44'/60'/0'/0/0" | ||
* @returns {Promise<PrivateKey>} the private key | ||
*/ | ||
async toStandardECDSAsecp256k1PrivateKeyCustomDerivationPath( | ||
passphrase = "", | ||
derivationPath, | ||
) { | ||
const derivationPathValues = | ||
this.calculateDerivationPathValues(derivationPath); | ||
return await this.toStandardECDSAsecp256k1PrivateKeyImpl( | ||
passphrase, | ||
derivationPathValues, | ||
); | ||
} | ||
|
||
/** | ||
* Recover a mnemonic phrase from a string, splitting on spaces. Handles 12, 22 (legacy), and 24 words. | ||
* | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters