schnorrsig: Add BIP-340 nonce function
This commit is contained in:
parent
7a703fd97d
commit
7332d2db6b
|
@ -8,7 +8,55 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* TODO */
|
||||
/** This module implements a variant of Schnorr signatures compliant with
|
||||
* Bitcoin Improvement Proposal 340 "Schnorr Signatures for secp256k1"
|
||||
* (https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki).
|
||||
*/
|
||||
|
||||
/** A pointer to a function to deterministically generate a nonce.
|
||||
*
|
||||
* Same as secp256k1_nonce function with the exception of accepting an
|
||||
* additional pubkey argument and not requiring an attempt argument. The pubkey
|
||||
* argument can protect signature schemes with key-prefixed challenge hash
|
||||
* inputs against reusing the nonce when signing with the wrong precomputed
|
||||
* pubkey.
|
||||
*
|
||||
* Returns: 1 if a nonce was successfully generated. 0 will cause signing to
|
||||
* return an error.
|
||||
* Out: nonce32: pointer to a 32-byte array to be filled by the function.
|
||||
* In: msg32: the 32-byte message hash being verified (will not be NULL)
|
||||
* key32: pointer to a 32-byte secret key (will not be NULL)
|
||||
* xonly_pk32: the 32-byte serialized xonly pubkey corresponding to key32
|
||||
* (will not be NULL)
|
||||
* algo16: pointer to a 16-byte array describing the signature
|
||||
* algorithm (will not be NULL).
|
||||
* data: Arbitrary data pointer that is passed through.
|
||||
*
|
||||
* Except for test cases, this function should compute some cryptographic hash of
|
||||
* the message, the key, the pubkey, the algorithm description, and data.
|
||||
*/
|
||||
typedef int (*secp256k1_nonce_function_hardened)(
|
||||
unsigned char *nonce32,
|
||||
const unsigned char *msg32,
|
||||
const unsigned char *key32,
|
||||
const unsigned char *xonly_pk32,
|
||||
const unsigned char *algo16,
|
||||
void *data
|
||||
);
|
||||
|
||||
/** An implementation of the nonce generation function as defined in Bitcoin
|
||||
* Improvement Proposal 340 "Schnorr Signatures for secp256k1"
|
||||
* (https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki).
|
||||
*
|
||||
* If a data pointer is passed, it is assumed to be a pointer to 32 bytes of
|
||||
* auxiliary random data as defined in BIP-340. If the data pointer is NULL,
|
||||
* schnorrsig_sign does not produce BIP-340 compliant signatures. The algo16
|
||||
* argument must be non-NULL, otherwise the function will fail and return 0.
|
||||
* The hash will be tagged with algo16 after removing all terminating null
|
||||
* bytes. Therefore, to create BIP-340 compliant signatures, algo16 must be set
|
||||
* to "BIP0340/nonce\0\0\0"
|
||||
*/
|
||||
SECP256K1_API extern const secp256k1_nonce_function_hardened secp256k1_nonce_function_bip340;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -11,6 +11,86 @@
|
|||
#include "include/secp256k1_schnorrsig.h"
|
||||
#include "hash.h"
|
||||
|
||||
/* TODO */
|
||||
/* Initializes SHA256 with fixed midstate. This midstate was computed by applying
|
||||
* SHA256 to SHA256("BIP0340/nonce")||SHA256("BIP0340/nonce"). */
|
||||
static void secp256k1_nonce_function_bip340_sha256_tagged(secp256k1_sha256 *sha) {
|
||||
secp256k1_sha256_initialize(sha);
|
||||
sha->s[0] = 0x46615b35ul;
|
||||
sha->s[1] = 0xf4bfbff7ul;
|
||||
sha->s[2] = 0x9f8dc671ul;
|
||||
sha->s[3] = 0x83627ab3ul;
|
||||
sha->s[4] = 0x60217180ul;
|
||||
sha->s[5] = 0x57358661ul;
|
||||
sha->s[6] = 0x21a29e54ul;
|
||||
sha->s[7] = 0x68b07b4cul;
|
||||
|
||||
sha->bytes = 64;
|
||||
}
|
||||
|
||||
/* Initializes SHA256 with fixed midstate. This midstate was computed by applying
|
||||
* SHA256 to SHA256("BIP0340/aux")||SHA256("BIP0340/aux"). */
|
||||
static void secp256k1_nonce_function_bip340_sha256_tagged_aux(secp256k1_sha256 *sha) {
|
||||
secp256k1_sha256_initialize(sha);
|
||||
sha->s[0] = 0x24dd3219ul;
|
||||
sha->s[1] = 0x4eba7e70ul;
|
||||
sha->s[2] = 0xca0fabb9ul;
|
||||
sha->s[3] = 0x0fa3166dul;
|
||||
sha->s[4] = 0x3afbe4b1ul;
|
||||
sha->s[5] = 0x4c44df97ul;
|
||||
sha->s[6] = 0x4aac2739ul;
|
||||
sha->s[7] = 0x249e850aul;
|
||||
|
||||
sha->bytes = 64;
|
||||
}
|
||||
|
||||
/* algo16 argument for nonce_function_bip340 to derive the nonce exactly as stated in BIP-340
|
||||
* by using the correct tagged hash function. */
|
||||
static const unsigned char bip340_algo16[16] = "BIP0340/nonce\0\0\0";
|
||||
|
||||
static int nonce_function_bip340(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *xonly_pk32, const unsigned char *algo16, void *data) {
|
||||
secp256k1_sha256 sha;
|
||||
unsigned char masked_key[32];
|
||||
int i;
|
||||
|
||||
if (algo16 == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (data != NULL) {
|
||||
secp256k1_nonce_function_bip340_sha256_tagged_aux(&sha);
|
||||
secp256k1_sha256_write(&sha, data, 32);
|
||||
secp256k1_sha256_finalize(&sha, masked_key);
|
||||
for (i = 0; i < 32; i++) {
|
||||
masked_key[i] ^= key32[i];
|
||||
}
|
||||
}
|
||||
|
||||
/* Tag the hash with algo16 which is important to avoid nonce reuse across
|
||||
* algorithms. If this nonce function is used in BIP-340 signing as defined
|
||||
* in the spec, an optimized tagging implementation is used. */
|
||||
if (memcmp(algo16, bip340_algo16, 16) == 0) {
|
||||
secp256k1_nonce_function_bip340_sha256_tagged(&sha);
|
||||
} else {
|
||||
int algo16_len = 16;
|
||||
/* Remove terminating null bytes */
|
||||
while (algo16_len > 0 && !algo16[algo16_len - 1]) {
|
||||
algo16_len--;
|
||||
}
|
||||
secp256k1_sha256_initialize_tagged(&sha, algo16, algo16_len);
|
||||
}
|
||||
|
||||
/* Hash (masked-)key||pk||msg using the tagged hash as per the spec */
|
||||
if (data != NULL) {
|
||||
secp256k1_sha256_write(&sha, masked_key, 32);
|
||||
} else {
|
||||
secp256k1_sha256_write(&sha, key32, 32);
|
||||
}
|
||||
secp256k1_sha256_write(&sha, xonly_pk32, 32);
|
||||
secp256k1_sha256_write(&sha, msg32, 32);
|
||||
secp256k1_sha256_finalize(&sha, nonce32);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const secp256k1_nonce_function_hardened secp256k1_nonce_function_bip340 = nonce_function_bip340;
|
||||
|
||||
#endif
|
||||
|
|
|
@ -10,8 +10,96 @@
|
|||
|
||||
#include "secp256k1_schnorrsig.h"
|
||||
|
||||
/* Checks that a bit flip in the n_flip-th argument (that has n_bytes many
|
||||
* bytes) changes the hash function
|
||||
*/
|
||||
void nonce_function_bip340_bitflip(unsigned char **args, size_t n_flip, size_t n_bytes) {
|
||||
unsigned char nonces[2][32];
|
||||
CHECK(nonce_function_bip340(nonces[0], args[0], args[1], args[2], args[3], args[4]) == 1);
|
||||
secp256k1_rand_flip(args[n_flip], n_bytes);
|
||||
CHECK(nonce_function_bip340(nonces[1], args[0], args[1], args[2], args[3], args[4]) == 1);
|
||||
CHECK(memcmp(nonces[0], nonces[1], 32) != 0);
|
||||
}
|
||||
|
||||
/* Tests for the equality of two sha256 structs. This function only produces a
|
||||
* correct result if an integer multiple of 64 many bytes have been written
|
||||
* into the hash functions. */
|
||||
void test_sha256_eq(const secp256k1_sha256 *sha1, const secp256k1_sha256 *sha2) {
|
||||
/* Is buffer fully consumed? */
|
||||
CHECK((sha1->bytes & 0x3F) == 0);
|
||||
|
||||
CHECK(sha1->bytes == sha2->bytes);
|
||||
CHECK(memcmp(sha1->s, sha2->s, sizeof(sha1->s)) == 0);
|
||||
}
|
||||
|
||||
void run_nonce_function_bip340_tests(void) {
|
||||
unsigned char tag[13] = "BIP0340/nonce";
|
||||
unsigned char aux_tag[11] = "BIP0340/aux";
|
||||
unsigned char algo16[16] = "BIP0340/nonce\0\0\0";
|
||||
secp256k1_sha256 sha;
|
||||
secp256k1_sha256 sha_optimized;
|
||||
unsigned char nonce[32];
|
||||
unsigned char msg[32];
|
||||
unsigned char key[32];
|
||||
unsigned char pk[32];
|
||||
unsigned char aux_rand[32];
|
||||
unsigned char *args[5];
|
||||
int i;
|
||||
|
||||
/* Check that hash initialized by
|
||||
* secp256k1_nonce_function_bip340_sha256_tagged has the expected
|
||||
* state. */
|
||||
secp256k1_sha256_initialize_tagged(&sha, tag, sizeof(tag));
|
||||
secp256k1_nonce_function_bip340_sha256_tagged(&sha_optimized);
|
||||
test_sha256_eq(&sha, &sha_optimized);
|
||||
|
||||
/* Check that hash initialized by
|
||||
* secp256k1_nonce_function_bip340_sha256_tagged_aux has the expected
|
||||
* state. */
|
||||
secp256k1_sha256_initialize_tagged(&sha, aux_tag, sizeof(aux_tag));
|
||||
secp256k1_nonce_function_bip340_sha256_tagged_aux(&sha_optimized);
|
||||
test_sha256_eq(&sha, &sha_optimized);
|
||||
|
||||
secp256k1_rand256(msg);
|
||||
secp256k1_rand256(key);
|
||||
secp256k1_rand256(pk);
|
||||
secp256k1_rand256(aux_rand);
|
||||
|
||||
/* Check that a bitflip in an argument results in different nonces. */
|
||||
args[0] = msg;
|
||||
args[1] = key;
|
||||
args[2] = pk;
|
||||
args[3] = algo16;
|
||||
args[4] = aux_rand;
|
||||
for (i = 0; i < count; i++) {
|
||||
nonce_function_bip340_bitflip(args, 0, 32);
|
||||
nonce_function_bip340_bitflip(args, 1, 32);
|
||||
nonce_function_bip340_bitflip(args, 2, 32);
|
||||
/* Flip algo16 special case "BIP0340/nonce" */
|
||||
nonce_function_bip340_bitflip(args, 3, 16);
|
||||
/* Flip algo16 again */
|
||||
nonce_function_bip340_bitflip(args, 3, 16);
|
||||
nonce_function_bip340_bitflip(args, 4, 32);
|
||||
}
|
||||
|
||||
/* NULL algo16 is disallowed */
|
||||
CHECK(nonce_function_bip340(nonce, msg, key, pk, NULL, NULL) == 0);
|
||||
/* Empty algo16 is fine */
|
||||
memset(algo16, 0x00, 16);
|
||||
CHECK(nonce_function_bip340(nonce, msg, key, pk, algo16, NULL) == 1);
|
||||
/* algo16 with terminating null bytes is fine */
|
||||
algo16[1] = 65;
|
||||
CHECK(nonce_function_bip340(nonce, msg, key, pk, algo16, NULL) == 1);
|
||||
/* Other algo16 is fine */
|
||||
memset(algo16, 0xFF, 16);
|
||||
CHECK(nonce_function_bip340(nonce, msg, key, pk, algo16, NULL) == 1);
|
||||
|
||||
/* NULL aux_rand argument is allowed. */
|
||||
CHECK(nonce_function_bip340(nonce, msg, key, pk, algo16, NULL) == 1);
|
||||
}
|
||||
|
||||
void run_schnorrsig_tests(void) {
|
||||
/* TODO */
|
||||
run_nonce_function_bip340_tests();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -35,4 +35,7 @@ static void secp256k1_rand256_test(unsigned char *b32);
|
|||
/** Generate pseudorandom bytes with long sequences of zero and one bits. */
|
||||
static void secp256k1_rand_bytes_test(unsigned char *bytes, size_t len);
|
||||
|
||||
/** Flip a single random bit in a byte array */
|
||||
static void secp256k1_rand_flip(unsigned char *b, size_t len);
|
||||
|
||||
#endif /* SECP256K1_TESTRAND_H */
|
||||
|
|
|
@ -107,4 +107,8 @@ static void secp256k1_rand256_test(unsigned char *b32) {
|
|||
secp256k1_rand_bytes_test(b32, 32);
|
||||
}
|
||||
|
||||
static void secp256k1_rand_flip(unsigned char *b, size_t len) {
|
||||
b[secp256k1_rand_int(len)] ^= (1 << secp256k1_rand_int(8));
|
||||
}
|
||||
|
||||
#endif /* SECP256K1_TESTRAND_IMPL_H */
|
||||
|
|
Loading…
Reference in New Issue