feat(crypto): introduce ecdsa_tweak_public_key

[no changelog]
pull/4129/head
Ondřej Vejpustek 1 month ago
parent a5fd5e179f
commit a12a81a321

@ -1142,6 +1142,49 @@ int tc_ecdsa_verify_digest(const ecdsa_curve *curve, const uint8_t *pub_key,
return result;
}
ecdsa_tweak_pubkey_result tc_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 result = ECDSA_TWEAK_PUBKEY_SUCCESS;
curve_point public_key = {0};
bignum256 tweak = {0};
curve_point public_tweak = {0};
if (public_key_bytes[0] != 0x02 && public_key_bytes[0] != 0x03) {
result = ECDSA_TWEAK_PUBKEY_INVALID_PUBKEY_ERR;
goto end;
}
if (!ecdsa_read_pubkey(curve, public_key_bytes, &public_key)) {
result = ECDSA_TWEAK_PUBKEY_INVALID_PUBKEY_ERR;
goto end;
}
bn_read_be(tweak_bytes, &tweak);
if (!bn_is_less(&tweak, &curve->order)) {
result = ECDSA_TWEAK_PUBKEY_INVALID_TWEAK_OR_RESULT_ERR;
goto end;
}
(void)scalar_multiply(curve, &tweak, &public_tweak);
point_add(curve, &public_tweak, &public_key);
if (point_is_infinity(&public_key)) {
result = ECDSA_TWEAK_PUBKEY_INVALID_TWEAK_OR_RESULT_ERR;
goto end;
}
tweaked_public_key_bytes[0] = 0x02 | (public_key.y.val[0] & 0x01);
bn_write_be(&public_key.x, tweaked_public_key_bytes + 1);
end:
memzero(&public_key, sizeof(public_key));
memzero(&tweak, sizeof(tweak));
memzero(&public_tweak, sizeof(public_tweak));
return result;
}
int ecdsa_sig_to_der(const uint8_t *sig, uint8_t *der) {
int i = 0;
uint8_t *p = der, *len = NULL, *len1 = NULL, *len2 = NULL;
@ -1322,3 +1365,15 @@ int ecdh_multiply(const ecdsa_curve *curve, const uint8_t *priv_key,
#endif
return tc_ecdh_multiply(curve, priv_key, pub_key, session_key);
}
ecdsa_tweak_pubkey_result ecdsa_tweak_pubkey(const ecdsa_curve *curve,
const uint8_t *pub_key,
const uint8_t *tweak,
uint8_t *tweaked_pub_key) {
#ifdef USE_SECP256K1_ZKP_ECDSA
if (curve == &secp256k1) {
return zkp_ecdsa_tweak_pubkey(curve, pub_key, tweak, tweaked_pub_key);
}
#endif
return tc_ecdsa_tweak_pubkey(curve, pub_key, tweak, tweaked_pub_key);
}

@ -125,4 +125,17 @@ int ecdsa_recover_pub_from_sig(const ecdsa_curve *curve, uint8_t *pub_key,
int ecdsa_sig_to_der(const uint8_t *sig, uint8_t *der);
int ecdsa_sig_from_der(const uint8_t *der, size_t der_len, uint8_t sig[64]);
typedef enum {
ECDSA_TWEAK_PUBKEY_SUCCESS = 0,
ECDSA_TWEAK_PUBKEY_INVALID_CURVE_ERR,
ECDSA_TWEAK_PUBKEY_CONTEXT_ERR,
ECDSA_TWEAK_PUBKEY_INVALID_PUBKEY_ERR,
ECDSA_TWEAK_PUBKEY_INVALID_TWEAK_OR_RESULT_ERR,
ECDSA_TWEAK_PUBKEY_UNSPECIFIED_ERR,
} ecdsa_tweak_pubkey_result;
ecdsa_tweak_pubkey_result ecdsa_tweak_pubkey(const ecdsa_curve *curve,
const uint8_t *pub_key,
const uint8_t *tweak,
uint8_t *tweaked_pub_key);
#endif

@ -41,4 +41,7 @@ int tc_ecdsa_recover_pub_from_sig(const ecdsa_curve *curve, uint8_t *pub_key,
int recid);
int tc_ecdh_multiply(const ecdsa_curve *curve, const uint8_t *priv_key,
const uint8_t *pub_key, uint8_t *session_key);
ecdsa_tweak_pubkey_result tc_ecdsa_tweak_pubkey(
const ecdsa_curve *curve, const uint8_t *public_key_bytes,
const uint8_t *tweak_bytes, uint8_t *tweaked_public_key_bytes);
#endif

@ -4510,6 +4510,81 @@ START_TEST(test_zkp_ecdh_multiply) {
}
END_TEST
static void test_ecdsa_tweak_pubkey_helper(ecdsa_tweak_pubkey_result (
*ecdsa_tweak_pubkey_fn)(const ecdsa_curve *curve, const uint8_t *pub_key,
const uint8_t *priv_tweak,
uint8_t *tweaked_pub_key)) {
static struct {
const char *pub_key;
const char *tweak;
ecdsa_tweak_pubkey_result res;
const char *tweaked_pub_key;
} tests[] = {
{"03062c585e0bf279a06f1741a481e1fc5d6f85fe98c66ba4535b80e72b8cff459e",
"ca3f9fcb28ca7a6819ad4d233a26b2d4cda78dea15278b162428bbb88c36b7f9",
ECDSA_TWEAK_PUBKEY_SUCCESS,
"03375d82263d6a01f13be81210d0c20cafd4b15f1896a9e4acb8bb9c4ee2e524e2"},
// Zero private tweak
{"03a61dfafced7279cda56dd9535d639b9eb1fde842ffb5c1741efb7a5de61f1784",
"0000000000000000000000000000000000000000000000000000000000000000",
ECDSA_TWEAK_PUBKEY_SUCCESS,
"03a61dfafced7279cda56dd9535d639b9eb1fde842ffb5c1741efb7a5de61f1784"},
// Invalid public key - uncompressed format
{"0480622d89bb56d2752861db0f0d95ab90dbd962030655d85ab5fc8072ca43e1b0f8e1b"
"63f0949ab0803c471d09a279c93cd85161d35c8efc2acfd21dd4a77e8d3",
"5862cd69aabb8a6804656b4adea447ea89bf384875dfb3a19591ec910e29dae9",
ECDSA_TWEAK_PUBKEY_INVALID_PUBKEY_ERR, ""},
// Invalid public key - the point at infinity
{"0000000000000000000000000000000000000000000000000000000000000000",
"89406ab5f3e2be503dbbbaf4e63e0154ddedeea6793494296f42d4f88c9ae814",
ECDSA_TWEAK_PUBKEY_INVALID_PUBKEY_ERR, ""},
// Invalid public key - the point doesn't lie on the curve
{"025b685000eabe640bdb1fd714d6dea837d76e16a6dbd3f0c839ea18328091def9",
"9180a64d35e5b47b6c4eb296a0936334c0499fa27439b5d507372ee8af8c67bd",
ECDSA_TWEAK_PUBKEY_INVALID_PUBKEY_ERR, ""},
// Invalid private tweak - the group order
{"02b3303f7bc64cef5894f265a6f0412260359850bf5b57e6fd931791e21abcd1ff",
"fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141",
ECDSA_TWEAK_PUBKEY_INVALID_TWEAK_OR_RESULT_ERR, ""},
// Invalid tweaked public key - the point at infinity
{"023c0429e944048c61cdad02e73f3277806eaabfa92b3326f834f7a363f126a355",
"7b30f9ed2f8ec13ecbd77788815c53773ace8b2f6900f2b386c96e2c440bce02",
ECDSA_TWEAK_PUBKEY_INVALID_TWEAK_OR_RESULT_ERR, ""}};
const ecdsa_curve *curve = &secp256k1;
uint8_t pub_key[33] = {0};
uint8_t tweak[32] = {0};
uint8_t tweaked_pub_key[33] = {0};
uint8_t expected_tweaked_pub_key[33] = {0};
ecdsa_tweak_pubkey_result expected_res = 0;
ecdsa_tweak_pubkey_result res = 0;
for (size_t i = 0; i < sizeof(tests) / sizeof(*tests); i++) {
memcpy(pub_key, fromhex(tests[i].pub_key), 33);
memcpy(tweak, fromhex(tests[i].tweak), 32);
expected_res = tests[i].res;
if (expected_res == ECDSA_TWEAK_PUBKEY_SUCCESS) {
memcpy(expected_tweaked_pub_key, fromhex(tests[i].tweaked_pub_key), 33);
}
res = ecdsa_tweak_pubkey_fn(curve, pub_key, tweak, tweaked_pub_key);
ck_assert_int_eq(expected_res, res);
if (expected_res == ECDSA_TWEAK_PUBKEY_SUCCESS) {
ck_assert_mem_eq(expected_tweaked_pub_key, tweaked_pub_key, 33);
}
}
}
START_TEST(test_tc_ecdsa_tweak_pubkey) {
test_ecdsa_tweak_pubkey_helper(tc_ecdsa_tweak_pubkey);
}
END_TEST
START_TEST(test_zkp_ecdsa_tweak_pubkey) {
test_ecdsa_tweak_pubkey_helper(zkp_ecdsa_tweak_pubkey);
}
END_TEST
// test vectors from
// https://datatracker.ietf.org/doc/html/rfc3610
// https://doi.org/10.6028/NIST.SP.800-38C
@ -11436,11 +11511,13 @@ Suite *test_suite(void) {
tcase_add_test(tc, test_tc_ecdsa_recover_pub_from_sig);
tcase_add_test(tc, test_tc_ecdsa_verify_digest);
tcase_add_test(tc, test_tc_ecdh_multiply);
tcase_add_test(tc, test_tc_ecdsa_tweak_pubkey);
tcase_add_test(tc, test_zkp_ecdsa_get_public_key33);
tcase_add_test(tc, test_zkp_ecdsa_get_public_key65);
tcase_add_test(tc, test_zkp_ecdsa_recover_pub_from_sig);
tcase_add_test(tc, test_zkp_ecdsa_verify_digest);
tcase_add_test(tc, test_zkp_ecdh_multiply);
tcase_add_test(tc, test_zkp_ecdsa_tweak_pubkey);
#if USE_RFC6979
tcase_add_test(tc, test_tc_ecdsa_sign_digest_deterministic);
tcase_add_test(tc, test_zkp_ecdsa_sign_digest_deterministic);

@ -24,6 +24,7 @@
#include <stdbool.h>
#include <string.h>
#include "ecdsa.h"
#include "memzero.h"
#include "secp256k1.h"
#include "zkp_context.h"
@ -473,3 +474,69 @@ end:
return result;
}
// ECDSA public key tweak
// tweaked_public_key = public_key + tweak * G
// curve has to be &secp256k1
// public_key_bytes has 33 bytes
// tweak_bytes has 32 bytes
// tweaked_public_key_bytes has 33 bytes
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) {
assert(curve == &secp256k1);
if (curve != &secp256k1) {
return ECDSA_TWEAK_PUBKEY_INVALID_CURVE_ERR;
}
int result = ECDSA_TWEAK_PUBKEY_SUCCESS;
secp256k1_context *context_writable = NULL;
const secp256k1_context *context_read_only = zkp_context_get_read_only();
secp256k1_pubkey public_key = {0};
size_t public_key_length = get_public_key_length(public_key_bytes);
if (public_key_length != 33) {
result = ECDSA_TWEAK_PUBKEY_INVALID_PUBKEY_ERR;
goto end;
}
if (secp256k1_ec_pubkey_parse(context_read_only, &public_key,
public_key_bytes, public_key_length) != 1) {
result = ECDSA_TWEAK_PUBKEY_INVALID_PUBKEY_ERR;
goto end;
}
context_writable = zkp_context_acquire_writable();
if (context_writable == NULL) {
result = ECDSA_TWEAK_PUBKEY_CONTEXT_ERR;
goto end;
}
if (secp256k1_context_writable_randomize(context_writable) != 0) {
result = ECDSA_TWEAK_PUBKEY_CONTEXT_ERR;
goto end;
}
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.
result = ECDSA_TWEAK_PUBKEY_INVALID_TWEAK_OR_RESULT_ERR;
goto end;
}
public_key_length = 33;
if (secp256k1_ec_pubkey_serialize(context_read_only, tweaked_public_key_bytes,
&public_key_length, &public_key,
SECP256K1_EC_COMPRESSED) != 1) {
result = ECDSA_TWEAK_PUBKEY_UNSPECIFIED_ERR;
goto end;
}
end:
if (context_writable) {
zkp_context_release_writable();
context_writable = NULL;
}
memzero(&public_key, sizeof(public_key));
return result;
}

@ -3,6 +3,7 @@
#include <stdint.h>
#include "ecdsa.h"
#include "hasher.h"
int zkp_ecdsa_get_public_key33(const ecdsa_curve *curve,
@ -29,4 +30,7 @@ int zkp_ecdsa_verify(const ecdsa_curve *curve, HasherType hasher_sign,
const uint8_t *msg, uint32_t msg_len);
int zkp_ecdh_multiply(const ecdsa_curve *curve, const uint8_t *priv_key,
const uint8_t *pub_key, uint8_t *session_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);
#endif

Loading…
Cancel
Save