Changeset View
Changeset View
Standalone View
Standalone View
src/secp256k1/src/modules/schnorr/schnorr_impl.h
- This file was added.
/*********************************************************************** | |||||
* Copyright (c) 2017 Amaury SÉCHET * | |||||
* Distributed under the MIT software license, see the accompanying * | |||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php. * | |||||
***********************************************************************/ | |||||
#ifndef _SECP256K1_SCHNORR_IMPL_H_ | |||||
#define _SECP256K1_SCHNORR_IMPL_H_ | |||||
#include <string.h> | |||||
#include "schnorr.h" | |||||
#include "field.h" | |||||
#include "group.h" | |||||
#include "hash.h" | |||||
#include "ecmult.h" | |||||
#include "ecmult_gen.h" | |||||
/** | |||||
* Custom Schnorr-based signature scheme. | |||||
* | |||||
* Signing: | |||||
* Inputs: | |||||
* 32-byte message m, | |||||
* 32-byte scalar key x (!=0) | |||||
* public key point P, | |||||
* 32-byte scalar nonce k (!=0) | |||||
* | |||||
* Compute point R = k * G. Negate nonce if R.y is not a quadratic residue. | |||||
* Compute scalar e = Hash(R.x || compressed(P) || m). Reject nonce if e == 0 or e >= order. | |||||
* Compute scalar s = k + e * x. | |||||
* The signature is (R.x, s). | |||||
* | |||||
* Verification: | |||||
* Inputs: | |||||
* 32-byte message m, | |||||
* public key point P, | |||||
* signature: (32-byte r, scalar s) | |||||
* | |||||
* Signature is invalid if s >= order or r >= p. | |||||
jasonbcox: I don't see these checks being made. Am I missing something? | |||||
deadalnixAuthorUnsubmitted Done Inline ActionsYes deadalnix: Yes | |||||
* Compute scalar e = Hash(r || compressed(P) || m). Reject e == 0 or e >= order. | |||||
* Option 1 (faster for single verification): | |||||
* Compute point R = s * G - e * P. | |||||
* Reject if R is infinity or R.y is not a quadratic residue. | |||||
* Signature is valid if the serialization of R.x equals r. | |||||
* Option 2 (allows batch validation): | |||||
* Decompress x coordinate r into point R, with R.y a quadratic residue. | |||||
* Reject if R is not on the curve. | |||||
* Signature is valid if R + e * P - s * G == 0. | |||||
*/ | |||||
static int secp256k1_schnorr_sig_verify( | |||||
const secp256k1_ecmult_context* ctx, | |||||
const unsigned char *sig64, | |||||
secp256k1_ge *pubkey, | |||||
const unsigned char *msg32 | |||||
) { | |||||
secp256k1_gej Pj, Rj; | |||||
secp256k1_fe Rx; | |||||
secp256k1_scalar e, s; | |||||
int overflow; | |||||
if (secp256k1_ge_is_infinity(pubkey)) { | |||||
return 0; | |||||
} | |||||
/* Extract s */ | |||||
overflow = 0; | |||||
secp256k1_scalar_set_b32(&s, sig64 + 32, &overflow); | |||||
if (overflow) { | |||||
return 0; | |||||
} | |||||
/* Extract R.x */ | |||||
if (!secp256k1_fe_set_b32(&Rx, sig64)) { | |||||
jasonbcoxUnsubmitted Not Done Inline ActionsShouldn't this be r instead of Rx? jasonbcox: Shouldn't this be `r` instead of `Rx`? | |||||
return 0; | |||||
} | |||||
/* Compute e */ | |||||
if (!secp256k1_schnorr_compute_e(&e, sig64, pubkey, msg32)) { | |||||
jasonbcoxUnsubmitted Not Done Inline ActionsAny reason to use sig64 here instead of r? jasonbcox: Any reason to use `sig64` here instead of `r`? | |||||
deadalnixAuthorUnsubmitted Done Inline ActionsYes deadalnix: Yes | |||||
return 0; | |||||
} | |||||
/* Verify the signature */ | |||||
secp256k1_scalar_negate(&e, &e); | |||||
secp256k1_gej_set_ge(&Pj, pubkey); | |||||
secp256k1_ecmult(ctx, &Rj, &Pj, &e, &s); | |||||
if (secp256k1_gej_is_infinity(&Rj)) { | |||||
return 0; | |||||
} | |||||
/* Check that R.x is what we expect */ | |||||
if (!secp256k1_gej_eq_x_var(&Rx, &Rj)) { | |||||
return 0; | |||||
} | |||||
/* Check that jacobi(R.y) is 1 */ | |||||
if (!secp256k1_gej_has_quad_y_var(&Rj)) { | |||||
return 0; | |||||
} | |||||
/* All good, we have a valid signature. */ | |||||
return 1; | |||||
} | |||||
static int secp256k1_schnorr_compute_e( | |||||
secp256k1_scalar* e, | |||||
const unsigned char *rx, | |||||
jasonbcoxUnsubmitted Not Done Inline ActionsEither this should be r or Rx to be consistent with the above code. jasonbcox: Either this should be `r` or `Rx` to be consistent with the above code. | |||||
deadalnixAuthorUnsubmitted Done Inline Actionsok deadalnix: ok | |||||
secp256k1_ge *p, | |||||
const unsigned char *msg32 | |||||
) { | |||||
int overflow = 0; | |||||
size_t size; | |||||
secp256k1_sha256 sha; | |||||
unsigned char buf[33]; | |||||
secp256k1_sha256_initialize(&sha); | |||||
/* R.x */ | |||||
secp256k1_sha256_write(&sha, rx, 32); | |||||
/* compressed P */ | |||||
secp256k1_eckey_pubkey_serialize(p, buf, &size, 1); | |||||
VERIFY_CHECK(size == 33); | |||||
secp256k1_sha256_write(&sha, buf, 33); | |||||
/* msg */ | |||||
secp256k1_sha256_write(&sha, msg32, 32); | |||||
/* compute e */ | |||||
secp256k1_sha256_finalize(&sha, buf); | |||||
secp256k1_scalar_set_b32(e, buf, &overflow); | |||||
return !overflow & !secp256k1_scalar_is_zero(e); | |||||
} | |||||
static int secp256k1_schnorr_sig_sign( | |||||
const secp256k1_ecmult_gen_context* ctx, | |||||
unsigned char *sig64, | |||||
const secp256k1_scalar *privkey, | |||||
secp256k1_ge *pubkey, | |||||
const secp256k1_scalar *nonce, | |||||
const unsigned char *msg32 | |||||
) { | |||||
secp256k1_gej Rj; | |||||
secp256k1_ge Ra; | |||||
secp256k1_scalar e, s, k; | |||||
if (secp256k1_scalar_is_zero(privkey) || secp256k1_scalar_is_zero(nonce)) { | |||||
return 0; | |||||
} | |||||
k = *nonce; | |||||
secp256k1_ecmult_gen(ctx, &Rj, &k); | |||||
secp256k1_ge_set_gej(&Ra, &Rj); | |||||
if (!secp256k1_fe_is_quad_var(&Ra.y)) { | |||||
/** | |||||
* R's y coordinate is not a quadratic residue, which is not allowed. | |||||
* Negate the nonce to ensure it is. | |||||
*/ | |||||
secp256k1_scalar_negate(&k, &k); | |||||
} | |||||
secp256k1_fe_normalize(&Ra.x); | |||||
secp256k1_fe_get_b32(sig64, &Ra.x); | |||||
if (!secp256k1_schnorr_compute_e(&e, sig64, pubkey, msg32)) { | |||||
secp256k1_scalar_clear(&k); | |||||
return 0; | |||||
} | |||||
secp256k1_scalar_mul(&s, &e, privkey); | |||||
secp256k1_scalar_add(&s, &s, &k); | |||||
secp256k1_scalar_clear(&k); | |||||
secp256k1_scalar_get_b32(sig64 + 32, &s); | |||||
return 1; | |||||
} | |||||
static int secp256k1_schnorr_sig_generate_k( | |||||
secp256k1_scalar *k, | |||||
const unsigned char *msg32, | |||||
const unsigned char *seckey, | |||||
secp256k1_nonce_function noncefp, | |||||
const void *ndata | |||||
) { | |||||
int overflow = 0; | |||||
int ret = 0; | |||||
unsigned int count = 0; | |||||
unsigned char nonce32[32]; | |||||
/* Seed used to make sure we generate different values of k for schnorr */ | |||||
const unsigned char secp256k1_schnorr_algo16[17] = "Schnorr+SHA256 "; | |||||
if (noncefp == NULL) { | |||||
noncefp = secp256k1_nonce_function_default; | |||||
} | |||||
while (1) { | |||||
ret = noncefp(nonce32, msg32, seckey, secp256k1_schnorr_algo16, (void*)ndata, count++); | |||||
if (!ret) { | |||||
break; | |||||
} | |||||
secp256k1_scalar_set_b32(k, nonce32, &overflow); | |||||
if (!overflow && !secp256k1_scalar_is_zero(k)) { | |||||
break; | |||||
} | |||||
} | |||||
memset(nonce32, 0, 32); | |||||
return ret; | |||||
} | |||||
#endif |
I don't see these checks being made. Am I missing something?