Skip to content

Commit 6943d66

Browse files
authored
Implement BIP-32 Test Vector 5 validation (#1294)
Reject invalid extended keys per BIP-32 spec: - Master keys (depth 0) must have zero parent fingerprint and child index - Add tests for all Test Vector 5 invalid key cases
1 parent be80036 commit 6943d66

File tree

3 files changed

+45
-0
lines changed

3 files changed

+45
-0
lines changed

NBitcoin.Tests/bip32_tests.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,5 +338,42 @@ public void KeyPathShouldNotParseBIP32Overflow()
338338
Assert.Equal(0x80000000U, uint.Parse("2147483648"));
339339
Assert.Throws<FormatException>(() => KeyPath.Parse("/2147483648"));
340340
}
341+
342+
[Fact]
343+
[Trait("UnitTest", "UnitTest")]
344+
public void ShouldRejectInvalidExtendedKeys_BIP32TestVector5()
345+
{
346+
// BIP-32 Test Vector 5: These vectors test that invalid extended keys are recognized as invalid
347+
348+
// pubkey version / prvkey mismatch
349+
Assert.Throws<FormatException>(() => ExtKey.Parse("xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6LBpB85b3D2yc8sfvZU521AAwdZafEz7mnzBBsz4wKY5fTtTQBm", Network.Main));
350+
// prvkey version / pubkey mismatch
351+
Assert.Throws<FormatException>(() => ExtPubKey.Parse("xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFGTQQD3dC4H2D5GBj7vWvSQaaBv5cxi9gafk7NF3pnBju6dwKvH", Network.Main));
352+
// invalid pubkey prefix 04
353+
Assert.Throws<FormatException>(() => ExtPubKey.Parse("xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6Txnt3siSujt9RCVYsx4qHZGc62TG4McvMGcAUjeuwZdduYEvFn", Network.Main));
354+
// invalid prvkey prefix 04
355+
Assert.Throws<FormatException>(() => ExtKey.Parse("xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFGpWnsj83BHtEy5Zt8CcDr1UiRXuWCmTQLxEK9vbz5gPstX92JQ", Network.Main));
356+
// invalid pubkey prefix 01
357+
Assert.Throws<FormatException>(() => ExtPubKey.Parse("xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6N8ZMMXctdiCjxTNq964yKkwrkBJJwpzZS4HS2fxvyYUA4q2Xe4", Network.Main));
358+
// invalid prvkey prefix 01
359+
Assert.Throws<FormatException>(() => ExtKey.Parse("xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFAzHGBP2UuGCqWLTAPLcMtD9y5gkZ6Eq3Rjuahrv17fEQ3Qen6J", Network.Main));
360+
// zero depth with non-zero parent fingerprint
361+
Assert.Throws<ArgumentException>(() => ExtKey.Parse("xprv9s2SPatNQ9Vc6GTbVMFPFo7jsaZySyzk7L8n2uqKXJen3KUmvQNTuLh3fhZMBoG3G4ZW1N2kZuHEPY53qmbZzCHshoQnNf4GvELZfqTUrcv", Network.Main));
362+
Assert.Throws<FormatException>(() => ExtPubKey.Parse("xpub661no6RGEX3uJkY4bNnPcw4URcQTrSibUZ4NqJEw5eBkv7ovTwgiT91XX27VbEXGENhYRCf7hyEbWrR3FewATdCEebj6znwMfQkhRYHRLpJ", Network.Main));
363+
// zero depth with non-zero index
364+
Assert.Throws<ArgumentException>(() => ExtKey.Parse("xprv9s21ZrQH4r4TsiLvyLXqM9P7k1K3EYhA1kkD6xuquB5i39AU8KF42acDyL3qsDbU9NmZn6MsGSUYZEsuoePmjzsB3eFKSUEh3Gu1N3cqVUN", Network.Main));
365+
Assert.Throws<FormatException>(() => ExtPubKey.Parse("xpub661MyMwAuDcm6CRQ5N4qiHKrJ39Xe1R1NyfouMKTTWcguwVcfrZJaNvhpebzGerh7gucBvzEQWRugZDuDXjNDRmXzSZe4c7mnTK97pTvGS8", Network.Main));
366+
// unknown extended key version
367+
Assert.Throws<FormatException>(() => ExtKey.Parse("DMwo58pR1QLEFihHiXPVykYB6fJmsTeHvyTp7hRThAtCX8CvYzgPcn8XnmdfHGMQzT7ayAmfo4z3gY5KfbrZWZ6St24UVf2Qgo6oujFktLHdHY4", Network.Main));
368+
Assert.Throws<FormatException>(() => ExtPubKey.Parse("DMwo58pR1QLEFihHiXPVykYB6fJmsTeHvyTp7hRThAtCX8CvYzgPcn8XnmdfHPmHJiEDXkTiJTVV9rHEBUem2mwVbbNfvT2MTcAqj3nesx8uBf9", Network.Main));
369+
// private key 0 not in 1..n-1
370+
Assert.Throws<ArgumentException>(() => ExtKey.Parse("xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzF93Y5wvzdUayhgkkFoicQZcP3y52uPPxFnfoLZB21Teqt1VvEHx", Network.Main));
371+
// private key n not in 1..n-1
372+
Assert.Throws<ArgumentException>(() => ExtKey.Parse("xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFAzHGBP2UuGCqWLTAPLcMtD5SDKr24z3aiUvKr9bJpdrcLg1y3G", Network.Main));
373+
// invalid pubkey 020000000000000000000000000000000000000000000000000000000000000007
374+
Assert.Throws<FormatException>(() => ExtPubKey.Parse("xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6Q5JXayek4PRsn35jii4veMimro1xefsM58PgBMrvdYre8QyULY", Network.Main));
375+
// invalid checksum
376+
Assert.Throws<FormatException>(() => ExtKey.Parse("xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHL", Network.Main));
377+
}
341378
}
342379
}

NBitcoin/BIP32/ExtKey.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,8 @@ private ExtKey(byte[] bytes, bool isSeed)
237237
i += 4;
238238
nChild = Utils.ToUInt32(bytes, i, false);
239239
i += 4;
240+
if (nDepth == 0 && (parentFingerprint != default || nChild != 0))
241+
throw new ArgumentException("Invalid ExtKey: Master key (depth 0) must have zero parent fingerprint and zero child number");
240242
vchChainCode = new byte[32];
241243
Array.Copy(bytes, i, vchChainCode, 0, 32);
242244
i += 32;
@@ -277,6 +279,8 @@ private ExtKey(ReadOnlySpan<byte> bytes, bool isSeed)
277279
i += 4;
278280
nChild = Utils.ToUInt32(bytes.Slice(i, 4), false);
279281
i += 4;
282+
if (nDepth == 0 && (parentFingerprint != default || nChild != 0))
283+
throw new ArgumentException("Invalid ExtKey: Master key (depth 0) must have zero parent fingerprint and zero child number");
280284
vchChainCode = new byte[32];
281285
bytes.Slice(i, 32).CopyTo(vchChainCode);
282286
i += 32;

NBitcoin/BIP32/ExtPubKey.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ public ExtPubKey(byte[] bytes, int offset, int length)
9191
i += 4;
9292
nChild = Utils.ToUInt32(bytes, i, false);
9393
i += 4;
94+
if (nDepth == 0 && (parentFingerprint != default || nChild != 0))
95+
throw new ArgumentException("Invalid ExtPubKey: Master key (depth 0) must have zero parent fingerprint and zero child number");
9496
vchChainCode = new byte[32];
9597
Array.Copy(bytes, i, vchChainCode, 0, 32);
9698
i += 32;
@@ -115,6 +117,8 @@ public ExtPubKey(ReadOnlySpan<byte> bytes)
115117
i += 4;
116118
nChild = Utils.ToUInt32(bytes.Slice(i, 4), false);
117119
i += 4;
120+
if (nDepth == 0 && (parentFingerprint != default || nChild != 0))
121+
throw new ArgumentException("Invalid ExtPubKey: Master key (depth 0) must have zero parent fingerprint and zero child number");
118122
vchChainCode = new byte[32];
119123
bytes.Slice(i, 32).CopyTo(vchChainCode);
120124
i += 32;

0 commit comments

Comments
 (0)