frost: signature generation and aggregation
This commit adds signature generation and aggregation, as well as partial signature serialization and parsing.
This commit is contained in:
parent
4464586de1
commit
123ae6a62d
@ -14,7 +14,8 @@ extern "C" {
|
|||||||
*
|
*
|
||||||
* This module implements a variant of Flexible Round-Optimized Schnorr
|
* This module implements a variant of Flexible Round-Optimized Schnorr
|
||||||
* Threshold Signatures (FROST) by Chelsea Komlo and Ian Goldberg
|
* Threshold Signatures (FROST) by Chelsea Komlo and Ian Goldberg
|
||||||
* (https://crysp.uwaterloo.ca/software/frost/).
|
* (https://crysp.uwaterloo.ca/software/frost/). Signatures are compatible with
|
||||||
|
* BIP-340 ("Schnorr").
|
||||||
*
|
*
|
||||||
* The module also supports BIP-341 ("Taproot") and BIP-32 ("ordinary") public
|
* The module also supports BIP-341 ("Taproot") and BIP-32 ("ordinary") public
|
||||||
* key tweaking, and adaptor signatures.
|
* key tweaking, and adaptor signatures.
|
||||||
@ -88,6 +89,15 @@ typedef struct {
|
|||||||
unsigned char data[133];
|
unsigned char data[133];
|
||||||
} secp256k1_frost_session;
|
} secp256k1_frost_session;
|
||||||
|
|
||||||
|
/** Opaque data structure that holds a partial FROST signature.
|
||||||
|
*
|
||||||
|
* Guaranteed to be 36 bytes in size. Serialized and parsed with
|
||||||
|
* `frost_partial_sig_serialize` and `frost_partial_sig_parse`.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
unsigned char data[36];
|
||||||
|
} secp256k1_frost_partial_sig;
|
||||||
|
|
||||||
/** Parse a signer's public nonce.
|
/** Parse a signer's public nonce.
|
||||||
*
|
*
|
||||||
* Returns: 1 when the nonce could be parsed, 0 otherwise.
|
* Returns: 1 when the nonce could be parsed, 0 otherwise.
|
||||||
@ -114,6 +124,36 @@ SECP256K1_API int secp256k1_frost_pubnonce_serialize(
|
|||||||
const secp256k1_frost_pubnonce *nonce
|
const secp256k1_frost_pubnonce *nonce
|
||||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
||||||
|
|
||||||
|
/** Serialize a FROST partial signature
|
||||||
|
*
|
||||||
|
* Returns: 1 when the signature could be serialized, 0 otherwise
|
||||||
|
* Args: ctx: pointer to a context object
|
||||||
|
* Out: out32: pointer to a 32-byte array to store the serialized signature
|
||||||
|
* In: sig: pointer to the signature
|
||||||
|
*/
|
||||||
|
SECP256K1_API int secp256k1_frost_partial_sig_serialize(
|
||||||
|
const secp256k1_context *ctx,
|
||||||
|
unsigned char *out32,
|
||||||
|
const secp256k1_frost_partial_sig *sig
|
||||||
|
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
||||||
|
|
||||||
|
/** Parse a FROST partial signature.
|
||||||
|
*
|
||||||
|
* Returns: 1 when the signature could be parsed, 0 otherwise.
|
||||||
|
* Args: ctx: pointer to a context object
|
||||||
|
* Out: sig: pointer to a signature object
|
||||||
|
* In: in32: pointer to the 32-byte signature to be parsed
|
||||||
|
*
|
||||||
|
* After the call, sig will always be initialized. If parsing failed or the
|
||||||
|
* encoded numbers are out of range, signature verification with it is
|
||||||
|
* guaranteed to fail for every message and public key.
|
||||||
|
*/
|
||||||
|
SECP256K1_API int secp256k1_frost_partial_sig_parse(
|
||||||
|
const secp256k1_context *ctx,
|
||||||
|
secp256k1_frost_partial_sig *sig,
|
||||||
|
const unsigned char *in32
|
||||||
|
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
||||||
|
|
||||||
/** Serialize a FROST share
|
/** Serialize a FROST share
|
||||||
*
|
*
|
||||||
* Returns: 1 when the share could be serialized, 0 otherwise
|
* Returns: 1 when the share could be serialized, 0 otherwise
|
||||||
@ -467,6 +507,90 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_nonce_process(
|
|||||||
const secp256k1_pubkey *adaptor
|
const secp256k1_pubkey *adaptor
|
||||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8);
|
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8);
|
||||||
|
|
||||||
|
/** Produces a partial signature
|
||||||
|
*
|
||||||
|
* This function overwrites the given secnonce with zeros and will abort if given a
|
||||||
|
* secnonce that is all zeros. This is a best effort attempt to protect against nonce
|
||||||
|
* reuse. However, this is of course easily defeated if the secnonce has been
|
||||||
|
* copied (or serialized). Remember that nonce reuse will leak the secret key!
|
||||||
|
*
|
||||||
|
* Returns: 0 if the arguments are invalid or the provided secnonce has already
|
||||||
|
* been used for signing, 1 otherwise
|
||||||
|
* Args: ctx: pointer to a context object
|
||||||
|
* Out: partial_sig: pointer to struct to store the partial signature
|
||||||
|
* In/Out: secnonce: pointer to the secnonce struct created in
|
||||||
|
* frost_nonce_gen that has been never used in a
|
||||||
|
* partial_sign call before
|
||||||
|
* In: agg_share: the aggregated share
|
||||||
|
* session: pointer to the session that was created with
|
||||||
|
* frost_nonce_process
|
||||||
|
* tweak_cache: pointer to frost_tweak_cache struct (can be NULL)
|
||||||
|
*/
|
||||||
|
SECP256K1_API int secp256k1_frost_partial_sign(
|
||||||
|
const secp256k1_context *ctx,
|
||||||
|
secp256k1_frost_partial_sig *partial_sig,
|
||||||
|
secp256k1_frost_secnonce *secnonce,
|
||||||
|
const secp256k1_frost_share *agg_share,
|
||||||
|
const secp256k1_frost_session *session,
|
||||||
|
const secp256k1_frost_tweak_cache *tweak_cache
|
||||||
|
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);
|
||||||
|
|
||||||
|
/** Verifies an individual signer's partial signature
|
||||||
|
*
|
||||||
|
* The signature is verified for a specific signing session. In order to avoid
|
||||||
|
* accidentally verifying a signature from a different or non-existing signing
|
||||||
|
* session, you must ensure the following:
|
||||||
|
* 1. The `tweak_cache` argument is identical to the one used to create the
|
||||||
|
* `session` with `frost_nonce_process`.
|
||||||
|
* 2. The `pubshare` argument must be the output of
|
||||||
|
* `secp256k1_frost_shares_trusted_gen` for the signer's 'pk'.
|
||||||
|
* 3. The `pubnonce` argument must be identical to the one sent by the
|
||||||
|
* signer and used to create the `session` with `frost_nonce_process`.
|
||||||
|
*
|
||||||
|
* This function can be used to assign blame for a failed signature.
|
||||||
|
*
|
||||||
|
* Returns: 0 if the arguments are invalid or the partial signature does not
|
||||||
|
* verify, 1 otherwise
|
||||||
|
* Args ctx: pointer to a context object
|
||||||
|
* In: partial_sig: pointer to partial signature to verify, sent by
|
||||||
|
* the signer associated with `pubnonce` and `pubkey`
|
||||||
|
* pubnonce: public nonce of the signer in the signing session
|
||||||
|
* pubshare: public verification share of the signer in the signing
|
||||||
|
* session that is the output of
|
||||||
|
* `secp256k1_frost_shares_trusted_gen`
|
||||||
|
* session: pointer to the session that was created with
|
||||||
|
* `frost_nonce_process`
|
||||||
|
* tweak_cache: pointer to frost_tweak_cache struct (can be NULL)
|
||||||
|
*/
|
||||||
|
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_frost_partial_sig_verify(
|
||||||
|
const secp256k1_context *ctx,
|
||||||
|
const secp256k1_frost_partial_sig *partial_sig,
|
||||||
|
const secp256k1_frost_pubnonce *pubnonce,
|
||||||
|
const secp256k1_pubkey *pubshare,
|
||||||
|
const secp256k1_frost_session *session,
|
||||||
|
const secp256k1_frost_tweak_cache *tweak_cache
|
||||||
|
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);
|
||||||
|
|
||||||
|
/** Aggregates partial signatures
|
||||||
|
*
|
||||||
|
* Returns: 0 if the arguments are invalid, 1 otherwise (which does NOT mean
|
||||||
|
* the resulting signature verifies).
|
||||||
|
* Args: ctx: pointer to a context object
|
||||||
|
* Out: sig64: complete (but possibly invalid) Schnorr signature
|
||||||
|
* In: session: pointer to the session that was created with
|
||||||
|
* frost_nonce_process
|
||||||
|
* partial_sigs: array of pointers to partial signatures to aggregate
|
||||||
|
* n_sigs: number of elements in the partial_sigs array. Must be
|
||||||
|
* greater than 0.
|
||||||
|
*/
|
||||||
|
SECP256K1_API int secp256k1_frost_partial_sig_agg(
|
||||||
|
const secp256k1_context *ctx,
|
||||||
|
unsigned char *sig64,
|
||||||
|
const secp256k1_frost_session *session,
|
||||||
|
const secp256k1_frost_partial_sig * const *partial_sigs,
|
||||||
|
size_t n_sigs
|
||||||
|
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
|
||||||
|
|
||||||
/** Extracts the nonce_parity bit from a session
|
/** Extracts the nonce_parity bit from a session
|
||||||
*
|
*
|
||||||
* This is used for adaptor signatures.
|
* This is used for adaptor signatures.
|
||||||
|
@ -117,6 +117,23 @@ static int secp256k1_frost_session_load(const secp256k1_context* ctx, secp256k1_
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const unsigned char secp256k1_frost_partial_sig_magic[4] = { 0x8d, 0xd8, 0x31, 0x6e };
|
||||||
|
|
||||||
|
static void secp256k1_frost_partial_sig_save(secp256k1_frost_partial_sig* sig, secp256k1_scalar *s) {
|
||||||
|
memcpy(&sig->data[0], secp256k1_frost_partial_sig_magic, 4);
|
||||||
|
secp256k1_scalar_get_b32(&sig->data[4], s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int secp256k1_frost_partial_sig_load(const secp256k1_context* ctx, secp256k1_scalar *s, const secp256k1_frost_partial_sig* sig) {
|
||||||
|
int overflow;
|
||||||
|
|
||||||
|
ARG_CHECK(secp256k1_memcmp_var(&sig->data[0], secp256k1_frost_partial_sig_magic, 4) == 0);
|
||||||
|
secp256k1_scalar_set_b32(s, &sig->data[4], &overflow);
|
||||||
|
/* Parsed signatures can not overflow */
|
||||||
|
VERIFY_CHECK(!overflow);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
int secp256k1_frost_pubnonce_serialize(const secp256k1_context* ctx, unsigned char *out66, const secp256k1_frost_pubnonce* nonce) {
|
int secp256k1_frost_pubnonce_serialize(const secp256k1_context* ctx, unsigned char *out66, const secp256k1_frost_pubnonce* nonce) {
|
||||||
secp256k1_ge ge[2];
|
secp256k1_ge ge[2];
|
||||||
int i;
|
int i;
|
||||||
@ -163,6 +180,29 @@ int secp256k1_frost_pubnonce_parse(const secp256k1_context* ctx, secp256k1_frost
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int secp256k1_frost_partial_sig_serialize(const secp256k1_context* ctx, unsigned char *out32, const secp256k1_frost_partial_sig* sig) {
|
||||||
|
VERIFY_CHECK(ctx != NULL);
|
||||||
|
ARG_CHECK(out32 != NULL);
|
||||||
|
ARG_CHECK(sig != NULL);
|
||||||
|
memcpy(out32, &sig->data[4], 32);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int secp256k1_frost_partial_sig_parse(const secp256k1_context* ctx, secp256k1_frost_partial_sig* sig, const unsigned char *in32) {
|
||||||
|
secp256k1_scalar tmp;
|
||||||
|
int overflow;
|
||||||
|
VERIFY_CHECK(ctx != NULL);
|
||||||
|
ARG_CHECK(sig != NULL);
|
||||||
|
ARG_CHECK(in32 != NULL);
|
||||||
|
|
||||||
|
secp256k1_scalar_set_b32(&tmp, in32, &overflow);
|
||||||
|
if (overflow) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
secp256k1_frost_partial_sig_save(sig, &tmp);
|
||||||
|
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) {
|
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;
|
secp256k1_sha256 sha;
|
||||||
unsigned char seed[32];
|
unsigned char seed[32];
|
||||||
@ -469,4 +509,154 @@ int secp256k1_frost_nonce_process(const secp256k1_context* ctx, secp256k1_frost_
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void secp256k1_frost_partial_sign_clear(secp256k1_scalar *sk, secp256k1_scalar *k) {
|
||||||
|
secp256k1_scalar_clear(sk);
|
||||||
|
secp256k1_scalar_clear(&k[0]);
|
||||||
|
secp256k1_scalar_clear(&k[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
int secp256k1_frost_partial_sign(const secp256k1_context* ctx, secp256k1_frost_partial_sig *partial_sig, secp256k1_frost_secnonce *secnonce, const secp256k1_frost_share *share, const secp256k1_frost_session *session, const secp256k1_frost_tweak_cache *tweak_cache) {
|
||||||
|
secp256k1_scalar sk;
|
||||||
|
secp256k1_scalar k[2];
|
||||||
|
secp256k1_scalar s;
|
||||||
|
secp256k1_frost_session_internal session_i;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
VERIFY_CHECK(ctx != NULL);
|
||||||
|
|
||||||
|
ARG_CHECK(secnonce != NULL);
|
||||||
|
/* Fails if the magic doesn't match */
|
||||||
|
ret = secp256k1_frost_secnonce_load(ctx, k, secnonce);
|
||||||
|
/* Set nonce to zero to avoid nonce reuse. This will cause subsequent calls
|
||||||
|
* of this function to fail */
|
||||||
|
memset(secnonce, 0, sizeof(*secnonce));
|
||||||
|
if (!ret) {
|
||||||
|
secp256k1_frost_partial_sign_clear(&sk, k);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ARG_CHECK(partial_sig != NULL);
|
||||||
|
ARG_CHECK(share != NULL);
|
||||||
|
ARG_CHECK(session != NULL);
|
||||||
|
|
||||||
|
if (!secp256k1_frost_share_load(ctx, &sk, share)) {
|
||||||
|
secp256k1_frost_partial_sign_clear(&sk, k);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!secp256k1_frost_session_load(ctx, &session_i, session)) {
|
||||||
|
secp256k1_frost_partial_sign_clear(&sk, k);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tweak_cache != NULL) {
|
||||||
|
secp256k1_tweak_cache_internal cache_i;
|
||||||
|
if (!secp256k1_tweak_cache_load(ctx, &cache_i, tweak_cache)) {
|
||||||
|
secp256k1_frost_partial_sign_clear(&sk, k);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (secp256k1_fe_is_odd(&cache_i.pk.y) != cache_i.parity_acc) {
|
||||||
|
secp256k1_scalar_negate(&sk, &sk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (session_i.fin_nonce_parity) {
|
||||||
|
secp256k1_scalar_negate(&k[0], &k[0]);
|
||||||
|
secp256k1_scalar_negate(&k[1], &k[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sign */
|
||||||
|
secp256k1_scalar_mul(&s, &session_i.challenge, &sk);
|
||||||
|
secp256k1_scalar_mul(&k[1], &session_i.noncecoef, &k[1]);
|
||||||
|
secp256k1_scalar_add(&k[0], &k[0], &k[1]);
|
||||||
|
secp256k1_scalar_add(&s, &s, &k[0]);
|
||||||
|
secp256k1_frost_partial_sig_save(partial_sig, &s);
|
||||||
|
secp256k1_frost_partial_sign_clear(&sk, k);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int secp256k1_frost_partial_sig_verify(const secp256k1_context* ctx, const secp256k1_frost_partial_sig *partial_sig, const secp256k1_frost_pubnonce *pubnonce, const secp256k1_pubkey *pubshare, const secp256k1_frost_session *session, const secp256k1_frost_tweak_cache *tweak_cache) {
|
||||||
|
secp256k1_frost_session_internal session_i;
|
||||||
|
secp256k1_scalar e, s;
|
||||||
|
secp256k1_gej pkj;
|
||||||
|
secp256k1_ge nonce_pt[2];
|
||||||
|
secp256k1_gej rj;
|
||||||
|
secp256k1_gej tmp;
|
||||||
|
secp256k1_ge pkp;
|
||||||
|
|
||||||
|
VERIFY_CHECK(ctx != NULL);
|
||||||
|
ARG_CHECK(partial_sig != NULL);
|
||||||
|
ARG_CHECK(pubnonce != NULL);
|
||||||
|
ARG_CHECK(pubshare != NULL);
|
||||||
|
ARG_CHECK(session != NULL);
|
||||||
|
|
||||||
|
if (!secp256k1_frost_session_load(ctx, &session_i, session)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compute "effective" nonce rj = aggnonce[0] + b*aggnonce[1] */
|
||||||
|
/* TODO: use multiexp to compute -s*G + e*pubshare + aggnonce[0] + b*aggnonce[1] */
|
||||||
|
if (!secp256k1_frost_pubnonce_load(ctx, nonce_pt, pubnonce)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
secp256k1_gej_set_ge(&rj, &nonce_pt[1]);
|
||||||
|
secp256k1_ecmult(&rj, &rj, &session_i.noncecoef, NULL);
|
||||||
|
secp256k1_gej_add_ge_var(&rj, &rj, &nonce_pt[0], NULL);
|
||||||
|
|
||||||
|
if (!secp256k1_pubkey_load(ctx, &pkp, pubshare)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
secp256k1_scalar_set_int(&e, 1);
|
||||||
|
if (tweak_cache != NULL) {
|
||||||
|
secp256k1_tweak_cache_internal cache_i;
|
||||||
|
if (!secp256k1_tweak_cache_load(ctx, &cache_i, tweak_cache)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (secp256k1_fe_is_odd(&cache_i.pk.y)
|
||||||
|
!= cache_i.parity_acc) {
|
||||||
|
secp256k1_scalar_negate(&e, &e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
secp256k1_scalar_mul(&e, &e, &session_i.challenge);
|
||||||
|
|
||||||
|
if (!secp256k1_frost_partial_sig_load(ctx, &s, partial_sig)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* Compute -s*G + e*pkj + rj (e already includes the lagrange coefficient l) */
|
||||||
|
secp256k1_scalar_negate(&s, &s);
|
||||||
|
secp256k1_gej_set_ge(&pkj, &pkp);
|
||||||
|
secp256k1_ecmult(&tmp, &pkj, &e, &s);
|
||||||
|
if (session_i.fin_nonce_parity) {
|
||||||
|
secp256k1_gej_neg(&rj, &rj);
|
||||||
|
}
|
||||||
|
secp256k1_gej_add_var(&tmp, &tmp, &rj, NULL);
|
||||||
|
|
||||||
|
return secp256k1_gej_is_infinity(&tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
int secp256k1_frost_partial_sig_agg(const secp256k1_context* ctx, unsigned char *sig64, const secp256k1_frost_session *session, const secp256k1_frost_partial_sig * const* partial_sigs, size_t n_sigs) {
|
||||||
|
size_t i;
|
||||||
|
secp256k1_frost_session_internal session_i;
|
||||||
|
|
||||||
|
VERIFY_CHECK(ctx != NULL);
|
||||||
|
ARG_CHECK(sig64 != NULL);
|
||||||
|
ARG_CHECK(session != NULL);
|
||||||
|
ARG_CHECK(partial_sigs != NULL);
|
||||||
|
ARG_CHECK(n_sigs > 0);
|
||||||
|
|
||||||
|
if (!secp256k1_frost_session_load(ctx, &session_i, session)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
for (i = 0; i < n_sigs; i++) {
|
||||||
|
secp256k1_scalar term;
|
||||||
|
if (!secp256k1_frost_partial_sig_load(ctx, &term, partial_sigs[i])) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
secp256k1_scalar_add(&session_i.s_part, &session_i.s_part, &term);
|
||||||
|
}
|
||||||
|
secp256k1_scalar_get_b32(&sig64[32], &session_i.s_part);
|
||||||
|
memcpy(&sig64[0], session_i.fin_nonce, 32);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
x
Reference in New Issue
Block a user