def getChangePublicKey( mnemonic: str, password: str, skip: int ) -> bytes: seed: bytes = sha256(Bip39SeedGenerator(mnemonic).Generate(password)).digest() extendedKey: bytes = bytes() #Above's getIndex, yet utilizing the return value of derive c: int = -1 failures: int = 0 while skip != -1: c += 1 try: extendedKey = BIP32.derive( seed, [44 + (1 << 31), 5132 + (1 << 31), 0 + (1 << 31), 1, c] ) #Since we derived a valid address, decrement skip. skip -= 1 failures = 0 except Exception: #Safety check to prevent infinite execution. failures += 1 if failures == 100: raise Exception("Invalid mnemonic passed to getPrivateKey.") continue return RistrettoScalar(extendedKey[:32]).toPoint().serialize()
def getIndex( mnemonic: str, password: str, skip: int ) -> int: seed: bytes = sha256(Bip39SeedGenerator(mnemonic).Generate(password)).digest() c: int = -1 failures: int = 0 while skip != -1: c += 1 try: BIP32.derive( seed, [44 + (1 << 31), 5132 + (1 << 31), 0 + (1 << 31), 0, c] ) #Since we derived a valid address, decrement skip. skip -= 1 failures = 0 except Exception: #Safety check to prevent infinite execution. failures += 1 if failures == 100: raise Exception("Invalid mnemonic passed to getPrivateKey.") continue return c
def main() -> None: # Print info print("\nBenchmark started!") print("Configuration:") print(f" - Test type: {TestsConf.TEST_TYPE}") print(f" - Number of tests: {TestsConf.TEST_NUM}") print(f" - Number of iterations for each test: {TestsConf.TEST_ITR_NUM}") print( f" - Number of iterations for caching: {TestsConf.TEST_CACHE_NUM}\n") # Generate a seed mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon "\ "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art" seed_bytes = Bip39SeedGenerator(mnemonic).Generate() # Get tests class type tests_cls = TestsConsts.TEST_TYPE_TO_CLASS_TYPE[TestsConf.TEST_TYPE] # Run tests tests = tests_cls(TestsConf.TEST_NUM, TestsConf.TEST_ITR_NUM, TestsConf.TEST_CACHE_NUM) tests.RunTests(seed_bytes) # Print average time print("\nBenchmark completed.") print(f"Average time: {tests.GetAverageTime():.0f}ms\n")
def test_vector(self): for test in TEST_VECT: lang = test["lang"] if "lang" in test else Bip39Languages.ENGLISH # Test mnemonic generator mnemonic = Bip39MnemonicGenerator(lang).FromEntropy( binascii.unhexlify(test["entropy"])) self.assertEqual(test["mnemonic"], mnemonic) # Test mnemonic validator using string (language specified) bip39_mnemonic_validator = Bip39MnemonicValidator(mnemonic, lang) entropy = bip39_mnemonic_validator.GetEntropy() self.assertEqual(test["entropy"], binascii.hexlify(entropy)) self.assertTrue(bip39_mnemonic_validator.IsValid()) # Test mnemonic validator using list (automatic language detection) bip39_mnemonic_validator = Bip39MnemonicValidator( mnemonic.split(" ")) entropy = bip39_mnemonic_validator.GetEntropy() self.assertEqual(test["entropy"], binascii.hexlify(entropy)) self.assertTrue(bip39_mnemonic_validator.IsValid()) # Test seed generator seed = Bip39SeedGenerator(mnemonic, lang).Generate(TEST_PASSPHRASE) self.assertEqual(test["seed"], binascii.hexlify(seed))
def test_vector(self): for test in TEST_VECT: # Test mnemonic generator mnemonic = Bip39MnemonicGenerator.FromEntropy( binascii.unhexlify(test["entropy"])) self.assertEqual(test["mnemonic"], mnemonic) # Test mnemonic validator using string bip39_mnemonic_validator = Bip39MnemonicValidator(mnemonic) entropy = bip39_mnemonic_validator.GetEntropy() self.assertEqual(test["entropy"], binascii.hexlify(entropy)) self.assertTrue(bip39_mnemonic_validator.Validate()) # Test mnemonic validator using list bip39_mnemonic_validator = Bip39MnemonicValidator( mnemonic.split(" ")) entropy = bip39_mnemonic_validator.GetEntropy() self.assertEqual(test["entropy"], binascii.hexlify(entropy)) self.assertTrue(bip39_mnemonic_validator.Validate()) # Test seed generator seed = Bip39SeedGenerator(mnemonic).Generate(TEST_PASSPHRASE) self.assertEqual(test["seed"], binascii.hexlify(seed))
def get_addresses_from(mnemonic): seed_bytes = Bip39SeedGenerator(mnemonic).Generate() addresses = [] # TODO: manually derive last index from one parent to save some time, allow other derive paths for i in range(0, options.indices - 1): bip32_ctx = Bip32.FromSeedAndPath(seed_bytes, f"m/0'/2'/{i}") key = Key(bip32_ctx.PrivateKey().Raw().ToHex()) addresses.append(key.address) return addresses
def getPrivateKey( mnemonic: str, password: str, skip: int ) -> bytes: seed: bytes = sha256(Bip39SeedGenerator(mnemonic).Generate(password)).digest() return BIP32.derive( seed, [44 + (1 << 31), 5132 + (1 << 31), 0 + (1 << 31), 0, getIndex(mnemonic, password, skip)] )
def getMnemonic( password: str = "" ) -> str: while True: res: str = Bip39MnemonicGenerator.FromWordsNumber(Bip39WordsNum.WORDS_NUM_24) seed: bytes = sha256(Bip39SeedGenerator(res).Generate(password)).digest() try: BIP32.derive(seed, [44 + (1 << 31), 5132 + (1 << 31), 0 + (1 << 31), 0]) BIP32.derive(seed, [44 + (1 << 31), 5132 + (1 << 31), 0 + (1 << 31), 1]) except Exception: continue return res
def get_account_from_words(words: str, index: int = 0, hd_path: str = ETHEREUM_PATH) -> Account: """ :param words: Mnemonic words generated using Bip39 :param index: Index of account :param hd_path: Bip44 Path. By default Ethereum is used :return: List of ethereum public addresses """ seed = Bip39SeedGenerator(words).Generate() bip32_ctx = Bip32.FromSeedAndPath(seed, hd_path) return Account.from_key( bip32_ctx.ChildKey(index).PrivateKey().Raw().ToBytes())
def generate(self): # Generate random mnemonic mnemonic = Bip39MnemonicGenerator.FromWordsNumber(12) # Generate seed from mnemonic seed_bytes = Bip39SeedGenerator(mnemonic).Generate() # Generate BIP44 master keys bip_obj_mst = Bip44.FromSeed(seed_bytes, Bip44Coins.DASH) address = bip_obj_mst.PublicKey().ToAddress() wif = bip_obj_mst.PrivateKey().ToWif() seed = mnemonic return CryptoCoin(address, wif, seed)
def get_address_from_words(words: str, index: int = 0, hd_path: str = ETHEREUM_PATH) -> str: """ :param words: Mnemonic words generated using Bip39 :param index: Index of account :param hd_path: Bip44 Path. By default Ethereum is used :return: List of ethereum public addresses """ seed = Bip39SeedGenerator(words).Generate() bip32_ctx = Bip32.FromSeedAndPath(seed, hd_path) pub_key = bip32_ctx.ChildKey(index).m_ver_key.pubkey return checksum_encode( sha3( encode_int32(pub_key.point.x()) + encode_int32(pub_key.point.y()))[12:])
def generate_bitcoin_wallet(query_params={}): if blockchain_validator.generate_litecoin_wallet(query_params): if query_params != {}: mnemonic = query_params['mnemonic'] else: mnemonic = wallet.generate_mnemonic(strength=256) if Bip39MnemonicValidator(mnemonic).Validate(): seed_bytes = Bip39SeedGenerator(mnemonic).Generate() bip32_ctx = Bip32.FromSeedAndPath(seed_bytes, "m/44'/0'/0'/0") return { "xpriv": bip32_ctx.PrivateKey().ToExtended(), "xpub": bip32_ctx.PublicKey().ToExtended(), "mnemonic": mnemonic } else: return 'Mnemonic is not valid!'
def test_vector(self): for test in TEST_VECT: lang = test["lang"] if "lang" in test else Bip39Languages.ENGLISH # Test mnemonic generator mnemonic = Bip39MnemonicGenerator(lang).FromEntropy( binascii.unhexlify(test["entropy"])) self.assertEqual(test["mnemonic"], mnemonic.ToStr()) self.assertEqual(test["mnemonic"], str(mnemonic)) self.assertEqual(test["mnemonic"].split(" "), mnemonic.ToList()) self.assertEqual(len(test["mnemonic"].split(" ")), mnemonic.WordsCount()) # Test mnemonic validator (language specified) mnemonic_validator = Bip39MnemonicValidator(lang) self.assertTrue(mnemonic_validator.IsValid(mnemonic)) # Test mnemonic validator (automatic language detection) mnemonic_validator = Bip39MnemonicValidator() self.assertTrue(mnemonic_validator.IsValid(mnemonic)) # Test decoder (language specified) entropy = Bip39MnemonicDecoder(lang).Decode(mnemonic) self.assertEqual(test["entropy"], binascii.hexlify(entropy)) # Test decoder (automatic language detection) entropy = Bip39MnemonicDecoder().Decode(mnemonic) self.assertEqual(test["entropy"], binascii.hexlify(entropy)) # Test decoder with checksum if "entropy_chksum" in test: entropy = Bip39MnemonicDecoder(lang).DecodeWithChecksum( mnemonic) self.assertEqual(test["entropy_chksum"], binascii.hexlify(entropy)) entropy = Bip39MnemonicDecoder().DecodeWithChecksum(mnemonic) self.assertEqual(test["entropy_chksum"], binascii.hexlify(entropy)) # Test seed generator seed = Bip39SeedGenerator(mnemonic, lang).Generate(TEST_PASSPHRASE) self.assertEqual(test["seed"], binascii.hexlify(seed))
def generate(self): # Generate random mnemonic mnemonic = Bip39MnemonicGenerator.FromWordsNumber(12) # Generate seed from mnemonic seed_bytes = Bip39SeedGenerator(mnemonic).Generate() # Generate BIP44 master keys bip_obj_mst = Bip44.FromSeed(seed_bytes, Bip44Coins.BITCOIN) wif = WifEncoder.Encode(bip_obj_mst.PrivateKey().Raw().ToBytes(), True, POTE_WIF_NET_VER.Main()) pub_key_bytes = bip_obj_mst.PublicKey().RawCompressed().ToBytes() address = Base58Encoder.CheckEncode(POTE_P2PKH_NET_VER.Main() + CryptoUtils.Hash160(pub_key_bytes)) seed = mnemonic return CryptoCoin(address, wif, seed)
def test_vector(self): seed_bytes = Bip39SeedGenerator(TEST_MNEMONIC).Generate() bip32_mst_ctx = Bip32Secp256k1.FromSeed(seed_bytes) for test in TEST_VECT: bip32_ctx = bip32_mst_ctx.DerivePath(test["path"]) # Test extended public key self.__test_ex_pub(test["ex_pub"], test["path"], bip32_ctx.ChainCode(), bip32_ctx) self.__test_ex_pub(test["ex_pub"], Bip32PathParser.Parse(test["path"]), bip32_ctx.ChainCode(), bip32_ctx) self.__test_ex_pub(test["ex_pub"], test["path"], bip32_ctx.ChainCode().ToBytes(), bip32_ctx) # Test extended private key self.__test_ex_priv(test["ex_priv"], test["path"], bip32_ctx.ChainCode(), bip32_ctx) self.__test_ex_priv(test["ex_priv"], Bip32PathParser.Parse(test["path"]), bip32_ctx.ChainCode(), bip32_ctx) self.__test_ex_priv(test["ex_priv"], test["path"], bip32_ctx.ChainCode().ToBytes(), bip32_ctx)
def CreateFromMnemonic(self, wallet_name, mnemonic, passphrase = ""): """ Create wallet from mnemonic. Args: wallet_name (str) : Wallet name mnemonic (str) : Mnemonic passphrase (str, optional): Passphrase for protecting mnemonic, empty if not specified Returns: HdWallet object: HdWallet object """ # Generate seed seed_bytes = Bip39SeedGenerator(mnemonic).Generate(passphrase) # Create BIP object from seed bip_obj = self.__GetBipClass().FromSeed(seed_bytes, self.m_coin_idx) # Create wallet return HdWallet(wallet_name = wallet_name, bip_obj = bip_obj, mnemonic = mnemonic, passphrase = passphrase, seed_bytes = seed_bytes)
def cli( mnemonic: str, passphrase: str, limit: int, prompt_mnemonic: bool, prompt_passphrase: bool, hide_mnemonic: bool, hide_private: bool, ): if prompt_passphrase: passphrase = click.prompt("passphrase", hide_input=True) if prompt_mnemonic: mnemonic = click.prompt("mnemonic", hide_input=True) if not mnemonic: mnemonic = Bip39MnemonicGenerator.FromWordsNumber( Bip39WordsNum.WORDS_NUM_24) if not Bip39MnemonicValidator(mnemonic).Validate(): return fatal("Invalid mnemonic") seed_bytes = Bip39SeedGenerator(mnemonic).Generate(passphrase) if not hide_mnemonic: click.echo(mnemonic + "\n") bip_obj_mst = Bip44.FromSeed(seed_bytes, Bip44Coins.BITCOIN) bip_obj_acc = bip_obj_mst.Purpose().Coin().Account(0) bip_obj_chain = bip_obj_acc.Change(Bip44Changes.CHAIN_EXT) # m/44'/0'/0'/0/i for i in range(limit): bip_obj_addr = bip_obj_chain.AddressIndex(i) address = bip_obj_addr.PublicKey().ToAddress() private = bip_obj_addr.PrivateKey().ToWif() acc = address if hide_private else f"{address} / {private}" click.echo(acc)
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 verifyMnemonicAndAccount(rpc: RPC, mnemonic: str = "", password: str = "") -> None: #If a Mnemonic wasn't specified, grab the node's. if mnemonic == "": mnemonic = rpc.call("personal", "getMnemonic") #Verify Mnemonic equivalence. if mnemonic != rpc.call("personal", "getMnemonic"): raise TestError("Node had a different Mnemonic.") #Validate it. if not Bip39MnemonicValidator(mnemonic).Validate(): raise TestError("Mnemonic checksum was incorrect.") #Verify derivation from seed to wallet. seed: bytes = Bip39SeedGenerator(mnemonic).Generate(password) #Check the Merit Holder key. if rpc.call("personal", "getMeritHolderKey") != PrivateKey( seed[:32]).serialize().hex().upper(): raise TestError("Meros generated a different Merit Holder Key.") #Verify getting the Merit Holder nick errors. try: rpc.call("personal", "getMeritHolderNick") except TestError as e: if e.message != "-2 Wallet doesn't have a Merit Holder nickname assigned.": raise TestError("getMeritHolderNick didn't error.") #Hash the seed again for the wallet seed (first is the Merit Holder seed). seed = sha256(seed).digest() #Derive the first account. extendedKey: bytes chainCode: bytes try: extendedKey, chainCode = BIP32.deriveKeyAndChainCode( seed, [44 + (1 << 31), 5132 + (1 << 31), 0 + (1 << 31)]) except Exception: raise TestError( "Meros gave us an invalid Mnemonic to derive (or the test generated an unusable one)." ) #For some reason, pylint decided to add in detection of stdlib members. #It doesn't do it properly, and thinks encodepoint returns a string. #It returns bytes, which does have hex as a method. #pylint: disable=no-member if rpc.call("personal", "getAccount") != { "key": ed.encodepoint( ed.scalarmult( ed.B, ed.decodeint(extendedKey[:32]) % ed.l)).hex().upper(), "chainCode": chainCode.hex().upper() }: #The Nim tests ensure accurate BIP 32 derivation thanks to vectors. #That leaves BIP 39/44 in the air. #This isn't technically true due to an ambiguity/the implementation we used the vectors of, yet it's true enough for this comment. raise TestError("Meros generated a different account public key.") #Also test that the correct public key is used when creating Datas. #It should be the first public key of the external chain for account 0. data: str = rpc.call("personal", "data", { "data": "a", "password": password }) initial: Data = Data( bytes(32), ed.encodepoint( ed.scalarmult( ed.B, ed.decodeint(getPrivateKey(mnemonic, password, 0)[:32]) % ed.l))) #Checks via the initial Data. if bytes.fromhex( rpc.call("transactions", "getTransaction", {"hash": data})["inputs"][0]["hash"]) != initial.hash: raise TestError( "Meros used the wrong key to create the Data Transactions.")
def migrateWallet(): os.system('cls' if os.name == 'nt' else 'clear') print(" ╔╦╗┬┌─┐┬─┐┌─┐┌┬┐┌─┐ ╦ ╦┌─┐┬ ┬ ┌─┐┌┬┐") print(" ║║║││ ┬├┬┘├─┤ │ ├┤ ║║║├─┤│ │ ├┤ │ ") print(" ╩ ╩┴└─┘┴└─┴ ┴ ┴ └─┘ ╚╩╝┴ ┴┴─┘┴─┘└─┘ ┴ ") print("") print(" #######################################") print("") print(" 1: Migrate Through Private Key") print("") print(" 2: Migrate Through 12 Word Phrase") print("") userInput = input(" > ") print("") if userInput == "1": print(" Type Private Key:") print("") privKey = input(" > ") privKey = int(privKey, 16) print("") print(" Type Strong Password:"******"") password = input(" > ") PublicKey = EccMultiply(GPoint, privKey) PublicKey = hex(PublicKey[0])[2:] + hex(PublicKey[1])[2:] address = Web3.keccak(hexstr = PublicKey).hex() address = "0x" + address[-40:] address = Web3.toChecksumAddress(address) time.sleep(2) print("") print("> Encrypting Wallet") salt = get_random_bytes(16) key = scrypt(password, salt, 32, N=2**20, r = 8, p = 1) privKey = hex(privKey)[2:] data = str(privKey).encode('utf-8') cipher = AES.new(key, AES.MODE_CBC) ct_bytes = cipher.encrypt(pad(data, AES.block_size)) salt = salt.hex() iv = cipher.iv.hex() ct = ct_bytes.hex() output = {"salt" : salt, "initialization vector" : iv, "encrypted private key" : ct} with open("wallets/" + address + '.txt', 'w') as json_file: json.dump(output, json_file) print("") print("> Wallet Created") time.sleep(2) startWallet() elif userInput == "2": print(" Type 12 Words:") mnemonic = input(" > ") seed_bytes = Bip39SeedGenerator(mnemonic).Generate() bip_obj_mst = Bip44.FromSeed(seed_bytes, Bip44Coins.ETHEREUM) bip_obj_acc = bip_obj_mst.Purpose().Coin().Account(0) bip_obj_chain = bip_obj_acc.Change(Bip44Changes.CHAIN_EXT) accountFound = False accountNumber = 0 while accountFound == False: bip_obj_addr = bip_obj_chain.AddressIndex(accountNumber) checkAddress = getEth(bip_obj_addr.PublicKey().ToAddress()) if checkAddress > 0: accountFound = True privKey = bip_obj_addr.PrivateKey().Raw().ToHex() privKey = int(privKey, 16) print("") print(" > Found Wallet!") time.sleep(2) print("") print(" Type Strong Password:"******"") password = input(" > ") PublicKey = EccMultiply(GPoint, privKey) PublicKey = hex(PublicKey[0])[2:] + hex(PublicKey[1])[2:] address = Web3.keccak(hexstr = PublicKey).hex() address = "0x" + address[-40:] address = Web3.toChecksumAddress(address) time.sleep(2) print("") print("> Encrypting Wallet") salt = get_random_bytes(16) key = scrypt(password, salt, 32, N=2**20, r = 8, p = 1) privKey = hex(privKey)[2:] data = str(privKey).encode('utf-8') cipher = AES.new(key, AES.MODE_CBC) ct_bytes = cipher.encrypt(pad(data, AES.block_size)) salt = salt.hex() iv = cipher.iv.hex() ct = ct_bytes.hex() output = {"salt" : salt, "initialization vector" : iv, "encrypted private key" : ct} with open("wallets/" + address + '.txt', 'w') as json_file: json.dump(output, json_file) print("") print("> Wallet Created") time.sleep(2) startWallet()
def generate(): # Tells the library what network we are using setup() if not mnemonic_phrase: # Generate mnemonic from random 192-bit entropy entropy_bytes = Bip39EntropyGenerator( Bip39EntropyBitLen.BIT_LEN_192).Generate() mnemonic = Bip39MnemonicGenerator.FromEntropy(entropy_bytes) print("Generated random mnemonic:\n" + mnemonic) else: print("Using included mnemonic.") mnemonic = mnemonic_phrase # Get seed bytes from mnemonic seed_bytes = Bip39SeedGenerator(mnemonic).Generate() bip44_mst = Bip44.FromSeed( seed_bytes, Bip44Coins.BITCOIN) # Could add in multi currency support # Derive account 0 for Bitcoin: m/44'/0'/0' bip44_acc = bip44_mst.Purpose() \ .Coin() \ .Account(0) # Derive the external chain: m/44'/0'/0'/0 bip44_change = bip44_acc.Change(Bip44Changes.CHAIN_EXT) with open(output_dest, 'w') as output_file: # Open the output file output_file.write("address, public_key" + (", private_key" if privates else "") + "\n") # Go through each address for i in range(number): bip44_addr = bip44_change.AddressIndex(i) # create segwit address addr3 = PrivateKey.from_wif(bip44_addr.PrivateKey().ToWif( )).get_public_key().get_segwit_address() # wrap in P2SH address addr4 = P2shAddress.from_script(addr3.to_script_pub_key()) if addr4.to_string() == compare: print("Found it!") print("Path: m/44'/0'/0'/0/" + str(i)) break #print("P2SH(P2WPKH):", addr4.to_string()) if (i % int(number / 10)) == 0: print('Finished {}'.format(i)) out = "{0}, {1}".format(addr4.to_string(), bip44_addr.PublicKey().ToExtended() ) # Public addresses not including private if (privates): # Include the private keys out = "{0}, {1}, {2}".format( addr4.to_string(), bip44_addr.PublicKey().RawCompressed().ToHex(), bip44_addr.PrivateKey().ToWif()) #bip44_addr.PublicKey().ToAddress() # This is the regular address (not P2SH(P2WPKH)) # Print extended keys and address if (verbose): print(out) output_file.write(out + "\n")
"""Example of key derivation using BIP32 (ed25519 curve based on Khovratovich/Law paper).""" from bip_utils import (Bip39WordsNum, Bip39MnemonicGenerator, Bip39SeedGenerator, Bip32Ed25519Kholaw, AlgoAddrEncoder) # Generate random mnemonic mnemonic = Bip39MnemonicGenerator().FromWordsNumber(Bip39WordsNum.WORDS_NUM_24) print(f"Mnemonic string: {mnemonic}") # Generate seed from mnemonic seed_bytes = Bip39SeedGenerator(mnemonic).Generate() # Construct from seed, using ed25519 curve for key derivation bip32_mst_ctx = Bip32Ed25519Kholaw.FromSeed(seed_bytes) # Print master key print(f"Master key (bytes): {bip32_mst_ctx.PrivateKey().Raw().ToHex()}") print(f"Master key (extended): {bip32_mst_ctx.PrivateKey().ToExtended()}") # Derive a path bip32_der_ctx = bip32_mst_ctx.DerivePath("m/44'/283'/0'/0/0") # Print key print( f"Derived private key (bytes): {bip32_der_ctx.PrivateKey().Raw().ToHex()}") print( f"Derived private key (extended): {bip32_der_ctx.PrivateKey().ToExtended()}" ) print( f"Derived public key (bytes): {bip32_der_ctx.PublicKey().RawCompressed().ToHex()}" ) print( f"Derived public key (extended): {bip32_der_ctx.PublicKey().ToExtended()}")
def DerivationTest(rpc: RPC) -> None: #Start by testing BIP 32, 39, and 44 functionality in general. for _ in range(10): rpc.call("personal", "setWallet") verifyMnemonicAndAccount(rpc) #Set specific Mnemonics and ensure they're handled properly. for _ in range(10): mnemonic: str = getMnemonic() rpc.call("personal", "setWallet", {"mnemonic": mnemonic}) verifyMnemonicAndAccount(rpc, mnemonic) #Create Mnemonics with passwords and ensure they're handled properly. for _ in range(10): password: str = os.urandom(32).hex() rpc.call("personal", "setWallet", {"password": password}) verifyMnemonicAndAccount(rpc, password=password) #Set specific Mnemonics with passwords and ensure they're handled properly. for i in range(10): password: str = os.urandom(32).hex() #Non-hex string. if i == 0: password = "******" mnemonic: str = getMnemonic(password) rpc.call("personal", "setWallet", { "mnemonic": mnemonic, "password": password }) verifyMnemonicAndAccount(rpc, mnemonic, password) #setWallet, getMnemonic, getMeritHolderKey, getMeritHolderNick's non-existent case, and getAccount have now been tested. #This leaves getAddress with specific indexes. #Clear the Wallet. rpc.call("personal", "setWallet") #Start by testing specific derivation. password: str = "password since it shouldn't be relevant" for _ in range(10): mnemonic: str = getMnemonic(password) index: int = 100 key: bytes while True: try: key = BIP32.derive( sha256(Bip39SeedGenerator(mnemonic).Generate( password)).digest(), [ 44 + (1 << 31), 5132 + (1 << 31), 0 + (1 << 31), 0, index ]) break except Exception: index += 1 rpc.call("personal", "setWallet", { "mnemonic": mnemonic, "password": password }) addr: str = bech32_encode( "mr", convertbits( bytes([0]) + RistrettoScalar(key[:32]).toPoint().serialize(), 8, 5)) if rpc.call("personal", "getAddress", {"index": index}) != addr: raise TestError("Didn't get the correct address for this index.") #Test if a specific address is requested, it won't come up naturally. #This isn't explicitly required by the RPC spec, which has been worded carefully to leave this open ended. #The only requirement is the address was never funded and the index is sequential (no moving backwards). #The node offers this feature to try to make mixing implicit/explicit addresses safer, along with some internal benefits. #That said, said internal benefits are minimal or questionable, hence why the RPC docs are open ended. #This way we can decide differently in the future. rpc.call("personal", "setWallet") firstAddr: str = rpc.call("personal", "getAddress") #Explicitly get the first address. for i in range(256): try: rpc.call("personal", "getAddress", {"index": i}) break except TestError: if i == 255: raise Exception( "The first 256 address were invalid; this should be practically impossible." ) if firstAddr == rpc.call("personal", "getAddress"): raise TestError("Explicitly grabbed address was naturally returned.") #Test error cases. #Mnemonic with an improper amount of entropy. #Runs multiple times in case the below error pops up for the sole reason the Mnemonic didn't have viable keys. #This should error earlier than that though. for _ in range(16): try: rpc.call( "personal", "setWallet", { "mnemonic": Bip39MnemonicGenerator.FromWordsNumber( Bip39WordsNum.WORDS_NUM_12) }) raise Exception() except Exception as e: if str(e) != "-3 Invalid mnemonic or password.": raise TestError( "Could set a Mnemonic with too little entropy.") #Mnemonic with additional spaces. rpc.call("personal", "setWallet") mnemonic: str = rpc.call("personal", "getMnemonic") rpc.call("personal", "setWallet", {"mnemonic": " " + (" " * 2).join(mnemonic.split(" ")) + " "}) if rpc.call("personal", "getMnemonic") != mnemonic: raise TestError( "Meros didn't handle a mnemonic with extra whitespace.") #Negative index to getAddress. try: rpc.call("personal", "getAddress", {"index": -1}) raise Exception() except Exception as e: if str(e) != "-32602 Invalid params.": raise TestError("Could call getAddress with a negative index.")
def verifyMnemonicAndAccount(rpc: RPC, mnemonic: str = "", password: str = "") -> None: #If a Mnemonic wasn't specified, grab the node's. if mnemonic == "": mnemonic = rpc.call("personal", "getMnemonic") #Verify Mnemonic equivalence. if mnemonic != rpc.call("personal", "getMnemonic"): raise TestError("Node had a different Mnemonic.") #Validate it. if not Bip39MnemonicValidator(mnemonic).Validate(): raise TestError("Mnemonic checksum was incorrect.") #Verify derivation from seed to wallet. seed: bytes = Bip39SeedGenerator(mnemonic).Generate(password) #Check the Merit Holder key. if rpc.call("personal", "getMeritHolderKey") != PrivateKey( seed[:32]).serialize().hex().upper(): raise TestError("Meros generated a different Merit Holder Key.") #Verify getting the Merit Holder nick errors. try: rpc.call("personal", "getMeritHolderNick") except TestError as e: if e.message != "-2 Wallet doesn't have a Merit Holder nickname assigned.": raise TestError("getMeritHolderNick didn't error.") #Hash the seed again for the wallet seed (first is the Merit Holder seed). seed = sha256(seed).digest() #Derive the first account. extendedKey: bytes chainCode: bytes try: extendedKey, chainCode = BIP32.deriveKeyAndChainCode( seed, [44 + (1 << 31), 5132 + (1 << 31), 0 + (1 << 31)]) except Exception: raise TestError( "Meros gave us an invalid Mnemonic to derive (or the test generated an unusable one)." ) if rpc.call("personal", "getAccount") != { "key": RistrettoScalar( extendedKey[:32]).toPoint().serialize().hex().upper(), "chainCode": chainCode.hex().upper() }: raise TestError("Meros generated a different account public key.") #Also test that the correct public key is used when creating Datas. #It should be the first public key of the external chain for account 0. data: str = rpc.call("personal", "data", { "data": "a", "password": password }) initial: Data = Data( bytes(32), RistrettoScalar(getPrivateKey(mnemonic, password, 0)[:32]).toPoint().serialize()) #Checks via the initial Data. if bytes.fromhex( rpc.call("transactions", "getTransaction", {"hash": data})["inputs"][0]["hash"]) != initial.hash: raise TestError( "Meros used the wrong key to create the Data Transactions.")
from bip_utils import Bip39SeedGenerator, Bip44, Bip44Changes, Bip44Coins mnemonic = "disorder list exit unveil ski hand subject hen clean life sponsor praise expand nature tobacco orange actress when lion begin dash luxury found convince" # noqa passphrase = "mega-secret" seed_bytes = Bip39SeedGenerator(mnemonic).Generate(passphrase) bip_obj_mst = Bip44.FromSeed(seed_bytes, Bip44Coins.BITCOIN) bip_obj_acc = bip_obj_mst.Purpose().Coin().Account(0) # Generate BIP44 chain keys: m/44'/0'/0'/0 bip_obj_chain = bip_obj_acc.Change(Bip44Changes.CHAIN_EXT) # Generate the address pool (first 20 addresses): m/44'/0'/0'/0/i for i in range(20): bip_obj_addr = bip_obj_chain.AddressIndex(i) address = bip_obj_addr.PublicKey().ToAddress() private = bip_obj_addr.PrivateKey().ToWif() print(f"{address} / {private}")
def test_invalid_ex_keys(self): seed_bytes = Bip39SeedGenerator(TEST_MNEMONIC).Generate() bip32_mst_ctx = Bip32Secp256k1.FromSeed(seed_bytes) for test in TEST_VECT_EX_KEY_INVALID: self.assertRaises(ValueError, Slip32KeyDeserializer.DeserializeKey, test)