schnorrsig: Add BIP-340 compatible signing and verification
This commit is contained in:
@@ -93,4 +93,146 @@ static int nonce_function_bip340(unsigned char *nonce32, const unsigned char *ms
|
||||
|
||||
const secp256k1_nonce_function_hardened secp256k1_nonce_function_bip340 = nonce_function_bip340;
|
||||
|
||||
/* Initializes SHA256 with fixed midstate. This midstate was computed by applying
|
||||
* SHA256 to SHA256("BIP0340/challenge")||SHA256("BIP0340/challenge"). */
|
||||
static void secp256k1_schnorrsig_sha256_tagged(secp256k1_sha256 *sha) {
|
||||
secp256k1_sha256_initialize(sha);
|
||||
sha->s[0] = 0x9cecba11ul;
|
||||
sha->s[1] = 0x23925381ul;
|
||||
sha->s[2] = 0x11679112ul;
|
||||
sha->s[3] = 0xd1627e0ful;
|
||||
sha->s[4] = 0x97c87550ul;
|
||||
sha->s[5] = 0x003cc765ul;
|
||||
sha->s[6] = 0x90f61164ul;
|
||||
sha->s[7] = 0x33e9b66aul;
|
||||
sha->bytes = 64;
|
||||
}
|
||||
|
||||
int secp256k1_schnorrsig_sign(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg32, const secp256k1_keypair *keypair, secp256k1_nonce_function_hardened noncefp, void *ndata) {
|
||||
secp256k1_scalar sk;
|
||||
secp256k1_scalar e;
|
||||
secp256k1_scalar k;
|
||||
secp256k1_gej rj;
|
||||
secp256k1_ge pk;
|
||||
secp256k1_ge r;
|
||||
secp256k1_sha256 sha;
|
||||
unsigned char buf[32] = { 0 };
|
||||
unsigned char pk_buf[32];
|
||||
unsigned char seckey[32];
|
||||
int ret = 1;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
|
||||
ARG_CHECK(sig64 != NULL);
|
||||
ARG_CHECK(msg32 != NULL);
|
||||
ARG_CHECK(keypair != NULL);
|
||||
|
||||
if (noncefp == NULL) {
|
||||
noncefp = secp256k1_nonce_function_bip340;
|
||||
}
|
||||
|
||||
ret &= secp256k1_keypair_load(ctx, &sk, &pk, keypair);
|
||||
/* Because we are signing for a x-only pubkey, the secret key is negated
|
||||
* before signing if the point corresponding to the secret key does not
|
||||
* have an even Y. */
|
||||
if (secp256k1_fe_is_odd(&pk.y)) {
|
||||
secp256k1_scalar_negate(&sk, &sk);
|
||||
}
|
||||
|
||||
secp256k1_scalar_get_b32(seckey, &sk);
|
||||
secp256k1_fe_get_b32(pk_buf, &pk.x);
|
||||
ret &= !!noncefp(buf, msg32, seckey, pk_buf, bip340_algo16, ndata);
|
||||
secp256k1_scalar_set_b32(&k, buf, NULL);
|
||||
ret &= !secp256k1_scalar_is_zero(&k);
|
||||
secp256k1_scalar_cmov(&k, &secp256k1_scalar_one, !ret);
|
||||
|
||||
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &k);
|
||||
secp256k1_ge_set_gej(&r, &rj);
|
||||
|
||||
/* We declassify r to allow using it as a branch point. This is fine
|
||||
* because r is not a secret. */
|
||||
secp256k1_declassify(ctx, &r, sizeof(r));
|
||||
secp256k1_fe_normalize_var(&r.y);
|
||||
if (secp256k1_fe_is_odd(&r.y)) {
|
||||
secp256k1_scalar_negate(&k, &k);
|
||||
}
|
||||
secp256k1_fe_normalize_var(&r.x);
|
||||
secp256k1_fe_get_b32(&sig64[0], &r.x);
|
||||
|
||||
/* tagged hash(r.x, pk.x, msg32) */
|
||||
secp256k1_schnorrsig_sha256_tagged(&sha);
|
||||
secp256k1_sha256_write(&sha, &sig64[0], 32);
|
||||
secp256k1_sha256_write(&sha, pk_buf, sizeof(pk_buf));
|
||||
secp256k1_sha256_write(&sha, msg32, 32);
|
||||
secp256k1_sha256_finalize(&sha, buf);
|
||||
|
||||
/* Set scalar e to the challenge hash modulo the curve order as per
|
||||
* BIP340. */
|
||||
secp256k1_scalar_set_b32(&e, buf, NULL);
|
||||
secp256k1_scalar_mul(&e, &e, &sk);
|
||||
secp256k1_scalar_add(&e, &e, &k);
|
||||
secp256k1_scalar_get_b32(&sig64[32], &e);
|
||||
|
||||
memczero(sig64, 64, !ret);
|
||||
secp256k1_scalar_clear(&k);
|
||||
secp256k1_scalar_clear(&sk);
|
||||
memset(seckey, 0, sizeof(seckey));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int secp256k1_schnorrsig_verify(const secp256k1_context* ctx, const unsigned char *sig64, const unsigned char *msg32, const secp256k1_xonly_pubkey *pubkey) {
|
||||
secp256k1_scalar s;
|
||||
secp256k1_scalar e;
|
||||
secp256k1_gej rj;
|
||||
secp256k1_ge pk;
|
||||
secp256k1_gej pkj;
|
||||
secp256k1_fe rx;
|
||||
secp256k1_ge r;
|
||||
secp256k1_sha256 sha;
|
||||
unsigned char buf[32];
|
||||
int overflow;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
|
||||
ARG_CHECK(sig64 != NULL);
|
||||
ARG_CHECK(msg32 != NULL);
|
||||
ARG_CHECK(pubkey != NULL);
|
||||
|
||||
if (!secp256k1_fe_set_b32(&rx, &sig64[0])) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
secp256k1_scalar_set_b32(&s, &sig64[32], &overflow);
|
||||
if (overflow) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!secp256k1_xonly_pubkey_load(ctx, &pk, pubkey)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
secp256k1_schnorrsig_sha256_tagged(&sha);
|
||||
secp256k1_sha256_write(&sha, &sig64[0], 32);
|
||||
secp256k1_fe_get_b32(buf, &pk.x);
|
||||
secp256k1_sha256_write(&sha, buf, sizeof(buf));
|
||||
secp256k1_sha256_write(&sha, msg32, 32);
|
||||
secp256k1_sha256_finalize(&sha, buf);
|
||||
secp256k1_scalar_set_b32(&e, buf, NULL);
|
||||
|
||||
/* Compute rj = s*G + (-e)*pkj */
|
||||
secp256k1_scalar_negate(&e, &e);
|
||||
secp256k1_gej_set_ge(&pkj, &pk);
|
||||
secp256k1_ecmult(&ctx->ecmult_ctx, &rj, &pkj, &e, &s);
|
||||
|
||||
secp256k1_ge_set_gej_var(&r, &rj);
|
||||
if (secp256k1_ge_is_infinity(&r)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
secp256k1_fe_normalize_var(&r.y);
|
||||
return !secp256k1_fe_is_odd(&r.y) &&
|
||||
secp256k1_fe_equal_var(&rx, &r.x);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user