Skip to content

Commit 0440945

Browse files
Merge #844: schnorrsig API overhaul
5f6ceaf schnorrsig: allow setting MSGLEN != 32 in benchmark (Jonas Nick) fdd06b7 schnorrsig: add tests for sign_custom and varlen msg verification (Jonas Nick) d8d806a schnorrsig: add extra parameter struct for sign_custom (Jonas Nick) a0c3fc1 schnorrsig: allow signing and verification of variable length msgs (Jonas Nick) 5a8e499 Add secp256k1_tagged_sha256 as defined in BIP-340 (Jonas Nick) b6c0b72 schnorrsig: remove noncefp args from sign; add sign_custom function (Jonas Nick) 442cee5 schnorrsig: add algolen argument to nonce_function_hardened (Jonas Nick) df3bfa1 schnorrsig: clarify result of calling nonce_function_bip340 without data (Jonas Nick) 99e8614 README: mention schnorrsig module (Jonas Nick) Pull request description: This is a work in progress because I wanted to put this up for discussion before writing tests. It addresses the TODOs that didn't make it in the schnorrsig PR and changes the APIs of `schnorrsig_sign`, `schnorrsig_verify` and `hardened_nonce_function`. - Ideally, the new `aux_rand32` argument for `sign` would be const, but didn't find a solution I was happy with. - Support for variable length message signing and verification supports the [suggested BIP amendment](sipa/bips#207 (comment)) for such messages. - ~~`sign_custom` with its opaque config object allows adding more arguments later without having to change the API again. Perhaps there are other sensible customization options, but I'm thinking of [sign-to-contract/covert-channel](#590) in particular. It would require adding the fields `unsigned char *s2c_data32` and `secp256k1_s2c_opening *s2c_opening` to the config struct. The former is the data to commit to and the latter is written to by `sign_custom`.~~ (EDIT: see below) ACKs for top commit: ariard: utACK 5f6ceaf LLFourn: utACK 5f6ceaf Tree-SHA512: cf1716dddf4f29bcacf542ed22622a817d0ec9c20d0592333cb7e6105902c77d819952e776b9407fae1333cbd03d63fded492d3a5df7769dcc5b450d91bb4761
2 parents ec3aaa5 + 5f6ceaf commit 0440945

10 files changed

+380
-138
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Features:
1717
* Suitable for embedded systems.
1818
* Optional module for public key recovery.
1919
* Optional module for ECDH key exchange.
20+
* Optional module for Schnorr signatures according to [BIP-340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki) (experimental).
2021

2122
Experimental features have not received enough scrutiny to satisfy the standard of quality of this library but are made available for testing and review by the community. The APIs of these features should not be considered stable.
2223

include/secp256k1.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -793,6 +793,31 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_combine(
793793
size_t n
794794
) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
795795

796+
/** Compute a tagged hash as defined in BIP-340.
797+
*
798+
* This is useful for creating a message hash and achieving domain separation
799+
* through an application-specific tag. This function returns
800+
* SHA256(SHA256(tag)||SHA256(tag)||msg). Therefore, tagged hash
801+
* implementations optimized for a specific tag can precompute the SHA256 state
802+
* after hashing the tag hashes.
803+
*
804+
* Returns 0 if the arguments are invalid and 1 otherwise.
805+
* Args: ctx: pointer to a context object
806+
* Out: hash32: pointer to a 32-byte array to store the resulting hash
807+
* In: tag: pointer to an array containing the tag
808+
* taglen: length of the tag array
809+
* msg: pointer to an array containing the message
810+
* msglen: length of the message array
811+
*/
812+
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_tagged_sha256(
813+
const secp256k1_context* ctx,
814+
unsigned char *hash32,
815+
const unsigned char *tag,
816+
size_t taglen,
817+
const unsigned char *msg,
818+
size_t msglen
819+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5);
820+
796821
#ifdef __cplusplus
797822
}
798823
#endif

include/secp256k1_schnorrsig.h

Lines changed: 87 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -23,24 +23,29 @@ extern "C" {
2323
*
2424
* Returns: 1 if a nonce was successfully generated. 0 will cause signing to
2525
* return an error.
26-
* Out: nonce32: pointer to a 32-byte array to be filled by the function.
27-
* In: msg32: the 32-byte message hash being verified (will not be NULL)
28-
* key32: pointer to a 32-byte secret key (will not be NULL)
29-
* xonly_pk32: the 32-byte serialized xonly pubkey corresponding to key32
30-
* (will not be NULL)
31-
* algo16: pointer to a 16-byte array describing the signature
32-
* algorithm (will not be NULL).
33-
* data: Arbitrary data pointer that is passed through.
26+
* Out: nonce32: pointer to a 32-byte array to be filled by the function
27+
* In: msg: the message being verified. Is NULL if and only if msglen
28+
* is 0.
29+
* msglen: the length of the message
30+
* key32: pointer to a 32-byte secret key (will not be NULL)
31+
* xonly_pk32: the 32-byte serialized xonly pubkey corresponding to key32
32+
* (will not be NULL)
33+
* algo: pointer to an array describing the signature
34+
* algorithm (will not be NULL)
35+
* algolen: the length of the algo array
36+
* data: arbitrary data pointer that is passed through
3437
*
3538
* Except for test cases, this function should compute some cryptographic hash of
3639
* the message, the key, the pubkey, the algorithm description, and data.
3740
*/
3841
typedef int (*secp256k1_nonce_function_hardened)(
3942
unsigned char *nonce32,
40-
const unsigned char *msg32,
43+
const unsigned char *msg,
44+
size_t msglen,
4145
const unsigned char *key32,
4246
const unsigned char *xonly_pk32,
43-
const unsigned char *algo16,
47+
const unsigned char *algo,
48+
size_t algolen,
4449
void *data
4550
);
4651

@@ -50,59 +55,113 @@ typedef int (*secp256k1_nonce_function_hardened)(
5055
*
5156
* If a data pointer is passed, it is assumed to be a pointer to 32 bytes of
5257
* auxiliary random data as defined in BIP-340. If the data pointer is NULL,
53-
* schnorrsig_sign does not produce BIP-340 compliant signatures. The algo16
54-
* argument must be non-NULL, otherwise the function will fail and return 0.
55-
* The hash will be tagged with algo16 after removing all terminating null
56-
* bytes. Therefore, to create BIP-340 compliant signatures, algo16 must be set
57-
* to "BIP0340/nonce\0\0\0"
58+
* the nonce derivation procedure follows BIP-340 by setting the auxiliary
59+
* random data to zero. The algo argument must be non-NULL, otherwise the
60+
* function will fail and return 0. The hash will be tagged with algo.
61+
* Therefore, to create BIP-340 compliant signatures, algo must be set to
62+
* "BIP0340/nonce" and algolen to 13.
5863
*/
5964
SECP256K1_API extern const secp256k1_nonce_function_hardened secp256k1_nonce_function_bip340;
6065

66+
/** Data structure that contains additional arguments for schnorrsig_sign_custom.
67+
*
68+
* A schnorrsig_extraparams structure object can be initialized correctly by
69+
* setting it to SECP256K1_SCHNORRSIG_EXTRAPARAMS_INIT.
70+
*
71+
* Members:
72+
* magic: set to SECP256K1_SCHNORRSIG_EXTRAPARAMS_MAGIC at initialization
73+
* and has no other function than making sure the object is
74+
* initialized.
75+
* noncefp: pointer to a nonce generation function. If NULL,
76+
* secp256k1_nonce_function_bip340 is used
77+
* ndata: pointer to arbitrary data used by the nonce generation function
78+
* (can be NULL). If it is non-NULL and
79+
* secp256k1_nonce_function_bip340 is used, then ndata must be a
80+
* pointer to 32-byte auxiliary randomness as per BIP-340.
81+
*/
82+
typedef struct {
83+
unsigned char magic[4];
84+
secp256k1_nonce_function_hardened noncefp;
85+
void* ndata;
86+
} secp256k1_schnorrsig_extraparams;
87+
88+
#define SECP256K1_SCHNORRSIG_EXTRAPARAMS_MAGIC "\xda\x6f\xb3\x8c"
89+
#define SECP256K1_SCHNORRSIG_EXTRAPARAMS_INIT {\
90+
SECP256K1_SCHNORRSIG_EXTRAPARAMS_MAGIC,\
91+
NULL,\
92+
NULL\
93+
}
94+
6195
/** Create a Schnorr signature.
6296
*
6397
* Does _not_ strictly follow BIP-340 because it does not verify the resulting
6498
* signature. Instead, you can manually use secp256k1_schnorrsig_verify and
6599
* abort if it fails.
66100
*
67-
* Otherwise BIP-340 compliant if the noncefp argument is NULL or
68-
* secp256k1_nonce_function_bip340 and the ndata argument is 32-byte auxiliary
69-
* randomness.
101+
* This function only signs 32-byte messages. If you have messages of a
102+
* different size (or the same size but without a context-specific tag
103+
* prefix), it is recommended to create a 32-byte message hash with
104+
* secp256k1_tagged_sha256 and then sign the hash. Tagged hashing allows
105+
* providing an context-specific tag for domain separation. This prevents
106+
* signatures from being valid in multiple contexts by accident.
70107
*
71108
* Returns 1 on success, 0 on failure.
72109
* Args: ctx: pointer to a context object, initialized for signing (cannot be NULL)
73110
* Out: sig64: pointer to a 64-byte array to store the serialized signature (cannot be NULL)
74111
* In: msg32: the 32-byte message being signed (cannot be NULL)
75112
* keypair: pointer to an initialized keypair (cannot be NULL)
76-
* noncefp: pointer to a nonce generation function. If NULL, secp256k1_nonce_function_bip340 is used
77-
* ndata: pointer to arbitrary data used by the nonce generation
78-
* function (can be NULL). If it is non-NULL and
79-
* secp256k1_nonce_function_bip340 is used, then ndata must be a
80-
* pointer to 32-byte auxiliary randomness as per BIP-340.
113+
* aux_rand32: 32 bytes of fresh randomness. While recommended to provide
114+
* this, it is only supplemental to security and can be NULL. See
115+
* BIP-340 "Default Signing" for a full explanation of this
116+
* argument and for guidance if randomness is expensive.
81117
*/
82118
SECP256K1_API int secp256k1_schnorrsig_sign(
83119
const secp256k1_context* ctx,
84120
unsigned char *sig64,
85121
const unsigned char *msg32,
86122
const secp256k1_keypair *keypair,
87-
secp256k1_nonce_function_hardened noncefp,
88-
void *ndata
123+
unsigned char *aux_rand32
89124
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
90125

126+
/** Create a Schnorr signature with a more flexible API.
127+
*
128+
* Same arguments as secp256k1_schnorrsig_sign except that it allows signing
129+
* variable length messages and accepts a pointer to an extraparams object that
130+
* allows customizing signing by passing additional arguments.
131+
*
132+
* Creates the same signatures as schnorrsig_sign if msglen is 32 and the
133+
* extraparams.ndata is the same as aux_rand32.
134+
*
135+
* In: msg: the message being signed. Can only be NULL if msglen is 0.
136+
* msglen: length of the message
137+
* extraparams: pointer to a extraparams object (can be NULL)
138+
*/
139+
SECP256K1_API int secp256k1_schnorrsig_sign_custom(
140+
const secp256k1_context* ctx,
141+
unsigned char *sig64,
142+
const unsigned char *msg,
143+
size_t msglen,
144+
const secp256k1_keypair *keypair,
145+
secp256k1_schnorrsig_extraparams *extraparams
146+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(5);
147+
91148
/** Verify a Schnorr signature.
92149
*
93150
* Returns: 1: correct signature
94151
* 0: incorrect signature
95152
* Args: ctx: a secp256k1 context object, initialized for verification.
96153
* In: sig64: pointer to the 64-byte signature to verify (cannot be NULL)
97-
* msg32: the 32-byte message being verified (cannot be NULL)
154+
* msg: the message being verified. Can only be NULL if msglen is 0.
155+
* msglen: length of the message
98156
* pubkey: pointer to an x-only public key to verify with (cannot be NULL)
99157
*/
100158
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_verify(
101159
const secp256k1_context* ctx,
102160
const unsigned char *sig64,
103-
const unsigned char *msg32,
161+
const unsigned char *msg,
162+
size_t msglen,
104163
const secp256k1_xonly_pubkey *pubkey
105-
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
164+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(5);
106165

107166
#ifdef __cplusplus
108167
}

src/bench_schnorrsig.c

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
#include "util.h"
1414
#include "bench.h"
1515

16+
#define MSGLEN 32
17+
1618
typedef struct {
1719
secp256k1_context *ctx;
1820
int n;
@@ -26,13 +28,13 @@ typedef struct {
2628
void bench_schnorrsig_sign(void* arg, int iters) {
2729
bench_schnorrsig_data *data = (bench_schnorrsig_data *)arg;
2830
int i;
29-
unsigned char msg[32] = "benchmarkexamplemessagetemplate";
31+
unsigned char msg[MSGLEN] = {0};
3032
unsigned char sig[64];
3133

3234
for (i = 0; i < iters; i++) {
3335
msg[0] = i;
3436
msg[1] = i >> 8;
35-
CHECK(secp256k1_schnorrsig_sign(data->ctx, sig, msg, data->keypairs[i], NULL, NULL));
37+
CHECK(secp256k1_schnorrsig_sign_custom(data->ctx, sig, msg, MSGLEN, data->keypairs[i], NULL));
3638
}
3739
}
3840

@@ -43,7 +45,7 @@ void bench_schnorrsig_verify(void* arg, int iters) {
4345
for (i = 0; i < iters; i++) {
4446
secp256k1_xonly_pubkey pk;
4547
CHECK(secp256k1_xonly_pubkey_parse(data->ctx, &pk, data->pk[i]) == 1);
46-
CHECK(secp256k1_schnorrsig_verify(data->ctx, data->sigs[i], data->msgs[i], &pk));
48+
CHECK(secp256k1_schnorrsig_verify(data->ctx, data->sigs[i], data->msgs[i], MSGLEN, &pk));
4749
}
4850
}
4951

@@ -58,9 +60,10 @@ int main(void) {
5860
data.msgs = (const unsigned char **)malloc(iters * sizeof(unsigned char *));
5961
data.sigs = (const unsigned char **)malloc(iters * sizeof(unsigned char *));
6062

63+
CHECK(MSGLEN >= 4);
6164
for (i = 0; i < iters; i++) {
6265
unsigned char sk[32];
63-
unsigned char *msg = (unsigned char *)malloc(32);
66+
unsigned char *msg = (unsigned char *)malloc(MSGLEN);
6467
unsigned char *sig = (unsigned char *)malloc(64);
6568
secp256k1_keypair *keypair = (secp256k1_keypair *)malloc(sizeof(*keypair));
6669
unsigned char *pk_char = (unsigned char *)malloc(32);
@@ -69,7 +72,7 @@ int main(void) {
6972
msg[1] = sk[1] = i >> 8;
7073
msg[2] = sk[2] = i >> 16;
7174
msg[3] = sk[3] = i >> 24;
72-
memset(&msg[4], 'm', 28);
75+
memset(&msg[4], 'm', MSGLEN - 4);
7376
memset(&sk[4], 's', 28);
7477

7578
data.keypairs[i] = keypair;
@@ -78,7 +81,7 @@ int main(void) {
7881
data.sigs[i] = sig;
7982

8083
CHECK(secp256k1_keypair_create(data.ctx, keypair, sk));
81-
CHECK(secp256k1_schnorrsig_sign(data.ctx, sig, msg, keypair, NULL, NULL));
84+
CHECK(secp256k1_schnorrsig_sign_custom(data.ctx, sig, msg, MSGLEN, keypair, NULL));
8285
CHECK(secp256k1_keypair_xonly_pub(data.ctx, &pk, NULL, keypair));
8386
CHECK(secp256k1_xonly_pubkey_serialize(data.ctx, pk_char, &pk) == 1);
8487
}

0 commit comments

Comments
 (0)