def sign(message: str, fingerprint: int, hd_path: str): k = Keychain() private_keys = k.get_all_private_keys() path: List[uint32] = [uint32(int(i)) for i in hd_path.split("/") if i != "m"] for sk, _ in private_keys: if sk.get_g1().get_fingerprint() == fingerprint: for c in path: sk = AugSchemeMPL.derive_child_sk(sk, c) print("Public key:", sk.get_g1()) print("Signature:", AugSchemeMPL.sign(sk, bytes(message, "utf-8"))) return print(f"Fingerprint {fingerprint} not found in keychain")
class KeychainProxy(DaemonProxy): """ KeychainProxy can act on behalf of a local or remote keychain. In the case of wrapping a local keychain, the proxy object simply forwards-along the calls to the underlying local keychain. In the remote case, calls are made to the daemon over the RPC interface, allowing the daemon to act as the keychain authority. """ def __init__( self, log: logging.Logger, uri: str = None, ssl_context: Optional[ssl.SSLContext] = None, local_keychain: Optional[Keychain] = None, user: str = None, testing: bool = False, ): self.log = log if local_keychain: self.keychain = local_keychain elif not supports_keyring_passphrase(): self.keychain = Keychain() # Proxy locally, don't use RPC else: self.keychain = None # type: ignore self.keychain_user = user self.keychain_testing = testing super().__init__(uri or "", ssl_context) def use_local_keychain(self) -> bool: """ Indicates whether the proxy forwards calls to a local keychain """ return self.keychain is not None def format_request(self, command: str, data: Dict[str, Any]) -> WsRpcMessage: """ Overrides DaemonProxy.format_request() to add keychain-specific RPC params """ if data is None: data = {} if self.keychain_user or self.keychain_testing: data["kc_user"] = self.keychain_user data["kc_testing"] = self.keychain_testing return super().format_request(command, data) async def get_response_for_request(self, request_name: str, data: Dict[str, Any]) -> Tuple[WsRpcMessage, bool]: request = self.format_request(request_name, data) response = await self._get(request) success = response["data"].get("success", False) return response, success def handle_error(self, response: WsRpcMessage): """ Common error handling for RPC responses """ error = response["data"].get("error", None) if error: error_details = response["data"].get("error_details", {}) if error == KEYCHAIN_ERR_LOCKED: raise KeyringIsLocked() elif error == KEYCHAIN_ERR_NO_KEYS: raise KeyringIsEmpty() elif error == KEYCHAIN_ERR_MALFORMED_REQUEST: message = error_details.get("message", "") raise MalformedKeychainRequest(message) else: err = f"{response['data'].get('command')} failed with error: {error}" self.log.error(f"{err}") raise Exception(f"{err}") 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 async def check_keys(self, root_path): """ Forwards to init_funcs.check_keys() """ if self.use_local_keychain(): check_keys(root_path, self.keychain) else: response, success = await self.get_response_for_request("check_keys", {"root_path": str(root_path)}) if not success: self.handle_error(response) async def delete_all_keys(self): """ Forwards to Keychain.delete_all_keys() """ if self.use_local_keychain(): self.keychain.delete_all_keys() else: response, success = await self.get_response_for_request("delete_all_keys", {}) if not success: self.handle_error(response) async def delete_key_by_fingerprint(self, fingerprint: int): """ Forwards to Keychain.delete_key_by_fingerprint() """ if self.use_local_keychain(): self.keychain.delete_key_by_fingerprint(fingerprint) else: response, success = await self.get_response_for_request( "delete_key_by_fingerprint", {"fingerprint": fingerprint} ) if not success: self.handle_error(response) 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 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_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
if selected_key is not None: break print("\n___________ HD PATH ____________") while True: hd_path = input( "Enter the HD path in the form 'm/12381/8444/n/n', or enter Q to quit: " ).lower() if hd_path == "q": quit() verify = input(f"Is this correct path: {hd_path}? (y/n) ").lower() if verify == "y": break k = Keychain() private_keys = k.get_all_private_keys() path: List[uint32] = [uint32(int(i)) for i in hd_path.split("/") if i != "m"] # Derive HD key using path form input for c in path: selected_key = AugSchemeMPL.derive_child_sk(selected_key, c) print("Public key:", selected_key.get_g1()) # get file path file_path = None while True: file_path = input( "Enter the path where you want to save signed alert file, or q to quit: " ) if file_path == "q" or file_path == "Q": quit()
def migrate_legacy_keyring(self): """ Handle importing keys from the legacy keyring into the new keyring. Prior to beginning, we'll ensure that we at least suggest setting a master passphrase and backing up mnemonic seeds. After importing keys from the legacy keyring, we'll perform a before/after comparison of the keyring contents, and on success we'll prompt to cleanup the legacy keyring. """ from chia.util.keychain import Keychain, MAX_KEYS # Make sure the user is ready to begin migration. We want to ensure that response = self.confirm_migration() if not response: print("Skipping migration. Unable to proceed") exit(0) print("Migrating contents from legacy keyring") keychain = Keychain() # Obtain contents from the legacy keyring. When using the Keychain interface # to read, the legacy keyring will be preferred over the new keyring. original_private_keys = keychain.get_all_private_keys() service = keychain._get_service() user_passphrase_pairs = [] index = 0 user = keychain._get_private_key_user(index) while index <= MAX_KEYS: # Build up a list of user/passphrase tuples from the legacy keyring contents if user is not None: passphrase = self.get_passphrase(service, user) if passphrase is not None: user_passphrase_pairs.append((user, passphrase)) index += 1 user = keychain._get_private_key_user(index) # Write the keys directly to the new keyring (self.keyring) for (user, passphrase) in user_passphrase_pairs: self.keyring.set_password(service, user, passphrase) # Stop using the legacy keyring. This will direct subsequent reads to the new keyring. old_keyring = self.legacy_keyring self.legacy_keyring = None print("Verifying migration results...", end="") # Compare the original keyring contents with the new try: post_migration_private_keys = keychain.get_all_private_keys() if post_migration_private_keys == original_private_keys: print(" Verified") except Exception as e: print(f"\nMigration failed: {e}") print("Leaving legacy keyring intact") exit(1) print(f"Keyring migration completed successfully ({str(self.keyring.keyring_path)})\n") # Ask if we should clean up the legacy keyring self.confirm_legacy_keyring_cleanup(old_keyring, service, [user for (user, _) in user_passphrase_pairs])