mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-18 04:18:10 +00:00
feat(legacy): Support Electrum signatures in VerifyMessage.
This commit is contained in:
parent
b5c9f573b2
commit
cb21c7e415
1
legacy/firmware/.changelog.d/2100.added
Normal file
1
legacy/firmware/.changelog.d/2100.added
Normal file
@ -0,0 +1 @@
|
|||||||
|
Support Electrum signatures in VerifyMessage.
|
@ -177,12 +177,78 @@ int cryptoMessageSign(const CoinInfo *coin, HDNode *node,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Determines the script type from a non-multisig address.
|
||||||
|
static InputScriptType address_to_script_type(const CoinInfo *coin,
|
||||||
|
const char *address) {
|
||||||
|
uint8_t addr_raw[MAX_ADDR_RAW_SIZE] = {0};
|
||||||
|
size_t addr_raw_len = 0;
|
||||||
|
|
||||||
|
// Native SegWit
|
||||||
|
if (coin->bech32_prefix) {
|
||||||
|
int witver = 0;
|
||||||
|
if (segwit_addr_decode(&witver, addr_raw, &addr_raw_len,
|
||||||
|
coin->bech32_prefix, address)) {
|
||||||
|
switch (witver) {
|
||||||
|
case 0:
|
||||||
|
return InputScriptType_SPENDWITNESS;
|
||||||
|
case 1:
|
||||||
|
return InputScriptType_SPENDTAPROOT;
|
||||||
|
default:
|
||||||
|
return InputScriptType_EXTERNAL; // unknown script type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !BITCOIN_ONLY
|
||||||
|
if (coin->cashaddr_prefix &&
|
||||||
|
cash_addr_decode(addr_raw, &addr_raw_len, coin->cashaddr_prefix,
|
||||||
|
address)) {
|
||||||
|
return InputScriptType_SPENDADDRESS;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
addr_raw_len = base58_decode_check(address, coin->curve->hasher_base58,
|
||||||
|
addr_raw, sizeof(addr_raw));
|
||||||
|
|
||||||
|
// P2PKH
|
||||||
|
if (addr_raw_len > address_prefix_bytes_len(coin->address_type) &&
|
||||||
|
address_check_prefix(addr_raw, coin->address_type)) {
|
||||||
|
return InputScriptType_SPENDADDRESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// P2SH
|
||||||
|
if (addr_raw_len > address_prefix_bytes_len(coin->address_type_p2sh) &&
|
||||||
|
address_check_prefix(addr_raw, coin->address_type_p2sh)) {
|
||||||
|
return InputScriptType_SPENDP2SHWITNESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
return InputScriptType_EXTERNAL; // unknown script type
|
||||||
|
}
|
||||||
|
|
||||||
int cryptoMessageVerify(const CoinInfo *coin, const uint8_t *message,
|
int cryptoMessageVerify(const CoinInfo *coin, const uint8_t *message,
|
||||||
size_t message_len, const char *address,
|
size_t message_len, const char *address,
|
||||||
const uint8_t *signature) {
|
const uint8_t *signature) {
|
||||||
// check for invalid signature prefix
|
// check if the address is correct
|
||||||
if (signature[0] < 27 || signature[0] > 43) {
|
InputScriptType script_type = address_to_script_type(coin, address);
|
||||||
return 1;
|
if (script_type == InputScriptType_EXTERNAL) {
|
||||||
|
return 1; // invalid address
|
||||||
|
}
|
||||||
|
|
||||||
|
if (signature[0] >= 27 && signature[0] <= 34) {
|
||||||
|
// p2pkh or no script type provided
|
||||||
|
// use the script type from the address
|
||||||
|
} else if (signature[0] >= 35 && signature[0] <= 38) {
|
||||||
|
// segwit-in-p2sh
|
||||||
|
if (script_type != InputScriptType_SPENDP2SHWITNESS) {
|
||||||
|
return 2; // script type mismatch
|
||||||
|
}
|
||||||
|
} else if (signature[0] >= 39 && signature[0] <= 42) {
|
||||||
|
// segwit
|
||||||
|
if (script_type != InputScriptType_SPENDWITNESS) {
|
||||||
|
return 2; // script type mismatch
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return 3; // invalid signature prefix
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t hash[HASHER_DIGEST_LENGTH] = {0};
|
uint8_t hash[HASHER_DIGEST_LENGTH] = {0};
|
||||||
@ -195,23 +261,23 @@ int cryptoMessageVerify(const CoinInfo *coin, const uint8_t *message,
|
|||||||
uint8_t pubkey[65] = {0};
|
uint8_t pubkey[65] = {0};
|
||||||
if (ecdsa_recover_pub_from_sig(coin->curve->params, pubkey, signature + 1,
|
if (ecdsa_recover_pub_from_sig(coin->curve->params, pubkey, signature + 1,
|
||||||
hash, recid) != 0) {
|
hash, recid) != 0) {
|
||||||
return 3;
|
return 4; // invalid signature data
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert public key to compressed pubkey if necessary
|
// convert public key to compressed pubkey if necessary
|
||||||
if (compressed) {
|
if (compressed) {
|
||||||
pubkey[0] = 0x02 | (pubkey[64] & 1);
|
pubkey[0] = 0x02 | (pubkey[64] & 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if the address is correct
|
|
||||||
uint8_t addr_raw[MAX_ADDR_RAW_SIZE] = {0};
|
uint8_t addr_raw[MAX_ADDR_RAW_SIZE] = {0};
|
||||||
uint8_t recovered_raw[MAX_ADDR_RAW_SIZE] = {0};
|
uint8_t recovered_raw[MAX_ADDR_RAW_SIZE] = {0};
|
||||||
|
|
||||||
|
if (script_type == InputScriptType_SPENDADDRESS) {
|
||||||
// p2pkh
|
// p2pkh
|
||||||
if (signature[0] >= 27 && signature[0] <= 34) {
|
|
||||||
size_t len = 0;
|
size_t len = 0;
|
||||||
if (coin->cashaddr_prefix) {
|
if (coin->cashaddr_prefix) {
|
||||||
if (!cash_addr_decode(addr_raw, &len, coin->cashaddr_prefix, address)) {
|
if (!cash_addr_decode(addr_raw, &len, coin->cashaddr_prefix, address)) {
|
||||||
return 2;
|
return 1; // invalid address
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
len = base58_decode_check(address, coin->curve->hasher_base58, addr_raw,
|
len = base58_decode_check(address, coin->curve->hasher_base58, addr_raw,
|
||||||
@ -221,9 +287,9 @@ int cryptoMessageVerify(const CoinInfo *coin, const uint8_t *message,
|
|||||||
coin->curve->hasher_pubkey, recovered_raw);
|
coin->curve->hasher_pubkey, recovered_raw);
|
||||||
if (memcmp(recovered_raw, addr_raw, len) != 0 ||
|
if (memcmp(recovered_raw, addr_raw, len) != 0 ||
|
||||||
len != address_prefix_bytes_len(coin->address_type) + 20) {
|
len != address_prefix_bytes_len(coin->address_type) + 20) {
|
||||||
return 2;
|
return 5; // signature does not match address and message
|
||||||
}
|
}
|
||||||
} else if (signature[0] >= 35 && signature[0] <= 38) {
|
} else if (script_type == InputScriptType_SPENDP2SHWITNESS) {
|
||||||
// segwit-in-p2sh
|
// segwit-in-p2sh
|
||||||
size_t len = base58_decode_check(address, coin->curve->hasher_base58,
|
size_t len = base58_decode_check(address, coin->curve->hasher_base58,
|
||||||
addr_raw, MAX_ADDR_RAW_SIZE);
|
addr_raw, MAX_ADDR_RAW_SIZE);
|
||||||
@ -232,23 +298,23 @@ int cryptoMessageVerify(const CoinInfo *coin, const uint8_t *message,
|
|||||||
recovered_raw);
|
recovered_raw);
|
||||||
if (memcmp(recovered_raw, addr_raw, len) != 0 ||
|
if (memcmp(recovered_raw, addr_raw, len) != 0 ||
|
||||||
len != address_prefix_bytes_len(coin->address_type_p2sh) + 20) {
|
len != address_prefix_bytes_len(coin->address_type_p2sh) + 20) {
|
||||||
return 2;
|
return 5; // signature does not match address and message
|
||||||
}
|
}
|
||||||
} else if (signature[0] >= 39 && signature[0] <= 42) {
|
} else if (script_type == InputScriptType_SPENDWITNESS) {
|
||||||
// segwit
|
// segwit
|
||||||
int witver = 0;
|
int witver = 0;
|
||||||
size_t len = 0;
|
size_t len = 0;
|
||||||
if (!coin->bech32_prefix ||
|
if (!coin->bech32_prefix ||
|
||||||
!segwit_addr_decode(&witver, recovered_raw, &len, coin->bech32_prefix,
|
!segwit_addr_decode(&witver, recovered_raw, &len, coin->bech32_prefix,
|
||||||
address)) {
|
address)) {
|
||||||
return 4;
|
return 1; // invalid address
|
||||||
}
|
}
|
||||||
ecdsa_get_pubkeyhash(pubkey, coin->curve->hasher_pubkey, addr_raw);
|
ecdsa_get_pubkeyhash(pubkey, coin->curve->hasher_pubkey, addr_raw);
|
||||||
if (memcmp(recovered_raw, addr_raw, len) != 0 || witver != 0 || len != 20) {
|
if (memcmp(recovered_raw, addr_raw, len) != 0 || witver != 0 || len != 20) {
|
||||||
return 2;
|
return 5; // signature does not match address and message
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return 4;
|
return 1; // invalid address
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -327,9 +327,15 @@ void fsm_msgVerifyMessage(const VerifyMessage *msg) {
|
|||||||
const CoinInfo *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name);
|
const CoinInfo *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name);
|
||||||
if (!coin) return;
|
if (!coin) return;
|
||||||
layoutProgressSwipe(_("Verifying"), 0);
|
layoutProgressSwipe(_("Verifying"), 0);
|
||||||
if (msg->signature.size == 65 &&
|
if (msg->signature.size != 65) {
|
||||||
cryptoMessageVerify(coin, msg->message.bytes, msg->message.size,
|
fsm_sendFailure(FailureType_Failure_ProcessError, _("Invalid signature"));
|
||||||
msg->address, msg->signature.bytes) == 0) {
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int result = cryptoMessageVerify(coin, msg->message.bytes, msg->message.size,
|
||||||
|
msg->address, msg->signature.bytes);
|
||||||
|
if (result == 0) {
|
||||||
layoutVerifyAddress(coin, msg->address);
|
layoutVerifyAddress(coin, msg->address);
|
||||||
if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) {
|
if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) {
|
||||||
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
|
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
|
||||||
@ -352,8 +358,10 @@ void fsm_msgVerifyMessage(const VerifyMessage *msg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fsm_sendSuccess(_("Message verified"));
|
fsm_sendSuccess(_("Message verified"));
|
||||||
|
} else if (result == 1) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_DataError, _("Invalid address"));
|
||||||
} else {
|
} else {
|
||||||
fsm_sendFailure(FailureType_Failure_DataError, _("Invalid signature"));
|
fsm_sendFailure(FailureType_Failure_ProcessError, _("Invalid signature"));
|
||||||
}
|
}
|
||||||
layoutHome();
|
layoutHome();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user