def test_public_derivation(self): # Construct from extended private key bip32_ctx = Bip32.FromExtendedKey(TEST_PUBLIC_DER_MAIN["ex_priv"]) # Shall not be public self.assertFalse(bip32_ctx.IsPublicOnly()) # Convert to public bip32_ctx.ConvertToPublic() # Shall be public and the public key shall be correct self.assertTrue(bip32_ctx.IsPublicOnly()) self.assertEqual(TEST_PUBLIC_DER_MAIN["ex_pub"], bip32_ctx.PublicKey().ToExtended()) # Getting the private key shall raise an exception self.assertRaises(Bip32KeyError, bip32_ctx.PrivateKey) self.assertRaises(Bip32KeyError, bip32_ctx.EcdsaPrivateKey) # Test derivation paths for test in TEST_PUBLIC_DER_MAIN["der_paths"]: # Public derivation does not support hardened indexes if Bip32Utils.IsHardenedIndex(test["index"]): self.assertRaises(Bip32KeyError, bip32_ctx.ChildKey, test["index"]) else: bip32_ctx = bip32_ctx.ChildKey(test["index"]) self.assertEqual(test["ex_pub"], bip32_ctx.PublicKey().ToExtended())
def generate_uuid(mnemonic, passphrase=""): # At this point the Cobo Vault hardware is directly using the (secret) # BIP39 mnemonic, converting it to a BIP39 seed value. If one is using a # Shamir (SLIP39) mnemonic a different path is taken to generate these # seed bytes. seed_bytes = Bip39SeedGenerator(mnemonic).Generate(passphrase) # From this point onwards the code is identical whether a BIP39 or SLIP39 # mnemonic. The following extracts the BIP32 Root Key. This is still secret # data that we don't want leaking from the Cobo Vault hardware. bip32_root = Bip32.FromSeed(seed_bytes) # Given the BIP32 root key, derive a BIP32 extended (public/private) keypair, # using Cobo's BIP32 derivation path. Note that this path is only used for # generating the 'UUID' used by the Cobo Vault (and app). For coin-specific # (e.g. Bitcoin etc.) keys the Cobo Vault hardware and App use more typical # hardened paths that are commonly used - E.g. m/49'/0'/0' for bitcoin, # m/44'/60'/0' for Ethereum, etc. Still secret data here! cobo_extend_key = ( bip32_root.ChildKey(Bip32Utils.HardenIndex(44)) .ChildKey(Bip32Utils.HardenIndex(1131373167)) .ChildKey(Bip32Utils.HardenIndex(0)) ) # Up until this point the Cobo Vault hardware has been dealing with secret # information that should never be leaked outside the device, as doing so # would allow stealing of one's keys and thus cryptocurrency. The next step # discards the private key information and extracts the public key only. The # public key can be used to find all transactions associated with a wallet, but # it *cannot* be used to spend (or steal) cryptocurrency. public_key = cobo_extend_key.PublicKey().RawCompressed().ToHex() # After discarding the private (secret) key, compress the public key and remove # a couple bytes to produce the 'uuid'. uuid = public_key[2:] return uuid
def __test_public_derivation_ex_key(ut_class, bip32_ctx, test_vector): # Shall be public and the public key shall be correct ut_class.assertTrue(bip32_ctx.IsPublicOnly()) ut_class.assertEqual(test_vector["ex_pub"], bip32_ctx.PublicKey().ToExtended()) # Getting the private key shall raise an exception ut_class.assertRaises(Bip32KeyError, bip32_ctx.PrivateKey) # Test derivation paths for test in test_vector["der_paths"]: # Public derivation does not support hardened indexes if Bip32Utils.IsHardenedIndex(test["index"]): ut_class.assertRaises(Bip32KeyError, bip32_ctx.ChildKey, test["index"]) else: bip32_ctx = bip32_ctx.ChildKey(test["index"]) ut_class.assertEqual(test["ex_pub"], bip32_ctx.PublicKey().ToExtended())
def __test_path(self, test, path): # Check length self.assertEqual(len(test["parsed"]), path.Length()) # Check string conversion self.assertEqual(test["to_str"], path.ToStr()) self.assertEqual(test["to_str"], str(path)) # Check by iterating for idx, elem in enumerate(path): test_elem = test["parsed"][idx] self.assertEqual(test_elem, int(elem)) self.assertEqual(test_elem, int(path[idx])) self.assertEqual(test_elem, elem.ToInt()) self.assertEqual(Bip32Utils.IsHardenedIndex(test_elem), elem.IsHardened()) # Check by converting to list for idx, elem in enumerate(path.ToList()): self.assertEqual(test["parsed"][idx], elem)
# Tests from BIP32 page TEST_VECT_BIP32 = \ [ { "seed" : b"000102030405060708090a0b0c0d0e0f", "master" : { "ex_pub" : "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8", "ex_priv" : "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi", }, "der_paths" : [ # m/0' { "path" : "m/0'", "index" : Bip32Utils.HardenIndex(0), "ex_pub" : "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw", "ex_priv" : "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7", }, # m/0'/1 { "path" : "m/0'/1", "index" : 1, "ex_pub" : "xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ", "ex_priv" : "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs", }, # m/0'/1/2' { "path" : "m/0'/1/2'", "index" : Bip32Utils.HardenIndex(2), "ex_pub" : "xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5",
"xpub661MyMwAqRbcFybaNRzmKwjLEeQdU4ciWTZ1zPxvvN683xNT57Gr2k7YbdqBz5N5NdqeCEhiZLvSmZE721EUipgYL2v1QEGunyEs8JviJ6x", "ex_priv": "xprv9s21ZrQH143K3VX7GQTkxonbgca94bts9EdRC1ZKN2Z9BA3JXZxbUwo4kS28ECmXhK1NicjQ7yBwWbZXgjRVktP6Tzi4YqetK5ueSA2CaXP", "pub_key": "00835e3307bf32df124bc0bd3e3d5eb4a751ceeebe06b69fbce54fef97bc37c062", "priv_key": "2b4be7f19ee27bbf30c667b642d5f4aa69fd169872f8fc3059c08ebae2eb19e7", "chain_code": "90046a93de5380a72b5e45010748567d5ea02bbf6522f979e05c0d8d8ca9fffb", "parent_fprint": "00000000", }, "der_paths": [ # m/0' { "path": "m/0'", "index": Bip32Utils.HardenIndex(0), "ex_pub": "xpub69F72R5LsVNeZxu7hYqxueqXRsieg29CFBMAd5rr1YuAK4CCuG3wc7dT2pucqZppRkHRDGoRyvQNHLnSVPYQ8Eph974t7ok1k17Q56yv79x", "ex_priv": "xprv9vFkcuYT37pMMUpebXJxYWtnsqtAGZRLsxRZphTETDNBSFs4Mijh4KJyBcsGNuNDY4SsYz8pwQ4L9ob2Qw2gviLdtQPAwG3t4Wf8Lb4rrXX", "pub_key": "00df1f51aae49a3c17d07f603ded31c409e4c81fa8b32425a7e0de4143d3cfbeac", "priv_key": "68e0fe46dfb67e368c75379acec591dad19df3cde26e63b93a8e704f1dade7a3", "chain_code": "8b59aa11380b624e81507a27fedda59fea6d0b779a778918a2fd3590e16e9c69", "parent_fprint": "b8637b7e", }, # m/0'/1' { "path": "m/0'/1'",
}, { "path": "m/ 0/1", "parsed": [0, 1], "is_absolute": True, "to_str": "m/0/1", }, { "path": "m/// 0//1", "parsed": [0, 1], "is_absolute": True, "to_str": "m/0/1", }, { "path": "m/0 /1'", "parsed": [0, Bip32Utils.HardenIndex(1)], "is_absolute": True, "to_str": "m/0/1'", }, { "path": "m/0 /1p", "parsed": [0, Bip32Utils.HardenIndex(1)], "is_absolute": True, "to_str": "m/0/1'", }, { "path": "m/0'/1'/2/", "parsed": [Bip32Utils.HardenIndex(0), Bip32Utils.HardenIndex(1), 2], "is_absolute": True, "to_str": "m/0'/1'/2",