frost: nonce generation
This commits adds nonce generation, as well as serialization and parsing.
This commit is contained in:
parent
197fb7efb9
commit
f606507120
@ -15,6 +15,9 @@ extern "C" {
|
||||
* This module implements a variant of Flexible Round-Optimized Schnorr
|
||||
* Threshold Signatures (FROST) by Chelsea Komlo and Ian Goldberg
|
||||
* (https://crysp.uwaterloo.ca/software/frost/).
|
||||
*
|
||||
* Following the convention used in the MuSig module, the API uses the singular
|
||||
* term "nonce" to refer to the two "nonces" used by the FROST scheme.
|
||||
*/
|
||||
|
||||
/** Opaque data structures
|
||||
@ -34,6 +37,61 @@ typedef struct {
|
||||
unsigned char data[36];
|
||||
} secp256k1_frost_share;
|
||||
|
||||
/** Opaque data structure that holds a signer's _secret_ nonce.
|
||||
*
|
||||
* Guaranteed to be 68 bytes in size.
|
||||
*
|
||||
* WARNING: This structure MUST NOT be copied or read or written to directly.
|
||||
* A signer who is online throughout the whole process and can keep this
|
||||
* structure in memory can use the provided API functions for a safe standard
|
||||
* workflow. See
|
||||
* https://blockstream.com/2019/02/18/musig-a-new-multisignature-standard/ for
|
||||
* more details about the risks associated with serializing or deserializing
|
||||
* this structure.
|
||||
*
|
||||
* We repeat, copying this data structure can result in nonce reuse which will
|
||||
* leak the secret signing key.
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned char data[68];
|
||||
} secp256k1_frost_secnonce;
|
||||
|
||||
/** Opaque data structure that holds a signer's public nonce.
|
||||
*
|
||||
* Guaranteed to be 132 bytes in size. It can be safely copied/moved.
|
||||
* Serialized and parsed with `frost_pubnonce_serialize` and
|
||||
* `frost_pubnonce_parse`.
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned char data[132];
|
||||
} secp256k1_frost_pubnonce;
|
||||
|
||||
/** Parse a signer's public nonce.
|
||||
*
|
||||
* Returns: 1 when the nonce could be parsed, 0 otherwise.
|
||||
* Args: ctx: pointer to a context object
|
||||
* Out: nonce: pointer to a nonce object
|
||||
* In: in66: pointer to the 66-byte nonce to be parsed
|
||||
*/
|
||||
SECP256K1_API int secp256k1_frost_pubnonce_parse(
|
||||
const secp256k1_context *ctx,
|
||||
secp256k1_frost_pubnonce *nonce,
|
||||
const unsigned char *in66
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
||||
|
||||
/** Serialize a signer's public nonce
|
||||
*
|
||||
* Returns: 1 when the nonce could be serialized, 0 otherwise
|
||||
* Args: ctx: pointer to a context object
|
||||
* Out: out66: pointer to a 66-byte array to store the serialized nonce
|
||||
* In: nonce: pointer to the nonce
|
||||
*/
|
||||
SECP256K1_API int secp256k1_frost_pubnonce_serialize(
|
||||
const secp256k1_context *ctx,
|
||||
unsigned char *out66,
|
||||
const secp256k1_frost_pubnonce *nonce
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
||||
|
||||
/** Serialize a FROST share
|
||||
*
|
||||
* Returns: 1 when the share could be serialized, 0 otherwise
|
||||
@ -181,6 +239,59 @@ SECP256K1_API int secp256k1_frost_compute_pubshare(
|
||||
size_t n_participants
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);
|
||||
|
||||
/** Starts a signing session by generating a nonce
|
||||
*
|
||||
* This function outputs a secret nonce that will be required for signing and a
|
||||
* corresponding public nonce that is intended to be sent to other signers.
|
||||
*
|
||||
* FROST, like MuSig, differs from regular Schnorr signing in that
|
||||
* implementers _must_ take special care to not reuse a nonce. This can be
|
||||
* ensured by following these rules:
|
||||
*
|
||||
* 1. Each call to this function must have a UNIQUE session_id32 that must NOT BE
|
||||
* REUSED in subsequent calls to this function.
|
||||
* If you do not provide a seckey, session_id32 _must_ be UNIFORMLY RANDOM
|
||||
* AND KEPT SECRET (even from other signers). If you do provide a seckey,
|
||||
* session_id32 can instead be a counter (that must never repeat!). However,
|
||||
* it is recommended to always choose session_id32 uniformly at random.
|
||||
* 2. If you already know the seckey, message or aggregate public key, they
|
||||
* can be optionally provided to derive the nonce and increase
|
||||
* misuse-resistance. The extra_input32 argument can be used to provide
|
||||
* additional data that does not repeat in normal scenarios, such as the
|
||||
* current time.
|
||||
* 3. Avoid copying (or serializing) the secnonce. This reduces the possibility
|
||||
* that it is used more than once for signing.
|
||||
*
|
||||
* Remember that nonce reuse will leak the secret key!
|
||||
* Note that using the same agg_share for multiple FROST sessions is fine.
|
||||
*
|
||||
* Returns: 0 if the arguments are invalid and 1 otherwise
|
||||
* Args: ctx: pointer to a context object (not secp256k1_context_static)
|
||||
* Out: secnonce: pointer to a structure to store the secret nonce
|
||||
* pubnonce: pointer to a structure to store the public nonce
|
||||
* In: session_id32: a 32-byte session_id32 as explained above. Must be
|
||||
* unique to this call to secp256k1_frost_nonce_gen and
|
||||
* must be uniformly random unless you really know what you
|
||||
* are doing.
|
||||
* agg_share: the aggregated share that will later be used for
|
||||
* signing, if already known (can be NULL)
|
||||
* msg32: the 32-byte message that will later be signed, if
|
||||
* already known (can be NULL)
|
||||
* agg_pk: the FROST-aggregated public key (can be NULL)
|
||||
* extra_input32: an optional 32-byte array that is input to the nonce
|
||||
* derivation function (can be NULL)
|
||||
*/
|
||||
SECP256K1_API int secp256k1_frost_nonce_gen(
|
||||
const secp256k1_context *ctx,
|
||||
secp256k1_frost_secnonce *secnonce,
|
||||
secp256k1_frost_pubnonce *pubnonce,
|
||||
const unsigned char *session_id32,
|
||||
const secp256k1_frost_share *agg_share,
|
||||
const unsigned char *msg32,
|
||||
const secp256k1_xonly_pubkey *agg_pk,
|
||||
const unsigned char *extra_input32
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -202,6 +202,10 @@ static void secp256k1_ge_from_bytes(secp256k1_ge *r, const unsigned char *buf);
|
||||
*/
|
||||
static int secp256k1_ge_is_in_correct_subgroup(const secp256k1_ge* ge);
|
||||
|
||||
static void secp256k1_point_save_ext(unsigned char *data, secp256k1_ge *ge);
|
||||
|
||||
static void secp256k1_point_load_ext(secp256k1_ge *ge, const unsigned char *data);
|
||||
|
||||
/** Check invariants on an affine group element (no-op unless VERIFY is enabled). */
|
||||
static void secp256k1_ge_verify(const secp256k1_ge *a);
|
||||
#define SECP256K1_GE_VERIFY(a) secp256k1_ge_verify(a)
|
||||
|
@ -914,6 +914,23 @@ static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a) {
|
||||
return secp256k1_fe_is_square_var(&yz);
|
||||
}
|
||||
|
||||
static void secp256k1_point_save_ext(unsigned char *data, secp256k1_ge *ge) {
|
||||
if (secp256k1_ge_is_infinity(ge)) {
|
||||
memset(data, 0, 64);
|
||||
} else {
|
||||
secp256k1_ge_to_bytes(data, ge);
|
||||
}
|
||||
}
|
||||
|
||||
static void secp256k1_point_load_ext(secp256k1_ge *ge, const unsigned char *data) {
|
||||
unsigned char zeros[64] = { 0 };
|
||||
if (secp256k1_memcmp_var(data, zeros, sizeof(zeros)) == 0) {
|
||||
secp256k1_ge_set_infinity(ge);
|
||||
} else {
|
||||
secp256k1_ge_from_bytes(ge, data);
|
||||
}
|
||||
}
|
||||
|
||||
static int secp256k1_ge_is_in_correct_subgroup(const secp256k1_ge* ge) {
|
||||
#ifdef EXHAUSTIVE_TEST_ORDER
|
||||
secp256k1_gej out;
|
||||
|
@ -2,3 +2,5 @@ include_HEADERS += include/secp256k1_frost.h
|
||||
noinst_HEADERS += src/modules/frost/main_impl.h
|
||||
noinst_HEADERS += src/modules/frost/keygen.h
|
||||
noinst_HEADERS += src/modules/frost/keygen_impl.h
|
||||
noinst_HEADERS += src/modules/frost/session.h
|
||||
noinst_HEADERS += src/modules/frost/session_impl.h
|
||||
|
@ -10,4 +10,8 @@
|
||||
#include "../../../include/secp256k1.h"
|
||||
#include "../../../include/secp256k1_frost.h"
|
||||
|
||||
#include "../../scalar.h"
|
||||
|
||||
static int secp256k1_frost_share_load(const secp256k1_context* ctx, secp256k1_scalar *s, const secp256k1_frost_share* share);
|
||||
|
||||
#endif
|
||||
|
@ -8,5 +8,6 @@
|
||||
#define SECP256K1_MODULE_FROST_MAIN
|
||||
|
||||
#include "keygen_impl.h"
|
||||
#include "session_impl.h"
|
||||
|
||||
#endif
|
||||
|
10
src/modules/frost/session.h
Normal file
10
src/modules/frost/session.h
Normal file
@ -0,0 +1,10 @@
|
||||
/**********************************************************************
|
||||
* Copyright (c) 2021-2024 Jesse Posner *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef SECP256K1_MODULE_FROST_SESSION_H
|
||||
#define SECP256K1_MODULE_FROST_SESSION_H
|
||||
|
||||
#endif
|
233
src/modules/frost/session_impl.h
Normal file
233
src/modules/frost/session_impl.h
Normal file
@ -0,0 +1,233 @@
|
||||
/**********************************************************************
|
||||
* Copyright (c) 2021-2024 Jesse Posner *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef SECP256K1_MODULE_FROST_SESSION_IMPL_H
|
||||
#define SECP256K1_MODULE_FROST_SESSION_IMPL_H
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "../../../include/secp256k1.h"
|
||||
#include "../../../include/secp256k1_extrakeys.h"
|
||||
#include "../../../include/secp256k1_frost.h"
|
||||
|
||||
#include "keygen.h"
|
||||
#include "session.h"
|
||||
#include "../../eckey.h"
|
||||
#include "../../hash.h"
|
||||
#include "../../scalar.h"
|
||||
#include "../../util.h"
|
||||
|
||||
static const unsigned char secp256k1_frost_secnonce_magic[4] = { 0x84, 0x7d, 0x46, 0x25 };
|
||||
|
||||
static void secp256k1_frost_secnonce_save(secp256k1_frost_secnonce *secnonce, secp256k1_scalar *k) {
|
||||
memcpy(&secnonce->data[0], secp256k1_frost_secnonce_magic, 4);
|
||||
secp256k1_scalar_get_b32(&secnonce->data[4], &k[0]);
|
||||
secp256k1_scalar_get_b32(&secnonce->data[36], &k[1]);
|
||||
}
|
||||
|
||||
static int secp256k1_frost_secnonce_load(const secp256k1_context* ctx, secp256k1_scalar *k, secp256k1_frost_secnonce *secnonce) {
|
||||
int is_zero;
|
||||
ARG_CHECK(secp256k1_memcmp_var(&secnonce->data[0], secp256k1_frost_secnonce_magic, 4) == 0);
|
||||
secp256k1_scalar_set_b32(&k[0], &secnonce->data[4], NULL);
|
||||
secp256k1_scalar_set_b32(&k[1], &secnonce->data[36], NULL);
|
||||
/* We make very sure that the nonce isn't invalidated by checking the values
|
||||
* in addition to the magic. */
|
||||
is_zero = secp256k1_scalar_is_zero(&k[0]) & secp256k1_scalar_is_zero(&k[1]);
|
||||
secp256k1_declassify(ctx, &is_zero, sizeof(is_zero));
|
||||
ARG_CHECK(!is_zero);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* If flag is true, invalidate the secnonce; otherwise leave it. Constant-time. */
|
||||
static void secp256k1_frost_secnonce_invalidate(const secp256k1_context* ctx, secp256k1_frost_secnonce *secnonce, int flag) {
|
||||
secp256k1_memczero(secnonce->data, sizeof(secnonce->data), flag);
|
||||
/* The flag argument is usually classified. So, above code makes the magic
|
||||
* classified. However, we need the magic to be declassified to be able to
|
||||
* compare it during secnonce_load. */
|
||||
secp256k1_declassify(ctx, secnonce->data, sizeof(secp256k1_frost_secnonce_magic));
|
||||
}
|
||||
|
||||
static const unsigned char secp256k1_frost_pubnonce_magic[4] = { 0x8b, 0xcf, 0xe2, 0xc2 };
|
||||
|
||||
/* Requires that none of the provided group elements is infinity. Works for both
|
||||
* frost_pubnonce and frost_aggnonce. */
|
||||
static void secp256k1_frost_pubnonce_save(secp256k1_frost_pubnonce* nonce, secp256k1_ge* ge) {
|
||||
int i;
|
||||
memcpy(&nonce->data[0], secp256k1_frost_pubnonce_magic, 4);
|
||||
for (i = 0; i < 2; i++) {
|
||||
secp256k1_point_save_ext(nonce->data + 4+64*i, &ge[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Works for both frost_pubnonce and frost_aggnonce. Returns 1 unless the nonce
|
||||
* wasn't properly initialized */
|
||||
static int secp256k1_frost_pubnonce_load(const secp256k1_context* ctx, secp256k1_ge* ge, const secp256k1_frost_pubnonce* nonce) {
|
||||
int i;
|
||||
|
||||
ARG_CHECK(secp256k1_memcmp_var(&nonce->data[0], secp256k1_frost_pubnonce_magic, 4) == 0);
|
||||
for (i = 0; i < 2; i++) {
|
||||
secp256k1_point_load_ext(&ge[i], nonce->data + 4+64*i);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int secp256k1_frost_pubnonce_serialize(const secp256k1_context* ctx, unsigned char *out66, const secp256k1_frost_pubnonce* nonce) {
|
||||
secp256k1_ge ge[2];
|
||||
int i;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(out66 != NULL);
|
||||
memset(out66, 0, 66);
|
||||
ARG_CHECK(nonce != NULL);
|
||||
|
||||
if (!secp256k1_frost_pubnonce_load(ctx, ge, nonce)) {
|
||||
return 0;
|
||||
}
|
||||
for (i = 0; i < 2; i++) {
|
||||
int ret;
|
||||
size_t size = 33;
|
||||
ret = secp256k1_eckey_pubkey_serialize(&ge[i], &out66[33*i], &size, 1);
|
||||
#ifdef VERIFY
|
||||
/* serialize must succeed because the point was just loaded */
|
||||
VERIFY_CHECK(ret && size == 33);
|
||||
#else
|
||||
(void) ret;
|
||||
#endif
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int secp256k1_frost_pubnonce_parse(const secp256k1_context* ctx, secp256k1_frost_pubnonce* nonce, const unsigned char *in66) {
|
||||
secp256k1_ge ge[2];
|
||||
int i;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(nonce != NULL);
|
||||
ARG_CHECK(in66 != NULL);
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (!secp256k1_eckey_pubkey_parse(&ge[i], &in66[33*i], 33)) {
|
||||
return 0;
|
||||
}
|
||||
if (!secp256k1_ge_is_in_correct_subgroup(&ge[i])) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/* The group elements can not be infinity because they were just parsed */
|
||||
secp256k1_frost_pubnonce_save(nonce, ge);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void secp256k1_nonce_function_frost(secp256k1_scalar *k, const unsigned char *session_id, const unsigned char *msg32, const unsigned char *key32, const unsigned char *pk32, const unsigned char *extra_input32) {
|
||||
secp256k1_sha256 sha;
|
||||
unsigned char seed[32];
|
||||
unsigned char i;
|
||||
enum { n_extra_in = 4 };
|
||||
const unsigned char *extra_in[n_extra_in];
|
||||
|
||||
/* TODO: this doesn't have the same sidechannel resistance as the BIP340
|
||||
* nonce function because the seckey feeds directly into SHA. */
|
||||
|
||||
/* Subtract one from `sizeof` to avoid hashing the implicit null byte */
|
||||
secp256k1_sha256_initialize_tagged(&sha, (unsigned char*)"FROST/nonce", sizeof("FROST/nonce") - 1);
|
||||
secp256k1_sha256_write(&sha, session_id, 32);
|
||||
extra_in[0] = msg32;
|
||||
extra_in[1] = key32;
|
||||
extra_in[2] = pk32;
|
||||
extra_in[3] = extra_input32;
|
||||
for (i = 0; i < n_extra_in; i++) {
|
||||
unsigned char len;
|
||||
if (extra_in[i] != NULL) {
|
||||
len = 32;
|
||||
secp256k1_sha256_write(&sha, &len, 1);
|
||||
secp256k1_sha256_write(&sha, extra_in[i], 32);
|
||||
} else {
|
||||
len = 0;
|
||||
secp256k1_sha256_write(&sha, &len, 1);
|
||||
}
|
||||
}
|
||||
secp256k1_sha256_finalize(&sha, seed);
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
unsigned char buf[32];
|
||||
secp256k1_sha256_initialize(&sha);
|
||||
secp256k1_sha256_write(&sha, seed, 32);
|
||||
secp256k1_sha256_write(&sha, &i, sizeof(i));
|
||||
secp256k1_sha256_finalize(&sha, buf);
|
||||
secp256k1_scalar_set_b32(&k[i], buf, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
int secp256k1_frost_nonce_gen(const secp256k1_context* ctx, secp256k1_frost_secnonce *secnonce, secp256k1_frost_pubnonce *pubnonce, const unsigned char *session_id32, const secp256k1_frost_share *share, const unsigned char *msg32, const secp256k1_xonly_pubkey *pk, const unsigned char *extra_input32) {
|
||||
secp256k1_scalar k[2];
|
||||
secp256k1_ge nonce_pt[2];
|
||||
int i;
|
||||
unsigned char pk_ser[32];
|
||||
unsigned char *pk_ser_ptr = NULL;
|
||||
unsigned char sk_ser[32];
|
||||
unsigned char *sk_ser_ptr = NULL;
|
||||
int sk_serialize_success;
|
||||
int ret = 1;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(secnonce != NULL);
|
||||
memset(secnonce, 0, sizeof(*secnonce));
|
||||
ARG_CHECK(pubnonce != NULL);
|
||||
memset(pubnonce, 0, sizeof(*pubnonce));
|
||||
ARG_CHECK(session_id32 != NULL);
|
||||
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
|
||||
if (share == NULL) {
|
||||
/* Check in constant time that the session_id is not 0 as a
|
||||
* defense-in-depth measure that may protect against a faulty RNG. */
|
||||
unsigned char acc = 0;
|
||||
for (i = 0; i < 32; i++) {
|
||||
acc |= session_id32[i];
|
||||
}
|
||||
ret &= !!acc;
|
||||
memset(&acc, 0, sizeof(acc));
|
||||
}
|
||||
|
||||
if (share != NULL) {
|
||||
/* Check that the share is valid to be able to sign for it later. */
|
||||
secp256k1_scalar sk;
|
||||
|
||||
ret &= secp256k1_frost_share_load(ctx, &sk, share);
|
||||
secp256k1_scalar_clear(&sk);
|
||||
|
||||
sk_serialize_success = secp256k1_frost_share_serialize(ctx, sk_ser, share);
|
||||
sk_ser_ptr = sk_ser;
|
||||
#ifdef VERIFY
|
||||
VERIFY_CHECK(sk_serialize_success);
|
||||
#else
|
||||
(void) sk_serialize_success;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (pk != NULL) {
|
||||
if (!secp256k1_xonly_pubkey_serialize(ctx, pk_ser, pk)) {
|
||||
return 0;
|
||||
}
|
||||
pk_ser_ptr = pk_ser;
|
||||
}
|
||||
secp256k1_nonce_function_frost(k, session_id32, msg32, sk_ser_ptr, pk_ser_ptr, extra_input32);
|
||||
VERIFY_CHECK(!secp256k1_scalar_is_zero(&k[0]));
|
||||
VERIFY_CHECK(!secp256k1_scalar_is_zero(&k[1]));
|
||||
VERIFY_CHECK(!secp256k1_scalar_eq(&k[0], &k[1]));
|
||||
secp256k1_frost_secnonce_save(secnonce, k);
|
||||
secp256k1_frost_secnonce_invalidate(ctx, secnonce, !ret);
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
secp256k1_gej nonce_ptj;
|
||||
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &nonce_ptj, &k[i]);
|
||||
secp256k1_ge_set_gej(&nonce_pt[i], &nonce_ptj);
|
||||
secp256k1_declassify(ctx, &nonce_pt[i], sizeof(nonce_pt));
|
||||
secp256k1_scalar_clear(&k[i]);
|
||||
}
|
||||
/* nonce_pt won't be infinity because k != 0 with overwhelming probability */
|
||||
secp256k1_frost_pubnonce_save(pubnonce, nonce_pt);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
@ -27,12 +27,6 @@ typedef struct {
|
||||
int parity_acc;
|
||||
} secp256k1_keyagg_cache_internal;
|
||||
|
||||
/* point_save_ext and point_load_ext are identical to point_save and point_load
|
||||
* except that they allow saving and loading the point at infinity */
|
||||
static void secp256k1_point_save_ext(unsigned char *data, secp256k1_ge *ge);
|
||||
|
||||
static void secp256k1_point_load_ext(secp256k1_ge *ge, const unsigned char *data);
|
||||
|
||||
static int secp256k1_keyagg_cache_load(const secp256k1_context* ctx, secp256k1_keyagg_cache_internal *cache_i, const secp256k1_musig_keyagg_cache *cache);
|
||||
|
||||
static void secp256k1_musig_keyaggcoef(secp256k1_scalar *r, const secp256k1_keyagg_cache_internal *cache_i, secp256k1_ge *pk);
|
||||
|
@ -17,23 +17,6 @@
|
||||
#include "../../hash.h"
|
||||
#include "../../util.h"
|
||||
|
||||
static void secp256k1_point_save_ext(unsigned char *data, secp256k1_ge *ge) {
|
||||
if (secp256k1_ge_is_infinity(ge)) {
|
||||
memset(data, 0, 64);
|
||||
} else {
|
||||
secp256k1_ge_to_bytes(data, ge);
|
||||
}
|
||||
}
|
||||
|
||||
static void secp256k1_point_load_ext(secp256k1_ge *ge, const unsigned char *data) {
|
||||
unsigned char zeros[64] = { 0 };
|
||||
if (secp256k1_memcmp_var(data, zeros, sizeof(zeros)) == 0) {
|
||||
secp256k1_ge_set_infinity(ge);
|
||||
} else {
|
||||
secp256k1_ge_from_bytes(ge, data);
|
||||
}
|
||||
}
|
||||
|
||||
static const unsigned char secp256k1_musig_keyagg_cache_magic[4] = { 0xf4, 0xad, 0xbb, 0xdf };
|
||||
|
||||
/* A keyagg cache consists of
|
||||
|
Loading…
x
Reference in New Issue
Block a user