mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-22 07:28:10 +00:00
fix(crypto): Fix nonce bias in CoSi signing.
This commit is contained in:
parent
b7dde50d5f
commit
fa5e7feda6
@ -240,6 +240,25 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(
|
||||
mod_trezorcrypto_ed25519_cosi_combine_signatures_obj,
|
||||
mod_trezorcrypto_ed25519_cosi_combine_signatures);
|
||||
|
||||
/// def cosi_commit() -> tuple[bytes, bytes]:
|
||||
/// """
|
||||
/// Generate a nonce and commitment for the CoSi cosigning scheme.
|
||||
/// """
|
||||
STATIC mp_obj_t mod_trezorcrypto_ed25519_cosi_commit() {
|
||||
vstr_t nonce = {0};
|
||||
vstr_t commitment = {0};
|
||||
vstr_init_len(&nonce, 32);
|
||||
vstr_init_len(&commitment, 32);
|
||||
ed25519_cosi_commit(*(ed25519_secret_key *)nonce.buf,
|
||||
*(ed25519_public_key *)commitment.buf);
|
||||
mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(2, NULL));
|
||||
tuple->items[0] = mp_obj_new_str_from_vstr(&mp_type_bytes, &nonce);
|
||||
tuple->items[1] = mp_obj_new_str_from_vstr(&mp_type_bytes, &commitment);
|
||||
return MP_OBJ_FROM_PTR(tuple);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorcrypto_ed25519_cosi_commit_obj,
|
||||
mod_trezorcrypto_ed25519_cosi_commit);
|
||||
|
||||
/// def cosi_sign(
|
||||
/// secret_key: bytes,
|
||||
/// message: bytes,
|
||||
@ -272,12 +291,15 @@ STATIC mp_obj_t mod_trezorcrypto_ed25519_cosi_sign(size_t n_args,
|
||||
}
|
||||
vstr_t sig = {0};
|
||||
vstr_init_len(&sig, sizeof(ed25519_cosi_signature));
|
||||
;
|
||||
ed25519_cosi_sign(msg.buf, msg.len, *(const ed25519_secret_key *)sk.buf,
|
||||
*(const ed25519_secret_key *)nonce.buf,
|
||||
*(const ed25519_public_key *)sigR.buf,
|
||||
*(const ed25519_secret_key *)pk.buf,
|
||||
*(ed25519_cosi_signature *)sig.buf);
|
||||
if (0 != ed25519_cosi_sign(msg.buf, msg.len,
|
||||
*(const ed25519_secret_key *)sk.buf,
|
||||
*(const ed25519_secret_key *)nonce.buf,
|
||||
*(const ed25519_public_key *)sigR.buf,
|
||||
*(const ed25519_secret_key *)pk.buf,
|
||||
*(ed25519_cosi_signature *)sig.buf)) {
|
||||
vstr_clear(&sig);
|
||||
mp_raise_ValueError("Signing failed");
|
||||
}
|
||||
return mp_obj_new_str_from_vstr(&mp_type_bytes, &sig);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(
|
||||
@ -301,6 +323,8 @@ STATIC const mp_rom_map_elem_t mod_trezorcrypto_ed25519_globals_table[] = {
|
||||
MP_ROM_PTR(&mod_trezorcrypto_ed25519_cosi_combine_publickeys_obj)},
|
||||
{MP_ROM_QSTR(MP_QSTR_cosi_combine_signatures),
|
||||
MP_ROM_PTR(&mod_trezorcrypto_ed25519_cosi_combine_signatures_obj)},
|
||||
{MP_ROM_QSTR(MP_QSTR_cosi_commit),
|
||||
MP_ROM_PTR(&mod_trezorcrypto_ed25519_cosi_commit_obj)},
|
||||
{MP_ROM_QSTR(MP_QSTR_cosi_sign),
|
||||
MP_ROM_PTR(&mod_trezorcrypto_ed25519_cosi_sign_obj)},
|
||||
};
|
||||
|
@ -53,6 +53,13 @@ def cosi_combine_signatures(R: bytes, signatures: list[bytes]) -> bytes:
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-ed25519.h
|
||||
def cosi_commit() -> tuple[bytes, bytes]:
|
||||
"""
|
||||
Generate a nonce and commitment for the CoSi cosigning scheme.
|
||||
"""
|
||||
|
||||
|
||||
# extmod/modtrezorcrypto/modtrezorcrypto-ed25519.h
|
||||
def cosi_sign(
|
||||
secret_key: bytes,
|
||||
|
@ -26,8 +26,7 @@ class TestCryptoEd25519Cosi(unittest.TestCase):
|
||||
nonces = [None] * N
|
||||
Rs = [None] * N
|
||||
for j in range(N):
|
||||
nonces[j] = ed25519.generate_secret()
|
||||
Rs[j] = ed25519.publickey(nonces[j])
|
||||
nonces[j], Rs[j] = ed25519.cosi_commit()
|
||||
|
||||
R = ed25519.cosi_combine_publickeys(Rs)
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "ed25519.h"
|
||||
|
||||
#include "ed25519-hash-custom.h"
|
||||
#include "rand.h"
|
||||
#include "memzero.h"
|
||||
|
||||
/*
|
||||
@ -50,16 +51,34 @@ ED25519_FN(ed25519_publickey) (const ed25519_secret_key sk, ed25519_public_key p
|
||||
}
|
||||
|
||||
void
|
||||
ED25519_FN(ed25519_cosi_commit) (ed25519_secret_key nonce, ed25519_public_key commitment) {
|
||||
bignum256modm r = {0};
|
||||
ge25519 ALIGN(16) R;
|
||||
unsigned char extnonce[64] = {0};
|
||||
|
||||
/* r = random512 mod L */
|
||||
random_buffer(extnonce, sizeof(extnonce));
|
||||
expand256_modm(r, extnonce, sizeof(extnonce));
|
||||
memzero(&extnonce, sizeof(extnonce));
|
||||
contract256_modm(nonce, r);
|
||||
|
||||
/* R = rB */
|
||||
ge25519_scalarmult_base_niels(&R, ge25519_niels_base_multiples, r);
|
||||
memzero(&r, sizeof(r));
|
||||
ge25519_pack(commitment, &R);
|
||||
}
|
||||
|
||||
int
|
||||
ED25519_FN(ed25519_cosi_sign) (const unsigned char *m, size_t mlen, const ed25519_secret_key sk, const ed25519_secret_key nonce, const ed25519_public_key R, const ed25519_public_key pk, ed25519_cosi_signature sig) {
|
||||
bignum256modm r = {0}, S = {0}, a = {0};
|
||||
hash_512bits extsk = {0}, extnonce = {0}, hram = {0};
|
||||
hash_512bits extsk = {0}, hram = {0};
|
||||
|
||||
ed25519_extsk(extsk, sk);
|
||||
ed25519_extsk(extnonce, nonce);
|
||||
|
||||
/* r = nonce */
|
||||
expand256_modm(r, extnonce, 32);
|
||||
memzero(&extnonce, sizeof(extnonce));
|
||||
/* r */
|
||||
expand_raw256_modm(r, nonce);
|
||||
if (!is_reduced256_modm(r))
|
||||
return -1;
|
||||
|
||||
/* S = H(R,A,m).. */
|
||||
ed25519_hram(hram, R, pk, m, mlen);
|
||||
@ -77,6 +96,8 @@ ED25519_FN(ed25519_cosi_sign) (const unsigned char *m, size_t mlen, const ed2551
|
||||
|
||||
/* S = (r + H(R,A,m)a) mod L */
|
||||
contract256_modm(sig, S);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
@ -155,7 +176,7 @@ ED25519_FN(ed25519_sign_open) (const unsigned char *m, size_t mlen, const ed2551
|
||||
/* S */
|
||||
expand_raw256_modm(S, RS + 32);
|
||||
if (!is_reduced256_modm(S))
|
||||
return -1;
|
||||
return -1;
|
||||
|
||||
/* SB - H(R,A,m)A */
|
||||
ge25519_double_scalarmult_vartime(&R, &A, hram, S);
|
||||
|
@ -35,7 +35,8 @@ void curve25519_scalarmult_basepoint(curve25519_key mypublic, const curve25519_k
|
||||
|
||||
int ed25519_cosi_combine_publickeys(ed25519_public_key res, CONST ed25519_public_key *pks, size_t n);
|
||||
void ed25519_cosi_combine_signatures(ed25519_signature res, const ed25519_public_key R, CONST ed25519_cosi_signature *sigs, size_t n);
|
||||
void ed25519_cosi_sign(const unsigned char *m, size_t mlen, const ed25519_secret_key key, const ed25519_secret_key nonce, const ed25519_public_key R, const ed25519_public_key pk, ed25519_cosi_signature sig);
|
||||
void ed25519_cosi_commit(ed25519_secret_key nonce, ed25519_public_key commitment);
|
||||
int ed25519_cosi_sign(const unsigned char *m, size_t mlen, const ed25519_secret_key key, const ed25519_secret_key nonce, const ed25519_public_key R, const ed25519_public_key pk, ed25519_cosi_signature sig);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
|
@ -6926,8 +6926,7 @@ START_TEST(test_ed25519_cosi) {
|
||||
|
||||
/* phase 1: create nonces, commitments (R values) and combine commitments */
|
||||
for (int j = 0; j < N; j++) {
|
||||
generate_rfc6979(nonces[j], &rng);
|
||||
ed25519_publickey(nonces[j], Rs[j]);
|
||||
ed25519_cosi_commit(nonces[j], Rs[j]);
|
||||
}
|
||||
res = ed25519_cosi_combine_publickeys(R, Rs, N);
|
||||
ck_assert_int_eq(res, 0);
|
||||
@ -6935,7 +6934,9 @@ START_TEST(test_ed25519_cosi) {
|
||||
MARK_SECRET_DATA(keys, sizeof(keys));
|
||||
/* phase 2: sign and combine signatures */
|
||||
for (int j = 0; j < N; j++) {
|
||||
ed25519_cosi_sign(msg, sizeof(msg), keys[j], nonces[j], R, pk, sigs[j]);
|
||||
res = ed25519_cosi_sign(msg, sizeof(msg), keys[j], nonces[j], R, pk,
|
||||
sigs[j]);
|
||||
ck_assert_int_eq(res, 0);
|
||||
}
|
||||
UNMARK_SECRET_DATA(sigs, sizeof(sigs));
|
||||
|
||||
|
1
legacy/firmware/.changelog.d/noissue.security.2
Normal file
1
legacy/firmware/.changelog.d/noissue.security.2
Normal file
@ -0,0 +1 @@
|
||||
Fix nonce bias in CoSi signing.
|
@ -18,6 +18,7 @@
|
||||
*/
|
||||
|
||||
static uint8_t cosi_nonce[32] = {0};
|
||||
static uint8_t cosi_commitment[32] = {0};
|
||||
static bool cosi_nonce_is_set = false;
|
||||
|
||||
void fsm_msgCipherKeyValue(const CipherKeyValue *msg) {
|
||||
@ -271,7 +272,7 @@ void fsm_msgCosiCommit(const CosiCommit *msg) {
|
||||
if (!node) return;
|
||||
|
||||
if (!cosi_nonce_is_set) {
|
||||
random_buffer(cosi_nonce, sizeof(cosi_nonce));
|
||||
ed25519_cosi_commit(cosi_nonce, cosi_commitment);
|
||||
cosi_nonce_is_set = true;
|
||||
}
|
||||
|
||||
@ -280,7 +281,7 @@ void fsm_msgCosiCommit(const CosiCommit *msg) {
|
||||
resp->commitment.size = 32;
|
||||
resp->pubkey.size = 32;
|
||||
|
||||
ed25519_publickey(cosi_nonce, resp->commitment.bytes);
|
||||
memcpy(resp->commitment.bytes, cosi_commitment, sizeof(cosi_commitment));
|
||||
ed25519_publickey(node->private_key, resp->pubkey.bytes);
|
||||
|
||||
msg_write(MessageType_MessageType_CosiCommitment, resp);
|
||||
@ -326,11 +327,13 @@ void fsm_msgCosiSign(const CosiSign *msg) {
|
||||
resp->signature.size = 32;
|
||||
cosi_nonce_is_set = false;
|
||||
|
||||
ed25519_cosi_sign(msg->data.bytes, msg->data.size, node->private_key,
|
||||
cosi_nonce, msg->global_commitment.bytes,
|
||||
msg->global_pubkey.bytes, resp->signature.bytes);
|
||||
if (ed25519_cosi_sign(msg->data.bytes, msg->data.size, node->private_key,
|
||||
cosi_nonce, msg->global_commitment.bytes,
|
||||
msg->global_pubkey.bytes, resp->signature.bytes) == 0) {
|
||||
msg_write(MessageType_MessageType_CosiSignature, resp);
|
||||
} else {
|
||||
fsm_sendFailure(FailureType_Failure_FirmwareError, NULL);
|
||||
}
|
||||
memzero(cosi_nonce, sizeof(cosi_nonce));
|
||||
|
||||
msg_write(MessageType_MessageType_CosiSignature, resp);
|
||||
layoutHome();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user