Skip to content

Commit d1d6e47

Browse files
Merge #196: surjectionproof: fail to generate proofs when an input equals the output
d1175d2 surjectionproof: use secp256k1_memcmp_var rather than bare memcmp (Andrew Poelstra) bf18ff5 surjectionproof: fix generation to fail when any input == the output (Andrew Poelstra) 4ff6e42 surjectionproof: add test for existing behavior on input=output proofs (Andrew Poelstra) Pull request description: If any ephemeral input tag equals the ephemeral output tag (i.e. an input asset is exactly equal to the output asset), verification will fail due to an unexpected interaction between our surjectionproof logic and the underlying borromean ring siganture logic. However, our generation code still allows creating proofs like this, "succeeding" in creating bad proofs. Since we cannot fix the verification side without hardforking Liquid, fix the generation side to fail in this situation. ACKs for top commit: real-or-random: utACK d1175d2 Tree-SHA512: c15e130de028d6c1f705543fe2774ec23016c71f9d6b38ef0708820a517d156e2126f8369e94f16f9fd1855c29cd907d406f6ea26c95499a9ae1ce0dd92f77b2
2 parents 71a206f + d1175d2 commit d1d6e47

File tree

2 files changed

+34
-4
lines changed

2 files changed

+34
-4
lines changed

src/modules/surjection/main_impl.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -307,10 +307,14 @@ int secp256k1_surjectionproof_generate(const secp256k1_context* ctx, secp256k1_s
307307
if (overflow) {
308308
return 0;
309309
}
310-
/* The only time the input may equal the output is if neither one was blinded in the first place,
311-
* i.e. both blinding keys are zero. Otherwise this is a privacy leak. */
312-
if (secp256k1_scalar_eq(&tmps, &blinding_key) && !secp256k1_scalar_is_zero(&blinding_key)) {
313-
return 0;
310+
/* If any input tag is equal to an output tag, verification will fail, because our ring
311+
* signature logic would receive a zero-key, which is illegal. This is unfortunate but
312+
* it is deployed on Liquid and cannot be fixed without a hardfork. We should review
313+
* this at the same time that we relax the max-256-inputs rule. */
314+
for (i = 0; i < n_ephemeral_input_tags; i++) {
315+
if (secp256k1_memcmp_var(ephemeral_input_tags[i].data, ephemeral_output_tag->data, sizeof(ephemeral_output_tag->data)) == 0) {
316+
return 0;
317+
}
314318
}
315319
secp256k1_scalar_negate(&tmps, &tmps);
316320
secp256k1_scalar_add(&blinding_key, &blinding_key, &tmps);

src/modules/surjection/tests_impl.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,31 @@ void test_bad_parse(void) {
524524
CHECK(secp256k1_surjectionproof_parse(ctx, &proof, serialized_proof2, sizeof(serialized_proof2)) == 0);
525525
}
526526

527+
void test_input_eq_output(void) {
528+
secp256k1_surjectionproof proof;
529+
secp256k1_fixed_asset_tag fixed_tag;
530+
secp256k1_generator ephemeral_tag;
531+
unsigned char blinding_key[32];
532+
unsigned char entropy[32];
533+
size_t input_index;
534+
535+
secp256k1_testrand256(fixed_tag.data);
536+
secp256k1_testrand256(blinding_key);
537+
secp256k1_testrand256(entropy);
538+
539+
CHECK(secp256k1_surjectionproof_initialize(ctx, &proof, &input_index, &fixed_tag, 1, 1, &fixed_tag, 100, entropy) == 1);
540+
CHECK(input_index == 0);
541+
542+
/* Generation should fail */
543+
CHECK(secp256k1_generator_generate_blinded(ctx, &ephemeral_tag, fixed_tag.data, blinding_key));
544+
CHECK(!secp256k1_surjectionproof_generate(ctx, &proof, &ephemeral_tag, 1, &ephemeral_tag, input_index, blinding_key, blinding_key));
545+
546+
/* ...even when the blinding key is zero */
547+
memset(blinding_key, 0, 32);
548+
CHECK(secp256k1_generator_generate_blinded(ctx, &ephemeral_tag, fixed_tag.data, blinding_key));
549+
CHECK(!secp256k1_surjectionproof_generate(ctx, &proof, &ephemeral_tag, 1, &ephemeral_tag, input_index, blinding_key, blinding_key));
550+
}
551+
527552
void test_fixed_vectors(void) {
528553
const unsigned char tag0_ser[] = {
529554
0x0a,
@@ -672,6 +697,7 @@ void test_fixed_vectors(void) {
672697

673698
void run_surjection_tests(void) {
674699
test_surjectionproof_api();
700+
test_input_eq_output();
675701
test_fixed_vectors();
676702

677703
test_input_selection(0);

0 commit comments

Comments
 (0)