Files
brk/website_next/wallets/derive/bip32.js
T
2026-06-17 11:25:42 +02:00

85 lines
2.0 KiB
JavaScript

import { concatBytes, writeUint32 } from "./bytes.js";
import { hmacSha512 } from "./hash.js";
import { parseExtendedPublicKey } from "./key.js";
import { addPublicKeyTweak } from "./secp256k1.js";
const HARDENED_INDEX = 0x80000000;
/**
* @typedef {import("./key.js").ExtendedPublicKey} ExtendedPublicKey
*/
/**
* @typedef {Object} DerivedPublicKey
* @property {number} index
* @property {Uint8Array} publicKey
*/
/**
* @param {ExtendedPublicKey} key
* @param {number} index
* @returns {Promise<ExtendedPublicKey>}
*/
async function derivePublicChild(key, index) {
if (!Number.isSafeInteger(index) || index < 0 || index >= HARDENED_INDEX) {
throw new Error("Expected a non-hardened child index");
}
const data = concatBytes([key.publicKey, writeUint32(index)]);
const digest = await hmacSha512(key.chainCode, data);
const tweak = digest.slice(0, 32);
const chainCode = digest.slice(32);
return {
text: key.text,
depth: key.depth + 1,
childNumber: index,
parentFingerprint: key.parentFingerprint,
chainCode,
publicKey: addPublicKeyTweak(key.publicKey, tweak),
version: key.version,
};
}
/**
* @param {ExtendedPublicKey} key
* @param {readonly number[]} path
*/
async function derivePublicPath(key, path) {
let child = key;
for (const index of path) {
child = await derivePublicChild(child, index);
}
return child;
}
/**
* @param {ExtendedPublicKey} key
* @param {number} start
* @param {number} count
* @param {readonly number[]} [path]
* @returns {Promise<DerivedPublicKey[]>}
*/
export async function derivePublicKeys(key, start, count, path = []) {
const parent = await derivePublicPath(key, path);
const children = /** @type {DerivedPublicKey[]} */ ([]);
for (let offset = 0; offset < count; offset += 1) {
const index = start + offset;
const child = await derivePublicChild(parent, index);
children.push({ index, publicKey: child.publicKey });
}
return children;
}
/**
* @param {string} text
*/
export async function parseXpub(text) {
return parseExtendedPublicKey(text);
}