mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-26 17:38:39 +00:00
core/seed: Add SLIP-0021 implementation and tests.
This commit is contained in:
parent
3750ade2ab
commit
5f604b5fea
@ -54,7 +54,7 @@ STATIC mp_obj_t mod_trezorcrypto_Sha512_make_new(const mp_obj_type_t *type,
|
|||||||
return MP_OBJ_FROM_PTR(o);
|
return MP_OBJ_FROM_PTR(o);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// def hash(self, data: bytes) -> None:
|
/// def update(self, data: bytes) -> None:
|
||||||
/// """
|
/// """
|
||||||
/// Update the hash context with hashed data.
|
/// Update the hash context with hashed data.
|
||||||
/// """
|
/// """
|
||||||
|
@ -358,7 +358,7 @@ class sha512:
|
|||||||
Creates a hash context object.
|
Creates a hash context object.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def hash(self, data: bytes) -> None:
|
def update(self, data: bytes) -> None:
|
||||||
"""
|
"""
|
||||||
Update the hash context with hashed data.
|
Update the hash context with hashed data.
|
||||||
"""
|
"""
|
||||||
|
@ -1,11 +1,36 @@
|
|||||||
from trezor import wire
|
from trezor import wire
|
||||||
from trezor.crypto import bip32
|
from trezor.crypto import bip32, hashlib, hmac
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
from typing import List, Optional
|
from typing import List, Union
|
||||||
|
|
||||||
|
|
||||||
|
class Slip21Node:
|
||||||
|
def __init__(self, seed: bytes = None) -> None:
|
||||||
|
if seed is not None:
|
||||||
|
self.data = hmac.new(b"Symmetric key seed", seed, hashlib.sha512).digest()
|
||||||
|
else:
|
||||||
|
self.data = b""
|
||||||
|
|
||||||
|
def __del__(self) -> None:
|
||||||
|
del self.data
|
||||||
|
|
||||||
|
def derive_path(self, path: list) -> None:
|
||||||
|
for label in path:
|
||||||
|
h = hmac.new(self.data[0:32], b"\x00", hashlib.sha512)
|
||||||
|
h.update(label)
|
||||||
|
self.data = h.digest()
|
||||||
|
|
||||||
|
def key(self) -> bytes:
|
||||||
|
return self.data[32:64]
|
||||||
|
|
||||||
|
def clone(self) -> "Slip21Node":
|
||||||
|
node = Slip21Node()
|
||||||
|
node.data = self.data
|
||||||
|
return node
|
||||||
|
|
||||||
|
|
||||||
class Keychain:
|
class Keychain:
|
||||||
@ -17,7 +42,9 @@ class Keychain:
|
|||||||
def __init__(self, seed: bytes, namespaces: list):
|
def __init__(self, seed: bytes, namespaces: list):
|
||||||
self.seed = seed
|
self.seed = seed
|
||||||
self.namespaces = namespaces
|
self.namespaces = namespaces
|
||||||
self.roots = [None] * len(namespaces) # type: List[Optional[bip32.HDNode]]
|
self.roots = [None] * len(
|
||||||
|
namespaces
|
||||||
|
) # type: List[Union[bip32.HDNode, Slip21Node, None]]
|
||||||
|
|
||||||
def __del__(self) -> None:
|
def __del__(self) -> None:
|
||||||
for root in self.roots:
|
for root in self.roots:
|
||||||
@ -34,7 +61,9 @@ class Keychain:
|
|||||||
return
|
return
|
||||||
raise wire.DataError("Forbidden key path")
|
raise wire.DataError("Forbidden key path")
|
||||||
|
|
||||||
def derive(self, node_path: list, curve_name: str = "secp256k1") -> bip32.HDNode:
|
def derive(
|
||||||
|
self, node_path: list, curve_name: str = "secp256k1"
|
||||||
|
) -> Union[bip32.HDNode, Slip21Node]:
|
||||||
# find the root node index
|
# find the root node index
|
||||||
root_index = 0
|
root_index = 0
|
||||||
for curve, *path in self.namespaces:
|
for curve, *path in self.namespaces:
|
||||||
@ -49,7 +78,10 @@ class Keychain:
|
|||||||
# create the root node if not cached
|
# create the root node if not cached
|
||||||
root = self.roots[root_index]
|
root = self.roots[root_index]
|
||||||
if root is None:
|
if root is None:
|
||||||
|
if curve_name != "slip21":
|
||||||
root = bip32.from_seed(self.seed, curve_name)
|
root = bip32.from_seed(self.seed, curve_name)
|
||||||
|
else:
|
||||||
|
root = Slip21Node(self.seed)
|
||||||
root.derive_path(path)
|
root.derive_path(path)
|
||||||
self.roots[root_index] = root
|
self.roots[root_index] = root
|
||||||
|
|
||||||
@ -86,6 +118,15 @@ def derive_node_without_passphrase(
|
|||||||
return node
|
return node
|
||||||
|
|
||||||
|
|
||||||
|
def derive_slip21_node_without_passphrase(path: list) -> Slip21Node:
|
||||||
|
if not storage.is_initialized():
|
||||||
|
raise Exception("Device is not initialized")
|
||||||
|
seed = mnemonic.get_seed(progress_bar=False)
|
||||||
|
node = Slip21Node(seed)
|
||||||
|
node.derive_path(path)
|
||||||
|
return node
|
||||||
|
|
||||||
|
|
||||||
def remove_ed25519_prefix(pubkey: bytes) -> bytes:
|
def remove_ed25519_prefix(pubkey: bytes) -> bytes:
|
||||||
# 0x01 prefix is not part of the actual public key, hence removed
|
# 0x01 prefix is not part of the actual public key, hence removed
|
||||||
return pubkey[1:]
|
return pubkey[1:]
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
from common import *
|
from common import *
|
||||||
from apps.common import HARDENED
|
from apps.common import HARDENED
|
||||||
from apps.common.seed import Keychain, _path_hardened
|
from apps.common.seed import Keychain, Slip21Node, _path_hardened
|
||||||
from trezor import wire
|
from trezor import wire
|
||||||
|
from trezor.crypto import bip39
|
||||||
|
|
||||||
|
|
||||||
class TestKeychain(unittest.TestCase):
|
class TestKeychain(unittest.TestCase):
|
||||||
@ -75,5 +76,45 @@ class TestKeychain(unittest.TestCase):
|
|||||||
self.assertFalse(_path_hardened([0, ]))
|
self.assertFalse(_path_hardened([0, ]))
|
||||||
self.assertFalse(_path_hardened([44 | HARDENED, 1 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0]))
|
self.assertFalse(_path_hardened([44 | HARDENED, 1 | HARDENED, 0 | HARDENED, 0 | HARDENED, 0]))
|
||||||
|
|
||||||
|
def test_slip21(self):
|
||||||
|
seed = bip39.seed(' '.join(['all'] * 12), '')
|
||||||
|
node1 = Slip21Node(seed)
|
||||||
|
node2 = node1.clone()
|
||||||
|
keychain = Keychain(seed, [["slip21", b"SLIP-0021"]])
|
||||||
|
|
||||||
|
# Key(m)
|
||||||
|
KEY_M = unhexlify(b"dbf12b44133eaab506a740f6565cc117228cbf1dd70635cfa8ddfdc9af734756")
|
||||||
|
self.assertEqual(node1.key(), KEY_M)
|
||||||
|
|
||||||
|
# Key(m/"SLIP-0021")
|
||||||
|
KEY_M_SLIP0021 = unhexlify(b"1d065e3ac1bbe5c7fad32cf2305f7d709dc070d672044a19e610c77cdf33de0d")
|
||||||
|
node1.derive_path([b"SLIP-0021"])
|
||||||
|
self.assertEqual(node1.key(), KEY_M_SLIP0021)
|
||||||
|
self.assertIsNone(keychain.validate_path([b"SLIP-0021"], "slip21"))
|
||||||
|
self.assertEqual(keychain.derive([b"SLIP-0021"], "slip21").key(), KEY_M_SLIP0021)
|
||||||
|
|
||||||
|
# Key(m/"SLIP-0021"/"Master encryption key")
|
||||||
|
KEY_M_SLIP0021_MEK = unhexlify(b"ea163130e35bbafdf5ddee97a17b39cef2be4b4f390180d65b54cf05c6a82fde")
|
||||||
|
node1.derive_path([b"Master encryption key"])
|
||||||
|
self.assertEqual(node1.key(), KEY_M_SLIP0021_MEK)
|
||||||
|
self.assertIsNone(keychain.validate_path([b"SLIP-0021", b"Master encryption key"], "slip21"))
|
||||||
|
self.assertEqual(keychain.derive([b"SLIP-0021", b"Master encryption key"], "slip21").key(), KEY_M_SLIP0021_MEK)
|
||||||
|
|
||||||
|
# Key(m/"SLIP-0021"/"Authentication key")
|
||||||
|
KEY_M_SLIP0021_AK = unhexlify(b"47194e938ab24cc82bfa25f6486ed54bebe79c40ae2a5a32ea6db294d81861a6")
|
||||||
|
node2.derive_path([b"SLIP-0021", b"Authentication key"])
|
||||||
|
self.assertEqual(node2.key(), KEY_M_SLIP0021_AK)
|
||||||
|
self.assertIsNone(keychain.validate_path([b"SLIP-0021", b"Authentication key"], "slip21"))
|
||||||
|
self.assertEqual(keychain.derive([b"SLIP-0021", b"Authentication key"], "slip21").key(), KEY_M_SLIP0021_AK)
|
||||||
|
|
||||||
|
# Forbidden paths.
|
||||||
|
with self.assertRaises(wire.DataError):
|
||||||
|
self.assertFalse(keychain.validate_path([], "slip21"))
|
||||||
|
with self.assertRaises(wire.DataError):
|
||||||
|
self.assertFalse(keychain.validate_path([b"SLIP-9999", b"Authentication key"], "slip21"))
|
||||||
|
with self.assertRaises(wire.DataError):
|
||||||
|
keychain.derive([b"SLIP-9999", b"Authentication key"], "slip21").key()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
Loading…
Reference in New Issue
Block a user