Skip to content

Commit

Permalink
Detect unspendable pubkeys during wallet policy registration
Browse files Browse the repository at this point in the history
  • Loading branch information
bigspider committed Apr 4, 2024
1 parent 031719b commit 63b9884
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 28 deletions.
59 changes: 37 additions & 22 deletions src/handler/register_wallet.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@
static bool is_policy_acceptable(const policy_node_t *policy);
static bool is_policy_name_acceptable(const char *name, size_t name_len);

static const uint8_t BIP0341_NUMS_PUBKEY[] = {0x02, 0x50, 0x92, 0x9b, 0x74, 0xc1, 0xa0, 0x49, 0x54,
0xb7, 0x8b, 0x4b, 0x60, 0x35, 0xe9, 0x7a, 0x5e, 0x07,
0x8a, 0x5a, 0x0f, 0x28, 0xec, 0x96, 0xd5, 0x47, 0xbf,
0xee, 0x9a, 0xce, 0x80, 0x3a, 0xc0};

/**
* Validates the input, initializes the hash context and starts accumulating the wallet header in
* it.
Expand Down Expand Up @@ -171,35 +176,45 @@ void handler_register_wallet(dispatcher_context_t *dc, uint8_t protocol_version)
// supported, but disabled for now (question to address: can only _some_ of the keys have a
// wildcard?).

bool is_key_internal = false;
// if there is key origin information and the fingerprint matches, we make sure it's not a
// false positive (it could be wrong info, or a collision).
if (key_info.has_key_origin &&
read_u32_be(key_info.master_key_fingerprint, 0) == master_key_fingerprint) {
// we verify that we can actually generate the same pubkey
serialized_extended_pubkey_t pubkey_derived;
int serialized_pubkey_len =
get_extended_pubkey_at_path(key_info.master_key_derivation,
key_info.master_key_derivation_len,
BIP32_PUBKEY_VERSION,
&pubkey_derived);
if (serialized_pubkey_len == -1) {
SEND_SW(dc, SW_BAD_STATE);
ui_post_processing_confirm_wallet_registration(dc, false);
return;
}

if (memcmp(&key_info.ext_pubkey, &pubkey_derived, sizeof(pubkey_derived)) == 0) {
is_key_internal = true;
++n_internal_keys;
key_type_e key_type;

if (memcmp(key_info.ext_pubkey.compressed_pubkey,
BIP0341_NUMS_PUBKEY,
sizeof(BIP0341_NUMS_PUBKEY)) == 0) {
// this public key is known to be unspendable
key_type = PUBKEY_TYPE_UNSPENDABLE;
} else {
key_type = PUBKEY_TYPE_EXTERNAL;

// if there is key origin information and the fingerprint matches, we make sure it's not
// a false positive (it could be wrong info, or a collision).
if (key_info.has_key_origin &&
read_u32_be(key_info.master_key_fingerprint, 0) == master_key_fingerprint) {
// we verify that we can actually generate the same pubkey
serialized_extended_pubkey_t pubkey_derived;
int serialized_pubkey_len =
get_extended_pubkey_at_path(key_info.master_key_derivation,
key_info.master_key_derivation_len,
BIP32_PUBKEY_VERSION,
&pubkey_derived);
if (serialized_pubkey_len == -1) {
SEND_SW(dc, SW_BAD_STATE);
ui_post_processing_confirm_wallet_registration(dc, false);
return;
}

if (memcmp(&key_info.ext_pubkey, &pubkey_derived, sizeof(pubkey_derived)) == 0) {
key_type = PUBKEY_TYPE_INTERNAL;
++n_internal_keys;
}
}
}

if (!ui_display_policy_map_cosigner_pubkey(dc,
(char *) next_pubkey_info,
cosigner_index, // 1-indexed for the UI
wallet_header.n_keys,
is_key_internal)) {
key_type)) {
SEND_SW(dc, SW_DENY);
return;
}
Expand Down
14 changes: 9 additions & 5 deletions src/ui/display.c
Original file line number Diff line number Diff line change
Expand Up @@ -152,24 +152,28 @@ bool ui_display_policy_map_cosigner_pubkey(dispatcher_context_t *context,
const char *pubkey,
uint8_t cosigner_index,
uint8_t n_keys,
bool is_internal) {
key_type_e key_type) {
(void) (n_keys);

ui_cosigner_pubkey_and_index_state_t *state =
(ui_cosigner_pubkey_and_index_state_t *) &g_ui_state;

strncpy(state->pubkey, pubkey, sizeof(state->pubkey));

if (is_internal) {
if (key_type == PUBKEY_TYPE_INTERNAL) {
snprintf(state->signer_index, sizeof(state->signer_index), "Key @%u, ours", cosigner_index);
} else if (key_type == PUBKEY_TYPE_EXTERNAL) {
snprintf(state->signer_index,
sizeof(state->signer_index),
"Key @%u <ours>",
"Key @%u, theirs",
cosigner_index);
} else {
} else if (key_type == PUBKEY_TYPE_UNSPENDABLE) {
snprintf(state->signer_index,
sizeof(state->signer_index),
"Key @%u <theirs>",
"Key @%u, dummy",
cosigner_index);
} else {
LEDGER_ASSERT(false, "Unreachable code");
}
ui_display_policy_map_cosigner_pubkey_flow();

Expand Down
8 changes: 7 additions & 1 deletion src/ui/display.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,11 +116,17 @@ bool ui_display_register_wallet(dispatcher_context_t *context,
const policy_map_wallet_header_t *wallet_header,
const char *policy_descriptor);

typedef enum {
PUBKEY_TYPE_INTERNAL = 0, // a key controlled by the wallet policy
PUBKEY_TYPE_EXTERNAL = 1, // a key not controlled by the wallet policy
PUBKEY_TYPE_UNSPENDABLE = 2 // the provably unspendable public key defined in BIP-341
} key_type_e;

bool ui_display_policy_map_cosigner_pubkey(dispatcher_context_t *dispatcher_context,
const char *pubkey,
uint8_t cosigner_index,
uint8_t n_keys,
bool is_internal);
key_type_e key_type);

bool ui_display_wallet_address(dispatcher_context_t *context,
const char *wallet_name,
Expand Down

0 comments on commit 63b9884

Please sign in to comment.