1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-12-22 22:38:08 +00:00

move Sign/Verify to crypto.c/h, implement Encrypt/Decrypt

This commit is contained in:
Pavol Rusnak 2014-11-15 02:01:21 +01:00
parent 960c665aac
commit f75515544f
10 changed files with 422 additions and 145 deletions

View File

@ -15,6 +15,7 @@ OBJS += layout2.o
OBJS += recovery.o
OBJS += reset.o
OBJS += signing.o
OBJS += crypto.o
OBJS += debug.o

263
firmware/crypto.c Normal file
View File

@ -0,0 +1,263 @@
/*
* This file is part of the TREZOR project.
*
* Copyright (C) 2014 Pavol Rusnak <stick@satoshilabs.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#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;
}

41
firmware/crypto.h Normal file
View File

@ -0,0 +1,41 @@
/*
* This file is part of the TREZOR project.
*
* Copyright (C) 2014 Pavol Rusnak <stick@satoshilabs.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef __CRYPTO_H__
#define __CRYPTO_H__
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <secp256k1.h>
#include <pb.h>
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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

@ -1 +1 @@
Subproject commit f6560c7d1303a50d215cffab87acc7fc8c8964a4
Subproject commit 9469a64a0a1ec032b829e7a1465d0e4b2996cd61