Skip to content

Commit 4c817db

Browse files
committed
rangeproof: add verify_value function to create single-value proofs
1 parent 3662a01 commit 4c817db

File tree

3 files changed

+183
-7
lines changed

3 files changed

+183
-7
lines changed

include/secp256k1_rangeproof.h

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -288,10 +288,10 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_info(
288288
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);
289289

290290
/** Verify a rangeproof with a single-value range. Useful as a "proof of value"
291-
* of a Pedersen commitment. Such proofs can be created with `secp256k1_rangeproof_sign`
292-
* by passing an `exp` parameter of -1 and the target value as both `value` and `min_value`.
293-
* (In this case `min_bits` is ignored and may take any value, but for clarity it's best
294-
* to pass zero.)
291+
* of a Pedersen commitment. Such proofs can be created with `secp256k1_rangeproof_create_value`,
292+
* or with `secp256k1_rangeproof_sign` by passing an `exp` parameter of -1 and the
293+
* target value as both `value` and `min_value`. (In this case `min_bits` is ignored
294+
* and may take any value, but for clarity it's best to pass zero.)
295295
* Returns 1: Proof was valid and proved the given value
296296
* 0: Otherwise
297297
* In: ctx: pointer to a context object
@@ -310,6 +310,28 @@ SECP256K1_API int secp256k1_rangeproof_verify_value(
310310
const secp256k1_generator* gen
311311
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6);
312312

313+
/** Create a rangeproof with a single-value range.
314+
* Returns 1: Proof was successfully generated
315+
* 0: Otherwise. The contents of `proof` are unspecified in this case.
316+
* Args: ctx: pointer to a context object
317+
* Out: proof: pointer to character array to populate the proof with. Must be at least 73
318+
* bytes unless `value` is 0, in which case it must be at least 65 bytes
319+
* In/Out: plen: length of the `proof` buffer; will be overwritten with the actual length
320+
* In: value: value being claimed for the Pedersen commitment
321+
* blind: the blinding factor for the Pedersen commitment `commit`
322+
* commit: the Pedersen commitment whose value is being proven
323+
* gen: additional generator 'h'
324+
*/
325+
SECP256K1_API int secp256k1_rangeproof_create_value(
326+
const secp256k1_context* ctx,
327+
unsigned char* proof,
328+
size_t* plen,
329+
uint64_t value,
330+
const unsigned char* blind,
331+
const secp256k1_pedersen_commitment* commit,
332+
const secp256k1_generator* gen
333+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7);
334+
313335
# ifdef __cplusplus
314336
}
315337
# endif

src/modules/rangeproof/main_impl.h

Lines changed: 113 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,7 @@ int secp256k1_rangeproof_verify_value(const secp256k1_context* ctx, const unsign
359359
/* Now we just have a Schnorr signature in (e, s) form. The verification
360360
* equation is e == H(sG - eX || proof params) */
361361

362-
/* 1. Compute slow/overwrought commitment to proof params */
362+
/* 0. Compute slow/overwrought commitment to proof params */
363363
secp256k1_sha256_initialize(&sha2);
364364
secp256k1_rangeproof_serialize_point(tmpch, &commitp);
365365
secp256k1_sha256_write(&sha2, tmpch, 33);
@@ -375,7 +375,7 @@ int secp256k1_rangeproof_verify_value(const secp256k1_context* ctx, const unsign
375375
return 0;
376376
}
377377

378-
/* 1. Compute R = sG - eX */
378+
/* 1. Compute R = sG + eX */
379379
secp256k1_scalar_set_b32(&ss, &proof[offset + 32], &overflow);
380380
if (overflow || secp256k1_scalar_is_zero(&ss)) {
381381
return 0;
@@ -397,4 +397,115 @@ int secp256k1_rangeproof_verify_value(const secp256k1_context* ctx, const unsign
397397
return !secp256k1_memcmp_var(tmpch, &proof[offset], 32);
398398
}
399399

400+
int secp256k1_rangeproof_create_value(const secp256k1_context* ctx, unsigned char* proof, size_t* plen, uint64_t value, const unsigned char* blind, const secp256k1_pedersen_commitment* commit, const secp256k1_generator* gen) {
401+
secp256k1_ge commitp;
402+
secp256k1_ge genp;
403+
secp256k1_gej tmpj;
404+
secp256k1_ge tmpp;
405+
secp256k1_scalar es;
406+
secp256k1_scalar ks;
407+
secp256k1_scalar xs;
408+
secp256k1_sha256 sha2;
409+
unsigned char tmpch[33];
410+
unsigned char pp_comm[32];
411+
size_t offset;
412+
size_t sz;
413+
int overflow;
414+
415+
VERIFY_CHECK(ctx != NULL);
416+
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
417+
ARG_CHECK(proof != NULL);
418+
ARG_CHECK(plen != NULL);
419+
ARG_CHECK(blind != NULL);
420+
ARG_CHECK(commit != NULL);
421+
ARG_CHECK(gen != NULL);
422+
423+
if (*plen < (value == 0 ? 65 : 73)) {
424+
return 0;
425+
}
426+
*plen = value == 0 ? 65 : 73;
427+
428+
secp256k1_pedersen_commitment_load(&commitp, commit);
429+
secp256k1_generator_load(&genp, gen);
430+
431+
/* Encode header */
432+
if (value > 0) {
433+
proof[0] = 0x20;
434+
proof[1] = value >> 56;
435+
proof[2] = value >> 48;
436+
proof[3] = value >> 40;
437+
proof[4] = value >> 32;
438+
proof[5] = value >> 24;
439+
proof[6] = value >> 16;
440+
proof[7] = value >> 8;
441+
proof[8] = value;
442+
offset = 9;
443+
} else {
444+
proof[0] = 0x00;
445+
offset = 1;
446+
}
447+
448+
/* Now we have to make a Schnorr signature in (e, s) form. */
449+
450+
/* 1. Compute random k */
451+
secp256k1_sha256_initialize(&sha2);
452+
secp256k1_sha256_write(&sha2, blind, 32);
453+
secp256k1_sha256_write(&sha2, proof, offset);
454+
secp256k1_rangeproof_serialize_point(tmpch, &genp);
455+
secp256k1_sha256_write(&sha2, tmpch, 33);
456+
secp256k1_sha256_finalize(&sha2, tmpch);
457+
secp256k1_scalar_set_b32(&ks, tmpch, &overflow);
458+
if (overflow || secp256k1_scalar_is_zero(&ks)) {
459+
secp256k1_scalar_clear(&ks);
460+
memset(tmpch, 0, sizeof(tmpch));
461+
return 0;
462+
}
463+
464+
/* 2. Compute R = kG */
465+
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &tmpj, &ks);
466+
secp256k1_ge_set_gej(&tmpp, &tmpj);
467+
468+
/* 3. Compute slow/overwrought commitment to proof params */
469+
secp256k1_sha256_initialize(&sha2);
470+
secp256k1_rangeproof_serialize_point(tmpch, &commitp);
471+
secp256k1_sha256_write(&sha2, tmpch, 33);
472+
secp256k1_rangeproof_serialize_point(tmpch, &genp);
473+
secp256k1_sha256_write(&sha2, tmpch, 33);
474+
secp256k1_sha256_write(&sha2, proof, offset);
475+
secp256k1_sha256_finalize(&sha2, pp_comm);
476+
477+
/* 4. Compute e0 = H(R || proof params) and serialize it into the proof */
478+
secp256k1_sha256_initialize(&sha2);
479+
secp256k1_eckey_pubkey_serialize(&tmpp, tmpch, &sz, 1);
480+
secp256k1_sha256_write(&sha2, tmpch, sz);
481+
secp256k1_sha256_write(&sha2, pp_comm, sizeof(pp_comm));
482+
secp256k1_sha256_finalize(&sha2, &proof[offset]);
483+
484+
/* ... feed this into our hash e, along with e0 */
485+
secp256k1_borromean_hash(tmpch, pp_comm, 32, &proof[offset], 32, 0, 0);
486+
secp256k1_scalar_set_b32(&es, tmpch, &overflow);
487+
if (overflow || secp256k1_scalar_is_zero(&es)) {
488+
secp256k1_scalar_clear(&ks);
489+
secp256k1_scalar_clear(&es);
490+
return 0;
491+
}
492+
493+
/* 5. Compute k - ex from this, and serialize it */
494+
secp256k1_scalar_set_b32(&xs, blind, &overflow);
495+
if (overflow || secp256k1_scalar_is_zero(&xs)) {
496+
secp256k1_scalar_clear(&ks);
497+
secp256k1_scalar_clear(&xs);
498+
secp256k1_scalar_clear(&es);
499+
return 0;
500+
}
501+
secp256k1_scalar_mul(&es, &es, &xs);
502+
secp256k1_scalar_negate(&es, &es);
503+
secp256k1_scalar_add(&es, &es, &ks);
504+
secp256k1_scalar_get_b32(&proof[offset + 32], &es);
505+
506+
secp256k1_scalar_clear(&ks);
507+
secp256k1_scalar_clear(&xs);
508+
return 1;
509+
}
510+
400511
#endif

src/modules/rangeproof/tests_impl.h

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ static void test_pedersen_api(const secp256k1_context *none, const secp256k1_con
7676
CHECK(*ecount == 14);
7777
}
7878

79-
static void test_rangeproof_api(const secp256k1_context *none, const secp256k1_context *sign, const secp256k1_context *vrfy, const secp256k1_context *both, const secp256k1_context *sttc, const int32_t *ecount) {
79+
static void test_rangeproof_api(const secp256k1_context *none, const secp256k1_context *sign, const secp256k1_context *vrfy, const secp256k1_context *both, const secp256k1_context *sttc, int32_t *ecount) {
8080
unsigned char proof[5134];
8181
unsigned char blind[32];
8282
secp256k1_pedersen_commitment commit;
@@ -225,6 +225,49 @@ static void test_rangeproof_api(const secp256k1_context *none, const secp256k1_c
225225
CHECK(secp256k1_rangeproof_rewind(both, blind_out, &value_out, NULL, 0, commit.data, &min_value, &max_value, &commit, proof, len, NULL, 0, NULL) == 0);
226226
CHECK(*ecount == 29);
227227
}
228+
229+
{
230+
*ecount = 0;
231+
len = sizeof(proof);
232+
CHECK(secp256k1_rangeproof_create_value(none, proof, &len, val, blind, &commit, secp256k1_generator_h) == 1);
233+
CHECK(secp256k1_rangeproof_create_value(none, NULL, &len, val, blind, &commit, secp256k1_generator_h) == 0);
234+
CHECK(*ecount == 1);
235+
CHECK(secp256k1_rangeproof_create_value(none, proof, NULL, val, blind, &commit, secp256k1_generator_h) == 0);
236+
CHECK(*ecount == 2);
237+
CHECK(secp256k1_rangeproof_create_value(none, proof, &len, val, NULL, &commit, secp256k1_generator_h) == 0);
238+
CHECK(*ecount == 3);
239+
CHECK(secp256k1_rangeproof_create_value(none, proof, &len, val, blind, NULL, secp256k1_generator_h) == 0);
240+
CHECK(*ecount == 4);
241+
CHECK(secp256k1_rangeproof_create_value(none, proof, &len, val, blind, &commit, NULL) == 0);
242+
CHECK(*ecount == 5);
243+
len = 0;
244+
CHECK(secp256k1_rangeproof_create_value(none, proof, &len, 0, blind, &commit, secp256k1_generator_h) == 0);
245+
len = 64;
246+
CHECK(secp256k1_rangeproof_create_value(none, proof, &len, 0, blind, &commit, secp256k1_generator_h) == 0);
247+
len = 65;
248+
CHECK(secp256k1_rangeproof_create_value(none, proof, &len, 0, blind, &commit, secp256k1_generator_h) == 1);
249+
len = 65;
250+
CHECK(secp256k1_rangeproof_create_value(none, proof, &len, 1, blind, &commit, secp256k1_generator_h) == 0);
251+
len = 72;
252+
CHECK(secp256k1_rangeproof_create_value(none, proof, &len, 1, blind, &commit, secp256k1_generator_h) == 0);
253+
len = 73;
254+
CHECK(secp256k1_rangeproof_create_value(none, proof, &len, val, blind, &commit, secp256k1_generator_h) == 1);
255+
CHECK(*ecount == 5);
256+
257+
*ecount = 0;
258+
CHECK(secp256k1_rangeproof_verify_value(none, proof, len, val, &commit, secp256k1_generator_h) == 1);
259+
CHECK(*ecount == 0);
260+
CHECK(secp256k1_rangeproof_verify_value(none, NULL, len, val, &commit, secp256k1_generator_h) == 0);
261+
CHECK(*ecount == 1);
262+
CHECK(secp256k1_rangeproof_verify_value(none, proof, len, val, NULL, secp256k1_generator_h) == 0);
263+
CHECK(*ecount == 2);
264+
CHECK(secp256k1_rangeproof_verify_value(none, proof, len, val, &commit, NULL) == 0);
265+
CHECK(*ecount == 3);
266+
CHECK(secp256k1_rangeproof_verify_value(none, proof, 0, val, &commit, secp256k1_generator_h) == 0);
267+
CHECK(secp256k1_rangeproof_verify_value(none, proof, len - 1, val, &commit, secp256k1_generator_h) == 0);
268+
CHECK(secp256k1_rangeproof_verify_value(none, proof, len, val ^ 1, &commit, secp256k1_generator_h) == 0);
269+
CHECK(*ecount == 3);
270+
}
228271
}
229272

230273
static void test_api(void) {

0 commit comments

Comments
 (0)