Skip to content

Commit c8939d1

Browse files
committed
rangeproof: use rangeproof_header structure for proving
1 parent 6f04f55 commit c8939d1

File tree

2 files changed

+154
-119
lines changed

2 files changed

+154
-119
lines changed

src/modules/rangeproof/rangeproof.h

+16-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ typedef struct secp256k1_rangeproof_header {
2828
/** Number of bits used to represent the proven value
2929
*
3030
* Encoded in the header. */
31-
int mantissa;
31+
size_t mantissa;
3232
/** 10 to the power of exp, or 1 for a proof of an exact value.
3333
*
3434
* Implied by `exp`, not encoded. */
@@ -70,6 +70,21 @@ static int secp256k1_rangeproof_header_parse(
7070
size_t plen
7171
);
7272

73+
/** Serializes out a rangeproof header which has at least `exp`, `min_value` and `mantissa` set
74+
*
75+
* Returns: 1 on success, 0 on failure
76+
* Out: proof: the buffer to serialize into
77+
* offset: the number of bytes of `proof` that the header occupies
78+
* In: plen: the length of the proof buffer
79+
* header: the header to serialize
80+
*/
81+
static int secp256k1_rangeproof_header_serialize(
82+
unsigned char* proof,
83+
size_t plen,
84+
size_t* offset,
85+
const secp256k1_rangeproof_header* header
86+
);
87+
7388
static int secp256k1_rangeproof_verify_impl(const secp256k1_ecmult_gen_context* ecmult_gen_ctx,
7489
unsigned char *blindout, uint64_t *value_out, unsigned char *message_out, size_t *outlen, const unsigned char *nonce,
7590
uint64_t *min_value, uint64_t *max_value, const secp256k1_ge *commit, const unsigned char *proof, size_t plen,

src/modules/rangeproof/rangeproof_impl.h

+138-118
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,105 @@ static int secp256k1_rangeproof_header_parse(
121121
return secp256k1_rangeproof_header_expand(header);
122122
}
123123

124+
static int secp256k1_rangeproof_header_set_for_value(
125+
secp256k1_rangeproof_header* header,
126+
uint64_t* proven_value,
127+
const uint64_t min_value,
128+
const uint64_t min_bits,
129+
const int exp,
130+
const uint64_t value
131+
) {
132+
memset(header, 0, sizeof(*header));
133+
*proven_value = 0;
134+
135+
/* Sanity checks */
136+
if (min_value > value || min_bits > 64 || exp < -1 || exp > 18) {
137+
return 0;
138+
}
139+
140+
/* Start by just using the user's requested values, then adjust them in
141+
* various ways to make them compatible. This is probably not advisable
142+
* from a privacy point-of-view but it's important to be compatible with
143+
* the 2015-era API, and all of these issues will go away when we merge
144+
* Bulletproofs. */
145+
header->exp = exp;
146+
header->min_value = min_value;
147+
header->mantissa = min_bits ? min_bits : 1; /* force mantissa to be nonzero */
148+
149+
/* Special-case single-value proofs */
150+
if (header->exp == -1) {
151+
header->mantissa = 0; /* ignore user's min_bits */
152+
return secp256k1_rangeproof_header_expand(header);
153+
}
154+
155+
/* Deal with extreme values (copied directly from 2015 code) */
156+
if (min_bits > 61 || value > INT64_MAX) {
157+
/* Ten is not a power of two, so dividing by ten and then representing in base-2 times ten
158+
* expands the representable range. The verifier requires the proven range is within 0..2**64.
159+
* For very large numbers (all over 2**63) we must change our exponent to compensate.
160+
* Rather than handling it precisely, this just disables use of the exponent for big values.
161+
*/
162+
header->exp = 0;
163+
}
164+
{
165+
/* If the user has asked for more bits of proof then there is room for in the exponent, reduce the exponent. */
166+
uint64_t max = min_bits ? (UINT64_MAX >> (64 - min_bits)) : 0;
167+
int i;
168+
for (i = 0; i < header->exp && max <= UINT64_MAX / 10; i++) {
169+
max *= 10;
170+
}
171+
header->exp = i;
172+
}
173+
174+
175+
/* Increase the mantissa from min_bits until it actually covers the proven value */
176+
if (!secp256k1_rangeproof_header_expand(header)) {
177+
return 0;
178+
}
179+
*proven_value = (value - header->min_value) / header->scale;
180+
while (header->mantissa < 64 && (*proven_value >> header->mantissa) > 0) {
181+
header->mantissa++;
182+
}
183+
/* Fudge min_value so we don't lose the low-order digits of `value` */
184+
header->min_value = value - (*proven_value * header->scale);
185+
186+
/* Increasing the mantissa will have increased the number of rings etc
187+
* so re-expand the header to recompute the other derived values. */
188+
return secp256k1_rangeproof_header_expand(header);
189+
}
190+
191+
static int secp256k1_rangeproof_header_serialize(
192+
unsigned char* proof,
193+
size_t plen,
194+
size_t* offset,
195+
const secp256k1_rangeproof_header* header
196+
) {
197+
*offset = 0;
198+
if (plen < 65) {
199+
return 0;
200+
}
201+
202+
/* Write control byte */
203+
proof[0] = (header->exp >= 0 ? (64 | header->exp) : 0) | (header->min_value ? 32 : 0);
204+
*offset += 1;
205+
/* Write mantissa, for non-exact-value proofs */
206+
if (header->exp >= 0) {
207+
VERIFY_CHECK(header->mantissa > 0 && header->mantissa <= 64);
208+
proof[1] = header->mantissa - 1;
209+
*offset += 1;
210+
}
211+
/* Write min_value, if present */
212+
if (header->min_value > 0) {
213+
size_t i;
214+
for (i = 0; i < 8; i++) {
215+
proof[*offset + i] = (header->min_value >> ((7-i) * 8)) & 255;
216+
}
217+
*offset += 8;
218+
}
219+
220+
return 1;
221+
}
222+
124223
SECP256K1_INLINE static void secp256k1_rangeproof_pub_expand(secp256k1_gej *pubs,
125224
int exp, size_t *rsizes, size_t rings, const secp256k1_ge* genp) {
126225
secp256k1_gej base;
@@ -214,88 +313,12 @@ SECP256K1_INLINE static int secp256k1_rangeproof_genrand(secp256k1_scalar *sec,
214313
return ret;
215314
}
216315

217-
SECP256K1_INLINE static int secp256k1_range_proveparams(uint64_t *v, size_t *rings, size_t *rsizes, size_t *npub, secp256k1_borromean_sz_closure* secidx_closure, uint64_t *min_value,
218-
int *mantissa, uint64_t *scale, int *exp, int *min_bits, uint64_t value) {
219-
size_t i;
220-
*rings = 1;
221-
rsizes[0] = 1;
222-
*scale = 1;
223-
*mantissa = 0;
224-
*npub = 0;
225-
if (*min_value == UINT64_MAX) {
226-
/* If the minimum value is the maximal representable value, then we cannot code a range. */
227-
*exp = -1;
228-
}
229-
if (*exp >= 0) {
230-
int max_bits;
231-
uint64_t v2;
232-
if ((*min_value && value > INT64_MAX) || (value && *min_value >= INT64_MAX)) {
233-
/* If either value or min_value is >= 2^63-1 then the other must by zero to avoid overflowing the proven range. */
234-
return 0;
235-
}
236-
max_bits = *min_value ? secp256k1_clz64_var(*min_value) : 64;
237-
if (*min_bits > max_bits) {
238-
*min_bits = max_bits;
239-
}
240-
if (*min_bits > 61 || value > INT64_MAX) {
241-
/** Ten is not a power of two, so dividing by ten and then representing in base-2 times ten
242-
* expands the representable range. The verifier requires the proven range is within 0..2**64.
243-
* For very large numbers (all over 2**63) we must change our exponent to compensate.
244-
* Rather than handling it precisely, this just disables use of the exponent for big values.
245-
*/
246-
*exp = 0;
247-
}
248-
/* Mask off the least significant digits, as requested. */
249-
*v = value - *min_value;
250-
/* If the user has asked for more bits of proof then there is room for in the exponent, reduce the exponent. */
251-
v2 = *min_bits ? (UINT64_MAX>>(64-*min_bits)) : 0;
252-
for (i = 0; (int) i < *exp && (v2 <= UINT64_MAX / 10); i++) {
253-
*v /= 10;
254-
v2 *= 10;
255-
}
256-
*exp = i;
257-
v2 = *v;
258-
for (i = 0; (int) i < *exp; i++) {
259-
v2 *= 10;
260-
*scale *= 10;
261-
}
262-
/* If the masked number isn't precise, compute the public offset. */
263-
*min_value = value - v2;
264-
/* How many bits do we need to represent our value? */
265-
*mantissa = *v ? 64 - secp256k1_clz64_var(*v) : 1;
266-
if (*min_bits > *mantissa) {
267-
/* If the user asked for more precision, give it to them. */
268-
*mantissa = *min_bits;
269-
}
270-
/* Digits in radix-4, except for the last digit if our mantissa length is odd. */
271-
*rings = (*mantissa + 1) >> 1;
272-
for (i = 0; i < *rings; i++) {
273-
rsizes[i] = ((i < *rings - 1) | (!(*mantissa&1))) ? 4 : 2;
274-
*npub += rsizes[i];
275-
}
276-
VERIFY_CHECK(*mantissa>0);
277-
VERIFY_CHECK((*v & ~(UINT64_MAX>>(64-*mantissa))) == 0); /* Did this get all the bits? */
278-
*secidx_closure = secp256k1_borromean_sz_closure_secidx(*v);
279-
} else {
280-
/* A proof for an exact value. */
281-
*exp = 0;
282-
*min_value = value;
283-
*v = 0;
284-
*npub = 2;
285-
*secidx_closure = secp256k1_borromean_sz_closure_const(0);
286-
}
287-
VERIFY_CHECK(*v * *scale + *min_value == value);
288-
VERIFY_CHECK(*rings > 0);
289-
VERIFY_CHECK(*rings <= 32);
290-
VERIFY_CHECK(*npub <= 128);
291-
return 1;
292-
}
293-
294316
/* strawman interface, writes proof in proof, a buffer of plen, proves with respect to min_value the range for commit which has the provided blinding factor and value. */
295317
SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmult_gen_context* ecmult_gen_ctx,
296318
unsigned char *proof, size_t *plen, uint64_t min_value,
297319
const secp256k1_ge *commit, const unsigned char *blind, const unsigned char *nonce, int exp, int min_bits, uint64_t value,
298320
const unsigned char *message, size_t msg_len, const unsigned char *extra_commit, size_t extra_commit_len, const secp256k1_ge* genp){
321+
secp256k1_rangeproof_header header;
299322
secp256k1_gej pubs[128]; /* Candidate digits for our proof, most inferred. */
300323
secp256k1_scalar s[128]; /* Signatures in our proof, most forged. */
301324
secp256k1_scalar sec[32]; /* Blinding factors for the correct digits. */
@@ -305,44 +328,41 @@ SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmul
305328
unsigned char tmp[33];
306329
unsigned char *signs; /* Location of sign flags in the proof. */
307330
uint64_t v;
308-
uint64_t scale; /* scale = 10^exp. */
309-
int mantissa; /* Number of bits proven in the blinded value. */
310-
size_t rings; /* How many digits will our proof cover. */
311-
size_t rsizes[32]; /* How many possible values there are for each place. */
312331
secp256k1_borromean_sz_closure secidx_closure;
313332
size_t len; /* Number of bytes used so far. */
314333
size_t i;
334+
size_t pub_idx;
315335
int overflow;
316-
size_t npub;
317336
len = 0;
318-
if (*plen < 65 || min_value > value || min_bits > 64 || min_bits < 0 || exp < -1 || exp > 18) {
337+
if (*plen < 65) {
319338
return 0;
320339
}
321-
if (!secp256k1_range_proveparams(&v, &rings, rsizes, &npub, &secidx_closure, &min_value, &mantissa, &scale, &exp, &min_bits, value)) {
340+
341+
if (!secp256k1_rangeproof_header_set_for_value(&header, &v, min_value, min_bits, exp, value)) {
322342
return 0;
323343
}
324-
proof[len] = (rsizes[0] > 1 ? (64 | exp) : 0) | (min_value ? 32 : 0);
325-
len++;
326-
if (rsizes[0] > 1) {
327-
VERIFY_CHECK(mantissa > 0 && mantissa <= 64);
328-
proof[len] = mantissa - 1;
329-
len++;
330-
}
331-
if (min_value) {
332-
for (i = 0; i < 8; i++) {
333-
proof[len + i] = (min_value >> ((7-i) * 8)) & 255;
334-
}
335-
len += 8;
344+
if (header.exp >= 0) {
345+
secidx_closure = secp256k1_borromean_sz_closure_secidx(v);
346+
} else {
347+
secidx_closure = secp256k1_borromean_sz_closure_const(0);
336348
}
349+
350+
VERIFY_CHECK(v * header.scale + header.min_value == value);
351+
VERIFY_CHECK(header.n_rings > 0);
352+
VERIFY_CHECK(header.n_rings <= 32);
353+
VERIFY_CHECK(header.n_pubs <= 128);
354+
355+
secp256k1_rangeproof_header_serialize (proof, *plen, &len, &header);
356+
337357
/* Do we have enough room in the proof for the message? Each ring gives us 128 bytes, but the
338358
* final ring is used to encode the blinding factor and the value, so we can't use that. (Well,
339359
* technically there are 64 bytes available if we avoided the other data, but this is difficult
340360
* because it's not always in the same place. */
341-
if (msg_len > 0 && msg_len > 128 * (rings - 1)) {
361+
if (msg_len > 0 && msg_len > 128 * (header.n_rings - 1)) {
342362
return 0;
343363
}
344364
/* Do we have enough room for the proof? */
345-
if (*plen - len < 32 * (npub + rings - 1) + 32 + ((rings+6) >> 3)) {
365+
if (*plen - len < 32 * (header.n_pubs + header.n_rings - 1) + 32 + ((header.n_rings + 6) >> 3)) {
346366
return 0;
347367
}
348368
secp256k1_sha256_initialize(&sha256_m);
@@ -357,19 +377,19 @@ SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmul
357377
memcpy(prep, message, msg_len);
358378
}
359379
/* Note, the data corresponding to the blinding factors must be zero. */
360-
if (rsizes[rings - 1] > 1) {
380+
if (header.rsizes[header.n_rings - 1] > 1) {
361381
size_t idx;
362382
/* Value encoding sidechannel. */
363-
idx = rsizes[rings - 1] - 1;
364-
idx -= secidx_closure.call(&secidx_closure, rings - 1) == idx;
365-
idx = ((rings - 1) * 4 + idx) * 32;
383+
idx = header.rsizes[header.n_rings - 1] - 1;
384+
idx -= secidx_closure.call(&secidx_closure, header.n_rings - 1) == idx;
385+
idx = ((header.n_rings - 1) * 4 + idx) * 32;
366386
for (i = 0; i < 8; i++) {
367387
prep[8 + i + idx] = prep[16 + i + idx] = prep[24 + i + idx] = (v >> (56 - i * 8)) & 255;
368388
prep[i + idx] = 0;
369389
}
370390
prep[idx] = 128;
371391
}
372-
if (!secp256k1_rangeproof_genrand(sec, s, prep, rsizes, rings, nonce, commit, proof, len, genp)) {
392+
if (!secp256k1_rangeproof_genrand(sec, s, prep, header.rsizes, header.n_rings, nonce, commit, proof, len, genp)) {
373393
return 0;
374394
}
375395
memset(prep, 0, 4096);
@@ -380,51 +400,51 @@ SECP256K1_INLINE static int secp256k1_rangeproof_sign_impl(const secp256k1_ecmul
380400
* blinded value for one digit.
381401
*/
382402
secp256k1_scalar_set_b32(&stmp, blind, &overflow);
383-
secp256k1_scalar_add(&sec[rings - 1], &sec[rings - 1], &stmp);
384-
if (overflow || secp256k1_scalar_is_zero(&sec[rings - 1])) {
403+
secp256k1_scalar_add(&sec[header.n_rings - 1], &sec[header.n_rings - 1], &stmp);
404+
if (overflow || secp256k1_scalar_is_zero(&sec[header.n_rings - 1])) {
385405
return 0;
386406
}
387407
signs = &proof[len];
388408
/* We need one sign bit for each blinded value we send. */
389-
for (i = 0; i < (rings + 6) >> 3; i++) {
409+
for (i = 0; i < (header.n_rings + 6) >> 3; i++) {
390410
signs[i] = 0;
391411
len++;
392412
}
393-
npub = 0;
394-
for (i = 0; i < rings; i++) {
413+
pub_idx = 0;
414+
for (i = 0; i < header.n_rings; i++) {
395415
uint64_t secidx_i = secidx_closure.call(&secidx_closure, i);
396416
/*OPT: Use the precomputed gen2 basis?*/
397-
secp256k1_pedersen_ecmult(ecmult_gen_ctx, &pubs[npub], &sec[i], (secidx_i * scale) << (i*2), genp);
398-
if (secp256k1_gej_is_infinity(&pubs[npub])) {
417+
secp256k1_pedersen_ecmult(ecmult_gen_ctx, &pubs[pub_idx], &sec[i], (secidx_i * header.scale) << (i*2), genp);
418+
if (secp256k1_gej_is_infinity(&pubs[pub_idx])) {
399419
return 0;
400420
}
401-
if (i < rings - 1) {
421+
if (i < header.n_rings - 1) {
402422
unsigned char tmpc[33];
403423
secp256k1_ge c;
404424
unsigned char quadness;
405425
/*OPT: split loop and batch invert.*/
406-
/*OPT: do not compute full pubs[npub] in ge form; we only need x */
407-
secp256k1_ge_set_gej_var(&c, &pubs[npub]);
426+
/*OPT: do not compute full pubs[pub_idx] in ge form; we only need x */
427+
secp256k1_ge_set_gej_var(&c, &pubs[pub_idx]);
408428
secp256k1_rangeproof_serialize_point(tmpc, &c);
409429
quadness = tmpc[0];
410430
secp256k1_sha256_write(&sha256_m, tmpc, 33);
411431
signs[i>>3] |= quadness << (i&7);
412432
memcpy(&proof[len], tmpc + 1, 32);
413433
len += 32;
414434
}
415-
npub += rsizes[i];
435+
pub_idx += header.rsizes[i];
416436
}
417-
secp256k1_rangeproof_pub_expand(pubs, exp, rsizes, rings, genp);
437+
secp256k1_rangeproof_pub_expand(pubs, header.exp, header.rsizes, header.n_rings, genp);
418438
if (extra_commit != NULL) {
419439
secp256k1_sha256_write(&sha256_m, extra_commit, extra_commit_len);
420440
}
421441
secp256k1_sha256_finalize(&sha256_m, tmp);
422-
if (!secp256k1_borromean_sign(ecmult_gen_ctx, &proof[len], s, pubs, sec, rsizes, &secidx_closure, rings, tmp, 32)) {
442+
if (!secp256k1_borromean_sign(ecmult_gen_ctx, &proof[len], s, pubs, sec, header.rsizes, &secidx_closure, header.n_rings, tmp, 32)) {
423443
return 0;
424444
}
425445
len += 32;
426-
for (i = 0; i < npub; i++) {
427-
secp256k1_scalar_get_b32(&proof[len],&s[i]);
446+
for (i = 0; i < pub_idx; i++) {
447+
secp256k1_scalar_get_b32(&proof[len], &s[i]);
428448
len += 32;
429449
}
430450
VERIFY_CHECK(len <= *plen);

0 commit comments

Comments
 (0)