1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-06-25 17:32:34 +00:00

feat(crypto): add wrappers for anti-exfil functions

This commit is contained in:
Ondřej Vejpustek 2025-03-03 20:54:18 +01:00
parent 55031fb30b
commit d202a6bf49
4 changed files with 217 additions and 11 deletions

View File

@ -70,7 +70,8 @@ ZKP_CFLAGS = \
-DENABLE_MODULE_RECOVERY \ -DENABLE_MODULE_RECOVERY \
-DENABLE_MODULE_SCHNORRSIG \ -DENABLE_MODULE_SCHNORRSIG \
-DENABLE_MODULE_EXTRAKEYS \ -DENABLE_MODULE_EXTRAKEYS \
-DENABLE_MODULE_ECDH -DENABLE_MODULE_ECDH \
-DENABLE_MODULE_ECDSA_S2C
ZKP_PATH = ../vendor/secp256k1-zkp ZKP_PATH = ../vendor/secp256k1-zkp
# this is specific for 64-bit builds # this is specific for 64-bit builds
CFLAGS += -DSECP256K1_CONTEXT_SIZE=208 CFLAGS += -DSECP256K1_CONTEXT_SIZE=208

View File

@ -4172,6 +4172,78 @@ START_TEST(test_zkp_ecdsa_sign_digest_recoverable_deterministic) {
} }
END_TEST END_TEST
START_TEST(test_zkp_ecdsa_anti_exfil_commit_nonce) {
static const struct {
const char *priv_key;
const char *digest;
const char *entropy;
const char *expected_nonce_commitment;
} tests[] = {
{"8521fa4b1f08d39b91e2fcc6a342b2395fbada5146c494be485b6d5ff8002da0",
"0d841da7d1d6c5edb90864118f52c865f2ac56deb13a41229ef7cbd2951593fd",
"88e381faa8f2297cd6295b19e8a3563291e4c86a873f9a758b0cd6629a102eac",
"032b756f0b1937eef6d269b35781c7a1a5435e00e3c06245ef2162d51c15b2992c"}};
const ecdsa_curve *curve = &secp256k1;
uint8_t priv_key[32] = {0};
uint8_t digest[32] = {0};
uint8_t entropy[32] = {0};
uint8_t nonce_commitment[64] = {0};
uint8_t expected_nonce_commitment[64] = {0};
int res = 0;
for (size_t i = 0; i < sizeof(tests) / sizeof(*tests); i++) {
memcpy(priv_key, fromhex(tests[i].priv_key), 32);
memcpy(digest, fromhex(tests[i].digest), 32);
memcpy(entropy, fromhex(tests[i].entropy), 32);
memcpy(expected_nonce_commitment,
fromhex(tests[i].expected_nonce_commitment), 33);
res = zkp_ecdsa_anti_exfil_commit_nonce(curve, priv_key, digest, entropy,
nonce_commitment);
ck_assert_int_eq(res, 0);
ck_assert_mem_eq(expected_nonce_commitment, nonce_commitment, 33);
}
}
END_TEST
START_TEST(test_zkp_ecdsa_anti_exfil_sign_digest) {
static const struct {
const char *priv_key;
const char *digest;
const char *entropy_commitment;
const char *expected_sig;
} tests[] = {
{"8521fa4b1f08d39b91e2fcc6a342b2395fbada5146c494be485b6d5ff8002da0",
"0d841da7d1d6c5edb90864118f52c865f2ac56deb13a41229ef7cbd2951593fd",
// entropy:
// 88e381faa8f2297cd6295b19e8a3563291e4c86a873f9a758b0cd6629a102eac
"bc07f96c351484fecb4c1b1820070c446f4f5398530e85ed2955041bdef54abf",
"e4450fb09d21528a3b75e34e1a7d0872f8f02f243087f69a472029c6622ed9790b1855b"
"91149ea55d5336b0967d25c73890b126220dd4ad885b69b65028178da"}};
const ecdsa_curve *curve = &secp256k1;
uint8_t priv_key[32] = {0};
uint8_t digest[32] = {0};
uint8_t entropy_commitment[32] = {0};
uint8_t expected_sig[64] = {0};
uint8_t computed_sig[64] = {0};
int res = 0;
for (size_t i = 0; i < sizeof(tests) / sizeof(*tests); i++) {
memcpy(priv_key, fromhex(tests[i].priv_key), 32);
memcpy(digest, fromhex(tests[i].digest), 32);
memcpy(entropy_commitment, fromhex(tests[i].entropy_commitment), 32);
memcpy(expected_sig, fromhex(tests[i].expected_sig), 64);
res = zkp_ecdsa_anti_exfil_sign_digest(curve, priv_key, digest,
entropy_commitment, computed_sig);
ck_assert_int_eq(res, 0);
ck_assert_mem_eq(expected_sig, computed_sig, 64);
}
}
END_TEST
// test vectors from // test vectors from
// http://www.inconteam.com/software-development/41-encryption/55-aes-test-vectors // http://www.inconteam.com/software-development/41-encryption/55-aes-test-vectors
START_TEST(test_aes) { START_TEST(test_aes) {
@ -11524,6 +11596,8 @@ Suite *test_suite(void) {
tcase_add_test(tc, test_tc_ecdsa_sign_digest_recoverable_deterministic); tcase_add_test(tc, test_tc_ecdsa_sign_digest_recoverable_deterministic);
tcase_add_test(tc, test_zkp_ecdsa_sign_digest_recoverable_deterministic); tcase_add_test(tc, test_zkp_ecdsa_sign_digest_recoverable_deterministic);
#endif #endif
tcase_add_test(tc, test_zkp_ecdsa_anti_exfil_commit_nonce);
tcase_add_test(tc, test_zkp_ecdsa_anti_exfil_sign_digest);
suite_add_tcase(s, tc); suite_add_tcase(s, tc);
tc = tcase_create("rfc6979"); tc = tcase_create("rfc6979");

View File

@ -28,9 +28,11 @@
#include "memzero.h" #include "memzero.h"
#include "secp256k1.h" #include "secp256k1.h"
#include "zkp_context.h" #include "zkp_context.h"
#include "zkp_ecdsa.h"
#include "vendor/secp256k1-zkp/include/secp256k1.h" #include "vendor/secp256k1-zkp/include/secp256k1.h"
#include "vendor/secp256k1-zkp/include/secp256k1_ecdh.h" #include "vendor/secp256k1-zkp/include/secp256k1_ecdh.h"
#include "vendor/secp256k1-zkp/include/secp256k1_ecdsa_s2c.h"
#include "vendor/secp256k1-zkp/include/secp256k1_recovery.h" #include "vendor/secp256k1-zkp/include/secp256k1_recovery.h"
#include "zkp_ecdsa.h" #include "zkp_ecdsa.h"
@ -260,6 +262,125 @@ int zkp_ecdsa_sign_digest_recoverable(
return result; return result;
} }
// anti-exfil ECDSA signing
// curve has to be &secp256k1
// private_key_bytes has 32 bytes
// digest has 32 bytes
// entropy_commitment has 32 bytes
// signature_bytes has 64 bytes
// returns 0 on success
int zkp_ecdsa_anti_exfil_sign_digest(const ecdsa_curve *curve,
const uint8_t *private_key_bytes,
const uint8_t *digest,
const uint8_t *entropy,
uint8_t *signature_bytes) {
int result = 0;
secp256k1_context *context_writable = NULL;
if (curve != &secp256k1) {
result = 1;
goto cleanup;
}
if (is_zero_digest(digest)) {
// The probability of the digest being all-zero by chance is
// infinitesimal, so this is most likely an indication of a bug.
// Furthermore, the signature has no value, because in this case it can be
// easily forged for any public key, see zkp_ecdsa_verify_digest().
result = 1;
goto cleanup;
}
context_writable = zkp_context_acquire_writable();
if (context_writable == NULL) {
result = 1;
goto cleanup;
}
if (secp256k1_context_writable_randomize(context_writable) != 0) {
result = 1;
goto cleanup;
}
secp256k1_ecdsa_signature signature = {0};
if (secp256k1_anti_exfil_sign(context_writable, &signature, digest,
private_key_bytes, entropy) != 1) {
result = 1;
goto cleanup;
}
const secp256k1_context *context_read_only = zkp_context_get_read_only();
if (secp256k1_ecdsa_signature_serialize_compact(
context_read_only, signature_bytes, &signature) != 1) {
result = 1;
goto cleanup;
}
cleanup:
if (context_writable) {
zkp_context_release_writable();
context_writable = NULL;
}
return result;
}
// anti-exfil ECDSA commit
// curve has to be &secp256k1
// private_key_bytes has 32 bytes
// digest has 32 bytes
// entropy_commitment has 32 bytes
// public_nonce_bytes has 33 bytes
// returns 0 on success
int zkp_ecdsa_anti_exfil_commit_nonce(const ecdsa_curve *curve,
const uint8_t *private_key_bytes,
const uint8_t *digest,
const uint8_t *entropy_commitment,
uint8_t *nonce_commitment) {
int result = 0;
secp256k1_context *context_writable = NULL;
assert(curve == &secp256k1);
if (curve != &secp256k1) {
result = 1;
goto cleanup;
}
context_writable = zkp_context_acquire_writable();
if (context_writable == NULL) {
result = 1;
goto cleanup;
}
if (secp256k1_context_writable_randomize(context_writable) != 0) {
result = 1;
goto cleanup;
}
secp256k1_ecdsa_s2c_opening s2c_opening = {0};
if (secp256k1_ecdsa_anti_exfil_signer_commit(context_writable, &s2c_opening,
digest, private_key_bytes,
entropy_commitment) != 1) {
result = 1;
goto cleanup;
}
const secp256k1_context *context_read_only = zkp_context_get_read_only();
if (secp256k1_ecdsa_s2c_opening_serialize(context_read_only, nonce_commitment,
&s2c_opening) != 1) {
result = 1;
goto cleanup;
}
cleanup:
if (context_writable) {
zkp_context_release_writable();
context_writable = NULL;
}
return result;
}
// ECDSA public key recovery // ECDSA public key recovery
// public_key_bytes has 65 bytes // public_key_bytes has 65 bytes
// signature_bytes has 64 bytes // signature_bytes has 64 bytes
@ -338,12 +459,12 @@ int zkp_ecdsa_verify_digest(const ecdsa_curve *curve,
if (result == 0) { if (result == 0) {
if (is_zero_digest(digest)) { if (is_zero_digest(digest)) {
// The digest was all-zero. The probability of this happening by chance is // The digest was all-zero. The probability of this happening by
// infinitesimal, but it could be induced by a fault injection. In this // chance is infinitesimal, but it could be induced by a fault
// case the signature (r,s) can be forged by taking r := (t * Q).x mod n // injection. In this case the signature (r,s) can be forged by taking
// and s := r * t^-1 mod n for any t in [1, n-1]. We fail verification, // r := (t * Q).x mod n and s := r * t^-1 mod n for any t in [1, n-1].
// because there is no guarantee that the signature was created by the // We fail verification, because there is no guarantee that the
// owner of the private key. // signature was created by the owner of the private key.
result = 3; result = 3;
} }
} }
@ -370,8 +491,8 @@ int zkp_ecdsa_verify_digest(const ecdsa_curve *curve,
if (result == 0) { if (result == 0) {
(void)secp256k1_ecdsa_signature_normalize( (void)secp256k1_ecdsa_signature_normalize(
context_read_only, &signature, context_read_only, &signature,
&signature); // The return value inidicates whether the signature was &signature); // The return value inidicates whether the signature
// already normalized // was already normalized
if (secp256k1_ecdsa_verify(context_read_only, &signature, digest, if (secp256k1_ecdsa_verify(context_read_only, &signature, digest,
&public_key) != 1) { &public_key) != 1) {
@ -515,8 +636,8 @@ ecdsa_tweak_pubkey_result zkp_ecdsa_tweak_pubkey(
if (secp256k1_ec_pubkey_tweak_add(context_writable, &public_key, if (secp256k1_ec_pubkey_tweak_add(context_writable, &public_key,
tweak_bytes) != 1) { tweak_bytes) != 1) {
// The tweak is not less than the group order, or the resulting public key // The tweak is not less than the group order, or the resulting public
// is the point at infinity. // key is the point at infinity.
result = ECDSA_TWEAK_PUBKEY_INVALID_TWEAK_OR_RESULT_ERR; result = ECDSA_TWEAK_PUBKEY_INVALID_TWEAK_OR_RESULT_ERR;
goto end; goto end;
} }

View File

@ -32,4 +32,14 @@ int zkp_ecdh_multiply(const ecdsa_curve *curve, const uint8_t *priv_key,
ecdsa_tweak_pubkey_result zkp_ecdsa_tweak_pubkey( ecdsa_tweak_pubkey_result zkp_ecdsa_tweak_pubkey(
const ecdsa_curve *curve, const uint8_t *public_key_bytes, const ecdsa_curve *curve, const uint8_t *public_key_bytes,
const uint8_t *tweak_bytes, uint8_t *tweaked_public_key_bytes); const uint8_t *tweak_bytes, uint8_t *tweaked_public_key_bytes);
int zkp_ecdsa_anti_exfil_sign_digest(const ecdsa_curve *curve,
const uint8_t *private_key_bytes,
const uint8_t *digest,
const uint8_t *entropy,
uint8_t *signature_bytes);
int zkp_ecdsa_anti_exfil_commit_nonce(const ecdsa_curve *curve,
const uint8_t *private_key_bytes,
const uint8_t *digest,
const uint8_t *entropy_commitment,
uint8_t *nonce_commitment);
#endif #endif