diff --git a/crypto/zkp_bip340.c b/crypto/zkp_bip340.c index f45505faf..5d450bbb6 100644 --- a/crypto/zkp_bip340.c +++ b/crypto/zkp_bip340.c @@ -184,6 +184,27 @@ int zkp_bip340_verify_digest(const uint8_t *public_key_bytes, return result; } +// BIP340 Schnorr public key verification +// public_key_bytes has 32 bytes +// returns 0 if verification succeeded +int zkp_bip340_verify_publickey(const uint8_t *public_key_bytes) { + int result = 0; + + secp256k1_xonly_pubkey xonly_pubkey = {0}; + const secp256k1_context *context_read_only = zkp_context_get_read_only(); + + if (result == 0) { + if (secp256k1_xonly_pubkey_parse(context_read_only, &xonly_pubkey, + public_key_bytes) != 1) { + result = 1; + } + } + + memzero(&xonly_pubkey, sizeof(xonly_pubkey)); + + return result; +} + // BIP340 Schnorr public key tweak // internal_public_key has 32 bytes // root_hash has 32 bytes or is empty (NULL) diff --git a/crypto/zkp_bip340.h b/crypto/zkp_bip340.h index d44057cba..670384a4c 100644 --- a/crypto/zkp_bip340.h +++ b/crypto/zkp_bip340.h @@ -11,6 +11,7 @@ int zkp_bip340_sign_digest(const uint8_t *private_key_bytes, int zkp_bip340_verify_digest(const uint8_t *public_key_bytes, const uint8_t *signature_bytes, const uint8_t *digest); +int zkp_bip340_verify_publickey(const uint8_t *public_key_bytes); int zkp_bip340_tweak_public_key(const uint8_t *internal_public_key, const uint8_t *root_hash, uint8_t *output_public_key); diff --git a/legacy/firmware/.changelog.d/2077.added b/legacy/firmware/.changelog.d/2077.added new file mode 100644 index 000000000..6ca321ffa --- /dev/null +++ b/legacy/firmware/.changelog.d/2077.added @@ -0,0 +1 @@ +Add extra check for Taproot scripts validity diff --git a/legacy/firmware/transaction.c b/legacy/firmware/transaction.c index 074c13121..9e2dcf7ac 100644 --- a/legacy/firmware/transaction.c +++ b/legacy/firmware/transaction.c @@ -295,6 +295,16 @@ static int address_to_script_pubkey(const CoinInfo *coin, const char *address, coin->bech32_prefix, address)) { return 0; } + // check that the witness version is recognized + if (witver != 0 && witver != 1) { + return 0; + } + // check that P2TR address encodes a valid BIP340 public key + if (witver == 1) { + if (addr_raw_len != 32 || zkp_bip340_verify_publickey(addr_raw) != 0) { + return 0; + } + } // push 1 byte version id (opcode OP_0 = 0, OP_i = 80+i) // push addr_raw (segwit_addr_decode makes sure addr_raw_len is at most 40) script_pubkey[0] = witver == 0 ? 0 : 80 + witver;