def main(): # Mnemonics can be generated using `chia keys generate_and_print`, or `chia keys generate`. The latter stored # the key in the OS keychain (unencrypted file if linux). mnemonic: str = "neither medal holiday echo link dog sleep idea turkey logic security sword save taxi chapter artwork toddler wealth local mind manual never unlock narrow" seed: bytes = mnemonic_to_seed(mnemonic, passphrase="") master_private_key: PrivateKey = AugSchemeMPL.key_gen(seed) intermediate_sk: PrivateKey = AugSchemeMPL.derive_child_sk( master_private_key, 12381) intermediate_sk = AugSchemeMPL.derive_child_sk(intermediate_sk, 8444) intermediate_sk = AugSchemeMPL.derive_child_sk(intermediate_sk, 2) print( f"Parent public key is: {intermediate_sk.get_g1()}. Please use this within `create_unsigned_tx.py`" ) # If you want to use hardened keys which are more secure against quantum computers, you need to export # The public keys # create_hardened_child_public_keys(mnemonic, 1000) try: with open("tx_3.json", "r") as f: spend_bundle_json = f.read() except Exception: print( "Error: create your transaction (spend bundle) json and put it into the json file." ) return spend_bundle_json_dict: Dict = json.loads(spend_bundle_json) spend_bundle: SpendBundle = SpendBundle.from_json_dict( spend_bundle_json_dict) sign_tx(intermediate_sk, spend_bundle, use_hardened_keys=False)
def private_key_from_mnemonic_seed_file(filename: Path) -> PrivateKey: """ Create a private key from a mnemonic seed file. """ mnemonic = filename.read_text().rstrip() seed = mnemonic_to_seed(mnemonic, "") return AugSchemeMPL.key_gen(seed)
def mnemonic(): # mnemonic = "imitate obvious arch square fan bike thumb hedgehog crystal innocent shoe glare share father romance local size gloom hurt maid denial weapon wave bulb" mnemonic = "turn acquire ring mind empower ahead section often habit sick sail mountain pen repair catch drum insect file dry trend venue junk novel laptop" seed = mnemonic_to_seed(mnemonic, "") master_private_key = AugSchemeMPL.key_gen(seed) child_private_key = master_sk_to_wallet_sk(master_private_key, 0) child_public_key = child_private_key.get_g1() puzzle = puzzle_for_pk(child_public_key) puzzle_hash = puzzle.get_tree_hash() address = encode_puzzle_hash(puzzle_hash, "txch")
def test_bip39_test_vectors_trezor(self): with open("tests/util/bip39_test_vectors.json") as f: all_vectors = json.loads(f.read()) for vector_list in all_vectors["english"]: entropy_bytes = bytes.fromhex(vector_list[0]) mnemonic = vector_list[1] seed = bytes.fromhex(vector_list[2]) assert bytes_from_mnemonic(mnemonic) == entropy_bytes assert bytes_to_mnemonic(entropy_bytes) == mnemonic assert mnemonic_to_seed(mnemonic, "TREZOR") == seed
def test_utf8_nfkd(self): # Test code from trezor: # Copyright (c) 2013 Pavol Rusnak # Copyright (c) 2017 mruddy # https://github.com/trezor/python-mnemonic/blob/master/test_mnemonic.py # The same sentence in various UTF-8 forms words_nfkd = "Pr\u030ci\u0301s\u030cerne\u030c z\u030clut\u030couc\u030cky\u0301 ku\u030an\u030c u\u0301pe\u030cl d\u030ca\u0301belske\u0301 o\u0301dy za\u0301ker\u030cny\u0301 uc\u030cen\u030c be\u030cz\u030ci\u0301 pode\u0301l zo\u0301ny u\u0301lu\u030a" # noqa: E501 words_nfc = "P\u0159\xed\u0161ern\u011b \u017elu\u0165ou\u010dk\xfd k\u016f\u0148 \xfap\u011bl \u010f\xe1belsk\xe9 \xf3dy z\xe1ke\u0159n\xfd u\u010de\u0148 b\u011b\u017e\xed pod\xe9l z\xf3ny \xfal\u016f" # noqa: E501 words_nfkc = "P\u0159\xed\u0161ern\u011b \u017elu\u0165ou\u010dk\xfd k\u016f\u0148 \xfap\u011bl \u010f\xe1belsk\xe9 \xf3dy z\xe1ke\u0159n\xfd u\u010de\u0148 b\u011b\u017e\xed pod\xe9l z\xf3ny \xfal\u016f" # noqa: E501 words_nfd = "Pr\u030ci\u0301s\u030cerne\u030c z\u030clut\u030couc\u030cky\u0301 ku\u030an\u030c u\u0301pe\u030cl d\u030ca\u0301belske\u0301 o\u0301dy za\u0301ker\u030cny\u0301 uc\u030cen\u030c be\u030cz\u030ci\u0301 pode\u0301l zo\u0301ny u\u0301lu\u030a" # noqa: E501 passphrase_nfkd = "Neuve\u030cr\u030citelne\u030c bezpec\u030cne\u0301 hesli\u0301c\u030cko" passphrase_nfc = "Neuv\u011b\u0159iteln\u011b bezpe\u010dn\xe9 hesl\xed\u010dko" passphrase_nfkc = "Neuv\u011b\u0159iteln\u011b bezpe\u010dn\xe9 hesl\xed\u010dko" passphrase_nfd = "Neuve\u030cr\u030citelne\u030c bezpec\u030cne\u0301 hesli\u0301c\u030cko" seed_nfkd = mnemonic_to_seed(words_nfkd, passphrase_nfkd) seed_nfc = mnemonic_to_seed(words_nfc, passphrase_nfc) seed_nfkc = mnemonic_to_seed(words_nfkc, passphrase_nfkc) seed_nfd = mnemonic_to_seed(words_nfd, passphrase_nfd) assert seed_nfkd == seed_nfc assert seed_nfkd == seed_nfkc assert seed_nfkd == seed_nfd
def test_bip39_eip2333_test_vector(self): kc: Keychain = Keychain(testing=True) kc.delete_all_keys() mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" passphrase = "TREZOR" print("entropy to seed:", mnemonic_to_seed(mnemonic, passphrase).hex()) master_sk = kc.add_private_key(mnemonic, passphrase) tv_master_int = 5399117110774477986698372024995405256382522670366369834617409486544348441851 tv_child_int = 11812940737387919040225825939013910852517748782307378293770044673328955938106 assert master_sk == PrivateKey.from_bytes( tv_master_int.to_bytes(32, "big")) child_sk = AugSchemeMPL.derive_child_sk(master_sk, 0) assert child_sk == PrivateKey.from_bytes( tv_child_int.to_bytes(32, "big"))
async def get_key_for_fingerprint(self, fingerprint: Optional[int]) -> Optional[PrivateKey]: """ Locates and returns a private key matching the provided fingerprint """ key: Optional[PrivateKey] = None if self.use_local_keychain(): private_keys = self.keychain.get_all_private_keys() if len(private_keys) == 0: raise KeyringIsEmpty() else: if fingerprint is not None: for sk, _ in private_keys: if sk.get_g1().get_fingerprint() == fingerprint: key = sk break else: key = private_keys[0][0] else: response, success = await self.get_response_for_request( "get_key_for_fingerprint", {"fingerprint": fingerprint} ) if success: pk = response["data"].get("pk", None) ent = response["data"].get("entropy", None) if pk is None or ent is None: err = f"Missing pk and/or ent in {response.get('command')} response" self.log.error(f"{err}") raise MalformedKeychainResponse(f"{err}") else: mnemonic = bytes_to_mnemonic(bytes.fromhex(ent)) seed = mnemonic_to_seed(mnemonic, passphrase="") private_key = AugSchemeMPL.key_gen(seed) if bytes(private_key.get_g1()).hex() == pk: key = private_key else: err = "G1Elements don't match" self.log.error(f"{err}") else: self.handle_error(response) return key
def create_hardened_child_public_keys(mnemonic: str, number: int = 5000): """ Creates child public keys, derived from the master private key, using hardened derivation. This method is more secure than public key derivation since it's following the EIP-2333 spec for quantum security. """ seed: bytes = mnemonic_to_seed(mnemonic, passphrase="") master_private_key: PrivateKey = AugSchemeMPL.key_gen(seed) intermediate_sk: PrivateKey = AugSchemeMPL.derive_child_sk( master_private_key, 12381) intermediate_sk = AugSchemeMPL.derive_child_sk(intermediate_sk, 8444) intermediate_sk = AugSchemeMPL.derive_child_sk(intermediate_sk, 2) all_pks: List[G1Element] = [] for i in range(number): child_sk: PrivateKey = AugSchemeMPL.derive_child_sk(intermediate_sk, i) child_pk: G1Element = child_sk.get_g1() all_pks.append(child_pk) with open("child_public_keys.txt", "w") as f: lines = [f"{bytes(pk).hex()}\n" for pk in all_pks] f.writelines(lines)
async def get_first_private_key(self) -> Optional[PrivateKey]: """ Forwards to Keychain.get_first_private_key() """ key: Optional[PrivateKey] = None if self.use_local_keychain(): sk_ent = self.keychain.get_first_private_key() if sk_ent: key = sk_ent[0] else: response, success = await self.get_response_for_request("get_first_private_key", {}) if success: private_key = response["data"].get("private_key", None) if private_key is None: err = f"Missing private_key in {response.get('command')} response" self.log.error(f"{err}") raise MalformedKeychainResponse(f"{err}") else: pk = private_key.get("pk", None) ent_str = private_key.get("entropy", None) if pk is None or ent_str is None: err = f"Missing pk and/or ent in {response.get('command')} response" self.log.error(f"{err}") raise MalformedKeychainResponse(f"{err}") ent = bytes.fromhex(ent_str) mnemonic = bytes_to_mnemonic(ent) seed = mnemonic_to_seed(mnemonic, passphrase="") sk = AugSchemeMPL.key_gen(seed) if bytes(sk.get_g1()).hex() == pk: key = sk else: err = "G1Elements don't match" self.log.error(f"{err}") else: self.handle_error(response) return key
async def get_all_private_keys(self) -> List[Tuple[PrivateKey, bytes]]: """ Forwards to Keychain.get_all_private_keys() """ keys: List[Tuple[PrivateKey, bytes]] = [] if self.use_local_keychain(): keys = self.keychain.get_all_private_keys() else: response, success = await self.get_response_for_request("get_all_private_keys", {}) if success: private_keys = response["data"].get("private_keys", None) if private_keys is None: err = f"Missing private_keys in {response.get('command')} response" self.log.error(f"{err}") raise MalformedKeychainResponse(f"{err}") else: for key_dict in private_keys: pk = key_dict.get("pk", None) ent_str = key_dict.get("entropy", None) if pk is None or ent_str is None: err = f"Missing pk and/or ent in {response.get('command')} response" self.log.error(f"{err}") continue # We'll skip the incomplete key entry ent = bytes.fromhex(ent_str) mnemonic = bytes_to_mnemonic(ent) seed = mnemonic_to_seed(mnemonic, passphrase="") key = AugSchemeMPL.key_gen(seed) if bytes(key.get_g1()).hex() == pk: keys.append((key, ent)) else: err = "G1Elements don't match" self.log.error(f"{err}") else: self.handle_error(response) return keys
async def add_private_key(self, mnemonic: str, passphrase: str) -> PrivateKey: """ Forwards to Keychain.add_private_key() """ key: PrivateKey if self.use_local_keychain(): key = self.keychain.add_private_key(mnemonic, passphrase) else: response, success = await self.get_response_for_request( "add_private_key", {"mnemonic": mnemonic, "passphrase": passphrase} ) if success: seed = mnemonic_to_seed(mnemonic, passphrase) key = AugSchemeMPL.key_gen(seed) else: error = response["data"].get("error", None) if error == KEYCHAIN_ERR_KEYERROR: error_details = response["data"].get("error_details", {}) word = error_details.get("word", "") raise KeyError(word) else: self.handle_error(response) return key
def test_basic_add_delete(self): kc: Keychain = Keychain(user="******", service="chia-testing-1.8.0") kc.delete_all_keys() assert kc._get_free_private_key_index() == 0 assert len(kc.get_all_private_keys()) == 0 assert kc.get_first_private_key() is None assert kc.get_first_public_key() is None mnemonic = generate_mnemonic() entropy = bytes_from_mnemonic(mnemonic) assert bytes_to_mnemonic(entropy) == mnemonic mnemonic_2 = generate_mnemonic() # misspelled words in the mnemonic bad_mnemonic = mnemonic.split(" ") bad_mnemonic[6] = "ZZZZZZ" self.assertRaisesRegex( ValueError, "'ZZZZZZ' is not in the mnemonic dictionary; may be misspelled", bytes_from_mnemonic, " ".join(bad_mnemonic), ) kc.add_private_key(mnemonic, "") assert kc._get_free_private_key_index() == 1 assert len(kc.get_all_private_keys()) == 1 kc.add_private_key(mnemonic_2, "") kc.add_private_key(mnemonic_2, "") # checks to not add duplicates assert kc._get_free_private_key_index() == 2 assert len(kc.get_all_private_keys()) == 2 assert kc._get_free_private_key_index() == 2 assert len(kc.get_all_private_keys()) == 2 assert len(kc.get_all_public_keys()) == 2 assert kc.get_all_private_keys()[0] == kc.get_first_private_key() assert kc.get_all_public_keys()[0] == kc.get_first_public_key() assert len(kc.get_all_private_keys()) == 2 seed_2 = mnemonic_to_seed(mnemonic, "") seed_key_2 = AugSchemeMPL.key_gen(seed_2) kc.delete_key_by_fingerprint(seed_key_2.get_g1().get_fingerprint()) assert kc._get_free_private_key_index() == 0 assert len(kc.get_all_private_keys()) == 1 kc.delete_all_keys() assert kc._get_free_private_key_index() == 0 assert len(kc.get_all_private_keys()) == 0 kc.add_private_key(bytes_to_mnemonic(token_bytes(32)), "my passphrase") kc.add_private_key(bytes_to_mnemonic(token_bytes(32)), "") kc.add_private_key(bytes_to_mnemonic(token_bytes(32)), "third passphrase") assert len(kc.get_all_public_keys()) == 3 assert len(kc.get_all_private_keys()) == 1 assert len(kc.get_all_private_keys(["my passphrase", ""])) == 2 assert len( kc.get_all_private_keys( ["my passphrase", "", "third passphrase", "another"])) == 3 assert len(kc.get_all_private_keys(["my passhrase wrong"])) == 0 assert kc.get_first_private_key() is not None assert kc.get_first_private_key(["bad passphrase"]) is None assert kc.get_first_public_key() is not None kc.delete_all_keys() kc.add_private_key(bytes_to_mnemonic(token_bytes(32)), "my passphrase") assert kc.get_first_public_key() is not None
def test_basic_add_delete(self): kc: Keychain = Keychain(testing=True) kc.delete_all_keys() assert kc._get_free_private_key_index() == 0 assert len(kc.get_all_private_keys()) == 0 assert kc.get_first_private_key() is None assert kc.get_first_public_key() is None mnemonic = generate_mnemonic() entropy = bytes_from_mnemonic(mnemonic) assert bytes_to_mnemonic(entropy) == mnemonic mnemonic_2 = generate_mnemonic() kc.add_private_key(mnemonic, "") assert kc._get_free_private_key_index() == 1 assert len(kc.get_all_private_keys()) == 1 kc.add_private_key(mnemonic_2, "") kc.add_private_key(mnemonic_2, "") # checks to not add duplicates assert kc._get_free_private_key_index() == 2 assert len(kc.get_all_private_keys()) == 2 assert kc._get_free_private_key_index() == 2 assert len(kc.get_all_private_keys()) == 2 assert len(kc.get_all_public_keys()) == 2 assert kc.get_all_private_keys()[0] == kc.get_first_private_key() assert kc.get_all_public_keys()[0] == kc.get_first_public_key() assert len(kc.get_all_private_keys()) == 2 seed_2 = mnemonic_to_seed(mnemonic, "") seed_key_2 = AugSchemeMPL.key_gen(seed_2) kc.delete_key_by_fingerprint(seed_key_2.get_g1().get_fingerprint()) assert kc._get_free_private_key_index() == 0 assert len(kc.get_all_private_keys()) == 1 kc.delete_all_keys() assert kc._get_free_private_key_index() == 0 assert len(kc.get_all_private_keys()) == 0 kc.add_private_key(bytes_to_mnemonic(token_bytes(32)), "my passphrase") kc.add_private_key(bytes_to_mnemonic(token_bytes(32)), "") kc.add_private_key(bytes_to_mnemonic(token_bytes(32)), "third passphrase") assert len(kc.get_all_public_keys()) == 3 assert len(kc.get_all_private_keys()) == 1 assert len(kc.get_all_private_keys(["my passphrase", ""])) == 2 assert len( kc.get_all_private_keys( ["my passphrase", "", "third passphrase", "another"])) == 3 assert len(kc.get_all_private_keys(["my passhrase wrong"])) == 0 assert kc.get_first_private_key() is not None assert kc.get_first_private_key(["bad passphrase"]) is None assert kc.get_first_public_key() is not None kc.delete_all_keys() kc.add_private_key(bytes_to_mnemonic(token_bytes(32)), "my passphrase") assert kc.get_first_public_key() is not None