Skip to content

Commit 9d28292

Browse files
committed
rangeproof: add verify_value function to create single-value proofs
1 parent 593686c commit 9d28292

File tree

3 files changed

+177
-7
lines changed

3 files changed

+177
-7
lines changed

include/secp256k1_rangeproof.h

+26-4
Original file line numberDiff line numberDiff line change
@@ -287,10 +287,10 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_info(
287287
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);
288288

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

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

src/modules/rangeproof/main_impl.h

+103-2
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,105 @@ int secp256k1_rangeproof_verify_value(const secp256k1_context* ctx, const unsign
397397
return !memcmp(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_scalar es;
405+
secp256k1_scalar tmps;
406+
secp256k1_sha256 sha2;
407+
unsigned char tmpch[33];
408+
unsigned char pp_comm[32];
409+
size_t offset;
410+
size_t sz;
411+
int overflow;
412+
413+
VERIFY_CHECK(ctx != NULL);
414+
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
415+
ARG_CHECK(proof != NULL);
416+
ARG_CHECK(plen != NULL);
417+
ARG_CHECK(blind != NULL);
418+
ARG_CHECK(commit != NULL);
419+
ARG_CHECK(gen != NULL);
420+
421+
if (*plen < 73 || (value == 0 && *plen < 65)) {
422+
return 0;
423+
}
424+
425+
secp256k1_pedersen_commitment_load(&commitp, commit);
426+
secp256k1_generator_load(&genp, gen);
427+
428+
/* Encode header */
429+
if (value > 0) {
430+
proof[0] = 0x20;
431+
proof[1] = value >> 56;
432+
proof[2] = value >> 48;
433+
proof[3] = value >> 40;
434+
proof[4] = value >> 32;
435+
proof[5] = value >> 24;
436+
proof[6] = value >> 16;
437+
proof[7] = value >> 8;
438+
proof[8] = value;
439+
offset = 9;
440+
} else {
441+
proof[0] = 0x00;
442+
offset = 1;
443+
}
444+
445+
/* Now we have to make a Schnorr signature in (e, s) form. */
446+
447+
/* 1. Compute slow/overwrought commitment to proof params */
448+
secp256k1_sha256_initialize(&sha2);
449+
secp256k1_rangeproof_serialize_point(tmpch, &commitp);
450+
secp256k1_sha256_write(&sha2, tmpch, 33);
451+
secp256k1_rangeproof_serialize_point(tmpch, &genp);
452+
secp256k1_sha256_write(&sha2, tmpch, 33);
453+
secp256k1_sha256_write(&sha2, proof, offset); /* lol we commit to one extra byte here */
454+
secp256k1_sha256_finalize(&sha2, pp_comm);
455+
456+
/* ... feed this into our hash e */
457+
secp256k1_borromean_hash(tmpch, pp_comm, 32, &proof[offset], 32, 0, 0);
458+
secp256k1_scalar_set_b32(&es, tmpch, &overflow);
459+
if (overflow || secp256k1_scalar_is_zero(&es)) {
460+
return 0;
461+
}
462+
463+
/* ... and compute -ex from this */
464+
secp256k1_scalar_set_b32(&tmps, blind, &overflow);
465+
if (overflow || secp256k1_scalar_is_zero(&tmps)) {
466+
secp256k1_scalar_clear(&tmps);
467+
secp256k1_scalar_clear(&es);
468+
return 0;
469+
}
470+
secp256k1_scalar_mul(&es, &es, &tmps);
471+
secp256k1_scalar_negate(&es, &es);
472+
473+
/* 2. Compute random k and set `es` to k - ex */
474+
secp256k1_sha256_initialize(&sha2);
475+
secp256k1_sha256_write(&sha2, blind, 32);
476+
secp256k1_sha256_write(&sha2, pp_comm, 32);
477+
secp256k1_sha256_finalize(&sha2, tmpch);
478+
secp256k1_scalar_set_b32(&tmps, tmpch, &overflow);
479+
if (overflow || secp256k1_scalar_is_zero(&tmps)) {
480+
secp256k1_scalar_clear(&es);
481+
return 0;
482+
}
483+
secp256k1_scalar_add(&es, &es, &tmps);
484+
secp256k1_scalar_get_b32(&proof[offset + 32], &es);
485+
486+
/* Compute R = kG and serialize it*/
487+
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &tmpj, &tmps);
488+
secp256k1_scalar_clear(&tmps);
489+
secp256k1_ge_set_gej(&genp, &tmpj); /* Reuse genp which is no longer used */
490+
secp256k1_eckey_pubkey_serialize(&genp, tmpch, &sz, 1);
491+
492+
/* 3. Compute e0 = H(R || proof params) and serialize it */
493+
secp256k1_sha256_initialize(&sha2);
494+
secp256k1_sha256_write(&sha2, tmpch, sz);
495+
secp256k1_sha256_write(&sha2, pp_comm, sizeof(pp_comm));
496+
secp256k1_sha256_finalize(&sha2, &proof[offset]);
497+
498+
return 1;
499+
}
500+
400501
#endif

src/modules/rangeproof/tests_impl.h

+48-1
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,53 @@ 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(vrfy, proof, &len, val, blind, &commit, secp256k1_generator_h) == 1);
234+
CHECK(secp256k1_rangeproof_create_value(sign, proof, &len, val, blind, &commit, secp256k1_generator_h) == 1);
235+
CHECK(secp256k1_rangeproof_create_value(sign, NULL, &len, val, blind, &commit, secp256k1_generator_h) == 0);
236+
CHECK(*ecount == 1);
237+
CHECK(secp256k1_rangeproof_create_value(sign, proof, NULL, val, blind, &commit, secp256k1_generator_h) == 0);
238+
CHECK(*ecount == 2);
239+
CHECK(secp256k1_rangeproof_create_value(sign, proof, &len, val, NULL, &commit, secp256k1_generator_h) == 0);
240+
CHECK(*ecount == 3);
241+
CHECK(secp256k1_rangeproof_create_value(sign, proof, &len, val, blind, NULL, secp256k1_generator_h) == 0);
242+
CHECK(*ecount == 4);
243+
CHECK(secp256k1_rangeproof_create_value(sign, proof, &len, val, blind, &commit, NULL) == 0);
244+
CHECK(*ecount == 5);
245+
len = 0;
246+
CHECK(secp256k1_rangeproof_create_value(sign, proof, &len, 0, blind, &commit, secp256k1_generator_h) == 0);
247+
len = 64;
248+
CHECK(secp256k1_rangeproof_create_value(sign, proof, &len, 0, blind, &commit, secp256k1_generator_h) == 0);
249+
len = 65;
250+
CHECK(secp256k1_rangeproof_create_value(sign, proof, &len, 0, blind, &commit, secp256k1_generator_h) == 0);
251+
len = 65;
252+
CHECK(secp256k1_rangeproof_create_value(sign, proof, &len, 1, blind, &commit, secp256k1_generator_h) == 0);
253+
len = 72;
254+
CHECK(secp256k1_rangeproof_create_value(sign, proof, &len, 1, blind, &commit, secp256k1_generator_h) == 0);
255+
len = 73;
256+
CHECK(secp256k1_rangeproof_create_value(sign, proof, &len, val, blind, &commit, secp256k1_generator_h) == 1);
257+
CHECK(*ecount == 5);
258+
259+
*ecount = 0;
260+
CHECK(secp256k1_rangeproof_verify_value(none, proof, len, val, &commit, secp256k1_generator_h) == 1);
261+
CHECK(secp256k1_rangeproof_verify_value(sign, proof, len, val, &commit, secp256k1_generator_h) == 1);
262+
CHECK(secp256k1_rangeproof_verify_value(vrfy, proof, len, val, &commit, secp256k1_generator_h) == 1);
263+
CHECK(*ecount == 0);
264+
CHECK(secp256k1_rangeproof_verify_value(vrfy, NULL, len, val, &commit, secp256k1_generator_h) == 0);
265+
CHECK(*ecount == 1);
266+
CHECK(secp256k1_rangeproof_verify_value(vrfy, proof, len, val, NULL, secp256k1_generator_h) == 0);
267+
CHECK(*ecount == 2);
268+
CHECK(secp256k1_rangeproof_verify_value(vrfy, proof, len, val, &commit, NULL) == 0);
269+
CHECK(*ecount == 3);
270+
CHECK(secp256k1_rangeproof_verify_value(vrfy, proof, 0, val, &commit, secp256k1_generator_h) == 0);
271+
CHECK(secp256k1_rangeproof_verify_value(vrfy, proof, len - 1, val, &commit, secp256k1_generator_h) == 0);
272+
CHECK(secp256k1_rangeproof_verify_value(vrfy, proof, len, val ^ 1, &commit, secp256k1_generator_h) == 0);
273+
CHECK(*ecount == 3);
274+
}
228275
}
229276

230277
static void test_api(void) {

0 commit comments

Comments
 (0)