1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-11-22 07:28:10 +00:00

cardano: replace derivation scheme v1 for v2 (#342)

This commit is contained in:
Dušan Plavák 2018-09-07 18:07:15 +01:00 committed by Pavol Rusnak
parent 7128337031
commit 060a8cbdfd
10 changed files with 133 additions and 142 deletions

View File

@ -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){

View File

@ -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")

View File

@ -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

View File

@ -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(),

View File

@ -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
) )

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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):

@ -1 +1 @@
Subproject commit 843facd2c18b22cefea2ba6e14cbad509d91988f Subproject commit ab58324dc034328dc00cd14691d6bb86fe8ecec5