1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-04-15 23:05:45 +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_SCHNORRSIG \
-DENABLE_MODULE_EXTRAKEYS \
-DENABLE_MODULE_ECDH
-DENABLE_MODULE_ECDH \
-DENABLE_MODULE_ECDSA_S2C
ZKP_PATH = ../vendor/secp256k1-zkp
# this is specific for 64-bit builds
CFLAGS += -DSECP256K1_CONTEXT_SIZE=208

View File

@ -4172,6 +4172,78 @@ START_TEST(test_zkp_ecdsa_sign_digest_recoverable_deterministic) {
}
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
// http://www.inconteam.com/software-development/41-encryption/55-aes-test-vectors
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_zkp_ecdsa_sign_digest_recoverable_deterministic);
#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);
tc = tcase_create("rfc6979");

View File

@ -28,9 +28,11 @@
#include "memzero.h"
#include "secp256k1.h"
#include "zkp_context.h"
#include "zkp_ecdsa.h"
#include "vendor/secp256k1-zkp/include/secp256k1.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 "zkp_ecdsa.h"
@ -260,6 +262,125 @@ int zkp_ecdsa_sign_digest_recoverable(
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
// public_key_bytes has 65 bytes
// signature_bytes has 64 bytes
@ -338,12 +459,12 @@ int zkp_ecdsa_verify_digest(const ecdsa_curve *curve,
if (result == 0) {
if (is_zero_digest(digest)) {
// The digest was all-zero. The probability of this happening by chance is
// infinitesimal, but it could be induced by a fault injection. In this
// case the signature (r,s) can be forged by taking r := (t * Q).x mod n
// and s := r * t^-1 mod n for any t in [1, n-1]. We fail verification,
// because there is no guarantee that the signature was created by the
// owner of the private key.
// The digest was all-zero. The probability of this happening by
// chance is infinitesimal, but it could be induced by a fault
// injection. In this case the signature (r,s) can be forged by taking
// r := (t * Q).x mod n and s := r * t^-1 mod n for any t in [1, n-1].
// We fail verification, because there is no guarantee that the
// signature was created by the owner of the private key.
result = 3;
}
}
@ -370,8 +491,8 @@ int zkp_ecdsa_verify_digest(const ecdsa_curve *curve,
if (result == 0) {
(void)secp256k1_ecdsa_signature_normalize(
context_read_only, &signature,
&signature); // The return value inidicates whether the signature was
// already normalized
&signature); // The return value inidicates whether the signature
// was already normalized
if (secp256k1_ecdsa_verify(context_read_only, &signature, digest,
&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,
tweak_bytes) != 1) {
// The tweak is not less than the group order, or the resulting public key
// is the point at infinity.
// The tweak is not less than the group order, or the resulting public
// key is the point at infinity.
result = ECDSA_TWEAK_PUBKEY_INVALID_TWEAK_OR_RESULT_ERR;
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(
const ecdsa_curve *curve, const uint8_t *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