123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269 |
- /*! scure-bip32 - MIT License (c) 2022 Patricio Palladino, Paul Miller (paulmillr.com) */
- import { hmac } from '@noble/hashes/hmac';
- import { ripemd160 } from '@noble/hashes/ripemd160';
- import { sha256 } from '@noble/hashes/sha256';
- import { sha512 } from '@noble/hashes/sha512';
- import { bytes as assertBytes } from '@noble/hashes/_assert';
- import { bytesToHex, concatBytes, createView, hexToBytes, utf8ToBytes } from '@noble/hashes/utils';
- import { secp256k1 as secp } from '@noble/curves/secp256k1';
- import { mod } from '@noble/curves/abstract/modular';
- import { createBase58check } from '@scure/base';
- const Point = secp.ProjectivePoint;
- const base58check = createBase58check(sha256);
- function bytesToNumber(bytes) {
- return BigInt(`0x${bytesToHex(bytes)}`);
- }
- function numberToBytes(num) {
- return hexToBytes(num.toString(16).padStart(64, '0'));
- }
- const MASTER_SECRET = utf8ToBytes('Bitcoin seed');
- // Bitcoin hardcoded by default
- const BITCOIN_VERSIONS = { private: 0x0488ade4, public: 0x0488b21e };
- export const HARDENED_OFFSET = 0x80000000;
- const hash160 = (data) => ripemd160(sha256(data));
- const fromU32 = (data) => createView(data).getUint32(0, false);
- const toU32 = (n) => {
- if (!Number.isSafeInteger(n) || n < 0 || n > 2 ** 32 - 1) {
- throw new Error(`Invalid number=${n}. Should be from 0 to 2 ** 32 - 1`);
- }
- const buf = new Uint8Array(4);
- createView(buf).setUint32(0, n, false);
- return buf;
- };
- export class HDKey {
- get fingerprint() {
- if (!this.pubHash) {
- throw new Error('No publicKey set!');
- }
- return fromU32(this.pubHash);
- }
- get identifier() {
- return this.pubHash;
- }
- get pubKeyHash() {
- return this.pubHash;
- }
- get privateKey() {
- return this.privKeyBytes || null;
- }
- get publicKey() {
- return this.pubKey || null;
- }
- get privateExtendedKey() {
- const priv = this.privateKey;
- if (!priv) {
- throw new Error('No private key');
- }
- return base58check.encode(this.serialize(this.versions.private, concatBytes(new Uint8Array([0]), priv)));
- }
- get publicExtendedKey() {
- if (!this.pubKey) {
- throw new Error('No public key');
- }
- return base58check.encode(this.serialize(this.versions.public, this.pubKey));
- }
- static fromMasterSeed(seed, versions = BITCOIN_VERSIONS) {
- assertBytes(seed);
- if (8 * seed.length < 128 || 8 * seed.length > 512) {
- throw new Error(`HDKey: wrong seed length=${seed.length}. Should be between 128 and 512 bits; 256 bits is advised)`);
- }
- const I = hmac(sha512, MASTER_SECRET, seed);
- return new HDKey({
- versions,
- chainCode: I.slice(32),
- privateKey: I.slice(0, 32),
- });
- }
- static fromExtendedKey(base58key, versions = BITCOIN_VERSIONS) {
- // => version(4) || depth(1) || fingerprint(4) || index(4) || chain(32) || key(33)
- const keyBuffer = base58check.decode(base58key);
- const keyView = createView(keyBuffer);
- const version = keyView.getUint32(0, false);
- const opt = {
- versions,
- depth: keyBuffer[4],
- parentFingerprint: keyView.getUint32(5, false),
- index: keyView.getUint32(9, false),
- chainCode: keyBuffer.slice(13, 45),
- };
- const key = keyBuffer.slice(45);
- const isPriv = key[0] === 0;
- if (version !== versions[isPriv ? 'private' : 'public']) {
- throw new Error('Version mismatch');
- }
- if (isPriv) {
- return new HDKey({ ...opt, privateKey: key.slice(1) });
- }
- else {
- return new HDKey({ ...opt, publicKey: key });
- }
- }
- static fromJSON(json) {
- return HDKey.fromExtendedKey(json.xpriv);
- }
- constructor(opt) {
- this.depth = 0;
- this.index = 0;
- this.chainCode = null;
- this.parentFingerprint = 0;
- if (!opt || typeof opt !== 'object') {
- throw new Error('HDKey.constructor must not be called directly');
- }
- this.versions = opt.versions || BITCOIN_VERSIONS;
- this.depth = opt.depth || 0;
- this.chainCode = opt.chainCode || null;
- this.index = opt.index || 0;
- this.parentFingerprint = opt.parentFingerprint || 0;
- if (!this.depth) {
- if (this.parentFingerprint || this.index) {
- throw new Error('HDKey: zero depth with non-zero index/parent fingerprint');
- }
- }
- if (opt.publicKey && opt.privateKey) {
- throw new Error('HDKey: publicKey and privateKey at same time.');
- }
- if (opt.privateKey) {
- if (!secp.utils.isValidPrivateKey(opt.privateKey)) {
- throw new Error('Invalid private key');
- }
- this.privKey =
- typeof opt.privateKey === 'bigint' ? opt.privateKey : bytesToNumber(opt.privateKey);
- this.privKeyBytes = numberToBytes(this.privKey);
- this.pubKey = secp.getPublicKey(opt.privateKey, true);
- }
- else if (opt.publicKey) {
- this.pubKey = Point.fromHex(opt.publicKey).toRawBytes(true); // force compressed point
- }
- else {
- throw new Error('HDKey: no public or private key provided');
- }
- this.pubHash = hash160(this.pubKey);
- }
- derive(path) {
- if (!/^[mM]'?/.test(path)) {
- throw new Error('Path must start with "m" or "M"');
- }
- if (/^[mM]'?$/.test(path)) {
- return this;
- }
- const parts = path.replace(/^[mM]'?\//, '').split('/');
- // tslint:disable-next-line
- let child = this;
- for (const c of parts) {
- const m = /^(\d+)('?)$/.exec(c);
- const m1 = m && m[1];
- if (!m || m.length !== 3 || typeof m1 !== 'string') {
- throw new Error(`Invalid child index: ${c}`);
- }
- let idx = +m1;
- if (!Number.isSafeInteger(idx) || idx >= HARDENED_OFFSET) {
- throw new Error('Invalid index');
- }
- // hardened key
- if (m[2] === "'") {
- idx += HARDENED_OFFSET;
- }
- child = child.deriveChild(idx);
- }
- return child;
- }
- deriveChild(index) {
- if (!this.pubKey || !this.chainCode) {
- throw new Error('No publicKey or chainCode set');
- }
- let data = toU32(index);
- if (index >= HARDENED_OFFSET) {
- // Hardened
- const priv = this.privateKey;
- if (!priv) {
- throw new Error('Could not derive hardened child key');
- }
- // Hardened child: 0x00 || ser256(kpar) || ser32(index)
- data = concatBytes(new Uint8Array([0]), priv, data);
- }
- else {
- // Normal child: serP(point(kpar)) || ser32(index)
- data = concatBytes(this.pubKey, data);
- }
- const I = hmac(sha512, this.chainCode, data);
- const childTweak = bytesToNumber(I.slice(0, 32));
- const chainCode = I.slice(32);
- if (!secp.utils.isValidPrivateKey(childTweak)) {
- throw new Error('Tweak bigger than curve order');
- }
- const opt = {
- versions: this.versions,
- chainCode,
- depth: this.depth + 1,
- parentFingerprint: this.fingerprint,
- index,
- };
- try {
- // Private parent key -> private child key
- if (this.privateKey) {
- const added = mod(this.privKey + childTweak, secp.CURVE.n);
- if (!secp.utils.isValidPrivateKey(added)) {
- throw new Error('The tweak was out of range or the resulted private key is invalid');
- }
- opt.privateKey = added;
- }
- else {
- const added = Point.fromHex(this.pubKey).add(Point.fromPrivateKey(childTweak));
- // Cryptographically impossible: hmac-sha512 preimage would need to be found
- if (added.equals(Point.ZERO)) {
- throw new Error('The tweak was equal to negative P, which made the result key invalid');
- }
- opt.publicKey = added.toRawBytes(true);
- }
- return new HDKey(opt);
- }
- catch (err) {
- return this.deriveChild(index + 1);
- }
- }
- sign(hash) {
- if (!this.privateKey) {
- throw new Error('No privateKey set!');
- }
- assertBytes(hash, 32);
- return secp.sign(hash, this.privKey).toCompactRawBytes();
- }
- verify(hash, signature) {
- assertBytes(hash, 32);
- assertBytes(signature, 64);
- if (!this.publicKey) {
- throw new Error('No publicKey set!');
- }
- let sig;
- try {
- sig = secp.Signature.fromCompact(signature);
- }
- catch (error) {
- return false;
- }
- return secp.verify(sig, hash, this.publicKey);
- }
- wipePrivateData() {
- this.privKey = undefined;
- if (this.privKeyBytes) {
- this.privKeyBytes.fill(0);
- this.privKeyBytes = undefined;
- }
- return this;
- }
- toJSON() {
- return {
- xpriv: this.privateExtendedKey,
- xpub: this.publicExtendedKey,
- };
- }
- serialize(version, key) {
- if (!this.chainCode) {
- throw new Error('No chainCode set');
- }
- assertBytes(key, 33);
- // version(4) || depth(1) || fingerprint(4) || index(4) || chain(32) || key(33)
- return concatBytes(toU32(version), new Uint8Array([this.depth]), toU32(this.parentFingerprint), toU32(this.index), this.chainCode, key);
- }
- }
- //# sourceMappingURL=index.js.map
|