Skip to content

Commit 1ac7e31

Browse files
Merge bitcoin-core/secp256k1#1089: Schnorrsig API improvements
b8f8b99 docs: Fix return value for functions that don't have invalid inputs (Tim Ruffing) f813bb0 schnorrsig: Adapt example to new API (Tim Ruffing) 99e6568 schnorrsig: Rename schnorrsig_sign to schnorsig_sign32 and deprecate (Tim Ruffing) fc94a2d Use SECP256K1_DEPRECATED for existing deprecated API functions (Tim Ruffing) 3db0560 Add SECP256K1_DEPRECATED attribute for marking API parts as deprecated (Tim Ruffing) Pull request description: Should be merged before bitcoin#995 if we want this. I suspect the only change here which is debatable on a conceptual level is the renaming. I can drop this of course. ACKs for top commit: sipa: utACK b8f8b99 jonasnick: ACK b8f8b99 Tree-SHA512: 7c5b9715013002eecbf2e649032673204f6eaffe156f20e3ddf51fab938643847d23068f11b127ef3d7fe759e42a20ecaf2ec98718d901ef9eaadbc9853c1dfe
2 parents 587239d + b8f8b99 commit 1ac7e31

File tree

7 files changed

+92
-45
lines changed

7 files changed

+92
-45
lines changed

examples/schnorr.c

+33-17
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,9 @@
1818
#include "random.h"
1919

2020
int main(void) {
21-
/* Instead of signing the message directly, we must sign a 32-byte hash.
22-
* Here the message is "Hello, world!" and the hash function was SHA-256.
23-
* An actual implementation should just call SHA-256, but this example
24-
* hardcodes the output to avoid depending on an additional library. */
25-
unsigned char msg_hash[32] = {
26-
0x31, 0x5F, 0x5B, 0xDB, 0x76, 0xD0, 0x78, 0xC4,
27-
0x3B, 0x8A, 0xC0, 0x06, 0x4E, 0x4A, 0x01, 0x64,
28-
0x61, 0x2B, 0x1F, 0xCE, 0x77, 0xC8, 0x69, 0x34,
29-
0x5B, 0xFC, 0x94, 0xC7, 0x58, 0x94, 0xED, 0xD3,
30-
};
21+
unsigned char msg[12] = "Hello World!";
22+
unsigned char msg_hash[32];
23+
unsigned char tag[17] = "my_fancy_protocol";
3124
unsigned char seckey[32];
3225
unsigned char randomize[32];
3326
unsigned char auxiliary_rand[32];
@@ -84,19 +77,38 @@ int main(void) {
8477

8578
/*** Signing ***/
8679

80+
/* Instead of signing (possibly very long) messages directly, we sign a
81+
* 32-byte hash of the message in this example.
82+
*
83+
* We use secp256k1_tagged_sha256 to create this hash. This function expects
84+
* a context-specific "tag", which restricts the context in which the signed
85+
* messages should be considered valid. For example, if protocol A mandates
86+
* to use the tag "my_fancy_protocol" and protocol B mandates to use the tag
87+
* "my_boring_protocol", then signed messages from protocol A will never be
88+
* valid in protocol B (and vice versa), even if keys are reused across
89+
* protocols. This implements "domain separation", which is considered good
90+
* practice. It avoids attacks in which users are tricked into signing a
91+
* message that has intended consequences in the intended context (e.g.,
92+
* protocol A) but would have unintended consequences if it were valid in
93+
* some other context (e.g., protocol B). */
94+
return_val = secp256k1_tagged_sha256(ctx, msg_hash, tag, sizeof(tag), msg, sizeof(msg));
95+
assert(return_val);
96+
8797
/* Generate 32 bytes of randomness to use with BIP-340 schnorr signing. */
8898
if (!fill_random(auxiliary_rand, sizeof(auxiliary_rand))) {
8999
printf("Failed to generate randomness\n");
90100
return 1;
91101
}
92102

93-
/* Generate a Schnorr signature `noncefp` and `ndata` allows you to pass a
94-
* custom nonce function, passing `NULL` will use the BIP-340 safe default.
95-
* BIP-340 recommends passing 32 bytes of randomness to the nonce function to
96-
* improve security against side-channel attacks. Signing with a valid
97-
* context, verified keypair and the default nonce function should never
98-
* fail. */
99-
return_val = secp256k1_schnorrsig_sign(ctx, signature, msg_hash, &keypair, auxiliary_rand);
103+
/* Generate a Schnorr signature.
104+
*
105+
* We use the secp256k1_schnorrsig_sign32 function that provides a simple
106+
* interface for signing 32-byte messages (which in our case is a hash of
107+
* the actual message). BIP-340 recommends passing 32 bytes of randomness
108+
* to the signing function to improve security against side-channel attacks.
109+
* Signing with a valid context, a 32-byte message, a verified keypair, and
110+
* any 32 bytes of auxiliary random data should never fail. */
111+
return_val = secp256k1_schnorrsig_sign32(ctx, signature, msg_hash, &keypair, auxiliary_rand);
100112
assert(return_val);
101113

102114
/*** Verification ***/
@@ -108,6 +120,10 @@ int main(void) {
108120
return 1;
109121
}
110122

123+
/* Compute the tagged hash on the received messages using the same tag as the signer. */
124+
return_val = secp256k1_tagged_sha256(ctx, msg_hash, tag, sizeof(tag), msg, sizeof(msg));
125+
assert(return_val);
126+
111127
/* Verify a signature. This will return 1 if it's valid and 0 if it's not. */
112128
is_signature_valid = secp256k1_schnorrsig_verify(ctx, signature, msg_hash, 32, &pubkey);
113129

include/secp256k1.h

+18-4
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,17 @@ typedef int (*secp256k1_nonce_function)(
169169
# define SECP256K1_ARG_NONNULL(_x)
170170
# endif
171171

172+
/** Attribute for marking functions, types, and variables as deprecated */
173+
#if !defined(SECP256K1_BUILD) && defined(__has_attribute)
174+
# if __has_attribute(__deprecated__)
175+
# define SECP256K1_DEPRECATED(_msg) __attribute__ ((__deprecated__(_msg)))
176+
# else
177+
# define SECP256K1_DEPRECATED(_msg)
178+
# endif
179+
#else
180+
# define SECP256K1_DEPRECATED(_msg)
181+
#endif
182+
172183
/** All flags' lower 8 bits indicate what they're for. Do not use directly. */
173184
#define SECP256K1_FLAGS_TYPE_MASK ((1 << 8) - 1)
174185
#define SECP256K1_FLAGS_TYPE_CONTEXT (1 << 0)
@@ -641,7 +652,8 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_negate(
641652
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_negate(
642653
const secp256k1_context* ctx,
643654
unsigned char *seckey
644-
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2);
655+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2)
656+
SECP256K1_DEPRECATED("Use secp256k1_ec_seckey_negate instead");
645657

646658
/** Negates a public key in place.
647659
*
@@ -681,7 +693,8 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_add(
681693
const secp256k1_context* ctx,
682694
unsigned char *seckey,
683695
const unsigned char *tweak32
684-
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
696+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3)
697+
SECP256K1_DEPRECATED("Use secp256k1_ec_seckey_tweak_add instead");
685698

686699
/** Tweak a public key by adding tweak times the generator to it.
687700
*
@@ -727,7 +740,8 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_mul(
727740
const secp256k1_context* ctx,
728741
unsigned char *seckey,
729742
const unsigned char *tweak32
730-
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
743+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3)
744+
SECP256K1_DEPRECATED("Use secp256k1_ec_seckey_tweak_mul instead");
731745

732746
/** Tweak a public key by multiplying it by a tweak value.
733747
*
@@ -800,7 +814,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_combine(
800814
* implementations optimized for a specific tag can precompute the SHA256 state
801815
* after hashing the tag hashes.
802816
*
803-
* Returns 0 if the arguments are invalid and 1 otherwise.
817+
* Returns: 1 always.
804818
* Args: ctx: pointer to a context object
805819
* Out: hash32: pointer to a 32-byte array to store the resulting hash
806820
* In: tag: pointer to an array containing the tag

include/secp256k1_extrakeys.h

+4-5
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,7 @@ SECP256K1_API int secp256k1_xonly_pubkey_cmp(
8181

8282
/** Converts a secp256k1_pubkey into a secp256k1_xonly_pubkey.
8383
*
84-
* Returns: 1 if the public key was successfully converted
85-
* 0 otherwise
84+
* Returns: 1 always.
8685
*
8786
* Args: ctx: pointer to a context object.
8887
* Out: xonly_pubkey: pointer to an x-only public key object for placing the converted public key.
@@ -172,7 +171,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_create(
172171

173172
/** Get the secret key from a keypair.
174173
*
175-
* Returns: 0 if the arguments are invalid. 1 otherwise.
174+
* Returns: 1 always.
176175
* Args: ctx: pointer to a context object.
177176
* Out: seckey: pointer to a 32-byte buffer for the secret key.
178177
* In: keypair: pointer to a keypair.
@@ -185,7 +184,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_sec(
185184

186185
/** Get the public key from a keypair.
187186
*
188-
* Returns: 0 if the arguments are invalid. 1 otherwise.
187+
* Returns: 1 always.
189188
* Args: ctx: pointer to a context object.
190189
* Out: pubkey: pointer to a pubkey object. If 1 is returned, it is set to
191190
* the keypair public key. If not, it's set to an invalid value.
@@ -202,7 +201,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_pub(
202201
* This is the same as calling secp256k1_keypair_pub and then
203202
* secp256k1_xonly_pubkey_from_pubkey.
204203
*
205-
* Returns: 0 if the arguments are invalid. 1 otherwise.
204+
* Returns: 1 always.
206205
* Args: ctx: pointer to a context object.
207206
* Out: pubkey: pointer to an xonly_pubkey object. If 1 is returned, it is set
208207
* to the keypair public key after converting it to an

include/secp256k1_schnorrsig.h

+12-1
Original file line numberDiff line numberDiff line change
@@ -116,14 +116,25 @@ typedef struct {
116116
* BIP-340 "Default Signing" for a full explanation of this
117117
* argument and for guidance if randomness is expensive.
118118
*/
119-
SECP256K1_API int secp256k1_schnorrsig_sign(
119+
SECP256K1_API int secp256k1_schnorrsig_sign32(
120120
const secp256k1_context* ctx,
121121
unsigned char *sig64,
122122
const unsigned char *msg32,
123123
const secp256k1_keypair *keypair,
124124
const unsigned char *aux_rand32
125125
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
126126

127+
/** Same as secp256k1_schnorrsig_sign32, but DEPRECATED. Will be removed in
128+
* future versions. */
129+
SECP256K1_API int secp256k1_schnorrsig_sign(
130+
const secp256k1_context* ctx,
131+
unsigned char *sig64,
132+
const unsigned char *msg32,
133+
const secp256k1_keypair *keypair,
134+
const unsigned char *aux_rand32
135+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4)
136+
SECP256K1_DEPRECATED("Use secp256k1_schnorrsig_sign32 instead");
137+
127138
/** Create a Schnorr signature with a more flexible API.
128139
*
129140
* Same arguments as secp256k1_schnorrsig_sign except that it allows signing

src/modules/schnorrsig/main_impl.h

+5-1
Original file line numberDiff line numberDiff line change
@@ -192,11 +192,15 @@ static int secp256k1_schnorrsig_sign_internal(const secp256k1_context* ctx, unsi
192192
return ret;
193193
}
194194

195-
int secp256k1_schnorrsig_sign(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg32, const secp256k1_keypair *keypair, const unsigned char *aux_rand32) {
195+
int secp256k1_schnorrsig_sign32(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg32, const secp256k1_keypair *keypair, const unsigned char *aux_rand32) {
196196
/* We cast away const from the passed aux_rand32 argument since we know the default nonce function does not modify it. */
197197
return secp256k1_schnorrsig_sign_internal(ctx, sig64, msg32, 32, keypair, secp256k1_nonce_function_bip340, (unsigned char*)aux_rand32);
198198
}
199199

200+
int secp256k1_schnorrsig_sign(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg32, const secp256k1_keypair *keypair, const unsigned char *aux_rand32) {
201+
return secp256k1_schnorrsig_sign32(ctx, sig64, msg32, keypair, aux_rand32);
202+
}
203+
200204
int secp256k1_schnorrsig_sign_custom(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg, size_t msglen, const secp256k1_keypair *keypair, secp256k1_schnorrsig_extraparams *extraparams) {
201205
secp256k1_nonce_function_hardened noncefp = NULL;
202206
void *ndata = NULL;

src/modules/schnorrsig/tests_impl.h

+19-16
Original file line numberDiff line numberDiff line change
@@ -160,21 +160,21 @@ void test_schnorrsig_api(void) {
160160

161161
/** main test body **/
162162
ecount = 0;
163-
CHECK(secp256k1_schnorrsig_sign(none, sig, msg, &keypairs[0], NULL) == 1);
163+
CHECK(secp256k1_schnorrsig_sign32(none, sig, msg, &keypairs[0], NULL) == 1);
164164
CHECK(ecount == 0);
165-
CHECK(secp256k1_schnorrsig_sign(vrfy, sig, msg, &keypairs[0], NULL) == 1);
165+
CHECK(secp256k1_schnorrsig_sign32(vrfy, sig, msg, &keypairs[0], NULL) == 1);
166166
CHECK(ecount == 0);
167-
CHECK(secp256k1_schnorrsig_sign(sign, sig, msg, &keypairs[0], NULL) == 1);
167+
CHECK(secp256k1_schnorrsig_sign32(sign, sig, msg, &keypairs[0], NULL) == 1);
168168
CHECK(ecount == 0);
169-
CHECK(secp256k1_schnorrsig_sign(sign, NULL, msg, &keypairs[0], NULL) == 0);
169+
CHECK(secp256k1_schnorrsig_sign32(sign, NULL, msg, &keypairs[0], NULL) == 0);
170170
CHECK(ecount == 1);
171-
CHECK(secp256k1_schnorrsig_sign(sign, sig, NULL, &keypairs[0], NULL) == 0);
171+
CHECK(secp256k1_schnorrsig_sign32(sign, sig, NULL, &keypairs[0], NULL) == 0);
172172
CHECK(ecount == 2);
173-
CHECK(secp256k1_schnorrsig_sign(sign, sig, msg, NULL, NULL) == 0);
173+
CHECK(secp256k1_schnorrsig_sign32(sign, sig, msg, NULL, NULL) == 0);
174174
CHECK(ecount == 3);
175-
CHECK(secp256k1_schnorrsig_sign(sign, sig, msg, &invalid_keypair, NULL) == 0);
175+
CHECK(secp256k1_schnorrsig_sign32(sign, sig, msg, &invalid_keypair, NULL) == 0);
176176
CHECK(ecount == 4);
177-
CHECK(secp256k1_schnorrsig_sign(sttc, sig, msg, &keypairs[0], NULL) == 0);
177+
CHECK(secp256k1_schnorrsig_sign32(sttc, sig, msg, &keypairs[0], NULL) == 0);
178178
CHECK(ecount == 5);
179179

180180
ecount = 0;
@@ -202,7 +202,7 @@ void test_schnorrsig_api(void) {
202202
CHECK(ecount == 6);
203203

204204
ecount = 0;
205-
CHECK(secp256k1_schnorrsig_sign(sign, sig, msg, &keypairs[0], NULL) == 1);
205+
CHECK(secp256k1_schnorrsig_sign32(sign, sig, msg, &keypairs[0], NULL) == 1);
206206
CHECK(secp256k1_schnorrsig_verify(none, sig, msg, sizeof(msg), &pk[0]) == 1);
207207
CHECK(ecount == 0);
208208
CHECK(secp256k1_schnorrsig_verify(sign, sig, msg, sizeof(msg), &pk[0]) == 1);
@@ -247,7 +247,7 @@ void test_schnorrsig_bip_vectors_check_signing(const unsigned char *sk, const un
247247
secp256k1_xonly_pubkey pk, pk_expected;
248248

249249
CHECK(secp256k1_keypair_create(ctx, &keypair, sk));
250-
CHECK(secp256k1_schnorrsig_sign(ctx, sig, msg32, &keypair, aux_rand));
250+
CHECK(secp256k1_schnorrsig_sign32(ctx, sig, msg32, &keypair, aux_rand));
251251
CHECK(secp256k1_memcmp_var(sig, expected_sig, 64) == 0);
252252

253253
CHECK(secp256k1_xonly_pubkey_parse(ctx, &pk_expected, pk_serialized));
@@ -740,8 +740,11 @@ void test_schnorrsig_sign(void) {
740740
secp256k1_testrand256(aux_rand);
741741
CHECK(secp256k1_keypair_create(ctx, &keypair, sk));
742742
CHECK(secp256k1_keypair_xonly_pub(ctx, &pk, NULL, &keypair));
743-
CHECK(secp256k1_schnorrsig_sign(ctx, sig, msg, &keypair, NULL) == 1);
743+
CHECK(secp256k1_schnorrsig_sign32(ctx, sig, msg, &keypair, NULL) == 1);
744744
CHECK(secp256k1_schnorrsig_verify(ctx, sig, msg, sizeof(msg), &pk));
745+
/* Check that deprecated alias gives the same result */
746+
CHECK(secp256k1_schnorrsig_sign(ctx, sig2, msg, &keypair, NULL) == 1);
747+
CHECK(secp256k1_memcmp_var(sig, sig2, sizeof(sig)) == 0);
745748

746749
/* Test different nonce functions */
747750
CHECK(secp256k1_schnorrsig_sign_custom(ctx, sig, msg, sizeof(msg), &keypair, &extraparams) == 1);
@@ -764,7 +767,7 @@ void test_schnorrsig_sign(void) {
764767
extraparams.noncefp = NULL;
765768
extraparams.ndata = aux_rand;
766769
CHECK(secp256k1_schnorrsig_sign_custom(ctx, sig, msg, sizeof(msg), &keypair, &extraparams) == 1);
767-
CHECK(secp256k1_schnorrsig_sign(ctx, sig2, msg, &keypair, extraparams.ndata) == 1);
770+
CHECK(secp256k1_schnorrsig_sign32(ctx, sig2, msg, &keypair, extraparams.ndata) == 1);
768771
CHECK(secp256k1_memcmp_var(sig, sig2, sizeof(sig)) == 0);
769772
}
770773

@@ -787,7 +790,7 @@ void test_schnorrsig_sign_verify(void) {
787790

788791
for (i = 0; i < N_SIGS; i++) {
789792
secp256k1_testrand256(msg[i]);
790-
CHECK(secp256k1_schnorrsig_sign(ctx, sig[i], msg[i], &keypair, NULL));
793+
CHECK(secp256k1_schnorrsig_sign32(ctx, sig[i], msg[i], &keypair, NULL));
791794
CHECK(secp256k1_schnorrsig_verify(ctx, sig[i], msg[i], sizeof(msg[i]), &pk));
792795
}
793796

@@ -816,13 +819,13 @@ void test_schnorrsig_sign_verify(void) {
816819
}
817820

818821
/* Test overflowing s */
819-
CHECK(secp256k1_schnorrsig_sign(ctx, sig[0], msg[0], &keypair, NULL));
822+
CHECK(secp256k1_schnorrsig_sign32(ctx, sig[0], msg[0], &keypair, NULL));
820823
CHECK(secp256k1_schnorrsig_verify(ctx, sig[0], msg[0], sizeof(msg[0]), &pk));
821824
memset(&sig[0][32], 0xFF, 32);
822825
CHECK(!secp256k1_schnorrsig_verify(ctx, sig[0], msg[0], sizeof(msg[0]), &pk));
823826

824827
/* Test negative s */
825-
CHECK(secp256k1_schnorrsig_sign(ctx, sig[0], msg[0], &keypair, NULL));
828+
CHECK(secp256k1_schnorrsig_sign32(ctx, sig[0], msg[0], &keypair, NULL));
826829
CHECK(secp256k1_schnorrsig_verify(ctx, sig[0], msg[0], sizeof(msg[0]), &pk));
827830
secp256k1_scalar_set_b32(&s, &sig[0][32], NULL);
828831
secp256k1_scalar_negate(&s, &s);
@@ -873,7 +876,7 @@ void test_schnorrsig_taproot(void) {
873876

874877
/* Key spend */
875878
secp256k1_testrand256(msg);
876-
CHECK(secp256k1_schnorrsig_sign(ctx, sig, msg, &keypair, NULL) == 1);
879+
CHECK(secp256k1_schnorrsig_sign32(ctx, sig, msg, &keypair, NULL) == 1);
877880
/* Verify key spend */
878881
CHECK(secp256k1_xonly_pubkey_parse(ctx, &output_pk, output_pk_bytes) == 1);
879882
CHECK(secp256k1_schnorrsig_verify(ctx, sig, msg, sizeof(msg), &output_pk) == 1);

src/valgrind_ctime_test.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ void run_tests(secp256k1_context *ctx, unsigned char *key) {
166166
ret = secp256k1_keypair_create(ctx, &keypair, key);
167167
VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
168168
CHECK(ret == 1);
169-
ret = secp256k1_schnorrsig_sign(ctx, sig, msg, &keypair, NULL);
169+
ret = secp256k1_schnorrsig_sign32(ctx, sig, msg, &keypair, NULL);
170170
VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
171171
CHECK(ret == 1);
172172
#endif

0 commit comments

Comments
 (0)