Skip to content
This repository has been archived by the owner on Apr 16, 2019. It is now read-only.

Commit

Permalink
segwit support for SignMessage, VerifyMessage
Browse files Browse the repository at this point in the history
  • Loading branch information
prusnak committed Jul 25, 2017
1 parent d8ad44f commit b5f9a57
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 38 deletions.
65 changes: 52 additions & 13 deletions firmware/crypto.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <string.h>
#include "crypto.h"
#include "sha2.h"
#include "ripemd160.h"
#include "pbkdf2.h"
#include "aes.h"
#include "hmac.h"
Expand Down Expand Up @@ -109,7 +110,7 @@ int gpgMessageSign(HDNode *node, const uint8_t *message, size_t message_len, uin
}
}

int cryptoMessageSign(const CoinType *coin, HDNode *node, const uint8_t *message, size_t message_len, uint8_t *signature)
int cryptoMessageSign(const CoinType *coin, HDNode *node, InputScriptType script_type, const uint8_t *message, size_t message_len, uint8_t *signature)
{
SHA256_CTX ctx;
sha256_Init(&ctx);
Expand All @@ -124,46 +125,84 @@ int cryptoMessageSign(const CoinType *coin, HDNode *node, const uint8_t *message
uint8_t pby;
int result = hdnode_sign_digest(node, hash, signature + 1, &pby, NULL);
if (result == 0) {
signature[0] = 27 + pby + 4;
switch (script_type) {
case InputScriptType_SPENDP2SHWITNESS:
// segwit-in-p2sh
signature[0] = 35 + pby;
break;
case InputScriptType_SPENDWITNESS:
// segwit
signature[0] = 39 + pby;
break;
default:
// p2pkh
signature[0] = 31 + pby;
break;
}
}
return result;
}

int cryptoMessageVerify(const CoinType *coin, const uint8_t *message, size_t message_len, uint32_t address_type, const uint8_t *address_raw, const uint8_t *signature)
{
SHA256_CTX ctx;
uint8_t pubkey[65], addr_raw[MAX_ADDR_RAW_SIZE], hash[32];
// check for invalid signature prefix
if (signature[0] < 27 || signature[0] > 43) {
return 1;
}

// calculate hash
SHA256_CTX ctx;
sha256_Init(&ctx);
sha256_Update(&ctx, (const uint8_t *)coin->signed_message_header, strlen(coin->signed_message_header));
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(&ctx, hash);
sha256_Raw(hash, 32, hash);

uint8_t recid = signature[0] - 27;
if (recid >= 8) {
return 1;
}
bool compressed = (recid >= 4);
recid &= 3;
uint8_t recid = (signature[0] - 27) % 4;
bool compressed = signature[0] >= 31;

// check if signature verifies the digest and recover the public key
uint8_t pubkey[65];
if (ecdsa_verify_digest_recover(&secp256k1, pubkey, signature + 1, hash, recid) != 0) {
return 3;
}
// convert public key to compressed pubkey if necessary
if (compressed) {
pubkey[0] = 0x02 | (pubkey[64] & 1);
}

// check if the address is correct
ecdsa_get_address_raw(pubkey, address_type, addr_raw);
if (memcmp(addr_raw, address_raw, address_prefix_bytes_len(address_type) + 20) != 0) {
return 2;
uint8_t addr_raw[MAX_ADDR_RAW_SIZE];

// p2pkh
if (signature[0] >= 27 && signature[0] <= 34) {
if (address_type != coin->address_type) {
return 4;
}
ecdsa_get_address_raw(pubkey, address_type, addr_raw);
if (memcmp(addr_raw, address_raw, address_prefix_bytes_len(address_type) + 20) != 0) {
return 2;
}
} else
// segwit-in-p2sh
if (signature[0] >= 35 && signature[0] <= 38) {
if (address_type != coin->address_type_p2sh) {
return 4;
}
ecdsa_get_address_segwit_p2sh_raw(pubkey, address_type, addr_raw);
if (memcmp(addr_raw, address_raw, address_prefix_bytes_len(address_type) + 20) != 0) {
return 2;
}
} else
// segwit
if (signature[0] >= 39 && signature[0] <= 42) {
return 2; // not supported yet
}

return 0;
}

Expand Down
2 changes: 1 addition & 1 deletion firmware/crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ int sshMessageSign(HDNode *node, const uint8_t *message, size_t message_len, uin

int gpgMessageSign(HDNode *node, const uint8_t *message, size_t message_len, uint8_t *signature);

int cryptoMessageSign(const CoinType *coin, HDNode *node, const uint8_t *message, size_t message_len, uint8_t *signature);
int cryptoMessageSign(const CoinType *coin, HDNode *node, InputScriptType script_type, const uint8_t *message, size_t message_len, uint8_t *signature);

int cryptoMessageVerify(const CoinType *coin, const uint8_t *message, size_t message_len, uint32_t address_type, const uint8_t *address_raw, const uint8_t *signature);

Expand Down
11 changes: 8 additions & 3 deletions firmware/fsm.c
Original file line number Diff line number Diff line change
Expand Up @@ -795,9 +795,14 @@ void fsm_msgSignMessage(SignMessage *msg)
if (!node) return;

layoutProgressSwipe(_("Signing"), 0);
if (cryptoMessageSign(coin, node, msg->message.bytes, msg->message.size, resp->signature.bytes) == 0) {
if (cryptoMessageSign(coin, node, msg->script_type, msg->message.bytes, msg->message.size, resp->signature.bytes) == 0) {
resp->has_address = true;
hdnode_get_address(node, coin->address_type, resp->address, sizeof(resp->address));
hdnode_fill_public_key(node);
if (!compute_address(coin, msg->script_type, node, false, NULL, resp->address)) {
fsm_sendFailure(FailureType_Failure_ProcessError, _("Error computing address"));
layoutHome();
return;
}
resp->has_signature = true;
resp->signature.size = 65;
msg_write(MessageType_MessageType_MessageSignature, resp);
Expand Down Expand Up @@ -890,7 +895,7 @@ void fsm_msgSignIdentity(SignIdentity *msg)
uint8_t digest[64];
sha256_Raw(msg->challenge_hidden.bytes, msg->challenge_hidden.size, digest);
sha256_Raw((const uint8_t *)msg->challenge_visual, strlen(msg->challenge_visual), digest + 32);
result = cryptoMessageSign(&(coins[0]), node, digest, 64, resp->signature.bytes);
result = cryptoMessageSign(&(coins[0]), node, InputScriptType_SPENDADDRESS, digest, 64, resp->signature.bytes);
}

if (result == 0) {
Expand Down
8 changes: 4 additions & 4 deletions firmware/protob/messages.options
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,12 @@ SignMessage.address_n max_count:8
SignMessage.message max_size:1024
SignMessage.coin_name max_size:17

VerifyMessage.address max_size:41
VerifyMessage.address max_size:60
VerifyMessage.signature max_size:65
VerifyMessage.message max_size:1024
VerifyMessage.coin_name max_size:17

MessageSignature.address max_size:41
MessageSignature.address max_size:60
MessageSignature.signature max_size:65

EthereumSignMessage.address_n max_count:8
Expand Down Expand Up @@ -97,7 +97,7 @@ DecryptMessage skip_message:true

# deprecated
DecryptedMessage skip_message:true
# DecryptedMessage.address max_size:41
# DecryptedMessage.address max_size:60
# DecryptedMessage.message max_size:1024

CipherKeyValue.address_n max_count:8
Expand Down Expand Up @@ -133,7 +133,7 @@ SignIdentity.challenge_hidden max_size:256
SignIdentity.challenge_visual max_size:256
SignIdentity.ecdsa_curve_name max_size:32

SignedIdentity.address max_size:41
SignedIdentity.address max_size:60
SignedIdentity.public_key max_size:33
SignedIdentity.signature max_size:65

Expand Down
12 changes: 6 additions & 6 deletions firmware/protob/messages.pb.h
Original file line number Diff line number Diff line change
Expand Up @@ -595,7 +595,7 @@ typedef struct {

typedef struct _MessageSignature {
bool has_address;
char address[41];
char address[60];
bool has_signature;
MessageSignature_signature_t signature;
} MessageSignature;
Expand Down Expand Up @@ -729,7 +729,7 @@ typedef struct {

typedef struct _SignedIdentity {
bool has_address;
char address[41];
char address[60];
bool has_public_key;
SignedIdentity_public_key_t public_key;
bool has_signature;
Expand Down Expand Up @@ -767,7 +767,7 @@ typedef struct {

typedef struct _VerifyMessage {
bool has_address;
char address[41];
char address[60];
bool has_signature;
VerifyMessage_signature_t signature;
bool has_message;
Expand Down Expand Up @@ -1192,8 +1192,8 @@ extern const pb_field_t DebugLinkFlashErase_fields[2];
#define WordRequest_size 6
#define WordAck_size 14
#define SignMessage_size 1100
#define VerifyMessage_size 1156
#define MessageSignature_size 110
#define VerifyMessage_size 1175
#define MessageSignature_size 129
#define CipherKeyValue_size 1358
#define CipheredKeyValue_size 1027
#define SignTx_size 43
Expand All @@ -1206,7 +1206,7 @@ extern const pb_field_t DebugLinkFlashErase_fields[2];
#define EthereumVerifyMessage_size 1116
#define EthereumMessageSignature_size 89
#define SignIdentity_size (558 + IdentityType_size)
#define SignedIdentity_size 145
#define SignedIdentity_size 164
#define GetECDHSessionKey_size (107 + IdentityType_size)
#define ECDHSessionKey_size 67
#define SetU2FCounter_size 6
Expand Down
11 changes: 1 addition & 10 deletions firmware/transaction.c
Original file line number Diff line number Diff line change
Expand Up @@ -124,16 +124,7 @@ bool compute_address(const CoinType *coin,
if (!coin->has_address_type_p2sh) {
return 0;
}
prelen = address_prefix_bytes_len(coin->address_type_p2sh);
raw[0] = 0; // version byte
raw[1] = 20; // push 20 bytes
ecdsa_get_pubkeyhash(node->public_key, raw + 2);
sha256_Raw(raw, 22, digest);
address_write_prefix_bytes(coin->address_type_p2sh, raw);
ripemd160(digest, 32, raw + prelen);
if (!base58_encode_check(raw, prelen + 20, address, MAX_ADDR_SIZE)) {
return 0;
}
ecdsa_get_address_segwit_p2sh(node->public_key, coin->address_type_p2sh, address, MAX_ADDR_SIZE);
} else {
ecdsa_get_address(node->public_key, coin->address_type, address, MAX_ADDR_SIZE);
}
Expand Down

0 comments on commit b5f9a57

Please sign in to comment.