]> Git Repo - secp256k1.git/commitdiff
extrakeys: Add keypair struct with create, pub and pub_xonly
authorJonas Nick <[email protected]>
Tue, 12 May 2020 14:52:34 +0000 (14:52 +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
src/valgrind_ctime_test.c

index cda7eee0c861fdd413e7c3c93a54ffc522e02f7c..96e470db43570dd05642815bb479aa6fc2dea805 100644 (file)
@@ -23,6 +23,17 @@ typedef struct {
     unsigned char data[64];
 } secp256k1_xonly_pubkey;
 
+/** Opaque data structure that holds a keypair consisting of a secret and a
+ *  public key.
+ *
+ *  The exact representation of data inside is implementation defined and not
+ *  guaranteed to be portable between different platforms or versions. It is
+ *  however guaranteed to be 96 bytes in size, and can be safely copied/moved.
+ */
+typedef struct {
+    unsigned char data[96];
+} secp256k1_keypair;
+
 /** Parse a 32-byte sequence into a xonly_pubkey object.
  *
  *  Returns: 1 if the public key was fully valid.
@@ -140,6 +151,57 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_tweak_add_
     const unsigned char *tweak32
 ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);
 
+/** Compute the keypair for a secret key.
+ *
+ *  Returns: 1: secret was valid, keypair is ready to use
+ *           0: secret was invalid, try again with a different secret
+ *  Args:    ctx: pointer to a context object, initialized for signing (cannot be NULL)
+ *  Out: keypair: pointer to the created keypair (cannot be NULL)
+ *  In:   seckey: pointer to a 32-byte secret key (cannot be NULL)
+ */
+SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_create(
+    const secp256k1_context* ctx,
+    secp256k1_keypair *keypair,
+    const unsigned char *seckey
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
+
+/** Get the public key from a keypair.
+ *
+ *  Returns: 0 if the arguments are invalid. 1 otherwise.
+ *  Args:    ctx: pointer to a context object (cannot be NULL)
+ *  Out: pubkey: pointer to a pubkey object. If 1 is returned, it is set to
+ *               the keypair public key. If not, it's set to an invalid value.
+ *               (cannot be NULL)
+ *  In: keypair: pointer to a keypair (cannot be NULL)
+ */
+SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_pub(
+    const secp256k1_context* ctx,
+    secp256k1_pubkey *pubkey,
+    const secp256k1_keypair *keypair
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
+
+/** Get the x-only public key from a keypair.
+ *
+ *  This is the same as calling secp256k1_keypair_pub and then
+ *  secp256k1_xonly_pubkey_from_pubkey.
+ *
+ *  Returns: 0 if the arguments are invalid. 1 otherwise.
+ *  Args:   ctx: pointer to a context object (cannot be NULL)
+ *  Out: pubkey: pointer to an xonly_pubkey object. If 1 is returned, it is set
+ *               to the keypair public key after converting it to an
+ *               xonly_pubkey. If not, it's set to an invalid value (cannot be
+ *               NULL).
+ *    pk_parity: pointer to an integer that will be set to the pk_parity
+ *               argument of secp256k1_xonly_pubkey_from_pubkey (can be NULL).
+ *  In: keypair: pointer to a keypair (cannot be NULL)
+ */
+SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_xonly_pub(
+    const secp256k1_context* ctx,
+    secp256k1_xonly_pubkey *pubkey,
+    int *pk_parity,
+    const secp256k1_keypair *keypair
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4);
+
 #ifdef __cplusplus
 }
 #endif
index c01ede12dc912501fcb5db1df151801933f6ecab..a0c44e98b1269d2897791ae02ba57460310eb183 100644 (file)
@@ -125,4 +125,93 @@ int secp256k1_xonly_pubkey_tweak_add_check(const secp256k1_context* ctx, const u
             && secp256k1_fe_is_odd(&pk.y) == tweaked_pk_parity;
 }
 
+static void secp256k1_keypair_save(secp256k1_keypair *keypair, const secp256k1_scalar *sk, secp256k1_ge *pk) {
+    secp256k1_scalar_get_b32(&keypair->data[0], sk);
+    secp256k1_pubkey_save((secp256k1_pubkey *)&keypair->data[32], pk);
+}
+
+
+static int secp256k1_keypair_seckey_load(const secp256k1_context* ctx, secp256k1_scalar *sk, const secp256k1_keypair *keypair) {
+    int ret;
+
+    ret = secp256k1_scalar_set_b32_seckey(sk, &keypair->data[0]);
+    /* We can declassify ret here because sk is only zero if a keypair function
+     * failed (which zeroes the keypair) and its return value is ignored. */
+    secp256k1_declassify(ctx, &ret, sizeof(ret));
+    ARG_CHECK(ret);
+    return ret;
+}
+
+/* Load a keypair into pk and sk (if non-NULL). This function declassifies pk
+ * and ARG_CHECKs that the keypair is not invalid. It always initializes sk and
+ * pk with dummy values. */
+static int secp256k1_keypair_load(const secp256k1_context* ctx, secp256k1_scalar *sk, secp256k1_ge *pk, const secp256k1_keypair *keypair) {
+    int ret;
+    const secp256k1_pubkey *pubkey = (const secp256k1_pubkey *)&keypair->data[32];
+
+    /* Need to declassify the pubkey because pubkey_load ARG_CHECKs if it's
+     * invalid. */
+    secp256k1_declassify(ctx, pubkey, sizeof(*pubkey));
+    ret = secp256k1_pubkey_load(ctx, pk, pubkey);
+    if (sk != NULL) {
+        ret = ret && secp256k1_keypair_seckey_load(ctx, sk, keypair);
+    }
+    if (!ret) {
+        *pk = secp256k1_ge_const_g;
+        if (sk != NULL) {
+            *sk = secp256k1_scalar_one;
+        }
+    }
+    return ret;
+}
+
+int secp256k1_keypair_create(const secp256k1_context* ctx, secp256k1_keypair *keypair, const unsigned char *seckey32) {
+    secp256k1_scalar sk;
+    secp256k1_ge pk;
+    int ret = 0;
+    VERIFY_CHECK(ctx != NULL);
+    ARG_CHECK(keypair != NULL);
+    memset(keypair, 0, sizeof(*keypair));
+    ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
+    ARG_CHECK(seckey32 != NULL);
+
+    ret = secp256k1_ec_pubkey_create_helper(&ctx->ecmult_gen_ctx, &sk, &pk, seckey32);
+    secp256k1_keypair_save(keypair, &sk, &pk);
+    memczero(keypair, sizeof(*keypair), !ret);
+
+    secp256k1_scalar_clear(&sk);
+    return ret;
+}
+
+int secp256k1_keypair_pub(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const secp256k1_keypair *keypair) {
+    VERIFY_CHECK(ctx != NULL);
+    ARG_CHECK(pubkey != NULL);
+    memset(pubkey, 0, sizeof(*pubkey));
+    ARG_CHECK(keypair != NULL);
+
+    memcpy(pubkey->data, &keypair->data[32], sizeof(*pubkey));
+    return 1;
+}
+
+int secp256k1_keypair_xonly_pub(const secp256k1_context* ctx, secp256k1_xonly_pubkey *pubkey, int *pk_parity, const secp256k1_keypair *keypair) {
+    secp256k1_ge pk;
+    int tmp;
+
+    VERIFY_CHECK(ctx != NULL);
+    ARG_CHECK(pubkey != NULL);
+    memset(pubkey, 0, sizeof(*pubkey));
+    ARG_CHECK(keypair != NULL);
+
+    if (!secp256k1_keypair_load(ctx, NULL, &pk, keypair)) {
+        return 0;
+    }
+    tmp = secp256k1_extrakeys_ge_even_y(&pk);
+    if (pk_parity != NULL) {
+        *pk_parity = tmp;
+    }
+    secp256k1_xonly_pubkey_save(pubkey, &pk);
+
+    return 1;
+}
+
 #endif
index 644ff303ce1259fd2354e24898480eaa757f77c5..af289c86e17f79d413acebac5843f6a01d80c4d4 100644 (file)
@@ -309,12 +309,107 @@ void test_xonly_pubkey_tweak_recursive(void) {
 }
 #undef N_PUBKEYS
 
+void test_keypair(void) {
+    unsigned char sk[32];
+    unsigned char zeros96[96] = { 0 };
+    unsigned char overflows[32];
+    secp256k1_keypair keypair;
+    secp256k1_pubkey pk, pk_tmp;
+    secp256k1_xonly_pubkey xonly_pk, xonly_pk_tmp;
+    int pk_parity, pk_parity_tmp;
+    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);
+
+    CHECK(sizeof(zeros96) == sizeof(keypair));
+    memset(overflows, 0xFF, sizeof(overflows));
+
+    /* Test keypair_create */
+    ecount = 0;
+    secp256k1_rand256(sk);
+    CHECK(secp256k1_keypair_create(none, &keypair, sk) == 0);
+    CHECK(memcmp(zeros96, &keypair, sizeof(keypair)) == 0);
+    CHECK(ecount == 1);
+    CHECK(secp256k1_keypair_create(verify, &keypair, sk) == 0);
+    CHECK(memcmp(zeros96, &keypair, sizeof(keypair)) == 0);
+    CHECK(ecount == 2);
+    CHECK(secp256k1_keypair_create(sign, &keypair, sk) == 1);
+    CHECK(secp256k1_keypair_create(sign, NULL, sk) == 0);
+    CHECK(ecount == 3);
+    CHECK(secp256k1_keypair_create(sign, &keypair, NULL) == 0);
+    CHECK(memcmp(zeros96, &keypair, sizeof(keypair)) == 0);
+    CHECK(ecount == 4);
+
+    /* Invalid secret key */
+    CHECK(secp256k1_keypair_create(sign, &keypair, zeros96) == 0);
+    CHECK(memcmp(zeros96, &keypair, sizeof(keypair)) == 0);
+    CHECK(secp256k1_keypair_create(sign, &keypair, overflows) == 0);
+    CHECK(memcmp(zeros96, &keypair, sizeof(keypair)) == 0);
+
+    /* Test keypair_pub */
+    ecount = 0;
+    secp256k1_rand256(sk);
+    CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
+    CHECK(secp256k1_keypair_pub(none, &pk, &keypair) == 1);
+    CHECK(secp256k1_keypair_pub(none, NULL, &keypair) == 0);
+    CHECK(ecount == 1);
+    CHECK(secp256k1_keypair_pub(none, &pk, NULL) == 0);
+    CHECK(ecount == 2);
+    CHECK(memcmp(zeros96, &pk, sizeof(pk)) == 0);
+
+    /* Using an invalid keypair is fine for keypair_pub */
+    memset(&keypair, 0, sizeof(keypair));
+    CHECK(secp256k1_keypair_pub(none, &pk, &keypair) == 1);
+    CHECK(memcmp(zeros96, &pk, sizeof(pk)) == 0);
+
+    /* keypair holds the same pubkey as pubkey_create */
+    CHECK(secp256k1_ec_pubkey_create(sign, &pk, sk) == 1);
+    CHECK(secp256k1_keypair_create(sign, &keypair, sk) == 1);
+    CHECK(secp256k1_keypair_pub(none, &pk_tmp, &keypair) == 1);
+    CHECK(memcmp(&pk, &pk_tmp, sizeof(pk)) == 0);
+
+    /** Test keypair_xonly_pub **/
+    ecount = 0;
+    secp256k1_rand256(sk);
+    CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
+    CHECK(secp256k1_keypair_xonly_pub(none, &xonly_pk, &pk_parity, &keypair) == 1);
+    CHECK(secp256k1_keypair_xonly_pub(none, NULL, &pk_parity, &keypair) == 0);
+    CHECK(ecount == 1);
+    CHECK(secp256k1_keypair_xonly_pub(none, &xonly_pk, NULL, &keypair) == 1);
+    CHECK(secp256k1_keypair_xonly_pub(none, &xonly_pk, &pk_parity, NULL) == 0);
+    CHECK(ecount == 2);
+    CHECK(memcmp(zeros96, &xonly_pk, sizeof(xonly_pk)) == 0);
+    /* Using an invalid keypair will set the xonly_pk to 0 (first reset
+     * xonly_pk). */
+    CHECK(secp256k1_keypair_xonly_pub(none, &xonly_pk, &pk_parity, &keypair) == 1);
+    memset(&keypair, 0, sizeof(keypair));
+    CHECK(secp256k1_keypair_xonly_pub(none, &xonly_pk, &pk_parity, &keypair) == 0);
+    CHECK(memcmp(zeros96, &xonly_pk, sizeof(xonly_pk)) == 0);
+    CHECK(ecount == 3);
+
+    /** keypair holds the same xonly pubkey as pubkey_create **/
+    CHECK(secp256k1_ec_pubkey_create(sign, &pk, sk) == 1);
+    CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &xonly_pk, &pk_parity, &pk) == 1);
+    CHECK(secp256k1_keypair_create(sign, &keypair, sk) == 1);
+    CHECK(secp256k1_keypair_xonly_pub(none, &xonly_pk_tmp, &pk_parity_tmp, &keypair) == 1);
+    CHECK(memcmp(&xonly_pk, &xonly_pk_tmp, sizeof(pk)) == 0);
+    CHECK(pk_parity == pk_parity_tmp);
+
+    secp256k1_context_destroy(none);
+    secp256k1_context_destroy(sign);
+    secp256k1_context_destroy(verify);
+}
+
 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();
+
+    /* keypair tests */
+    test_keypair();
 }
 
 #endif
index 090fde2b6e4953239ca186ba5225bfbb42f578e6..09b578a415c7d0aa16bc434690b5c14a15ffe649 100644 (file)
 # include "include/secp256k1_recovery.h"
 #endif
 
+#if ENABLE_MODULE_EXTRAKEYS
+# include "include/secp256k1_extrakeys.h"
+#endif
+
 int main(void) {
     secp256k1_context* ctx;
     secp256k1_ecdsa_signature signature;
@@ -33,6 +37,9 @@ int main(void) {
     secp256k1_ecdsa_recoverable_signature recoverable_signature;
     int recid;
 #endif
+#if ENABLE_MODULE_EXTRAKEYS
+    secp256k1_keypair keypair;
+#endif
 
     if (!RUNNING_ON_VALGRIND) {
         fprintf(stderr, "This test can only usefully be run inside valgrind.\n");
@@ -115,6 +122,13 @@ int main(void) {
     VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
     CHECK(ret);
 
+#if ENABLE_MODULE_EXTRAKEYS
+    VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
+    ret = secp256k1_keypair_create(ctx, &keypair, key);
+    VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
+    CHECK(ret == 1);
+#endif
+
     secp256k1_context_destroy(ctx);
     return 0;
 }
This page took 0.032209 seconds and 4 git commands to generate.