Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/handler/lib/policy.c
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,7 @@ __attribute__((noinline, warn_unused_result)) int get_extended_pubkey_from_clien
key_index,
(uint8_t *) key_info_str,
sizeof(key_info_str));
if (key_info_len == -1) {
if (key_info_len < 0) {
return -1;
}

Expand Down Expand Up @@ -1799,7 +1799,7 @@ static int get_pubkey_from_merkle_tree(dispatcher_context_t *dispatcher_context,
index,
(uint8_t *) key_info_str,
sizeof(key_info_str));
if (key_info_len == -1) {
if (key_info_len < 0) {
return WITH_ERROR(-1, "Failed to retrieve key info");
}

Expand Down
2 changes: 1 addition & 1 deletion src/handler/lib/stream_preimage.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ int call_stream_preimage(dispatcher_context_t *dispatcher_context,
}
uint32_t preimage_len = (uint32_t) preimage_len_u64;

if (preimage_len < 1) {
if (preimage_len < 1 || partial_data_len == 0) {
// at least the initial 0x00 prefix should be there
return -3;
}
Expand Down
6 changes: 3 additions & 3 deletions src/handler/sign_psbt.c
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ init_global_state(dispatcher_context_t *dc, sign_psbt_state_t *st) {
1,
raw_result,
sizeof(raw_result));
if (result_len == -1) {
if (result_len < 0) {
st->locktime = 0;
} else if (result_len != 4) {
SEND_SW(dc, SW_INCORRECT_DATA);
Expand Down Expand Up @@ -962,7 +962,7 @@ preprocess_outputs(dispatcher_context_t *dc,
output.in_out.scriptPubKey,
sizeof(output.in_out.scriptPubKey));

if (result_len == -1 || result_len > (int) sizeof(output.in_out.scriptPubKey)) {
if (result_len < 0 || result_len > (int) sizeof(output.in_out.scriptPubKey)) {
SEND_SW(dc, SW_INCORRECT_DATA);
return false;
}
Expand Down Expand Up @@ -1272,7 +1272,7 @@ static bool get_output_script_and_amount(
out_scriptPubKey,
MAX_OUTPUT_SCRIPTPUBKEY_LEN);

if (result_len == -1 || result_len > MAX_OUTPUT_SCRIPTPUBKEY_LEN) {
if (result_len < 0 || result_len > MAX_OUTPUT_SCRIPTPUBKEY_LEN) {
SEND_SW(dc, SW_INCORRECT_DATA);
return false;
}
Expand Down
30 changes: 22 additions & 8 deletions src/handler/sign_psbt/musig_signing.c
Original file line number Diff line number Diff line change
Expand Up @@ -269,20 +269,22 @@ bool produce_and_yield_pubnonce(dispatcher_context_t *dc,
return false;
}

bool ret = false;
uint8_t rand_i_j[32];
compute_rand_i_j(psbt_session, cur_input_index, keyexpr_info->index, rand_i_j);

musig_secnonce_t secnonce;
musig_pubnonce_t pubnonce;
if (0 > musig_nonce_gen(rand_i_j,
sizeof(rand_i_j),
keyexpr_info->internal_pubkey.compressed_pubkey,
musig_per_input_info.agg_key_tweaked.compressed_pubkey + 1,
&secnonce,
&pubnonce)) {
int res = musig_nonce_gen(rand_i_j,
sizeof(rand_i_j),
keyexpr_info->internal_pubkey.compressed_pubkey,
musig_per_input_info.agg_key_tweaked.compressed_pubkey + 1,
&secnonce,
&pubnonce);
explicit_bzero(&secnonce, sizeof(secnonce));
if (0 > res) {
PRINTF("MuSig2 nonce generation failed\n");
SEND_SW(dc, SW_BAD_STATE); // should never happen
return false;
goto cleanup;
}

if (!yield_musig_pubnonce(dc,
Expand All @@ -293,6 +295,14 @@ bool produce_and_yield_pubnonce(dispatcher_context_t *dc,
musig_per_input_info.agg_key_tweaked.compressed_pubkey,
keyexpr_info->is_tapscript ? keyexpr_info->tapleaf_hash : NULL)) {
PRINTF("Failed yielding MuSig2 pubnonce\n");
goto cleanup;
}

ret = true;

cleanup:
explicit_bzero(rand_i_j, sizeof(rand_i_j));
if (!ret) {
SEND_SW(dc, SW_BAD_STATE); // should never happen
return false;
}
Expand Down Expand Up @@ -413,6 +423,8 @@ bool __attribute__((noinline)) sign_sighash_musig_and_yield(dispatcher_context_t
&secnonce,
&pubnonce)) {
PRINTF("MuSig2 nonce generation failed\n");
explicit_bzero(rand_i_j, sizeof(rand_i_j));
explicit_bzero(&secnonce, sizeof(secnonce));
SEND_SW(dc, SW_BAD_STATE); // should never happen
return false;
}
Expand Down Expand Up @@ -464,6 +476,8 @@ bool __attribute__((noinline)) sign_sighash_musig_and_yield(dispatcher_context_t
} while (false);

explicit_bzero(&private_key, sizeof(private_key));
explicit_bzero(rand_i_j, sizeof(rand_i_j));
explicit_bzero(&secnonce, sizeof(secnonce));

if (err) {
PRINTF("Partial signature generation failed\n");
Expand Down
2 changes: 1 addition & 1 deletion src/handler/sign_psbt/txhashes.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ static int hash_output_n(dispatcher_context_t *dc,
1,
out_script,
sizeof(out_script));
if (out_script_len == -1) {
if (out_script_len < 0) {
return -1;
}

Expand Down
55 changes: 37 additions & 18 deletions src/musig/musig.c
Original file line number Diff line number Diff line change
Expand Up @@ -264,27 +264,32 @@ int musig_nonce_gen(const uint8_t *rand,
uint8_t msg[] = {0x00};

musig_nonce_hash(rand, rand_len, pk, aggpk, 0, msg, 1, NULL, 0, secnonce->k_1);
if (CX_OK != cx_math_modm_no_throw(secnonce->k_1, 32, secp256k1_n, 32)) return -1;
if (CX_OK != cx_math_modm_no_throw(secnonce->k_1, 32, secp256k1_n, 32)) goto nonce_gen_fail;
musig_nonce_hash(rand, rand_len, pk, aggpk, 1, msg, 1, NULL, 0, secnonce->k_2);
if (CX_OK != cx_math_modm_no_throw(secnonce->k_2, 32, secp256k1_n, 32)) return -1;
if (CX_OK != cx_math_modm_no_throw(secnonce->k_2, 32, secp256k1_n, 32)) goto nonce_gen_fail;

if (is_array_all_zeros(secnonce->k_1, sizeof(secnonce->k_1)) ||
is_array_all_zeros(secnonce->k_2, sizeof(secnonce->k_2))) {
// this can only happen with negligible probability
return -1;
goto nonce_gen_fail;
}

memcpy(secnonce->pk, pk, sizeof(secnonce->pk));

point_t R_s1, R_s2;

if (CX_OK != point_mul(G, secnonce->k_1, &R_s1)) return -1;
if (CX_OK != point_mul(G, secnonce->k_2, &R_s2)) return -1;
if (CX_OK != point_mul(G, secnonce->k_1, &R_s1)) goto nonce_gen_fail;
if (CX_OK != point_mul(G, secnonce->k_2, &R_s2)) goto nonce_gen_fail;

if (0 > crypto_get_compressed_pubkey(R_s1.raw, pubnonce->R_s1)) return -1;
if (0 > crypto_get_compressed_pubkey(R_s2.raw, pubnonce->R_s2)) return -1;
if (0 > crypto_get_compressed_pubkey(R_s1.raw, pubnonce->R_s1)) goto nonce_gen_fail;
if (0 > crypto_get_compressed_pubkey(R_s2.raw, pubnonce->R_s2)) goto nonce_gen_fail;

return 0;

nonce_gen_fail:
explicit_bzero(secnonce->k_1, sizeof(secnonce->k_1));
explicit_bzero(secnonce->k_2, sizeof(secnonce->k_2));
return -1;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't really a vulnerability because in case of musig2 signing failures, the signing is invalidated and it has to start over from Round 1. So we'd be leaving in memory a 'secret' that is just a randomly generated number that is no longer useful for anything - a new attempt at signing will generate a new random number.
However, I guess the hardening doesn't hurt.

In that case, however, we should therefore also do the same in the callers of musig_nonce_gen:

  • produce_and_yield_pubnonce doesn't need the secnonce at all, so it can delete it straight away
  • sign_sighash_musig_and_yield should delete it once done (both on success and error cases).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Added immediate secnonce zeroing out in produce_and_yield_pubnonce()
  • In sign_sighash_musig_and_yield() if I'm reading correctly that was already the case:
    • secnonce iz zeroed out in case of musig_nonce_gen() error
    • it is zeroed out right after the latest use as well


int musig_nonce_agg(const musig_pubnonce_t pubnonces[], size_t n_keys, musig_pubnonce_t *out) {
Expand Down Expand Up @@ -484,40 +489,49 @@ int musig_sign(musig_secnonce_t *secnonce,
explicit_bzero(secnonce->k_1, sizeof(secnonce->k_1));
explicit_bzero(secnonce->k_2, sizeof(secnonce->k_2));

bool err = false;
uint8_t bk_2[32];

if (CX_OK != cx_math_cmp_no_throw(k_1, secp256k1_n, 32, &diff)) {
return -1;
err = true;
goto cleanup;
}
if (is_array_all_zeros(k_1, sizeof(k_1)) || diff >= 0) {
PRINTF("first secnonce value is out of range\n");
return -1;
err = true;
goto cleanup;
}
if (CX_OK != cx_math_cmp_no_throw(k_2, secp256k1_n, 32, &diff)) {
return -1;
err = true;
goto cleanup;
}
if (is_array_all_zeros(k_2, sizeof(k_2)) || diff >= 0) {
PRINTF("second secnonce value is out of range\n");
return -1;
err = true;
goto cleanup;
}

if (!has_even_y(&R)) {
if (CX_OK != cx_math_sub_no_throw(k_1, secp256k1_n, k_1, 32)) {
return -1;
err = true;
goto cleanup;
};
if (CX_OK != cx_math_sub_no_throw(k_2, secp256k1_n, k_2, 32)) {
return -1;
err = true;
goto cleanup;
};
}

if (CX_OK != cx_math_cmp_no_throw(sk, secp256k1_n, 32, &diff)) {
return -1;
err = true;
goto cleanup;
}
if (is_array_all_zeros(sk, 32) || diff >= 0) {
PRINTF("secret key value is out of range\n");
return -1;
err = true;
goto cleanup;
}

bool err = false;

// Put together all the variables that we want to always zero out before returning.
// As an excess of safety, we put here any variable that is (directly or indirectly) derived
// from the secret during the computation of the signature
Expand Down Expand Up @@ -574,7 +588,7 @@ int musig_sign(musig_secnonce_t *secnonce,
break;
}

uint8_t bk_2[32]; // b * k_2
// bk_2 = b * k_2
if (CX_OK != cx_math_multm_no_throw(bk_2, b, k_2, secp256k1_n, 32)) {
err = true;
break;
Expand Down Expand Up @@ -607,6 +621,11 @@ int musig_sign(musig_secnonce_t *secnonce,
// make sure to zero out any variable derived from secrets before returning
explicit_bzero(&secrets, sizeof(secrets));

cleanup:
explicit_bzero(k_1, sizeof(k_1));
explicit_bzero(k_2, sizeof(k_2));
explicit_bzero(bk_2, sizeof(bk_2));

if (err) {
return -1;
}
Expand Down
Loading