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:
parent
55031fb30b
commit
d202a6bf49
@ -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
|
||||||
|
@ -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");
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user