mpt-crypto
Confidential Multi-Purpose Tokens Cryptographic Library
Loading...
Searching...
No Matches
bulletproof_aggregated.c
Go to the documentation of this file.
1
38#include "secp256k1_mpt.h"
39#include <openssl/sha.h>
40#include <openssl/rand.h>
41#include <string.h>
42#include <assert.h>
43#include <stdlib.h>
44#include <secp256k1.h>
45
46/* Bit-size of each value proved in range */
47#define BP_VALUE_BITS 64
48
49/* Compute total vector length for aggregated Bulletproof */
50#define BP_TOTAL_BITS(m) ((size_t)(BP_VALUE_BITS * (m)))
51
52/* Compute IPA rounds = log2(total_bits) */
53static inline size_t bp_ipa_rounds(size_t total_bits) {
54 size_t r = 0;
55 while (total_bits > 1) {
56 total_bits >>= 1;
57 r++;
58 }
59 return r;
60}
61
66 const secp256k1_context* ctx,
67 unsigned char* scalar_bytes)
68{
69 do {
70 if (RAND_bytes(scalar_bytes, 32) != 1) {
71 return 0; // Randomness failure
72 }
73 } while (secp256k1_ec_seckey_verify(ctx, scalar_bytes) != 1);
74 return 1;
75}
76
82 const secp256k1_context* ctx,
83 secp256k1_pubkey* mG,
84 uint64_t amount)
85{
86 unsigned char amount_scalar[32] = {0};
87
88 /* Zero amount is handled by the caller (no G term needed) */
89 if (amount == 0) {
90 return 0;
91 }
92
93 for (int i = 0; i < 8; ++i) {
94 amount_scalar[31 - i] = (amount >> (i * 8)) & 0xFF;
95 }
96 return secp256k1_ec_pubkey_create(ctx, mG, amount_scalar);
97}
98
102static int add_term(
103 const secp256k1_context* ctx,
104 secp256k1_pubkey* acc,
105 int* acc_inited,
106 const secp256k1_pubkey* term
107) {
108 if (!(*acc_inited)) {
109 *acc = *term;
110 *acc_inited = 1;
111 return 1;
112 } else {
113 const secp256k1_pubkey* pts[2] = { acc, term };
114 secp256k1_pubkey sum;
115 if (!secp256k1_ec_pubkey_combine(ctx, &sum, pts, 2)) return 0;
116 *acc = sum;
117 return 1;
118 }
119}
120
124static void secp256k1_mpt_scalar_sub(unsigned char *res,
125 const unsigned char *a,
126 const unsigned char *b)
127{
128 unsigned char neg_b[32];
129 memcpy(neg_b, b, 32);
130 secp256k1_mpt_scalar_negate(neg_b, neg_b); /* neg_b = -b mod q */
131 secp256k1_mpt_scalar_add(res, a, neg_b); /* res = a + (-b) */
132 OPENSSL_cleanse(neg_b, 32);
133}
134
145 const secp256k1_context* ctx,
146 unsigned char* out,
147 const unsigned char* a,
148 const unsigned char* b,
149 size_t n
150) {
151 unsigned char acc[32] = {0};
152 unsigned char term[32];
153
154 for (size_t i = 0; i < n; i++) {
155 secp256k1_mpt_scalar_mul(term, a + i * 32, b + i * 32);
156 secp256k1_mpt_scalar_add(acc, acc, term);
157 }
158 memcpy(out, acc, 32);
159 return 1;
160}
161
165
167 const secp256k1_context* ctx,
168 secp256k1_pubkey* acc,
169 const secp256k1_pubkey* term)
170{
171 const secp256k1_pubkey* points[2] = {acc, term};
172 secp256k1_pubkey temp_sum;
173
174 if (secp256k1_ec_pubkey_combine(ctx, &temp_sum, points, 2) != 1) return 0;
175 *acc = temp_sum;
176 return 1;
177}
178
191 const secp256k1_context* ctx,
192 secp256k1_pubkey* r_out,
193 const secp256k1_pubkey* points,
194 const unsigned char* scalars,
195 size_t n
196) {
197 secp256k1_pubkey acc;
198 memset(&acc, 0, sizeof(acc));
199 int initialized = 0;
200 unsigned char zero[32] = {0};
201
202 for (size_t i = 0; i < n; ++i) {
203 if (memcmp(scalars + i * 32, zero, 32) == 0)
204 continue;
205
206 secp256k1_pubkey term = points[i];
207 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &term, scalars + i * 32))
208 return 0;
209
210 if (!add_term(ctx, &acc, &initialized, &term))
211 return 0;
212 }
213
214 /* All scalars zero → result is infinity (not representable here) */
215 if (!initialized)
216 return 0;
217
218 *r_out = acc;
219 return 1;
220}
221/* Try to add MSM(points, scalars) into acc.
222 * If MSM is all-zero, do nothing and succeed.
223 */
224static int msm_try_add(
225 const secp256k1_context* ctx,
226 secp256k1_pubkey* acc,
227 int* acc_inited,
228 const secp256k1_pubkey* points,
229 const unsigned char* scalars,
230 size_t n
231) {
232 secp256k1_pubkey tmp;
233
234 /* MSM returns 0 iff all scalars are zero.
235 * In that case, we have nothing to add, so we return success (1). */
236 if (!secp256k1_bulletproof_ipa_msm(ctx, &tmp, points, scalars, n)) {
237 return 1;
238 }
239 return add_term(ctx, acc, acc_inited, &tmp);
240}
241
245void scalar_vector_mul(const secp256k1_context* ctx, unsigned char res[][32],
246 unsigned char a[][32], unsigned char b[][32], size_t n) {
247 for (size_t i = 0; i < n; i++) {
248 secp256k1_mpt_scalar_mul(res[i], a[i], b[i]);
249 }
250}
251
255void scalar_vector_add(const secp256k1_context* ctx, unsigned char res[][32],
256 unsigned char a[][32], unsigned char b[][32], size_t n) {
257 for (size_t i = 0; i < n; i++) {
258 secp256k1_mpt_scalar_add(res[i], a[i], b[i]);
259 }
260
261}
262
267 const secp256k1_context* ctx,
268 unsigned char res[][32],
269 const unsigned char* y,
270 size_t n
271) {
272 if (n == 0) return;
273
274 unsigned char one[32] = {0};
275 one[31] = 1;
276 memcpy(res[0], one, 32);
277
278 for (size_t i = 1; i < n; i++) {
279 secp256k1_mpt_scalar_mul(res[i], res[i-1], y);
280 }
281}
282
285static void scalar_pow_u32(
286 const secp256k1_context* ctx,
287 unsigned char y_pow_out[32],
288 const unsigned char y[32],
289 unsigned int i)
290{
291 (void)ctx;
292 unsigned char one[32] = {0};
293 one[31] = 1;
294 memcpy(y_pow_out, one, 32);
295
296 while (i--) {
297 secp256k1_mpt_scalar_mul(y_pow_out, y_pow_out, y);
298 }
299}
300
304 const secp256k1_context* ctx,
305 unsigned char (*z_j2)[32], /* m x 32 */
306 const unsigned char z[32],
307 size_t m
308) {
309 for (size_t j = 0; j < m; j++) {
310 scalar_pow_u32(ctx, z_j2[j], z, (unsigned int)(j + 2));
311 }
312}
313
318 const secp256k1_context* ctx,
319 secp256k1_pubkey* r_out,
320 const secp256k1_pubkey* p_in,
321 const unsigned char* s_scalar)
322{
323 *r_out = *p_in;
324 return secp256k1_ec_pubkey_tweak_mul(ctx, r_out, s_scalar);
325}
326
336 const secp256k1_context* ctx,
337 unsigned char (*y_block_sum)[32], /* m blocks */
338 unsigned char two_sum[32],
339 const unsigned char y[32],
340 size_t m
341) {
342 (void)ctx;
343
344 unsigned char one[32] = {0};
345 unsigned char y_pow[32];
346 unsigned char two_pow[32];
347
348 one[31] = 1;
349
350 /* Compute two_sum = sum_{i=0}^{63} 2^i */
351 memset(two_sum, 0, 32);
352 memcpy(two_pow, one, 32);
353 for (size_t i = 0; i < 64; i++) {
354 secp256k1_mpt_scalar_add(two_sum, two_sum, two_pow);
355 secp256k1_mpt_scalar_add(two_pow, two_pow, two_pow);
356 }
357
358 /* Compute y_block_sum[j] = sum_{i=0}^{63} y^{64j + i} */
359 memcpy(y_pow, one, 32); /* y^0 */
360
361 for (size_t j = 0; j < m; j++) {
362 memset(y_block_sum[j], 0, 32);
363
364 for (size_t i = 0; i < 64; i++) {
365 secp256k1_mpt_scalar_add(y_block_sum[j], y_block_sum[j], y_pow);
366 secp256k1_mpt_scalar_mul(y_pow, y_pow, y); /* advance y^k */
367 }
368 }
369}
370
373static int pubkey_equal(const secp256k1_context* ctx, const secp256k1_pubkey* a, const secp256k1_pubkey* b) {
374 return secp256k1_ec_pubkey_cmp(ctx, a, b) == 0;
375}
376
382 const secp256k1_context* ctx,
383 secp256k1_pubkey* final_point,
384 const secp256k1_pubkey* generators,
385 const unsigned char* u_flat,
386 const unsigned char* uinv_flat,
387 size_t n,
388 size_t rounds,
389 int is_H /* 0 = G folding, 1 = H folding */
390) {
391 /* n must be power-of-two and rounds must match log2(n) */
392 if (n == 0 || (n & (n - 1)) != 0) return 0;
393 if (((size_t)1 << rounds) != n) return 0;
394
395 /* Allocate scalars for MSM: n * 32 bytes */
396 unsigned char* s_flat = (unsigned char*)malloc(n * 32);
397 if (!s_flat) return 0;
398
399 unsigned char current_s[32];
400 int ok = 0;
401
402 for (size_t i = 0; i < n; i++) {
403 /* current_s = 1 */
404 memset(current_s, 0, 32);
405 current_s[31] = 1;
406
407 for (size_t j = 0; j < rounds; j++) {
408 /* bit from MSB to LSB across 'rounds' bits */
409 int bit = (int)((i >> (rounds - 1 - j)) & 1);
410
411 const unsigned char* uj = u_flat + 32 * j;
412 const unsigned char* ujinv = uinv_flat + 32 * j;
413
414 if (!is_H) {
415 /* G folding: bit 0 -> u_inv, bit 1 -> u */
416 secp256k1_mpt_scalar_mul(current_s, current_s, bit ? uj : ujinv);
417 } else {
418 /* H folding: bit 0 -> u, bit 1 -> u_inv */
419 secp256k1_mpt_scalar_mul(current_s, current_s, bit ? ujinv : uj);
420 }
421 }
422
423 memcpy(s_flat + (i * 32), current_s, 32);
424 }
425
426 ok = secp256k1_bulletproof_ipa_msm(ctx, final_point, generators, s_flat, n);
427
428 OPENSSL_cleanse(current_s, 32);
429 OPENSSL_cleanse(s_flat, n * 32);
430 free(s_flat);
431
432 return ok;
433}
434/*
435 * Apply verifier-side IPA updates to P for `rounds` rounds.
436 * Update rule per round i:
437 * P <- P + (u_i^2) * L_i + (u_i^{-2}) * R_i
438 * u_flat / uinv_flat are (rounds * 32)-byte arrays:
439 * u_i = u_flat + 32*i
440 * u_iinv = uinv_flat + 32*i
441 */
443 const secp256k1_context* ctx,
444 secp256k1_pubkey* P,
445 const secp256k1_pubkey* L_vec,
446 const secp256k1_pubkey* R_vec,
447 const unsigned char* u_flat,
448 const unsigned char* uinv_flat,
449 size_t rounds
450) {
451 unsigned char u_sq[32], uinv_sq[32];
452 secp256k1_pubkey tL, tR;
453 const secp256k1_pubkey* pts[3];
454
455 for (size_t i = 0; i < rounds; i++) {
456 const unsigned char* ui = u_flat + 32 * i;
457 const unsigned char* uiinv = uinv_flat + 32 * i;
458
459 /* u_sq = u_i^2, uinv_sq = (u_i^{-1})^2 = u_i^{-2} */
460 secp256k1_mpt_scalar_mul(u_sq, ui, ui);
461 secp256k1_mpt_scalar_mul(uinv_sq, uiinv, uiinv);
462
463 /* tL = (u_i^2) * L_i */
464 tL = L_vec[i];
465 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &tL, u_sq)) {
466 OPENSSL_cleanse(u_sq, 32);
467 OPENSSL_cleanse(uinv_sq, 32);
468 return 0;
469 }
470
471 /* tR = (u_i^{-2}) * R_i */
472 tR = R_vec[i];
473 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &tR, uinv_sq)) {
474 OPENSSL_cleanse(u_sq, 32);
475 OPENSSL_cleanse(uinv_sq, 32);
476 return 0;
477 }
478
479 /* P <- P + tL + tR */
480 pts[0] = P;
481 pts[1] = &tL;
482 pts[2] = &tR;
483
484 secp256k1_pubkey newP;
485 if (!secp256k1_ec_pubkey_combine(ctx, &newP, pts, 3)) {
486 OPENSSL_cleanse(u_sq, 32);
487 OPENSSL_cleanse(uinv_sq, 32);
488 return 0;
489 }
490 *P = newP;
491
492 }
493
494 OPENSSL_cleanse(u_sq, 32);
495 OPENSSL_cleanse(uinv_sq, 32);
496 return 1;
497}
498
510
512 const secp256k1_context* ctx,
513 secp256k1_pubkey* L,
514 secp256k1_pubkey* R,
515 const unsigned char* a_L,
516 const unsigned char* a_R,
517 const unsigned char* b_L,
518 const unsigned char* b_R,
519 const secp256k1_pubkey* G_L,
520 const secp256k1_pubkey* G_R,
521 const secp256k1_pubkey* H_L,
522 const secp256k1_pubkey* H_R,
523 const secp256k1_pubkey* U,
524 const unsigned char* ux,
525 size_t half_n
526) {
527 unsigned char cL[32], cR[32];
528 unsigned char cLux[32], cRux[32];
529 unsigned char zero[32] = {0};
530
531 secp256k1_pubkey acc, term;
532 int acc_inited; /* Tracks if acc contains a valid point */
533
534 /* cL = <a_L, b_R>, cR = <a_R, b_L> */
535 if (!secp256k1_bulletproof_ipa_dot(ctx, cL, a_L, b_R, half_n)) return 0;
536 if (!secp256k1_bulletproof_ipa_dot(ctx, cR, a_R, b_L, half_n)) return 0;
537
538 /* ---------------- L Calculation ---------------- */
539 acc_inited = 0;
540
541 /* Try adding terms. correct logic updates acc_inited to 1 */
542 if (!msm_try_add(ctx, &acc, &acc_inited, G_R, a_L, half_n)) goto cleanup;
543 if (!msm_try_add(ctx, &acc, &acc_inited, H_L, b_R, half_n)) goto cleanup;
544
545 secp256k1_mpt_scalar_mul(cLux, cL, ux);
546 if (memcmp(cLux, zero, 32) != 0) {
547 term = *U;
548 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &term, cLux)) goto cleanup;
549 if (!add_term(ctx, &acc, &acc_inited, &term)) goto cleanup;
550 }
551
552 /* Check initialization explicitly */
553 if (acc_inited == 0) {
554 /* L resulted in Infinity. This is theoretically possible but invalid for serialization.
555 * We cannot proceed. */
556 goto cleanup;
557 }
558 *L = acc;
559
560 /* ---------------- R Calculation ---------------- */
561 acc_inited = 0;
562
563 if (!msm_try_add(ctx, &acc, &acc_inited, G_L, a_R, half_n)) goto cleanup;
564 if (!msm_try_add(ctx, &acc, &acc_inited, H_R, b_L, half_n)) goto cleanup;
565
566 secp256k1_mpt_scalar_mul(cRux, cR, ux);
567 if (memcmp(cRux, zero, 32) != 0) {
568 term = *U;
569 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &term, cRux)) goto cleanup;
570 if (!add_term(ctx, &acc, &acc_inited, &term)) goto cleanup;
571 }
572
573 if (acc_inited == 0) {
574 goto cleanup;
575 }
576 *R = acc;
577
578 OPENSSL_cleanse(cL, 32);
579 OPENSSL_cleanse(cR, 32);
580 OPENSSL_cleanse(cLux, 32);
581 OPENSSL_cleanse(cRux, 32);
582 return 1;
583
584 cleanup:
585 OPENSSL_cleanse(cL, 32);
586 OPENSSL_cleanse(cR, 32);
587 OPENSSL_cleanse(cLux, 32);
588 OPENSSL_cleanse(cRux, 32);
589 return 0;
590}
591
608 const secp256k1_context* ctx,
609 unsigned char* a,
610 unsigned char* b,
611 secp256k1_pubkey* G,
612 secp256k1_pubkey* H,
613 size_t half_n,
614 const unsigned char* x,
615 const unsigned char* x_inv
616) {
617 size_t i;
618 int ok = 0;
619
620 unsigned char t1[32], t2[32];
621 secp256k1_pubkey left, right;
622 const secp256k1_pubkey* pts[2];
623
624 if (ctx == NULL || a == NULL || b == NULL || G == NULL || H == NULL) return 0;
625 if (half_n == 0) return 0;
626
627 /* x and x_inv must be valid non-zero scalars */
628 if (secp256k1_ec_seckey_verify(ctx, x) != 1) return 0;
629 if (secp256k1_ec_seckey_verify(ctx, x_inv) != 1) return 0;
630
631 for (i = 0; i < half_n; ++i) {
632 unsigned char* aL = a + (i * 32);
633 unsigned char* aR = a + ((i + half_n) * 32);
634
635 unsigned char* bL = b + (i * 32);
636 unsigned char* bR = b + ((i + half_n) * 32);
637
638 /* a'[i] = aL*x + aR*x_inv */
639 secp256k1_mpt_scalar_mul(t1, aL, x);
640 secp256k1_mpt_scalar_mul(t2, aR, x_inv);
641 secp256k1_mpt_scalar_add(aL, t1, t2);
642
643 /* b'[i] = bL*x_inv + bR*x */
644 secp256k1_mpt_scalar_mul(t1, bL, x_inv);
645 secp256k1_mpt_scalar_mul(t2, bR, x);
646 secp256k1_mpt_scalar_add(bL, t1, t2);
647
648 /* G'[i] = GL*x_inv + GR*x */
649 left = G[i];
650 right = G[i + half_n];
651 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &left, x_inv)) goto cleanup;
652 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &right, x)) goto cleanup;
653 pts[0] = &left; pts[1] = &right;
654 if (!secp256k1_ec_pubkey_combine(ctx, &G[i], pts, 2)) goto cleanup;
655
656 /* H'[i] = HL*x + HR*x_inv */
657 left = H[i];
658 right = H[i + half_n];
659 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &left, x)) goto cleanup;
660 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &right, x_inv)) goto cleanup;
661 pts[0] = &left; pts[1] = &right;
662 if (!secp256k1_ec_pubkey_combine(ctx, &H[i], pts, 2)) goto cleanup;
663 }
664
665 ok = 1;
666
667 cleanup:
668 OPENSSL_cleanse(t1, 32);
669 OPENSSL_cleanse(t2, 32);
670 return ok;
671}
672static int scalar_is_zero(const unsigned char s[32]) {
673 unsigned char z[32] = {0};
674 return memcmp(s, z, 32) == 0;
675}
676
677/*
678 * ux is the fixed IPA binding scalar.
679 *
680 * It MUST be derived exactly once from:
681 * ux = H(commit_inp || <a,b>)
682 *
683 * and reused consistently throughout the IPA:
684 * - L/R cross-term construction
685 * - final (a·b·ux)·g term
686 *
687 * It MUST NOT depend on per-round challenges (u_i),
688 * and MUST be identical for prover and verifier.
689 */
691 const secp256k1_context* ctx,
692 unsigned char* ux_out,
693 const unsigned char* commit_inp_32,
694 const unsigned char* dot_32)
695{
696 unsigned char hash_input[64];
697 unsigned char hash_output[32];
698
699 /* 1. Build hash input = commit_inp || dot */
700 memcpy(hash_input, commit_inp_32, 32);
701 memcpy(hash_input + 32, dot_32, 32);
702
703 /* 2. Hash */
704 SHA256(hash_input, 64, hash_output);
705
706 /* 3. Reduce hash to a valid scalar */
707 /* CRITICAL: Wraps the 32-byte random string into the curve order */
708 secp256k1_mpt_scalar_reduce32(ux_out, hash_output);
709
710 /* * 4. Verify (Sanity check)
711 * Reduce32 guarantees the value is < Order.
712 * This checks for the virtually impossible case where hash is exactly 0.
713 */
714 if (secp256k1_ec_seckey_verify(ctx, ux_out) != 1) {
715 return 0;
716 }
717
718 return 1;
719}
720
725 const secp256k1_context* ctx,
726 unsigned char u_out[32],
727 const unsigned char last_challenge[32],
728 const secp256k1_pubkey* L,
729 const secp256k1_pubkey* R)
730{
731 unsigned char L_ser[33], R_ser[33];
732 size_t len = 33;
733 SHA256_CTX sha;
734 unsigned char hash[32];
735
736 if (!secp256k1_ec_pubkey_serialize(ctx, L_ser, &len, L, SECP256K1_EC_COMPRESSED)) return 0;
737 len = 33;
738 if (!secp256k1_ec_pubkey_serialize(ctx, R_ser, &len, R, SECP256K1_EC_COMPRESSED)) return 0;
739
740 SHA256_Init(&sha);
741 SHA256_Update(&sha, last_challenge, 32);
742 SHA256_Update(&sha, L_ser, 33);
743 SHA256_Update(&sha, R_ser, 33);
744 SHA256_Final(hash, &sha);
746 memcpy(u_out, hash, 32);
747
748 /* Reject invalid scalar (0 or >= group order). */
749 if (secp256k1_ec_seckey_verify(ctx, u_out) != 1) return 0;
750
751 return 1;
752}
753
760 const secp256k1_context* ctx,
761 const secp256k1_pubkey* g,
762 secp256k1_pubkey* G_vec,
763 secp256k1_pubkey* H_vec,
764 unsigned char* a_vec,
765 unsigned char* b_vec,
766 size_t n,
767 const unsigned char ipa_transcript_id[32],
768 const unsigned char ux_scalar[32],
769 secp256k1_pubkey* L_out,
770 secp256k1_pubkey* R_out,
771 size_t max_rounds,
772 size_t* rounds_out,
773 unsigned char a_final[32],
774 unsigned char b_final[32]
775) {
776 size_t rounds = 0;
777 size_t cur_n = n;
778 int ok = 0;
779
780 unsigned char u_scalar[32], u_inv[32];
781 unsigned char last_challenge[32];
782
783 /* Validate n is power of 2 */
784 if (n == 0 || (n & (n - 1)) != 0) return 0;
785
786 /* rounds = log2(n) */
787 while (cur_n > 1) { cur_n >>= 1; rounds++; }
788 cur_n = n;
789
790 /* Bounds check (CRITICAL for aggregated proofs) */
791 if (rounds > max_rounds) return 0;
792
793 /* Seed transcript */
794 memcpy(last_challenge, ipa_transcript_id, 32);
795
796 for (size_t r = 0; r < rounds; ++r) {
797 size_t half_n = cur_n >> 1;
798 secp256k1_pubkey Lr, Rr;
799
800 /* 1) Compute cross-term commitments Lr, Rr */
802 ctx, &Lr, &Rr,
803 a_vec, a_vec + half_n * 32,
804 b_vec, b_vec + half_n * 32,
805 G_vec, G_vec + half_n,
806 H_vec, H_vec + half_n,
807 g,
808 ux_scalar,
809 half_n
810 )) goto cleanup;
811
812 /* 2) Store L/R */
813 L_out[r] = Lr;
814 R_out[r] = Rr;
815
816 /* 3) Fiat–Shamir round challenge u_r */
817 if (!derive_ipa_round_challenge(ctx, u_scalar, last_challenge, &Lr, &Rr))
818 goto cleanup;
819
820 /* 4) u_r^{-1} */
821 secp256k1_mpt_scalar_inverse(u_inv, u_scalar);
822 if (!secp256k1_ec_seckey_verify(ctx, u_inv)) goto cleanup;
823
824 /* 5) Update transcript chaining state */
825 memcpy(last_challenge, u_scalar, 32);
826
827 /* 6) Fold vectors in-place */
829 ctx, a_vec, b_vec, G_vec, H_vec, half_n, u_scalar, u_inv
830 )) goto cleanup;
831
832 cur_n = half_n;
833 }
834
835 /* Final folded scalars */
836 memcpy(a_final, a_vec, 32);
837 memcpy(b_final, b_vec, 32);
838
839 if (rounds_out) *rounds_out = rounds;
840 ok = 1;
841
842 cleanup:
843 OPENSSL_cleanse(u_scalar, 32);
844 OPENSSL_cleanse(u_inv, 32);
845 return ok;
846}
847
848/*
849 * Verifies a Bulletproof Inner Product Argument (IPA).
850 *
851 * Given:
852 * - the original generator vectors G_vec and H_vec,
853 * - the prover’s cross-term commitments L_i and R_i,
854 * - the final folded scalars a_final and b_final,
855 * - the binding scalar ux,
856 * - and the initial commitment P,
857 *
858 * this function re-derives all Fiat–Shamir challenges u_i from the transcript
859 * and reconstructs the folded generators G_f and H_f implicitly.
860 *
861 * Verification checks that the folded commitment P' equals:
862 *
863 * P' = a_final * G_f
864 * + b_final * H_f
865 * + (a_final * b_final * ux) * U
866 *
867 * where G_f and H_f are obtained by folding G_vec and H_vec using the challenges
868 * u_i and their inverses, and P' is obtained by applying the same folding
869 * operations to P using the L_i and R_i commitments.
870 *
871 * All group operations avoid explicit construction of the point at infinity,
872 * which is not representable via the libsecp256k1 public-key API.
873 */
875 const secp256k1_context* ctx,
876 const secp256k1_pubkey* G_vec, /* original G generators (length n) */
877 const secp256k1_pubkey* H_vec, /* original H generators (length n) */
878 const secp256k1_pubkey* U,
879 const secp256k1_pubkey* P_in, /* initial P */
880 const secp256k1_pubkey* L_vec, /* length = rounds */
881 const secp256k1_pubkey* R_vec, /* length = rounds */
882 size_t n, /* total vector length (64*m) */
883 const unsigned char a_final[32],
884 const unsigned char b_final[32],
885 const unsigned char ux[32],
886 const unsigned char ipa_transcript_id[32]
887) {
888 secp256k1_pubkey P = *P_in;
889 secp256k1_pubkey Gf, Hf, RHS, tmp;
890 int RHS_inited = 0;
891 int ok = 0;
892
893 /* --- derive rounds --- */
894 if (n == 0 || (n & (n - 1)) != 0)
895 return 0;
896
897 size_t rounds = bp_ipa_rounds(n);
898
899 /* --- allocate u / u_inv --- */
900 unsigned char* u_flat = (unsigned char*)malloc(rounds * 32);
901 unsigned char* uinv_flat = (unsigned char*)malloc(rounds * 32);
902 if (!u_flat || !uinv_flat)
903 goto cleanup_alloc;
904
905 unsigned char last[32];
906 memcpy(last, ipa_transcript_id, 32);
907
908 /* ---- 1. Re-derive u_i ---- */
909 for (size_t i = 0; i < rounds; i++) {
910 unsigned char* ui = u_flat + 32 * i;
911 unsigned char* uiinv = uinv_flat + 32 * i;
912
913 if (!derive_ipa_round_challenge(ctx, ui, last, &L_vec[i], &R_vec[i]))
914 goto cleanup;
915
917 if (!secp256k1_ec_seckey_verify(ctx, uiinv))
918 goto cleanup;
919
920 memcpy(last, ui, 32);
921 }
922
923 /* ---- 2. Fold generators ---- */
924 if (!fold_generators(ctx, &Gf, G_vec, u_flat, uinv_flat, n, rounds, 0))
925 goto cleanup;
926
927 if (!fold_generators(ctx, &Hf, H_vec, u_flat, uinv_flat, n, rounds, 1))
928 goto cleanup;
929
930 /* ---- 3. RHS = a*Gf + b*Hf + (a*b*ux)*U ---- */
931
932 if (!scalar_is_zero(a_final)) {
933 tmp = Gf;
934 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &tmp, a_final))
935 goto cleanup;
936 if (!add_term(ctx, &RHS, &RHS_inited, &tmp))
937 goto cleanup;
938 }
939
940 if (!scalar_is_zero(b_final)) {
941 tmp = Hf;
942 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &tmp, b_final))
943 goto cleanup;
944 if (!add_term(ctx, &RHS, &RHS_inited, &tmp))
945 goto cleanup;
946 }
947
948 {
949 unsigned char ab[32], ab_ux[32];
950 secp256k1_mpt_scalar_mul(ab, a_final, b_final);
951 secp256k1_mpt_scalar_mul(ab_ux, ab, ux);
952
953 if (!scalar_is_zero(ab_ux)) {
954 tmp = *U;
955 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &tmp, ab_ux))
956 goto cleanup;
957 if (!add_term(ctx, &RHS, &RHS_inited, &tmp))
958 goto cleanup;
959 }
960
961 OPENSSL_cleanse(ab, 32);
962 OPENSSL_cleanse(ab_ux, 32);
963 }
964
965 if (!RHS_inited)
966 goto cleanup;
967
968 /* ---- 4. Fold P using L/R ---- */
970 ctx, &P, L_vec, R_vec, u_flat, uinv_flat, rounds))
971 goto cleanup;
972
973 /* ---- 5. Compare P and RHS ---- */
974 if (pubkey_equal(ctx, &P, &RHS))
975 ok = 1;
976
977 cleanup:
978 OPENSSL_cleanse(u_flat, rounds * 32);
979 OPENSSL_cleanse(uinv_flat, rounds * 32);
980
981 cleanup_alloc:
982 free(u_flat);
983 free(uinv_flat);
984 return ok;
985}
986
999 const secp256k1_context* ctx,
1000 uint64_t value,
1001 size_t block_index, /* j-th value */
1002 unsigned char* al, /* length = BP_TOTAL_BITS(m) * 32 */
1003 unsigned char* ar,
1004 unsigned char* sl,
1005 unsigned char* sr
1006) {
1007 const size_t offset = BP_VALUE_BITS * block_index;
1008 int ok = 1;
1009
1010 /* Scalars */
1011 unsigned char one[32] = {0};
1012 unsigned char minus_one[32];
1013 unsigned char zero[32] = {0};
1014
1015 one[31] = 1;
1016 memcpy(minus_one, one, 32);
1017 secp256k1_mpt_scalar_negate(minus_one, minus_one);
1018
1019 /* ---- 1. Encode value bits into al/ar ---- */
1020 for (size_t i = 0; i < BP_VALUE_BITS; i++) {
1021 size_t idx = offset + i;
1022
1023 if ((value >> i) & 1) {
1024 /* bit = 1 => al = 1, ar = 0 */
1025 memcpy(al + idx * 32, one, 32);
1026 memcpy(ar + idx * 32, zero, 32);
1027 } else {
1028 /* bit = 0 => al = 0, ar = -1 */
1029 memcpy(al + idx * 32, zero, 32);
1030 memcpy(ar + idx * 32, minus_one, 32);
1031 }
1032 }
1033
1034 /* ---- 2. Generate random blinding vectors sl/sr ---- */
1035 for (size_t i = 0; i < BP_VALUE_BITS; i++) {
1036 size_t idx = offset + i;
1037
1038 if (!generate_random_scalar(ctx, sl + idx * 32)) {
1039 ok = 0;
1040 goto cleanup;
1041 }
1042 if (!generate_random_scalar(ctx, sr + idx * 32)) {
1043 ok = 0;
1044 goto cleanup;
1045 }
1046 }
1047
1048 return 1;
1049
1050 cleanup:
1051 /* Wipe only the affected block */
1052 OPENSSL_cleanse(al + offset * 32, BP_VALUE_BITS * 32);
1053 OPENSSL_cleanse(ar + offset * 32, BP_VALUE_BITS * 32);
1054 OPENSSL_cleanse(sl + offset * 32, BP_VALUE_BITS * 32);
1055 OPENSSL_cleanse(sr + offset * 32, BP_VALUE_BITS * 32);
1056 return 0;
1057}
1058
1062 const secp256k1_context* ctx,
1063 secp256k1_pubkey* commitment_C,
1064 uint64_t value,
1065 const unsigned char* blinding_factor,
1066 const secp256k1_pubkey* pk_base
1067) {
1068 secp256k1_pubkey G_term, Pk_term;
1069 const secp256k1_pubkey* points_to_add[2];
1070 int v_is_zero = (value == 0);
1071
1072 /* 1. Compute r * Pk_base (The Blinding Term) */
1073 Pk_term = *pk_base;
1074 if (secp256k1_ec_pubkey_tweak_mul(ctx, &Pk_term, blinding_factor) != 1) return 0;
1075
1076 /* 2. Handle Value Term */
1077 if (v_is_zero) {
1078 /* If v=0, C = 0*G + r*H = r*H.
1079 We skip G_term entirely because libsecp cannot represent infinity. */
1080 *commitment_C = Pk_term;
1081 return 1;
1082 }
1083
1084 /* 3. Compute v * G (The Value Term) */
1085 if (!compute_amount_point(ctx, &G_term, value)) return 0;
1086
1087 /* 4. Combine: C = v*G + r*Pk_base */
1088 points_to_add[0] = &G_term;
1089 points_to_add[1] = &Pk_term;
1090 if (secp256k1_ec_pubkey_combine(ctx, commitment_C, points_to_add, 2) != 1) return 0;
1091
1092 return 1;
1093}
1094
1115 const secp256k1_context* ctx,
1116 unsigned char* proof_out,
1117 size_t* proof_len,
1118 const uint64_t* values,
1119 const unsigned char* blindings_flat,
1120 size_t m,
1121 const secp256k1_pubkey* pk_base,
1122 const unsigned char* context_id
1123) {
1124 /* ---- 0. Dimensions ---- */
1125 const size_t n = BP_TOTAL_BITS(m); /* 64*m */
1126 const size_t rounds = bp_ipa_rounds(n); /* log2(64*m) */
1127
1128 /* 64*m must be power-of-two -> m must be power-of-two */
1129 if (m == 0) return 0;
1130 if ((n & (n - 1)) != 0) return 0;
1131
1132 /* Proof length = 4*33 + 2*rounds*33 + 5*32 */
1133 const size_t proof_size = 292 + 66 * rounds;
1134 if (proof_len) *proof_len = proof_size;
1135
1136 int ok = 0;
1137
1138 /* ---- 1. Allocate vectors ---- */
1139 secp256k1_pubkey* G_vec = (secp256k1_pubkey*)malloc(n * sizeof(secp256k1_pubkey));
1140 secp256k1_pubkey* H_vec = (secp256k1_pubkey*)malloc(n * sizeof(secp256k1_pubkey));
1141 secp256k1_pubkey* H_prime = (secp256k1_pubkey*)malloc(n * sizeof(secp256k1_pubkey));
1142 unsigned char* al = (unsigned char*)malloc(n * 32);
1143 unsigned char* ar = (unsigned char*)malloc(n * 32);
1144 unsigned char* sl = (unsigned char*)malloc(n * 32);
1145 unsigned char* sr = (unsigned char*)malloc(n * 32);
1146 unsigned char* l_vec = (unsigned char*)malloc(n * 32);
1147 unsigned char* r_vec = (unsigned char*)malloc(n * 32);
1148 unsigned char* r1_vec = (unsigned char*)malloc(n * 32);
1149
1150 secp256k1_pubkey* L_vec = (secp256k1_pubkey*)malloc(rounds * sizeof(secp256k1_pubkey));
1151 secp256k1_pubkey* R_vec = (secp256k1_pubkey*)malloc(rounds * sizeof(secp256k1_pubkey));
1152
1153 unsigned char* y_powers = (unsigned char*)malloc(n * 32); /* y^i */
1154 unsigned char* z_j2 = (unsigned char*)malloc(m * 32); /* z^(j+2) */
1155
1156 if (!G_vec || !H_vec || !H_prime ||
1157 !al || !ar || !sl || !sr || !l_vec || !r_vec || !r1_vec ||
1158 !L_vec || !R_vec ||
1159 !y_powers || !z_j2) {
1160 goto cleanup;
1161 }
1162
1163 /* ---- 2. Scalars / points ---- */
1164 secp256k1_pubkey A, S, T1, T2, U;
1165
1166 unsigned char alpha[32], rho[32];
1167 unsigned char tau1[32], tau2[32];
1168 unsigned char t1[32], t2[32];
1169 unsigned char t_hat[32], tau_x[32], mu[32];
1170 unsigned char a_final[32], b_final[32];
1171
1172 unsigned char y[32], z[32], x[32];
1173 unsigned char z_sq[32], z_neg[32], x_sq[32];
1174 unsigned char ux_scalar[32];
1175 unsigned char ipa_transcript[32];
1176
1177 unsigned char one[32] = {0}; one[31] = 1;
1178 unsigned char minus_one[32]; secp256k1_mpt_scalar_negate(minus_one, one);
1179 unsigned char zero[32] = {0};
1180
1181 /* ---- 3. Generator vectors ---- */
1182 if (!secp256k1_mpt_get_generator_vector(ctx, G_vec, n, (const unsigned char*)"G", 1)) goto cleanup;
1183 if (!secp256k1_mpt_get_generator_vector(ctx, H_vec, n, (const unsigned char*)"H", 1)) goto cleanup;
1184 {
1185 secp256k1_pubkey U_arr[1];
1186 if (!secp256k1_mpt_get_generator_vector(ctx, U_arr, 1, (const unsigned char*)"BP_U", 4)) goto cleanup;
1187 U = U_arr[0];
1188 }
1189
1190 /* ---- 4. Bit-decomposition for m values into al/ar (concat) + random sl/sr ---- */
1191 for (size_t j = 0; j < m; j++) {
1192 uint64_t v = values[j];
1193 for (size_t i = 0; i < BP_VALUE_BITS; i++) {
1194 const size_t k = j * BP_VALUE_BITS + i; /* 0..n-1 */
1195 unsigned char* al_k = al + 32*k;
1196 unsigned char* ar_k = ar + 32*k;
1197 unsigned char* sl_k = sl + 32*k;
1198 unsigned char* sr_k = sr + 32*k;
1199
1200 if ((v >> i) & 1) {
1201 memcpy(al_k, one, 32);
1202 memset(ar_k, 0, 32);
1203 } else {
1204 memset(al_k, 0, 32);
1205 memcpy(ar_k, minus_one, 32);
1206 }
1207
1208 if (!generate_random_scalar(ctx, sl_k)) goto cleanup;
1209 if (!generate_random_scalar(ctx, sr_k)) goto cleanup;
1210 }
1211 }
1212
1213 if (!generate_random_scalar(ctx, alpha)) goto cleanup;
1214 if (!generate_random_scalar(ctx, rho)) goto cleanup;
1215
1216 /* ---- 5. Commitments A and S ----
1217 * A = alpha*Base + <al,G> + <ar,H>
1218 * S = rho*Base + <sl,G> + <sr,H>
1219 */
1220 {
1221 secp256k1_pubkey tG, tH, tB;
1222 const secp256k1_pubkey* pts[3];
1223
1224 if (!secp256k1_bulletproof_ipa_msm(ctx, &tG, G_vec, al, n)) goto cleanup;
1225 if (!secp256k1_bulletproof_ipa_msm(ctx, &tH, H_vec, ar, n)) goto cleanup;
1226 tB = *pk_base;
1227 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &tB, alpha)) goto cleanup;
1228 pts[0] = &tB; pts[1] = &tG; pts[2] = &tH;
1229 if (!secp256k1_ec_pubkey_combine(ctx, &A, pts, 3)) goto cleanup;
1230
1231 if (!secp256k1_bulletproof_ipa_msm(ctx, &tG, G_vec, sl, n)) goto cleanup;
1232 if (!secp256k1_bulletproof_ipa_msm(ctx, &tH, H_vec, sr, n)) goto cleanup;
1233 tB = *pk_base;
1234 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &tB, rho)) goto cleanup;
1235 pts[0] = &tB; pts[1] = &tG; pts[2] = &tH;
1236 if (!secp256k1_ec_pubkey_combine(ctx, &S, pts, 3)) goto cleanup;
1237 }
1238
1239 /* ---- 6. Fiat–Shamir y,z ---- */
1240 {
1241 unsigned char A_ser[33], S_ser[33];
1242 size_t len = 33;
1243 SHA256_CTX sha;
1244
1245 if (!secp256k1_ec_pubkey_serialize(ctx, A_ser, &len, &A, SECP256K1_EC_COMPRESSED)) goto cleanup;
1246 len = 33;
1247 if (!secp256k1_ec_pubkey_serialize(ctx, S_ser, &len, &S, SECP256K1_EC_COMPRESSED)) goto cleanup;
1248
1249 /* --- START OF TRANSCRIPT --- */
1250 SHA256_Init(&sha);
1251
1252 /* 1. Domain Separation */
1253 SHA256_Update(&sha, "MPT_BULLETPROOF_RANGE", 21);
1254
1255 /* 2. Transaction Context [cite: 431] */
1256 if (context_id) SHA256_Update(&sha, context_id, 32);
1257
1258 /* 3. Statement: Value Commitments*/
1259 for (size_t i = 0; i < m; i++) {
1260 secp256k1_pubkey V_temp;
1261 unsigned char V_ser[33];
1262 size_t v_len = 33;
1263 /* Reconstruct commitment for the transcript */
1264 if (!secp256k1_bulletproof_create_commitment(ctx, &V_temp, values[i], blindings_flat + 32*i, pk_base)) goto cleanup;
1265 if (!secp256k1_ec_pubkey_serialize(ctx, V_ser, &v_len, &V_temp, SECP256K1_EC_COMPRESSED)) goto cleanup;
1266 SHA256_Update(&sha, V_ser, 33);
1267 }
1268
1269
1270 SHA256_Update(&sha, A_ser, 33);
1271 SHA256_Update(&sha, S_ser, 33);
1272
1273 SHA256_Final(y, &sha);
1275
1276 /* To match spec H(T1 || y), we continue from the previous state (or re-hash) */
1277 SHA256_Init(&sha);
1278 SHA256_Update(&sha, "MPT_BULLETPROOF_RANGE", 21);
1279 if (context_id) SHA256_Update(&sha, context_id, 32);
1280
1281 for (size_t i = 0; i < m; i++) {
1282 /* ... (re-add commitments) ... */
1283 secp256k1_pubkey V_temp;
1284 unsigned char V_ser[33];
1285 size_t v_len = 33;
1286 secp256k1_bulletproof_create_commitment(ctx, &V_temp, values[i], blindings_flat + 32*i, pk_base);
1287 secp256k1_ec_pubkey_serialize(ctx, V_ser, &v_len, &V_temp, SECP256K1_EC_COMPRESSED);
1288 SHA256_Update(&sha, V_ser, 33);
1289 }
1290 SHA256_Update(&sha, A_ser, 33);
1291 SHA256_Update(&sha, S_ser, 33);
1292 SHA256_Update(&sha, y, 32);
1293 SHA256_Final(z, &sha);
1295 memcpy(z_neg, z, 32);
1296 secp256k1_mpt_scalar_negate(z_neg, z_neg);
1297 }
1298
1299 /* ---- 7. Aggregated polynomial setup ---- */
1300
1301/* y_powers[k] = y^k */
1302 {
1303 unsigned char ypow[32];
1304 memcpy(ypow, one, 32);
1305
1306 for (size_t k = 0; k < n; k++) {
1307 memcpy(y_powers + 32*k, ypow, 32);
1308 secp256k1_mpt_scalar_mul(ypow, ypow, y);
1309 }
1310 OPENSSL_cleanse(ypow, 32);
1311 }
1312
1313/* z_j2[j] = z^(j+2) */
1314 compute_z_pows_j2(ctx, (unsigned char (*)[32])z_j2, z, m);
1315
1316/* l0, r0, r1 */
1317 for (size_t block = 0; block < m; block++) {
1318 const unsigned char* zblk = z_j2 + 32*block; /* z^(block+2) */
1319
1320 for (size_t i = 0; i < BP_VALUE_BITS; i++) {
1321 size_t k = block * BP_VALUE_BITS + i;
1322
1323 unsigned char* l0 = l_vec + 32*k;
1324 unsigned char* r0 = r_vec + 32*k;
1325 unsigned char* r1 = r1_vec + 32*k;
1326
1327 const unsigned char* al_k = al + 32*k;
1328 const unsigned char* ar_k = ar + 32*k;
1329 const unsigned char* sr_k = sr + 32*k;
1330 const unsigned char* yk = y_powers + 32*k;
1331
1332 unsigned char two_i[32] = {0};
1333 two_i[31 - (i >> 3)] = (unsigned char)(1u << (i & 7));
1334
1335 /* l0 = aL - z */
1336 secp256k1_mpt_scalar_add(l0, al_k, z_neg);
1337
1338 /* r0 = y^k * (aR + z) + z^(block+2) * 2^i */
1339 {
1340 unsigned char tmp1[32], tmp2[32];
1341
1342 /* tmp1 = aR + z */
1343 secp256k1_mpt_scalar_add(tmp1, ar_k, z);
1344
1345 /* r0 = y^k * tmp1 */
1346 secp256k1_mpt_scalar_mul(r0, tmp1, yk);
1347
1348 /* tmp2 = z^(block+2) * 2^i */
1349 secp256k1_mpt_scalar_mul(tmp2, zblk, two_i);
1350
1351 /* r0 += tmp2 */
1352 secp256k1_mpt_scalar_add(r0, r0, tmp2);
1353
1354 OPENSSL_cleanse(tmp1, 32);
1355 OPENSSL_cleanse(tmp2, 32);
1356 }
1357
1358 /* r1 = sR * y^k */
1359 secp256k1_mpt_scalar_mul(r1, sr_k, yk);
1360 }
1361 }
1362
1363/* t1 = <l0, r1> + <l1, r0> */
1364 {
1365 unsigned char dot1[32], dot2[32];
1366 if (!secp256k1_bulletproof_ipa_dot(ctx, dot1, l_vec, r1_vec, n)) goto cleanup;
1367 if (!secp256k1_bulletproof_ipa_dot(ctx, dot2, sl, r_vec, n)) goto cleanup;
1368 secp256k1_mpt_scalar_add(t1, dot1, dot2);
1369 OPENSSL_cleanse(dot1, 32);
1370 OPENSSL_cleanse(dot2, 32);
1371 }
1372
1373/* t2 = <l1, r1> */
1374 if (!secp256k1_bulletproof_ipa_dot(ctx, t2, sl, r1_vec, n)) goto cleanup;
1375
1376/* Make sure these exist before T1/T2 */
1377 if (!generate_random_scalar(ctx, tau1)) goto cleanup;
1378 if (!generate_random_scalar(ctx, tau2)) goto cleanup;
1379
1380/* ---- 8. Commit T1, T2 ---- */
1381/* T1 = t1*G + tau1*Base where G = G_vec[0] */
1382 {
1383 secp256k1_pubkey tG, tB;
1384 const secp256k1_pubkey* pts[2];
1385
1386 if (memcmp(t1, zero, 32) == 0) goto cleanup; /* prototype guard */
1387 /* tG = t1 * (curve base generator) */
1388 if (!secp256k1_ec_pubkey_create(ctx, &tG, t1)) goto cleanup;
1389
1390
1391 tB = *pk_base;
1392 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &tB, tau1)) goto cleanup;
1393
1394 pts[0] = &tG; pts[1] = &tB;
1395 if (!secp256k1_ec_pubkey_combine(ctx, &T1, pts, 2)) goto cleanup;
1396 }
1397
1398/* T2 = t2*G + tau2*Base */
1399 {
1400 secp256k1_pubkey tG, tB;
1401 const secp256k1_pubkey* pts[2];
1402
1403 if (memcmp(t2, zero, 32) == 0) goto cleanup; /* prototype guard */
1404 /* tG = t2 * (curve base generator) */
1405 if (!secp256k1_ec_pubkey_create(ctx, &tG, t2)) goto cleanup;
1406
1407
1408 tB = *pk_base;
1409 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &tB, tau2)) goto cleanup;
1410
1411 pts[0] = &tG; pts[1] = &tB;
1412 if (!secp256k1_ec_pubkey_combine(ctx, &T2, pts, 2)) goto cleanup;
1413 }
1414
1415/* ---- 9. Challenge x ---- */
1416/* x = H(context_id || A || S || y || z || T1 || T2) */
1417 {
1418 unsigned char A_ser[33], S_ser[33], T1_ser[33], T2_ser[33];
1419 size_t len = 33;
1420 SHA256_CTX sha;
1421
1422 if (!secp256k1_ec_pubkey_serialize(ctx, A_ser, &len, &A, SECP256K1_EC_COMPRESSED)) goto cleanup;
1423 len = 33;
1424 if (!secp256k1_ec_pubkey_serialize(ctx, S_ser, &len, &S, SECP256K1_EC_COMPRESSED)) goto cleanup;
1425 len = 33;
1426 if (!secp256k1_ec_pubkey_serialize(ctx, T1_ser, &len, &T1, SECP256K1_EC_COMPRESSED)) goto cleanup;
1427 len = 33;
1428 if (!secp256k1_ec_pubkey_serialize(ctx, T2_ser, &len, &T2, SECP256K1_EC_COMPRESSED)) goto cleanup;
1429
1430 SHA256_Init(&sha);
1431 if (context_id) SHA256_Update(&sha, context_id, 32);
1432 SHA256_Update(&sha, A_ser, 33);
1433 SHA256_Update(&sha, S_ser, 33);
1434 SHA256_Update(&sha, y, 32);
1435 SHA256_Update(&sha, z, 32);
1436 SHA256_Update(&sha, T1_ser, 33);
1437 SHA256_Update(&sha, T2_ser, 33);
1438 SHA256_Final(x, &sha);
1439
1441 if (memcmp(x, zero, 32) == 0) goto cleanup; /* avoid infinity later */
1442 }
1443
1444 /* ---- 10. Evaluate l(x), r(x), t_hat ---- */
1445 for (size_t k = 0; k < n; k++) {
1446 unsigned char tmp[32];
1447
1448 /* l = l0 + sL*x */
1449 secp256k1_mpt_scalar_mul(tmp, sl + 32*k, x);
1450 secp256k1_mpt_scalar_add(l_vec + 32*k, l_vec + 32*k, tmp);
1451
1452 /* r = r0 + r1*x */
1453 secp256k1_mpt_scalar_mul(tmp, r1_vec + 32*k, x);
1454 secp256k1_mpt_scalar_add(r_vec + 32*k, r_vec + 32*k, tmp);
1455
1456 OPENSSL_cleanse(tmp, 32);
1457 }
1458
1459 if (!secp256k1_bulletproof_ipa_dot(ctx, t_hat, l_vec, r_vec, n)) goto cleanup;
1460
1461 /* ---- 11. tau_x and mu (aggregation changes tau_x) ---- */
1462 secp256k1_mpt_scalar_mul(x_sq, x, x);
1463
1464 /* tau_x = tau2*x^2 + tau1*x + sum_j z^(j+2) * blinding_j */
1465 secp256k1_mpt_scalar_mul(tau_x, tau2, x_sq);
1466 {
1467 unsigned char tmp[32];
1468 secp256k1_mpt_scalar_mul(tmp, tau1, x);
1469 secp256k1_mpt_scalar_add(tau_x, tau_x, tmp);
1470
1471 /* + sum_j z^(j+2) * r_j */
1472 for (size_t j = 0; j < m; j++) {
1473 unsigned char add[32];
1474 secp256k1_mpt_scalar_mul(add, z_j2 + 32*j, blindings_flat + 32*j);
1475 secp256k1_mpt_scalar_add(tau_x, tau_x, add);
1476 OPENSSL_cleanse(add, 32);
1477 }
1478
1479 OPENSSL_cleanse(tmp, 32);
1480 }
1481
1482 /* mu = alpha + rho*x */
1483 {
1484 unsigned char tmp[32];
1485 secp256k1_mpt_scalar_mul(tmp, rho, x);
1486 secp256k1_mpt_scalar_add(mu, alpha, tmp);
1487 OPENSSL_cleanse(tmp, 32);
1488 }
1489
1490 /* ---- 12. IPA transcript + ux (binding), and H' normalization ---- */
1491
1492/* 12a. Build a stable IPA transcript seed (32 bytes).
1493 *
1494 * IMPORTANT:
1495 * - Prover and verifier MUST hash the exact same bytes in the exact same order.
1496 * - Use only public elements that both sides already know.
1497 * - Do NOT depend on internal intermediate buffers.
1498 *
1499 * Minimal, safe choice: context_id || A||S||T1||T2 || y||z||x || t_hat
1500 * (All points are serialized compressed 33 bytes.)
1501 */
1502 {
1503 unsigned char A_ser[33], S_ser[33], T1_ser[33], T2_ser[33];
1504 size_t ser_len;
1505 SHA256_CTX sha;
1506
1507 ser_len = 33;
1508 if (!secp256k1_ec_pubkey_serialize(ctx, A_ser, &ser_len, &A, SECP256K1_EC_COMPRESSED)) goto cleanup;
1509 ser_len = 33;
1510 if (!secp256k1_ec_pubkey_serialize(ctx, S_ser, &ser_len, &S, SECP256K1_EC_COMPRESSED)) goto cleanup;
1511 ser_len = 33;
1512 if (!secp256k1_ec_pubkey_serialize(ctx, T1_ser, &ser_len, &T1, SECP256K1_EC_COMPRESSED)) goto cleanup;
1513 ser_len = 33;
1514 if (!secp256k1_ec_pubkey_serialize(ctx, T2_ser, &ser_len, &T2, SECP256K1_EC_COMPRESSED)) goto cleanup;
1515
1516 SHA256_Init(&sha);
1517
1518 /* Domain/context binding */
1519 if (context_id) SHA256_Update(&sha, context_id, 32);
1520
1521 /* Outer commitments */
1522 SHA256_Update(&sha, A_ser, 33);
1523 SHA256_Update(&sha, S_ser, 33);
1524 SHA256_Update(&sha, T1_ser, 33);
1525 SHA256_Update(&sha, T2_ser, 33);
1526
1527 /* Outer challenges */
1528 SHA256_Update(&sha, y, 32);
1529 SHA256_Update(&sha, z, 32);
1530 SHA256_Update(&sha, x, 32);
1531
1532 /* Bind to t_hat as well (public scalar) */
1533 SHA256_Update(&sha, t_hat, 32);
1534
1535 SHA256_Final(ipa_transcript, &sha);
1536 }
1537
1538/* 12b. Derive u_x = H(ipa_transcript || t_hat) reduced to scalar. */
1539 if (!derive_ipa_binding_challenge(ctx, ux_scalar, ipa_transcript, t_hat)) goto cleanup;
1540
1541/* 12c. Normalize H: H'[k] = H[k] * y^{-k}.
1542 *
1543 * NOTE:
1544 * - Requires y != 0.
1545 * - If y==0 (mod q), abort (cannot invert).
1546 */
1547 {
1548 unsigned char y_inv[32];
1549 unsigned char y_inv_pow[32]; /* (y^{-1})^k */
1550 if (memcmp(y, zero, 32) == 0) goto cleanup;
1551
1553 memcpy(y_inv_pow, one, 32);
1554
1555 for (size_t k = 0; k < n; k++) {
1556 H_prime[k] = H_vec[k];
1557 /* H_prime[k] = H_vec[k] * (y^{-1})^k */
1558 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &H_prime[k], y_inv_pow)) goto cleanup;
1559 secp256k1_mpt_scalar_mul(y_inv_pow, y_inv_pow, y_inv);
1560 }
1561
1562 OPENSSL_cleanse(y_inv, 32);
1563 OPENSSL_cleanse(y_inv_pow, 32);
1564 }
1565
1566/* 12d. Run IPA prover */
1567 {
1568 size_t rounds_used = 0;
1569
1571 ctx,
1572 &U, /* binding generator point */
1573 G_vec, /* G */
1574 H_prime, /* H' */
1575 l_vec, /* l(x) scalars (flat 32*n) */
1576 r_vec, /* r(x) scalars (flat 32*n) */
1577 n,
1578 ipa_transcript, /* 32-byte seed */
1579 ux_scalar, /* u_x scalar */
1580 L_vec,
1581 R_vec,
1582 rounds, /* max_rounds = log2(n) */
1583 &rounds_used,
1584 a_final,
1585 b_final
1586 )) goto cleanup;
1587
1588 if (rounds_used != rounds) goto cleanup;
1589 }
1590
1591 /* ---- 13. Serialize (uses rounds) ---- */
1592 {
1593 const size_t expected = 292 + 66 * rounds; /* 4*33 + 2*rounds*33 + 5*32 */
1594
1595 /* Standard pattern: query size only */
1596 if (proof_out == NULL) {
1597 if (proof_len) *proof_len = expected;
1598 ok = 1;
1599 goto cleanup;
1600 }
1601
1602 if (proof_len == NULL) goto cleanup;
1603
1604 if (*proof_len < expected) {
1605 *proof_len = expected;
1606 goto cleanup; /* not enough space */
1607 }
1608
1609 unsigned char* ptr = proof_out;
1610 size_t ser_len;
1611
1612#define SER_PT(P) do { \
1613 ser_len = 33; \
1614 if (!secp256k1_ec_pubkey_serialize(ctx, ptr, &ser_len, &(P), SECP256K1_EC_COMPRESSED)) goto cleanup; \
1615 if (ser_len != 33) goto cleanup; \
1616 ptr += 33; \
1617 } while (0)
1618
1619 SER_PT(A); SER_PT(S); SER_PT(T1); SER_PT(T2);
1620
1621 for (size_t r = 0; r < rounds; r++) SER_PT(L_vec[r]);
1622 for (size_t r = 0; r < rounds; r++) SER_PT(R_vec[r]);
1623
1624 memcpy(ptr, a_final, 32); ptr += 32;
1625 memcpy(ptr, b_final, 32); ptr += 32;
1626 memcpy(ptr, t_hat, 32); ptr += 32;
1627 memcpy(ptr, tau_x, 32); ptr += 32;
1628 memcpy(ptr, mu, 32); ptr += 32;
1629
1630#undef SER_PT
1631
1632 /* Final sanity */
1633 if ((size_t)(ptr - proof_out) != expected) goto cleanup;
1634
1635 *proof_len = expected;
1636 }
1637
1638 ok = 1;
1639
1640 cleanup:
1641
1642 /* wipe sensitive scalars; free buffers */
1643 if (al) OPENSSL_cleanse(al, n * 32);
1644 if (ar) OPENSSL_cleanse(ar, n * 32);
1645 if (sl) OPENSSL_cleanse(sl, n * 32);
1646 if (sr) OPENSSL_cleanse(sr, n * 32);
1647 if (l_vec) OPENSSL_cleanse(l_vec, n * 32);
1648 if (r_vec) OPENSSL_cleanse(r_vec, n * 32);
1649 if (r1_vec) OPENSSL_cleanse(r1_vec, n * 32);
1650
1651 OPENSSL_cleanse(alpha, 32);
1652 OPENSSL_cleanse(rho, 32);
1653 OPENSSL_cleanse(tau1, 32);
1654 OPENSSL_cleanse(tau2, 32);
1655 OPENSSL_cleanse(t1, 32);
1656 OPENSSL_cleanse(t2, 32);
1657 OPENSSL_cleanse(t_hat, 32);
1658 OPENSSL_cleanse(tau_x, 32);
1659 OPENSSL_cleanse(mu, 32);
1660
1661 OPENSSL_cleanse(y, 32);
1662 OPENSSL_cleanse(z, 32);
1663 OPENSSL_cleanse(x, 32);
1664 OPENSSL_cleanse(z_sq, 32);
1665 OPENSSL_cleanse(z_neg, 32);
1666 OPENSSL_cleanse(x_sq, 32);
1667 OPENSSL_cleanse(ux_scalar, 32);
1668 OPENSSL_cleanse(ipa_transcript, 32);
1669
1670 if (G_vec) { OPENSSL_cleanse(G_vec, n * sizeof(secp256k1_pubkey)); free(G_vec); }
1671 if (H_vec) { OPENSSL_cleanse(H_vec, n * sizeof(secp256k1_pubkey)); free(H_vec); }
1672 if (H_prime) { OPENSSL_cleanse(H_prime, n * sizeof(secp256k1_pubkey)); free(H_prime); }
1673
1674 if (al) free(al);
1675 if (ar) free(ar);
1676 if (sl) free(sl);
1677 if (sr) free(sr);
1678 if (l_vec) free(l_vec);
1679 if (r_vec) free(r_vec);
1680 if (r1_vec) free(r1_vec);
1681
1682 if (L_vec) { OPENSSL_cleanse(L_vec, rounds * sizeof(secp256k1_pubkey)); free(L_vec); }
1683 if (R_vec) { OPENSSL_cleanse(R_vec, rounds * sizeof(secp256k1_pubkey)); free(R_vec); }
1684
1685 if (y_powers) { OPENSSL_cleanse(y_powers, n * 32); free(y_powers); }
1686 if (z_j2) { OPENSSL_cleanse(z_j2, m * 32); free(z_j2); }
1687
1688 return ok;
1689}
1690
1714
1716 const secp256k1_context* ctx,
1717 const secp256k1_pubkey* G_vec, /* length n = 64*m */
1718 const secp256k1_pubkey* H_vec, /* length n = 64*m */
1719 const unsigned char* proof,
1720 size_t proof_len,
1721 const secp256k1_pubkey* commitment_C_vec, /* length m */
1722 size_t m,
1723 const secp256k1_pubkey* pk_base,
1724 const unsigned char* context_id
1725) {
1726
1727 if (!ctx || !G_vec || !H_vec || !proof || !commitment_C_vec || !pk_base) return 0;
1728 if (m == 0) return 0;
1729 /* Aggregation requires n = 64*m to be power-of-two => m must be power-of-two. */
1730 if ((m & (m - 1)) != 0) return 0;
1731
1732 const size_t n = BP_TOTAL_BITS(m); /* 64*m */
1733 const size_t rounds = bp_ipa_rounds(n);
1734
1735 /* Proof length is dynamic in aggregated mode */
1736 const size_t expected_len = 292 + 66 * rounds;
1737 if (proof_len != expected_len) return 0;
1738
1739 /* --- Unpack proof --- */
1740 secp256k1_pubkey A, S, T1, T2;
1741 secp256k1_pubkey U;
1742
1743 secp256k1_pubkey* L_vec = (secp256k1_pubkey*)malloc(rounds * sizeof(secp256k1_pubkey));
1744 secp256k1_pubkey* R_vec = (secp256k1_pubkey*)malloc(rounds * sizeof(secp256k1_pubkey));
1745 if (!L_vec || !R_vec) { free(L_vec); free(R_vec); return 0; }
1746
1747 unsigned char a_final[32], b_final[32];
1748 unsigned char t_hat[32], tau_x[32], mu[32];
1749
1750 unsigned char y[32], z[32], x[32];
1751 unsigned char ux_scalar[32];
1752 unsigned char z_sq[32];
1753
1754 const unsigned char* ptr = proof;
1755
1756 if (!secp256k1_ec_pubkey_parse(ctx, &A, ptr, 33)) goto fail; ptr += 33;
1757 if (!secp256k1_ec_pubkey_parse(ctx, &S, ptr, 33)) goto fail; ptr += 33;
1758 if (!secp256k1_ec_pubkey_parse(ctx, &T1, ptr, 33)) goto fail; ptr += 33;
1759 if (!secp256k1_ec_pubkey_parse(ctx, &T2, ptr, 33)) goto fail; ptr += 33;
1760
1761 for (size_t i = 0; i < rounds; i++) {
1762 if (!secp256k1_ec_pubkey_parse(ctx, &L_vec[i], ptr, 33)) goto fail;
1763 ptr += 33;
1764 }
1765
1766 for (size_t i = 0; i < rounds; i++) {
1767 if (!secp256k1_ec_pubkey_parse(ctx, &R_vec[i], ptr, 33)) goto fail;
1768 ptr += 33;
1769 }
1770
1771 memcpy(a_final, ptr, 32); ptr += 32;
1772 memcpy(b_final, ptr, 32); ptr += 32;
1773 memcpy(t_hat, ptr, 32); ptr += 32;
1774 memcpy(tau_x, ptr, 32); ptr += 32;
1775 memcpy(mu, ptr, 32); ptr += 32;
1776
1777 /* Basic scalar validity */
1778 if (!secp256k1_ec_seckey_verify(ctx, a_final)) goto fail;
1779 if (!secp256k1_ec_seckey_verify(ctx, b_final)) goto fail;
1780 if (!secp256k1_ec_seckey_verify(ctx, t_hat)) goto fail;
1781 if (!secp256k1_ec_seckey_verify(ctx, tau_x)) goto fail;
1782 if (!secp256k1_ec_seckey_verify(ctx, mu)) goto fail;
1783
1784 /* Derive U */
1785 {
1786 secp256k1_pubkey U_arr[1];
1787 if (!secp256k1_mpt_get_generator_vector(ctx, U_arr, 1, (const unsigned char*)"BP_U", 4)) goto fail;
1788 U = U_arr[0];
1789 }
1790
1791 /* --- Fiat–Shamir: y,z --- */
1792 unsigned char A_ser[33], S_ser[33], T1_ser[33], T2_ser[33];
1793 size_t slen = 33;
1794 SHA256_CTX sha;
1795
1796 if (!secp256k1_ec_pubkey_serialize(ctx, A_ser, &slen, &A, SECP256K1_EC_COMPRESSED)) goto fail;
1797 slen = 33;
1798 if (!secp256k1_ec_pubkey_serialize(ctx, S_ser, &slen, &S, SECP256K1_EC_COMPRESSED)) goto fail;
1799 slen = 33;
1800 if (!secp256k1_ec_pubkey_serialize(ctx, T1_ser, &slen, &T1, SECP256K1_EC_COMPRESSED)) goto fail;
1801 slen = 33;
1802 if (!secp256k1_ec_pubkey_serialize(ctx, T2_ser, &slen, &T2, SECP256K1_EC_COMPRESSED)) goto fail;
1803
1804 SHA256_Init(&sha);
1805
1806 /* 1. Domain Separation */
1807 SHA256_Update(&sha, "MPT_BULLETPROOF_RANGE", 21);
1808
1809 /* 2. Transaction Context */
1810 if (context_id) SHA256_Update(&sha, context_id, 32);
1811
1812 /* 3. Value Commitments (Inputs to Verify) */
1813 for (size_t i = 0; i < m; i++) {
1814 unsigned char C_ser[33];
1815 size_t c_len = 33;
1816 if (!secp256k1_ec_pubkey_serialize(ctx, C_ser, &c_len, &commitment_C_vec[i], SECP256K1_EC_COMPRESSED)) goto fail;
1817 SHA256_Update(&sha, C_ser, 33);
1818 }
1819
1820 /* 4. A, S */
1821 SHA256_Update(&sha, A_ser, 33);
1822 SHA256_Update(&sha, S_ser, 33);
1823 SHA256_Final(y, &sha);
1825
1826 /* Generate z (Hash T1 || y) */
1827 SHA256_Init(&sha);
1828 SHA256_Update(&sha, "MPT_BULLETPROOF_RANGE", 21);
1829 if (context_id) SHA256_Update(&sha, context_id, 32);
1830 for (size_t i = 0; i < m; i++) {
1831 unsigned char C_ser[33];
1832 size_t c_len = 33;
1833 secp256k1_ec_pubkey_serialize(ctx, C_ser, &c_len, &commitment_C_vec[i], SECP256K1_EC_COMPRESSED);
1834 SHA256_Update(&sha, C_ser, 33);
1835 }
1836 SHA256_Update(&sha, A_ser, 33);
1837 SHA256_Update(&sha, S_ser, 33);
1838 SHA256_Update(&sha, y, 32);
1839 SHA256_Final(z, &sha);
1841
1842 if (!secp256k1_ec_seckey_verify(ctx, y) || !secp256k1_ec_seckey_verify(ctx, z)) goto fail;
1843
1844 /* Powers */
1845 unsigned char* y_powers = (unsigned char*)malloc(n * 32);
1846 unsigned char* y_inv_powers = (unsigned char*)malloc(n * 32);
1847 if (!y_powers || !y_inv_powers) { free(y_powers); free(y_inv_powers); goto fail; }
1848
1849 unsigned char y_inv[32];
1850 scalar_vector_powers(ctx, (unsigned char (*)[32])y_powers, y, n);
1852 scalar_vector_powers(ctx, (unsigned char (*)[32])y_inv_powers, y_inv, n);
1853
1854 /* --- Fiat–Shamir: x --- */
1855 SHA256_Init(&sha);
1856 if (context_id) SHA256_Update(&sha, context_id, 32);
1857 SHA256_Update(&sha, A_ser, 33);
1858 SHA256_Update(&sha, S_ser, 33);
1859 SHA256_Update(&sha, y, 32);
1860 SHA256_Update(&sha, z, 32);
1861 SHA256_Update(&sha, T1_ser, 33);
1862 SHA256_Update(&sha, T2_ser, 33);
1863 SHA256_Final(x, &sha);
1865
1866 if (!secp256k1_ec_seckey_verify(ctx, x)) { free(y_powers); free(y_inv_powers); goto fail; }
1867
1868 /* z^2 */
1869 secp256k1_mpt_scalar_mul(z_sq, z, z);
1870
1871 /* =========================================================================
1872 * Step 3: Verify polynomial identity:
1873 * t_hat*G + tau_x*H == (sum_j z^(j+2) * V_j) + delta(y,z)*G + x*T1 + x^2*T2
1874 * ========================================================================= */
1875
1876 /* --- delta(y,z) for aggregation --- */
1877 unsigned char (*y_block_sum)[32] = (unsigned char (*)[32])malloc(m * 32);
1878 if (!y_block_sum) { free(y_powers); free(y_inv_powers); goto fail; }
1879
1880 unsigned char two_sum[32];
1881 compute_delta_scalars(ctx, y_block_sum, two_sum, y, m);
1882
1883 unsigned char delta[32] = {0};
1884 unsigned char sum_y_all[32] = {0};
1885
1886/* sum_y_all = sum_{k=0}^{n-1} y^k = sum_j y_block_sum[j] */
1887 for (size_t j = 0; j < m; j++) {
1888 secp256k1_mpt_scalar_add(sum_y_all, sum_y_all, y_block_sum[j]);
1889 }
1890
1891/* delta += (z - z^2) * sum_y_all */
1892 {
1893 unsigned char z_minus_z2[32], tmp[32];
1894 secp256k1_mpt_scalar_sub(z_minus_z2, z, z_sq);
1895 secp256k1_mpt_scalar_mul(tmp, z_minus_z2, sum_y_all);
1896 secp256k1_mpt_scalar_add(delta, delta, tmp);
1897 OPENSSL_cleanse(z_minus_z2, 32);
1898 OPENSSL_cleanse(tmp, 32);
1899 }
1900
1901/* delta -= sum_{j=0}^{m-1} z^(j+3) * two_sum */
1902 for (size_t j = 0; j < m; j++) {
1903 unsigned char z_j3[32], tmp[32];
1904 scalar_pow_u32(ctx, z_j3, z, (unsigned int)(j + 3));
1905 secp256k1_mpt_scalar_mul(tmp, z_j3, two_sum);
1907 secp256k1_mpt_scalar_add(delta, delta, tmp);
1908 OPENSSL_cleanse(z_j3, 32);
1909 OPENSSL_cleanse(tmp, 32);
1910 }
1911
1912 OPENSSL_cleanse(sum_y_all, 32);
1913
1914
1915 /* LHS = t_hat*G + tau_x*Base */
1916 secp256k1_pubkey LHS;
1917 {
1918 unsigned char zero32[32] = {0};
1919 int have_t = 0, have_tau = 0;
1920 secp256k1_pubkey tG, tauH;
1921
1922 if (memcmp(t_hat, zero32, 32) != 0) {
1923 if (!secp256k1_ec_pubkey_create(ctx, &tG, t_hat)) { free(y_block_sum); free(y_powers); free(y_inv_powers); goto fail; }
1924 have_t = 1;
1925 }
1926 if (memcmp(tau_x, zero32, 32) != 0) {
1927 tauH = *pk_base;
1928 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &tauH, tau_x)) { free(y_block_sum); free(y_powers); free(y_inv_powers); goto fail; }
1929 have_tau = 1;
1930 }
1931 if (have_t && have_tau) {
1932 const secp256k1_pubkey* pts[2] = { &tG, &tauH };
1933 if (!secp256k1_ec_pubkey_combine(ctx, &LHS, pts, 2)) { free(y_block_sum); free(y_powers); free(y_inv_powers); goto fail; }
1934 } else if (have_t) {
1935 LHS = tG;
1936 } else if (have_tau) {
1937 LHS = tauH;
1938 } else {
1939 free(y_block_sum); free(y_powers); free(y_inv_powers); goto fail;
1940 }
1941 }
1942
1943 /* RHS = (sum_j z^(j+2) V_j) + delta*G + x*T1 + x^2*T2 */
1944 secp256k1_pubkey RHS;
1945 {
1946 secp256k1_pubkey acc, tmpP;
1947 int inited = 0;
1948 unsigned char zero32[32] = {0};
1949
1950 /* sum_j z^(j+2) V_j */
1951 for (size_t j = 0; j < m; j++) {
1952 unsigned char z_j2[32];
1953 scalar_pow_u32(ctx, z_j2, z, (unsigned int)(j + 2));
1954
1955 if (memcmp(z_j2, zero32, 32) != 0) {
1956 tmpP = commitment_C_vec[j];
1957 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &tmpP, z_j2)) { free(y_block_sum); free(y_powers); free(y_inv_powers); goto fail; }
1958 if (!add_term(ctx, &acc, &inited, &tmpP)) { free(y_block_sum); free(y_powers); free(y_inv_powers); goto fail; }
1959 }
1960 OPENSSL_cleanse(z_j2, 32);
1961 }
1962
1963 /* + delta*G */
1964 if (!scalar_is_zero(delta)) {
1965 secp256k1_pubkey deltaG;
1966 if (!secp256k1_ec_pubkey_create(ctx, &deltaG, delta)) goto fail;
1967 if (!add_term(ctx, &acc, &inited, &deltaG)) goto fail;
1968 }
1969
1970
1971
1972 /* + x*T1 */
1973 if (memcmp(x, zero32, 32) != 0) {
1974 tmpP = T1;
1975 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &tmpP, x)) { free(y_block_sum); free(y_powers); free(y_inv_powers); goto fail; }
1976 if (!add_term(ctx, &acc, &inited, &tmpP)) { free(y_block_sum); free(y_powers); free(y_inv_powers); goto fail; }
1977 }
1978
1979 /* + x^2*T2 */
1980 unsigned char x_sq[32];
1981 secp256k1_mpt_scalar_mul(x_sq, x, x);
1982 if (memcmp(x_sq, zero32, 32) != 0) {
1983 tmpP = T2;
1984 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &tmpP, x_sq)) { OPENSSL_cleanse(x_sq,32); free(y_block_sum); free(y_powers); free(y_inv_powers); goto fail; }
1985 if (!add_term(ctx, &acc, &inited, &tmpP)) { OPENSSL_cleanse(x_sq,32); free(y_block_sum); free(y_powers); free(y_inv_powers); goto fail; }
1986 }
1987 OPENSSL_cleanse(x_sq, 32);
1988
1989 // if (!inited) { free(y_block_sum); free(y_powers); free(y_inv_powers); goto fail; }
1990 RHS = acc;
1991
1992 }
1993
1994 if (!pubkey_equal(ctx, &LHS, &RHS)) {
1995 printf("[VERIFY] Step3 polynomial identity failed\n");
1996 free(y_block_sum); free(y_powers); free(y_inv_powers); goto fail;
1997 }
1998
1999 /* =========================================================================
2000 * Step 4: Build P and Verify IPA
2001 * ========================================================================= */
2002
2003 unsigned char ipa_transcript_id[32];
2004 {
2005 SHA256_CTX sha;
2006 unsigned char A_ser[33], S_ser[33], T1_ser[33], T2_ser[33];
2007 size_t len = 33;
2008
2009 if (!secp256k1_ec_pubkey_serialize(ctx, A_ser, &len, &A, SECP256K1_EC_COMPRESSED)) goto fail;
2010 len = 33;
2011 if (!secp256k1_ec_pubkey_serialize(ctx, S_ser, &len, &S, SECP256K1_EC_COMPRESSED)) goto fail;
2012 len = 33;
2013 if (!secp256k1_ec_pubkey_serialize(ctx, T1_ser,&len, &T1, SECP256K1_EC_COMPRESSED)) goto fail;
2014 len = 33;
2015 if (!secp256k1_ec_pubkey_serialize(ctx, T2_ser,&len, &T2, SECP256K1_EC_COMPRESSED)) goto fail;
2016
2017
2018
2019 SHA256_Init(&sha);
2020 if (context_id) SHA256_Update(&sha, context_id, 32);
2021 SHA256_Update(&sha, A_ser, 33);
2022 SHA256_Update(&sha, S_ser, 33);
2023 SHA256_Update(&sha, T1_ser,33);
2024 SHA256_Update(&sha, T2_ser,33);
2025 SHA256_Update(&sha, y, 32);
2026 SHA256_Update(&sha, z, 32);
2027 SHA256_Update(&sha, x, 32);
2028 SHA256_Update(&sha, t_hat, 32);
2029 SHA256_Final(ipa_transcript_id, &sha);
2030 }
2031
2032 if (!derive_ipa_binding_challenge(ctx, ux_scalar, ipa_transcript_id, t_hat))
2033 goto fail;
2034
2035
2036 secp256k1_pubkey P = A;
2037
2038 /* P += x*S */
2039 {
2040 secp256k1_pubkey xS = S;
2041 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &xS, x)) { free(y_block_sum); free(y_powers); free(y_inv_powers); goto fail; }
2042
2043
2044 secp256k1_pubkey newP;
2045 const secp256k1_pubkey* pts[2] = { &P, &xS };
2046 if (!secp256k1_ec_pubkey_combine(ctx, &newP, pts, 2)) goto fail;
2047 P = newP;
2048
2049 }
2050
2051 /* P += sum_{k=0}^{n-1} [ (-z)*G_k + ( z*y^k + z^(block+2)*z^2*2^i ) * (y^{-k}*H_k) ] */
2052 unsigned char neg_z[32];
2053 memcpy(neg_z, z, 32);
2054 if (!secp256k1_ec_seckey_negate(ctx, neg_z)) { free(y_block_sum); free(y_powers); free(y_inv_powers); goto fail; }
2055
2056 for (size_t j = 0; j < m; j++) {
2057 unsigned char z_j2[32];
2058 scalar_pow_u32(ctx, z_j2, z, (unsigned int)(j + 2));
2059
2060 for (size_t i = 0; i < 64; i++) {
2061 const size_t k = j * 64 + i;
2062
2063 /* ---- Gi term: (-z) * G_k ---- */
2064 if (!scalar_is_zero(neg_z)) {
2065 secp256k1_pubkey Gi = G_vec[k];
2066 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &Gi, neg_z)) goto fail;
2067
2068 secp256k1_pubkey newP;
2069 const secp256k1_pubkey* pts2[2] = { &P, &Gi };
2070 if (!secp256k1_ec_pubkey_combine(ctx, &newP, pts2, 2)) goto fail;
2071 P = newP;
2072 }
2073
2074 /* ---- Hi term: termH * (y^{-k} * H_k) ---- */
2075 unsigned char termH[32], tmp[32];
2076 unsigned char two_i[32] = {0};
2077
2078 secp256k1_mpt_scalar_mul(termH, z, (const unsigned char*)(y_powers + 32*k));
2079
2080 two_i[31 - (i / 8)] = (unsigned char)(1u << (i % 8));
2081 secp256k1_mpt_scalar_mul(tmp, z_j2, two_i);
2082 secp256k1_mpt_scalar_add(termH, termH, tmp);
2083
2084 if (!scalar_is_zero(termH)) {
2085 secp256k1_pubkey Hi = H_vec[k];
2086
2087 /* Hi = (y^{-k} * H_k) */
2088 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &Hi, (const unsigned char*)(y_inv_powers + 32*k)))
2089 goto fail;
2090
2091 /* Hi = termH * Hi */
2092 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &Hi, termH))
2093 goto fail;
2094
2095 secp256k1_pubkey newP;
2096 const secp256k1_pubkey* pts2[2] = { &P, &Hi };
2097 if (!secp256k1_ec_pubkey_combine(ctx, &newP, pts2, 2)) goto fail;
2098 P = newP;
2099 }
2100
2101 OPENSSL_cleanse(tmp, 32);
2102 OPENSSL_cleanse(termH, 32);
2103 }
2104
2105 OPENSSL_cleanse(z_j2, 32);
2106 }
2107
2108
2109 /* P += (t_hat * ux) * U */
2110 {
2111 unsigned char t_hat_ux[32];
2112 unsigned char zero32[32] = {0};
2113 secp256k1_mpt_scalar_mul(t_hat_ux, t_hat, ux_scalar);
2114
2115 if (memcmp(t_hat_ux, zero32, 32) != 0) {
2116 secp256k1_pubkey Q = U;
2117 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &Q, t_hat_ux)) { OPENSSL_cleanse(t_hat_ux,32); free(y_block_sum); free(y_powers); free(y_inv_powers); goto fail; }
2118 const secp256k1_pubkey* pts2[2] = { &P, &Q };
2119 secp256k1_pubkey newP;
2120 if (!secp256k1_ec_pubkey_combine(ctx, &newP, pts2, 2)) goto fail;
2121 P = newP;
2122
2123 }
2124 OPENSSL_cleanse(t_hat_ux, 32);
2125 }
2126
2127 /* P -= mu*pk_base */
2128 {
2129 unsigned char neg_mu[32];
2130 memcpy(neg_mu, mu, 32);
2131 if (!secp256k1_ec_seckey_negate(ctx, neg_mu)) { free(y_block_sum); free(y_powers); free(y_inv_powers); goto fail; }
2132
2133 secp256k1_pubkey mu_term = *pk_base;
2134 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &mu_term, neg_mu)) { OPENSSL_cleanse(neg_mu,32); free(y_block_sum); free(y_powers); free(y_inv_powers); goto fail; }
2135 OPENSSL_cleanse(neg_mu, 32);
2136
2137 const secp256k1_pubkey* pts2[2] = { &P, &mu_term };
2138 secp256k1_pubkey newP;
2139 if (!secp256k1_ec_pubkey_combine(ctx, &newP, pts2, 2)) goto fail;
2140 P = newP;
2141
2142 }
2143
2144 /* Build Hprime = y^{-k} * H_k (length n) */
2145 secp256k1_pubkey* Hprime = (secp256k1_pubkey*)malloc(n * sizeof(secp256k1_pubkey));
2146 if (!Hprime) { free(y_block_sum); free(y_powers); free(y_inv_powers); goto fail; }
2147
2148 for (size_t k = 0; k < n; k++) {
2149 Hprime[k] = H_vec[k];
2150 if (!secp256k1_ec_pubkey_tweak_mul(ctx, &Hprime[k], (const unsigned char*)(y_inv_powers + 32 * k))) {
2151 free(Hprime);
2152 free(y_block_sum); free(y_powers); free(y_inv_powers);
2153 goto fail;
2154 }
2155 }
2156 /* IPA verify */
2157
2158 int ok = ipa_verify_explicit(
2159 ctx,
2160 G_vec,
2161 Hprime,
2162 &U,
2163 &P,
2164 L_vec,
2165 R_vec,
2166 n, /* <-- add this */
2167 a_final,
2168 b_final,
2169 ux_scalar,
2170 ipa_transcript_id
2171 );
2172
2173
2174 free(Hprime);
2175 free(y_block_sum);
2176 free(y_powers);
2177 free(y_inv_powers);
2178 free(L_vec);
2179 free(R_vec);
2180
2181 return ok ? 1 : 0;
2182
2183 fail:
2184 free(L_vec);
2185 free(R_vec);
2186 return 0;
2187}
int secp256k1_bulletproof_run_ipa_prover(const secp256k1_context *ctx, const secp256k1_pubkey *g, secp256k1_pubkey *G_vec, secp256k1_pubkey *H_vec, unsigned char *a_vec, unsigned char *b_vec, size_t n, const unsigned char ipa_transcript_id[32], const unsigned char ux_scalar[32], secp256k1_pubkey *L_out, secp256k1_pubkey *R_out, size_t max_rounds, size_t *rounds_out, unsigned char a_final[32], unsigned char b_final[32])
int secp256k1_bulletproof_verify_agg(const secp256k1_context *ctx, const secp256k1_pubkey *G_vec, const secp256k1_pubkey *H_vec, const unsigned char *proof, size_t proof_len, const secp256k1_pubkey *commitment_C_vec, size_t m, const secp256k1_pubkey *pk_base, const unsigned char *context_id)
int derive_ipa_binding_challenge(const secp256k1_context *ctx, unsigned char *ux_out, const unsigned char *commit_inp_32, const unsigned char *dot_32)
static int add_term(const secp256k1_context *ctx, secp256k1_pubkey *acc, int *acc_inited, const secp256k1_pubkey *term)
int secp256k1_bulletproof_ipa_dot(const secp256k1_context *ctx, unsigned char *out, const unsigned char *a, const unsigned char *b, size_t n)
static int pubkey_equal(const secp256k1_context *ctx, const secp256k1_pubkey *a, const secp256k1_pubkey *b)
int secp256k1_bulletproof_create_commitment(const secp256k1_context *ctx, secp256k1_pubkey *commitment_C, uint64_t value, const unsigned char *blinding_factor, const secp256k1_pubkey *pk_base)
Computes a Pedersen Commitment: C = value*G + blinding_factor*Pk_base.
void scalar_vector_add(const secp256k1_context *ctx, unsigned char res[][32], unsigned char a[][32], unsigned char b[][32], size_t n)
#define SER_PT(P)
int secp256k1_bulletproof_add_point_to_accumulator(const secp256k1_context *ctx, secp256k1_pubkey *acc, const secp256k1_pubkey *term)
int secp256k1_bulletproof_compute_vectors_block(const secp256k1_context *ctx, uint64_t value, size_t block_index, unsigned char *al, unsigned char *ar, unsigned char *sl, unsigned char *sr)
static void secp256k1_mpt_scalar_sub(unsigned char *res, const unsigned char *a, const unsigned char *b)
static int secp256k1_bulletproof_point_scalar_mul(const secp256k1_context *ctx, secp256k1_pubkey *r_out, const secp256k1_pubkey *p_in, const unsigned char *s_scalar)
static int msm_try_add(const secp256k1_context *ctx, secp256k1_pubkey *acc, int *acc_inited, const secp256k1_pubkey *points, const unsigned char *scalars, size_t n)
static size_t bp_ipa_rounds(size_t total_bits)
int secp256k1_bulletproof_ipa_compress_step(const secp256k1_context *ctx, unsigned char *a, unsigned char *b, secp256k1_pubkey *G, secp256k1_pubkey *H, size_t half_n, const unsigned char *x, const unsigned char *x_inv)
int fold_generators(const secp256k1_context *ctx, secp256k1_pubkey *final_point, const secp256k1_pubkey *generators, const unsigned char *u_flat, const unsigned char *uinv_flat, size_t n, size_t rounds, int is_H)
int derive_ipa_round_challenge(const secp256k1_context *ctx, unsigned char u_out[32], const unsigned char last_challenge[32], const secp256k1_pubkey *L, const secp256k1_pubkey *R)
int secp256k1_bulletproof_prove_agg(const secp256k1_context *ctx, unsigned char *proof_out, size_t *proof_len, const uint64_t *values, const unsigned char *blindings_flat, size_t m, const secp256k1_pubkey *pk_base, const unsigned char *context_id)
static int ipa_verify_explicit(const secp256k1_context *ctx, const secp256k1_pubkey *G_vec, const secp256k1_pubkey *H_vec, const secp256k1_pubkey *U, const secp256k1_pubkey *P_in, const secp256k1_pubkey *L_vec, const secp256k1_pubkey *R_vec, size_t n, const unsigned char a_final[32], const unsigned char b_final[32], const unsigned char ux[32], const unsigned char ipa_transcript_id[32])
int secp256k1_bulletproof_ipa_msm(const secp256k1_context *ctx, secp256k1_pubkey *r_out, const secp256k1_pubkey *points, const unsigned char *scalars, size_t n)
static int compute_amount_point(const secp256k1_context *ctx, secp256k1_pubkey *mG, uint64_t amount)
static int generate_random_scalar(const secp256k1_context *ctx, unsigned char *scalar_bytes)
int secp256k1_bulletproof_ipa_compute_LR(const secp256k1_context *ctx, secp256k1_pubkey *L, secp256k1_pubkey *R, const unsigned char *a_L, const unsigned char *a_R, const unsigned char *b_L, const unsigned char *b_R, const secp256k1_pubkey *G_L, const secp256k1_pubkey *G_R, const secp256k1_pubkey *H_L, const secp256k1_pubkey *H_R, const secp256k1_pubkey *U, const unsigned char *ux, size_t half_n)
#define BP_VALUE_BITS
static int scalar_is_zero(const unsigned char s[32])
static void compute_delta_scalars(const secp256k1_context *ctx, unsigned char(*y_block_sum)[32], unsigned char two_sum[32], const unsigned char y[32], size_t m)
int apply_ipa_folding_to_P(const secp256k1_context *ctx, secp256k1_pubkey *P, const secp256k1_pubkey *L_vec, const secp256k1_pubkey *R_vec, const unsigned char *u_flat, const unsigned char *uinv_flat, size_t rounds)
static void compute_z_pows_j2(const secp256k1_context *ctx, unsigned char(*z_j2)[32], const unsigned char z[32], size_t m)
#define BP_TOTAL_BITS(m)
static void scalar_pow_u32(const secp256k1_context *ctx, unsigned char y_pow_out[32], const unsigned char y[32], unsigned int i)
void scalar_vector_mul(const secp256k1_context *ctx, unsigned char res[][32], unsigned char a[][32], unsigned char b[][32], size_t n)
void scalar_vector_powers(const secp256k1_context *ctx, unsigned char res[][32], const unsigned char *y, size_t n)
void secp256k1_mpt_scalar_negate(unsigned char *res, const unsigned char *in)
Definition mpt_scalar.c:96
int secp256k1_mpt_get_generator_vector(const secp256k1_context *ctx, secp256k1_pubkey *vec, size_t n, const unsigned char *label, size_t label_len)
Generates a vector of N independent NUMS generators.
void secp256k1_mpt_scalar_mul(unsigned char *res, const unsigned char *a, const unsigned char *b)
Definition mpt_scalar.c:73
void secp256k1_mpt_scalar_reduce32(unsigned char out32[32], const unsigned char in32[32])
Definition mpt_scalar.c:106
void secp256k1_mpt_scalar_add(unsigned char *res, const unsigned char *a, const unsigned char *b)
Definition mpt_scalar.c:60
void secp256k1_mpt_scalar_inverse(unsigned char *res, const unsigned char *in)
Definition mpt_scalar.c:86