Skip to content

Commit 436e5d7

Browse files
authored
Merge pull request #19 from paulmillr/audit
Audit fixes
2 parents 0652c61 + 8377186 commit 436e5d7

File tree

3 files changed

+181
-3
lines changed

3 files changed

+181
-3
lines changed

src/bip39/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ function getCoder(wordlist: string[]) {
4646
) {
4747
throw new Error("Worlist: expected array of 2048 strings");
4848
}
49+
for (const i of wordlist) {
50+
if (typeof i !== "string") {
51+
throw new Error(`Wordlist: non-string element: ${i}`);
52+
}
53+
}
4954
return baseUtils.chain(
5055
baseUtils.checksum(1, checksum),
5156
baseUtils.radix2(11, true),

src/hdkey.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ export interface Versions {
4040
const hash160 = (data: Uint8Array) => ripemd160(sha256(data));
4141
const fromU32 = (data: Uint8Array) => createView(data).getUint32(0, false);
4242
const toU32 = (n: number) => {
43+
if (!Number.isSafeInteger(n) || n < 0 || n >= 2 ** 32) {
44+
throw new Error(`Invalid number=${n}. Should be [0, 2**32)`);
45+
}
4346
const buf = new Uint8Array(4);
4447
createView(buf).setUint32(0, n, false);
4548
return buf;
@@ -97,6 +100,11 @@ export class HDKey {
97100
seed: Uint8Array,
98101
versions: Versions = BITCOIN_VERSIONS
99102
): HDKey {
103+
if (8 * seed.length < 128 || 8 * seed.length > 512) {
104+
throw new Error(
105+
`HDKey: wrong seed length=${seed.length}. Should be between 128 and 512 bits; 256 bits is advised)`
106+
);
107+
}
100108
const I = hmac(sha512, MASTER_SECRET, seed);
101109
return new HDKey({
102110
versions,
@@ -154,6 +162,13 @@ export class HDKey {
154162
this.chainCode = opt.chainCode;
155163
this.index = opt.index || 0;
156164
this.parentFingerprint = opt.parentFingerprint || 0;
165+
if (!this.depth) {
166+
if (this.parentFingerprint || this.index) {
167+
throw new Error(
168+
"HDKey: zero depth with non-zero index/parent fingerprint"
169+
);
170+
}
171+
}
157172
if (opt.publicKey && opt.privateKey) {
158173
throw new Error("HDKey: publicKey and privateKey at same time.");
159174
}
@@ -204,16 +219,15 @@ export class HDKey {
204219
}
205220

206221
public deriveChild(index: number): HDKey {
207-
if (!Number.isSafeInteger(index) || index < 0 || index >= 2 ** 33) {
222+
if (!Number.isSafeInteger(index) || index < 0 || index >= 2 ** 32) {
208223
throw new Error(
209224
`Child index should be positive 32-bit integer, not ${index}`
210225
);
211226
}
212227
if (!this.pubKey || !this.chainCode) {
213228
throw new Error("No publicKey or chainCode set");
214229
}
215-
let data = new Uint8Array(4);
216-
createView(data).setUint32(0, index, false);
230+
let data = toU32(index);
217231
if (index >= HARDENED_OFFSET) {
218232
// Hardened
219233
const priv = this.privateKey;

test/test-vectors/hdkey.ts

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ describe("hdkey", () => {
118118
childkey.privateExtendedKey,
119119
"xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef"
120120
);
121+
// Should throw on 2**32 childs
122+
throws(() => hdkey.deriveChild(2 ** 32));
121123
});
122124

123125
it("Should derive public key correctly", () => {
@@ -445,4 +447,161 @@ describe("hdkey", () => {
445447
throws(() => hdkey.derive(t));
446448
}
447449
});
450+
// https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
451+
describe("Spec test vectors", () => {
452+
it("Test Vector 1", () => {
453+
const master = HDKey.fromMasterSeed(
454+
hexToBytes("000102030405060708090a0b0c0d0e0f")
455+
);
456+
deepStrictEqual(master.derive("m").toJSON(), {
457+
xpriv:
458+
"xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi",
459+
xpub:
460+
"xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8"
461+
});
462+
deepStrictEqual(master.derive("m/0'").toJSON(), {
463+
xpriv:
464+
"xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7",
465+
xpub:
466+
"xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw"
467+
});
468+
deepStrictEqual(master.derive("m/0'/1").toJSON(), {
469+
xpriv:
470+
"xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs",
471+
xpub:
472+
"xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ"
473+
});
474+
deepStrictEqual(master.derive("m/0'/1/2'").toJSON(), {
475+
xpriv:
476+
"xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM",
477+
xpub:
478+
"xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5"
479+
});
480+
deepStrictEqual(master.derive("m/0'/1/2'/2").toJSON(), {
481+
xpriv:
482+
"xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334",
483+
xpub:
484+
"xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV"
485+
});
486+
deepStrictEqual(master.derive("m/0'/1/2'/2/1000000000").toJSON(), {
487+
xpriv:
488+
"xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76",
489+
xpub:
490+
"xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy"
491+
});
492+
});
493+
it("Test Vector 2", () => {
494+
const master = HDKey.fromMasterSeed(
495+
hexToBytes(
496+
"fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"
497+
)
498+
);
499+
deepStrictEqual(master.derive("m").toJSON(), {
500+
xpriv:
501+
"xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U",
502+
xpub:
503+
"xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB"
504+
});
505+
deepStrictEqual(master.derive("m/0").toJSON(), {
506+
xpriv:
507+
"xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt",
508+
xpub:
509+
"xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH"
510+
});
511+
deepStrictEqual(master.derive("m/0/2147483647'").toJSON(), {
512+
xpriv:
513+
"xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9",
514+
xpub:
515+
"xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a"
516+
});
517+
deepStrictEqual(master.derive("m/0/2147483647'/1").toJSON(), {
518+
xpriv:
519+
"xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef",
520+
xpub:
521+
"xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon"
522+
});
523+
deepStrictEqual(master.derive("m/0/2147483647'/1/2147483646'").toJSON(), {
524+
xpriv:
525+
"xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc",
526+
xpub:
527+
"xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL"
528+
});
529+
deepStrictEqual(
530+
master.derive("m/0/2147483647'/1/2147483646'/2").toJSON(),
531+
{
532+
xpriv:
533+
"xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j",
534+
xpub:
535+
"xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt"
536+
}
537+
);
538+
});
539+
it("Test Vector 3", () => {
540+
const master = HDKey.fromMasterSeed(
541+
hexToBytes(
542+
"4b381541583be4423346c643850da4b320e46a87ae3d2a4e6da11eba819cd4acba45d239319ac14f863b8d5ab5a0d0c64d2e8a1e7d1457df2e5a3c51c73235be"
543+
)
544+
);
545+
deepStrictEqual(master.derive("m").toJSON(), {
546+
xpriv:
547+
"xprv9s21ZrQH143K25QhxbucbDDuQ4naNntJRi4KUfWT7xo4EKsHt2QJDu7KXp1A3u7Bi1j8ph3EGsZ9Xvz9dGuVrtHHs7pXeTzjuxBrCmmhgC6",
548+
xpub:
549+
"xpub661MyMwAqRbcEZVB4dScxMAdx6d4nFc9nvyvH3v4gJL378CSRZiYmhRoP7mBy6gSPSCYk6SzXPTf3ND1cZAceL7SfJ1Z3GC8vBgp2epUt13"
550+
});
551+
deepStrictEqual(master.derive("m/0'").toJSON(), {
552+
xpriv:
553+
"xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L",
554+
xpub:
555+
"xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y"
556+
});
557+
});
558+
it("Test Vector 4", () => {
559+
const master = HDKey.fromMasterSeed(
560+
hexToBytes(
561+
"3ddd5602285899a946114506157c7997e5444528f3003f6134712147db19b678"
562+
)
563+
);
564+
deepStrictEqual(master.derive("m").toJSON(), {
565+
xpriv:
566+
"xprv9s21ZrQH143K48vGoLGRPxgo2JNkJ3J3fqkirQC2zVdk5Dgd5w14S7fRDyHH4dWNHUgkvsvNDCkvAwcSHNAQwhwgNMgZhLtQC63zxwhQmRv",
567+
xpub:
568+
"xpub661MyMwAqRbcGczjuMoRm6dXaLDEhW1u34gKenbeYqAix21mdUKJyuyu5F1rzYGVxyL6tmgBUAEPrEz92mBXjByMRiJdba9wpnN37RLLAXa"
569+
});
570+
deepStrictEqual(master.derive("m/0'").toJSON(), {
571+
xpriv:
572+
"xprv9vB7xEWwNp9kh1wQRfCCQMnZUEG21LpbR9NPCNN1dwhiZkjjeGRnaALmPXCX7SgjFTiCTT6bXes17boXtjq3xLpcDjzEuGLQBM5ohqkao9G",
573+
xpub:
574+
"xpub69AUMk3qDBi3uW1sXgjCmVjJ2G6WQoYSnNHyzkmdCHEhSZ4tBok37xfFEqHd2AddP56Tqp4o56AePAgCjYdvpW2PU2jbUPFKsav5ut6Ch1m"
575+
});
576+
deepStrictEqual(master.derive("m/0'/1'").toJSON(), {
577+
xpriv:
578+
"xprv9xJocDuwtYCMNAo3Zw76WENQeAS6WGXQ55RCy7tDJ8oALr4FWkuVoHJeHVAcAqiZLE7Je3vZJHxspZdFHfnBEjHqU5hG1Jaj32dVoS6XLT1",
579+
xpub:
580+
"xpub6BJA1jSqiukeaesWfxe6sNK9CCGaujFFSJLomWHprUL9DePQ4JDkM5d88n49sMGJxrhpjazuXYWdMf17C9T5XnxkopaeS7jGk1GyyVziaMt"
581+
});
582+
});
583+
it("Test Vector 5", () => {
584+
const keys = [
585+
"xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6LBpB85b3D2yc8sfvZU521AAwdZafEz7mnzBBsz4wKY5fTtTQBm", // (pubkey version / prvkey mismatch)
586+
"xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFGTQQD3dC4H2D5GBj7vWvSQaaBv5cxi9gafk7NF3pnBju6dwKvH", // (prvkey version / pubkey mismatch)
587+
"xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6Txnt3siSujt9RCVYsx4qHZGc62TG4McvMGcAUjeuwZdduYEvFn", // (invalid pubkey prefix 04)
588+
"xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFGpWnsj83BHtEy5Zt8CcDr1UiRXuWCmTQLxEK9vbz5gPstX92JQ", // (invalid prvkey prefix 04)
589+
"xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6N8ZMMXctdiCjxTNq964yKkwrkBJJwpzZS4HS2fxvyYUA4q2Xe4", // (invalid pubkey prefix 01)
590+
"xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFAzHGBP2UuGCqWLTAPLcMtD9y5gkZ6Eq3Rjuahrv17fEQ3Qen6J", // (invalid prvkey prefix 01)
591+
"xprv9s2SPatNQ9Vc6GTbVMFPFo7jsaZySyzk7L8n2uqKXJen3KUmvQNTuLh3fhZMBoG3G4ZW1N2kZuHEPY53qmbZzCHshoQnNf4GvELZfqTUrcv", // (zero depth with non-zero parent fingerprint)
592+
"xpub661no6RGEX3uJkY4bNnPcw4URcQTrSibUZ4NqJEw5eBkv7ovTwgiT91XX27VbEXGENhYRCf7hyEbWrR3FewATdCEebj6znwMfQkhRYHRLpJ", // (zero depth with non-zero parent fingerprint)
593+
"xprv9s21ZrQH4r4TsiLvyLXqM9P7k1K3EYhA1kkD6xuquB5i39AU8KF42acDyL3qsDbU9NmZn6MsGSUYZEsuoePmjzsB3eFKSUEh3Gu1N3cqVUN", // (zero depth with non-zero index)
594+
"xpub661MyMwAuDcm6CRQ5N4qiHKrJ39Xe1R1NyfouMKTTWcguwVcfrZJaNvhpebzGerh7gucBvzEQWRugZDuDXjNDRmXzSZe4c7mnTK97pTvGS8", // (zero depth with non-zero index)
595+
"DMwo58pR1QLEFihHiXPVykYB6fJmsTeHvyTp7hRThAtCX8CvYzgPcn8XnmdfHGMQzT7ayAmfo4z3gY5KfbrZWZ6St24UVf2Qgo6oujFktLHdHY4", // (unknown extended key version)
596+
"DMwo58pR1QLEFihHiXPVykYB6fJmsTeHvyTp7hRThAtCX8CvYzgPcn8XnmdfHPmHJiEDXkTiJTVV9rHEBUem2mwVbbNfvT2MTcAqj3nesx8uBf9", // (unknown extended key version)
597+
"xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzF93Y5wvzdUayhgkkFoicQZcP3y52uPPxFnfoLZB21Teqt1VvEHx", // (private key 0 not in 1..n-1)
598+
"xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFAzHGBP2UuGCqWLTAPLcMtD5SDKr24z3aiUvKr9bJpdrcLg1y3G", // (private key n not in 1..n-1)
599+
"xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6Q5JXayek4PRsn35jii4veMimro1xefsM58PgBMrvdYre8QyULY", // (invalid pubkey 020000000000000000000000000000000000000000000000000000000000000007)
600+
"xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHL" // (invalid checksum)
601+
];
602+
for (const c of keys) {
603+
throws(() => HDKey.fromExtendedKey(c));
604+
}
605+
});
606+
});
448607
});

0 commit comments

Comments
 (0)