18
18
#include "random.h"
19
19
20
20
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" ;
31
24
unsigned char seckey [32 ];
32
25
unsigned char randomize [32 ];
33
26
unsigned char auxiliary_rand [32 ];
@@ -84,18 +77,37 @@ int main(void) {
84
77
85
78
/*** Signing ***/
86
79
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
+
87
97
/* Generate 32 bytes of randomness to use with BIP-340 schnorr signing. */
88
98
if (!fill_random (auxiliary_rand , sizeof (auxiliary_rand ))) {
89
99
printf ("Failed to generate randomness\n" );
90
100
return 1 ;
91
101
}
92
102
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. */
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. */
99
111
return_val = secp256k1_schnorrsig_sign32 (ctx , signature , msg_hash , & keypair , auxiliary_rand );
100
112
assert (return_val );
101
113
@@ -108,6 +120,10 @@ int main(void) {
108
120
return 1 ;
109
121
}
110
122
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
+
111
127
/* Verify a signature. This will return 1 if it's valid and 0 if it's not. */
112
128
is_signature_valid = secp256k1_schnorrsig_verify (ctx , signature , msg_hash , 32 , & pubkey );
113
129
0 commit comments