Skip to content

Commit

Permalink
schnorrsig: Add BIP-340 nonce function
Browse files Browse the repository at this point in the history
  • Loading branch information
jonasnick committed Sep 6, 2020
1 parent 7a703fd commit 7332d2d
Show file tree
Hide file tree
Showing 5 changed files with 226 additions and 3 deletions.
50 changes: 49 additions & 1 deletion include/secp256k1_schnorrsig.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
82 changes: 81 additions & 1 deletion src/modules/schnorrsig/main_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
90 changes: 89 additions & 1 deletion src/modules/schnorrsig/tests_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
3 changes: 3 additions & 0 deletions src/testrand.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
4 changes: 4 additions & 0 deletions src/testrand_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */

0 comments on commit 7332d2d

Please sign in to comment.