1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-12-04 21:48:17 +00:00
trezor-firmware/tests/bip32.py

148 lines
4.5 KiB
Python
Raw Permalink Normal View History

# This file is part of the Trezor project.
2016-11-25 21:53:55 +00:00
#
2019-05-29 16:44:09 +00:00
# Copyright (C) 2012-2019 SatoshiLabs and contributors
2016-11-25 21:53:55 +00:00
#
# This library is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License version 3
# as published by the Free Software Foundation.
2016-11-25 21:53:55 +00:00
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the License along with this library.
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
2016-11-25 21:53:55 +00:00
2013-12-16 15:03:38 +00:00
import hashlib
2018-08-13 16:21:24 +00:00
import hmac
import struct
from copy import copy
from typing import Any, List, Tuple
2013-12-16 15:03:38 +00:00
2014-01-14 13:29:18 +00:00
import ecdsa
from ecdsa.curves import SECP256k1
2018-08-13 16:21:24 +00:00
from ecdsa.ellipticcurve import INFINITY, Point
from ecdsa.util import number_to_string, string_to_number
2013-12-16 15:03:38 +00:00
2018-08-13 16:21:24 +00:00
from trezorlib import messages, tools
def point_to_pubkey(point: Point) -> bytes:
2014-01-14 13:29:18 +00:00
order = SECP256k1.order
x_str = number_to_string(point.x(), order)
y_str = number_to_string(point.y(), order)
vk = x_str + y_str
2018-08-13 16:21:24 +00:00
return struct.pack("B", (vk[63] & 1) + 2) + vk[0:32] # To compressed key
2014-01-14 13:29:18 +00:00
2017-06-23 19:31:42 +00:00
def sec_to_public_pair(pubkey: bytes) -> Tuple[int, Any]:
2014-01-14 13:29:18 +00:00
"""Convert a public key in sec binary format to a public pair."""
x = string_to_number(pubkey[1:33])
sec0 = pubkey[:1]
2018-08-13 16:21:24 +00:00
if sec0 not in (b"\2", b"\3"):
raise ValueError("Compressed pubkey expected")
2014-01-14 13:29:18 +00:00
def public_pair_for_x(generator, x: int, is_even: bool) -> Tuple[int, Any]:
2014-01-14 13:29:18 +00:00
curve = generator.curve()
p = curve.p()
alpha = (pow(x, 3, p) + curve.a() * x + curve.b()) % p
2014-02-07 00:47:55 +00:00
beta = ecdsa.numbertheory.square_root_mod_prime(alpha, p)
2014-01-14 13:29:18 +00:00
if is_even == bool(beta & 1):
return (x, p - beta)
return (x, beta)
2018-08-13 16:21:24 +00:00
return public_pair_for_x(
ecdsa.ecdsa.generator_secp256k1, x, is_even=(sec0 == b"\2")
)
2014-01-14 13:29:18 +00:00
2017-06-23 19:31:42 +00:00
def fingerprint(pubkey: bytes) -> int:
2014-01-14 13:29:18 +00:00
return string_to_number(tools.hash_160(pubkey)[:4])
2017-06-23 19:31:42 +00:00
def get_address(public_node: messages.HDNodeType, address_type: int) -> str:
2014-01-14 13:29:18 +00:00
return tools.public_key_to_bc_address(public_node.public_key, address_type)
2017-06-23 19:31:42 +00:00
def public_ckd(public_node: messages.HDNodeType, n: List[int]):
2014-01-14 13:29:18 +00:00
if not isinstance(n, list):
2018-08-13 16:21:24 +00:00
raise ValueError("Parameter must be a list")
2014-01-14 13:29:18 +00:00
node = copy(public_node)
2014-01-14 13:29:18 +00:00
for i in n:
2019-03-05 15:28:17 +00:00
node = get_subnode(node, i)
2014-01-14 13:29:18 +00:00
return node
2013-12-16 15:03:38 +00:00
2017-06-23 19:31:42 +00:00
def get_subnode(node: messages.HDNodeType, i: int) -> messages.HDNodeType:
2013-12-16 15:03:38 +00:00
# Public Child key derivation (CKD) algorithm of BIP32
i_as_bytes = struct.pack(">L", i)
2020-03-04 14:12:30 +00:00
if i & tools.HARDENED_FLAG:
raise ValueError("Prime derivation not supported")
2013-12-16 15:03:38 +00:00
# Public derivation
data = node.public_key + i_as_bytes
I64 = hmac.HMAC(key=node.chain_code, msg=data, digestmod=hashlib.sha512).digest()
I_left_as_exponent = string_to_number(I64[:32])
2014-01-14 13:29:18 +00:00
# BIP32 magic converts old public key to new public point
x, y = sec_to_public_pair(node.public_key)
2018-08-13 16:21:24 +00:00
point = I_left_as_exponent * SECP256k1.generator + Point(
SECP256k1.curve, x, y, SECP256k1.order
)
2014-01-14 13:29:18 +00:00
if point == INFINITY:
raise ValueError("Point cannot be INFINITY")
2014-01-14 13:29:18 +00:00
return messages.HDNodeType(
depth=node.depth + 1,
child_num=i,
chain_code=I64[32:],
fingerprint=fingerprint(node.public_key),
# Convert public point to compressed public key
public_key=point_to_pubkey(point),
)
2017-06-23 19:31:42 +00:00
def serialize(node: messages.HDNodeType, version: int = 0x0488B21E) -> str:
2018-08-13 16:21:24 +00:00
s = b""
s += struct.pack(">I", version)
s += struct.pack(">B", node.depth)
s += struct.pack(">I", node.fingerprint)
s += struct.pack(">I", node.child_num)
s += node.chain_code
if node.private_key:
2018-08-13 16:21:24 +00:00
s += b"\x00" + node.private_key
2016-05-26 15:20:44 +00:00
else:
s += node.public_key
s += tools.btc_hash(s)[:4]
return tools.b58encode(s)
2017-06-23 19:31:42 +00:00
def deserialize(xpub: str) -> messages.HDNodeType:
data = tools.b58decode(xpub, None)
if tools.btc_hash(data[:-4])[:4] != data[-4:]:
raise ValueError("Checksum failed")
node = messages.HDNodeType(
depth=struct.unpack(">B", data[4:5])[0],
fingerprint=struct.unpack(">I", data[5:9])[0],
child_num=struct.unpack(">I", data[9:13])[0],
chain_code=data[13:45],
public_key=None,
)
2016-01-12 23:17:38 +00:00
key = data[45:-4]
2018-02-27 15:30:32 +00:00
if key[0] == 0:
node.private_key = key[1:]
else:
node.public_key = key
return node