core/seed: add SLIP-0077 derivation to Keychain (#398)

Following #66 and #317, it would allow deriving confidential addresses and
unblinding confidential transactions' outputs.
pull/475/head
Roman Zeyde 5 years ago committed by Pavol Rusnak
parent db86092319
commit 680e18a4ba

@ -1,5 +1,6 @@
from trezor import wire from trezor import wire
from trezor.crypto import bip32, hashlib, hmac from trezor.crypto import bip32, hashlib, hmac
from trezor.crypto.curve import secp256k1
from apps.common import HARDENED, cache, mnemonic, storage from apps.common import HARDENED, cache, mnemonic, storage
from apps.common.request_passphrase import protect_by_passphrase from apps.common.request_passphrase import protect_by_passphrase
@ -48,7 +49,7 @@ class Keychain:
def __del__(self) -> None: def __del__(self) -> None:
for root in self.roots: for root in self.roots:
if root is not None: if root is not None and hasattr(root, "__del__"):
root.__del__() root.__del__()
del self.roots del self.roots
del self.seed del self.seed
@ -93,6 +94,17 @@ class Keychain:
node.derive_path(suffix) node.derive_path(suffix)
return node 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: async def get_keychain(ctx: wire.Context, namespaces: list) -> Keychain:
if not storage.is_initialized(): if not storage.is_initialized():

@ -1,9 +1,10 @@
from common import * 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.common.seed import Keychain, Slip21Node, _path_hardened
from apps.wallet.sign_tx import scripts, addresses
from trezor import wire from trezor import wire
from trezor.crypto import bip39 from trezor.crypto import bip39
from trezor.crypto.curve import secp256k1
class TestKeychain(unittest.TestCase): class TestKeychain(unittest.TestCase):
@ -115,6 +116,21 @@ class TestKeychain(unittest.TestCase):
with self.assertRaises(wire.DataError): with self.assertRaises(wire.DataError):
keychain.derive([b"SLIP-9999", b"Authentication key"], "slip21").key() 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__': if __name__ == '__main__':
unittest.main() unittest.main()

Loading…
Cancel
Save