diff --git a/core/src/apps/common/seed.py b/core/src/apps/common/seed.py index 384333d24..3f87aeaba 100644 --- a/core/src/apps/common/seed.py +++ b/core/src/apps/common/seed.py @@ -1,5 +1,6 @@ from trezor import wire from trezor.crypto import bip32, hashlib, hmac +from trezor.crypto.curve import secp256k1 from apps.common import HARDENED, cache, mnemonic, storage from apps.common.request_passphrase import protect_by_passphrase @@ -48,7 +49,7 @@ class Keychain: def __del__(self) -> None: for root in self.roots: - if root is not None: + if root is not None and hasattr(root, "__del__"): root.__del__() del self.roots del self.seed @@ -93,6 +94,17 @@ class Keychain: node.derive_path(suffix) return node + def derive_slip77_blinding_private_key(self, script: bytes) -> bytes: + """Following the derivation by Elements/Liquid.""" + master_node = self.derive(node_path=[b"SLIP-0077"], curve_name="slip21") + return hmac.new( + key=master_node.key(), msg=script, digestmod=hashlib.sha256 + ).digest() + + def derive_slip77_blinding_public_key(self, script: bytes) -> bytes: + private_key = self.derive_slip77_blinding_private_key(script) + return secp256k1.publickey(private_key) + async def get_keychain(ctx: wire.Context, namespaces: list) -> Keychain: if not storage.is_initialized(): diff --git a/core/tests/test_apps.common.seed.py b/core/tests/test_apps.common.seed.py index 6e7b6aef4..994b1f560 100644 --- a/core/tests/test_apps.common.seed.py +++ b/core/tests/test_apps.common.seed.py @@ -1,9 +1,10 @@ from common import * -from apps.common import HARDENED +from apps.common import HARDENED, coins from apps.common.seed import Keychain, Slip21Node, _path_hardened +from apps.wallet.sign_tx import scripts, addresses from trezor import wire from trezor.crypto import bip39 - +from trezor.crypto.curve import secp256k1 class TestKeychain(unittest.TestCase): @@ -115,6 +116,21 @@ class TestKeychain(unittest.TestCase): with self.assertRaises(wire.DataError): keychain.derive([b"SLIP-9999", b"Authentication key"], "slip21").key() + def test_slip77(self): + seed = bip39.seed("alcohol woman abuse must during monitor noble actual mixed trade anger aisle", "") + keychain = Keychain(seed, [["slip21", b"SLIP-0077"], ["secp256k1"]]) + + node = keychain.derive([44 | HARDENED, 1 | HARDENED, 0 | HARDENED, 0, 0]) + coin = coins.by_name('Elements') + pubkey_hash = addresses.ecdsa_hash_pubkey(node.public_key(), coin) + script = scripts.output_script_p2pkh(pubkey_hash) + + private_key = keychain.derive_slip77_blinding_private_key(script) + self.assertEqual(private_key, unhexlify(b"26f1dc2c52222394236d76e0809516255cfcca94069fd5187c0f090d18f42ad6")) + public_key = keychain.derive_slip77_blinding_public_key(script) + self.assertEqual(public_key, unhexlify(b"03e84cd853fea825bd94f5d2d46580ae0d059c734707fa7a08f5e2f612a51c1acb")) + self.assertEqual(secp256k1.publickey(private_key), public_key) + if __name__ == '__main__': unittest.main()