* @param {number} account - Account number between 0 and 2^31-1
* @returns {Promise<ArrayBuffer>} Private child key for the account
*/
- static async ckd (seed: ArrayBuffer, coin: number, account: number, chain?: number, address?: number): Promise<ArrayBuffer> {
+ static ckd (seed: ArrayBuffer, coin: number, account: number, chain?: number, address?: number): Promise<ArrayBuffer | undefined> {
if (seed.byteLength < 16 || seed.byteLength > 64) {
throw new RangeError(`Invalid seed length`)
}
if (!Number.isSafeInteger(account) || account < 0 || account > 0x7fffffff) {
throw new RangeError(`Invalid child key index 0x${account.toString(16)}`)
}
- const masterKey = await this.slip10(SLIP10_ED25519, seed)
- const purposeKey = await this.CKDpriv(masterKey, BIP44_PURPOSE + HARDENED_OFFSET)
- const coinKey = await this.CKDpriv(purposeKey, coin + HARDENED_OFFSET)
- const accountKey = await this.CKDpriv(coinKey, account + HARDENED_OFFSET)
- if (chain == null) return accountKey.privateKey
- const chainKey = await this.CKDpriv(accountKey, chain + HARDENED_OFFSET)
- if (address == null) return chainKey.privateKey
- const addressKey = await this.CKDpriv(chainKey, address + HARDENED_OFFSET)
- return addressKey.privateKey
+ return this.slip10(SLIP10_ED25519, seed)
+ .then(masterKey => {
+ return this.CKDpriv(masterKey, BIP44_PURPOSE + HARDENED_OFFSET)
+ .then(purposeKey => {
+ return this.CKDpriv(purposeKey, coin + HARDENED_OFFSET)
+ .then(coinKey => {
+ return this.CKDpriv(coinKey, account + HARDENED_OFFSET)
+ .then(accountKey => {
+ if (chain == null) return accountKey.privateKey
+ this.CKDpriv(accountKey, chain + HARDENED_OFFSET)
+ .then(chainKey => {
+ if (address == null) return chainKey.privateKey
+ this.CKDpriv(chainKey, address + HARDENED_OFFSET)
+ .then(addressKey => {
+ return addressKey.privateKey
+ })
+ })
+ })
+ })
+ })
+ })
+ .catch(err => {
+ throw new Error('Failed to derive BIP-44 account', { cause: err })
+ })
}
- static async slip10 (curve: string, S: ArrayBuffer): Promise<ExtendedKey> {
+ static slip10 (curve: string, S: ArrayBuffer): Promise<ExtendedKey> {
const key = new Uint8Array(new TextEncoder().encode(curve))
const data = new Uint8Array(S)
- const I = await this.hmac(key, data)
- const IL = I.buffer.slice(0, I.length / 2)
- const IR = I.buffer.slice(I.length / 2)
- return ({ privateKey: IL, chainCode: IR })
+ return this.hmac(key, data)
+ .then(I => {
+ const IL = I.slice(0, I.byteLength / 2)
+ const IR = I.slice(I.byteLength / 2)
+ return ({ privateKey: IL, chainCode: IR })
+ })
}
- static async CKDpriv ({ privateKey, chainCode }: ExtendedKey, index: number): Promise<ExtendedKey> {
+ static CKDpriv ({ privateKey, chainCode }: ExtendedKey, index: number): Promise<ExtendedKey> {
const key = new Uint8Array(chainCode)
const data = new Uint8Array(37)
data.set([0])
data.set(this.ser256(privateKey), 1)
data.set(this.ser32(index), 33)
- const I = await this.hmac(key, data)
- const IL = I.buffer.slice(0, I.length / 2)
- const IR = I.buffer.slice(I.length / 2)
- return ({ privateKey: IL, chainCode: IR })
+ return this.hmac(key, data)
+ .then(I => {
+ const IL = I.slice(0, I.byteLength / 2)
+ const IR = I.slice(I.byteLength / 2)
+ return ({ privateKey: IL, chainCode: IR })
+ })
}
static ser32 (integer: number): Uint8Array<ArrayBuffer> {
return new Uint8Array(integer)
}
- static async hmac (key: Uint8Array<ArrayBuffer>, data: Uint8Array<ArrayBuffer>): Promise<Uint8Array<ArrayBuffer>> {
- const pk = await crypto.subtle.importKey('raw', key, { name: 'HMAC', hash: 'SHA-512' }, false, ['sign'])
- const signature = await crypto.subtle.sign('HMAC', pk, data)
- return new Uint8Array(signature)
+ static hmac (key: Uint8Array<ArrayBuffer>, data: Uint8Array<ArrayBuffer>): Promise<ArrayBuffer> {
+ return crypto.subtle.importKey('raw', key, { name: 'HMAC', hash: 'SHA-512' }, false, ['sign'])
+ .then(pk => {
+ return crypto.subtle.sign('HMAC', pk, data)
+ .then(signature => {
+ return signature
+ })
+ })
}
}