@@ -137,9 +137,184 @@ void test_xonly_pubkey(void) {
137137 secp256k1_context_destroy (verify );
138138}
139139
140+ void test_xonly_pubkey_tweak (void ) {
141+ unsigned char zeros64 [64 ] = { 0 };
142+ unsigned char overflows [32 ];
143+ unsigned char sk [32 ];
144+ secp256k1_pubkey internal_pk ;
145+ secp256k1_xonly_pubkey internal_xonly_pk ;
146+ secp256k1_pubkey output_pk ;
147+ int pk_parity ;
148+ unsigned char tweak [32 ];
149+ int i ;
150+
151+ int ecount ;
152+ secp256k1_context * none = api_test_context (SECP256K1_CONTEXT_NONE , & ecount );
153+ secp256k1_context * sign = api_test_context (SECP256K1_CONTEXT_SIGN , & ecount );
154+ secp256k1_context * verify = api_test_context (SECP256K1_CONTEXT_VERIFY , & ecount );
155+
156+ memset (overflows , 0xff , sizeof (overflows ));
157+ secp256k1_rand256 (tweak );
158+ secp256k1_rand256 (sk );
159+ CHECK (secp256k1_ec_pubkey_create (ctx , & internal_pk , sk ) == 1 );
160+ CHECK (secp256k1_xonly_pubkey_from_pubkey (none , & internal_xonly_pk , & pk_parity , & internal_pk ) == 1 );
161+
162+ ecount = 0 ;
163+ CHECK (secp256k1_xonly_pubkey_tweak_add (none , & output_pk , & internal_xonly_pk , tweak ) == 0 );
164+ CHECK (ecount == 1 );
165+ CHECK (secp256k1_xonly_pubkey_tweak_add (sign , & output_pk , & internal_xonly_pk , tweak ) == 0 );
166+ CHECK (ecount == 2 );
167+ CHECK (secp256k1_xonly_pubkey_tweak_add (verify , & output_pk , & internal_xonly_pk , tweak ) == 1 );
168+ CHECK (secp256k1_xonly_pubkey_tweak_add (verify , NULL , & internal_xonly_pk , tweak ) == 0 );
169+ CHECK (ecount == 3 );
170+ CHECK (secp256k1_xonly_pubkey_tweak_add (verify , & output_pk , NULL , tweak ) == 0 );
171+ CHECK (ecount == 4 );
172+ /* NULL internal_xonly_pk zeroes the output_pk */
173+ CHECK (memcmp (& output_pk , zeros64 , sizeof (output_pk )) == 0 );
174+ CHECK (secp256k1_xonly_pubkey_tweak_add (verify , & output_pk , & internal_xonly_pk , NULL ) == 0 );
175+ CHECK (ecount == 5 );
176+ /* NULL tweak zeroes the output_pk */
177+ CHECK (memcmp (& output_pk , zeros64 , sizeof (output_pk )) == 0 );
178+
179+ /* Invalid tweak zeroes the output_pk */
180+ CHECK (secp256k1_xonly_pubkey_tweak_add (verify , & output_pk , & internal_xonly_pk , overflows ) == 0 );
181+ CHECK (memcmp (& output_pk , zeros64 , sizeof (output_pk )) == 0 );
182+
183+ /* A zero tweak is fine */
184+ CHECK (secp256k1_xonly_pubkey_tweak_add (verify , & output_pk , & internal_xonly_pk , zeros64 ) == 1 );
185+
186+ /* Fails if the resulting key was infinity */
187+ for (i = 0 ; i < count ; i ++ ) {
188+ secp256k1_scalar scalar_tweak ;
189+ /* Because sk may be negated before adding, we need to try with tweak =
190+ * sk as well as tweak = -sk. */
191+ secp256k1_scalar_set_b32 (& scalar_tweak , sk , NULL );
192+ secp256k1_scalar_negate (& scalar_tweak , & scalar_tweak );
193+ secp256k1_scalar_get_b32 (tweak , & scalar_tweak );
194+ CHECK ((secp256k1_xonly_pubkey_tweak_add (verify , & output_pk , & internal_xonly_pk , sk ) == 0 )
195+ || (secp256k1_xonly_pubkey_tweak_add (verify , & output_pk , & internal_xonly_pk , tweak ) == 0 ));
196+ CHECK (memcmp (& output_pk , zeros64 , sizeof (output_pk )) == 0 );
197+ }
198+
199+ /* Invalid pk with a valid tweak */
200+ memset (& internal_xonly_pk , 0 , sizeof (internal_xonly_pk ));
201+ secp256k1_rand256 (tweak );
202+ ecount = 0 ;
203+ CHECK (secp256k1_xonly_pubkey_tweak_add (verify , & output_pk , & internal_xonly_pk , tweak ) == 0 );
204+ CHECK (ecount == 1 );
205+ CHECK (memcmp (& output_pk , zeros64 , sizeof (output_pk )) == 0 );
206+
207+ secp256k1_context_destroy (none );
208+ secp256k1_context_destroy (sign );
209+ secp256k1_context_destroy (verify );
210+ }
211+
212+ void test_xonly_pubkey_tweak_check (void ) {
213+ unsigned char zeros64 [64 ] = { 0 };
214+ unsigned char overflows [32 ];
215+ unsigned char sk [32 ];
216+ secp256k1_pubkey internal_pk ;
217+ secp256k1_xonly_pubkey internal_xonly_pk ;
218+ secp256k1_pubkey output_pk ;
219+ secp256k1_xonly_pubkey output_xonly_pk ;
220+ unsigned char output_pk32 [32 ];
221+ unsigned char buf32 [32 ];
222+ int pk_parity ;
223+ unsigned char tweak [32 ];
224+
225+ int ecount ;
226+ secp256k1_context * none = api_test_context (SECP256K1_CONTEXT_NONE , & ecount );
227+ secp256k1_context * sign = api_test_context (SECP256K1_CONTEXT_SIGN , & ecount );
228+ secp256k1_context * verify = api_test_context (SECP256K1_CONTEXT_VERIFY , & ecount );
229+
230+ memset (overflows , 0xff , sizeof (overflows ));
231+ secp256k1_rand256 (tweak );
232+ secp256k1_rand256 (sk );
233+ CHECK (secp256k1_ec_pubkey_create (ctx , & internal_pk , sk ) == 1 );
234+ CHECK (secp256k1_xonly_pubkey_from_pubkey (none , & internal_xonly_pk , & pk_parity , & internal_pk ) == 1 );
235+
236+ ecount = 0 ;
237+ CHECK (secp256k1_xonly_pubkey_tweak_add (verify , & output_pk , & internal_xonly_pk , tweak ) == 1 );
238+ CHECK (secp256k1_xonly_pubkey_from_pubkey (verify , & output_xonly_pk , & pk_parity , & output_pk ) == 1 );
239+ CHECK (secp256k1_xonly_pubkey_serialize (ctx , buf32 , & output_xonly_pk ) == 1 );
240+ CHECK (secp256k1_xonly_pubkey_tweak_add_check (none , buf32 , pk_parity , & internal_xonly_pk , tweak ) == 0 );
241+ CHECK (ecount == 1 );
242+ CHECK (secp256k1_xonly_pubkey_tweak_add_check (sign , buf32 , pk_parity , & internal_xonly_pk , tweak ) == 0 );
243+ CHECK (ecount == 2 );
244+ CHECK (secp256k1_xonly_pubkey_tweak_add_check (verify , buf32 , pk_parity , & internal_xonly_pk , tweak ) == 1 );
245+ CHECK (secp256k1_xonly_pubkey_tweak_add_check (verify , NULL , pk_parity , & internal_xonly_pk , tweak ) == 0 );
246+ CHECK (ecount == 3 );
247+ /* invalid pk_parity value */
248+ CHECK (secp256k1_xonly_pubkey_tweak_add_check (verify , buf32 , 2 , & internal_xonly_pk , tweak ) == 0 );
249+ CHECK (ecount == 3 );
250+ CHECK (secp256k1_xonly_pubkey_tweak_add_check (verify , buf32 , pk_parity , NULL , tweak ) == 0 );
251+ CHECK (ecount == 4 );
252+ CHECK (secp256k1_xonly_pubkey_tweak_add_check (verify , buf32 , pk_parity , & internal_xonly_pk , NULL ) == 0 );
253+ CHECK (ecount == 5 );
254+
255+ memset (tweak , 1 , sizeof (tweak ));
256+ CHECK (secp256k1_xonly_pubkey_from_pubkey (ctx , & internal_xonly_pk , NULL , & internal_pk ) == 1 );
257+ CHECK (secp256k1_xonly_pubkey_tweak_add (ctx , & output_pk , & internal_xonly_pk , tweak ) == 1 );
258+ CHECK (secp256k1_xonly_pubkey_from_pubkey (ctx , & output_xonly_pk , & pk_parity , & output_pk ) == 1 );
259+ CHECK (secp256k1_xonly_pubkey_serialize (ctx , output_pk32 , & output_xonly_pk ) == 1 );
260+ CHECK (secp256k1_xonly_pubkey_tweak_add_check (ctx , output_pk32 , pk_parity , & internal_xonly_pk , tweak ) == 1 );
261+
262+ /* Wrong pk_parity */
263+ CHECK (secp256k1_xonly_pubkey_tweak_add_check (ctx , output_pk32 , !pk_parity , & internal_xonly_pk , tweak ) == 0 );
264+ /* Wrong public key */
265+ CHECK (secp256k1_xonly_pubkey_serialize (ctx , buf32 , & internal_xonly_pk ) == 1 );
266+ CHECK (secp256k1_xonly_pubkey_tweak_add_check (ctx , buf32 , pk_parity , & internal_xonly_pk , tweak ) == 0 );
267+
268+ /* Overflowing tweak not allowed */
269+ CHECK (secp256k1_xonly_pubkey_tweak_add_check (ctx , output_pk32 , pk_parity , & internal_xonly_pk , overflows ) == 0 );
270+ CHECK (secp256k1_xonly_pubkey_tweak_add (ctx , & output_pk , & internal_xonly_pk , overflows ) == 0 );
271+ CHECK (memcmp (& output_pk , zeros64 , sizeof (output_pk )) == 0 );
272+ CHECK (ecount == 5 );
273+
274+ secp256k1_context_destroy (none );
275+ secp256k1_context_destroy (sign );
276+ secp256k1_context_destroy (verify );
277+ }
278+
279+ /* Starts with an initial pubkey and recursively creates N_PUBKEYS - 1
280+ * additional pubkeys by calling tweak_add. Then verifies every tweak starting
281+ * from the last pubkey. */
282+ #define N_PUBKEYS 32
283+ void test_xonly_pubkey_tweak_recursive (void ) {
284+ unsigned char sk [32 ];
285+ secp256k1_pubkey pk [N_PUBKEYS ];
286+ unsigned char pk_serialized [32 ];
287+ unsigned char tweak [N_PUBKEYS - 1 ][32 ];
288+ int i ;
289+
290+ secp256k1_rand256 (sk );
291+ CHECK (secp256k1_ec_pubkey_create (ctx , & pk [0 ], sk ) == 1 );
292+ /* Add tweaks */
293+ for (i = 0 ; i < N_PUBKEYS - 1 ; i ++ ) {
294+ secp256k1_xonly_pubkey xonly_pk ;
295+ memset (tweak [i ], i + 1 , sizeof (tweak [i ]));
296+ CHECK (secp256k1_xonly_pubkey_from_pubkey (ctx , & xonly_pk , NULL , & pk [i ]) == 1 );
297+ CHECK (secp256k1_xonly_pubkey_tweak_add (ctx , & pk [i + 1 ], & xonly_pk , tweak [i ]) == 1 );
298+ }
299+
300+ /* Verify tweaks */
301+ for (i = N_PUBKEYS - 1 ; i > 0 ; i -- ) {
302+ secp256k1_xonly_pubkey xonly_pk ;
303+ int pk_parity ;
304+ CHECK (secp256k1_xonly_pubkey_from_pubkey (ctx , & xonly_pk , & pk_parity , & pk [i ]) == 1 );
305+ CHECK (secp256k1_xonly_pubkey_serialize (ctx , pk_serialized , & xonly_pk ) == 1 );
306+ CHECK (secp256k1_xonly_pubkey_from_pubkey (ctx , & xonly_pk , NULL , & pk [i - 1 ]) == 1 );
307+ CHECK (secp256k1_xonly_pubkey_tweak_add_check (ctx , pk_serialized , pk_parity , & xonly_pk , tweak [i - 1 ]) == 1 );
308+ }
309+ }
310+ #undef N_PUBKEYS
311+
140312void run_extrakeys_tests (void ) {
141313 /* xonly key test cases */
142314 test_xonly_pubkey ();
315+ test_xonly_pubkey_tweak ();
316+ test_xonly_pubkey_tweak_check ();
317+ test_xonly_pubkey_tweak_recursive ();
143318}
144319
145320#endif
0 commit comments