Skip to content

Commit c16252d

Browse files
committed
modify silentpayments example to exercise worst-case scanning attack
assuming the labels cache has only one entry (for change) for now includes fixes by w0xlt in order to avoid running into a stack overflow and time measureing code, see bitcoin-core#1765 (comment)
1 parent b786477 commit c16252d

File tree

1 file changed

+35
-21
lines changed

1 file changed

+35
-21
lines changed

examples/silentpayments.c

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,15 @@
1010
#include <stdio.h>
1111
#include <stdlib.h>
1212
#include <string.h>
13+
#include <time.h>
1314

1415
#include <secp256k1_extrakeys.h>
1516
#include <secp256k1_silentpayments.h>
1617

1718
#include "examples_util.h"
1819

1920
#define N_INPUTS 2
20-
#define N_OUTPUTS 3
21+
#define N_OUTPUTS 23255 /* upper bound of maximum outputs per block: floor(1_000_000/43) */
2122

2223
/* Static data for Bob and Carol's Silent Payments addresses */
2324
static unsigned char smallest_outpoint[36] = {
@@ -112,15 +113,21 @@ const unsigned char* label_lookup(
112113
return NULL;
113114
}
114115

116+
static secp256k1_xonly_pubkey tx_inputs[N_INPUTS];
117+
static const secp256k1_xonly_pubkey *tx_input_ptrs[N_INPUTS];
118+
static secp256k1_xonly_pubkey tx_outputs[N_OUTPUTS];
119+
static secp256k1_xonly_pubkey *tx_output_ptrs[N_OUTPUTS];
120+
static secp256k1_silentpayments_found_output found_outputs[N_OUTPUTS];
121+
static secp256k1_silentpayments_found_output *found_output_ptrs[N_OUTPUTS];
122+
static secp256k1_silentpayments_recipient recipients[N_OUTPUTS];
123+
static const secp256k1_silentpayments_recipient *recipient_ptrs[N_OUTPUTS];
124+
/* 2D array for holding multiple public key pairs. The second index, i.e., [2],
125+
* is to represent the spend and scan public keys. */
126+
static unsigned char (*sp_addresses[N_OUTPUTS])[2][33];
127+
115128
int main(void) {
116129
unsigned char randomize[32];
117130
unsigned char serialized_xonly[32];
118-
secp256k1_xonly_pubkey tx_inputs[N_INPUTS];
119-
const secp256k1_xonly_pubkey *tx_input_ptrs[N_INPUTS];
120-
secp256k1_xonly_pubkey tx_outputs[N_OUTPUTS];
121-
secp256k1_xonly_pubkey *tx_output_ptrs[N_OUTPUTS];
122-
secp256k1_silentpayments_found_output found_outputs[N_OUTPUTS];
123-
secp256k1_silentpayments_found_output *found_output_ptrs[N_OUTPUTS];
124131
secp256k1_silentpayments_prevouts_summary prevouts_summary;
125132
secp256k1_pubkey unlabeled_spend_pubkey;
126133
struct labels_cache bob_labels_cache;
@@ -209,13 +216,10 @@ int main(void) {
209216
{
210217
secp256k1_keypair sender_keypairs[N_INPUTS];
211218
const secp256k1_keypair *sender_keypair_ptrs[N_INPUTS];
212-
secp256k1_silentpayments_recipient recipients[N_OUTPUTS];
213-
const secp256k1_silentpayments_recipient *recipient_ptrs[N_OUTPUTS];
214-
/* 2D array for holding multiple public key pairs. The second index, i.e., [2],
215-
* is to represent the spend and scan public keys. */
216-
unsigned char (*sp_addresses[N_OUTPUTS])[2][33];
217219
unsigned char seckey[32];
218220

221+
printf("Sending...\n");
222+
219223
/*** Generate secret keys for the sender ***
220224
*
221225
* In this example, only taproot inputs are used but the function can be
@@ -269,12 +273,12 @@ int main(void) {
269273
for (i = 0; i < N_OUTPUTS; i++) {
270274
ret = secp256k1_ec_pubkey_parse(ctx,
271275
&recipients[i].scan_pubkey,
272-
(*(sp_addresses[i]))[0],
276+
(*(sp_addresses[1]))[0], /* to exercise worst-case scanning, use bob as recipient repeatedly */
273277
33
274278
);
275279
ret &= secp256k1_ec_pubkey_parse(ctx,
276280
&recipients[i].spend_pubkey,
277-
(*(sp_addresses[i]))[1],
281+
(*(sp_addresses[1]))[1], /* to exercise worst-case scanning, use bob as recipient repeatedly */
278282
33
279283
);
280284
if (!ret) {
@@ -302,15 +306,15 @@ int main(void) {
302306
printf("Something went wrong, a recipient provided an invalid address.\n");
303307
return EXIT_FAILURE;
304308
}
305-
printf("Alice created the following outputs for Bob and Carol:\n");
309+
printf("Alice created the following outputs for Bob:\n...\n");
306310
for (i = 0; i < N_OUTPUTS; i++) {
307-
printf(" ");
311+
/* printf(" "); */
308312
ret = secp256k1_xonly_pubkey_serialize(ctx,
309313
serialized_xonly,
310314
&tx_outputs[i]
311315
);
312316
assert(ret);
313-
print_hex(serialized_xonly, sizeof(serialized_xonly));
317+
/* print_hex(serialized_xonly, sizeof(serialized_xonly)); */
314318
}
315319
/* It's best practice to try to clear secrets from memory after using
316320
* them. This is done because some bugs can allow an attacker to leak
@@ -328,6 +332,7 @@ int main(void) {
328332

329333
/*** Receiving ***/
330334
{
335+
printf("Receiving...\n");
331336
{
332337
/*** Scanning as a full node (Bob) ***
333338
*
@@ -337,6 +342,9 @@ int main(void) {
337342
* `secp256k1_silentpayments_recipient_prevouts_summary_create`
338343
* 2. Call `secp256k1_silentpayments_recipient_scan_outputs`
339344
*/
345+
clock_t start, end;
346+
double cpu_time_used;
347+
340348
ret = secp256k1_silentpayments_recipient_prevouts_summary_create(ctx,
341349
&prevouts_summary,
342350
smallest_outpoint,
@@ -353,6 +361,8 @@ int main(void) {
353361

354362
/* Scan the transaction */
355363
n_found_outputs = 0;
364+
365+
start = clock();
356366
ret = secp256k1_silentpayments_recipient_scan_outputs(ctx,
357367
found_output_ptrs, &n_found_outputs,
358368
(const secp256k1_xonly_pubkey * const *)tx_output_ptrs, N_OUTPUTS,
@@ -361,6 +371,10 @@ int main(void) {
361371
&unlabeled_spend_pubkey,
362372
label_lookup, &bob_labels_cache /* NULL, NULL for no labels */
363373
);
374+
end = clock();
375+
cpu_time_used = ((double) (end - start)) / CLOCKS_PER_SEC;
376+
printf("Bob's scan took %f seconds\n", cpu_time_used);
377+
364378
if (!ret) {
365379
printf("This transaction is not valid for Silent Payments, skipping.\n");
366380
return EXIT_SUCCESS;
@@ -371,15 +385,15 @@ int main(void) {
371385
unsigned char full_seckey[32];
372386

373387
printf("\n");
374-
printf("Bob found the following outputs: \n");
388+
printf("Bob found the following outputs [%lu in total]: \n...\n", (unsigned long)n_found_outputs);
375389
for (i = 0; i < n_found_outputs; i++) {
376-
printf(" ");
390+
/* printf(" "); */
377391
ret = secp256k1_xonly_pubkey_serialize(ctx,
378392
serialized_xonly,
379393
&found_outputs[i].output
380394
);
381395
assert(ret);
382-
print_hex(serialized_xonly, sizeof(serialized_xonly));
396+
/* print_hex(serialized_xonly, sizeof(serialized_xonly)); */
383397

384398
/* Verify that this output is spendable by Bob by reconstructing the full
385399
* secret key for the xonly output.
@@ -432,7 +446,7 @@ int main(void) {
432446
n_found_outputs = 0;
433447
ret = secp256k1_silentpayments_recipient_scan_outputs(ctx,
434448
found_output_ptrs, &n_found_outputs,
435-
(const secp256k1_xonly_pubkey * const *)tx_output_ptrs, N_OUTPUTS,
449+
(const secp256k1_xonly_pubkey * const *)tx_output_ptrs, 1, /* dummy scan with one output (we only care about Bob) */
436450
carol_scan_key,
437451
&prevouts_summary,
438452
&unlabeled_spend_pubkey,

0 commit comments

Comments
 (0)