diff --git a/firmware/Makefile b/firmware/Makefile index 710c36f4d..5f9759115 100644 --- a/firmware/Makefile +++ b/firmware/Makefile @@ -15,6 +15,7 @@ OBJS += layout2.o OBJS += recovery.o OBJS += reset.o OBJS += signing.o +OBJS += crypto.o OBJS += debug.o diff --git a/firmware/crypto.c b/firmware/crypto.c new file mode 100644 index 000000000..8aea82658 --- /dev/null +++ b/firmware/crypto.c @@ -0,0 +1,263 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * 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 GNU Lesser General Public License + * along with this library. If not, see . + */ + +#include +#include "crypto.h" +#include "sha2.h" +#include "ecdsa.h" +#include "pbkdf2.h" +#include "aes.h" +#include "hmac.h" + +uint32_t ser_length(uint32_t len, uint8_t *out) +{ + if (len < 253) { + out[0] = len & 0xFF; + return 1; + } + if (len < 0x10000) { + out[0] = 253; + out[1] = len & 0xFF; + out[2] = (len >> 8) & 0xFF; + return 3; + } + out[0] = 254; + out[1] = len & 0xFF; + out[2] = (len >> 8) & 0xFF; + out[3] = (len >> 16) & 0xFF; + out[4] = (len >> 24) & 0xFF; + return 5; +} + +uint32_t deser_length(const uint8_t *in, uint32_t *out) +{ + if (in[0] < 253) { + *out = in[0]; + return 1; + } + if (in[0] == 253) { + *out = in[1] + (in[2] << 8); + return 1 + 2; + } + if (in[0] == 254) { + *out = in[1] + (in[2] << 8) + (in[3] << 16) + (in[4] << 24); + return 1 + 4; + } + *out = 0; // ignore 64 bit + return 1 + 8; +} + +int cryptoMessageSign(const uint8_t *message, pb_size_t message_len, const uint8_t *privkey, const uint8_t *address_raw, uint8_t *signature) +{ + SHA256_CTX ctx; + sha256_Init(&ctx); + sha256_Update(&ctx, (const uint8_t *)"\x18" "Bitcoin Signed Message:" "\n", 25); + uint8_t varint[5]; + uint32_t l = ser_length(message_len, varint); + sha256_Update(&ctx, varint, l); + sha256_Update(&ctx, message, message_len); + uint8_t hash[32]; + sha256_Final(hash, &ctx); + sha256_Raw(hash, 32, hash); + ecdsa_sign_digest(privkey, hash, signature + 1); + uint8_t i; + for (i = 27 + 4; i < 27 + 4 + 4; i++) { + signature[0] = i; + if (cryptoMessageVerify(message, message_len, address_raw, signature) == 0) { + return 0; + } + } + return 1; +} + +int cryptoMessageVerify(const uint8_t *message, pb_size_t message_len, const uint8_t *address_raw, const uint8_t *signature) +{ + bignum256 r, s, e; + curve_point cp, cp2; + SHA256_CTX ctx; + uint8_t pubkey[65], addr_raw[21], hash[32]; + + uint8_t nV = signature[0]; + if (nV < 27 || nV >= 35) { + return 1; + } + bool compressed; + compressed = (nV >= 31); + if (compressed) { + nV -= 4; + } + uint8_t recid = nV - 27; + // read r and s + bn_read_be(signature + 1, &r); + bn_read_be(signature + 33, &s); + // x = r + (recid / 2) * order + bn_zero(&cp.x); + uint8_t i; + for (i = 0; i < recid / 2; i++) { + bn_addmod(&cp.x, &order256k1, &prime256k1); + } + bn_addmod(&cp.x, &r, &prime256k1); + // compute y from x + uncompress_coords(recid % 2, &cp.x, &cp.y); + // calculate hash + sha256_Init(&ctx); + sha256_Update(&ctx, (const uint8_t *)"\x18" "Bitcoin Signed Message:" "\n", 25); + uint8_t varint[5]; + uint32_t l = ser_length(message_len, varint); + sha256_Update(&ctx, varint, l); + sha256_Update(&ctx, message, message_len); + sha256_Final(hash, &ctx); + sha256_Raw(hash, 32, hash); + // e = -hash + bn_read_be(hash, &e); + bn_substract_noprime(&order256k1, &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); + pubkey[0] = 0x04; + bn_write_be(&cp.x, pubkey + 1); + bn_write_be(&cp.y, pubkey + 33); + // check if the address is correct + if (compressed) { + pubkey[0] = 0x02 | (cp.y.val[0] & 0x01); + } + ecdsa_get_address_raw(pubkey, address_raw[0], addr_raw); + if (memcmp(addr_raw, address_raw, 21) != 0) { + return 2; + } + // check if signature verifies the digest + if (ecdsa_verify_digest(pubkey, signature + 1, hash) != 0) { + return 3; + } + return 0; +} + +// internal from ecdsa.c +int generate_k_random(bignum256 *k); + +int cryptoMessageEncrypt(curve_point *pubkey, const uint8_t *msg, pb_size_t msg_size, bool display_only, uint8_t *nonce, pb_size_t *nonce_len, uint8_t *payload, pb_size_t *payload_len, uint8_t *hmac, pb_size_t *hmac_len, const uint8_t *privkey, const uint8_t *address_raw) +{ + if (privkey && address_raw) { // signing == true + payload[0] = display_only ? 0x81 : 0x01; + uint32_t l = ser_length(msg_size, payload + 1); + memcpy(payload + 1 + l, msg, msg_size); + memcpy(payload + 1 + l + msg_size, address_raw, 21); + if (cryptoMessageSign(msg, msg_size, privkey, address_raw, payload + 1 + l + msg_size + 21) != 0) { + return 1; + } + *payload_len = 1 + l + msg_size + 21 + 65; + } else { + payload[0] = display_only ? 0x80 : 0x00; + uint32_t l = ser_length(msg_size, payload + 1); + memcpy(payload + 1 + l, msg, msg_size); + *payload_len = 1 + l + msg_size; + } + // generate random nonce + curve_point R; + bignum256 k; + if (generate_k_random(&k) != 0) { + return 2; + } + // compute k*G + scalar_multiply(&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); + uint8_t shared_secret[33]; + shared_secret[0] = 0x02 | (R.y.val[0] & 0x01); + bn_write_be(&R.x, shared_secret + 1); + // generate keying bytes + uint8_t keying_bytes[80]; + uint8_t salt[22 + 33 + 4]; + memcpy(salt, "Bitcoin Secure Message", 22); + memcpy(salt + 22, nonce, 33); + pbkdf2_hmac_sha256(shared_secret, 33, salt, 22 + 33, 2048, keying_bytes, 80, NULL); + // encrypt payload + aes_encrypt_ctx ctx; + aes_encrypt_key256(keying_bytes, &ctx); + aes_cfb_encrypt(payload, payload, *payload_len, keying_bytes + 64, &ctx); + // compute hmac + uint8_t out[32]; + hmac_sha256(keying_bytes + 32, 32, payload, *payload_len, out); + memcpy(hmac, out, 8); + *hmac_len = 8; + + return 0; +} + +int cryptoMessageDecrypt(curve_point *nonce, uint8_t *payload, pb_size_t payload_len, const uint8_t *hmac, pb_size_t hmac_len, const uint8_t *privkey, uint8_t *msg, pb_size_t *msg_len, bool *display_only, bool *signing, uint8_t *address_raw) +{ + if (hmac_len != 8) { + return 1; + } + // compute shared secret + curve_point R; + bignum256 k; + bn_read_be(privkey, &k); + point_multiply(&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); + // generate keying bytes + uint8_t keying_bytes[80]; + uint8_t salt[22 + 33 + 4]; + memcpy(salt, "Bitcoin Secure Message", 22); + salt[22] = 0x02 | (nonce->y.val[0] & 0x01); + bn_write_be(&(nonce->x), salt + 23); + pbkdf2_hmac_sha256(shared_secret, 33, salt, 22 + 33, 2048, keying_bytes, 80, NULL); + // compute hmac + uint8_t out[32]; + hmac_sha256(keying_bytes + 32, 32, payload, payload_len, out); + if (memcmp(hmac, out, 8) != 0) { + return 2; + } + // decrypt payload + aes_encrypt_ctx ctx; + aes_encrypt_key256(keying_bytes, &ctx); + aes_cfb_decrypt(payload, payload, payload_len, keying_bytes + 64, &ctx); + // check first byte + if (payload[0] != 0x00 && payload[0] != 0x01 && payload[0] != 0x80 && payload[0] != 0x81) { + return 3; + } + *signing = payload[0] & 0x01; + *display_only = payload[0] & 0x80; + uint32_t l, o; + l = deser_length(payload + 1, &o); + if (*signing) { + if (1 + l + o + 21 + 65 != payload_len) { + return 4; + } + if (cryptoMessageVerify(payload + 1 + l, o, payload + 1 + l + o, payload + 1 + l + o + 21) != 0) { + return 5; + } + memcpy(address_raw, payload + 1 + l + o, 21); + } else { + if (1 + l + o != payload_len) { + return 4; + } + } + memcpy(msg, payload + 1 + l, o); + *msg_len = o; + return 0; +} diff --git a/firmware/crypto.h b/firmware/crypto.h new file mode 100644 index 000000000..603c52edc --- /dev/null +++ b/firmware/crypto.h @@ -0,0 +1,41 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * 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 GNU Lesser General Public License + * along with this library. If not, see . + */ + +#ifndef __CRYPTO_H__ +#define __CRYPTO_H__ + +#include +#include +#include +#include +#include + +uint32_t ser_length(uint32_t len, uint8_t *out); + +int cryptoMessageSign(const uint8_t *message, pb_size_t message_len, const uint8_t *privkey, const uint8_t *address_raw, uint8_t *signature); + +int cryptoMessageVerify(const uint8_t *message, pb_size_t message_len, const uint8_t *address_raw, const uint8_t *signature); + +// ECIES: http://memwallet.info/btcmssgs.html + +int cryptoMessageEncrypt(curve_point *pubkey, const uint8_t *msg, pb_size_t msg_size, bool display_only, uint8_t *nonce, pb_size_t *nonce_len, uint8_t *payload, pb_size_t *payload_len, uint8_t *hmac, pb_size_t *hmac_len, const uint8_t *privkey, const uint8_t *address_raw); + +int cryptoMessageDecrypt(curve_point *nonce, uint8_t *payload, pb_size_t payload_len, const uint8_t *hmac, pb_size_t hmac_len, const uint8_t *privkey, uint8_t *msg, pb_size_t *msg_len, bool *display_only, bool *signing, uint8_t *address_raw); + +#endif diff --git a/firmware/fsm.c b/firmware/fsm.c index b91f9faf7..d1f886fd1 100644 --- a/firmware/fsm.c +++ b/firmware/fsm.c @@ -40,6 +40,8 @@ #include "signing.h" #include "aes.h" #include "hmac.h" +#include "crypto.h" +#include "base58.h" // message methods @@ -404,19 +406,19 @@ void fsm_msgCipherKeyValue(CipherKeyValue *msg) hmac_sha512(node->private_key, 32, data, strlen((char *)data), data); - RESP_INIT(Success); + RESP_INIT(CipheredKeyValue); if (encrypt) { aes_encrypt_ctx ctx; aes_encrypt_key256(data, &ctx); - aes_cbc_encrypt(msg->value.bytes, resp->payload.bytes, msg->value.size, data + 32, &ctx); + aes_cbc_encrypt(msg->value.bytes, resp->value.bytes, msg->value.size, data + 32, &ctx); } else { aes_decrypt_ctx ctx; aes_decrypt_key256(data, &ctx); - aes_cbc_decrypt(msg->value.bytes, resp->payload.bytes, msg->value.size, data + 32, &ctx); + aes_cbc_decrypt(msg->value.bytes, resp->value.bytes, msg->value.size, data + 32, &ctx); } - resp->has_payload = true; - resp->payload.size = msg->value.size; - msg_write(MessageType_MessageType_Success, resp); + resp->has_value = true; + resp->value.size = msg->value.size; + msg_write(MessageType_MessageType_CipheredKeyValue, resp); layoutHome(); } @@ -526,10 +528,11 @@ void fsm_msgSignMessage(SignMessage *msg) } fsm_deriveKey(node, msg->address_n, msg->address_n_count); - - ecdsa_get_address(node->public_key, coin->address_type, resp->address); + uint8_t addr_raw[21]; + ecdsa_get_address_raw(node->public_key, coin->address_type, addr_raw); + base58_encode_check(addr_raw, 21, resp->address); layoutProgressSwipe("Signing", 0, 0); - if (transactionMessageSign(msg->message.bytes, msg->message.size, node->private_key, resp->address, resp->signature.bytes)) { + if (cryptoMessageSign(msg->message.bytes, msg->message.size, node->private_key, addr_raw, resp->signature.bytes) == 0) { resp->has_address = true; resp->has_signature = true; resp->signature.size = 65; @@ -542,9 +545,20 @@ void fsm_msgSignMessage(SignMessage *msg) void fsm_msgVerifyMessage(VerifyMessage *msg) { - const char *address = msg->has_address ? msg->address : 0; + if (!msg->has_address) { + fsm_sendFailure(FailureType_Failure_Other, "No address provided"); + return; + } + if (!msg->has_message) { + fsm_sendFailure(FailureType_Failure_Other, "No message provided"); + return; + } layoutProgressSwipe("Verifying", 0, 0); - if (msg->signature.size == 65 && transactionMessageVerify(msg->message.bytes, msg->message.size, msg->signature.bytes, address)) { + uint8_t addr_raw[21]; + if (!ecdsa_address_decode(msg->address, addr_raw)) { + fsm_sendFailure(FailureType_Failure_InvalidSignature, "Invalid address"); + } + if (msg->signature.size == 65 && cryptoMessageVerify(msg->message.bytes, msg->message.size, addr_raw, msg->signature.bytes) == 0) { layoutVerifyMessage(msg->message.bytes, msg->message.size); protectButton(ButtonRequestType_ButtonRequest_Other, true); fsm_sendSuccess("Message verified"); @@ -569,33 +583,65 @@ void fsm_msgEncryptMessage(EncryptMessage *msg) fsm_sendFailure(FailureType_Failure_SyntaxError, "Invalid public key provided"); return; } - - if (msg->address_n_count) { + bool display_only = msg->has_display_only && msg->display_only; + bool signing = msg->address_n_count > 0; + RESP_INIT(EncryptedMessage); + const CoinType *coin = 0; + HDNode *node = 0; + uint8_t address_raw[21]; + if (signing) { + coin = coinByName(msg->coin_name); + if (!coin) { + fsm_sendFailure(FailureType_Failure_Other, "Invalid coin name"); + return; + } if (!protectPin(true)) { layoutHome(); return; } - HDNode *node = fsm_getRootNode(); + node = fsm_getRootNode(); if (!node) return; fsm_deriveKey(node, msg->address_n, msg->address_n_count); + hdnode_fill_public_key(node); + ecdsa_get_address_raw(node->public_key, coin->address_type, address_raw); } - - // TODO - + layoutEncryptMessage(msg->message.bytes, msg->message.size, signing); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, "Encrypt message cancelled"); + layoutHome(); + return; + } + if (cryptoMessageEncrypt(&pubkey, msg->message.bytes, msg->message.size, display_only, resp->nonce.bytes, &(resp->nonce.size), resp->message.bytes, &(resp->message.size), resp->hmac.bytes, &(resp->hmac.size), signing ? node->private_key : 0, signing ? address_raw : 0) != 0) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, "Error encrypting message"); + layoutHome(); + return; + } + resp->has_nonce = true; + resp->has_message = true; + resp->has_hmac = true; + msg_write(MessageType_MessageType_EncryptedMessage, resp); layoutHome(); } void fsm_msgDecryptMessage(DecryptMessage *msg) { + if (!msg->has_nonce) { + fsm_sendFailure(FailureType_Failure_SyntaxError, "No nonce provided"); + return; + } if (!msg->has_message) { fsm_sendFailure(FailureType_Failure_SyntaxError, "No message provided"); return; } - if (msg->message.size % 16) { - fsm_sendFailure(FailureType_Failure_SyntaxError, "Message length must be a multiple of 16"); + if (!msg->has_hmac) { + fsm_sendFailure(FailureType_Failure_SyntaxError, "No message hmac provided"); + return; + } + curve_point nonce_pubkey; + if (msg->nonce.size != 33 || ecdsa_read_pubkey(msg->nonce.bytes, &nonce_pubkey) == 0) { + fsm_sendFailure(FailureType_Failure_SyntaxError, "Invalid nonce provided"); return; } - if (!protectPin(true)) { layoutHome(); return; @@ -604,8 +650,30 @@ void fsm_msgDecryptMessage(DecryptMessage *msg) if (!node) return; fsm_deriveKey(node, msg->address_n, msg->address_n_count); - // TODO - + RESP_INIT(DecryptedMessage); + bool display_only = false; + bool signing = false; + uint8_t address_raw[21]; + if (cryptoMessageDecrypt(&nonce_pubkey, msg->message.bytes, msg->message.size, msg->hmac.bytes, msg->hmac.size, node->private_key, resp->message.bytes, &(resp->message.size), &display_only, &signing, address_raw) != 0) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, "Error decrypting message"); + layoutHome(); + return; + } + if (signing) { + base58_encode_check(address_raw, 21, resp->address); + } + layoutDecryptMessage(resp->message.bytes, resp->message.size, signing ? resp->address : 0); + protectButton(ButtonRequestType_ButtonRequest_Other, true); + if (display_only) { + resp->has_address = false; + resp->has_message = false; + memset(resp->address, sizeof(resp->address), 0); + memset(&(resp->message), sizeof(resp->message), 0); + } else { + resp->has_address = signing; + resp->has_message = true; + } + msg_write(MessageType_MessageType_DecryptedMessage, resp); layoutHome(); } diff --git a/firmware/layout2.c b/firmware/layout2.c index 1e2ad5bb3..8b582d188 100644 --- a/firmware/layout2.c +++ b/firmware/layout2.c @@ -304,3 +304,23 @@ void layoutAddress(const char *address) oledRefresh(); } + +void layoutEncryptMessage(const uint8_t *msg, uint32_t len, bool signing) +{ + // TODO: finish + (void)msg; + (void)len; + layoutDialogSwipe(DIALOG_ICON_QUESTION, "Cancel", "Confirm", + signing ? "Encrypt message?" : "Encrypt+sign message?", + NULL, NULL, NULL, NULL, NULL, NULL); +} + +void layoutDecryptMessage(const uint8_t *msg, uint32_t len, const char *address) +{ + // TODO: finish + (void)msg; + (void)len; + layoutDialogSwipe(DIALOG_ICON_INFO, NULL, "OK", + address ? "Signed message contents" : "Message contents", + NULL, NULL, NULL, NULL, NULL, NULL); +} diff --git a/firmware/layout2.h b/firmware/layout2.h index 9a836d3b7..5fadb68e6 100644 --- a/firmware/layout2.h +++ b/firmware/layout2.h @@ -34,5 +34,7 @@ void layoutSignMessage(const uint8_t *msg, uint32_t len); void layoutVerifyMessage(const uint8_t *msg, uint32_t len); void layoutCipherKeyValue(bool encrypt, const char *key); void layoutAddress(const char *address); +void layoutEncryptMessage(const uint8_t *msg, uint32_t len, bool signing); +void layoutDecryptMessage(const uint8_t *msg, uint32_t len, const char *address); #endif diff --git a/firmware/messages.c b/firmware/messages.c index 63b40080e..0692ff5c0 100644 --- a/firmware/messages.c +++ b/firmware/messages.c @@ -75,10 +75,13 @@ static const struct MessagesMap_t MessagesMap[] = { {'n', 'o', MessageType_MessageType_Features, Features_fields, 0}, {'n', 'o', MessageType_MessageType_PinMatrixRequest, PinMatrixRequest_fields, 0}, {'n', 'o', MessageType_MessageType_TxRequest, TxRequest_fields, 0}, + {'n', 'o', MessageType_MessageType_CipheredKeyValue, CipheredKeyValue_fields, 0}, {'n', 'o', MessageType_MessageType_ButtonRequest, ButtonRequest_fields, 0}, {'n', 'o', MessageType_MessageType_Address, Address_fields, 0}, {'n', 'o', MessageType_MessageType_EntropyRequest, EntropyRequest_fields, 0}, {'n', 'o', MessageType_MessageType_MessageSignature, MessageSignature_fields, 0}, + {'n', 'o', MessageType_MessageType_EncryptedMessage, EncryptedMessage_fields, 0}, + {'n', 'o', MessageType_MessageType_DecryptedMessage, DecryptedMessage_fields, 0}, {'n', 'o', MessageType_MessageType_PassphraseRequest, PassphraseRequest_fields, 0}, {'n', 'o', MessageType_MessageType_TxSize, TxSize_fields, 0}, {'n', 'o', MessageType_MessageType_WordRequest, WordRequest_fields, 0}, diff --git a/firmware/transaction.c b/firmware/transaction.c index bd02bb1cc..fc3d6e144 100644 --- a/firmware/transaction.c +++ b/firmware/transaction.c @@ -25,29 +25,9 @@ #include "debug.h" #include "protect.h" #include "layout2.h" +#include "crypto.h" #include "messages.pb.h" -// aux methods - -uint32_t ser_length(uint32_t len, uint8_t *out) { - if (len < 253) { - out[0] = len & 0xFF; - return 1; - } - if (len < 0x10000) { - out[0] = 253; - out[1] = len & 0xFF; - out[2] = (len >> 8) & 0xFF; - return 3; - } - out[0] = 254; - out[1] = len & 0xFF; - out[2] = (len >> 8) & 0xFF; - out[3] = (len >> 16) & 0xFF; - out[4] = (len >> 24) & 0xFF; - return 5; -} - uint32_t op_push(uint32_t i, uint8_t *out) { if (i < 0x4C) { out[0] = i & 0xFF; @@ -290,100 +270,3 @@ uint32_t transactionEstimateSizeKb(uint32_t inputs, uint32_t outputs) { return (transactionEstimateSize(inputs, outputs) + 999) / 1000; } - -bool transactionMessageSign(uint8_t *message, uint32_t message_len, uint8_t *privkey, const char *address, uint8_t *signature) -{ - if (message_len >= 256) { - return false; - } - - SHA256_CTX ctx; - uint8_t i, hash[32]; - - sha256_Init(&ctx); - sha256_Update(&ctx, (const uint8_t *)"\x18" "Bitcoin Signed Message:" "\n", 25); - i = message_len; - sha256_Update(&ctx, &i, 1); - sha256_Update(&ctx, message, message_len); - sha256_Final(hash, &ctx); - sha256_Raw(hash, 32, hash); - - ecdsa_sign_digest(privkey, hash, signature + 1); - for (i = 27 + 4; i < 27 + 4 + 4; i++) { - signature[0] = i; - if (transactionMessageVerify(message, message_len, signature, address)) { - return true; - } - } - - return false; -} - -bool transactionMessageVerify(uint8_t *message, uint32_t message_len, uint8_t *signature, const char *address) -{ - if (message_len >= 256) { - return false; - } - - bool compressed; - uint8_t nV = signature[0]; - bignum256 r, s, e; - curve_point cp, cp2; - SHA256_CTX ctx; - uint8_t i, pubkey[65], decoded[21], hash[32]; - char addr[35]; - - if (nV < 27 || nV >= 35) { - return false; - } - compressed = (nV >= 31); - if (compressed) { - nV -= 4; - } - uint8_t recid = nV - 27; - // read r and s - bn_read_be(signature + 1, &r); - bn_read_be(signature + 33, &s); - // x = r + (recid / 2) * order - bn_zero(&cp.x); - for (i = 0; i < recid / 2; i++) { - bn_addmod(&cp.x, &order256k1, &prime256k1); - } - bn_addmod(&cp.x, &r, &prime256k1); - // compute y from x - uncompress_coords(recid % 2, &cp.x, &cp.y); - // calculate hash - sha256_Init(&ctx); - sha256_Update(&ctx, (const uint8_t *)"\x18" "Bitcoin Signed Message:" "\n", 25); - i = message_len; - sha256_Update(&ctx, &i, 1); - sha256_Update(&ctx, message, message_len); - sha256_Final(hash, &ctx); - sha256_Raw(hash, 32, hash); - // e = -hash - bn_read_be(hash, &e); - bn_substract_noprime(&order256k1, &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); - pubkey[0] = 0x04; - bn_write_be(&cp.x, pubkey + 1); - bn_write_be(&cp.y, pubkey + 33); - // check if the address is correct - ecdsa_address_decode(address, decoded); - if (compressed) { - pubkey[0] = 0x02 | (cp.y.val[0] & 0x01); - } - ecdsa_get_address(pubkey, decoded[0], addr); - if (strcmp(addr, address) != 0) { - return false; - } - // check if signature verifies the digest - if (ecdsa_verify_digest(pubkey, signature + 1, hash) != 0) { - return false; - } - return true; -} diff --git a/firmware/transaction.h b/firmware/transaction.h index 0939ea363..2a2e09849 100644 --- a/firmware/transaction.h +++ b/firmware/transaction.h @@ -56,8 +56,4 @@ uint32_t transactionEstimateSize(uint32_t inputs, uint32_t outputs); uint32_t transactionEstimateSizeKb(uint32_t inputs, uint32_t outputs); -bool transactionMessageSign(uint8_t *message, uint32_t message_len, uint8_t *privkey, const char *address, uint8_t *signature); - -bool transactionMessageVerify(uint8_t *message, uint32_t message_len, uint8_t *signature, const char *address); - #endif diff --git a/trezor-crypto b/trezor-crypto index f6560c7d1..9469a64a0 160000 --- a/trezor-crypto +++ b/trezor-crypto @@ -1 +1 @@ -Subproject commit f6560c7d1303a50d215cffab87acc7fc8c8964a4 +Subproject commit 9469a64a0a1ec032b829e7a1465d0e4b2996cd61