diff --git a/pycardano/crypto/bip32.py b/pycardano/crypto/bip32.py index 3095666b..e6454228 100644 --- a/pycardano/crypto/bip32.py +++ b/pycardano/crypto/bip32.py @@ -82,12 +82,12 @@ class HDWallet: def __init__( self, - root_xprivate_key: bytes, - root_public_key: bytes, - root_chain_code: bytes, - xprivate_key: bytes, - public_key: bytes, - chain_code: bytes, + root_xprivate_key: Optional[bytes] = None, + root_public_key: Optional[bytes] = None, + root_chain_code: Optional[bytes] = None, + xprivate_key: Optional[bytes] = None, + public_key: Optional[bytes] = None, + chain_code: Optional[bytes] = None, path: str = "m", seed: Optional[bytes] = None, mnemonic: Optional[str] = None, @@ -322,13 +322,13 @@ def derive( if not isinstance(index, int): raise ValueError("Bad index, Please import only integer number!") - if not self._root_xprivate_key and not self._root_public_key: - raise ValueError("Missing root keys. Can't do derivation.") - if hardened: index += 2**31 if private: + if self._xprivate_key is None: + raise ValueError("Missing private key. Can't do private derivation.") + private_node = ( self._xprivate_key[:32], self._xprivate_key[32:], @@ -336,14 +336,22 @@ def derive( self._chain_code, self._path, ) - return self._derive_private_child_key_by_index(private_node, index) + + if any(x is None for x in private_node): + raise ValueError(f"None values in private node: {private_node}") + + return self._derive_private_child_key_by_index(private_node, index) # type: ignore public_node = ( self._public_key, self._chain_code, self._path, ) - return self._derive_public_child_key_by_index(public_node, index) + + if any(x is None for x in public_node): + raise ValueError(f"None values in public node: {public_node}") + + return self._derive_public_child_key_by_index(public_node, index) # type: ignore def _derive_private_child_key_by_index( self, private_pnode: Tuple[bytes, bytes, bytes, bytes, str], index: int diff --git a/test/pycardano/crypto/test_bip32.py b/test/pycardano/crypto/test_bip32.py index 0559bedf..48eece73 100644 --- a/test/pycardano/crypto/test_bip32.py +++ b/test/pycardano/crypto/test_bip32.py @@ -387,3 +387,51 @@ def test_is_entropy_wrong_input(): def test_is_entropy_value_error(): is_entropy = HDWallet.is_entropy("*(#_") assert is_entropy is False + + +def test_non_harden_public_derivation(): + public_key = bytes.fromhex( + "601c86a2310f438079fff07454f4df4ce416bd181d972cd4c4615f15a733ba15" + ) + chain_code = bytes.fromhex( + "8a34b66a32c9d2d61d1d7a0e8fcf167efae3fb556af74808cd1ba8a22251c031" + ) + hdwallet = HDWallet(chain_code=chain_code, public_key=public_key) + hdwallet = hdwallet.derive(0, private=False, hardened=False) + child1 = hdwallet.derive(0, private=False, hardened=False) + child2 = hdwallet.derive(1, private=False, hardened=False) + child3 = hdwallet.derive(2, private=False, hardened=False) + + assert ( + child1.public_key.hex() + == "b3f354cbdc2837f823c5e0585995d3bd2d6edf1dc9fc8a1a90d01840c519408d" + ) + assert ( + child2.public_key.hex() + == "363aa29a3c93085859310ccddb182abcca18db6f6897a4f936ae82ba0a15af90" + ) + assert ( + child3.public_key.hex() + == "db58a4102f555ccb64a0db45259799cbd42fac14f7f6fd1059557fef3961e2c0" + ) + + +def test_non_harden_public_derivation_without_pubkey(): + chain_code = bytes.fromhex( + "8a34b66a32c9d2d61d1d7a0e8fcf167efae3fb556af74808cd1ba8a22251c031" + ) + hdwallet = HDWallet(chain_code=chain_code) + with pytest.raises(ValueError): + hdwallet.derive(0, private=False, hardened=False) + + +def test_non_harden_private_derivation_without_privkey(): + public_key = bytes.fromhex( + "601c86a2310f438079fff07454f4df4ce416bd181d972cd4c4615f15a733ba15" + ) + chain_code = bytes.fromhex( + "8a34b66a32c9d2d61d1d7a0e8fcf167efae3fb556af74808cd1ba8a22251c031" + ) + hdwallet = HDWallet(chain_code=chain_code, public_key=public_key) + with pytest.raises(ValueError): + hdwallet.derive(0, private=True, hardened=False)