From 2f40e1ec2ef89aac50b29eebace52cbf7af294ec Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Tue, 3 Apr 2018 22:06:07 +0000 Subject: [PATCH 1/7] add chacha20 function --- src/scalar.h | 3 ++ src/scalar_4x64_impl.h | 93 ++++++++++++++++++++++++++++++++++ src/scalar_8x32_impl.h | 100 +++++++++++++++++++++++++++++++++++++ src/scalar_low_impl.h | 5 ++ src/tests.c | 110 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 311 insertions(+) diff --git a/src/scalar.h b/src/scalar.h index 59304cb66e..640693a5e3 100644 --- a/src/scalar.h +++ b/src/scalar.h @@ -103,4 +103,7 @@ static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar /** Multiply a and b (without taking the modulus!), divide by 2**shift, and round to the nearest integer. Shift must be at least 256. */ static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b, unsigned int shift); +/** Generate two scalars from a 32-byte seed and an integer using the chacha20 stream cipher */ +static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2, const unsigned char *seed, uint64_t idx); + #endif /* SECP256K1_SCALAR_H */ diff --git a/src/scalar_4x64_impl.h b/src/scalar_4x64_impl.h index d378335d99..1000d9a962 100644 --- a/src/scalar_4x64_impl.h +++ b/src/scalar_4x64_impl.h @@ -7,6 +7,9 @@ #ifndef SECP256K1_SCALAR_REPR_IMPL_H #define SECP256K1_SCALAR_REPR_IMPL_H +#include "scalar.h" +#include + /* Limbs of the secp256k1 order. */ #define SECP256K1_N_0 ((uint64_t)0xBFD25E8CD0364141ULL) #define SECP256K1_N_1 ((uint64_t)0xBAAEDCE6AF48A03BULL) @@ -946,4 +949,94 @@ SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, secp256k1_scalar_cadd_bit(r, 0, (l[(shift - 1) >> 6] >> ((shift - 1) & 0x3f)) & 1); } +#define ROTL32(x,n) ((x) << (n) | (x) >> (32-(n))) +#define QUARTERROUND(a,b,c,d) \ + a += b; d = ROTL32(d ^ a, 16); \ + c += d; b = ROTL32(b ^ c, 12); \ + a += b; d = ROTL32(d ^ a, 8); \ + c += d; b = ROTL32(b ^ c, 7); + +#ifdef WORDS_BIGENDIAN +#define LE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) +#define BE32(p) (p) +#else +#define BE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) +#define LE32(p) (p) +#endif + +static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2, const unsigned char *seed, uint64_t idx) { + size_t n; + size_t over_count = 0; + uint32_t seed32[8]; + uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; + int over1, over2; + + memcpy((void *) seed32, (const void *) seed, 32); + do { + x0 = 0x61707865; + x1 = 0x3320646e; + x2 = 0x79622d32; + x3 = 0x6b206574; + x4 = LE32(seed32[0]); + x5 = LE32(seed32[1]); + x6 = LE32(seed32[2]); + x7 = LE32(seed32[3]); + x8 = LE32(seed32[4]); + x9 = LE32(seed32[5]); + x10 = LE32(seed32[6]); + x11 = LE32(seed32[7]); + x12 = idx; + x13 = idx >> 32; + x14 = 0; + x15 = over_count; + + n = 10; + while (n--) { + QUARTERROUND(x0, x4, x8,x12) + QUARTERROUND(x1, x5, x9,x13) + QUARTERROUND(x2, x6,x10,x14) + QUARTERROUND(x3, x7,x11,x15) + QUARTERROUND(x0, x5,x10,x15) + QUARTERROUND(x1, x6,x11,x12) + QUARTERROUND(x2, x7, x8,x13) + QUARTERROUND(x3, x4, x9,x14) + } + + x0 += 0x61707865; + x1 += 0x3320646e; + x2 += 0x79622d32; + x3 += 0x6b206574; + x4 += LE32(seed32[0]); + x5 += LE32(seed32[1]); + x6 += LE32(seed32[2]); + x7 += LE32(seed32[3]); + x8 += LE32(seed32[4]); + x9 += LE32(seed32[5]); + x10 += LE32(seed32[6]); + x11 += LE32(seed32[7]); + x12 += idx; + x13 += idx >> 32; + x14 += 0; + x15 += over_count; + + r1->d[3] = LE32((uint64_t) x0) << 32 | LE32(x1); + r1->d[2] = LE32((uint64_t) x2) << 32 | LE32(x3); + r1->d[1] = LE32((uint64_t) x4) << 32 | LE32(x5); + r1->d[0] = LE32((uint64_t) x6) << 32 | LE32(x7); + r2->d[3] = LE32((uint64_t) x8) << 32 | LE32(x9); + r2->d[2] = LE32((uint64_t) x10) << 32 | LE32(x11); + r2->d[1] = LE32((uint64_t) x12) << 32 | LE32(x13); + r2->d[0] = LE32((uint64_t) x14) << 32 | LE32(x15); + + over1 = secp256k1_scalar_check_overflow(r1); + over2 = secp256k1_scalar_check_overflow(r2); + over_count++; + } while (over1 | over2); +} + +#undef ROTL32 +#undef QUARTERROUND +#undef BE32 +#undef LE32 + #endif /* SECP256K1_SCALAR_REPR_IMPL_H */ diff --git a/src/scalar_8x32_impl.h b/src/scalar_8x32_impl.h index 4f9ed61fea..f99c741e40 100644 --- a/src/scalar_8x32_impl.h +++ b/src/scalar_8x32_impl.h @@ -7,6 +7,8 @@ #ifndef SECP256K1_SCALAR_REPR_IMPL_H #define SECP256K1_SCALAR_REPR_IMPL_H +#include + /* Limbs of the secp256k1 order. */ #define SECP256K1_N_0 ((uint32_t)0xD0364141UL) #define SECP256K1_N_1 ((uint32_t)0xBFD25E8CUL) @@ -718,4 +720,102 @@ SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, secp256k1_scalar_cadd_bit(r, 0, (l[(shift - 1) >> 5] >> ((shift - 1) & 0x1f)) & 1); } +#define ROTL32(x,n) ((x) << (n) | (x) >> (32-(n))) +#define QUARTERROUND(a,b,c,d) \ + a += b; d = ROTL32(d ^ a, 16); \ + c += d; b = ROTL32(b ^ c, 12); \ + a += b; d = ROTL32(d ^ a, 8); \ + c += d; b = ROTL32(b ^ c, 7); + +#ifdef WORDS_BIGENDIAN +#define LE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) +#define BE32(p) (p) +#else +#define BE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) +#define LE32(p) (p) +#endif + +static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2, const unsigned char *seed, uint64_t idx) { + size_t n; + size_t over_count = 0; + uint32_t seed32[8]; + uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; + int over1, over2; + + memcpy((void *) seed32, (const void *) seed, 32); + do { + x0 = 0x61707865; + x1 = 0x3320646e; + x2 = 0x79622d32; + x3 = 0x6b206574; + x4 = LE32(seed32[0]); + x5 = LE32(seed32[1]); + x6 = LE32(seed32[2]); + x7 = LE32(seed32[3]); + x8 = LE32(seed32[4]); + x9 = LE32(seed32[5]); + x10 = LE32(seed32[6]); + x11 = LE32(seed32[7]); + x12 = idx; + x13 = idx >> 32; + x14 = 0; + x15 = over_count; + + n = 10; + while (n--) { + QUARTERROUND(x0, x4, x8,x12) + QUARTERROUND(x1, x5, x9,x13) + QUARTERROUND(x2, x6,x10,x14) + QUARTERROUND(x3, x7,x11,x15) + QUARTERROUND(x0, x5,x10,x15) + QUARTERROUND(x1, x6,x11,x12) + QUARTERROUND(x2, x7, x8,x13) + QUARTERROUND(x3, x4, x9,x14) + } + + x0 += 0x61707865; + x1 += 0x3320646e; + x2 += 0x79622d32; + x3 += 0x6b206574; + x4 += LE32(seed32[0]); + x5 += LE32(seed32[1]); + x6 += LE32(seed32[2]); + x7 += LE32(seed32[3]); + x8 += LE32(seed32[4]); + x9 += LE32(seed32[5]); + x10 += LE32(seed32[6]); + x11 += LE32(seed32[7]); + x12 += idx; + x13 += idx >> 32; + x14 += 0; + x15 += over_count; + + r1->d[7] = LE32(x0); + r1->d[6] = LE32(x1); + r1->d[5] = LE32(x2); + r1->d[4] = LE32(x3); + r1->d[3] = LE32(x4); + r1->d[2] = LE32(x5); + r1->d[1] = LE32(x6); + r1->d[0] = LE32(x7); + r2->d[7] = LE32(x8); + r2->d[6] = LE32(x9); + r2->d[5] = LE32(x10); + r2->d[4] = LE32(x11); + r2->d[3] = LE32(x12); + r2->d[2] = LE32(x13); + r2->d[1] = LE32(x14); + r2->d[0] = LE32(x15); + + over1 = secp256k1_scalar_check_overflow(r1); + over2 = secp256k1_scalar_check_overflow(r2); + over_count++; + } while (over1 | over2); +} + +#undef ROTL32 +#undef QUARTERROUND +#undef BE32 +#undef LE32 + #endif /* SECP256K1_SCALAR_REPR_IMPL_H */ diff --git a/src/scalar_low_impl.h b/src/scalar_low_impl.h index c80e70c5a2..88e52d8031 100644 --- a/src/scalar_low_impl.h +++ b/src/scalar_low_impl.h @@ -111,4 +111,9 @@ SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const return *a == *b; } +SECP256K1_INLINE static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2, const unsigned char *seed, uint64_t n) { + *r1 = (seed[0] + n) % EXHAUSTIVE_TEST_ORDER; + *r2 = (seed[1] + n) % EXHAUSTIVE_TEST_ORDER; +} + #endif /* SECP256K1_SCALAR_REPR_IMPL_H */ diff --git a/src/tests.c b/src/tests.c index 132df9ba9c..c9ff3e947f 100644 --- a/src/tests.c +++ b/src/tests.c @@ -1077,12 +1077,122 @@ void scalar_test(void) { } +void scalar_chacha_tests(void) { + /* Test vectors 1 to 4 from https://tools.ietf.org/html/rfc8439#appendix-A + * Note that scalar_set_b32 and scalar_get_b32 represent integers + * underlying the scalar in big-endian format. */ + unsigned char expected1[64] = { + 0xad, 0xe0, 0xb8, 0x76, 0x90, 0x3d, 0xf1, 0xa0, + 0xe5, 0x6a, 0x5d, 0x40, 0x28, 0xbd, 0x86, 0x53, + 0xb8, 0x19, 0xd2, 0xbd, 0x1a, 0xed, 0x8d, 0xa0, + 0xcc, 0xef, 0x36, 0xa8, 0xc7, 0x0d, 0x77, 0x8b, + 0x7c, 0x59, 0x41, 0xda, 0x8d, 0x48, 0x57, 0x51, + 0x3f, 0xe0, 0x24, 0x77, 0x37, 0x4a, 0xd8, 0xb8, + 0xf4, 0xb8, 0x43, 0x6a, 0x1c, 0xa1, 0x18, 0x15, + 0x69, 0xb6, 0x87, 0xc3, 0x86, 0x65, 0xee, 0xb2 + }; + unsigned char expected2[64] = { + 0xbe, 0xe7, 0x07, 0x9f, 0x7a, 0x38, 0x51, 0x55, + 0x7c, 0x97, 0xba, 0x98, 0x0d, 0x08, 0x2d, 0x73, + 0xa0, 0x29, 0x0f, 0xcb, 0x69, 0x65, 0xe3, 0x48, + 0x3e, 0x53, 0xc6, 0x12, 0xed, 0x7a, 0xee, 0x32, + 0x76, 0x21, 0xb7, 0x29, 0x43, 0x4e, 0xe6, 0x9c, + 0xb0, 0x33, 0x71, 0xd5, 0xd5, 0x39, 0xd8, 0x74, + 0x28, 0x1f, 0xed, 0x31, 0x45, 0xfb, 0x0a, 0x51, + 0x1f, 0x0a, 0xe1, 0xac, 0x6f, 0x4d, 0x79, 0x4b + }; + unsigned char seed3[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 + }; + unsigned char expected3[64] = { + 0x24, 0x52, 0xeb, 0x3a, 0x92, 0x49, 0xf8, 0xec, + 0x8d, 0x82, 0x9d, 0x9b, 0xdd, 0xd4, 0xce, 0xb1, + 0xe8, 0x25, 0x20, 0x83, 0x60, 0x81, 0x8b, 0x01, + 0xf3, 0x84, 0x22, 0xb8, 0x5a, 0xaa, 0x49, 0xc9, + 0xbb, 0x00, 0xca, 0x8e, 0xda, 0x3b, 0xa7, 0xb4, + 0xc4, 0xb5, 0x92, 0xd1, 0xfd, 0xf2, 0x73, 0x2f, + 0x44, 0x36, 0x27, 0x4e, 0x25, 0x61, 0xb3, 0xc8, + 0xeb, 0xdd, 0x4a, 0xa6, 0xa0, 0x13, 0x6c, 0x00 + }; + unsigned char seed4[32] = { + 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + unsigned char expected4[64] = { + 0xfb, 0x4d, 0xd5, 0x72, 0x4b, 0xc4, 0x2e, 0xf1, + 0xdf, 0x92, 0x26, 0x36, 0x32, 0x7f, 0x13, 0x94, + 0xa7, 0x8d, 0xea, 0x8f, 0x5e, 0x26, 0x90, 0x39, + 0xa1, 0xbe, 0xbb, 0xc1, 0xca, 0xf0, 0x9a, 0xae, + 0xa2, 0x5a, 0xb2, 0x13, 0x48, 0xa6, 0xb4, 0x6c, + 0x1b, 0x9d, 0x9b, 0xcb, 0x09, 0x2c, 0x5b, 0xe6, + 0x54, 0x6c, 0xa6, 0x24, 0x1b, 0xec, 0x45, 0xd5, + 0x87, 0xf4, 0x74, 0x73, 0x96, 0xf0, 0x99, 0x2e + }; + unsigned char seed5[32] = { + 0x32, 0x56, 0x56, 0xf4, 0x29, 0x02, 0xc2, 0xf8, + 0xa3, 0x4b, 0x96, 0xf5, 0xa7, 0xf7, 0xe3, 0x6c, + 0x92, 0xad, 0xa5, 0x18, 0x1c, 0xe3, 0x41, 0xae, + 0xc3, 0xf3, 0x18, 0xd0, 0xfa, 0x5b, 0x72, 0x53 + }; + unsigned char expected5[64] = { + 0xe7, 0x56, 0xd3, 0x28, 0xe9, 0xc6, 0x19, 0x5c, + 0x6f, 0x17, 0x8e, 0x21, 0x8c, 0x1e, 0x72, 0x11, + 0xe7, 0xbd, 0x17, 0x0d, 0xac, 0x14, 0xad, 0xe9, + 0x3d, 0x9f, 0xb6, 0x92, 0xd6, 0x09, 0x20, 0xfb, + 0x43, 0x8e, 0x3b, 0x6d, 0xe3, 0x33, 0xdc, 0xc7, + 0x6c, 0x07, 0x6f, 0xbb, 0x1f, 0xb4, 0xc8, 0xb5, + 0xe3, 0x6c, 0xe5, 0x12, 0xd9, 0xd7, 0x64, 0x0c, + 0xf5, 0xa7, 0x0d, 0xab, 0x79, 0x03, 0xf1, 0x81 + }; + + secp256k1_scalar exp_r1, exp_r2; + secp256k1_scalar r1, r2; + unsigned char seed0[32] = { 0 }; + + secp256k1_scalar_chacha20(&r1, &r2, seed0, 0); + secp256k1_scalar_set_b32(&exp_r1, &expected1[0], NULL); + secp256k1_scalar_set_b32(&exp_r2, &expected1[32], NULL); + CHECK(secp256k1_scalar_eq(&exp_r1, &r1)); + CHECK(secp256k1_scalar_eq(&exp_r2, &r2)); + + secp256k1_scalar_chacha20(&r1, &r2, seed0, 1); + secp256k1_scalar_set_b32(&exp_r1, &expected2[0], NULL); + secp256k1_scalar_set_b32(&exp_r2, &expected2[32], NULL); + CHECK(secp256k1_scalar_eq(&exp_r1, &r1)); + CHECK(secp256k1_scalar_eq(&exp_r2, &r2)); + + secp256k1_scalar_chacha20(&r1, &r2, seed3, 1); + secp256k1_scalar_set_b32(&exp_r1, &expected3[0], NULL); + secp256k1_scalar_set_b32(&exp_r2, &expected3[32], NULL); + CHECK(secp256k1_scalar_eq(&exp_r1, &r1)); + CHECK(secp256k1_scalar_eq(&exp_r2, &r2)); + + secp256k1_scalar_chacha20(&r1, &r2, seed4, 2); + secp256k1_scalar_set_b32(&exp_r1, &expected4[0], NULL); + secp256k1_scalar_set_b32(&exp_r2, &expected4[32], NULL); + CHECK(secp256k1_scalar_eq(&exp_r1, &r1)); + CHECK(secp256k1_scalar_eq(&exp_r2, &r2)); + + secp256k1_scalar_chacha20(&r1, &r2, seed5, 0x6ff8602a7a78e2f2ULL); + secp256k1_scalar_set_b32(&exp_r1, &expected5[0], NULL); + secp256k1_scalar_set_b32(&exp_r2, &expected5[32], NULL); + CHECK(secp256k1_scalar_eq(&exp_r1, &r1)); + CHECK(secp256k1_scalar_eq(&exp_r2, &r2)); +} + void run_scalar_tests(void) { int i; for (i = 0; i < 128 * count; i++) { scalar_test(); } + scalar_chacha_tests(); + { /* (-1)+1 should be zero. */ secp256k1_scalar s, o; From 82645729cc717aaf224d130e8bcc25865d622e87 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Wed, 9 May 2018 15:37:35 +0000 Subject: [PATCH 2/7] Add schnorrsig module which implements BIP-schnorr [0] compatible signing, verification and batch verification. [0] https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki --- .gitignore | 2 +- .travis.yml | 12 +- Makefile.am | 4 + configure.ac | 15 + include/secp256k1.h | 6 + include/secp256k1_schnorrsig.h | 126 ++++ src/bench_schnorrsig.c | 128 ++++ src/modules/schnorrsig/Makefile.am.include | 8 + src/modules/schnorrsig/main_impl.h | 332 ++++++++++ src/modules/schnorrsig/tests_impl.h | 721 +++++++++++++++++++++ src/scalar_4x64_impl.h | 19 +- src/scalar_8x32_impl.h | 35 +- src/secp256k1.c | 28 + src/tests.c | 9 + 14 files changed, 1408 insertions(+), 37 deletions(-) create mode 100644 include/secp256k1_schnorrsig.h create mode 100644 src/bench_schnorrsig.c create mode 100644 src/modules/schnorrsig/Makefile.am.include create mode 100644 src/modules/schnorrsig/main_impl.h create mode 100644 src/modules/schnorrsig/tests_impl.h diff --git a/.gitignore b/.gitignore index 55d325aeef..905be98730 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,9 @@ bench_inv bench_ecdh bench_ecmult +bench_schnorrsig bench_sign bench_verify -bench_schnorr_verify bench_recover bench_internal tests diff --git a/.travis.yml b/.travis.yml index 74f658f4d1..9e9fd6f214 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,20 +11,20 @@ cache: - src/java/guava/ env: global: - - FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no RECOVERY=no EXPERIMENTAL=no JNI=no + - FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no RECOVERY=no EXPERIMENTAL=no JNI=no SCHNORRSIG=no - GUAVA_URL=https://search.maven.org/remotecontent?filepath=com/google/guava/guava/18.0/guava-18.0.jar GUAVA_JAR=src/java/guava/guava-18.0.jar matrix: - SCALAR=32bit RECOVERY=yes - - SCALAR=32bit FIELD=32bit ECDH=yes EXPERIMENTAL=yes + - SCALAR=32bit FIELD=32bit ECDH=yes EXPERIMENTAL=yes SCHNORRSIG=yes - SCALAR=64bit - - FIELD=64bit RECOVERY=yes + - FIELD=64bit RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes - FIELD=64bit ENDOMORPHISM=yes - - FIELD=64bit ENDOMORPHISM=yes ECDH=yes EXPERIMENTAL=yes + - FIELD=64bit ENDOMORPHISM=yes ECDH=yes EXPERIMENTAL=yes SCHNORRSIG=yes - FIELD=64bit ASM=x86_64 - FIELD=64bit ENDOMORPHISM=yes ASM=x86_64 - FIELD=32bit ENDOMORPHISM=yes - BIGNUM=no - - BIGNUM=no ENDOMORPHISM=yes RECOVERY=yes EXPERIMENTAL=yes + - BIGNUM=no ENDOMORPHISM=yes RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes - BIGNUM=no STATICPRECOMPUTATION=no - BUILD=distcheck - EXTRAFLAGS=CPPFLAGS=-DDETERMINISTIC @@ -65,4 +65,4 @@ before_script: ./autogen.sh script: - if [ -n "$HOST" ]; then export USE_HOST="--host=$HOST"; fi - if [ "x$HOST" = "xi686-linux-gnu" ]; then export CC="$CC -m32"; fi - - ./configure --enable-experimental=$EXPERIMENTAL --enable-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-scalar=$SCALAR --enable-ecmult-static-precomputation=$STATICPRECOMPUTATION --enable-module-ecdh=$ECDH --enable-module-recovery=$RECOVERY --enable-jni=$JNI $EXTRAFLAGS $USE_HOST && make -j2 $BUILD + - ./configure --enable-experimental=$EXPERIMENTAL --enable-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-scalar=$SCALAR --enable-ecmult-static-precomputation=$STATICPRECOMPUTATION --enable-module-ecdh=$ECDH --enable-module-recovery=$RECOVERY --enable-module-schnorrsig=$SCHNORRSIG --enable-jni=$JNI $EXTRAFLAGS $USE_HOST && make -j2 $BUILD diff --git a/Makefile.am b/Makefile.am index 21df09f41f..1d985c17e1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -178,6 +178,10 @@ if ENABLE_MODULE_ECDH include src/modules/ecdh/Makefile.am.include endif +if ENABLE_MODULE_SCHNORRSIG +include src/modules/schnorrsig/Makefile.am.include +endif + if ENABLE_MODULE_RECOVERY include src/modules/recovery/Makefile.am.include endif diff --git a/configure.ac b/configure.ac index b8340b7de1..3c21169e3f 100644 --- a/configure.ac +++ b/configure.ac @@ -129,6 +129,11 @@ AC_ARG_ENABLE(module_ecdh, [enable_module_ecdh=$enableval], [enable_module_ecdh=no]) +AC_ARG_ENABLE(module_schnorrsig, + AS_HELP_STRING([--enable-module-schnorrsig],[enable schnorrsig module (experimental)]), + [enable_module_schnorrsig=$enableval], + [enable_module_schnorrsig=no]) + AC_ARG_ENABLE(module_recovery, AS_HELP_STRING([--enable-module-recovery],[enable ECDSA pubkey recovery module [default=no]]), [enable_module_recovery=$enableval], @@ -488,6 +493,10 @@ if test x"$enable_module_ecdh" = x"yes"; then AC_DEFINE(ENABLE_MODULE_ECDH, 1, [Define this symbol to enable the ECDH module]) fi +if test x"$enable_module_schnorrsig" = x"yes"; then + AC_DEFINE(ENABLE_MODULE_SCHNORRSIG, 1, [Define this symbol to enable the schnorrsig module]) +fi + if test x"$enable_module_recovery" = x"yes"; then AC_DEFINE(ENABLE_MODULE_RECOVERY, 1, [Define this symbol to enable the ECDSA pubkey recovery module]) fi @@ -507,11 +516,15 @@ if test x"$enable_experimental" = x"yes"; then AC_MSG_NOTICE([WARNING: experimental build]) AC_MSG_NOTICE([Experimental features do not have stable APIs or properties, and may not be safe for production use.]) AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh]) + AC_MSG_NOTICE([Building schnorrsig module: $enable_module_schnorrsig]) AC_MSG_NOTICE([******]) else if test x"$enable_module_ecdh" = x"yes"; then AC_MSG_ERROR([ECDH module is experimental. Use --enable-experimental to allow.]) fi + if test x"$enable_module_schnorrsig" = x"yes"; then + AC_MSG_ERROR([schnorrsig module is experimental. Use --enable-experimental to allow.]) + fi if test x"$set_asm" = x"arm"; then AC_MSG_ERROR([ARM assembly optimization is experimental. Use --enable-experimental to allow.]) fi @@ -530,6 +543,7 @@ AM_CONDITIONAL([USE_EXHAUSTIVE_TESTS], [test x"$use_exhaustive_tests" != x"no"]) AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"]) AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$set_precomp" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"]) +AM_CONDITIONAL([ENABLE_MODULE_SCHNORRSIG], [test x"$enable_module_schnorrsig" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"]) AM_CONDITIONAL([USE_JNI], [test x"$use_jni" = x"yes"]) AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"]) @@ -552,6 +566,7 @@ echo " with benchmarks = $use_benchmark" echo " with coverage = $enable_coverage" echo " module ecdh = $enable_module_ecdh" echo " module recovery = $enable_module_recovery" +echo " module schnorrsig = $enable_module_schnorrsig" echo echo " asm = $set_asm" echo " bignum = $set_bignum" diff --git a/include/secp256k1.h b/include/secp256k1.h index 3e90b1bc7b..5988b14193 100644 --- a/include/secp256k1.h +++ b/include/secp256k1.h @@ -523,6 +523,12 @@ SECP256K1_API int secp256k1_ecdsa_signature_normalize( */ SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_rfc6979; +/** An implementation of the nonce generation function as defined in BIP-schnorr. + * If a data pointer is passed, it is assumed to be a pointer to 32 bytes of + * extra entropy. + */ +SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_bipschnorr; + /** A default safe nonce generation function (currently equal to secp256k1_nonce_function_rfc6979). */ SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_default; diff --git a/include/secp256k1_schnorrsig.h b/include/secp256k1_schnorrsig.h new file mode 100644 index 0000000000..77a9f0bc95 --- /dev/null +++ b/include/secp256k1_schnorrsig.h @@ -0,0 +1,126 @@ +#ifndef SECP256K1_SCHNORRSIG_H +#define SECP256K1_SCHNORRSIG_H + +#include "secp256k1.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** This module implements a variant of Schnorr signatures compliant with + * BIP-schnorr + * (https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki). + */ + +/** Opaque data structure that holds a parsed Schnorr signature. + * + * The exact representation of data inside is implementation defined and not + * guaranteed to be portable between different platforms or versions. It is + * however guaranteed to be 64 bytes in size, and can be safely copied/moved. + * If you need to convert to a format suitable for storage, transmission, or + * comparison, use the `secp256k1_schnorrsig_serialize` and + * `secp256k1_schnorrsig_parse` functions. + */ +typedef struct { + unsigned char data[64]; +} secp256k1_schnorrsig; + +/** Serialize a Schnorr signature. + * + * Returns: 1 + * Args: ctx: a secp256k1 context object + * Out: out64: pointer to a 64-byte array to store the serialized signature + * In: sig: pointer to the signature + * + * See secp256k1_schnorrsig_parse for details about the encoding. + */ +SECP256K1_API int secp256k1_schnorrsig_serialize( + const secp256k1_context* ctx, + unsigned char *out64, + const secp256k1_schnorrsig* sig +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Parse a Schnorr signature. + * + * Returns: 1 when the signature could be parsed, 0 otherwise. + * Args: ctx: a secp256k1 context object + * Out: sig: pointer to a signature object + * In: in64: pointer to the 64-byte signature to be parsed + * + * The signature is serialized in the form R||s, where R is a 32-byte public + * key (x-coordinate only; the y-coordinate is considered to be the unique + * y-coordinate satisfying the curve equation that is a quadratic residue) + * and s is a 32-byte big-endian scalar. + * + * After the call, sig will always be initialized. If parsing failed or the + * encoded numbers are out of range, signature validation with it is + * guaranteed to fail for every message and public key. + */ +SECP256K1_API int secp256k1_schnorrsig_parse( + const secp256k1_context* ctx, + secp256k1_schnorrsig* sig, + const unsigned char *in64 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Create a Schnorr signature. + * + * Returns 1 on success, 0 on failure. + * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL) + * Out: sig: pointer to the returned signature (cannot be NULL) + * In: msg32: the 32-byte message being signed (cannot be NULL) + * seckey: pointer to a 32-byte secret key (cannot be NULL) + * noncefp: pointer to a nonce generation function. If NULL, secp256k1_nonce_function_bipschnorr is used + * ndata: pointer to arbitrary data used by the nonce generation function (can be NULL) + */ +SECP256K1_API int secp256k1_schnorrsig_sign( + const secp256k1_context* ctx, + secp256k1_schnorrsig *sig, + const unsigned char *msg32, + const unsigned char *seckey, + secp256k1_nonce_function noncefp, + void *ndata +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Verify a Schnorr signature. + * + * Returns: 1: correct signature + * 0: incorrect or unparseable signature + * Args: ctx: a secp256k1 context object, initialized for verification. + * In: sig: the signature being verified (cannot be NULL) + * msg32: the 32-byte message being verified (cannot be NULL) + * pubkey: pointer to a public key to verify with (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_verify( + const secp256k1_context* ctx, + const secp256k1_schnorrsig *sig, + const unsigned char *msg32, + const secp256k1_pubkey *pubkey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Verifies a set of Schnorr signatures. + * + * Returns 1 if all succeeded, 0 otherwise. In particular, returns 1 if n_sigs is 0. + * + * Args: ctx: a secp256k1 context object, initialized for verification. + * scratch: scratch space used for the multiexponentiation + * In: sig: array of signatures, or NULL if there are no signatures + * msg32: array of messages, or NULL if there are no signatures + * pk: array of public keys, or NULL if there are no signatures + * n_sigs: number of signatures in above arrays. Must be smaller than + * 2^31 and smaller than half the maximum size_t value. Must be 0 + * if above arrays are NULL. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_verify_batch( + const secp256k1_context* ctx, + secp256k1_scratch_space *scratch, + const secp256k1_schnorrsig *const *sig, + const unsigned char *const *msg32, + const secp256k1_pubkey *const *pk, + size_t n_sigs +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); + +#ifdef __cplusplus +} +#endif + +#endif /* SECP256K1_SCHNORRSIG_H */ diff --git a/src/bench_schnorrsig.c b/src/bench_schnorrsig.c new file mode 100644 index 0000000000..b68adf5312 --- /dev/null +++ b/src/bench_schnorrsig.c @@ -0,0 +1,128 @@ +/********************************************************************** + * Copyright (c) 2018 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include +#include + +#include "include/secp256k1.h" +#include "include/secp256k1_schnorrsig.h" +#include "util.h" +#include "bench.h" + +#define MAX_SIGS (32768) + +typedef struct { + secp256k1_context *ctx; + secp256k1_scratch_space *scratch; + size_t n; + const unsigned char **pk; + const secp256k1_schnorrsig **sigs; + const unsigned char **msgs; +} bench_schnorrsig_data; + +void bench_schnorrsig_sign(void* arg) { + bench_schnorrsig_data *data = (bench_schnorrsig_data *)arg; + size_t i; + unsigned char sk[32] = "benchmarkexample secrettemplate"; + unsigned char msg[32] = "benchmarkexamplemessagetemplate"; + secp256k1_schnorrsig sig; + + for (i = 0; i < 1000; i++) { + msg[0] = i; + msg[1] = i >> 8; + sk[0] = i; + sk[1] = i >> 8; + CHECK(secp256k1_schnorrsig_sign(data->ctx, &sig, msg, sk, NULL, NULL)); + } +} + +void bench_schnorrsig_verify(void* arg) { + bench_schnorrsig_data *data = (bench_schnorrsig_data *)arg; + size_t i; + + for (i = 0; i < 1000; i++) { + secp256k1_pubkey pk; + CHECK(secp256k1_ec_pubkey_parse(data->ctx, &pk, data->pk[i], 33) == 1); + CHECK(secp256k1_schnorrsig_verify(data->ctx, data->sigs[i], data->msgs[i], &pk)); + } +} + +void bench_schnorrsig_verify_n(void* arg) { + bench_schnorrsig_data *data = (bench_schnorrsig_data *)arg; + size_t i, j; + const secp256k1_pubkey **pk = (const secp256k1_pubkey **)malloc(data->n * sizeof(*pk)); + + CHECK(pk != NULL); + for (j = 0; j < MAX_SIGS/data->n; j++) { + for (i = 0; i < data->n; i++) { + secp256k1_pubkey *pk_nonconst = (secp256k1_pubkey *)malloc(sizeof(*pk_nonconst)); + CHECK(secp256k1_ec_pubkey_parse(data->ctx, pk_nonconst, data->pk[i], 33) == 1); + pk[i] = pk_nonconst; + } + CHECK(secp256k1_schnorrsig_verify_batch(data->ctx, data->scratch, data->sigs, data->msgs, pk, data->n)); + for (i = 0; i < data->n; i++) { + free((void *)pk[i]); + } + } + free(pk); +} + +int main(void) { + size_t i; + bench_schnorrsig_data data; + + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN); + data.scratch = secp256k1_scratch_space_create(data.ctx, 1024 * 1024 * 1024); + data.pk = (const unsigned char **)malloc(MAX_SIGS * sizeof(unsigned char *)); + data.msgs = (const unsigned char **)malloc(MAX_SIGS * sizeof(unsigned char *)); + data.sigs = (const secp256k1_schnorrsig **)malloc(MAX_SIGS * sizeof(secp256k1_schnorrsig *)); + + for (i = 0; i < MAX_SIGS; i++) { + unsigned char sk[32]; + unsigned char *msg = (unsigned char *)malloc(32); + secp256k1_schnorrsig *sig = (secp256k1_schnorrsig *)malloc(sizeof(*sig)); + unsigned char *pk_char = (unsigned char *)malloc(33); + secp256k1_pubkey pk; + size_t pk_len = 33; + msg[0] = sk[0] = i; + msg[1] = sk[1] = i >> 8; + msg[2] = sk[2] = i >> 16; + msg[3] = sk[3] = i >> 24; + memset(&msg[4], 'm', 28); + memset(&sk[4], 's', 28); + + data.pk[i] = pk_char; + data.msgs[i] = msg; + data.sigs[i] = sig; + + CHECK(secp256k1_ec_pubkey_create(data.ctx, &pk, sk)); + CHECK(secp256k1_ec_pubkey_serialize(data.ctx, pk_char, &pk_len, &pk, SECP256K1_EC_COMPRESSED) == 1); + CHECK(secp256k1_schnorrsig_sign(data.ctx, sig, msg, sk, NULL, NULL)); + } + + run_benchmark("schnorrsig_sign", bench_schnorrsig_sign, NULL, NULL, (void *) &data, 10, 1000); + run_benchmark("schnorrsig_verify", bench_schnorrsig_verify, NULL, NULL, (void *) &data, 10, 1000); + for (i = 1; i <= MAX_SIGS; i *= 2) { + char name[64]; + sprintf(name, "schnorrsig_batch_verify_%d", (int) i); + + data.n = i; + run_benchmark(name, bench_schnorrsig_verify_n, NULL, NULL, (void *) &data, 3, MAX_SIGS); + } + + for (i = 0; i < MAX_SIGS; i++) { + free((void *)data.pk[i]); + free((void *)data.msgs[i]); + free((void *)data.sigs[i]); + } + free(data.pk); + free(data.msgs); + free(data.sigs); + + secp256k1_scratch_space_destroy(data.ctx, data.scratch); + secp256k1_context_destroy(data.ctx); + return 0; +} diff --git a/src/modules/schnorrsig/Makefile.am.include b/src/modules/schnorrsig/Makefile.am.include new file mode 100644 index 0000000000..a82bafe43f --- /dev/null +++ b/src/modules/schnorrsig/Makefile.am.include @@ -0,0 +1,8 @@ +include_HEADERS += include/secp256k1_schnorrsig.h +noinst_HEADERS += src/modules/schnorrsig/main_impl.h +noinst_HEADERS += src/modules/schnorrsig/tests_impl.h +if USE_BENCHMARK +noinst_PROGRAMS += bench_schnorrsig +bench_schnorrsig_SOURCES = src/bench_schnorrsig.c +bench_schnorrsig_LDADD = libsecp256k1.la $(SECP_LIBS) $(COMMON_LIB) +endif diff --git a/src/modules/schnorrsig/main_impl.h b/src/modules/schnorrsig/main_impl.h new file mode 100644 index 0000000000..739e98b0c3 --- /dev/null +++ b/src/modules/schnorrsig/main_impl.h @@ -0,0 +1,332 @@ +/********************************************************************** + * Copyright (c) 2018 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_MODULE_SCHNORRSIG_MAIN_ +#define _SECP256K1_MODULE_SCHNORRSIG_MAIN_ + +#include "include/secp256k1.h" +#include "include/secp256k1_schnorrsig.h" +#include "hash.h" + +int secp256k1_schnorrsig_serialize(const secp256k1_context* ctx, unsigned char *out64, const secp256k1_schnorrsig* sig) { + (void) ctx; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(out64 != NULL); + ARG_CHECK(sig != NULL); + memcpy(out64, sig->data, 64); + return 1; +} + +int secp256k1_schnorrsig_parse(const secp256k1_context* ctx, secp256k1_schnorrsig* sig, const unsigned char *in64) { + (void) ctx; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(in64 != NULL); + memcpy(sig->data, in64, 64); + return 1; +} + +int secp256k1_schnorrsig_sign(const secp256k1_context* ctx, secp256k1_schnorrsig *sig, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, void *ndata) { + secp256k1_scalar x; + secp256k1_scalar e; + secp256k1_scalar k; + secp256k1_gej pkj; + secp256k1_gej rj; + secp256k1_ge pk; + secp256k1_ge r; + secp256k1_sha256 sha; + int overflow; + unsigned char buf[33]; + size_t buflen = sizeof(buf); + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(sig != NULL); + ARG_CHECK(msg32 != NULL); + ARG_CHECK(seckey != NULL); + + if (noncefp == NULL) { + noncefp = secp256k1_nonce_function_bipschnorr; + } + secp256k1_scalar_set_b32(&x, seckey, &overflow); + /* Fail if the secret key is invalid. */ + if (overflow || secp256k1_scalar_is_zero(&x)) { + memset(sig, 0, sizeof(*sig)); + return 0; + } + + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pkj, &x); + secp256k1_ge_set_gej(&pk, &pkj); + + if (!noncefp(buf, msg32, seckey, NULL, (void*)ndata, 0)) { + return 0; + } + secp256k1_scalar_set_b32(&k, buf, NULL); + if (secp256k1_scalar_is_zero(&k)) { + return 0; + } + + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &k); + secp256k1_ge_set_gej(&r, &rj); + + if (!secp256k1_fe_is_quad_var(&r.y)) { + secp256k1_scalar_negate(&k, &k); + } + secp256k1_fe_normalize(&r.x); + secp256k1_fe_get_b32(&sig->data[0], &r.x); + + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, &sig->data[0], 32); + secp256k1_eckey_pubkey_serialize(&pk, buf, &buflen, 1); + secp256k1_sha256_write(&sha, buf, buflen); + secp256k1_sha256_write(&sha, msg32, 32); + secp256k1_sha256_finalize(&sha, buf); + + secp256k1_scalar_set_b32(&e, buf, NULL); + secp256k1_scalar_mul(&e, &e, &x); + secp256k1_scalar_add(&e, &e, &k); + + secp256k1_scalar_get_b32(&sig->data[32], &e); + secp256k1_scalar_clear(&k); + secp256k1_scalar_clear(&x); + + return 1; +} + +/* Helper function for verification and batch verification. + * Computes R = sG - eP. */ +static int secp256k1_schnorrsig_real_verify(const secp256k1_context* ctx, secp256k1_gej *rj, const secp256k1_scalar *s, const secp256k1_scalar *e, const secp256k1_pubkey *pk) { + secp256k1_scalar nege; + secp256k1_ge pkp; + secp256k1_gej pkj; + + secp256k1_scalar_negate(&nege, e); + + if (!secp256k1_pubkey_load(ctx, &pkp, pk)) { + return 0; + } + secp256k1_gej_set_ge(&pkj, &pkp); + + /* rj = s*G + (-e)*pkj */ + secp256k1_ecmult(&ctx->ecmult_ctx, rj, &pkj, &nege, s); + return 1; +} + +int secp256k1_schnorrsig_verify(const secp256k1_context* ctx, const secp256k1_schnorrsig *sig, const unsigned char *msg32, const secp256k1_pubkey *pk) { + secp256k1_scalar s; + secp256k1_scalar e; + secp256k1_gej rj; + secp256k1_fe rx; + secp256k1_sha256 sha; + unsigned char buf[33]; + size_t buflen = sizeof(buf); + int overflow; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(sig != NULL); + ARG_CHECK(msg32 != NULL); + ARG_CHECK(pk != NULL); + + if (!secp256k1_fe_set_b32(&rx, &sig->data[0])) { + return 0; + } + + secp256k1_scalar_set_b32(&s, &sig->data[32], &overflow); + if (overflow) { + return 0; + } + + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, &sig->data[0], 32); + secp256k1_ec_pubkey_serialize(ctx, buf, &buflen, pk, SECP256K1_EC_COMPRESSED); + secp256k1_sha256_write(&sha, buf, buflen); + secp256k1_sha256_write(&sha, msg32, 32); + secp256k1_sha256_finalize(&sha, buf); + secp256k1_scalar_set_b32(&e, buf, NULL); + + if (!secp256k1_schnorrsig_real_verify(ctx, &rj, &s, &e, pk) + || !secp256k1_gej_has_quad_y_var(&rj) /* fails if rj is infinity */ + || !secp256k1_gej_eq_x_var(&rx, &rj)) { + return 0; + } + + return 1; +} + +/* Data that is used by the batch verification ecmult callback */ +typedef struct { + const secp256k1_context *ctx; + /* Seed for the random number generator */ + unsigned char chacha_seed[32]; + /* Caches randomizers generated by the PRNG which returns two randomizers per call. Caching + * avoids having to call the PRNG twice as often. The very first randomizer will be set to 1 and + * the PRNG is called at every odd indexed schnorrsig to fill the cache. */ + secp256k1_scalar randomizer_cache[2]; + /* Signature, message, public key tuples to verify */ + const secp256k1_schnorrsig *const *sig; + const unsigned char *const *msg32; + const secp256k1_pubkey *const *pk; + size_t n_sigs; +} secp256k1_schnorrsig_verify_ecmult_context; + +/* Callback function which is called by ecmult_multi in order to convert the ecmult_context + * consisting of signature, message and public key tuples into scalars and points. */ +static int secp256k1_schnorrsig_verify_batch_ecmult_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) { + secp256k1_schnorrsig_verify_ecmult_context *ecmult_context = (secp256k1_schnorrsig_verify_ecmult_context *) data; + + if (idx % 4 == 2) { + /* Every idx corresponds to a (scalar,point)-tuple. So this callback is called with 4 + * consecutive tuples before we need to call the RNG for new randomizers: + * (-randomizer_cache[0], R1) + * (-randomizer_cache[0]*e1, P1) + * (-randomizer_cache[1], R2) + * (-randomizer_cache[1]*e2, P2) */ + secp256k1_scalar_chacha20(&ecmult_context->randomizer_cache[0], &ecmult_context->randomizer_cache[1], ecmult_context->chacha_seed, idx / 4); + } + + /* R */ + if (idx % 2 == 0) { + secp256k1_fe rx; + *sc = ecmult_context->randomizer_cache[(idx / 2) % 2]; + if (!secp256k1_fe_set_b32(&rx, &ecmult_context->sig[idx / 2]->data[0])) { + return 0; + } + if (!secp256k1_ge_set_xquad(pt, &rx)) { + return 0; + } + /* eP */ + } else { + unsigned char buf[33]; + size_t buflen = sizeof(buf); + secp256k1_sha256 sha; + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, &ecmult_context->sig[idx / 2]->data[0], 32); + secp256k1_ec_pubkey_serialize(ecmult_context->ctx, buf, &buflen, ecmult_context->pk[idx / 2], SECP256K1_EC_COMPRESSED); + secp256k1_sha256_write(&sha, buf, buflen); + secp256k1_sha256_write(&sha, ecmult_context->msg32[idx / 2], 32); + secp256k1_sha256_finalize(&sha, buf); + + secp256k1_scalar_set_b32(sc, buf, NULL); + secp256k1_scalar_mul(sc, sc, &ecmult_context->randomizer_cache[(idx / 2) % 2]); + + if (!secp256k1_pubkey_load(ecmult_context->ctx, pt, ecmult_context->pk[idx / 2])) { + return 0; + } + } + return 1; +} + +/** Helper function for batch verification. Hashes signature verification data into the + * randomization seed and initializes ecmult_context. + * + * Returns 1 if the randomizer was successfully initialized. + * + * Args: ctx: a secp256k1 context object + * Out: ecmult_context: context for batch_ecmult_callback + * In/Out sha: an initialized sha256 object which hashes the schnorrsig input in order to get a + * seed for the randomizer PRNG + * In: sig: array of signatures, or NULL if there are no signatures + * msg32: array of messages, or NULL if there are no signatures + * pk: array of public keys, or NULL if there are no signatures + * n_sigs: number of signatures in above arrays (must be 0 if they are NULL) + */ +static int secp256k1_schnorrsig_verify_batch_init_randomizer(const secp256k1_context *ctx, secp256k1_schnorrsig_verify_ecmult_context *ecmult_context, secp256k1_sha256 *sha, const secp256k1_schnorrsig *const *sig, const unsigned char *const *msg32, const secp256k1_pubkey *const *pk, size_t n_sigs) { + size_t i; + + if (n_sigs > 0) { + ARG_CHECK(sig != NULL); + ARG_CHECK(msg32 != NULL); + ARG_CHECK(pk != NULL); + } + + for (i = 0; i < n_sigs; i++) { + unsigned char buf[33]; + size_t buflen = sizeof(buf); + secp256k1_sha256_write(sha, sig[i]->data, 64); + secp256k1_sha256_write(sha, msg32[i], 32); + secp256k1_ec_pubkey_serialize(ctx, buf, &buflen, pk[i], SECP256K1_EC_COMPRESSED); + secp256k1_sha256_write(sha, buf, buflen); + } + ecmult_context->ctx = ctx; + ecmult_context->sig = sig; + ecmult_context->msg32 = msg32; + ecmult_context->pk = pk; + ecmult_context->n_sigs = n_sigs; + + return 1; +} + +/** Helper function for batch verification. Sums the s part of all signatures multiplied by their + * randomizer. + * + * Returns 1 if s is successfully summed. + * + * In/Out: s: the s part of the input sigs is added to this s argument + * In: chacha_seed: PRNG seed for computing randomizers + * sig: array of signatures, or NULL if there are no signatures + * n_sigs: number of signatures in above array (must be 0 if they are NULL) + */ +static int secp256k1_schnorrsig_verify_batch_sum_s(secp256k1_scalar *s, unsigned char *chacha_seed, const secp256k1_schnorrsig *const *sig, size_t n_sigs) { + secp256k1_scalar randomizer_cache[2]; + size_t i; + + secp256k1_scalar_set_int(&randomizer_cache[0], 1); + for (i = 0; i < n_sigs; i++) { + int overflow; + secp256k1_scalar term; + if (i % 2 == 1) { + secp256k1_scalar_chacha20(&randomizer_cache[0], &randomizer_cache[1], chacha_seed, i / 2); + } + + secp256k1_scalar_set_b32(&term, &sig[i]->data[32], &overflow); + if (overflow) { + return 0; + } + secp256k1_scalar_mul(&term, &term, &randomizer_cache[i % 2]); + secp256k1_scalar_add(s, s, &term); + } + return 1; +} + +/* schnorrsig batch verification. + * Seeds a random number generator with the inputs and derives a random number ai for every + * signature i. Fails if y-coordinate of any R is not a quadratic residue or if + * 0 != -(s1 + a2*s2 + ... + au*su)G + R1 + a2*R2 + ... + au*Ru + e1*P1 + (a2*e2)P2 + ... + (au*eu)Pu. */ +int secp256k1_schnorrsig_verify_batch(const secp256k1_context *ctx, secp256k1_scratch *scratch, const secp256k1_schnorrsig *const *sig, const unsigned char *const *msg32, const secp256k1_pubkey *const *pk, size_t n_sigs) { + secp256k1_schnorrsig_verify_ecmult_context ecmult_context; + secp256k1_sha256 sha; + secp256k1_scalar s; + secp256k1_gej rj; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(scratch != NULL); + /* Check that n_sigs is less than half of the maximum size_t value. This is necessary because + * the number of points given to ecmult_multi is 2*n_sigs. */ + ARG_CHECK(n_sigs <= SIZE_MAX / 2); + /* Check that n_sigs is less than 2^31 to ensure the same behavior of this function on 32-bit + * and 64-bit platforms. */ + ARG_CHECK(n_sigs < ((uint32_t)1 << 31)); + + secp256k1_sha256_initialize(&sha); + if (!secp256k1_schnorrsig_verify_batch_init_randomizer(ctx, &ecmult_context, &sha, sig, msg32, pk, n_sigs)) { + return 0; + } + secp256k1_sha256_finalize(&sha, ecmult_context.chacha_seed); + secp256k1_scalar_set_int(&ecmult_context.randomizer_cache[0], 1); + + secp256k1_scalar_clear(&s); + if (!secp256k1_schnorrsig_verify_batch_sum_s(&s, ecmult_context.chacha_seed, sig, n_sigs)) { + return 0; + } + secp256k1_scalar_negate(&s, &s); + + return secp256k1_ecmult_multi_var(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &rj, &s, secp256k1_schnorrsig_verify_batch_ecmult_callback, (void *) &ecmult_context, 2 * n_sigs) + && secp256k1_gej_is_infinity(&rj); +} + +#endif diff --git a/src/modules/schnorrsig/tests_impl.h b/src/modules/schnorrsig/tests_impl.h new file mode 100644 index 0000000000..2de43af1e3 --- /dev/null +++ b/src/modules/schnorrsig/tests_impl.h @@ -0,0 +1,721 @@ +/********************************************************************** + * Copyright (c) 2018 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_MODULE_SCHNORRSIG_TESTS_ +#define _SECP256K1_MODULE_SCHNORRSIG_TESTS_ + +#include "secp256k1_schnorrsig.h" + +void test_schnorrsig_serialize(void) { + secp256k1_schnorrsig sig; + unsigned char in[64]; + unsigned char out[64]; + + memset(in, 0x12, 64); + CHECK(secp256k1_schnorrsig_parse(ctx, &sig, in)); + CHECK(secp256k1_schnorrsig_serialize(ctx, out, &sig)); + CHECK(memcmp(in, out, 64) == 0); +} + +void test_schnorrsig_api(secp256k1_scratch_space *scratch) { + unsigned char sk1[32]; + unsigned char sk2[32]; + unsigned char sk3[32]; + unsigned char msg[32]; + unsigned char sig64[64]; + secp256k1_pubkey pk[3]; + secp256k1_schnorrsig sig; + const secp256k1_schnorrsig *sigptr = &sig; + const unsigned char *msgptr = msg; + const secp256k1_pubkey *pkptr = &pk[0]; + + /** setup **/ + secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); + secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); + secp256k1_context *both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + int ecount; + + secp256k1_context_set_error_callback(none, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(vrfy, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(both, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(both, counting_illegal_callback_fn, &ecount); + + secp256k1_rand256(sk1); + secp256k1_rand256(sk2); + secp256k1_rand256(sk3); + secp256k1_rand256(msg); + CHECK(secp256k1_ec_pubkey_create(ctx, &pk[0], sk1) == 1); + CHECK(secp256k1_ec_pubkey_create(ctx, &pk[1], sk2) == 1); + CHECK(secp256k1_ec_pubkey_create(ctx, &pk[2], sk3) == 1); + + /** main test body **/ + ecount = 0; + CHECK(secp256k1_schnorrsig_sign(none, &sig, msg, sk1, NULL, NULL) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_schnorrsig_sign(vrfy, &sig, msg, sk1, NULL, NULL) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_sign(sign, &sig, msg, sk1, NULL, NULL) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_sign(sign, NULL, msg, sk1, NULL, NULL) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_schnorrsig_sign(sign, &sig, NULL, sk1, NULL, NULL) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_schnorrsig_sign(sign, &sig, msg, NULL, NULL, NULL) == 0); + CHECK(ecount == 5); + + ecount = 0; + CHECK(secp256k1_schnorrsig_serialize(none, sig64, &sig) == 1); + CHECK(ecount == 0); + CHECK(secp256k1_schnorrsig_serialize(none, NULL, &sig) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_schnorrsig_serialize(none, sig64, NULL) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_parse(none, &sig, sig64) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_parse(none, NULL, sig64) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_schnorrsig_parse(none, &sig, NULL) == 0); + CHECK(ecount == 4); + + ecount = 0; + CHECK(secp256k1_schnorrsig_verify(none, &sig, msg, &pk[0]) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_schnorrsig_verify(sign, &sig, msg, &pk[0]) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_verify(vrfy, &sig, msg, &pk[0]) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_verify(vrfy, NULL, msg, &pk[0]) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_schnorrsig_verify(vrfy, &sig, NULL, &pk[0]) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_schnorrsig_verify(vrfy, &sig, msg, NULL) == 0); + CHECK(ecount == 5); + + ecount = 0; + CHECK(secp256k1_schnorrsig_verify_batch(none, scratch, &sigptr, &msgptr, &pkptr, 1) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_schnorrsig_verify_batch(sign, scratch, &sigptr, &msgptr, &pkptr, 1) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, &sigptr, &msgptr, &pkptr, 1) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, NULL, NULL, NULL, 0) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, NULL, &msgptr, &pkptr, 1) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, &sigptr, NULL, &pkptr, 1) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, &sigptr, &msgptr, NULL, 1) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, &sigptr, &msgptr, &pkptr, (size_t)1 << (sizeof(size_t)*8-1)) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, &sigptr, &msgptr, &pkptr, (uint32_t)1 << 31) == 0); + CHECK(ecount == 7); + + secp256k1_context_destroy(none); + secp256k1_context_destroy(sign); + secp256k1_context_destroy(vrfy); + secp256k1_context_destroy(both); +} + +/* Helper function for schnorrsig_bip_vectors + * Signs the message and checks that it's the same as expected_sig. */ +void test_schnorrsig_bip_vectors_check_signing(const unsigned char *sk, const unsigned char *pk_serialized, const unsigned char *msg, const unsigned char *expected_sig) { + secp256k1_schnorrsig sig; + unsigned char serialized_sig[64]; + secp256k1_pubkey pk; + + CHECK(secp256k1_schnorrsig_sign(ctx, &sig, msg, sk, NULL, NULL)); + CHECK(secp256k1_schnorrsig_serialize(ctx, serialized_sig, &sig)); + CHECK(memcmp(serialized_sig, expected_sig, 64) == 0); + + CHECK(secp256k1_ec_pubkey_parse(ctx, &pk, pk_serialized, 33)); + CHECK(secp256k1_schnorrsig_verify(ctx, &sig, msg, &pk)); +} + +/* Helper function for schnorrsig_bip_vectors + * Checks that both verify and verify_batch return the same value as expected. */ +void test_schnorrsig_bip_vectors_check_verify(secp256k1_scratch_space *scratch, const unsigned char *pk_serialized, const unsigned char *msg32, const unsigned char *sig_serialized, int expected) { + const unsigned char *msg_arr[1]; + const secp256k1_schnorrsig *sig_arr[1]; + const secp256k1_pubkey *pk_arr[1]; + secp256k1_pubkey pk; + secp256k1_schnorrsig sig; + + CHECK(secp256k1_ec_pubkey_parse(ctx, &pk, pk_serialized, 33)); + CHECK(secp256k1_schnorrsig_parse(ctx, &sig, sig_serialized)); + + sig_arr[0] = &sig; + msg_arr[0] = msg32; + pk_arr[0] = &pk; + + CHECK(expected == secp256k1_schnorrsig_verify(ctx, &sig, msg32, &pk)); + CHECK(expected == secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 1)); +} + +/* Test vectors according to BIP-schnorr + * (https://github.com/sipa/bips/blob/7f6a73e53c8bbcf2d008ea0546f76433e22094a8/bip-schnorr/test-vectors.csv). + */ +void test_schnorrsig_bip_vectors(secp256k1_scratch_space *scratch) { + { + /* Test vector 1 */ + const unsigned char sk1[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 + }; + const unsigned char pk1[33] = { + 0x02, 0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, + 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B, + 0x07, 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, + 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17, + 0x98 + }; + const unsigned char msg1[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + const unsigned char sig1[64] = { + 0x78, 0x7A, 0x84, 0x8E, 0x71, 0x04, 0x3D, 0x28, + 0x0C, 0x50, 0x47, 0x0E, 0x8E, 0x15, 0x32, 0xB2, + 0xDD, 0x5D, 0x20, 0xEE, 0x91, 0x2A, 0x45, 0xDB, + 0xDD, 0x2B, 0xD1, 0xDF, 0xBF, 0x18, 0x7E, 0xF6, + 0x70, 0x31, 0xA9, 0x88, 0x31, 0x85, 0x9D, 0xC3, + 0x4D, 0xFF, 0xEE, 0xDD, 0xA8, 0x68, 0x31, 0x84, + 0x2C, 0xCD, 0x00, 0x79, 0xE1, 0xF9, 0x2A, 0xF1, + 0x77, 0xF7, 0xF2, 0x2C, 0xC1, 0xDC, 0xED, 0x05 + }; + test_schnorrsig_bip_vectors_check_signing(sk1, pk1, msg1, sig1); + test_schnorrsig_bip_vectors_check_verify(scratch, pk1, msg1, sig1, 1); + } + { + /* Test vector 2 */ + const unsigned char sk2[32] = { + 0xB7, 0xE1, 0x51, 0x62, 0x8A, 0xED, 0x2A, 0x6A, + 0xBF, 0x71, 0x58, 0x80, 0x9C, 0xF4, 0xF3, 0xC7, + 0x62, 0xE7, 0x16, 0x0F, 0x38, 0xB4, 0xDA, 0x56, + 0xA7, 0x84, 0xD9, 0x04, 0x51, 0x90, 0xCF, 0xEF + }; + const unsigned char pk2[33] = { + 0x02, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, + 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, + 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, + 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, + 0x59 + }; + const unsigned char msg2[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig2[64] = { + 0x2A, 0x29, 0x8D, 0xAC, 0xAE, 0x57, 0x39, 0x5A, + 0x15, 0xD0, 0x79, 0x5D, 0xDB, 0xFD, 0x1D, 0xCB, + 0x56, 0x4D, 0xA8, 0x2B, 0x0F, 0x26, 0x9B, 0xC7, + 0x0A, 0x74, 0xF8, 0x22, 0x04, 0x29, 0xBA, 0x1D, + 0x1E, 0x51, 0xA2, 0x2C, 0xCE, 0xC3, 0x55, 0x99, + 0xB8, 0xF2, 0x66, 0x91, 0x22, 0x81, 0xF8, 0x36, + 0x5F, 0xFC, 0x2D, 0x03, 0x5A, 0x23, 0x04, 0x34, + 0xA1, 0xA6, 0x4D, 0xC5, 0x9F, 0x70, 0x13, 0xFD + }; + test_schnorrsig_bip_vectors_check_signing(sk2, pk2, msg2, sig2); + test_schnorrsig_bip_vectors_check_verify(scratch, pk2, msg2, sig2, 1); + } + { + /* Test vector 3 */ + const unsigned char sk3[32] = { + 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x14, 0xE5, 0xC7 + }; + const unsigned char pk3[33] = { + 0x03, 0xFA, 0xC2, 0x11, 0x4C, 0x2F, 0xBB, 0x09, + 0x15, 0x27, 0xEB, 0x7C, 0x64, 0xEC, 0xB1, 0x1F, + 0x80, 0x21, 0xCB, 0x45, 0xE8, 0xE7, 0x80, 0x9D, + 0x3C, 0x09, 0x38, 0xE4, 0xB8, 0xC0, 0xE5, 0xF8, + 0x4B + }; + const unsigned char msg3[32] = { + 0x5E, 0x2D, 0x58, 0xD8, 0xB3, 0xBC, 0xDF, 0x1A, + 0xBA, 0xDE, 0xC7, 0x82, 0x90, 0x54, 0xF9, 0x0D, + 0xDA, 0x98, 0x05, 0xAA, 0xB5, 0x6C, 0x77, 0x33, + 0x30, 0x24, 0xB9, 0xD0, 0xA5, 0x08, 0xB7, 0x5C + }; + const unsigned char sig3[64] = { + 0x00, 0xDA, 0x9B, 0x08, 0x17, 0x2A, 0x9B, 0x6F, + 0x04, 0x66, 0xA2, 0xDE, 0xFD, 0x81, 0x7F, 0x2D, + 0x7A, 0xB4, 0x37, 0xE0, 0xD2, 0x53, 0xCB, 0x53, + 0x95, 0xA9, 0x63, 0x86, 0x6B, 0x35, 0x74, 0xBE, + 0x00, 0x88, 0x03, 0x71, 0xD0, 0x17, 0x66, 0x93, + 0x5B, 0x92, 0xD2, 0xAB, 0x4C, 0xD5, 0xC8, 0xA2, + 0xA5, 0x83, 0x7E, 0xC5, 0x7F, 0xED, 0x76, 0x60, + 0x77, 0x3A, 0x05, 0xF0, 0xDE, 0x14, 0x23, 0x80 + }; + test_schnorrsig_bip_vectors_check_signing(sk3, pk3, msg3, sig3); + test_schnorrsig_bip_vectors_check_verify(scratch, pk3, msg3, sig3, 1); + } + { + /* Test vector 4 */ + const unsigned char pk4[33] = { + 0x03, 0xDE, 0xFD, 0xEA, 0x4C, 0xDB, 0x67, 0x77, + 0x50, 0xA4, 0x20, 0xFE, 0xE8, 0x07, 0xEA, 0xCF, + 0x21, 0xEB, 0x98, 0x98, 0xAE, 0x79, 0xB9, 0x76, + 0x87, 0x66, 0xE4, 0xFA, 0xA0, 0x4A, 0x2D, 0x4A, + 0x34 + }; + const unsigned char msg4[32] = { + 0x4D, 0xF3, 0xC3, 0xF6, 0x8F, 0xCC, 0x83, 0xB2, + 0x7E, 0x9D, 0x42, 0xC9, 0x04, 0x31, 0xA7, 0x24, + 0x99, 0xF1, 0x78, 0x75, 0xC8, 0x1A, 0x59, 0x9B, + 0x56, 0x6C, 0x98, 0x89, 0xB9, 0x69, 0x67, 0x03 + }; + const unsigned char sig4[64] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3B, 0x78, 0xCE, 0x56, 0x3F, + 0x89, 0xA0, 0xED, 0x94, 0x14, 0xF5, 0xAA, 0x28, + 0xAD, 0x0D, 0x96, 0xD6, 0x79, 0x5F, 0x9C, 0x63, + 0x02, 0xA8, 0xDC, 0x32, 0xE6, 0x4E, 0x86, 0xA3, + 0x33, 0xF2, 0x0E, 0xF5, 0x6E, 0xAC, 0x9B, 0xA3, + 0x0B, 0x72, 0x46, 0xD6, 0xD2, 0x5E, 0x22, 0xAD, + 0xB8, 0xC6, 0xBE, 0x1A, 0xEB, 0x08, 0xD4, 0x9D + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk4, msg4, sig4, 1); + } + { + /* Test vector 5 */ + const unsigned char pk5[33] = { + 0x03, 0x1B, 0x84, 0xC5, 0x56, 0x7B, 0x12, 0x64, + 0x40, 0x99, 0x5D, 0x3E, 0xD5, 0xAA, 0xBA, 0x05, + 0x65, 0xD7, 0x1E, 0x18, 0x34, 0x60, 0x48, 0x19, + 0xFF, 0x9C, 0x17, 0xF5, 0xE9, 0xD5, 0xDD, 0x07, + 0x8F + }; + const unsigned char msg5[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + const unsigned char sig5[64] = { + 0x52, 0x81, 0x85, 0x79, 0xAC, 0xA5, 0x97, 0x67, + 0xE3, 0x29, 0x1D, 0x91, 0xB7, 0x6B, 0x63, 0x7B, + 0xEF, 0x06, 0x20, 0x83, 0x28, 0x49, 0x92, 0xF2, + 0xD9, 0x5F, 0x56, 0x4C, 0xA6, 0xCB, 0x4E, 0x35, + 0x30, 0xB1, 0xDA, 0x84, 0x9C, 0x8E, 0x83, 0x04, + 0xAD, 0xC0, 0xCF, 0xE8, 0x70, 0x66, 0x03, 0x34, + 0xB3, 0xCF, 0xC1, 0x8E, 0x82, 0x5E, 0xF1, 0xDB, + 0x34, 0xCF, 0xAE, 0x3D, 0xFC, 0x5D, 0x81, 0x87 + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk5, msg5, sig5, 1); + } + { + /* Test vector 6 */ + const unsigned char pk6[33] = { + 0x03, 0xFA, 0xC2, 0x11, 0x4C, 0x2F, 0xBB, 0x09, + 0x15, 0x27, 0xEB, 0x7C, 0x64, 0xEC, 0xB1, 0x1F, + 0x80, 0x21, 0xCB, 0x45, 0xE8, 0xE7, 0x80, 0x9D, + 0x3C, 0x09, 0x38, 0xE4, 0xB8, 0xC0, 0xE5, 0xF8, + 0x4B + }; + const unsigned char msg6[32] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF + }; + const unsigned char sig6[64] = { + 0x57, 0x0D, 0xD4, 0xCA, 0x83, 0xD4, 0xE6, 0x31, + 0x7B, 0x8E, 0xE6, 0xBA, 0xE8, 0x34, 0x67, 0xA1, + 0xBF, 0x41, 0x9D, 0x07, 0x67, 0x12, 0x2D, 0xE4, + 0x09, 0x39, 0x44, 0x14, 0xB0, 0x50, 0x80, 0xDC, + 0xE9, 0xEE, 0x5F, 0x23, 0x7C, 0xBD, 0x10, 0x8E, + 0xAB, 0xAE, 0x1E, 0x37, 0x75, 0x9A, 0xE4, 0x7F, + 0x8E, 0x42, 0x03, 0xDA, 0x35, 0x32, 0xEB, 0x28, + 0xDB, 0x86, 0x0F, 0x33, 0xD6, 0x2D, 0x49, 0xBD + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk6, msg6, sig6, 1); + } + { + /* Test vector 7 */ + const unsigned char pk7[33] = { + 0x03, 0xEE, 0xFD, 0xEA, 0x4C, 0xDB, 0x67, 0x77, + 0x50, 0xA4, 0x20, 0xFE, 0xE8, 0x07, 0xEA, 0xCF, + 0x21, 0xEB, 0x98, 0x98, 0xAE, 0x79, 0xB9, 0x76, + 0x87, 0x66, 0xE4, 0xFA, 0xA0, 0x4A, 0x2D, 0x4A, + 0x34 + }; + secp256k1_pubkey pk7_parsed; + /* No need to check the signature of the test vector as parsing the pubkey already fails */ + CHECK(!secp256k1_ec_pubkey_parse(ctx, &pk7_parsed, pk7, 33)); + } + { + /* Test vector 8 */ + const unsigned char pk8[33] = { + 0x02, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, + 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, + 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, + 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, + 0x59 + }; + const unsigned char msg8[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig8[64] = { + 0x2A, 0x29, 0x8D, 0xAC, 0xAE, 0x57, 0x39, 0x5A, + 0x15, 0xD0, 0x79, 0x5D, 0xDB, 0xFD, 0x1D, 0xCB, + 0x56, 0x4D, 0xA8, 0x2B, 0x0F, 0x26, 0x9B, 0xC7, + 0x0A, 0x74, 0xF8, 0x22, 0x04, 0x29, 0xBA, 0x1D, + 0xFA, 0x16, 0xAE, 0xE0, 0x66, 0x09, 0x28, 0x0A, + 0x19, 0xB6, 0x7A, 0x24, 0xE1, 0x97, 0x7E, 0x46, + 0x97, 0x71, 0x2B, 0x5F, 0xD2, 0x94, 0x39, 0x14, + 0xEC, 0xD5, 0xF7, 0x30, 0x90, 0x1B, 0x4A, 0xB7 + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk8, msg8, sig8, 0); + } + { + /* Test vector 9 */ + const unsigned char pk9[33] = { + 0x03, 0xFA, 0xC2, 0x11, 0x4C, 0x2F, 0xBB, 0x09, + 0x15, 0x27, 0xEB, 0x7C, 0x64, 0xEC, 0xB1, 0x1F, + 0x80, 0x21, 0xCB, 0x45, 0xE8, 0xE7, 0x80, 0x9D, + 0x3C, 0x09, 0x38, 0xE4, 0xB8, 0xC0, 0xE5, 0xF8, + 0x4B + }; + const unsigned char msg9[32] = { + 0x5E, 0x2D, 0x58, 0xD8, 0xB3, 0xBC, 0xDF, 0x1A, + 0xBA, 0xDE, 0xC7, 0x82, 0x90, 0x54, 0xF9, 0x0D, + 0xDA, 0x98, 0x05, 0xAA, 0xB5, 0x6C, 0x77, 0x33, + 0x30, 0x24, 0xB9, 0xD0, 0xA5, 0x08, 0xB7, 0x5C + }; + const unsigned char sig9[64] = { + 0x00, 0xDA, 0x9B, 0x08, 0x17, 0x2A, 0x9B, 0x6F, + 0x04, 0x66, 0xA2, 0xDE, 0xFD, 0x81, 0x7F, 0x2D, + 0x7A, 0xB4, 0x37, 0xE0, 0xD2, 0x53, 0xCB, 0x53, + 0x95, 0xA9, 0x63, 0x86, 0x6B, 0x35, 0x74, 0xBE, + 0xD0, 0x92, 0xF9, 0xD8, 0x60, 0xF1, 0x77, 0x6A, + 0x1F, 0x74, 0x12, 0xAD, 0x8A, 0x1E, 0xB5, 0x0D, + 0xAC, 0xCC, 0x22, 0x2B, 0xC8, 0xC0, 0xE2, 0x6B, + 0x20, 0x56, 0xDF, 0x2F, 0x27, 0x3E, 0xFD, 0xEC + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk9, msg9, sig9, 0); + } + { + /* Test vector 10 */ + const unsigned char pk10[33] = { + 0x02, 0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, + 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B, + 0x07, 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, + 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17, + 0x98 + }; + const unsigned char msg10[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + const unsigned char sig10[64] = { + 0x78, 0x7A, 0x84, 0x8E, 0x71, 0x04, 0x3D, 0x28, + 0x0C, 0x50, 0x47, 0x0E, 0x8E, 0x15, 0x32, 0xB2, + 0xDD, 0x5D, 0x20, 0xEE, 0x91, 0x2A, 0x45, 0xDB, + 0xDD, 0x2B, 0xD1, 0xDF, 0xBF, 0x18, 0x7E, 0xF6, + 0x8F, 0xCE, 0x56, 0x77, 0xCE, 0x7A, 0x62, 0x3C, + 0xB2, 0x00, 0x11, 0x22, 0x57, 0x97, 0xCE, 0x7A, + 0x8D, 0xE1, 0xDC, 0x6C, 0xCD, 0x4F, 0x75, 0x4A, + 0x47, 0xDA, 0x6C, 0x60, 0x0E, 0x59, 0x54, 0x3C + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk10, msg10, sig10, 0); + } + { + /* Test vector 11 */ + const unsigned char pk11[33] = { + 0x03, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, + 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, + 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, + 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, + 0x59 + }; + const unsigned char msg11[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig11[64] = { + 0x2A, 0x29, 0x8D, 0xAC, 0xAE, 0x57, 0x39, 0x5A, + 0x15, 0xD0, 0x79, 0x5D, 0xDB, 0xFD, 0x1D, 0xCB, + 0x56, 0x4D, 0xA8, 0x2B, 0x0F, 0x26, 0x9B, 0xC7, + 0x0A, 0x74, 0xF8, 0x22, 0x04, 0x29, 0xBA, 0x1D, + 0x1E, 0x51, 0xA2, 0x2C, 0xCE, 0xC3, 0x55, 0x99, + 0xB8, 0xF2, 0x66, 0x91, 0x22, 0x81, 0xF8, 0x36, + 0x5F, 0xFC, 0x2D, 0x03, 0x5A, 0x23, 0x04, 0x34, + 0xA1, 0xA6, 0x4D, 0xC5, 0x9F, 0x70, 0x13, 0xFD + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk11, msg11, sig11, 0); + } + { + /* Test vector 12 */ + const unsigned char pk12[33] = { + 0x02, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, + 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, + 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, + 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, + 0x59 + }; + const unsigned char msg12[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig12[64] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9E, 0x9D, 0x01, 0xAF, 0x98, 0x8B, 0x5C, 0xED, + 0xCE, 0x47, 0x22, 0x1B, 0xFA, 0x9B, 0x22, 0x27, + 0x21, 0xF3, 0xFA, 0x40, 0x89, 0x15, 0x44, 0x4A, + 0x4B, 0x48, 0x90, 0x21, 0xDB, 0x55, 0x77, 0x5F + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk12, msg12, sig12, 0); + } + { + /* Test vector 13 */ + const unsigned char pk13[33] = { + 0x02, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, + 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, + 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, + 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, + 0x59 + }; + const unsigned char msg13[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig13[64] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0xD3, 0x7D, 0xDF, 0x02, 0x54, 0x35, 0x18, 0x36, + 0xD8, 0x4B, 0x1B, 0xD6, 0xA7, 0x95, 0xFD, 0x5D, + 0x52, 0x30, 0x48, 0xF2, 0x98, 0xC4, 0x21, 0x4D, + 0x18, 0x7F, 0xE4, 0x89, 0x29, 0x47, 0xF7, 0x28 + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk13, msg13, sig13, 0); + } + { + /* Test vector 14 */ + const unsigned char pk14[33] = { + 0x02, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, + 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, + 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, + 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, + 0x59 + }; + const unsigned char msg14[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x14, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig14[64] = { + 0x4A, 0x29, 0x8D, 0xAC, 0xAE, 0x57, 0x39, 0x5A, + 0x15, 0xD0, 0x79, 0x5D, 0xDB, 0xFD, 0x1D, 0xCB, + 0x56, 0x4D, 0xA8, 0x2B, 0x0F, 0x26, 0x9B, 0xC7, + 0x0A, 0x74, 0xF8, 0x22, 0x04, 0x29, 0xBA, 0x1D, + 0x1E, 0x51, 0xA2, 0x2C, 0xCE, 0xC3, 0x55, 0x99, + 0xB8, 0xF2, 0x66, 0x91, 0x22, 0x81, 0xF8, 0x36, + 0x5F, 0xFC, 0x2D, 0x03, 0x5A, 0x23, 0x04, 0x34, + 0xA1, 0xA6, 0x4D, 0xC5, 0x9F, 0x70, 0x13, 0xFD + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk14, msg14, sig14, 0); + } + { + /* Test vector 15 */ + const unsigned char pk15[33] = { + 0x02, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, + 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, + 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, + 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, + 0x59 + }; + const unsigned char msg15[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig15[64] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x2F, + 0x1E, 0x51, 0xA2, 0x2C, 0xCE, 0xC3, 0x55, 0x99, + 0xB8, 0xF2, 0x66, 0x91, 0x22, 0x81, 0xF8, 0x36, + 0x5F, 0xFC, 0x2D, 0x03, 0x5A, 0x23, 0x04, 0x34, + 0xA1, 0xA6, 0x4D, 0xC5, 0x9F, 0x70, 0x13, 0xFD + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk15, msg15, sig15, 0); + } + { + /* Test vector 16 */ + const unsigned char pk16[33] = { + 0x02, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, + 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, + 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, + 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, + 0x59 + }; + const unsigned char msg16[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig16[64] = { + 0x2A, 0x29, 0x8D, 0xAC, 0xAE, 0x57, 0x39, 0x5A, + 0x15, 0xD0, 0x79, 0x5D, 0xDB, 0xFD, 0x1D, 0xCB, + 0x56, 0x4D, 0xA8, 0x2B, 0x0F, 0x26, 0x9B, 0xC7, + 0x0A, 0x74, 0xF8, 0x22, 0x04, 0x29, 0xBA, 0x1D, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, + 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, + 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41 + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk16, msg16, sig16, 0); + } +} + +/* Nonce function that returns constant 0 */ +static int nonce_function_failing(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { + (void) msg32; + (void) key32; + (void) algo16; + (void) data; + (void) counter; + (void) nonce32; + return 0; +} + +/* Nonce function that sets nonce to 0 */ +static int nonce_function_0(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { + (void) msg32; + (void) key32; + (void) algo16; + (void) data; + (void) counter; + + memset(nonce32, 0, 32); + return 1; +} + +void test_schnorrsig_sign(void) { + unsigned char sk[32]; + const unsigned char msg[32] = "this is a msg for a schnorrsig.."; + secp256k1_schnorrsig sig; + + memset(sk, 23, sizeof(sk)); + CHECK(secp256k1_schnorrsig_sign(ctx, &sig, msg, sk, NULL, NULL) == 1); + + /* Overflowing secret key */ + memset(sk, 0xFF, sizeof(sk)); + CHECK(secp256k1_schnorrsig_sign(ctx, &sig, msg, sk, NULL, NULL) == 0); + memset(sk, 23, sizeof(sk)); + + CHECK(secp256k1_schnorrsig_sign(ctx, &sig, msg, sk, nonce_function_failing, NULL) == 0); + CHECK(secp256k1_schnorrsig_sign(ctx, &sig, msg, sk, nonce_function_0, NULL) == 0); +} + +#define N_SIGS 200 +/* Creates N_SIGS valid signatures and verifies them with verify and verify_batch. Then flips some + * bits and checks that verification now fails. */ +void test_schnorrsig_sign_verify(secp256k1_scratch_space *scratch) { + const unsigned char sk[32] = "shhhhhhhh! this key is a secret."; + unsigned char msg[N_SIGS][32]; + secp256k1_schnorrsig sig[N_SIGS]; + size_t i; + const secp256k1_schnorrsig *sig_arr[N_SIGS]; + const unsigned char *msg_arr[N_SIGS]; + const secp256k1_pubkey *pk_arr[N_SIGS]; + secp256k1_pubkey pk; + + CHECK(secp256k1_ec_pubkey_create(ctx, &pk, sk)); + + CHECK(secp256k1_schnorrsig_verify_batch(ctx, scratch, NULL, NULL, NULL, 0)); + + for (i = 0; i < N_SIGS; i++) { + secp256k1_rand256(msg[i]); + CHECK(secp256k1_schnorrsig_sign(ctx, &sig[i], msg[i], sk, NULL, NULL)); + CHECK(secp256k1_schnorrsig_verify(ctx, &sig[i], msg[i], &pk)); + sig_arr[i] = &sig[i]; + msg_arr[i] = msg[i]; + pk_arr[i] = &pk; + } + + CHECK(secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 1)); + CHECK(secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 2)); + CHECK(secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 4)); + CHECK(secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, N_SIGS)); + + { + /* Flip a few bits in the signature and in the message and check that + * verify and verify_batch fail */ + size_t sig_idx = secp256k1_rand_int(4); + size_t byte_idx = secp256k1_rand_int(32); + unsigned char xorbyte = secp256k1_rand_int(254)+1; + sig[sig_idx].data[byte_idx] ^= xorbyte; + CHECK(!secp256k1_schnorrsig_verify(ctx, &sig[sig_idx], msg[sig_idx], &pk)); + CHECK(!secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 4)); + sig[sig_idx].data[byte_idx] ^= xorbyte; + + byte_idx = secp256k1_rand_int(32); + sig[sig_idx].data[32+byte_idx] ^= xorbyte; + CHECK(!secp256k1_schnorrsig_verify(ctx, &sig[sig_idx], msg[sig_idx], &pk)); + CHECK(!secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 4)); + sig[sig_idx].data[32+byte_idx] ^= xorbyte; + + byte_idx = secp256k1_rand_int(32); + msg[sig_idx][byte_idx] ^= xorbyte; + CHECK(!secp256k1_schnorrsig_verify(ctx, &sig[sig_idx], msg[sig_idx], &pk)); + CHECK(!secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 4)); + msg[sig_idx][byte_idx] ^= xorbyte; + + /* Check that above bitflips have been reversed correctly */ + CHECK(secp256k1_schnorrsig_verify(ctx, &sig[sig_idx], msg[sig_idx], &pk)); + CHECK(secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 4)); + } +} +#undef N_SIGS + +void run_schnorrsig_tests(void) { + secp256k1_scratch_space *scratch = secp256k1_scratch_space_create(ctx, 1024 * 1024); + + test_schnorrsig_serialize(); + test_schnorrsig_api(scratch); + test_schnorrsig_bip_vectors(scratch); + test_schnorrsig_sign(); + test_schnorrsig_sign_verify(scratch); + + secp256k1_scratch_space_destroy(ctx, scratch); +} + +#endif diff --git a/src/scalar_4x64_impl.h b/src/scalar_4x64_impl.h index 1000d9a962..268cc68d60 100644 --- a/src/scalar_4x64_impl.h +++ b/src/scalar_4x64_impl.h @@ -958,9 +958,7 @@ SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, #ifdef WORDS_BIGENDIAN #define LE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) -#define BE32(p) (p) #else -#define BE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) #define LE32(p) (p) #endif @@ -1019,14 +1017,14 @@ static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2 x14 += 0; x15 += over_count; - r1->d[3] = LE32((uint64_t) x0) << 32 | LE32(x1); - r1->d[2] = LE32((uint64_t) x2) << 32 | LE32(x3); - r1->d[1] = LE32((uint64_t) x4) << 32 | LE32(x5); - r1->d[0] = LE32((uint64_t) x6) << 32 | LE32(x7); - r2->d[3] = LE32((uint64_t) x8) << 32 | LE32(x9); - r2->d[2] = LE32((uint64_t) x10) << 32 | LE32(x11); - r2->d[1] = LE32((uint64_t) x12) << 32 | LE32(x13); - r2->d[0] = LE32((uint64_t) x14) << 32 | LE32(x15); + r1->d[3] = (((uint64_t) x0) << 32) | x1; + r1->d[2] = (((uint64_t) x2) << 32) | x3; + r1->d[1] = (((uint64_t) x4) << 32) | x5; + r1->d[0] = (((uint64_t) x6) << 32) | x7; + r2->d[3] = (((uint64_t) x8) << 32) | x9; + r2->d[2] = (((uint64_t) x10) << 32) | x11; + r2->d[1] = (((uint64_t) x12) << 32) | x13; + r2->d[0] = (((uint64_t) x14) << 32) | x15; over1 = secp256k1_scalar_check_overflow(r1); over2 = secp256k1_scalar_check_overflow(r2); @@ -1036,7 +1034,6 @@ static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2 #undef ROTL32 #undef QUARTERROUND -#undef BE32 #undef LE32 #endif /* SECP256K1_SCALAR_REPR_IMPL_H */ diff --git a/src/scalar_8x32_impl.h b/src/scalar_8x32_impl.h index f99c741e40..a9a934c43c 100644 --- a/src/scalar_8x32_impl.h +++ b/src/scalar_8x32_impl.h @@ -729,9 +729,7 @@ SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, #ifdef WORDS_BIGENDIAN #define LE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) -#define BE32(p) (p) #else -#define BE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) #define LE32(p) (p) #endif @@ -790,22 +788,22 @@ static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2 x14 += 0; x15 += over_count; - r1->d[7] = LE32(x0); - r1->d[6] = LE32(x1); - r1->d[5] = LE32(x2); - r1->d[4] = LE32(x3); - r1->d[3] = LE32(x4); - r1->d[2] = LE32(x5); - r1->d[1] = LE32(x6); - r1->d[0] = LE32(x7); - r2->d[7] = LE32(x8); - r2->d[6] = LE32(x9); - r2->d[5] = LE32(x10); - r2->d[4] = LE32(x11); - r2->d[3] = LE32(x12); - r2->d[2] = LE32(x13); - r2->d[1] = LE32(x14); - r2->d[0] = LE32(x15); + r1->d[7] = x0; + r1->d[6] = x1; + r1->d[5] = x2; + r1->d[4] = x3; + r1->d[3] = x4; + r1->d[2] = x5; + r1->d[1] = x6; + r1->d[0] = x7; + r2->d[7] = x8; + r2->d[6] = x9; + r2->d[5] = x10; + r2->d[4] = x11; + r2->d[3] = x12; + r2->d[2] = x13; + r2->d[1] = x14; + r2->d[0] = x15; over1 = secp256k1_scalar_check_overflow(r1); over2 = secp256k1_scalar_check_overflow(r2); @@ -815,7 +813,6 @@ static void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2 #undef ROTL32 #undef QUARTERROUND -#undef BE32 #undef LE32 #endif /* SECP256K1_SCALAR_REPR_IMPL_H */ diff --git a/src/secp256k1.c b/src/secp256k1.c index a3f446e507..38c2b0d69e 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -413,6 +413,29 @@ static SECP256K1_INLINE void buffer_append(unsigned char *buf, unsigned int *off *offset += len; } +/* This nonce function is described in BIP-schnorr + * (https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki) */ +static int nonce_function_bipschnorr(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { + secp256k1_sha256 sha; + (void) counter; + VERIFY_CHECK(counter == 0); + + /* Hash x||msg as per the spec */ + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, key32, 32); + secp256k1_sha256_write(&sha, msg32, 32); + /* Hash in algorithm, which is not in the spec, but may be critical to + * users depending on it to avoid nonce reuse across algorithms. */ + if (algo16 != NULL) { + secp256k1_sha256_write(&sha, algo16, 16); + } + if (data != NULL) { + secp256k1_sha256_write(&sha, data, 32); + } + secp256k1_sha256_finalize(&sha, nonce32); + return 1; +} + static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { unsigned char keydata[112]; unsigned int offset = 0; @@ -443,6 +466,7 @@ static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *m return 1; } +const secp256k1_nonce_function secp256k1_nonce_function_bipschnorr = nonce_function_bipschnorr; const secp256k1_nonce_function secp256k1_nonce_function_rfc6979 = nonce_function_rfc6979; const secp256k1_nonce_function secp256k1_nonce_function_default = nonce_function_rfc6979; @@ -685,6 +709,10 @@ int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey * # include "modules/ecdh/main_impl.h" #endif +#ifdef ENABLE_MODULE_SCHNORRSIG +# include "modules/schnorrsig/main_impl.h" +#endif + #ifdef ENABLE_MODULE_RECOVERY # include "modules/recovery/main_impl.h" #endif diff --git a/src/tests.c b/src/tests.c index c9ff3e947f..ed66e83ac8 100644 --- a/src/tests.c +++ b/src/tests.c @@ -5272,6 +5272,10 @@ void run_ecdsa_openssl(void) { # include "modules/ecdh/tests_impl.h" #endif +#ifdef ENABLE_MODULE_SCHNORRSIG +# include "modules/schnorrsig/tests_impl.h" +#endif + #ifdef ENABLE_MODULE_RECOVERY # include "modules/recovery/tests_impl.h" #endif @@ -5385,6 +5389,11 @@ int main(int argc, char **argv) { run_ecdh_tests(); #endif +#ifdef ENABLE_MODULE_SCHNORRSIG + /* Schnorrsig tests */ + run_schnorrsig_tests(); +#endif + /* ecdsa tests */ run_random_pubkeys(); run_ecdsa_der_parse(); From 0b4bef4527e2b4694a83306f495ca61d574d2c1f Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Thu, 1 Nov 2018 10:31:24 +0000 Subject: [PATCH 3/7] Add ec_commitments which are essentially the pay-to-contract-style tweaks of public keys. The functionality is not exposed. --- src/secp256k1.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++ src/tests.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 163 insertions(+) diff --git a/src/secp256k1.c b/src/secp256k1.c index 38c2b0d69e..cf1771f04b 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -705,6 +705,89 @@ int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey * return 1; } +/* Compute an ec commitment tweak as hash(pubkey, data). */ +static int secp256k1_ec_commit_tweak(const secp256k1_context *ctx, unsigned char *tweak32, const secp256k1_pubkey *pubkey, const unsigned char *data, size_t data_size) { + secp256k1_ge p; + unsigned char rbuf[33]; + size_t rbuf_size = sizeof(rbuf); + secp256k1_sha256 sha; + + if (data_size == 0) { + /* That's probably not what the caller wanted */ + return 0; + } + if(!secp256k1_pubkey_load(ctx, &p, pubkey)) { + return 0; + } + secp256k1_eckey_pubkey_serialize(&p, rbuf, &rbuf_size, 1); + + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, rbuf, rbuf_size); + secp256k1_sha256_write(&sha, data, data_size); + secp256k1_sha256_finalize(&sha, tweak32); + return 1; +} + +/* Compute an ec commitment as pubkey + hash(pubkey, data)*G. */ +static int secp256k1_ec_commit(const secp256k1_context* ctx, secp256k1_pubkey *commitment, const secp256k1_pubkey *pubkey, const unsigned char *data, size_t data_size) { + unsigned char tweak[32]; + + *commitment = *pubkey; + if (!secp256k1_ec_commit_tweak(ctx, tweak, commitment, data, data_size)) { + return 0; + } + return secp256k1_ec_pubkey_tweak_add(ctx, commitment, tweak); +} + +/* Compute the seckey of an ec commitment from the original secret key of the pubkey as seckey + + * hash(pubkey, data). */ +static int secp256k1_ec_commit_seckey(const secp256k1_context* ctx, unsigned char *seckey, const secp256k1_pubkey *pubkey, const unsigned char *data, size_t data_size) { + unsigned char tweak[32]; + secp256k1_pubkey pubkey_tmp; + + if (pubkey == NULL) { + /* Compute pubkey from seckey if not provided */ + int overflow; + secp256k1_scalar x; + secp256k1_gej pj; + secp256k1_ge p; + + secp256k1_scalar_set_b32(&x, seckey, &overflow); + if (overflow != 0) { + return 0; + } + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &x); + secp256k1_ge_set_gej(&p, &pj); + secp256k1_pubkey_save(&pubkey_tmp, &p); + pubkey = &pubkey_tmp; + } + + if (!secp256k1_ec_commit_tweak(ctx, tweak, pubkey, data, data_size)) { + return 0; + } + return secp256k1_ec_privkey_tweak_add(ctx, seckey, tweak); +} + +/* Verify an ec commitment as pubkey + hash(pubkey, data)*G ?= commitment. */ +static int secp256k1_ec_commit_verify(const secp256k1_context* ctx, const secp256k1_pubkey *commitment, const secp256k1_pubkey *pubkey, const unsigned char *data, size_t data_size) { + secp256k1_gej pj; + secp256k1_ge p; + secp256k1_pubkey commitment_tmp; + + if (!secp256k1_ec_commit(ctx, &commitment_tmp, pubkey, data, data_size)) { + return 0; + } + + /* Return commitment == commitment_tmp */ + secp256k1_gej_set_infinity(&pj); + secp256k1_pubkey_load(ctx, &p, &commitment_tmp); + secp256k1_gej_add_ge_var(&pj, &pj, &p, NULL); + secp256k1_pubkey_load(ctx, &p, commitment); + secp256k1_ge_neg(&p, &p); + secp256k1_gej_add_ge_var(&pj, &pj, &p, NULL); + return secp256k1_gej_is_infinity(&pj); +} + #ifdef ENABLE_MODULE_ECDH # include "modules/ecdh/main_impl.h" #endif diff --git a/src/tests.c b/src/tests.c index ed66e83ac8..02f4daea16 100644 --- a/src/tests.c +++ b/src/tests.c @@ -2475,6 +2475,85 @@ void run_ec_combine(void) { } } +int test_ec_commit_seckey(unsigned char *seckey, secp256k1_pubkey *commitment) { + /* Return if seckey is the discrete log of commitment */ + secp256k1_pubkey pubkey_tmp; + return secp256k1_ec_pubkey_create(ctx, &pubkey_tmp, seckey) == 1 + && memcmp(&pubkey_tmp, commitment, sizeof(pubkey_tmp)) == 0; +} + +void test_ec_commit(void) { + unsigned char seckey[32]; + secp256k1_pubkey pubkey; + secp256k1_pubkey commitment; + unsigned char data[32]; + + /* Create random keypair and data */ + secp256k1_rand256(seckey); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, seckey)); + secp256k1_rand256_test(data); + + /* Commit to data and verify */ + CHECK(secp256k1_ec_commit(ctx, &commitment, &pubkey, data, 32)); + CHECK(secp256k1_ec_commit_verify(ctx, &commitment, &pubkey, data, 32)); + CHECK(secp256k1_ec_commit_seckey(ctx, seckey, &pubkey, data, 32)); + CHECK(test_ec_commit_seckey(seckey, &commitment) == 1); + + /* Check that verification fails with different data */ + CHECK(secp256k1_ec_commit_verify(ctx, &commitment, &pubkey, data, 31) == 0); +} + +void test_ec_commit_api(void) { + unsigned char seckey[32]; + secp256k1_pubkey pubkey; + secp256k1_pubkey commitment; + unsigned char data[32]; + + memset(data, 23, sizeof(data)); + + /* Create random keypair */ + secp256k1_rand256(seckey); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, seckey)); + + CHECK(secp256k1_ec_commit(ctx, &commitment, &pubkey, data, 1) == 1); + /* The same pubkey can be both input and output of the function */ + { + secp256k1_pubkey pubkey_tmp = pubkey; + CHECK(secp256k1_ec_commit(ctx, &pubkey_tmp, &pubkey_tmp, data, 1) == 1); + CHECK(memcmp(commitment.data, pubkey_tmp.data, sizeof(commitment.data)) == 0); + } + + /* If the pubkey is not provided it will be computed from seckey */ + CHECK(secp256k1_ec_commit_seckey(ctx, seckey, NULL, data, 1) == 1); + CHECK(test_ec_commit_seckey(seckey, &commitment) == 1); + /* pubkey is not provided but seckey overflows */ + { + unsigned char overflowed_seckey[32]; + memset(overflowed_seckey, 0xFF, sizeof(overflowed_seckey)); + CHECK(secp256k1_ec_commit_seckey(ctx, overflowed_seckey, NULL, data, 1) == 0); + } + + CHECK(secp256k1_ec_commit_verify(ctx, &commitment, &pubkey, data, 1) == 1); + + /* Commitment to 0-len data should fail */ + CHECK(secp256k1_ec_commit(ctx, &commitment, &pubkey, data, 0) == 0); + CHECK(secp256k1_ec_commit_verify(ctx, &commitment, &pubkey, data, 0) == 0); + CHECK(memcmp(&pubkey.data, &commitment.data, sizeof(pubkey.data)) == 0); + { + unsigned char seckey_tmp[32]; + memcpy(seckey_tmp, seckey, 32); + CHECK(secp256k1_ec_commit_seckey(ctx, seckey_tmp, &pubkey, data, 0) == 0); + } +} + +void run_ec_commit(void) { + int i; + for (i = 0; i < count * 8; i++) { + test_ec_commit(); + } + test_ec_commit_api(); +} + void test_group_decompress(const secp256k1_fe* x) { /* The input itself, normalized. */ secp256k1_fe fex = *x; @@ -5372,6 +5451,7 @@ int main(int argc, char **argv) { run_ecmult_const_tests(); run_ecmult_multi_tests(); run_ec_combine(); + run_ec_commit(); /* endomorphism tests */ #ifdef USE_ENDOMORPHISM From 854c1c495fe6cf4e17f71d14832909659ca2d922 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Wed, 13 Feb 2019 14:15:23 +0000 Subject: [PATCH 4/7] Add and expose sign-to-contract opening with parse and serialize functions --- include/secp256k1.h | 55 +++++++++++++++++++++++++++++++++++++++++++++ src/secp256k1.c | 33 +++++++++++++++++++++++++++ src/tests.c | 55 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 143 insertions(+) diff --git a/include/secp256k1.h b/include/secp256k1.h index 5988b14193..770b700660 100644 --- a/include/secp256k1.h +++ b/include/secp256k1.h @@ -6,6 +6,7 @@ extern "C" { #endif #include +#include /* These rules specify the order of arguments in API calls: * @@ -81,6 +82,29 @@ typedef struct { unsigned char data[64]; } secp256k1_ecdsa_signature; +/** Data structure that holds a sign-to-contract ("s2c") opening information. + * Sign-to-contract allows a signer to commit to some data as part of a signature. It + * can be used as an Out-argument in certain signing functions. + * + * This structure is not opaque, but it is strongly discouraged to read or write to + * it directly. + * + * The exact representation of data inside is implementation defined and not + * guaranteed to be portable between different platforms or versions. It can + * be safely copied/moved. + */ +typedef struct { + /* magic is set during initialization */ + uint64_t magic; + /* Public nonce before applying the sign-to-contract commitment */ + secp256k1_pubkey original_pubnonce; + /* Byte indicating if signing algorithm negated the nonce. Alternatively when + * verifying we could compute the EC commitment of original_pubnonce and the + * data and negate if this would not be a valid nonce. But this would prevent + * batch verification of sign-to-contract commitments. */ + int nonce_is_negated; +} secp256k1_s2c_opening; + /** A pointer to a function to deterministically generate a nonce. * * Returns: 1 if a nonce was successfully generated. 0 will cause signing to fail. @@ -444,6 +468,37 @@ SECP256K1_API int secp256k1_ecdsa_signature_serialize_compact( const secp256k1_ecdsa_signature* sig ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); +/** Parse a sign-to-contract opening. + * + * Returns: 1 if the opening was fully valid. + * 0 if the opening could not be parsed or is invalid. + * Args: ctx: a secp256k1 context object. + * Out: opening: pointer to an opening object. If 1 is returned, it is set to a + * parsed version of input. If not, its value is undefined. + * In: input34: pointer to 34-byte array with a serialized opening + * + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_s2c_opening_parse( + const secp256k1_context* ctx, + secp256k1_s2c_opening* opening, + const unsigned char *input34 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Serialize a sign-to-contract opening into a byte sequence. + * + * Returns: 1 if the opening was successfully serialized. + * 0 if the opening was not initializaed. + * Args: ctx: a secp256k1 context object. + * Out: output34: pointer to a 34-byte array to place the serialized opening + * in. + * In: opening: a pointer to an initialized `secp256k1_s2c_opening`. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_s2c_opening_serialize( + const secp256k1_context* ctx, + unsigned char *output34, + const secp256k1_s2c_opening* opening +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + /** Verify an ECDSA signature. * * Returns: 1: correct signature diff --git a/src/secp256k1.c b/src/secp256k1.c index cf1771f04b..808f36011e 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -788,6 +788,39 @@ static int secp256k1_ec_commit_verify(const secp256k1_context* ctx, const secp25 return secp256k1_gej_is_infinity(&pj); } +static uint64_t s2c_opening_magic = 0x5d0520b8b7f2b168ULL; + +static void secp256k1_s2c_opening_init(secp256k1_s2c_opening *opening) { + opening->magic = s2c_opening_magic; + opening->nonce_is_negated = 0; +} + +static int secp256k1_s2c_commit_is_init(const secp256k1_s2c_opening *opening) { + return opening->magic == s2c_opening_magic; +} + +int secp256k1_s2c_opening_parse(const secp256k1_context* ctx, secp256k1_s2c_opening* opening, const unsigned char *input34) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(opening != NULL); + ARG_CHECK(input34 != NULL); + + secp256k1_s2c_opening_init(opening); + opening->nonce_is_negated = input34[0]; + return secp256k1_ec_pubkey_parse(ctx, &opening->original_pubnonce, &input34[1], 33); +} + +int secp256k1_s2c_opening_serialize(const secp256k1_context* ctx, unsigned char *output34, const secp256k1_s2c_opening* opening) { + size_t outputlen = 33; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(output34 != NULL); + ARG_CHECK(opening != NULL); + ARG_CHECK(secp256k1_s2c_commit_is_init(opening)); + + output34[0] = opening->nonce_is_negated; + return secp256k1_ec_pubkey_serialize(ctx, &output34[1], &outputlen, &opening->original_pubnonce, SECP256K1_EC_COMPRESSED); +} + #ifdef ENABLE_MODULE_ECDH # include "modules/ecdh/main_impl.h" #endif diff --git a/src/tests.c b/src/tests.c index 02f4daea16..0c57da4d3b 100644 --- a/src/tests.c +++ b/src/tests.c @@ -4293,6 +4293,59 @@ void run_eckey_edge_case_test(void) { secp256k1_context_set_illegal_callback(ctx, NULL, NULL); } + +void run_s2c_opening_test(void) { + int i = 0; + unsigned char output[34]; + unsigned char input[34] = { + 0x01, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02 + }; + secp256k1_s2c_opening opening; + size_t ecount = 0; + + secp256k1_context_set_illegal_callback(ctx, counting_illegal_callback_fn, &ecount); + + /* Uninitialized opening can't be serialized. Actually testing that would be + * undefined behavior. Therefore we simulate it by setting the opening to 0. */ + memset(&opening, 0, sizeof(opening)); + CHECK(ecount == 0); + CHECK(secp256k1_s2c_opening_serialize(ctx, output, &opening) == 0); + CHECK(ecount == 1); + + /* First parsing, then serializing works */ + CHECK(secp256k1_s2c_opening_parse(ctx, &opening, input) == 1); + CHECK(secp256k1_s2c_opening_serialize(ctx, output, &opening) == 1); + + { + /* Invalid pubkey makes parsing fail */ + unsigned char input_tmp[34]; + memcpy(input_tmp, input, sizeof(input_tmp)); + input_tmp[33] = 0; + CHECK(secp256k1_s2c_opening_parse(ctx, &opening, input_tmp) == 0); + } + + /* Try parsing and serializing a bunch of openings */ + do { + /* This is expected to fail in about 50% of iterations because the + * points' x-coordinates are uniformly random */ + if (secp256k1_s2c_opening_parse(ctx, &opening, input) == 1) { + CHECK(secp256k1_s2c_opening_serialize(ctx, output, &opening) == 1); + CHECK(memcmp(output, input, 34) == 0); + } + secp256k1_rand256(input); + /* nonce_is_negated */ + input[0] = input[0] & 1; + /* oddness */ + input[1] = (input[1] % 2) + 2; + i++; + } while(i < count); +} + void random_sign(secp256k1_scalar *sigr, secp256k1_scalar *sigs, const secp256k1_scalar *key, const secp256k1_scalar *msg, int *recid) { secp256k1_scalar nonce; do { @@ -5464,6 +5517,8 @@ int main(int argc, char **argv) { /* EC key edge cases */ run_eckey_edge_case_test(); + run_s2c_opening_test(); + #ifdef ENABLE_MODULE_ECDH /* ecdh tests */ run_ecdh_tests(); From 9037bcf5e8321c9e638f910b680715c61add7db9 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Thu, 4 Jul 2019 22:04:00 +0000 Subject: [PATCH 5/7] Allow creating and verifying Schnorr sign-to-contract commitments --- include/secp256k1_schnorrsig.h | 31 ++++++- src/bench_schnorrsig.c | 4 +- src/modules/schnorrsig/main_impl.h | 65 ++++++++++++- src/modules/schnorrsig/tests_impl.h | 138 +++++++++++++++++++++++++--- 4 files changed, 220 insertions(+), 18 deletions(-) diff --git a/include/secp256k1_schnorrsig.h b/include/secp256k1_schnorrsig.h index 77a9f0bc95..773f9042a9 100644 --- a/include/secp256k1_schnorrsig.h +++ b/include/secp256k1_schnorrsig.h @@ -67,19 +67,28 @@ SECP256K1_API int secp256k1_schnorrsig_parse( * Returns 1 on success, 0 on failure. * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL) * Out: sig: pointer to the returned signature (cannot be NULL) - * In: msg32: the 32-byte message being signed (cannot be NULL) + * s2c_opening: pointer to an secp256k1_s2c_opening structure which can be + * NULL but is required to be not NULL if this signature creates + * a sign-to-contract commitment (i.e. the `s2c_data` argument + * is not NULL). + * In: msg32: the 32-byte message hash being signed (cannot be NULL) * seckey: pointer to a 32-byte secret key (cannot be NULL) + * s2c_data32: pointer to a 32-byte data to create an optional + * sign-to-contract commitment to if not NULL (can be NULL). * noncefp: pointer to a nonce generation function. If NULL, secp256k1_nonce_function_bipschnorr is used - * ndata: pointer to arbitrary data used by the nonce generation function (can be NULL) + * ndata: pointer to arbitrary data used by the nonce generation function. If s2c_data is not NULL, + * nust be NULL or `secp256k1_nonce_function_bipschnorr` (can be NULL) */ SECP256K1_API int secp256k1_schnorrsig_sign( const secp256k1_context* ctx, secp256k1_schnorrsig *sig, + secp256k1_s2c_opening *s2c_opening, const unsigned char *msg32, const unsigned char *seckey, + const unsigned char *s2c_data32, secp256k1_nonce_function noncefp, void *ndata -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); /** Verify a Schnorr signature. * @@ -119,6 +128,22 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_verify_batch size_t n_sigs ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); +/** Verify a sign-to-contract commitment. + * + * Returns: 1: the signature contains a commitment to data32 + * 0: incorrect opening + * Args: ctx: a secp256k1 context object, initialized for verification. + * In: sig: the signature containing the sign-to-contract commitment (cannot be NULL) + * data32: the 32-byte data that was committed to (cannot be NULL) + * opening: pointer to the opening created during signing (cannot be NULL) + */ +SECP256K1_API int secp256k1_schnorrsig_verify_s2c_commit( + const secp256k1_context* ctx, + const secp256k1_schnorrsig *sig, + const unsigned char *data32, + const secp256k1_s2c_opening *opening +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + #ifdef __cplusplus } #endif diff --git a/src/bench_schnorrsig.c b/src/bench_schnorrsig.c index b68adf5312..9803f8e5ef 100644 --- a/src/bench_schnorrsig.c +++ b/src/bench_schnorrsig.c @@ -35,7 +35,7 @@ void bench_schnorrsig_sign(void* arg) { msg[1] = i >> 8; sk[0] = i; sk[1] = i >> 8; - CHECK(secp256k1_schnorrsig_sign(data->ctx, &sig, msg, sk, NULL, NULL)); + CHECK(secp256k1_schnorrsig_sign(data->ctx, &sig, NULL, msg, sk, NULL, NULL, NULL)); } } @@ -100,7 +100,7 @@ int main(void) { CHECK(secp256k1_ec_pubkey_create(data.ctx, &pk, sk)); CHECK(secp256k1_ec_pubkey_serialize(data.ctx, pk_char, &pk_len, &pk, SECP256K1_EC_COMPRESSED) == 1); - CHECK(secp256k1_schnorrsig_sign(data.ctx, sig, msg, sk, NULL, NULL)); + CHECK(secp256k1_schnorrsig_sign(data.ctx, sig, NULL, msg, sk, NULL, NULL, NULL)); } run_benchmark("schnorrsig_sign", bench_schnorrsig_sign, NULL, NULL, (void *) &data, 10, 1000); diff --git a/src/modules/schnorrsig/main_impl.h b/src/modules/schnorrsig/main_impl.h index 739e98b0c3..7362156c51 100644 --- a/src/modules/schnorrsig/main_impl.h +++ b/src/modules/schnorrsig/main_impl.h @@ -29,7 +29,31 @@ int secp256k1_schnorrsig_parse(const secp256k1_context* ctx, secp256k1_schnorrsi return 1; } -int secp256k1_schnorrsig_sign(const secp256k1_context* ctx, secp256k1_schnorrsig *sig, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, void *ndata) { +int secp256k1_schnorrsig_verify_s2c_commit(const secp256k1_context* ctx, const secp256k1_schnorrsig *sig, const unsigned char *data32, const secp256k1_s2c_opening *opening) { + secp256k1_fe rx; + secp256k1_ge R; + secp256k1_pubkey pubnonce; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(data32 != NULL); + ARG_CHECK(opening != NULL); + ARG_CHECK(secp256k1_s2c_commit_is_init(opening)); + + if (!secp256k1_fe_set_b32(&rx, &sig->data[0])) { + return 0; + } + if (!secp256k1_ge_set_xquad(&R, &rx)) { + return 0; + } + if (opening->nonce_is_negated) { + secp256k1_ge_neg(&R, &R); + } + secp256k1_pubkey_save(&pubnonce, &R); + return secp256k1_ec_commit_verify(ctx, &pubnonce, &opening->original_pubnonce, data32, 32); +} + +int secp256k1_schnorrsig_sign(const secp256k1_context* ctx, secp256k1_schnorrsig *sig, secp256k1_s2c_opening *s2c_opening, const unsigned char *msg32, const unsigned char *seckey, const unsigned char *s2c_data32, secp256k1_nonce_function noncefp, void *ndata) { secp256k1_scalar x; secp256k1_scalar e; secp256k1_scalar k; @@ -41,12 +65,19 @@ int secp256k1_schnorrsig_sign(const secp256k1_context* ctx, secp256k1_schnorrsig int overflow; unsigned char buf[33]; size_t buflen = sizeof(buf); + unsigned char noncedata[32]; VERIFY_CHECK(ctx != NULL); ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); ARG_CHECK(sig != NULL); ARG_CHECK(msg32 != NULL); ARG_CHECK(seckey != NULL); + /* sign-to-contract commitments only work with the default nonce function, + * because we need to ensure that s2c_data is actually hashed into the nonce and + * not just ignored because otherwise this could result in nonce reuse. */ + ARG_CHECK(s2c_data32 == NULL || (noncefp == NULL || noncefp == secp256k1_nonce_function_bipschnorr)); + /* s2c_opening and s2c_data32 should be either both non-NULL or both NULL. */ + ARG_CHECK((s2c_opening != NULL) == (s2c_data32 != NULL)); if (noncefp == NULL) { noncefp = secp256k1_nonce_function_bipschnorr; @@ -61,6 +92,24 @@ int secp256k1_schnorrsig_sign(const secp256k1_context* ctx, secp256k1_schnorrsig secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pkj, &x); secp256k1_ge_set_gej(&pk, &pkj); + if (s2c_data32 != NULL) { + /* Provide s2c_data32 and ndata (if not NULL) to the the nonce function + * as additional data to derive the nonce from. If both pointers are + * not NULL, they need to be hashed to get the nonce data 32 bytes. + * Even if only s2c_data32 is not NULL, it's hashed because it should + * be possible to derive nonces even if only a SHA256 commitment to the + * data is known. This is for example important in the anti nonce + * sidechannel protocol. + */ + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, s2c_data32, 32); + if (ndata != NULL) { + secp256k1_sha256_write(&sha, ndata, 32); + } + secp256k1_sha256_finalize(&sha, noncedata); + ndata = &noncedata; + } + if (!noncefp(buf, msg32, seckey, NULL, (void*)ndata, 0)) { return 0; } @@ -72,8 +121,22 @@ int secp256k1_schnorrsig_sign(const secp256k1_context* ctx, secp256k1_schnorrsig secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &k); secp256k1_ge_set_gej(&r, &rj); + if (s2c_opening != NULL) { + secp256k1_s2c_opening_init(s2c_opening); + if (s2c_data32 != NULL) { + /* Create sign-to-contract commitment */ + secp256k1_pubkey_save(&s2c_opening->original_pubnonce, &r); + secp256k1_ec_commit_seckey(ctx, buf, &s2c_opening->original_pubnonce, s2c_data32, 32); + secp256k1_scalar_set_b32(&k, buf, NULL); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &k); + secp256k1_ge_set_gej(&r, &rj); + } + } if (!secp256k1_fe_is_quad_var(&r.y)) { secp256k1_scalar_negate(&k, &k); + if (s2c_opening != NULL) { + s2c_opening->nonce_is_negated = 1; + } } secp256k1_fe_normalize(&r.x); secp256k1_fe_get_b32(&sig->data[0], &r.x); diff --git a/src/modules/schnorrsig/tests_impl.h b/src/modules/schnorrsig/tests_impl.h index 2de43af1e3..9559f603ae 100644 --- a/src/modules/schnorrsig/tests_impl.h +++ b/src/modules/schnorrsig/tests_impl.h @@ -25,12 +25,16 @@ void test_schnorrsig_api(secp256k1_scratch_space *scratch) { unsigned char sk2[32]; unsigned char sk3[32]; unsigned char msg[32]; + unsigned char data32[32]; + unsigned char s2c_data32[32]; unsigned char sig64[64]; secp256k1_pubkey pk[3]; secp256k1_schnorrsig sig; const secp256k1_schnorrsig *sigptr = &sig; const unsigned char *msgptr = msg; const secp256k1_pubkey *pkptr = &pk[0]; + secp256k1_s2c_opening s2c_opening; + unsigned char ones[32]; /** setup **/ secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); @@ -38,6 +42,7 @@ void test_schnorrsig_api(secp256k1_scratch_space *scratch) { secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); secp256k1_context *both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); int ecount; + memset(ones, 0xff, 32); secp256k1_context_set_error_callback(none, counting_illegal_callback_fn, &ecount); secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, &ecount); @@ -52,24 +57,36 @@ void test_schnorrsig_api(secp256k1_scratch_space *scratch) { secp256k1_rand256(sk2); secp256k1_rand256(sk3); secp256k1_rand256(msg); + secp256k1_rand256(s2c_data32); CHECK(secp256k1_ec_pubkey_create(ctx, &pk[0], sk1) == 1); CHECK(secp256k1_ec_pubkey_create(ctx, &pk[1], sk2) == 1); CHECK(secp256k1_ec_pubkey_create(ctx, &pk[2], sk3) == 1); /** main test body **/ ecount = 0; - CHECK(secp256k1_schnorrsig_sign(none, &sig, msg, sk1, NULL, NULL) == 0); + CHECK(secp256k1_schnorrsig_sign(none, &sig, &s2c_opening, msg, sk1, s2c_data32, NULL, NULL) == 0); CHECK(ecount == 1); - CHECK(secp256k1_schnorrsig_sign(vrfy, &sig, msg, sk1, NULL, NULL) == 0); + CHECK(secp256k1_schnorrsig_sign(vrfy, &sig, &s2c_opening, msg, sk1, s2c_data32, NULL, NULL) == 0); CHECK(ecount == 2); - CHECK(secp256k1_schnorrsig_sign(sign, &sig, msg, sk1, NULL, NULL) == 1); + CHECK(secp256k1_schnorrsig_sign(sign, &sig, &s2c_opening, msg, sk1, s2c_data32, NULL, NULL) == 1); CHECK(ecount == 2); - CHECK(secp256k1_schnorrsig_sign(sign, NULL, msg, sk1, NULL, NULL) == 0); + CHECK(secp256k1_schnorrsig_sign(sign, NULL, &s2c_opening, msg, sk1, s2c_data32, NULL, NULL) == 0); CHECK(ecount == 3); - CHECK(secp256k1_schnorrsig_sign(sign, &sig, NULL, sk1, NULL, NULL) == 0); + CHECK(secp256k1_schnorrsig_sign(sign, &sig, NULL, msg, sk1, s2c_data32, NULL, NULL) == 0); CHECK(ecount == 4); - CHECK(secp256k1_schnorrsig_sign(sign, &sig, msg, NULL, NULL, NULL) == 0); + CHECK(secp256k1_schnorrsig_sign(sign, &sig, &s2c_opening, NULL, sk1, s2c_data32, NULL, NULL) == 0); CHECK(ecount == 5); + CHECK(secp256k1_schnorrsig_sign(sign, &sig, &s2c_opening, msg, NULL, s2c_data32, NULL, NULL) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_schnorrsig_sign(sign, &sig, &s2c_opening, msg, sk1, NULL, NULL, NULL) == 0); + CHECK(ecount == 7); + /* It's okay if both s2c_opening and s2c_data32 are NULL. It's just not okay if + * only a single one of them is NULL. */ + CHECK(secp256k1_schnorrsig_sign(sign, &sig, NULL, msg, sk1, NULL, NULL, NULL) == 1); + CHECK(ecount == 7); + /* s2c commitments with a different nonce function than bipschnorr are not allowed */ + CHECK(secp256k1_schnorrsig_sign(sign, &sig, &s2c_opening, msg, sk1, s2c_data32, secp256k1_nonce_function_rfc6979, NULL) == 0); + CHECK(ecount == 8); ecount = 0; CHECK(secp256k1_schnorrsig_serialize(none, sig64, &sig) == 1); @@ -85,6 +102,36 @@ void test_schnorrsig_api(secp256k1_scratch_space *scratch) { CHECK(secp256k1_schnorrsig_parse(none, &sig, NULL) == 0); CHECK(ecount == 4); + /* Create sign-to-contract commitment to data32 for testing verify_s2c_commit */ + secp256k1_rand256(data32); + CHECK(secp256k1_schnorrsig_sign(sign, &sig, &s2c_opening, msg, sk1, data32, NULL, NULL) == 1); + ecount = 0; + CHECK(secp256k1_schnorrsig_verify_s2c_commit(none, &sig, data32, &s2c_opening) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_schnorrsig_verify_s2c_commit(vrfy, &sig, data32, &s2c_opening) == 1); + CHECK(ecount == 1); + { + /* Overflowing x-coordinate in signature */ + secp256k1_schnorrsig sig_tmp = sig; + memcpy(&sig_tmp.data[0], ones, 32); + CHECK(secp256k1_schnorrsig_verify_s2c_commit(vrfy, &sig_tmp, data32, &s2c_opening) == 0); + } + CHECK(secp256k1_schnorrsig_verify_s2c_commit(vrfy, NULL, data32, &s2c_opening) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_verify_s2c_commit(vrfy, &sig, NULL, &s2c_opening) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_schnorrsig_verify_s2c_commit(vrfy, &sig, data32, NULL) == 0); + CHECK(ecount == 4); + { + /* Verification with uninitialized s2c_opening should fail. Actually testing + * that would be undefined behavior. Therefore we simulate it by setting the + * opening to 0. */ + secp256k1_s2c_opening s2c_opening_tmp; + memset(&s2c_opening_tmp, 0, sizeof(s2c_opening_tmp)); + CHECK(secp256k1_schnorrsig_verify_s2c_commit(vrfy, &sig, data32, &s2c_opening_tmp) == 0); + CHECK(ecount == 5); + } + ecount = 0; CHECK(secp256k1_schnorrsig_verify(none, &sig, msg, &pk[0]) == 0); CHECK(ecount == 1); @@ -132,7 +179,7 @@ void test_schnorrsig_bip_vectors_check_signing(const unsigned char *sk, const un unsigned char serialized_sig[64]; secp256k1_pubkey pk; - CHECK(secp256k1_schnorrsig_sign(ctx, &sig, msg, sk, NULL, NULL)); + CHECK(secp256k1_schnorrsig_sign(ctx, &sig, NULL, msg, sk, NULL, NULL, NULL)); CHECK(secp256k1_schnorrsig_serialize(ctx, serialized_sig, &sig)); CHECK(memcmp(serialized_sig, expected_sig, 64) == 0); @@ -634,15 +681,16 @@ void test_schnorrsig_sign(void) { secp256k1_schnorrsig sig; memset(sk, 23, sizeof(sk)); - CHECK(secp256k1_schnorrsig_sign(ctx, &sig, msg, sk, NULL, NULL) == 1); + + CHECK(secp256k1_schnorrsig_sign(ctx, &sig, NULL, msg, sk, NULL, NULL, NULL) == 1); /* Overflowing secret key */ memset(sk, 0xFF, sizeof(sk)); - CHECK(secp256k1_schnorrsig_sign(ctx, &sig, msg, sk, NULL, NULL) == 0); + CHECK(secp256k1_schnorrsig_sign(ctx, &sig, NULL, msg, sk, NULL, NULL, NULL) == 0); memset(sk, 23, sizeof(sk)); - CHECK(secp256k1_schnorrsig_sign(ctx, &sig, msg, sk, nonce_function_failing, NULL) == 0); - CHECK(secp256k1_schnorrsig_sign(ctx, &sig, msg, sk, nonce_function_0, NULL) == 0); + CHECK(secp256k1_schnorrsig_sign(ctx, &sig, NULL, msg, sk, NULL, nonce_function_failing, NULL) == 0); + CHECK(secp256k1_schnorrsig_sign(ctx, &sig, NULL, msg, sk, NULL, nonce_function_0, NULL) == 0); } #define N_SIGS 200 @@ -664,7 +712,7 @@ void test_schnorrsig_sign_verify(secp256k1_scratch_space *scratch) { for (i = 0; i < N_SIGS; i++) { secp256k1_rand256(msg[i]); - CHECK(secp256k1_schnorrsig_sign(ctx, &sig[i], msg[i], sk, NULL, NULL)); + CHECK(secp256k1_schnorrsig_sign(ctx, &sig[i], NULL, msg[i], sk, NULL, NULL, NULL)); CHECK(secp256k1_schnorrsig_verify(ctx, &sig[i], msg[i], &pk)); sig_arr[i] = &sig[i]; msg_arr[i] = msg[i]; @@ -706,7 +754,68 @@ void test_schnorrsig_sign_verify(secp256k1_scratch_space *scratch) { } #undef N_SIGS +void test_schnorrsig_s2c_commit_verify(void) { + unsigned char data32[32]; + secp256k1_schnorrsig sig; + secp256k1_s2c_opening s2c_opening; + unsigned char msg[32]; + unsigned char sk[32]; + secp256k1_pubkey pk; + unsigned char noncedata[32]; + + secp256k1_rand256(data32); + secp256k1_rand256(msg); + secp256k1_rand256(sk); + secp256k1_rand256(noncedata); + CHECK(secp256k1_ec_pubkey_create(ctx, &pk, sk) == 1); + + /* Create and verify correct commitment */ + CHECK(secp256k1_schnorrsig_sign(ctx, &sig, &s2c_opening, msg, sk, data32, NULL, noncedata) == 1); + CHECK(secp256k1_schnorrsig_verify(ctx, &sig, msg, &pk)); + CHECK(secp256k1_schnorrsig_verify_s2c_commit(ctx, &sig, data32, &s2c_opening) == 1); + { + /* verify_s2c_commit fails if nonce_is_negated is wrong */ + secp256k1_s2c_opening s2c_opening_tmp; + s2c_opening_tmp = s2c_opening; + s2c_opening_tmp.nonce_is_negated = !s2c_opening.nonce_is_negated; + CHECK(secp256k1_schnorrsig_verify_s2c_commit(ctx, &sig, data32, &s2c_opening_tmp) == 0); + } + { + /* verify_s2c_commit fails if given data does not match committed data */ + unsigned char data32_tmp[32]; + memcpy(data32_tmp, data32, sizeof(data32_tmp)); + data32_tmp[31] ^= 1; + CHECK(secp256k1_schnorrsig_verify_s2c_commit(ctx, &sig, data32_tmp, &s2c_opening) == 0); + } + { + /* verify_s2c_commit fails if signature does not commit to data */ + secp256k1_schnorrsig sig_tmp; + sig_tmp = sig; + secp256k1_rand256(&sig_tmp.data[0]); + CHECK(secp256k1_schnorrsig_verify_s2c_commit(ctx, &sig_tmp, data32, &s2c_opening) == 0); + } + { + /* A commitment to different data creates a different original_pubnonce + * (i.e. data is hashed into the nonce) */ + secp256k1_s2c_opening s2c_opening_tmp; + secp256k1_schnorrsig sig_tmp; + unsigned char data32_tmp[32]; + unsigned char serialized_nonce[33]; + unsigned char serialized_nonce_tmp[33]; + size_t outputlen = 33; + secp256k1_rand256(data32_tmp); + CHECK(secp256k1_schnorrsig_sign(ctx, &sig_tmp, &s2c_opening_tmp, msg, sk, data32_tmp, NULL, NULL) == 1); + CHECK(secp256k1_schnorrsig_verify(ctx, &sig_tmp, msg, &pk)); + CHECK(secp256k1_schnorrsig_verify_s2c_commit(ctx, &sig_tmp, data32_tmp, &s2c_opening_tmp) == 1); + secp256k1_ec_pubkey_serialize(ctx, serialized_nonce, &outputlen, &s2c_opening.original_pubnonce, SECP256K1_EC_COMPRESSED); + secp256k1_ec_pubkey_serialize(ctx, serialized_nonce_tmp, &outputlen, &s2c_opening_tmp.original_pubnonce, SECP256K1_EC_COMPRESSED); + CHECK(outputlen == 33); + CHECK(memcmp(serialized_nonce, serialized_nonce_tmp, outputlen) != 0); + } +} + void run_schnorrsig_tests(void) { + int i; secp256k1_scratch_space *scratch = secp256k1_scratch_space_create(ctx, 1024 * 1024); test_schnorrsig_serialize(); @@ -715,6 +824,11 @@ void run_schnorrsig_tests(void) { test_schnorrsig_sign(); test_schnorrsig_sign_verify(scratch); + for (i = 0; i < count; i++) { + /* Run multiple times to increase probability that the nonce is negated in + * a test. */ + test_schnorrsig_s2c_commit_verify(); + } secp256k1_scratch_space_destroy(ctx, scratch); } From 031ca34a4f44ed674c51d6697d25acd801c9bed7 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 30 Jul 2019 16:34:08 +0000 Subject: [PATCH 6/7] f Add ec_commitments --- src/secp256k1.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/secp256k1.c b/src/secp256k1.c index 808f36011e..557bcc0ed8 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -779,9 +779,8 @@ static int secp256k1_ec_commit_verify(const secp256k1_context* ctx, const secp25 } /* Return commitment == commitment_tmp */ - secp256k1_gej_set_infinity(&pj); secp256k1_pubkey_load(ctx, &p, &commitment_tmp); - secp256k1_gej_add_ge_var(&pj, &pj, &p, NULL); + secp256k1_gej_set_ge(&pj, &p); secp256k1_pubkey_load(ctx, &p, commitment); secp256k1_ge_neg(&p, &p); secp256k1_gej_add_ge_var(&pj, &pj, &p, NULL); From b631248693702b7b95449de185caef0f8c117f6a Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Wed, 31 Jul 2019 08:47:43 +0000 Subject: [PATCH 7/7] f serialize s2c_opening as 33 bytes instead of 34 --- include/secp256k1.h | 8 ++++---- src/secp256k1.c | 37 +++++++++++++++++++++++++++++-------- src/tests.c | 34 +++++++++++++++++++++------------- 3 files changed, 54 insertions(+), 25 deletions(-) diff --git a/include/secp256k1.h b/include/secp256k1.h index 770b700660..1f76985c15 100644 --- a/include/secp256k1.h +++ b/include/secp256k1.h @@ -475,13 +475,13 @@ SECP256K1_API int secp256k1_ecdsa_signature_serialize_compact( * Args: ctx: a secp256k1 context object. * Out: opening: pointer to an opening object. If 1 is returned, it is set to a * parsed version of input. If not, its value is undefined. - * In: input34: pointer to 34-byte array with a serialized opening + * In: input33: pointer to 33-byte array with a serialized opening * */ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_s2c_opening_parse( const secp256k1_context* ctx, secp256k1_s2c_opening* opening, - const unsigned char *input34 + const unsigned char *input33 ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); /** Serialize a sign-to-contract opening into a byte sequence. @@ -489,13 +489,13 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_s2c_opening_parse( * Returns: 1 if the opening was successfully serialized. * 0 if the opening was not initializaed. * Args: ctx: a secp256k1 context object. - * Out: output34: pointer to a 34-byte array to place the serialized opening + * Out: output33: pointer to a 33-byte array to place the serialized opening * in. * In: opening: a pointer to an initialized `secp256k1_s2c_opening`. */ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_s2c_opening_serialize( const secp256k1_context* ctx, - unsigned char *output34, + unsigned char *output33, const secp256k1_s2c_opening* opening ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); diff --git a/src/secp256k1.c b/src/secp256k1.c index 557bcc0ed8..525d1d2989 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -798,26 +798,47 @@ static int secp256k1_s2c_commit_is_init(const secp256k1_s2c_opening *opening) { return opening->magic == s2c_opening_magic; } -int secp256k1_s2c_opening_parse(const secp256k1_context* ctx, secp256k1_s2c_opening* opening, const unsigned char *input34) { +/* s2c_opening is serialized as 33 bytes containing the compressed original pubnonce. In addition to + * holding the EVEN or ODD tag, the first byte has the third bit set to 1 if the nonce was negated. + * The remaining bits in the first byte are 0. */ +int secp256k1_s2c_opening_parse(const secp256k1_context* ctx, secp256k1_s2c_opening* opening, const unsigned char *input33) { + unsigned char pk_ser[33]; VERIFY_CHECK(ctx != NULL); ARG_CHECK(opening != NULL); - ARG_CHECK(input34 != NULL); + ARG_CHECK(input33 != NULL); secp256k1_s2c_opening_init(opening); - opening->nonce_is_negated = input34[0]; - return secp256k1_ec_pubkey_parse(ctx, &opening->original_pubnonce, &input34[1], 33); + /* Return 0 if unknown bits are set */ + if ((input33[0] & ~0x06) != 0) { + return 0; + } + /* Read nonce_is_negated bit */ + opening->nonce_is_negated = input33[0] & (1 << 2); + memcpy(pk_ser, input33, sizeof(pk_ser)); + /* Unset nonce_is_negated bit to allow parsing the public key */ + pk_ser[0] &= ~(1 << 2); + return secp256k1_ec_pubkey_parse(ctx, &opening->original_pubnonce, &pk_ser[0], 33); } -int secp256k1_s2c_opening_serialize(const secp256k1_context* ctx, unsigned char *output34, const secp256k1_s2c_opening* opening) { +int secp256k1_s2c_opening_serialize(const secp256k1_context* ctx, unsigned char *output33, const secp256k1_s2c_opening* opening) { size_t outputlen = 33; VERIFY_CHECK(ctx != NULL); - ARG_CHECK(output34 != NULL); + ARG_CHECK(output33 != NULL); ARG_CHECK(opening != NULL); ARG_CHECK(secp256k1_s2c_commit_is_init(opening)); - output34[0] = opening->nonce_is_negated; - return secp256k1_ec_pubkey_serialize(ctx, &output34[1], &outputlen, &opening->original_pubnonce, SECP256K1_EC_COMPRESSED); + if (!secp256k1_ec_pubkey_serialize(ctx, &output33[0], &outputlen, &opening->original_pubnonce, SECP256K1_EC_COMPRESSED)) { + return 0; + } + /* Verify that ec_pubkey_serialize only sets the first two bits of the + * first byte, otherwise this function doesn't make any sense */ + VERIFY_CHECK(output33[0] == 0x02 || output33[0] == 0x03); + if (opening->nonce_is_negated) { + /* Set nonce_is_negated bit */ + output33[0] |= (1 << 2); + } + return 1; } #ifdef ENABLE_MODULE_ECDH diff --git a/src/tests.c b/src/tests.c index 0c57da4d3b..5cf81ac35b 100644 --- a/src/tests.c +++ b/src/tests.c @@ -4293,13 +4293,13 @@ void run_eckey_edge_case_test(void) { secp256k1_context_set_illegal_callback(ctx, NULL, NULL); } - void run_s2c_opening_test(void) { int i = 0; - unsigned char output[34]; - unsigned char input[34] = { - 0x01, - 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + unsigned char output[33]; + /* First byte 0x06 means that nonce_is_negated and EVEN tag for the + * following compressed pubkey (which is valid). */ + unsigned char input[33] = { + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -4320,12 +4320,20 @@ void run_s2c_opening_test(void) { /* First parsing, then serializing works */ CHECK(secp256k1_s2c_opening_parse(ctx, &opening, input) == 1); CHECK(secp256k1_s2c_opening_serialize(ctx, output, &opening) == 1); + CHECK(secp256k1_s2c_opening_parse(ctx, &opening, input) == 1); { /* Invalid pubkey makes parsing fail */ - unsigned char input_tmp[34]; + unsigned char input_tmp[33]; memcpy(input_tmp, input, sizeof(input_tmp)); - input_tmp[33] = 0; + /* Pubkey oddness tag is invalid */ + input_tmp[0] = 0; + CHECK(secp256k1_s2c_opening_parse(ctx, &opening, input_tmp) == 0); + /* nonce_is_negated bit is set but pubkey oddness tag is invalid */ + input_tmp[0] = 5; + CHECK(secp256k1_s2c_opening_parse(ctx, &opening, input_tmp) == 0); + /* Unknown bit is set */ + input_tmp[0] = 8; CHECK(secp256k1_s2c_opening_parse(ctx, &opening, input_tmp) == 0); } @@ -4335,13 +4343,13 @@ void run_s2c_opening_test(void) { * points' x-coordinates are uniformly random */ if (secp256k1_s2c_opening_parse(ctx, &opening, input) == 1) { CHECK(secp256k1_s2c_opening_serialize(ctx, output, &opening) == 1); - CHECK(memcmp(output, input, 34) == 0); + CHECK(memcmp(output, input, sizeof(output)) == 0); } - secp256k1_rand256(input); - /* nonce_is_negated */ - input[0] = input[0] & 1; - /* oddness */ - input[1] = (input[1] % 2) + 2; + secp256k1_rand256(&input[1]); + /* Set pubkey oddness tag to first bit of input[1] */ + input[0] = (input[1] & 1) + 2; + /* Set nonce_is_negated bit to input[1]'s 3rd bit */ + input[0] |= (input[1] & (1 << 2)); i++; } while(i < count); }