diff --git a/ngu/hdnode.c b/ngu/hdnode.c index 5bfd840..2c8b3c5 100644 --- a/ngu/hdnode.c +++ b/ngu/hdnode.c @@ -261,18 +261,29 @@ STATIC mp_obj_t s_hdnode_deserialize(mp_obj_t self_in, mp_obj_t encoded) { p += 32; if(p[0] == 0x00) { + /* mainnet/testnet private */ + assert((version == 0x0488ADE4) || (version == 0x04358394)); p++; memcpy(self->privkey, p, 32); p += 32; self->have_private = true; _calc_pubkey(self); } else if(p[0] == 0x02 || p[0] == 0x3) { + /* mainnet/testnet public */ + assert((version == 0x0488B21E) || (version == 0x043587CF)); // 33 bytes of pubkey self->have_private = false; memcpy(self->pubkey, p, 33); p += 33; + + /* verify that parsed pubkey is valid */ + secp256k1_pubkey pub; + int ok; + ok = secp256k1_ec_pubkey_parse(lib_ctx, &pub, self->pubkey, 33); + if(!ok) goto fail; + } else { - mp_raise_ValueError(MP_ERROR_TEXT("bad pubkey")); + goto fail; } _calc_hash160(self); @@ -291,6 +302,11 @@ STATIC mp_obj_t s_hdnode_deserialize(mp_obj_t self_in, mp_obj_t encoded) { assert(p == &tmp[78]); return mp_obj_new_int(version); + + fail: + self->depth = -1; + mp_raise_ValueError(MP_ERROR_TEXT("bad pubkey")); + return 0; // not reached } STATIC MP_DEFINE_CONST_FUN_OBJ_2(s_hdnode_deserialize_obj, s_hdnode_deserialize); diff --git a/ngu/ngu_tests/Makefile b/ngu/ngu_tests/Makefile index 21d94e0..1b91be7 100644 --- a/ngu/ngu_tests/Makefile +++ b/ngu/ngu_tests/Makefile @@ -25,6 +25,7 @@ test tests: $(MPY) test_ec_gen.py $(PY) test_bip39.py $(MPY) test_bip39.py + $(MPY) test_bip32.py # runs the test compiled in, not here # or, on target diff --git a/ngu/ngu_tests/run.py b/ngu/ngu_tests/run.py index 478535b..3c3644a 100644 --- a/ngu/ngu_tests/run.py +++ b/ngu/ngu_tests/run.py @@ -23,3 +23,4 @@ import ngu_tests.test_ec import ngu_tests.test_ec_gen import ngu_tests.test_bip39 +import ngu_tests.test_bip32 diff --git a/ngu/ngu_tests/test_bip32.py b/ngu/ngu_tests/test_bip32.py new file mode 100644 index 0000000..f08c3f9 --- /dev/null +++ b/ngu/ngu_tests/test_bip32.py @@ -0,0 +1,35 @@ +# Invalid Extended Keys test +# https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#test-vector-5 +import ngu + +TO_DO = [ + "xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6LBpB85b3D2yc8sfvZU521AAwdZafEz7mnzBBsz4wKY5fTtTQBm", #(pubkey version / prvkey mismatch) + "xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6Txnt3siSujt9RCVYsx4qHZGc62TG4McvMGcAUjeuwZdduYEvFn", #(invalid pubkey prefix 04) + "xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6N8ZMMXctdiCjxTNq964yKkwrkBJJwpzZS4HS2fxvyYUA4q2Xe4", #(invalid pubkey prefix 01) + # allowed + # "xpub661no6RGEX3uJkY4bNnPcw4URcQTrSibUZ4NqJEw5eBkv7ovTwgiT91XX27VbEXGENhYRCf7hyEbWrR3FewATdCEebj6znwMfQkhRYHRLpJ", # (zero depth with non - zero parent fingerprint) + # "xpub661MyMwAuDcm6CRQ5N4qiHKrJ39Xe1R1NyfouMKTTWcguwVcfrZJaNvhpebzGerh7gucBvzEQWRugZDuDXjNDRmXzSZe4c7mnTK97pTvGS8", # (zero depth with non - zero index) + "xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6Q5JXayek4PRsn35jii4veMimro1xefsM58PgBMrvdYre8QyULY", # (invalid pubkey 020000000000000000000000000000000000000000000000000000000000000007) + "DMwo58pR1QLEFihHiXPVykYB6fJmsTeHvyTp7hRThAtCX8CvYzgPcn8XnmdfHPmHJiEDXkTiJTVV9rHEBUem2mwVbbNfvT2MTcAqj3nesx8uBf9", # unknown version + # private below + "xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFGTQQD3dC4H2D5GBj7vWvSQaaBv5cxi9gafk7NF3pnBju6dwKvH", # (prvkey version / pubkey mismatch) + "xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFGpWnsj83BHtEy5Zt8CcDr1UiRXuWCmTQLxEK9vbz5gPstX92JQ", # (invalid prvkey prefix 04) + "xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFAzHGBP2UuGCqWLTAPLcMtD9y5gkZ6Eq3Rjuahrv17fEQ3Qen6J", # (invalid prvkey prefix 01) + # allowed + # "xprv9s2SPatNQ9Vc6GTbVMFPFo7jsaZySyzk7L8n2uqKXJen3KUmvQNTuLh3fhZMBoG3G4ZW1N2kZuHEPY53qmbZzCHshoQnNf4GvELZfqTUrcv", # (zero depth with non - zero parent fingerprint) + # "xprv9s21ZrQH4r4TsiLvyLXqM9P7k1K3EYhA1kkD6xuquB5i39AU8KF42acDyL3qsDbU9NmZn6MsGSUYZEsuoePmjzsB3eFKSUEh3Gu1N3cqVUN", # (zero depth with non - zero index) + "xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzF93Y5wvzdUayhgkkFoicQZcP3y52uPPxFnfoLZB21Teqt1VvEHx", # (private key 0 not in 1..n - 1) + "xprv9s21ZrQH143K24Mfq5zL5MhWK9hUhhGbd45hLXo2Pq2oqzMMo63oStZzFAzHGBP2UuGCqWLTAPLcMtD5SDKr24z3aiUvKr9bJpdrcLg1y3G", # (private key n not in 1..n - 1) + "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHL", # (invalid checksum) + "DMwo58pR1QLEFihHiXPVykYB6fJmsTeHvyTp7hRThAtCX8CvYzgPcn8XnmdfHGMQzT7ayAmfo4z3gY5KfbrZWZ6St24UVf2Qgo6oujFktLHdHY4", # (unknown version) +] + +HDNode = ngu.hdnode.HDNode +for ek in TO_DO: + n = HDNode() + try: + n.deserialize(ek) + raise AttributeError + except (AssertionError, ValueError, RuntimeError): pass + +print('PASS - test_bip32') \ No newline at end of file diff --git a/ngu/ngu_tests/test_hdnode.py b/ngu/ngu_tests/test_hdnode.py index f72cb0f..bfb185f 100644 --- a/ngu/ngu_tests/test_hdnode.py +++ b/ngu/ngu_tests/test_hdnode.py @@ -85,7 +85,7 @@ def test_derive(): p1 = a.derive(43, False).pubkey() b = HDNode() - assert b.deserialize(a.serialize(0x123, 0)) == 0x123 + assert b.deserialize(a.serialize(0x0488B21E, 0)) == 0x0488B21E p2 = b.pubkey() assert b.depth() == 1 assert b.parent_fp() == m_fp