mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-22 15:38:11 +00:00
cardano: replace derivation scheme v1 for v2 (#342)
This commit is contained in:
parent
7128337031
commit
060a8cbdfd
@ -165,6 +165,7 @@ STATIC mp_obj_t mod_trezorcrypto_HDNode_derive_cardano(mp_obj_t self, mp_obj_t i
|
|||||||
mp_obj_HDNode_t *o = MP_OBJ_TO_PTR(self);
|
mp_obj_HDNode_t *o = MP_OBJ_TO_PTR(self);
|
||||||
uint32_t i = mp_obj_get_int_truncated(index);
|
uint32_t i = mp_obj_get_int_truncated(index);
|
||||||
uint32_t fp = hdnode_fingerprint(&o->hdnode);
|
uint32_t fp = hdnode_fingerprint(&o->hdnode);
|
||||||
|
|
||||||
int res;
|
int res;
|
||||||
// same as in derive
|
// same as in derive
|
||||||
if (0 == memcmp(o->hdnode.private_key, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 32)) {
|
if (0 == memcmp(o->hdnode.private_key, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 32)) {
|
||||||
@ -510,14 +511,16 @@ STATIC mp_obj_t mod_trezorcrypto_bip32_from_mnemonic_cardano(mp_obj_t mnemonic)
|
|||||||
mp_get_buffer_raise(mnemonic, &mnemo, MP_BUFFER_READ);
|
mp_get_buffer_raise(mnemonic, &mnemo, MP_BUFFER_READ);
|
||||||
HDNode hdnode;
|
HDNode hdnode;
|
||||||
const char *pmnemonic = mnemo.len > 0 ? mnemo.buf : "";
|
const char *pmnemonic = mnemo.len > 0 ? mnemo.buf : "";
|
||||||
uint8_t entropy[66];
|
|
||||||
int entropy_len = mnemonic_to_entropy(pmnemonic, entropy + 2);
|
uint8_t entropy[64];
|
||||||
|
int entropy_len = mnemonic_to_entropy(pmnemonic, entropy);
|
||||||
|
|
||||||
if (entropy_len == 0) {
|
if (entropy_len == 0) {
|
||||||
mp_raise_ValueError("Invalid mnemonic");
|
mp_raise_ValueError("Invalid mnemonic");
|
||||||
}
|
}
|
||||||
|
|
||||||
const int res = hdnode_from_seed_cardano((const uint8_t *)"", 0, entropy, entropy_len / 8, &hdnode);
|
int res = hdnode_from_seed_cardano((const uint8_t *)"", 0, entropy, entropy_len / 8, &hdnode);
|
||||||
|
|
||||||
if (!res) {
|
if (!res) {
|
||||||
mp_raise_ValueError("Secret key generation from mnemonic is looping forever");
|
mp_raise_ValueError("Secret key generation from mnemonic is looping forever");
|
||||||
}else if(res == -1){
|
}else if(res == -1){
|
||||||
|
@ -5,4 +5,4 @@ from trezor.messages import MessageType
|
|||||||
def boot():
|
def boot():
|
||||||
wire.add(MessageType.CardanoGetAddress, __name__, "get_address")
|
wire.add(MessageType.CardanoGetAddress, __name__, "get_address")
|
||||||
wire.add(MessageType.CardanoGetPublicKey, __name__, "get_public_key")
|
wire.add(MessageType.CardanoGetPublicKey, __name__, "get_public_key")
|
||||||
wire.add(MessageType.CardanoSignTx, __name__, "sign_transaction")
|
wire.add(MessageType.CardanoSignTx, __name__, "sign_tx")
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
from micropython import const
|
from micropython import const
|
||||||
|
|
||||||
from trezor import wire
|
from trezor import wire
|
||||||
from trezor.crypto import base58, chacha20poly1305, crc, hashlib, pbkdf2
|
from trezor.crypto import base58, crc, hashlib
|
||||||
|
|
||||||
from . import cbor
|
from . import cbor
|
||||||
|
|
||||||
@ -18,14 +18,6 @@ def validate_derivation_path(path: list):
|
|||||||
return path
|
return path
|
||||||
|
|
||||||
|
|
||||||
def _derive_hd_passphrase(node) -> bytes:
|
|
||||||
iterations = const(500)
|
|
||||||
length = const(32)
|
|
||||||
passwd = seed.remove_ed25519_prefix(node.public_key()) + node.chain_code()
|
|
||||||
x = pbkdf2("hmac-sha512", passwd, b"address-hashing", iterations)
|
|
||||||
return x.key()[:length]
|
|
||||||
|
|
||||||
|
|
||||||
def _address_hash(data) -> bytes:
|
def _address_hash(data) -> bytes:
|
||||||
data = cbor.encode(data)
|
data = cbor.encode(data)
|
||||||
data = hashlib.sha3_256(data).digest()
|
data = hashlib.sha3_256(data).digest()
|
||||||
@ -42,34 +34,16 @@ def _get_address_root(node, payload):
|
|||||||
return _address_hash([0, [0, extpubkey], payload])
|
return _address_hash([0, [0, extpubkey], payload])
|
||||||
|
|
||||||
|
|
||||||
def _encrypt_derivation_path(path: list, hd_passphrase: bytes) -> bytes:
|
|
||||||
serialized = cbor.encode(cbor.IndefiniteLengthArray(path))
|
|
||||||
ctx = chacha20poly1305(hd_passphrase, b"serokellfore")
|
|
||||||
data = ctx.encrypt(serialized)
|
|
||||||
tag = ctx.finish()
|
|
||||||
return data + tag
|
|
||||||
|
|
||||||
|
|
||||||
def derive_address_and_node(root_node, path: list):
|
def derive_address_and_node(root_node, path: list):
|
||||||
validate_derivation_path(path)
|
validate_derivation_path(path)
|
||||||
|
|
||||||
derived_node = root_node.clone()
|
derived_node = root_node.clone()
|
||||||
|
|
||||||
# this means empty derivation path m/44'/1815'
|
address_payload = None
|
||||||
if len(path) == 2:
|
address_attributes = {}
|
||||||
address_payload = None
|
|
||||||
address_attributes = {}
|
|
||||||
else:
|
|
||||||
if len(path) == 5:
|
|
||||||
p = [path[2], path[4]]
|
|
||||||
else:
|
|
||||||
p = [path[2]]
|
|
||||||
for indice in p:
|
|
||||||
derived_node.derive_cardano(indice)
|
|
||||||
|
|
||||||
hd_passphrase = _derive_hd_passphrase(root_node)
|
for indice in path:
|
||||||
address_payload = _encrypt_derivation_path(p, hd_passphrase)
|
derived_node.derive_cardano(indice)
|
||||||
address_attributes = {1: cbor.encode(address_payload)}
|
|
||||||
|
|
||||||
address_root = _get_address_root(derived_node, address_payload)
|
address_root = _get_address_root(derived_node, address_payload)
|
||||||
address_type = 0
|
address_type = 0
|
||||||
|
@ -5,7 +5,7 @@ from trezor.crypto import bip32
|
|||||||
from trezor.messages.CardanoPublicKey import CardanoPublicKey
|
from trezor.messages.CardanoPublicKey import CardanoPublicKey
|
||||||
from trezor.messages.HDNodeType import HDNodeType
|
from trezor.messages.HDNodeType import HDNodeType
|
||||||
|
|
||||||
from .address import _derive_hd_passphrase, derive_address_and_node
|
from .address import derive_address_and_node
|
||||||
|
|
||||||
from apps.common import layout, seed, storage
|
from apps.common import layout, seed, storage
|
||||||
|
|
||||||
@ -34,7 +34,9 @@ def _get_public_key(root_node, derivation_path: list):
|
|||||||
public_key = hexlify(seed.remove_ed25519_prefix(node.public_key())).decode()
|
public_key = hexlify(seed.remove_ed25519_prefix(node.public_key())).decode()
|
||||||
chain_code = hexlify(node.chain_code()).decode()
|
chain_code = hexlify(node.chain_code()).decode()
|
||||||
xpub_key = public_key + chain_code
|
xpub_key = public_key + chain_code
|
||||||
root_hd_passphrase = hexlify(_derive_hd_passphrase(root_node)).decode()
|
|
||||||
|
# In derivation scheme v2 the passphrase is not used
|
||||||
|
root_hd_passphrase = None
|
||||||
|
|
||||||
node_type = HDNodeType(
|
node_type = HDNodeType(
|
||||||
depth=node.depth(),
|
depth=node.depth(),
|
||||||
|
@ -7,7 +7,7 @@ from trezor.messages.MessageType import CardanoTxAck
|
|||||||
from trezor.ui.text import BR
|
from trezor.ui.text import BR
|
||||||
|
|
||||||
from .address import _break_address_n_to_lines, derive_address_and_node
|
from .address import _break_address_n_to_lines, derive_address_and_node
|
||||||
from .ui import confirm_with_pagination, progress
|
from .layout import confirm_with_pagination, progress
|
||||||
|
|
||||||
from apps.cardano import cbor
|
from apps.cardano import cbor
|
||||||
from apps.common import seed, storage
|
from apps.common import seed, storage
|
||||||
@ -22,6 +22,7 @@ async def show_tx(
|
|||||||
change_coins: list,
|
change_coins: list,
|
||||||
fee: float,
|
fee: float,
|
||||||
tx_size: float,
|
tx_size: float,
|
||||||
|
network_name: str,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
lines = ("%s ADA" % _micro_ada_to_ada(fee), BR, "Tx size:", "%s bytes" % tx_size)
|
lines = ("%s ADA" % _micro_ada_to_ada(fee), BR, "Tx size:", "%s bytes" % tx_size)
|
||||||
if not await confirm_with_pagination(
|
if not await confirm_with_pagination(
|
||||||
@ -29,6 +30,11 @@ async def show_tx(
|
|||||||
):
|
):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
if not await confirm_with_pagination(
|
||||||
|
ctx, "%s network" % network_name, "Confirm network", ui.ICON_SEND, ui.GREEN
|
||||||
|
):
|
||||||
|
return False
|
||||||
|
|
||||||
for index, output in enumerate(outputs):
|
for index, output in enumerate(outputs):
|
||||||
if not await confirm_with_pagination(
|
if not await confirm_with_pagination(
|
||||||
ctx, output, "Confirm output", ui.ICON_SEND, ui.GREEN
|
ctx, output, "Confirm output", ui.ICON_SEND, ui.GREEN
|
||||||
@ -71,7 +77,7 @@ async def request_transaction(ctx, tx_req: CardanoTxRequest, index: int):
|
|||||||
return await ctx.call(tx_req, CardanoTxAck)
|
return await ctx.call(tx_req, CardanoTxAck)
|
||||||
|
|
||||||
|
|
||||||
async def sign_transaction(ctx, msg):
|
async def sign_tx(ctx, msg):
|
||||||
mnemonic = storage.get_mnemonic()
|
mnemonic = storage.get_mnemonic()
|
||||||
root_node = bip32.from_mnemonic_cardano(mnemonic)
|
root_node = bip32.from_mnemonic_cardano(mnemonic)
|
||||||
|
|
||||||
@ -90,7 +96,9 @@ async def sign_transaction(ctx, msg):
|
|||||||
display_homescreen()
|
display_homescreen()
|
||||||
|
|
||||||
# sign the transaction bundle and prepare the result
|
# sign the transaction bundle and prepare the result
|
||||||
transaction = Transaction(msg.inputs, msg.outputs, transactions, root_node)
|
transaction = Transaction(
|
||||||
|
msg.inputs, msg.outputs, transactions, root_node, msg.network
|
||||||
|
)
|
||||||
tx_body, tx_hash = transaction.serialise_tx()
|
tx_body, tx_hash = transaction.serialise_tx()
|
||||||
tx = CardanoSignedTx(tx_body=tx_body, tx_hash=tx_hash)
|
tx = CardanoSignedTx(tx_body=tx_body, tx_hash=tx_hash)
|
||||||
|
|
||||||
@ -108,6 +116,7 @@ async def sign_transaction(ctx, msg):
|
|||||||
transaction.change_coins,
|
transaction.change_coins,
|
||||||
transaction.fee,
|
transaction.fee,
|
||||||
len(tx_body),
|
len(tx_body),
|
||||||
|
transaction.network.get("name"),
|
||||||
):
|
):
|
||||||
raise wire.ActionCancelled("Signing cancelled")
|
raise wire.ActionCancelled("Signing cancelled")
|
||||||
|
|
||||||
@ -119,17 +128,34 @@ def _micro_ada_to_ada(amount: float) -> float:
|
|||||||
|
|
||||||
|
|
||||||
class Transaction:
|
class Transaction:
|
||||||
CARDANO_WITNESS_MAGIC_PREFIX = b"\x01\x1a\x2d\x96\x4a\x09\x58\x20"
|
def __init__(
|
||||||
|
self, inputs: list, outputs: list, transactions: list, root_node, network
|
||||||
def __init__(self, inputs: list, outputs: list, transactions: list, root_node):
|
):
|
||||||
self.inputs = inputs
|
self.inputs = inputs
|
||||||
self.outputs = outputs
|
self.outputs = outputs
|
||||||
self.transactions = transactions
|
self.transactions = transactions
|
||||||
self.root_node = root_node
|
self.root_node = root_node
|
||||||
|
self.network = None
|
||||||
|
|
||||||
|
self._set_network(network)
|
||||||
|
|
||||||
# attributes have to be always empty in current Cardano
|
# attributes have to be always empty in current Cardano
|
||||||
self.attributes = {}
|
self.attributes = {}
|
||||||
|
|
||||||
|
def _set_network(self, network):
|
||||||
|
if network == 1:
|
||||||
|
self.network = {
|
||||||
|
"name": "Testnet",
|
||||||
|
"magic_prefix": b"\x01\x1a\x41\x70\xcb\x17\x58\x20",
|
||||||
|
}
|
||||||
|
elif network == 2:
|
||||||
|
self.network = {
|
||||||
|
"name": "Mainnet",
|
||||||
|
"magic_prefix": b"\x01\x1a\x2d\x96\x4a\x09\x58\x20",
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
raise wire.ProcessError("Unknown network index " + str(network))
|
||||||
|
|
||||||
def _process_inputs(self):
|
def _process_inputs(self):
|
||||||
input_coins = []
|
input_coins = []
|
||||||
input_hashes = []
|
input_hashes = []
|
||||||
@ -198,7 +224,7 @@ class Transaction:
|
|||||||
def _build_witnesses(self, tx_aux_hash: str):
|
def _build_witnesses(self, tx_aux_hash: str):
|
||||||
witnesses = []
|
witnesses = []
|
||||||
for index, node in enumerate(self.nodes):
|
for index, node in enumerate(self.nodes):
|
||||||
message = self.CARDANO_WITNESS_MAGIC_PREFIX + tx_aux_hash
|
message = self.network.get("magic_prefix") + tx_aux_hash
|
||||||
signature = ed25519.sign_ext(
|
signature = ed25519.sign_ext(
|
||||||
node.private_key(), node.private_key_ext(), message
|
node.private_key(), node.private_key_ext(), message
|
||||||
)
|
)
|
@ -18,6 +18,7 @@ class CardanoSignTx(p.MessageType):
|
|||||||
1: ('inputs', CardanoTxInputType, p.FLAG_REPEATED),
|
1: ('inputs', CardanoTxInputType, p.FLAG_REPEATED),
|
||||||
2: ('outputs', CardanoTxOutputType, p.FLAG_REPEATED),
|
2: ('outputs', CardanoTxOutputType, p.FLAG_REPEATED),
|
||||||
3: ('transactions_count', p.UVarintType, 0),
|
3: ('transactions_count', p.UVarintType, 0),
|
||||||
|
4: ('network', p.UVarintType, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@ -25,7 +26,9 @@ class CardanoSignTx(p.MessageType):
|
|||||||
inputs: List[CardanoTxInputType] = None,
|
inputs: List[CardanoTxInputType] = None,
|
||||||
outputs: List[CardanoTxOutputType] = None,
|
outputs: List[CardanoTxOutputType] = None,
|
||||||
transactions_count: int = None,
|
transactions_count: int = None,
|
||||||
|
network: int = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.inputs = inputs if inputs is not None else []
|
self.inputs = inputs if inputs is not None else []
|
||||||
self.outputs = outputs if outputs is not None else []
|
self.outputs = outputs if outputs is not None else []
|
||||||
self.transactions_count = transactions_count
|
self.transactions_count = transactions_count
|
||||||
|
self.network = network
|
||||||
|
@ -109,9 +109,6 @@ StellarAccountMergeOp = 218
|
|||||||
StellarManageDataOp = 220
|
StellarManageDataOp = 220
|
||||||
StellarBumpSequenceOp = 221
|
StellarBumpSequenceOp = 221
|
||||||
StellarSignedTx = 230
|
StellarSignedTx = 230
|
||||||
CardanoSignMessage = 300
|
|
||||||
CardanoMessageSignature = 301
|
|
||||||
CardanoVerifyMessage = 302
|
|
||||||
CardanoSignTx = 303
|
CardanoSignTx = 303
|
||||||
CardanoTxRequest = 304
|
CardanoTxRequest = 304
|
||||||
CardanoGetPublicKey = 305
|
CardanoGetPublicKey = 305
|
||||||
|
@ -3,8 +3,6 @@ from apps.common import seed
|
|||||||
from trezor import wire
|
from trezor import wire
|
||||||
|
|
||||||
from apps.cardano.address import (
|
from apps.cardano.address import (
|
||||||
_derive_hd_passphrase,
|
|
||||||
_encrypt_derivation_path,
|
|
||||||
_get_address_root,
|
_get_address_root,
|
||||||
_address_hash,
|
_address_hash,
|
||||||
validate_derivation_path,
|
validate_derivation_path,
|
||||||
@ -14,14 +12,14 @@ from trezor.crypto import bip32
|
|||||||
|
|
||||||
|
|
||||||
class TestCardanoAddress(unittest.TestCase):
|
class TestCardanoAddress(unittest.TestCase):
|
||||||
def test_hardened_address_derivation(self):
|
def test_hardened_address_derivation_scheme(self):
|
||||||
mnemonic = "plastic that delay conduct police ticket swim gospel intact harsh obtain entire"
|
mnemonic = "all all all all all all all all all all all all"
|
||||||
node = bip32.from_mnemonic_cardano(mnemonic)
|
node = bip32.from_mnemonic_cardano(mnemonic)
|
||||||
|
|
||||||
addresses = [
|
addresses = [
|
||||||
"DdzFFzCqrhtDB6YEgPQqFiVnhKsfyEMe9MLQabhayVUL2WRN1dbLLFS7VfKYBy8n3uemZRcDyqMnv7STCU9vj2eAR8CgFgKMDG2mkQN7",
|
"Ae2tdPwUPEZ98eHFwxSsPBDz73amioKpr58Vw85mP1tMkzq8siaftiejJ3j",
|
||||||
"DdzFFzCqrhtCGRQ2UYpcouvRgDnPsAYpmzWVtd5YLvaRrMAMoDmYsKhNMAWePbK7a1XbZ8ghTeyaSLZ2488extnB5F9SwHus4UFaFwkS",
|
"Ae2tdPwUPEZKA971NCHuHqaEnxZDFWPzH3fEsLpDnbEpG6UeMRHnRzCzEwK",
|
||||||
"DdzFFzCqrhsqHyZLVLeFrgcxUrPA5YMJJRJCxkESHcPkV1EuuDKhKkJNPkEyrWXhPbuMHxSnz1cNYUCN8tJsLwaFiSxMz3ab19GEvaNP",
|
"Ae2tdPwUPEZL9Ag1ouS4b1zjuPxKpvEUgjpVpG1KQFs5pNewQb65F1WXVQ2",
|
||||||
]
|
]
|
||||||
|
|
||||||
for i, expected in enumerate(addresses):
|
for i, expected in enumerate(addresses):
|
||||||
@ -31,96 +29,104 @@ class TestCardanoAddress(unittest.TestCase):
|
|||||||
|
|
||||||
nodes = [
|
nodes = [
|
||||||
(
|
(
|
||||||
"d4dd69a2f2a6374f3733f53e03f610d73dd4f1d5131169bc144e6d34c9bcbe04",
|
b"3881a8de77d069001010d7f7d5211552e7d539b0e253add710367f95e528ed51",
|
||||||
"21d97a697583630e2cef01e5fc1555ea4fd9625ff8fcde1fc72e67aa42f975ec",
|
b"9b77608b38e0a0c7861aa234557c81482f42aae2d17993a8ddaec1868fb04d60",
|
||||||
"2df46e04ebf0816e242bfaa1c73e5ebe8863d05d7a96c8aac16f059975e63f30",
|
b"a938c8554ae04616cfaae7cd0eb557475082c4e910242ce774967e0bd7492408",
|
||||||
"057658de1308930ad4a5663e4f77477014b04954a9d488e62d73b04fc659a35c"
|
b"cbf6ab47c8eb1a0477fc40b25dbb6c4a99454edb97d6fe5acedd3e238ef46fe0"
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"3476630290051477e4cc206fd5f6587065d3c9558c9891cc1c0ed5a408d5b60c",
|
b"3003aca659846540b9ed04f2b844f2d8ea964856ca38a7dffedef4f6e528ed51",
|
||||||
"3f1d4beaefd2ffff59a45cb75519960d02f4de62c076a165bc39a7d7b1fec168",
|
b"8844ccc81d633e1c7126f30c2524c1652617cf58da755014070215bf5070ba38",
|
||||||
"35b0cc0b770e04d86a9cddb0e2068b3a242f6b6e93c9a9d3c4f0899bd62b4266",
|
b"be28c00ed6cb9b70310f78028f8e3a2db935baf482d84afa590b0b5b864571cc",
|
||||||
"35bb811c631b3db3b10559bc15821a39969654ebcad80cedf544ac8bf2a73ce7"
|
b"584b4631d752023a249e980779517280e6c0b3ac7a7f27c6e9456bfd228ca60b"
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"06a6f53baf84ac6713cd1c441081dff00d1c4abee33091dc5c5ebdec2044270c",
|
b"68e4482add0a741e14c8f2306bf83206a623e3729dd24175915eedece428ed51",
|
||||||
"4978871e479a3a58adabb030565162832c63a2909442d306c96eaf03823ff5c9",
|
b"3165a80c5efe846224d46a0427cdb2be4f31ea3585c51f4131faefc4328ad95a",
|
||||||
"9f26aad725aef1bb0609085f2c961b4d2579bceccfb1b01f3c7d1dbdd02b50b1",
|
b"9a32499976ffb582daa9988dfc42a303de5ed00c320c929f496be3c6eb1cf405",
|
||||||
"70f72ce51d0c984c4bbddd0297f4ffe0b4710c2c3f9a7e17f7d7e3e1810b5c33"
|
b"da07ca30a3d1c5fe3c34ce5fa197722446a646624a10bdf8889a4b9c347b2ef2"
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
for i, (priv, ext, pub, chain) in enumerate(nodes):
|
for i, (priv, ext, pub, chain) in enumerate(nodes):
|
||||||
_, n = derive_address_and_node(node, [0x80000000 | 44, 0x80000000 | 1815, 0x80000000, 0, 0x80000000 + i])
|
_, n = derive_address_and_node(node, [0x80000000 | 44, 0x80000000 | 1815, 0x80000000, 0, 0x80000000 + i])
|
||||||
self.assertEqual(unhexlify(priv), n.private_key())
|
self.assertEqual(hexlify(n.private_key()), priv)
|
||||||
self.assertEqual(unhexlify(ext), n.private_key_ext())
|
self.assertEqual(hexlify(n.private_key_ext()), ext)
|
||||||
self.assertEqual(unhexlify(pub), seed.remove_ed25519_prefix(n.public_key()))
|
self.assertEqual(hexlify(seed.remove_ed25519_prefix(n.public_key())), pub)
|
||||||
self.assertEqual(unhexlify(chain), n.chain_code())
|
self.assertEqual(hexlify(n.chain_code()), chain)
|
||||||
|
|
||||||
def test_non_hardened_address_derivation(self):
|
def test_non_hardened_address_derivation_scheme(self):
|
||||||
mnemonic = "plastic that delay conduct police ticket swim gospel intact harsh obtain entire"
|
mnemonic = "all all all all all all all all all all all all"
|
||||||
node = bip32.from_mnemonic_cardano(mnemonic)
|
node = bip32.from_mnemonic_cardano(mnemonic)
|
||||||
|
|
||||||
addresses = [
|
addresses = [
|
||||||
"2w1sdSJu3GVezU6nw8LodErz7kSrEQ9hKQhsGLWk4JxTCxg7tkJvSowGKLFE7PMxknbkuYjtaWbpnJLhJgwmwNA98GPX2SGSN1t",
|
"Ae2tdPwUPEZ5YUb8sM3eS8JqKgrRLzhiu71crfuH2MFtqaYr5ACNRdsswsZ",
|
||||||
"2w1sdSJu3GVg7mRbtq2aGUFKxXnpFoP9hesA1n7KJrnQ9QEgyy7DGbLU52L2cytPqCoNNhkvRCF9ZsBLwMv1E35CVh6XBiWj2GE",
|
"Ae2tdPwUPEZJb8r1VZxweSwHDTYtqeYqF39rZmVbrNK62JHd4Wd7Ytsc8eG",
|
||||||
"2w1sdSJu3GVg193D2yhiiH947J9UwrbPAmNao6ciAZi3GeU7sG1D3fTAnQakzHSe1FVyuRdUjcx52Q7575LxBBNE8aCunKFA4kA",
|
"Ae2tdPwUPEZFm6Y7aPZGKMyMAK16yA5pWWKU9g73ncUQNZsAjzjhszenCsq",
|
||||||
]
|
]
|
||||||
|
|
||||||
for i, expected in enumerate(addresses):
|
for i, expected in enumerate(addresses):
|
||||||
# 44'/1815'/0'/0/i
|
# 44'/1815'/0'/0/i
|
||||||
address, _ = derive_address_and_node(node, [0x80000000 | 44, 0x80000000 | 1815, 0x80000000, 0, i])
|
address, _ = derive_address_and_node(node, [0x80000000 | 44, 0x80000000 | 1815, 0x80000000, 0, i])
|
||||||
self.assertEqual(expected, address)
|
self.assertEqual(address, expected)
|
||||||
|
|
||||||
nodes = [
|
nodes = [
|
||||||
(
|
(
|
||||||
"a75a851505db79ee8557a8cb3ef561ab7d6bd24d7cc0e97b8496654431fc2e0c",
|
b"d03ba81163fd55af97bd132bf651a0da5b5e6201b15b1caca60b0be8e028ed51",
|
||||||
"21fa8154e009a46a1c44709fe23b75735c8abc6256c44cc3c208c1c914f037ce",
|
b"493f44aa8d25fe0d3fe2935c76ea6b3e9e41c79e9dbcbe7131357c5aa1b6cac5",
|
||||||
"723fdc0eb1300fe7f2b9b6989216a831835a88695ba2c2d5c50c8470b7d1b239",
|
b"b90fb812a2268e9569ff1172e8daed1da3dc7e72c7bded7c5bcb7282039f90d5",
|
||||||
"ae09010e921de259b02f34ce7fd76f9c09ad224d436fe8fa38aa212177937ffe"
|
b"fd8e71c1543de2cdc7f7623130c5f2cceb53549055fa1f5bc88199989e08cce7"
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"48ded246510a563f759fde920016ad1356238ab5936869e45ccec5b4d8fcce0c",
|
b"08b6438c8dd49d34b71c8e914d6ac3184e5ab3dcc8af023d08503a7edf28ed51",
|
||||||
"0216c5c777bfe196576b776bd9faf2ac1318966c820edb203754166d5a0f4d92",
|
b"3fee605fdfaddc1ee2ea0b246b02c9abc54ad741054bc83943e8b21487b5a053",
|
||||||
"6dc82a0d40257cfc1ea5d728c6ccfa52ad5673c2dc4cfed239dff642d29fbc46",
|
b"89053545a6c254b0d9b1464e48d2b5fcf91d4e25c128afb1fcfc61d0843338ea",
|
||||||
"cd490ae08bd2ff18e8b61c39173f6bf0db85709130baa103b9f00e4160ec150f"
|
b"26308151516f3b0e02bb1638142747863c520273ce9bd3e5cd91e1d46fe2a635"
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"8e651d540f55a4670bb5ec8cd0812731ce734a1e745059c4f445fd8cd8fcb604",
|
b"088f0275bf4a1bd18f08d7ef06c6ddb6ce7e3dc415fb4e89fe21bf39e628ed51",
|
||||||
"ab7f8d9e7927a1a71b7b08eb3b871246dc4717d9e309b7682df0eee202a5a97a",
|
b"4c44563c7df519ea9b4d1801c1ab98b449db28b87f1c3837759c20f68c4c1e65",
|
||||||
"e55323d6881ca92a0816695def558145ef22f0d0c4f6133aab7a8a3f2f98ef78",
|
b"52548cb98e6f46a592bdf7f3598a9abc0126c78dfa3f46d1894ee52a5213e833",
|
||||||
"6c9313fcf93b55a977184514aefa1c778c1abadb2ba9f2c1351b587b7c1e1572"
|
b"91af0668ee449e613e61bbb2482e5ddee1d9b15785727ec3e362c36861bff923"
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
for i, (priv, ext, pub, chain) in enumerate(nodes):
|
for i, (priv, ext, pub, chain) in enumerate(nodes):
|
||||||
_, n = derive_address_and_node(node, [0x80000000 | 44, 0x80000000 | 1815, 0x80000000, 0, i])
|
_, n = derive_address_and_node(node, [0x80000000 | 44, 0x80000000 | 1815, 0x80000000, 0, i])
|
||||||
self.assertEqual(unhexlify(priv), n.private_key())
|
self.assertEqual(hexlify(n.private_key()), priv)
|
||||||
self.assertEqual(unhexlify(ext), n.private_key_ext())
|
self.assertEqual(hexlify(n.private_key_ext()), ext)
|
||||||
self.assertEqual(unhexlify(pub), seed.remove_ed25519_prefix(n.public_key()))
|
self.assertEqual(hexlify(seed.remove_ed25519_prefix(n.public_key())), pub)
|
||||||
self.assertEqual(unhexlify(chain), n.chain_code())
|
self.assertEqual(hexlify(n.chain_code()), chain)
|
||||||
|
|
||||||
|
|
||||||
def test_root_address_derivation(self):
|
def test_root_address_derivation_scheme(self):
|
||||||
mnemonic = "plastic that delay conduct police ticket swim gospel intact harsh obtain entire"
|
mnemonic = "all all all all all all all all all all all all"
|
||||||
node = bip32.from_mnemonic_cardano(mnemonic)
|
node = bip32.from_mnemonic_cardano(mnemonic)
|
||||||
|
|
||||||
# 44'/1815'
|
# 44'/1815'
|
||||||
address, _ = derive_address_and_node(node, [0x80000000 | 44, 0x80000000 | 1815])
|
address, _ = derive_address_and_node(node, [0x80000000 | 44, 0x80000000 | 1815])
|
||||||
self.assertEqual("Ae2tdPwUPEYygPo2ZNZ7Ve6ZExaFZvkGcQFZ5oSyqVNoJn5J65Foyz2XiSU", address)
|
self.assertEqual(address, "Ae2tdPwUPEZ2FGHX3yCKPSbSgyuuTYgMxNq652zKopxT4TuWvEd8Utd92w3")
|
||||||
|
|
||||||
priv, ext, pub, chain = (
|
priv, ext, pub, chain = (
|
||||||
"90bc16ad766aebce31b407f111db3ba95de2780c5bb760f3333dac1b3823ee53",
|
b"204ec79cbb6502a141de60d274962010c7f1c94a2987b26506433184d228ed51",
|
||||||
"10f20917dcfa2b3c295386413ae3564365e4a51f063da644d0945f4d3da57699",
|
b"975cdd1c8610b44701567f05934c45c8716064263ccfe72ed2167ccb705c09b6",
|
||||||
"7d1de3f22f53904d007ff833fadd7cd6482ea1e83918b985b4ea33e63c16d183",
|
b"8c47ebce34234d04fd3dfbac33feaba6133e4e3d77c4b5ab18120ec6878ad4ce",
|
||||||
"7a04a6aab0ed12af562a26db4d10344454274d0bfa6e3581df1dc02f13c5fbe5"
|
b"02ac67c59a8b0264724a635774ca2c242afa10d7ab70e2bf0a8f7d4bb10f1f7a"
|
||||||
)
|
)
|
||||||
|
|
||||||
_, n = derive_address_and_node(node, [0x80000000 | 44, 0x80000000 | 1815])
|
_, n = derive_address_and_node(node, [0x80000000 | 44, 0x80000000 | 1815])
|
||||||
self.assertEqual(unhexlify(priv), n.private_key())
|
self.assertEqual(hexlify(n.private_key()), priv)
|
||||||
self.assertEqual(unhexlify(ext), n.private_key_ext())
|
self.assertEqual(hexlify(n.private_key_ext()), ext)
|
||||||
self.assertEqual(unhexlify(pub), seed.remove_ed25519_prefix(n.public_key()))
|
self.assertEqual(hexlify(seed.remove_ed25519_prefix(n.public_key())), pub)
|
||||||
self.assertEqual(unhexlify(chain), n.chain_code())
|
self.assertEqual(hexlify(n.chain_code()), chain)
|
||||||
|
|
||||||
|
|
||||||
|
def test_address_hash(self):
|
||||||
|
data = [0, [0, b"}\x1d\xe3\xf2/S\x90M\x00\x7f\xf83\xfa\xdd|\xd6H.\xa1\xe89\x18\xb9\x85\xb4\xea3\xe6<\x16\xd1\x83z\x04\xa6\xaa\xb0\xed\x12\xafV*&\xdbM\x104DT'M\x0b\xfan5\x81\xdf\x1d\xc0/\x13\xc5\xfb\xe5"], {}]
|
||||||
|
result = _address_hash(data)
|
||||||
|
|
||||||
|
self.assertEqual(result, b'\x1c\xca\xee\xc9\x80\xaf}\xb0\x9a\xa8\x96E\xd6\xa4\xd1\xb4\x13\x85\xb9\xc2q\x1d5/{\x12"\xca')
|
||||||
|
|
||||||
|
|
||||||
def test_validate_derivation_path(self):
|
def test_validate_derivation_path(self):
|
||||||
incorrect_derivation_paths = [
|
incorrect_derivation_paths = [
|
||||||
@ -144,32 +150,12 @@ class TestCardanoAddress(unittest.TestCase):
|
|||||||
for derivation_path in correct_derivation_paths:
|
for derivation_path in correct_derivation_paths:
|
||||||
self.assertEqual(derivation_path, validate_derivation_path(derivation_path))
|
self.assertEqual(derivation_path, validate_derivation_path(derivation_path))
|
||||||
|
|
||||||
def test_derive_hd_passphrase(self):
|
def test_get_address_root_scheme(self):
|
||||||
mnemonic = "plastic that delay conduct police ticket swim gospel intact harsh obtain entire"
|
mnemonic = "all all all all all all all all all all all all"
|
||||||
root_node = bip32.from_mnemonic_cardano(mnemonic)
|
|
||||||
|
|
||||||
self.assertEqual(hexlify(_derive_hd_passphrase(root_node)).decode('utf8'), "8ee689a22e1ec569d2ada515c4ee712ad089901b7fe0afb94fe196de944ee814")
|
|
||||||
|
|
||||||
def test_encrypt_derivation_path(self):
|
|
||||||
encrypted_path = _encrypt_derivation_path([0x80000000, 0x80000000], unhexlify("8ee689a22e1ec569d2ada515c4ee712ad089901b7fe0afb94fe196de944ee814"))
|
|
||||||
self.assertEqual(hexlify(encrypted_path).decode('utf8'), "722c7a75813fafde9ff9e6d4dec19adfd57f0d20194fa4c703770020")
|
|
||||||
|
|
||||||
encrypted_path = _encrypt_derivation_path([0x80000000, 0], unhexlify("8ee689a22e1ec569d2ada515c4ee712ad089901b7fe0afb94fe196de944ee814"))
|
|
||||||
self.assertEqual(hexlify(encrypted_path).decode('utf8'), "722c7a75813fb5a13d916748b3fb0561c5c7b59f9bc644ea")
|
|
||||||
|
|
||||||
def test_get_address_root(self):
|
|
||||||
mnemonic = "plastic that delay conduct police ticket swim gospel intact harsh obtain entire"
|
|
||||||
root_node = bip32.from_mnemonic_cardano(mnemonic)
|
root_node = bip32.from_mnemonic_cardano(mnemonic)
|
||||||
|
|
||||||
address_root = _get_address_root(root_node, {1: b'X\x1cr,zu\x81?\xaf\xde\x9f\xf9\xe4\xd4\x90\xadH$\xe9\xf3\x88\x16\xcb\xd2)\x02M\x0c#\xde'})
|
address_root = _get_address_root(root_node, {1: b'X\x1cr,zu\x81?\xaf\xde\x9f\xf9\xe4\xd4\x90\xadH$\xe9\xf3\x88\x16\xcb\xd2)\x02M\x0c#\xde'})
|
||||||
self.assertEqual(address_root, b'\xca\x9bbQ\xa5\xaa}\x01U\xba\xe5\xa5\xaa~\x84M\x0b;\x1dM\xd8z\xe7Y\x01\xc8\x92\x91')
|
self.assertEqual(address_root, b'\xb3\xbbS\xa8;uN:E=\xe8\xe5\x9c\x18\xbcn\xcf\xd0c\xba\x0e\xba\xaelL}\xba\xbb')
|
||||||
|
|
||||||
def test_address_hash(self):
|
|
||||||
data = [0, [0, b"}\x1d\xe3\xf2/S\x90M\x00\x7f\xf83\xfa\xdd|\xd6H.\xa1\xe89\x18\xb9\x85\xb4\xea3\xe6<\x16\xd1\x83z\x04\xa6\xaa\xb0\xed\x12\xafV*&\xdbM\x104DT'M\x0b\xfan5\x81\xdf\x1d\xc0/\x13\xc5\xfb\xe5"], {}]
|
|
||||||
result = _address_hash(data)
|
|
||||||
|
|
||||||
self.assertEqual(result, b'\x1c\xca\xee\xc9\x80\xaf}\xb0\x9a\xa8\x96E\xd6\xa4\xd1\xb4\x13\x85\xb9\xc2q\x1d5/{\x12"\xca')
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
@ -6,8 +6,8 @@ from ubinascii import hexlify
|
|||||||
|
|
||||||
|
|
||||||
class TestCardanoGetPublicKey(unittest.TestCase):
|
class TestCardanoGetPublicKey(unittest.TestCase):
|
||||||
def test_get_public_key(self):
|
def test_get_public_key_scheme(self):
|
||||||
mnemonic = "plastic that delay conduct police ticket swim gospel intact harsh obtain entire"
|
mnemonic = "all all all all all all all all all all all all"
|
||||||
node = bip32.from_mnemonic_cardano(mnemonic)
|
node = bip32.from_mnemonic_cardano(mnemonic)
|
||||||
|
|
||||||
derivation_paths = [
|
derivation_paths = [
|
||||||
@ -17,27 +17,27 @@ class TestCardanoGetPublicKey(unittest.TestCase):
|
|||||||
[0x80000000 | 44, 0x80000000 | 1815, 0x80000000, 0, 0],
|
[0x80000000 | 44, 0x80000000 | 1815, 0x80000000, 0, 0],
|
||||||
]
|
]
|
||||||
|
|
||||||
root_hd_passphrase = '8ee689a22e1ec569d2ada515c4ee712ad089901b7fe0afb94fe196de944ee814'
|
root_hd_passphrase = None
|
||||||
|
|
||||||
public_keys = [
|
public_keys = [
|
||||||
'2df46e04ebf0816e242bfaa1c73e5ebe8863d05d7a96c8aac16f059975e63f30',
|
'a938c8554ae04616cfaae7cd0eb557475082c4e910242ce774967e0bd7492408',
|
||||||
'7d1de3f22f53904d007ff833fadd7cd6482ea1e83918b985b4ea33e63c16d183',
|
'8c47ebce34234d04fd3dfbac33feaba6133e4e3d77c4b5ab18120ec6878ad4ce',
|
||||||
'f59a28d704df090d8fc641248bdb27d0d001da13ddb332a79cfba8a9fa7233e7',
|
'17cc0bf978756d0d5c76f931629036a810c61801b78beecb44555773d13e3791',
|
||||||
'723fdc0eb1300fe7f2b9b6989216a831835a88695ba2c2d5c50c8470b7d1b239',
|
'b90fb812a2268e9569ff1172e8daed1da3dc7e72c7bded7c5bcb7282039f90d5',
|
||||||
]
|
]
|
||||||
|
|
||||||
chain_codes = [
|
chain_codes = [
|
||||||
'057658de1308930ad4a5663e4f77477014b04954a9d488e62d73b04fc659a35c',
|
'cbf6ab47c8eb1a0477fc40b25dbb6c4a99454edb97d6fe5acedd3e238ef46fe0',
|
||||||
'7a04a6aab0ed12af562a26db4d10344454274d0bfa6e3581df1dc02f13c5fbe5',
|
'02ac67c59a8b0264724a635774ca2c242afa10d7ab70e2bf0a8f7d4bb10f1f7a',
|
||||||
'7f01fc65468ed420e135535261b03845d97b9098f8f08245197c9526d80994f6',
|
'646ac4a6295326bae6831be05921edfbcb362de48dfd37b12e74c227dfad768d',
|
||||||
'ae09010e921de259b02f34ce7fd76f9c09ad224d436fe8fa38aa212177937ffe',
|
'fd8e71c1543de2cdc7f7623130c5f2cceb53549055fa1f5bc88199989e08cce7',
|
||||||
]
|
]
|
||||||
|
|
||||||
xpub_keys = [
|
xpub_keys = [
|
||||||
'2df46e04ebf0816e242bfaa1c73e5ebe8863d05d7a96c8aac16f059975e63f30057658de1308930ad4a5663e4f77477014b04954a9d488e62d73b04fc659a35c',
|
'a938c8554ae04616cfaae7cd0eb557475082c4e910242ce774967e0bd7492408cbf6ab47c8eb1a0477fc40b25dbb6c4a99454edb97d6fe5acedd3e238ef46fe0',
|
||||||
'7d1de3f22f53904d007ff833fadd7cd6482ea1e83918b985b4ea33e63c16d1837a04a6aab0ed12af562a26db4d10344454274d0bfa6e3581df1dc02f13c5fbe5',
|
'8c47ebce34234d04fd3dfbac33feaba6133e4e3d77c4b5ab18120ec6878ad4ce02ac67c59a8b0264724a635774ca2c242afa10d7ab70e2bf0a8f7d4bb10f1f7a',
|
||||||
'f59a28d704df090d8fc641248bdb27d0d001da13ddb332a79cfba8a9fa7233e77f01fc65468ed420e135535261b03845d97b9098f8f08245197c9526d80994f6',
|
'17cc0bf978756d0d5c76f931629036a810c61801b78beecb44555773d13e3791646ac4a6295326bae6831be05921edfbcb362de48dfd37b12e74c227dfad768d',
|
||||||
'723fdc0eb1300fe7f2b9b6989216a831835a88695ba2c2d5c50c8470b7d1b239ae09010e921de259b02f34ce7fd76f9c09ad224d436fe8fa38aa212177937ffe',
|
'b90fb812a2268e9569ff1172e8daed1da3dc7e72c7bded7c5bcb7282039f90d5fd8e71c1543de2cdc7f7623130c5f2cceb53549055fa1f5bc88199989e08cce7',
|
||||||
]
|
]
|
||||||
|
|
||||||
for index, derivation_path in enumerate(derivation_paths):
|
for index, derivation_path in enumerate(derivation_paths):
|
||||||
|
2
vendor/trezor-common
vendored
2
vendor/trezor-common
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 843facd2c18b22cefea2ba6e14cbad509d91988f
|
Subproject commit ab58324dc034328dc00cd14691d6bb86fe8ecec5
|
Loading…
Reference in New Issue
Block a user