]> Git Repo - secp256k1.git/commitdiff
extrakeys: Add xonly_pubkey_tweak_add & xonly_pubkey_tweak_add_test
authorJonas Nick <[email protected]>
Tue, 12 May 2020 14:49:12 +0000 (14:49 +0000)
committerJonas Nick <[email protected]>
Sun, 6 Sep 2020 18:59:57 +0000 (18:59 +0000)
include/secp256k1_extrakeys.h
src/modules/extrakeys/main_impl.h
src/modules/extrakeys/tests_impl.h

index fdbfdeec33249d271da68ba98890248465c073d1..cda7eee0c861fdd413e7c3c93a54ffc522e02f7c 100644 (file)
@@ -76,6 +76,70 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_from_pubke
     const secp256k1_pubkey *pubkey
 ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4);
 
+/** Tweak an x-only public key by adding the generator multiplied with tweak32
+ *  to it.
+ *
+ *  Note that the resulting point can not in general be represented by an x-only
+ *  pubkey because it may have an odd Y coordinate. Instead, the output_pubkey
+ *  is a normal secp256k1_pubkey.
+ *
+ *  Returns: 0 if the arguments are invalid or the resulting public key would be
+ *           invalid (only when the tweak is the negation of the corresponding
+ *           secret key). 1 otherwise.
+ *
+ *  Args:           ctx: pointer to a context object initialized for verification
+ *                       (cannot be NULL)
+ *  Out:  output_pubkey: pointer to a public key to store the result. Will be set
+ *                       to an invalid value if this function returns 0 (cannot
+ *                       be NULL)
+ *  In: internal_pubkey: pointer to an x-only pubkey to apply the tweak to.
+ *                       (cannot be NULL).
+ *              tweak32: pointer to a 32-byte tweak. If the tweak is invalid
+ *                       according to secp256k1_ec_seckey_verify, this function
+ *                       returns 0. For uniformly random 32-byte arrays the
+ *                       chance of being invalid is negligible (around 1 in
+ *                       2^128) (cannot be NULL).
+ */
+SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_tweak_add(
+    const secp256k1_context* ctx,
+    secp256k1_pubkey *output_pubkey,
+    const secp256k1_xonly_pubkey *internal_pubkey,
+    const unsigned char *tweak32
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
+
+/** Checks that a tweaked pubkey is the result of calling
+ *  secp256k1_xonly_pubkey_tweak_add with internal_pubkey and tweak32.
+ *
+ *  The tweaked pubkey is represented by its 32-byte x-only serialization and
+ *  its pk_parity, which can both be obtained by converting the result of
+ *  tweak_add to a secp256k1_xonly_pubkey.
+ *
+ *  Note that this alone does _not_ verify that the tweaked pubkey is a
+ *  commitment. If the tweak is not chosen in a specific way, the tweaked pubkey
+ *  can easily be the result of a different internal_pubkey and tweak.
+ *
+ *  Returns: 0 if the arguments are invalid or the tweaked pubkey is not the
+ *           result of tweaking the internal_pubkey with tweak32. 1 otherwise.
+ *  Args:            ctx: pointer to a context object initialized for verification
+ *                       (cannot be NULL)
+ *  In: tweaked_pubkey32: pointer to a serialized xonly_pubkey (cannot be NULL)
+ *     tweaked_pk_parity: the parity of the tweaked pubkey (whose serialization
+ *                        is passed in as tweaked_pubkey32). This must match the
+ *                        pk_parity value that is returned when calling
+ *                        secp256k1_xonly_pubkey with the tweaked pubkey, or
+ *                        this function will fail.
+ *       internal_pubkey: pointer to an x-only public key object to apply the
+ *                        tweak to (cannot be NULL)
+ *               tweak32: pointer to a 32-byte tweak (cannot be NULL)
+ */
+SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_tweak_add_check(
+    const secp256k1_context* ctx,
+    const unsigned char *tweaked_pubkey32,
+    int tweaked_pk_parity,
+    const secp256k1_xonly_pubkey *internal_pubkey,
+    const unsigned char *tweak32
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);
+
 #ifdef __cplusplus
 }
 #endif
index 4f6fd3622d4bbd1ba69a1e93d7f5c941a7a72f2f..c01ede12dc912501fcb5db1df151801933f6ecab 100644 (file)
@@ -85,4 +85,44 @@ int secp256k1_xonly_pubkey_from_pubkey(const secp256k1_context* ctx, secp256k1_x
     return 1;
 }
 
+int secp256k1_xonly_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_pubkey *output_pubkey, const secp256k1_xonly_pubkey *internal_pubkey, const unsigned char *tweak32) {
+    secp256k1_ge pk;
+
+    VERIFY_CHECK(ctx != NULL);
+    ARG_CHECK(output_pubkey != NULL);
+    memset(output_pubkey, 0, sizeof(*output_pubkey));
+    ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
+    ARG_CHECK(internal_pubkey != NULL);
+    ARG_CHECK(tweak32 != NULL);
+
+    if (!secp256k1_xonly_pubkey_load(ctx, &pk, internal_pubkey)
+        || !secp256k1_ec_pubkey_tweak_add_helper(&ctx->ecmult_ctx, &pk, tweak32)) {
+        return 0;
+    }
+    secp256k1_pubkey_save(output_pubkey, &pk);
+    return 1;
+}
+
+int secp256k1_xonly_pubkey_tweak_add_check(const secp256k1_context* ctx, const unsigned char *tweaked_pubkey32, int tweaked_pk_parity, const secp256k1_xonly_pubkey *internal_pubkey, const unsigned char *tweak32) {
+    secp256k1_ge pk;
+    unsigned char pk_expected32[32];
+
+    VERIFY_CHECK(ctx != NULL);
+    ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
+    ARG_CHECK(internal_pubkey != NULL);
+    ARG_CHECK(tweaked_pubkey32 != NULL);
+    ARG_CHECK(tweak32 != NULL);
+
+    if (!secp256k1_xonly_pubkey_load(ctx, &pk, internal_pubkey)
+        || !secp256k1_ec_pubkey_tweak_add_helper(&ctx->ecmult_ctx, &pk, tweak32)) {
+        return 0;
+    }
+    secp256k1_fe_normalize_var(&pk.x);
+    secp256k1_fe_normalize_var(&pk.y);
+    secp256k1_fe_get_b32(pk_expected32, &pk.x);
+
+    return memcmp(&pk_expected32, tweaked_pubkey32, 32) == 0
+            && secp256k1_fe_is_odd(&pk.y) == tweaked_pk_parity;
+}
+
 #endif
index ebe51325588c9c016c3f51d590d8ca02920b025d..644ff303ce1259fd2354e24898480eaa757f77c5 100644 (file)
@@ -137,9 +137,184 @@ void test_xonly_pubkey(void) {
     secp256k1_context_destroy(verify);
 }
 
+void test_xonly_pubkey_tweak(void) {
+    unsigned char zeros64[64] = { 0 };
+    unsigned char overflows[32];
+    unsigned char sk[32];
+    secp256k1_pubkey internal_pk;
+    secp256k1_xonly_pubkey internal_xonly_pk;
+    secp256k1_pubkey output_pk;
+    int pk_parity;
+    unsigned char tweak[32];
+    int i;
+
+    int ecount;
+    secp256k1_context *none = api_test_context(SECP256K1_CONTEXT_NONE, &ecount);
+    secp256k1_context *sign = api_test_context(SECP256K1_CONTEXT_SIGN, &ecount);
+    secp256k1_context *verify = api_test_context(SECP256K1_CONTEXT_VERIFY, &ecount);
+
+    memset(overflows, 0xff, sizeof(overflows));
+    secp256k1_rand256(tweak);
+    secp256k1_rand256(sk);
+    CHECK(secp256k1_ec_pubkey_create(ctx, &internal_pk, sk) == 1);
+    CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &internal_xonly_pk, &pk_parity, &internal_pk) == 1);
+
+    ecount = 0;
+    CHECK(secp256k1_xonly_pubkey_tweak_add(none, &output_pk, &internal_xonly_pk, tweak) == 0);
+    CHECK(ecount == 1);
+    CHECK(secp256k1_xonly_pubkey_tweak_add(sign, &output_pk, &internal_xonly_pk, tweak) == 0);
+    CHECK(ecount == 2);
+    CHECK(secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, &internal_xonly_pk, tweak) == 1);
+    CHECK(secp256k1_xonly_pubkey_tweak_add(verify, NULL, &internal_xonly_pk, tweak) == 0);
+    CHECK(ecount == 3);
+    CHECK(secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, NULL, tweak) == 0);
+    CHECK(ecount == 4);
+    /* NULL internal_xonly_pk zeroes the output_pk */
+    CHECK(memcmp(&output_pk, zeros64, sizeof(output_pk)) == 0);
+    CHECK(secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, &internal_xonly_pk, NULL) == 0);
+    CHECK(ecount == 5);
+    /* NULL tweak zeroes the output_pk */
+    CHECK(memcmp(&output_pk, zeros64, sizeof(output_pk)) == 0);
+
+    /* Invalid tweak zeroes the output_pk */
+    CHECK(secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, &internal_xonly_pk, overflows) == 0);
+    CHECK(memcmp(&output_pk, zeros64, sizeof(output_pk))  == 0);
+
+    /* A zero tweak is fine */
+    CHECK(secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, &internal_xonly_pk, zeros64) == 1);
+
+    /* Fails if the resulting key was infinity */
+    for (i = 0; i < count; i++) {
+        secp256k1_scalar scalar_tweak;
+        /* Because sk may be negated before adding, we need to try with tweak =
+         * sk as well as tweak = -sk. */
+        secp256k1_scalar_set_b32(&scalar_tweak, sk, NULL);
+        secp256k1_scalar_negate(&scalar_tweak, &scalar_tweak);
+        secp256k1_scalar_get_b32(tweak, &scalar_tweak);
+        CHECK((secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, &internal_xonly_pk, sk) == 0)
+              || (secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, &internal_xonly_pk, tweak) == 0));
+        CHECK(memcmp(&output_pk, zeros64, sizeof(output_pk)) == 0);
+    }
+
+    /* Invalid pk with a valid tweak */
+    memset(&internal_xonly_pk, 0, sizeof(internal_xonly_pk));
+    secp256k1_rand256(tweak);
+    ecount = 0;
+    CHECK(secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, &internal_xonly_pk, tweak) == 0);
+    CHECK(ecount == 1);
+    CHECK(memcmp(&output_pk, zeros64, sizeof(output_pk))  == 0);
+
+    secp256k1_context_destroy(none);
+    secp256k1_context_destroy(sign);
+    secp256k1_context_destroy(verify);
+}
+
+void test_xonly_pubkey_tweak_check(void) {
+    unsigned char zeros64[64] = { 0 };
+    unsigned char overflows[32];
+    unsigned char sk[32];
+    secp256k1_pubkey internal_pk;
+    secp256k1_xonly_pubkey internal_xonly_pk;
+    secp256k1_pubkey output_pk;
+    secp256k1_xonly_pubkey output_xonly_pk;
+    unsigned char output_pk32[32];
+    unsigned char buf32[32];
+    int pk_parity;
+    unsigned char tweak[32];
+
+    int ecount;
+    secp256k1_context *none = api_test_context(SECP256K1_CONTEXT_NONE, &ecount);
+    secp256k1_context *sign = api_test_context(SECP256K1_CONTEXT_SIGN, &ecount);
+    secp256k1_context *verify = api_test_context(SECP256K1_CONTEXT_VERIFY, &ecount);
+
+    memset(overflows, 0xff, sizeof(overflows));
+    secp256k1_rand256(tweak);
+    secp256k1_rand256(sk);
+    CHECK(secp256k1_ec_pubkey_create(ctx, &internal_pk, sk) == 1);
+    CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &internal_xonly_pk, &pk_parity, &internal_pk) == 1);
+
+    ecount = 0;
+    CHECK(secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, &internal_xonly_pk, tweak) == 1);
+    CHECK(secp256k1_xonly_pubkey_from_pubkey(verify, &output_xonly_pk, &pk_parity, &output_pk) == 1);
+    CHECK(secp256k1_xonly_pubkey_serialize(ctx, buf32, &output_xonly_pk) == 1);
+    CHECK(secp256k1_xonly_pubkey_tweak_add_check(none, buf32, pk_parity, &internal_xonly_pk, tweak) == 0);
+    CHECK(ecount == 1);
+    CHECK(secp256k1_xonly_pubkey_tweak_add_check(sign, buf32, pk_parity, &internal_xonly_pk, tweak) == 0);
+    CHECK(ecount == 2);
+    CHECK(secp256k1_xonly_pubkey_tweak_add_check(verify, buf32, pk_parity, &internal_xonly_pk, tweak) == 1);
+    CHECK(secp256k1_xonly_pubkey_tweak_add_check(verify, NULL, pk_parity, &internal_xonly_pk, tweak) == 0);
+    CHECK(ecount == 3);
+    /* invalid pk_parity value */
+    CHECK(secp256k1_xonly_pubkey_tweak_add_check(verify, buf32, 2, &internal_xonly_pk, tweak) == 0);
+    CHECK(ecount == 3);
+    CHECK(secp256k1_xonly_pubkey_tweak_add_check(verify, buf32, pk_parity, NULL, tweak) == 0);
+    CHECK(ecount == 4);
+    CHECK(secp256k1_xonly_pubkey_tweak_add_check(verify, buf32, pk_parity, &internal_xonly_pk, NULL) == 0);
+    CHECK(ecount == 5);
+
+    memset(tweak, 1, sizeof(tweak));
+    CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &internal_xonly_pk, NULL, &internal_pk) == 1);
+    CHECK(secp256k1_xonly_pubkey_tweak_add(ctx, &output_pk, &internal_xonly_pk, tweak) == 1);
+    CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &output_xonly_pk, &pk_parity, &output_pk) == 1);
+    CHECK(secp256k1_xonly_pubkey_serialize(ctx, output_pk32, &output_xonly_pk) == 1);
+    CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, output_pk32, pk_parity, &internal_xonly_pk, tweak) == 1);
+
+    /* Wrong pk_parity */
+    CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, output_pk32, !pk_parity, &internal_xonly_pk, tweak) == 0);
+    /* Wrong public key */
+    CHECK(secp256k1_xonly_pubkey_serialize(ctx, buf32, &internal_xonly_pk) == 1);
+    CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, buf32, pk_parity, &internal_xonly_pk, tweak) == 0);
+
+    /* Overflowing tweak not allowed */
+    CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, output_pk32, pk_parity, &internal_xonly_pk, overflows) == 0);
+    CHECK(secp256k1_xonly_pubkey_tweak_add(ctx, &output_pk, &internal_xonly_pk, overflows) == 0);
+    CHECK(memcmp(&output_pk, zeros64, sizeof(output_pk)) == 0);
+    CHECK(ecount == 5);
+
+    secp256k1_context_destroy(none);
+    secp256k1_context_destroy(sign);
+    secp256k1_context_destroy(verify);
+}
+
+/* Starts with an initial pubkey and recursively creates N_PUBKEYS - 1
+ * additional pubkeys by calling tweak_add. Then verifies every tweak starting
+ * from the last pubkey. */
+#define N_PUBKEYS 32
+void test_xonly_pubkey_tweak_recursive(void) {
+    unsigned char sk[32];
+    secp256k1_pubkey pk[N_PUBKEYS];
+    unsigned char pk_serialized[32];
+    unsigned char tweak[N_PUBKEYS - 1][32];
+    int i;
+
+    secp256k1_rand256(sk);
+    CHECK(secp256k1_ec_pubkey_create(ctx, &pk[0], sk) == 1);
+    /* Add tweaks */
+    for (i = 0; i < N_PUBKEYS - 1; i++) {
+        secp256k1_xonly_pubkey xonly_pk;
+        memset(tweak[i], i + 1, sizeof(tweak[i]));
+        CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly_pk, NULL, &pk[i]) == 1);
+        CHECK(secp256k1_xonly_pubkey_tweak_add(ctx, &pk[i + 1], &xonly_pk, tweak[i]) == 1);
+    }
+
+    /* Verify tweaks */
+    for (i = N_PUBKEYS - 1; i > 0; i--) {
+        secp256k1_xonly_pubkey xonly_pk;
+        int pk_parity;
+        CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly_pk, &pk_parity, &pk[i]) == 1);
+        CHECK(secp256k1_xonly_pubkey_serialize(ctx, pk_serialized, &xonly_pk) == 1);
+        CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly_pk, NULL, &pk[i - 1]) == 1);
+        CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, pk_serialized, pk_parity, &xonly_pk, tweak[i - 1]) == 1);
+    }
+}
+#undef N_PUBKEYS
+
 void run_extrakeys_tests(void) {
     /* xonly key test cases */
     test_xonly_pubkey();
+    test_xonly_pubkey_tweak();
+    test_xonly_pubkey_tweak_check();
+    test_xonly_pubkey_tweak_recursive();
 }
 
 #endif
This page took 0.033516 seconds and 4 git commands to generate.