From 0ac032917b502456c28bdb4a703c08b41e1c62fb Mon Sep 17 00:00:00 2001 From: Roman Zeyde Date: Wed, 3 Jun 2015 22:21:16 +0300 Subject: [PATCH] enable Trezor to perform SSH public key authentication support both NIST256P1 and SECP256K1 ECDSA curves. --- .gitmodules | 4 +- firmware-docker-build.sh | 4 +- firmware/Makefile | 1 + firmware/crypto.c | 35 +++++++++------ firmware/crypto.h | 4 +- firmware/fsm.c | 75 +++++++++++++++++++++++++------- firmware/protob/messages.options | 2 + firmware/protob/messages.pb.c | 6 ++- firmware/protob/messages.pb.h | 22 ++++++---- firmware/signing.c | 3 +- trezor-common | 2 +- trezor-crypto | 2 +- 12 files changed, 113 insertions(+), 47 deletions(-) diff --git a/.gitmodules b/.gitmodules index 97aef8fa1c..4dcf3d77e0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,9 @@ [submodule "trezor-crypto"] path = trezor-crypto - url = https://github.com/trezor/trezor-crypto.git + url = https://github.com/romanz/trezor-crypto.git [submodule "trezor-common"] path = trezor-common - url = https://github.com/trezor/trezor-common.git + url = https://github.com/romanz/trezor-common.git [submodule "trezor-qrenc"] path = trezor-qrenc url = https://github.com/trezor/trezor-qrenc.git diff --git a/firmware-docker-build.sh b/firmware-docker-build.sh index f705c8ffbd..5df459a3a5 100755 --- a/firmware-docker-build.sh +++ b/firmware-docker-build.sh @@ -1,10 +1,10 @@ #!/bin/bash IMAGETAG=trezor-mcu-build -FIRMWARETAG=${1:-master} +FIRMWARETAG="ssh-agent" docker build -t $IMAGETAG . docker run -t -v $(pwd)/output:/output $IMAGETAG /bin/sh -c "\ - git clone https://github.com/trezor/trezor-mcu && \ + git clone https://github.com/romanz/trezor-mcu && \ cd trezor-mcu && \ git checkout $FIRMWARETAG && \ git submodule update --init && \ diff --git a/firmware/Makefile b/firmware/Makefile index 74a642814e..fa4aa3e16a 100644 --- a/firmware/Makefile +++ b/firmware/Makefile @@ -22,6 +22,7 @@ OBJS += debug.o OBJS += ../trezor-crypto/bignum.o OBJS += ../trezor-crypto/ecdsa.o OBJS += ../trezor-crypto/secp256k1.o +OBJS += ../trezor-crypto/nist256p1.o OBJS += ../trezor-crypto/hmac.o OBJS += ../trezor-crypto/bip32.o OBJS += ../trezor-crypto/bip39.o diff --git a/firmware/crypto.c b/firmware/crypto.c index a78aa4002e..3470159dfc 100644 --- a/firmware/crypto.c +++ b/firmware/crypto.c @@ -20,12 +20,13 @@ #include #include "crypto.h" #include "sha2.h" -#include "ecdsa.h" #include "pbkdf2.h" #include "aes.h" #include "hmac.h" #include "bip32.h" #include "layout.h" +#include "secp256k1.h" +#include "nist256p1.h" uint32_t ser_length(uint32_t len, uint8_t *out) { @@ -83,6 +84,12 @@ uint32_t deser_length(const uint8_t *in, uint32_t *out) return 1 + 8; } +int sshMessageSign(const uint8_t *message, size_t message_len, const uint8_t *privkey, uint8_t *signature) +{ + signature[0] = 0; // prefix: pad with zero, so all signatures are 65 bytes + return ecdsa_sign(&nist256p1, privkey, message, message_len, signature + 1, NULL); +} + int cryptoMessageSign(const uint8_t *message, size_t message_len, const uint8_t *privkey, uint8_t *signature) { SHA256_CTX ctx; @@ -96,7 +103,7 @@ int cryptoMessageSign(const uint8_t *message, size_t message_len, const uint8_t sha256_Final(hash, &ctx); sha256_Raw(hash, 32, hash); uint8_t pby; - ecdsa_sign_digest(privkey, hash, signature + 1, &pby); + ecdsa_sign_digest(&secp256k1, privkey, hash, signature + 1, &pby); signature[0] = 27 + pby + 4; return 0; } @@ -124,7 +131,7 @@ int cryptoMessageVerify(const uint8_t *message, size_t message_len, const uint8_ // x = r memcpy(&cp.x, &r, sizeof(bignum256)); // compute y from x - uncompress_coords(recid % 2, &cp.x, &cp.y); + uncompress_coords(&secp256k1, recid % 2, &cp.x, &cp.y); // calculate hash sha256_Init(&ctx); sha256_Update(&ctx, (const uint8_t *)"\x18" "Bitcoin Signed Message:" "\n", 25); @@ -136,13 +143,13 @@ int cryptoMessageVerify(const uint8_t *message, size_t message_len, const uint8_ sha256_Raw(hash, 32, hash); // e = -hash bn_read_be(hash, &e); - bn_subtract(&order256k1, &e, &e); + bn_subtract(&secp256k1.order, &e, &e); // r = r^-1 - bn_inverse(&r, &order256k1); - point_multiply(&s, &cp, &cp); - scalar_multiply(&e, &cp2); - point_add(&cp2, &cp); - point_multiply(&r, &cp, &cp); + bn_inverse(&r, &secp256k1.order); + point_multiply(&secp256k1, &s, &cp, &cp); + scalar_multiply(&secp256k1, &e, &cp2); + point_add(&secp256k1, &cp2, &cp); + point_multiply(&secp256k1, &r, &cp, &cp); pubkey[0] = 0x04; bn_write_be(&cp.x, pubkey + 1); bn_write_be(&cp.y, pubkey + 33); @@ -155,7 +162,7 @@ int cryptoMessageVerify(const uint8_t *message, size_t message_len, const uint8_ return 2; } // check if signature verifies the digest - if (ecdsa_verify_digest(pubkey, signature + 1, hash) != 0) { + if (ecdsa_verify_digest(&secp256k1, pubkey, signature + 1, hash) != 0) { return 3; } return 0; @@ -181,16 +188,16 @@ int cryptoMessageEncrypt(curve_point *pubkey, const uint8_t *msg, size_t msg_siz // generate random nonce curve_point R; bignum256 k; - if (generate_k_random(&k) != 0) { + if (generate_k_random(&secp256k1, &k) != 0) { return 2; } // compute k*G - scalar_multiply(&k, &R); + scalar_multiply(&secp256k1, &k, &R); nonce[0] = 0x02 | (R.y.val[0] & 0x01); bn_write_be(&R.x, nonce + 1); *nonce_len = 33; // compute shared secret - point_multiply(&k, pubkey, &R); + point_multiply(&secp256k1, &k, pubkey, &R); uint8_t shared_secret[33]; shared_secret[0] = 0x02 | (R.y.val[0] & 0x01); bn_write_be(&R.x, shared_secret + 1); @@ -222,7 +229,7 @@ int cryptoMessageDecrypt(curve_point *nonce, uint8_t *payload, size_t payload_le curve_point R; bignum256 k; bn_read_be(privkey, &k); - point_multiply(&k, nonce, &R); + point_multiply(&secp256k1, &k, nonce, &R); uint8_t shared_secret[33]; shared_secret[0] = 0x02 | (R.y.val[0] & 0x01); bn_write_be(&R.x, shared_secret + 1); diff --git a/firmware/crypto.h b/firmware/crypto.h index 89878003c7..b9d03fde8a 100644 --- a/firmware/crypto.h +++ b/firmware/crypto.h @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include #include "types.pb.h" @@ -32,6 +32,8 @@ uint32_t ser_length(uint32_t len, uint8_t *out); uint32_t ser_length_hash(SHA256_CTX *ctx, uint32_t len); +int sshMessageSign(const uint8_t *message, size_t message_len, const uint8_t *privkey, uint8_t *signature); + int cryptoMessageSign(const uint8_t *message, size_t message_len, const uint8_t *privkey, uint8_t *signature); int cryptoMessageVerify(const uint8_t *message, size_t message_len, const uint8_t *address_raw, const uint8_t *signature); diff --git a/firmware/fsm.c b/firmware/fsm.c index 332733d5a5..a3d6e314f6 100644 --- a/firmware/fsm.c +++ b/firmware/fsm.c @@ -44,6 +44,8 @@ #include "base58.h" #include "bip39.h" #include "ripemd160.h" +#include "secp256k1.h" +#include "nist256p1.h" // message methods @@ -285,6 +287,17 @@ void fsm_msgGetPublicKey(GetPublicKey *msg) const HDNode *node = fsm_getDerivedNode(msg->address_n, msg->address_n_count); if (!node) return; + uint8_t public_key[33]; // copy public key to temporary buffer + memcpy(public_key, node->public_key, sizeof(public_key)); + + if (msg->has_ecdsa_curve_name) { + const ecdsa_curve *curve = get_curve_by_name(msg->ecdsa_curve_name); + if (curve) { + // correct public key (since fsm_getDerivedNode uses secp256k1 curve) + ecdsa_get_public_key33(curve, node->private_key, public_key); + } + } + resp->node.depth = node->depth; resp->node.fingerprint = node->fingerprint; resp->node.child_num = node->child_num; @@ -293,7 +306,7 @@ void fsm_msgGetPublicKey(GetPublicKey *msg) resp->node.has_private_key = false; resp->node.has_public_key = true; resp->node.public_key.size = 33; - memcpy(resp->node.public_key.bytes, node->public_key, 33); + memcpy(resp->node.public_key.bytes, public_key, 33); resp->has_xpub = true; hdnode_serialize_public(node, resp->xpub, sizeof(resp->xpub)); msg_write(MessageType_MessageType_PublicKey, resp); @@ -662,6 +675,7 @@ void fsm_msgSignIdentity(SignIdentity *msg) layoutHome(); return; } + uint32_t address_n[5]; address_n[0] = 0x80000000 | 13; address_n[1] = 0x80000000 | hash[ 0] | (hash[ 1] << 8) | (hash[ 2] << 16) | (hash[ 3] << 24); @@ -672,20 +686,51 @@ void fsm_msgSignIdentity(SignIdentity *msg) const HDNode *node = fsm_getDerivedNode(address_n, 5); if (!node) return; - uint8_t message[256 + 256]; - memcpy(message, msg->challenge_hidden.bytes, msg->challenge_hidden.size); - const int len = strlen(msg->challenge_visual); - memcpy(message + msg->challenge_hidden.size, msg->challenge_visual, len); + uint8_t public_key[33]; // copy public key to temporary buffer + memcpy(public_key, node->public_key, sizeof(public_key)); - layoutProgressSwipe("Signing", 0); - if (cryptoMessageSign(message, msg->challenge_hidden.size + len, node->private_key, resp->signature.bytes) == 0) { - resp->has_address = true; - uint8_t addr_raw[21]; - ecdsa_get_address_raw(node->public_key, 0x00, addr_raw); // hardcoded Bitcoin address type - base58_encode_check(addr_raw, 21, resp->address, sizeof(resp->address)); + if (msg->has_ecdsa_curve_name) { + const ecdsa_curve *curve = get_curve_by_name(msg->ecdsa_curve_name); + if (curve) { + // correct public key (since fsm_getDerivedNode uses secp256k1 curve) + ecdsa_get_public_key33(curve, node->private_key, public_key); + } + } + + bool sign_ssh = false; + if (msg->identity.has_proto) { + sign_ssh = (strcmp(msg->identity.proto, "ssh") == 0); + } + + uint8_t message_bytes[256 + 256]; + memcpy(message_bytes, msg->challenge_hidden.bytes, msg->challenge_hidden.size); + int message_size = msg->challenge_hidden.size; + + int result = 0; + if (sign_ssh) { + // SSH doesn't sign visual challenge. + layoutProgressSwipe("Signing SSH", 0); + result = sshMessageSign(message_bytes, message_size, node->private_key, resp->signature.bytes); + } else { + const int len = strlen(msg->challenge_visual); + memcpy(message_bytes + message_size, msg->challenge_visual, len); + message_size = message_size + len; + layoutProgressSwipe("Signing", 0); + result = cryptoMessageSign(message_bytes, message_size, node->private_key, resp->signature.bytes); + } + + if (result == 0) { + if (sign_ssh) { + resp->has_address = false; + } else { + resp->has_address = true; + uint8_t addr_raw[21]; + ecdsa_get_address_raw(node->public_key, 0x00, addr_raw); // hardcoded Bitcoin address type + base58_encode_check(addr_raw, 21, resp->address, sizeof(resp->address)); + } resp->has_public_key = true; resp->public_key.size = 33; - memcpy(resp->public_key.bytes, node->public_key, 33); + memcpy(resp->public_key.bytes, public_key, 33); resp->has_signature = true; resp->signature.size = 65; msg_write(MessageType_MessageType_SignedIdentity, resp); @@ -706,7 +751,7 @@ void fsm_msgEncryptMessage(EncryptMessage *msg) return; } curve_point pubkey; - if (msg->pubkey.size != 33 || ecdsa_read_pubkey(msg->pubkey.bytes, &pubkey) == 0) { + if (msg->pubkey.size != 33 || ecdsa_read_pubkey(&secp256k1, msg->pubkey.bytes, &pubkey) == 0) { fsm_sendFailure(FailureType_Failure_SyntaxError, "Invalid public key provided"); return; } @@ -729,7 +774,7 @@ void fsm_msgEncryptMessage(EncryptMessage *msg) node = fsm_getDerivedNode(msg->address_n, msg->address_n_count); if (!node) return; uint8_t public_key[33]; - ecdsa_get_public_key33(node->private_key, public_key); + ecdsa_get_public_key33(&secp256k1, node->private_key, public_key); ecdsa_get_address_raw(public_key, coin->address_type, address_raw); } layoutEncryptMessage(msg->message.bytes, msg->message.size, signing); @@ -766,7 +811,7 @@ void fsm_msgDecryptMessage(DecryptMessage *msg) return; } curve_point nonce_pubkey; - if (msg->nonce.size != 33 || ecdsa_read_pubkey(msg->nonce.bytes, &nonce_pubkey) == 0) { + if (msg->nonce.size != 33 || ecdsa_read_pubkey(&secp256k1, msg->nonce.bytes, &nonce_pubkey) == 0) { fsm_sendFailure(FailureType_Failure_SyntaxError, "Invalid nonce provided"); return; } diff --git a/firmware/protob/messages.options b/firmware/protob/messages.options index a322f98601..1a4b194baa 100644 --- a/firmware/protob/messages.options +++ b/firmware/protob/messages.options @@ -25,6 +25,7 @@ PassphraseAck.passphrase max_size:51 Entropy.entropy max_size:1024 GetPublicKey.address_n max_count:8 +GetPublicKey.ecdsa_curve_name max_size:32 PublicKey.xpub max_size:113 @@ -88,6 +89,7 @@ SignTx.coin_name max_size:17 SignIdentity.challenge_hidden max_size:256 SignIdentity.challenge_visual max_size:256 +SignIdentity.ecdsa_curve_name max_size:32 SignedIdentity.address max_size:36 SignedIdentity.public_key max_size:33 diff --git a/firmware/protob/messages.pb.c b/firmware/protob/messages.pb.c index 8823d70f96..c5737d6e8a 100644 --- a/firmware/protob/messages.pb.c +++ b/firmware/protob/messages.pb.c @@ -123,8 +123,9 @@ const pb_field_t Entropy_fields[2] = { PB_LAST_FIELD }; -const pb_field_t GetPublicKey_fields[2] = { +const pb_field_t GetPublicKey_fields[3] = { PB_FIELD2( 1, UINT32 , REPEATED, STATIC , FIRST, GetPublicKey, address_n, address_n, 0), + PB_FIELD2( 2, STRING , OPTIONAL, STATIC , OTHER, GetPublicKey, ecdsa_curve_name, address_n, 0), PB_LAST_FIELD }; @@ -304,10 +305,11 @@ const pb_field_t TxAck_fields[2] = { PB_LAST_FIELD }; -const pb_field_t SignIdentity_fields[4] = { +const pb_field_t SignIdentity_fields[5] = { PB_FIELD2( 1, MESSAGE , OPTIONAL, STATIC , FIRST, SignIdentity, identity, identity, &IdentityType_fields), PB_FIELD2( 2, BYTES , OPTIONAL, STATIC , OTHER, SignIdentity, challenge_hidden, identity, 0), PB_FIELD2( 3, STRING , OPTIONAL, STATIC , OTHER, SignIdentity, challenge_visual, challenge_hidden, 0), + PB_FIELD2( 4, STRING , OPTIONAL, STATIC , OTHER, SignIdentity, ecdsa_curve_name, challenge_visual, 0), PB_LAST_FIELD }; diff --git a/firmware/protob/messages.pb.h b/firmware/protob/messages.pb.h index b867cc947d..ea39b1c597 100644 --- a/firmware/protob/messages.pb.h +++ b/firmware/protob/messages.pb.h @@ -417,6 +417,8 @@ typedef struct _GetEntropy { typedef struct _GetPublicKey { size_t address_n_count; uint32_t address_n[8]; + bool has_ecdsa_curve_name; + char ecdsa_curve_name[32]; } GetPublicKey; typedef struct _LoadDevice { @@ -520,6 +522,8 @@ typedef struct _SignIdentity { SignIdentity_challenge_hidden_t challenge_hidden; bool has_challenge_visual; char challenge_visual[256]; + bool has_ecdsa_curve_name; + char ecdsa_curve_name[32]; } SignIdentity; typedef struct { @@ -650,7 +654,7 @@ extern const char SimpleSignTx_coin_name_default[17]; #define PassphraseAck_init_default {""} #define GetEntropy_init_default {0} #define Entropy_init_default {{0, {0}}} -#define GetPublicKey_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}} +#define GetPublicKey_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}, false, ""} #define PublicKey_init_default {HDNodeType_init_default, false, ""} #define GetAddress_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}, false, "Bitcoin", false, 0, false, MultisigRedeemScriptType_init_default} #define Address_init_default {""} @@ -677,7 +681,7 @@ extern const char SimpleSignTx_coin_name_default[17]; #define SimpleSignTx_init_default {0, {}, 0, {}, 0, {}, false, "Bitcoin"} #define TxRequest_init_default {false, (RequestType)0, false, TxRequestDetailsType_init_default, false, TxRequestSerializedType_init_default} #define TxAck_init_default {false, TransactionType_init_default} -#define SignIdentity_init_default {false, IdentityType_init_default, false, {0, {0}}, false, ""} +#define SignIdentity_init_default {false, IdentityType_init_default, false, {0, {0}}, false, "", false, ""} #define SignedIdentity_init_default {false, "", false, {0, {0}}, false, {0, {0}}} #define FirmwareErase_init_default {0} #define FirmwareUpload_init_default {{0, {0}}} @@ -704,7 +708,7 @@ extern const char SimpleSignTx_coin_name_default[17]; #define PassphraseAck_init_zero {""} #define GetEntropy_init_zero {0} #define Entropy_init_zero {{0, {0}}} -#define GetPublicKey_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}} +#define GetPublicKey_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}, false, ""} #define PublicKey_init_zero {HDNodeType_init_zero, false, ""} #define GetAddress_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}, false, "", false, 0, false, MultisigRedeemScriptType_init_zero} #define Address_init_zero {""} @@ -731,7 +735,7 @@ extern const char SimpleSignTx_coin_name_default[17]; #define SimpleSignTx_init_zero {0, {}, 0, {}, 0, {}, false, ""} #define TxRequest_init_zero {false, (RequestType)0, false, TxRequestDetailsType_init_zero, false, TxRequestSerializedType_init_zero} #define TxAck_init_zero {false, TransactionType_init_zero} -#define SignIdentity_init_zero {false, IdentityType_init_zero, false, {0, {0}}, false, ""} +#define SignIdentity_init_zero {false, IdentityType_init_zero, false, {0, {0}}, false, "", false, ""} #define SignedIdentity_init_zero {false, "", false, {0, {0}}, false, {0, {0}}} #define FirmwareErase_init_zero {0} #define FirmwareUpload_init_zero {{0, {0}}} @@ -816,6 +820,7 @@ extern const char SimpleSignTx_coin_name_default[17]; #define GetAddress_multisig_tag 4 #define GetEntropy_size_tag 1 #define GetPublicKey_address_n_tag 1 +#define GetPublicKey_ecdsa_curve_name_tag 2 #define LoadDevice_mnemonic_tag 1 #define LoadDevice_node_tag 2 #define LoadDevice_pin_tag 3 @@ -849,6 +854,7 @@ extern const char SimpleSignTx_coin_name_default[17]; #define SignIdentity_identity_tag 1 #define SignIdentity_challenge_hidden_tag 2 #define SignIdentity_challenge_visual_tag 3 +#define SignIdentity_ecdsa_curve_name_tag 4 #define SignMessage_address_n_tag 1 #define SignMessage_message_tag 2 #define SignMessage_coin_name_tag 3 @@ -892,7 +898,7 @@ extern const pb_field_t PassphraseRequest_fields[1]; extern const pb_field_t PassphraseAck_fields[2]; extern const pb_field_t GetEntropy_fields[2]; extern const pb_field_t Entropy_fields[2]; -extern const pb_field_t GetPublicKey_fields[2]; +extern const pb_field_t GetPublicKey_fields[3]; extern const pb_field_t PublicKey_fields[3]; extern const pb_field_t GetAddress_fields[5]; extern const pb_field_t Address_fields[2]; @@ -919,7 +925,7 @@ extern const pb_field_t SignTx_fields[4]; extern const pb_field_t SimpleSignTx_fields[5]; extern const pb_field_t TxRequest_fields[4]; extern const pb_field_t TxAck_fields[2]; -extern const pb_field_t SignIdentity_fields[4]; +extern const pb_field_t SignIdentity_fields[5]; extern const pb_field_t SignedIdentity_fields[4]; extern const pb_field_t FirmwareErase_fields[1]; extern const pb_field_t FirmwareUpload_fields[2]; @@ -948,7 +954,7 @@ extern const pb_field_t DebugLinkLog_fields[4]; #define PassphraseAck_size 53 #define GetEntropy_size 6 #define Entropy_size 1027 -#define GetPublicKey_size 48 +#define GetPublicKey_size 82 #define PublicKey_size (121 + HDNodeType_size) #define GetAddress_size (75 + MultisigRedeemScriptType_size) #define Address_size 38 @@ -975,7 +981,7 @@ extern const pb_field_t DebugLinkLog_fields[4]; #define SimpleSignTx_size (19 + 0*TxInputType_size + 0*TxOutputType_size + 0*TransactionType_size) #define TxRequest_size (18 + TxRequestDetailsType_size + TxRequestSerializedType_size) #define TxAck_size (6 + TransactionType_size) -#define SignIdentity_size (524 + IdentityType_size) +#define SignIdentity_size (558 + IdentityType_size) #define SignedIdentity_size 140 #define FirmwareErase_size 0 #define FirmwareUpload_size 2 diff --git a/firmware/signing.c b/firmware/signing.c index ca6a6682df..289315bc37 100644 --- a/firmware/signing.c +++ b/firmware/signing.c @@ -25,6 +25,7 @@ #include "ecdsa.h" #include "protect.h" #include "crypto.h" +#include "secp256k1.h" static uint32_t inputs_count; static uint32_t outputs_count; @@ -536,7 +537,7 @@ void signing_txack(TransactionType *tx) resp.serialized.signature_index = idx1; resp.serialized.has_signature = true; resp.serialized.has_serialized_tx = true; - ecdsa_sign_digest(privkey, hash, sig, 0); + ecdsa_sign_digest(&secp256k1, privkey, hash, sig, 0); resp.serialized.signature.size = ecdsa_sig_to_der(sig, resp.serialized.signature.bytes); if (input.script_type == InputScriptType_SPENDMULTISIG) { if (!input.has_multisig) { diff --git a/trezor-common b/trezor-common index e96ec085d5..12288143f5 160000 --- a/trezor-common +++ b/trezor-common @@ -1 +1 @@ -Subproject commit e96ec085d55c20eccecb47e0a55b33295164de6d +Subproject commit 12288143f563cc51e4ae1d990de08db3add87380 diff --git a/trezor-crypto b/trezor-crypto index a757693fe3..7c58fc11a4 160000 --- a/trezor-crypto +++ b/trezor-crypto @@ -1 +1 @@ -Subproject commit a757693fe3904f60d80d36fe0bfe437dea55e02e +Subproject commit 7c58fc11a46361476e3a7862816adb0671de6a74