purify
C++ Purify implementation with native circuit and BPP support
Loading...
Searching...
No Matches
bppp_bridge.c
Go to the documentation of this file.
1// Copyright (c) 2026 Judica, Inc.
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or https://opensource.org/license/mit/.
4
9
10#define SECP256K1_BUILD
11#define ENABLE_MODULE_GENERATOR 1
12#define ENABLE_MODULE_BPPP 1
13#define ENABLE_MODULE_EXTRAKEYS 1
14#define ENABLE_MODULE_SCHNORRSIG 1
15
16#include "purify.h"
17#include "bppp_bridge.h"
18#include "purify/secp_bridge.h"
19
20#include <limits.h>
21#include <stdlib.h>
22#include <string.h>
23
24#include "third_party/secp256k1-zkp/include/secp256k1_extrakeys.h"
25#include "third_party/secp256k1-zkp/include/secp256k1_schnorrsig.h"
26#include "third_party/secp256k1-zkp/src/secp256k1.c"
27#include "third_party/secp256k1-zkp/src/precomputed_ecmult.c"
28#include "third_party/secp256k1-zkp/src/precomputed_ecmult_gen.c"
30
31#undef secp256k1_scratch_alloc
32
33_Static_assert(sizeof(purify_scalar) == sizeof(secp256k1_scalar), "purify_scalar size mismatch");
34_Static_assert(_Alignof(purify_scalar) >= _Alignof(secp256k1_scalar), "purify_scalar alignment mismatch");
35
37 secp256k1_context* ctx;
38};
39
40static void purify_bridge_secure_clear(void* data, size_t size);
41
42static secp256k1_context* purify_context_handle(const purify_secp_context* context) {
43 return context != NULL ? context->ctx : NULL;
44}
45
47 unsigned char seed32[32] = {0};
48 purify_secp_context* context =
49 (purify_secp_context*)calloc(1, sizeof(*context));
50 if (context == NULL) {
51 return NULL;
52 }
53 context->ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
54 if (context->ctx == NULL) {
55 free(context);
56 return NULL;
57 }
58 if (purify_fill_secure_random(seed32, sizeof(seed32)) != PURIFY_ERROR_OK
59 || !secp256k1_context_randomize(context->ctx, seed32)) {
60 purify_bridge_secure_clear(seed32, sizeof(seed32));
61 secp256k1_context_destroy(context->ctx);
62 free(context);
63 return NULL;
64 }
65 purify_bridge_secure_clear(seed32, sizeof(seed32));
66 return context;
67}
68
70 if (context == NULL) {
71 return;
72 }
73 if (context->ctx != NULL) {
74 secp256k1_context_destroy(context->ctx);
75 }
76 free(context);
77}
78
79static void purify_bridge_secure_clear(void* data, size_t size) {
80 if (data != NULL && size != 0u) {
81 secp256k1_memclear_explicit(data, size);
82 }
83}
84
86 size_t n_gates;
88 secp256k1_context* ctx;
89 secp256k1_scratch_space* scratch;
91};
92
95 secp256k1_context* ctx;
96 secp256k1_scratch_space* scratch;
97 secp256k1_bppp_generators* gens;
98 secp256k1_bppp_generators gens_scratch;
100};
101
105
106static int purify_bridge_checked_mul_size(size_t lhs, size_t rhs, size_t* out) {
107 if (out == NULL) {
108 return 0;
109 }
110 if (lhs != 0 && rhs > SIZE_MAX / lhs) {
111 return 0;
112 }
113 *out = lhs * rhs;
114 return 1;
115}
116
117static int purify_bridge_checked_add_size(size_t lhs, size_t rhs, size_t* out) {
118 if (out == NULL) {
119 return 0;
120 }
121 if (rhs > SIZE_MAX - lhs) {
122 return 0;
123 }
124 *out = lhs + rhs;
125 return 1;
126}
127
128/* Reserve this much work space for the batched multiscalar multiplication
129 * after the imported circuit and inner-product frames have been allocated.
130 */
131static const size_t purify_bulletproof_msm_scratch_size = (size_t)1u << 24;
132
133static int purify_bulletproof_add_scratch_frame(size_t* total, size_t bytes, size_t objects) {
134 size_t padding;
135 return purify_bridge_checked_mul_size(objects, ALIGNMENT - 1, &padding)
136 && purify_bridge_checked_add_size(bytes, padding, &bytes)
137 && purify_bridge_checked_add_size(*total, bytes, total);
138}
139
141 const secp256k1_bulletproof_circuit* circuit, size_t* out) {
142 size_t scalars;
143 size_t bytes;
144 size_t points;
145 size_t prover = 0;
146 size_t verifier = 0;
147 size_t allocation_size;
148
149 /* The prover's compressed circuit stays live throughout the nested
150 * inner-product proof. Match both frames in the imported implementation.
151 */
152 if (!purify_bridge_checked_mul_size(5, circuit->n_gates, &scalars)
153 || !purify_bridge_checked_add_size(scalars, circuit->n_constraints, &scalars)
154 || !purify_bridge_checked_add_size(scalars, circuit->n_commits, &scalars)
155 || !purify_bridge_checked_mul_size(scalars, sizeof(secp256k1_scalar), &bytes)
157 || !purify_bulletproof_add_scratch_frame(&prover, bytes, 2)) {
158 return 0;
159 }
160 if (circuit->n_gates > 2) {
161 if (!purify_bridge_checked_mul_size(2, circuit->n_gates, &scalars)
162 || !purify_bridge_checked_mul_size(scalars, sizeof(secp256k1_scalar) + sizeof(secp256k1_ge), &bytes)
164 || !purify_bridge_checked_mul_size(points, sizeof(secp256k1_ge), &points)
165 || !purify_bridge_checked_add_size(bytes, points, &bytes)
166 || !purify_bulletproof_add_scratch_frame(&prover, bytes, 5)) {
167 return 0;
168 }
169 }
170
171 /* The bridge verifies one proof: outer verifier, compressed circuit, and
172 * inner-product verifier frames are simultaneously live during the MSM.
173 */
176 || !purify_bulletproof_add_scratch_frame(&verifier, bytes, 2)
177 || !purify_bridge_checked_mul_size(2, circuit->n_gates, &scalars)
178 || !purify_bridge_checked_add_size(scalars, circuit->n_constraints, &scalars)
179 || !purify_bridge_checked_add_size(scalars, circuit->n_commits, &scalars)
180 || !purify_bridge_checked_mul_size(scalars, sizeof(secp256k1_scalar), &bytes)
182 || !purify_bulletproof_add_scratch_frame(&verifier, bytes, 2)
183 || !purify_bridge_checked_add_size(sizeof(secp256k1_scalar),
185 || !purify_bulletproof_add_scratch_frame(&verifier, bytes, 2)
186 || !purify_bridge_checked_add_size(prover > verifier ? prover : verifier,
188 || !purify_bridge_checked_add_size(*out, ROUND_TO_ALIGN(sizeof(secp256k1_scratch)), &allocation_size)) {
189 return 0;
190 }
191 return 1;
192}
193
196 const secp256k1_bulletproof_circuit* circuit) {
197 size_t required;
198 secp256k1_scratch_space* replacement;
199
200 if (!purify_bulletproof_circuit_scratch_size(circuit, &required)) {
201 return 0;
202 }
203 if (required <= resources->scratch_capacity) {
204 return 1;
205 }
206 replacement = secp256k1_scratch_space_create(resources->ctx, required);
207 if (replacement == NULL) {
208 return 0;
209 }
210 secp256k1_scratch_space_destroy(resources->ctx, resources->scratch);
211 resources->scratch = replacement;
212 resources->scratch_capacity = required;
213 return 1;
214}
215
216static void* purify_malloc_array(size_t count, size_t elem_size) {
217 size_t bytes = 0;
218
219 if (count == 0) {
220 return NULL;
221 }
222 if (!purify_bridge_checked_mul_size(count, elem_size, &bytes)) {
223 return NULL;
224 }
225 return malloc(bytes);
226}
227
228static void* purify_calloc_array(size_t count, size_t elem_size) {
229 size_t bytes = 0;
230
231 if (count == 0) {
232 return NULL;
233 }
234 if (!purify_bridge_checked_mul_size(count, elem_size, &bytes)) {
235 return NULL;
236 }
237 return calloc(1, bytes);
238}
239
240static secp256k1_bppp_generators* purify_bppp_generators_clone(
241 const secp256k1_bppp_generators* generators) {
242 secp256k1_bppp_generators* clone;
243 size_t generator_bytes = 0;
244
245 if (generators == NULL || generators->gens == NULL || generators->n == 0 ||
246 !purify_bridge_checked_mul_size(generators->n, sizeof(*generators->gens), &generator_bytes)) {
247 return NULL;
248 }
249
250 clone = (secp256k1_bppp_generators*)calloc(1, sizeof(*clone));
251 if (clone == NULL) {
252 return NULL;
253 }
254 clone->n = generators->n;
255 clone->gens = (secp256k1_ge*)purify_malloc_array(generators->n, sizeof(*clone->gens));
256 if (clone->gens == NULL) {
257 free(clone);
258 return NULL;
259 }
260 memcpy(clone->gens, generators->gens, generator_bytes);
261 return clone;
262}
263
264/* BPPP proving folds the generator table in place, so cached resources keep one
265 pristine parsed copy and one resettable scratch copy for prove calls. */
267 size_t generator_bytes = 0;
268
269 if (resources == NULL || resources->gens == NULL || resources->gens->gens == NULL ||
270 resources->gens->n != resources->generators_count ||
271 resources->gens_scratch.gens == NULL || resources->gens_scratch.n != resources->generators_count ||
272 !purify_bridge_checked_mul_size(resources->generators_count, sizeof(*resources->gens->gens), &generator_bytes)) {
273 return 0;
274 }
275
276 memcpy(resources->gens_scratch.gens, resources->gens->gens, generator_bytes);
277 return 1;
278}
279
283 if (guard == NULL || resources == NULL || resources->gens_scratch_in_use) {
284 return NULL;
285 }
287 return NULL;
288 }
289 resources->gens_scratch_in_use = 1;
290 guard->resources = resources;
291 return &resources->gens_scratch;
292}
293
296 if (guard != NULL && guard->resources != NULL) {
297 guard->resources->gens_scratch_in_use = 0;
298 guard->resources = NULL;
299 }
300}
301
303 size_t n_gates) {
305 size_t generator_count = 0;
306 secp256k1_context* ctx = purify_context_handle(context);
307
308 if (ctx == NULL || n_gates == 0 || !secp256k1_is_power_of_two(n_gates)) {
309 return NULL;
310 }
311 if (!purify_bridge_checked_mul_size(2u, n_gates, &generator_count)) {
312 return NULL;
313 }
314
315 resources = (purify_bulletproof_backend_resources*)calloc(1, sizeof(*resources));
316 if (resources == NULL) {
317 return NULL;
318 }
319
320 resources->ctx = ctx;
321
323 resources->scratch = secp256k1_scratch_space_create(resources->ctx, resources->scratch_capacity);
324 if (resources->scratch == NULL) {
326 return NULL;
327 }
328
329 resources->gens = secp256k1_bulletproof_generators_create(resources->ctx, secp256k1_generator_h, generator_count, 1);
330 if (resources->gens == NULL || resources->gens->n < generator_count) {
332 return NULL;
333 }
334
335 resources->n_gates = n_gates;
336 return resources;
337}
338
340 const purify_bulletproof_backend_resources* resources) {
342 size_t generator_count = 0;
343
344 if (resources == NULL || resources->ctx == NULL || resources->n_gates == 0 ||
345 !purify_bridge_checked_mul_size(2u, resources->n_gates, &generator_count)) {
346 return NULL;
347 }
348
349 clone = (purify_bulletproof_backend_resources*)calloc(1, sizeof(*clone));
350 if (clone == NULL) {
351 return NULL;
352 }
353 clone->ctx = resources->ctx;
354 clone->n_gates = resources->n_gates;
355 clone->scratch_capacity = resources->scratch_capacity;
356 clone->scratch = secp256k1_scratch_space_create(clone->ctx, clone->scratch_capacity);
357 if (clone->scratch == NULL) {
359 return NULL;
360 }
362 clone->ctx, secp256k1_generator_h, generator_count, 1);
363 if (clone->gens == NULL || clone->gens->n < generator_count) {
365 return NULL;
366 }
367 return clone;
368}
369
371 if (resources == NULL) {
372 return;
373 }
374 if (resources->gens != NULL && resources->ctx != NULL) {
375 secp256k1_bulletproof_generators_destroy(resources->ctx, resources->gens);
376 }
377 if (resources->scratch != NULL && resources->ctx != NULL) {
378 secp256k1_scratch_space_destroy(resources->ctx, resources->scratch);
379 }
380 free(resources);
381}
382
384 const unsigned char* generators33,
385 size_t generators_count) {
387 size_t serialized_len = 0;
388 secp256k1_context* ctx = purify_context_handle(context);
389
390 if (ctx == NULL || generators33 == NULL || generators_count == 0) {
391 return NULL;
392 }
393 if (!purify_bridge_checked_mul_size(generators_count, 33u, &serialized_len)) {
394 return NULL;
395 }
396
397 resources = (purify_bppp_backend_resources*)calloc(1, sizeof(*resources));
398 if (resources == NULL) {
399 return NULL;
400 }
401
402 resources->ctx = ctx;
403
404 resources->scratch = secp256k1_scratch_space_create(resources->ctx, 1u << 24);
405 if (resources->scratch == NULL) {
407 return NULL;
408 }
409
410 resources->gens = secp256k1_bppp_generators_parse(resources->ctx, generators33, serialized_len);
411 if (resources->gens == NULL || resources->gens->n != generators_count) {
413 return NULL;
414 }
415 resources->gens_scratch.n = generators_count;
416 resources->gens_scratch.gens =
417 (secp256k1_ge*)purify_malloc_array(generators_count, sizeof(*resources->gens_scratch.gens));
418 if (resources->gens_scratch.gens == NULL) {
420 return NULL;
421 }
422
423 resources->generators_count = generators_count;
424 return resources;
425}
426
428 const purify_bppp_backend_resources* resources) {
430 size_t generator_bytes = 0;
431
432 if (resources == NULL || resources->ctx == NULL || resources->generators_count == 0 ||
433 resources->gens == NULL || resources->gens->n != resources->generators_count ||
434 !purify_bridge_checked_mul_size(resources->generators_count, sizeof(*resources->gens->gens), &generator_bytes)) {
435 return NULL;
436 }
437
438 clone = (purify_bppp_backend_resources*)calloc(1, sizeof(*clone));
439 if (clone == NULL) {
440 return NULL;
441 }
442 clone->ctx = resources->ctx;
443 clone->generators_count = resources->generators_count;
444 clone->scratch = secp256k1_scratch_space_create(clone->ctx, 1u << 24);
445 if (clone->scratch == NULL) {
447 return NULL;
448 }
449 clone->gens = purify_bppp_generators_clone(resources->gens);
450 if (clone->gens == NULL) {
452 return NULL;
453 }
454 clone->gens_scratch.n = clone->generators_count;
455 clone->gens_scratch.gens =
456 (secp256k1_ge*)purify_malloc_array(clone->generators_count, sizeof(*clone->gens_scratch.gens));
457 if (clone->gens_scratch.gens == NULL) {
459 return NULL;
460 }
461 memcpy(clone->gens_scratch.gens, clone->gens->gens, generator_bytes);
462 return clone;
463}
464
466 if (resources == NULL) {
467 return;
468 }
469 if (resources->gens != NULL && resources->ctx != NULL) {
470 secp256k1_bppp_generators_destroy(resources->ctx, resources->gens);
471 }
472 if (resources->gens_scratch.gens != NULL) {
473 free(resources->gens_scratch.gens);
474 }
475 if (resources->scratch != NULL && resources->ctx != NULL) {
476 secp256k1_scratch_space_destroy(resources->ctx, resources->scratch);
477 }
478 free(resources);
479}
480
481static secp256k1_scalar* purify_scalar_cast(purify_scalar* scalar) {
482 return (secp256k1_scalar*)scalar;
483}
484
485static const secp256k1_scalar* purify_scalar_cast_const(const purify_scalar* scalar) {
486 return (const secp256k1_scalar*)scalar;
487}
488
489void purify_scalar_set_int(purify_scalar* out, unsigned int value) {
490 secp256k1_scalar_set_int(purify_scalar_cast(out), value);
491}
492
493void purify_scalar_set_u64(purify_scalar* out, uint64_t value) {
494 secp256k1_scalar_set_u64(purify_scalar_cast(out), value);
495}
496
497void purify_scalar_set_b32(purify_scalar* out, const unsigned char input32[32], int* overflow) {
498 secp256k1_scalar_set_b32(purify_scalar_cast(out), input32, overflow);
499}
500
501void purify_scalar_get_b32(unsigned char output32[32], const purify_scalar* value) {
502 secp256k1_scalar_get_b32(output32, purify_scalar_cast_const(value));
503}
504
506 return secp256k1_scalar_is_zero(purify_scalar_cast_const(value));
507}
508
510 return secp256k1_scalar_is_one(purify_scalar_cast_const(value));
511}
512
514 return secp256k1_scalar_is_even(purify_scalar_cast_const(value));
515}
516
517int purify_scalar_eq(const purify_scalar* lhs, const purify_scalar* rhs) {
518 return secp256k1_scalar_eq(purify_scalar_cast_const(lhs), purify_scalar_cast_const(rhs));
519}
520
522 secp256k1_scalar_negate(purify_scalar_cast(out), purify_scalar_cast_const(value));
523}
524
526 secp256k1_scalar_inverse(purify_scalar_cast(out), purify_scalar_cast_const(value));
527}
528
530 secp256k1_scalar_inverse_var(purify_scalar_cast(out), purify_scalar_cast_const(value));
531}
532
534 return secp256k1_scalar_add(purify_scalar_cast(out), purify_scalar_cast_const(lhs), purify_scalar_cast_const(rhs));
535}
536
537void purify_scalar_mul(purify_scalar* out, const purify_scalar* lhs, const purify_scalar* rhs) {
538 secp256k1_scalar_mul(purify_scalar_cast(out), purify_scalar_cast_const(lhs), purify_scalar_cast_const(rhs));
539}
540
541void purify_scalar_cmov(purify_scalar* dst, const purify_scalar* src, int flag) {
542 secp256k1_scalar_cmov(purify_scalar_cast(dst), purify_scalar_cast_const(src), flag);
543}
544
545void purify_sha256(unsigned char output32[32], const unsigned char *data,
546 size_t data_len) {
547 static const unsigned char zero = 0;
548 secp256k1_sha256 sha256;
549
550 if (data == NULL && data_len == 0) {
551 data = &zero;
552 }
553
554 secp256k1_sha256_initialize(&sha256);
555 if (data_len != 0) {
556 secp256k1_sha256_write(&sha256, data, data_len);
557 }
558 secp256k1_sha256_finalize(&sha256, output32);
559 secp256k1_sha256_clear(&sha256);
560}
561
562int purify_sha256_many(unsigned char output32[32],
563 const unsigned char *const *items,
564 const size_t *item_lens,
565 size_t items_count) {
566 secp256k1_sha256 sha256;
567 if (output32 == NULL) {
568 return 0;
569 }
570 if (items_count != 0 && (items == NULL || item_lens == NULL)) {
571 memset(output32, 0, 32);
572 return 0;
573 }
574 secp256k1_sha256_initialize(&sha256);
575 for (size_t i = 0; i < items_count; ++i) {
576 const unsigned char *item = items[i];
577 size_t item_len = item_lens[i];
578 if (item == NULL) {
579 if (item_len != 0) {
580 secp256k1_sha256_clear(&sha256);
581 memset(output32, 0, 32);
582 return 0;
583 }
584 continue;
585 }
586 if (item_len != 0) {
587 secp256k1_sha256_write(&sha256, item, item_len);
588 }
589 }
590 secp256k1_sha256_finalize(&sha256, output32);
591 secp256k1_sha256_clear(&sha256);
592 return 1;
593}
594
595void purify_hmac_sha256(unsigned char output32[32],
596 const unsigned char* key, size_t key_len,
597 const unsigned char* data, size_t data_len) {
598 static const unsigned char zero = 0;
599 secp256k1_hmac_sha256 hmac;
600
601 if (key == NULL && key_len == 0) {
602 key = &zero;
603 }
604 if (data == NULL && data_len == 0) {
605 data = &zero;
606 }
607
608 secp256k1_hmac_sha256_initialize(&hmac, key, key_len);
609 if (data_len != 0) {
610 secp256k1_hmac_sha256_write(&hmac, data, data_len);
611 }
612 secp256k1_hmac_sha256_finalize(&hmac, output32);
613 secp256k1_hmac_sha256_clear(&hmac);
614}
615
617 unsigned char seckey32[32],
618 unsigned char xonly_pubkey32[32]) {
619 secp256k1_context* ctx = purify_context_handle(context);
620 secp256k1_keypair keypair;
621 secp256k1_xonly_pubkey xonly;
622 int parity = 0;
623 int ok = 0;
624
625 memset(&keypair, 0, sizeof(keypair));
626 memset(&xonly, 0, sizeof(xonly));
627 if (xonly_pubkey32 != NULL) {
628 memset(xonly_pubkey32, 0, 32);
629 }
630
631 if (ctx == NULL || seckey32 == NULL || xonly_pubkey32 == NULL) {
632 return 0;
633 }
634
635 ok = secp256k1_keypair_create(ctx, &keypair, seckey32);
636 if (ok) {
637 ok = secp256k1_keypair_xonly_pub(ctx, &xonly, &parity, &keypair);
638 }
639 if (ok) {
640 ok = secp256k1_xonly_pubkey_serialize(ctx, xonly_pubkey32, &xonly);
641 }
642 if (ok && parity != 0) {
643 ok = secp256k1_ec_seckey_negate(ctx, seckey32);
644 }
645 if (!ok) {
646 purify_bridge_secure_clear(seckey32, 32);
647 memset(xonly_pubkey32, 0, 32);
648 }
649
650 purify_bridge_secure_clear(&keypair, sizeof(keypair));
651 purify_bridge_secure_clear(&xonly, sizeof(xonly));
652 return ok;
653}
654
656 unsigned char scalar32[32],
657 unsigned char xonly_nonce32[32]) {
658 secp256k1_context* ctx = purify_context_handle(context);
659 secp256k1_pubkey pubkey;
660 secp256k1_xonly_pubkey xonly;
661 int parity = 0;
662 int ok = 0;
663
664 memset(&pubkey, 0, sizeof(pubkey));
665 memset(&xonly, 0, sizeof(xonly));
666 if (xonly_nonce32 != NULL) {
667 memset(xonly_nonce32, 0, 32);
668 }
669
670 if (ctx == NULL || scalar32 == NULL || xonly_nonce32 == NULL) {
671 return 0;
672 }
673
674 ok = secp256k1_ec_pubkey_create(ctx, &pubkey, scalar32);
675 if (ok) {
676 ok = secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly, &parity, &pubkey);
677 }
678 if (ok) {
679 ok = secp256k1_xonly_pubkey_serialize(ctx, xonly_nonce32, &xonly);
680 }
681 if (ok && parity != 0) {
682 ok = secp256k1_ec_seckey_negate(ctx, scalar32);
683 }
684 if (!ok) {
685 purify_bridge_secure_clear(scalar32, 32);
686 memset(xonly_nonce32, 0, 32);
687 }
688
689 purify_bridge_secure_clear(&pubkey, sizeof(pubkey));
690 purify_bridge_secure_clear(&xonly, sizeof(xonly));
691 return ok;
692}
693
695 const unsigned char point33[33],
696 unsigned char xonly32[32],
697 int* parity_out) {
698 secp256k1_context* ctx = purify_context_handle(context);
699 secp256k1_pubkey pubkey;
700 secp256k1_xonly_pubkey xonly;
701 int parity = 0;
702 int ok = 0;
703
704 memset(&pubkey, 0, sizeof(pubkey));
705 memset(&xonly, 0, sizeof(xonly));
706 if (xonly32 != NULL) {
707 memset(xonly32, 0, 32);
708 }
709 if (parity_out != NULL) {
710 *parity_out = 0;
711 }
712
713 if (ctx == NULL || point33 == NULL || xonly32 == NULL) {
714 return 0;
715 }
716
717 ok = secp256k1_ec_pubkey_parse(ctx, &pubkey, point33, 33);
718 if (ok) {
719 ok = secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly, &parity, &pubkey);
720 }
721 if (ok) {
722 ok = secp256k1_xonly_pubkey_serialize(ctx, xonly32, &xonly);
723 }
724 if (ok && parity_out != NULL) {
725 *parity_out = parity;
726 }
727 if (!ok) {
728 memset(xonly32, 0, 32);
729 }
730
731 purify_bridge_secure_clear(&pubkey, sizeof(pubkey));
732 purify_bridge_secure_clear(&xonly, sizeof(xonly));
733 return ok;
734}
735
737 const unsigned char xonly_pubkey32[32]) {
738 secp256k1_context* ctx = purify_context_handle(context);
739 secp256k1_xonly_pubkey xonly;
740 int ok = 0;
741
742 memset(&xonly, 0, sizeof(xonly));
743 if (ctx == NULL || xonly_pubkey32 == NULL) {
744 return 0;
745 }
746
747 ok = secp256k1_xonly_pubkey_parse(ctx, &xonly, xonly_pubkey32);
748
749 purify_bridge_secure_clear(&xonly, sizeof(xonly));
750 return ok;
751}
752
754 const unsigned char sig64[64]) {
755 secp256k1_context* ctx = purify_context_handle(context);
756 secp256k1_xonly_pubkey rxonly;
757 secp256k1_scalar s;
758 int overflow = 0;
759 int ok = 0;
760
761 purify_bridge_secure_clear(&rxonly, sizeof(rxonly));
762 purify_bridge_secure_clear(&s, sizeof(s));
763 if (ctx == NULL || sig64 == NULL) {
764 return 0;
765 }
766
767 ok = secp256k1_xonly_pubkey_parse(ctx, &rxonly, sig64);
768 if (ok) {
769 secp256k1_scalar_set_b32(&s, sig64 + 32, &overflow);
770 ok = !overflow;
771 }
772
773 purify_bridge_secure_clear(&rxonly, sizeof(rxonly));
774 secp256k1_scalar_clear(&s);
775 return ok;
776}
777
778static int purify_fixed_nonce_function(unsigned char *nonce32,
779 const unsigned char *msg, size_t msglen,
780 const unsigned char *key32,
781 const unsigned char *xonly_pk32,
782 const unsigned char *algo, size_t algolen,
783 void *data) {
784 (void)msg;
785 (void)msglen;
786 (void)key32;
787 (void)xonly_pk32;
788 (void)algo;
789 (void)algolen;
790
791 if (nonce32 == NULL || data == NULL) {
792 return 0;
793 }
794 memcpy(nonce32, data, 32);
795 return 1;
796}
797
799 unsigned char sig64[64],
800 const unsigned char* msg, size_t msglen,
801 const unsigned char seckey32[32],
802 const unsigned char nonce32[32]) {
803 secp256k1_context* ctx = purify_context_handle(context);
804 secp256k1_keypair keypair;
805 secp256k1_scalar nonce_scalar;
806 secp256k1_schnorrsig_extraparams extraparams = SECP256K1_SCHNORRSIG_EXTRAPARAMS_INIT;
807 unsigned char canonical_nonce32[32];
808 unsigned char xonly_nonce32[32];
809 int ok = 0;
810 int overflow = 0;
811
812 purify_bridge_secure_clear(&keypair, sizeof(keypair));
813 purify_bridge_secure_clear(&nonce_scalar, sizeof(nonce_scalar));
814 purify_bridge_secure_clear(canonical_nonce32, sizeof(canonical_nonce32));
815 purify_bridge_secure_clear(xonly_nonce32, sizeof(xonly_nonce32));
816 if (sig64 != NULL) {
817 memset(sig64, 0, 64);
818 }
819
820 if (ctx == NULL || sig64 == NULL || seckey32 == NULL || nonce32 == NULL || (msg == NULL && msglen != 0)) {
821 return 0;
822 }
823
824 memcpy(canonical_nonce32, nonce32, 32);
825 ok = purify_bip340_nonce_from_scalar(context, canonical_nonce32, xonly_nonce32);
826 if (ok) {
827 ok = secp256k1_memcmp_var(canonical_nonce32, nonce32, 32) == 0;
828 }
829 secp256k1_scalar_set_b32(&nonce_scalar, nonce32, &overflow);
830 if (ok && !overflow && !secp256k1_scalar_is_zero(&nonce_scalar)) {
831 ok = secp256k1_keypair_create(ctx, &keypair, seckey32);
832 if (ok) {
833 extraparams.noncefp = purify_fixed_nonce_function;
834 extraparams.ndata = (void*)nonce32;
835 ok = secp256k1_schnorrsig_sign_custom(ctx, sig64, msg, msglen, &keypair, &extraparams);
836 }
837 }
838
839 purify_bridge_secure_clear(&keypair, sizeof(keypair));
840 secp256k1_scalar_clear(&nonce_scalar);
841 purify_bridge_secure_clear(canonical_nonce32, sizeof(canonical_nonce32));
842 purify_bridge_secure_clear(xonly_nonce32, sizeof(xonly_nonce32));
843 if (!ok) {
844 memset(sig64, 0, 64);
845 }
846 return ok;
847}
848
850 const unsigned char sig64[64],
851 const unsigned char* msg, size_t msglen,
852 const unsigned char xonly_pubkey32[32]) {
853 secp256k1_context* ctx = purify_context_handle(context);
854 secp256k1_xonly_pubkey pubkey;
855 int ok = 0;
856
857 memset(&pubkey, 0, sizeof(pubkey));
858 if (ctx == NULL || sig64 == NULL || xonly_pubkey32 == NULL || (msg == NULL && msglen != 0)) {
859 return 0;
860 }
861
862 ok = secp256k1_xonly_pubkey_parse(ctx, &pubkey, xonly_pubkey32);
863 if (ok) {
864 ok = secp256k1_schnorrsig_verify(ctx, sig64, msg, msglen, &pubkey);
865 }
866
867 memset(&pubkey, 0, sizeof(pubkey));
868 return ok;
869}
870
871static int purify_parse_scalar(const unsigned char input32[32], secp256k1_scalar* scalar, int reject_zero) {
872 int overflow = 0;
873 secp256k1_scalar_set_b32(scalar, input32, &overflow);
874 if (overflow) {
875 return 0;
876 }
877 if (reject_zero && secp256k1_scalar_is_zero(scalar)) {
878 return 0;
879 }
880 return 1;
881}
882
883static int purify_parse_scalar_array(const unsigned char* input32, size_t count, secp256k1_scalar* out) {
884 size_t i;
885 for (i = 0; i < count; ++i) {
886 if (!purify_parse_scalar(input32 + 32 * i, &out[i], 0)) {
887 return 0;
888 }
889 }
890 return 1;
891}
892
893static int purify_serialize_point(unsigned char out33[33], secp256k1_ge* point) {
894 secp256k1_fe_normalize_var(&point->x);
895 secp256k1_fe_normalize_var(&point->y);
896 return secp256k1_bppp_serialize_pt(out33, point);
897}
898
899static int purify_parse_fast_scalar(const unsigned char input32[32], secp256k1_fast_scalar* scalar) {
900 secp256k1_scalar one, two, neg_one, neg_two;
901
902 if (!purify_parse_scalar(input32, &scalar->scal, 0)) {
903 return 0;
904 }
905
906 secp256k1_scalar_set_int(&one, 1);
907 secp256k1_scalar_set_int(&two, 2);
908 secp256k1_scalar_negate(&neg_one, &one);
909 secp256k1_scalar_negate(&neg_two, &two);
910
911 scalar->special = 3;
912 if (secp256k1_scalar_is_zero(&scalar->scal)) {
913 scalar->special = 0;
914 } else if (secp256k1_scalar_eq(&scalar->scal, &one)) {
915 scalar->special = 1;
916 } else if (secp256k1_scalar_eq(&scalar->scal, &two)) {
917 scalar->special = 2;
918 } else if (secp256k1_scalar_eq(&scalar->scal, &neg_one)) {
919 scalar->special = -1;
920 } else if (secp256k1_scalar_eq(&scalar->scal, &neg_two)) {
921 scalar->special = -2;
922 }
923 return 1;
924}
925
926static int purify_parse_generator_as_ge(const secp256k1_context* ctx, const unsigned char generator33[33], secp256k1_ge* out) {
927 secp256k1_generator generator;
928 if (ctx == NULL || generator33 == NULL || out == NULL) {
929 return 0;
930 }
931 if (!secp256k1_generator_parse(ctx, &generator, generator33)) {
932 return 0;
933 }
934 secp256k1_generator_load(out, &generator);
935 return 1;
936}
937
938static int purify_parse_point_as_ge(const unsigned char point33[33], secp256k1_ge* out) {
939 if (point33 == NULL || out == NULL) {
940 return 0;
941 }
942 return secp256k1_eckey_pubkey_parse(out, point33, 33);
943}
944
946 if (circuit == NULL) {
947 return;
948 }
949 free(circuit->wl);
950 free(circuit->wr);
951 free(circuit->wo);
952 free(circuit->wv);
953 free(circuit->c);
954 free(circuit->entries);
955 memset(circuit, 0, sizeof(*circuit));
956}
957
959 if (assignment == NULL) {
960 return;
961 }
962 free(assignment->al);
963 free(assignment->ar);
964 free(assignment->ao);
965 free(assignment->v);
966 memset(assignment, 0, sizeof(*assignment));
967}
968
969static size_t purify_total_row_entries(const purify_bulletproof_row_view* rows, size_t count, int* ok) {
970 size_t total = 0;
971 size_t i;
972
973 if (ok != NULL) {
974 *ok = 0;
975 }
976 if (count == 0) {
977 if (ok != NULL) {
978 *ok = 1;
979 }
980 return 0;
981 }
982 if (rows == NULL) {
983 return 0;
984 }
985 for (i = 0; i < count; ++i) {
986 if (rows[i].size != 0 && (rows[i].indices == NULL || rows[i].scalars32 == NULL)) {
987 return 0;
988 }
989 if (!purify_bridge_checked_add_size(total, rows[i].size, &total)) {
990 return 0;
991 }
992 }
993 if (ok != NULL) {
994 *ok = 1;
995 }
996 return total;
997}
998
1000 size_t row_count,
1001 const purify_bulletproof_row_view* in_rows,
1002 size_t n_constraints,
1004 size_t* offset) {
1005 size_t i;
1006 for (i = 0; i < row_count; ++i) {
1007 size_t j;
1008 out_rows[i].size = in_rows[i].size;
1009 out_rows[i].entry = in_rows[i].size == 0 ? NULL : entries + *offset;
1010 for (j = 0; j < in_rows[i].size; ++j) {
1011 size_t entry_idx = *offset + j;
1012 if (in_rows[i].indices[j] >= n_constraints) {
1013 return 0;
1014 }
1015 out_rows[i].entry[j].idx = in_rows[i].indices[j];
1016 if (!purify_parse_fast_scalar(in_rows[i].scalars32 + 32 * j, &out_rows[i].entry[j].scal)) {
1017 return 0;
1018 }
1019 entries[entry_idx] = out_rows[i].entry[j];
1020 }
1021 *offset += in_rows[i].size;
1022 }
1023 return 1;
1024}
1025
1026static void purify_bulletproof_circuit_evaluate_sum_row(secp256k1_scalar* acc,
1028 const secp256k1_scalar* assn) {
1029 size_t j;
1030 for (j = 0; j < row->size; ++j) {
1031 secp256k1_scalar term;
1032 secp256k1_fast_scalar_mul(&term, &row->entry[j].scal, assn);
1033 secp256k1_scalar_add(&acc[row->entry[j].idx], &acc[row->entry[j].idx], &term);
1034 }
1035}
1036
1038 const secp256k1_bulletproof_circuit_assignment* assignment) {
1039 secp256k1_scalar* acc = NULL;
1040 size_t i;
1041 int ok = 0;
1042
1043 if (circuit == NULL || assignment == NULL) {
1044 return 0;
1045 }
1046 if (assignment->n_gates != circuit->n_gates || assignment->n_commits != circuit->n_commits) {
1047 return 0;
1048 }
1049
1050 acc = circuit->n_constraints == 0 ? NULL : (secp256k1_scalar*)calloc(circuit->n_constraints, sizeof(*acc));
1051 if (circuit->n_constraints != 0 && acc == NULL) {
1052 return 0;
1053 }
1054
1055 for (i = 0; i < assignment->n_gates; ++i) {
1056 secp256k1_scalar product;
1057 secp256k1_scalar_mul(&product, &assignment->al[i], &assignment->ar[i]);
1058 if (!secp256k1_scalar_eq(&product, &assignment->ao[i])) {
1059 goto done;
1060 }
1061 }
1062
1063 for (i = 0; i < circuit->n_constraints; ++i) {
1064 secp256k1_scalar_clear(&acc[i]);
1065 }
1066
1067 for (i = 0; i < circuit->n_gates; ++i) {
1068 purify_bulletproof_circuit_evaluate_sum_row(acc, &circuit->wl[i], &assignment->al[i]);
1069 purify_bulletproof_circuit_evaluate_sum_row(acc, &circuit->wr[i], &assignment->ar[i]);
1070 purify_bulletproof_circuit_evaluate_sum_row(acc, &circuit->wo[i], &assignment->ao[i]);
1071 }
1072 for (i = 0; i < circuit->n_commits; ++i) {
1073 secp256k1_scalar negated_v;
1074 secp256k1_scalar_negate(&negated_v, &assignment->v[i]);
1075 purify_bulletproof_circuit_evaluate_sum_row(acc, &circuit->wv[i], &negated_v);
1076 }
1077 for (i = 0; i < circuit->n_constraints; ++i) {
1078 secp256k1_scalar constant_term;
1079 secp256k1_scalar one;
1080 secp256k1_scalar_set_int(&one, 1);
1081 secp256k1_fast_scalar_mul(&constant_term, &circuit->c[i], &one);
1082 if (!secp256k1_scalar_eq(&acc[i], &constant_term)) {
1083 goto done;
1084 }
1085 }
1086 ok = 1;
1087
1088done:
1089 free(acc);
1090 return ok;
1091}
1092
1095 int ok = 0;
1096 size_t row_entries = 0;
1097 size_t total_entries = 0;
1098 size_t offset = 0;
1099 size_t i;
1100
1101 /* Bit/range-style circuits can have no explicit constraint rows, so the
1102 * constant vector is optional when n_constraints == 0.
1103 */
1104 if (view == NULL || out == NULL || (view->n_constraints != 0 && view->c32 == NULL)) {
1105 return 0;
1106 }
1107 if (!secp256k1_is_power_of_two(view->n_gates) || view->n_commits > 1) {
1108 return 0;
1109 }
1110 memset(out, 0, sizeof(*out));
1111
1112 row_entries = purify_total_row_entries(view->wl, view->n_gates, &ok);
1113 if (!ok || !purify_bridge_checked_add_size(total_entries, row_entries, &total_entries)) return 0;
1114 row_entries = purify_total_row_entries(view->wr, view->n_gates, &ok);
1115 if (!ok || !purify_bridge_checked_add_size(total_entries, row_entries, &total_entries)) return 0;
1116 row_entries = purify_total_row_entries(view->wo, view->n_gates, &ok);
1117 if (!ok || !purify_bridge_checked_add_size(total_entries, row_entries, &total_entries)) return 0;
1118 row_entries = purify_total_row_entries(view->wv, view->n_commits, &ok);
1119 if (!ok || !purify_bridge_checked_add_size(total_entries, row_entries, &total_entries)) return 0;
1120
1121 out->n_gates = view->n_gates;
1122 out->n_commits = view->n_commits;
1123 out->n_bits = view->n_bits;
1124 out->n_constraints = view->n_constraints;
1125 out->wl = (secp256k1_bulletproof_wmatrix_row*)purify_calloc_array(view->n_gates, sizeof(*out->wl));
1126 out->wr = (secp256k1_bulletproof_wmatrix_row*)purify_calloc_array(view->n_gates, sizeof(*out->wr));
1127 out->wo = (secp256k1_bulletproof_wmatrix_row*)purify_calloc_array(view->n_gates, sizeof(*out->wo));
1129 /* Mirror the legacy circuit representation: no explicit constraints means
1130 * no c vector allocation either.
1131 */
1132 out->c = (secp256k1_fast_scalar*)purify_malloc_array(view->n_constraints, sizeof(*out->c));
1133 out->entries = (secp256k1_bulletproof_wmatrix_entry*)purify_malloc_array(total_entries, sizeof(*out->entries));
1134 if ((view->n_gates != 0 && (out->wl == NULL || out->wr == NULL || out->wo == NULL)) ||
1135 (view->n_commits != 0 && out->wv == NULL) ||
1136 (view->n_constraints != 0 && out->c == NULL) ||
1137 (total_entries != 0 && out->entries == NULL)) {
1139 return 0;
1140 }
1141
1142 if (!purify_build_row_family(out->wl, view->n_gates, view->wl, view->n_constraints, out->entries, &offset) ||
1143 !purify_build_row_family(out->wr, view->n_gates, view->wr, view->n_constraints, out->entries, &offset) ||
1144 !purify_build_row_family(out->wo, view->n_gates, view->wo, view->n_constraints, out->entries, &offset) ||
1145 !purify_build_row_family(out->wv, view->n_commits, view->wv, view->n_constraints, out->entries, &offset)) {
1147 return 0;
1148 }
1149 for (i = 0; i < view->n_constraints; ++i) {
1150 if (!purify_parse_fast_scalar(view->c32 + 32 * i, &out->c[i])) {
1152 return 0;
1153 }
1154 }
1155 return 1;
1156}
1157
1160 if (view == NULL || out == NULL) {
1161 return 0;
1162 }
1163 if ((view->n_gates != 0 && (view->al32 == NULL || view->ar32 == NULL || view->ao32 == NULL)) ||
1164 (view->n_commits != 0 && view->v32 == NULL) ||
1165 view->n_commits > 1) {
1166 return 0;
1167 }
1168 memset(out, 0, sizeof(*out));
1169 out->n_gates = view->n_gates;
1170 out->n_commits = view->n_commits;
1171 out->al = (secp256k1_scalar*)purify_malloc_array(view->n_gates, sizeof(*out->al));
1172 out->ar = (secp256k1_scalar*)purify_malloc_array(view->n_gates, sizeof(*out->ar));
1173 out->ao = (secp256k1_scalar*)purify_malloc_array(view->n_gates, sizeof(*out->ao));
1174 out->v = (secp256k1_scalar*)purify_malloc_array(view->n_commits, sizeof(*out->v));
1175 if ((view->n_gates != 0 && (out->al == NULL || out->ar == NULL || out->ao == NULL)) ||
1176 (view->n_commits != 0 && out->v == NULL)) {
1178 return 0;
1179 }
1180 if (!purify_parse_scalar_array(view->al32, view->n_gates, out->al) ||
1181 !purify_parse_scalar_array(view->ar32, view->n_gates, out->ar) ||
1182 !purify_parse_scalar_array(view->ao32, view->n_gates, out->ao) ||
1183 !purify_parse_scalar_array(view->v32, view->n_commits, out->v)) {
1185 return 0;
1186 }
1187 return 1;
1188}
1189
1191 size_t proof_size = 0;
1192
1193 if (n_gates == 0) {
1194 return 0;
1195 }
1197 return 0;
1198 }
1199 return proof_size;
1200}
1201
1203 const purify_bulletproof_assignment_view* assignment,
1204 const unsigned char* blind32,
1205 const unsigned char value_gen33[33],
1206 const unsigned char nonce32[32],
1207 const unsigned char* extra_commit,
1208 size_t extra_commit_len,
1209 unsigned char commitment_out33[33],
1210 unsigned char* proof_out,
1211 size_t* proof_len,
1212 int require_valid_assignment,
1213 purify_secp_context* context,
1215 secp256k1_context* ctx = NULL;
1216 secp256k1_scratch_space* scratch = NULL;
1220 secp256k1_ge value_gen;
1221 secp256k1_ge commit_points[1];
1222 secp256k1_scalar blinds[1];
1223 size_t n_commits = 0;
1224 size_t required_generators = 0;
1225 int ok = 0;
1226 int owns_resources = 0;
1227
1228 memset(&bp_circuit, 0, sizeof(bp_circuit));
1229 memset(&bp_assignment, 0, sizeof(bp_assignment));
1230 memset(commit_points, 0, sizeof(commit_points));
1231 memset(blinds, 0, sizeof(blinds));
1232 if (commitment_out33 != NULL) {
1233 memset(commitment_out33, 0, 33);
1234 }
1235
1236 if (circuit == NULL || assignment == NULL || value_gen33 == NULL || nonce32 == NULL || proof_out == NULL || proof_len == NULL) {
1237 return 0;
1238 }
1239 if (extra_commit == NULL && extra_commit_len != 0) {
1240 return 0;
1241 }
1242 if (!purify_build_bulletproof_circuit(circuit, &bp_circuit) ||
1243 !purify_build_bulletproof_assignment(assignment, &bp_assignment)) {
1244 goto done;
1245 }
1246 if (require_valid_assignment && !purify_bulletproof_circuit_evaluate(&bp_circuit, &bp_assignment)) {
1247 goto done;
1248 }
1249 if (resources == NULL) {
1250 resources = purify_bulletproof_backend_resources_create(context, bp_circuit.n_gates);
1251 if (resources == NULL) {
1252 goto done;
1253 }
1254 owns_resources = 1;
1255 }
1256 if (resources->n_gates < bp_circuit.n_gates || resources->ctx == NULL || resources->scratch == NULL || resources->gens == NULL) {
1257 goto done;
1258 }
1259 if (!purify_bulletproof_reserve_circuit_scratch(resources, &bp_circuit)) {
1260 goto done;
1261 }
1262 ctx = resources->ctx;
1263 scratch = resources->scratch;
1264 gens = resources->gens;
1265 if (!purify_bridge_checked_mul_size(2u, bp_circuit.n_gates, &required_generators) ||
1266 gens->n < required_generators) {
1267 goto done;
1268 }
1269 if (!purify_parse_generator_as_ge(ctx, value_gen33, &value_gen)) {
1270 goto done;
1271 }
1272
1273 n_commits = bp_circuit.n_commits;
1274 if (n_commits == 1) {
1275 secp256k1_gej commitj;
1276 if (commitment_out33 == NULL) {
1277 goto done;
1278 }
1279 if (blind32 == NULL) {
1280 secp256k1_scalar_clear(&blinds[0]);
1281 } else if (!purify_parse_scalar(blind32, &blinds[0], 0)) {
1282 goto done;
1283 }
1284 secp256k1_pedersen_ecmult_scalar(&commitj, &blinds[0], &bp_assignment.v[0], &value_gen, gens->blinding_gen);
1285 if (secp256k1_gej_is_infinity(&commitj)) {
1286 goto done;
1287 }
1288 secp256k1_ge_set_gej(&commit_points[0], &commitj);
1289 if (!purify_serialize_point(commitment_out33, &commit_points[0])) {
1290 goto done;
1291 }
1292 } else if (n_commits > 1) {
1293 goto done;
1294 }
1295
1297 &ctx->error_callback,
1298 scratch,
1299 proof_out,
1300 proof_len,
1301 &bp_assignment,
1302 n_commits == 0 ? NULL : commit_points,
1303 n_commits == 0 ? NULL : blinds,
1304 n_commits,
1305 &value_gen,
1306 &bp_circuit,
1307 gens,
1308 nonce32,
1309 extra_commit,
1310 extra_commit_len
1311 );
1312
1313done:
1314 purify_free_bulletproof_assignment(&bp_assignment);
1316 if (owns_resources) {
1318 }
1319 return ok;
1320}
1321
1323 const purify_bulletproof_circuit_view* circuit,
1324 const purify_bulletproof_assignment_view* assignment,
1325 const unsigned char* blind32,
1326 const unsigned char value_gen33[33],
1327 const unsigned char nonce32[32],
1328 const unsigned char* extra_commit,
1329 size_t extra_commit_len,
1330 unsigned char commitment_out33[33],
1331 unsigned char* proof_out,
1332 size_t* proof_len) {
1333 return purify_bulletproof_prove_circuit_impl(circuit, assignment, blind32, value_gen33, nonce32,
1334 extra_commit, extra_commit_len, commitment_out33,
1335 proof_out, proof_len, 1, context, NULL);
1336}
1337
1339 const purify_bulletproof_circuit_view* circuit,
1340 const purify_bulletproof_assignment_view* assignment,
1341 const unsigned char* blind32,
1342 const unsigned char value_gen33[33],
1343 const unsigned char nonce32[32],
1344 const unsigned char* extra_commit,
1345 size_t extra_commit_len,
1346 unsigned char commitment_out33[33],
1347 unsigned char* proof_out,
1348 size_t* proof_len) {
1349 return purify_bulletproof_prove_circuit_impl(circuit, assignment, blind32, value_gen33, nonce32,
1350 extra_commit, extra_commit_len, commitment_out33,
1351 proof_out, proof_len, 1, NULL, resources);
1352}
1353
1355 const purify_bulletproof_circuit_view* circuit,
1356 const purify_bulletproof_assignment_view* assignment,
1357 const unsigned char* blind32,
1358 const unsigned char value_gen33[33],
1359 const unsigned char nonce32[32],
1360 const unsigned char* extra_commit,
1361 size_t extra_commit_len,
1362 unsigned char commitment_out33[33],
1363 unsigned char* proof_out,
1364 size_t* proof_len) {
1365 return purify_bulletproof_prove_circuit_impl(circuit, assignment, blind32, value_gen33, nonce32,
1366 extra_commit, extra_commit_len, commitment_out33,
1367 proof_out, proof_len, 0, context, NULL);
1368}
1369
1371 const purify_bulletproof_circuit_view* circuit,
1372 const purify_bulletproof_assignment_view* assignment,
1373 const unsigned char* blind32,
1374 const unsigned char value_gen33[33],
1375 const unsigned char nonce32[32],
1376 const unsigned char* extra_commit,
1377 size_t extra_commit_len,
1378 unsigned char commitment_out33[33],
1379 unsigned char* proof_out,
1380 size_t* proof_len) {
1381 return purify_bulletproof_prove_circuit_impl(circuit, assignment, blind32, value_gen33, nonce32,
1382 extra_commit, extra_commit_len, commitment_out33,
1383 proof_out, proof_len, 0, NULL, resources);
1384}
1385
1388 const purify_bulletproof_circuit_view* circuit,
1389 const unsigned char commitment33[33],
1390 const unsigned char value_gen33[33],
1391 const unsigned char* extra_commit,
1392 size_t extra_commit_len,
1393 const unsigned char* proof,
1394 size_t proof_len);
1395
1397 const purify_bulletproof_circuit_view* circuit,
1398 const unsigned char commitment33[33],
1399 const unsigned char value_gen33[33],
1400 const unsigned char* extra_commit,
1401 size_t extra_commit_len,
1402 const unsigned char* proof,
1403 size_t proof_len) {
1404 return purify_bulletproof_verify_circuit_impl(context, NULL, circuit, commitment33, value_gen33,
1405 extra_commit, extra_commit_len, proof, proof_len);
1406}
1407
1410 const purify_bulletproof_circuit_view* circuit,
1411 const unsigned char commitment33[33],
1412 const unsigned char value_gen33[33],
1413 const unsigned char* extra_commit,
1414 size_t extra_commit_len,
1415 const unsigned char* proof,
1416 size_t proof_len) {
1417 secp256k1_context* ctx = NULL;
1418 secp256k1_scratch_space* scratch = NULL;
1421 secp256k1_ge value_gen;
1422 secp256k1_ge commit_points[1];
1423 const secp256k1_ge* commit_ptr = NULL;
1424 const secp256k1_bulletproof_circuit* circuit_ptr = NULL;
1425 const unsigned char* proof_ptr = NULL;
1426 const unsigned char* extra_commit_ptr = NULL;
1427 size_t n_commits = 0;
1428 size_t required_generators = 0;
1429 int ok = 0;
1430 int owns_resources = 0;
1431
1432 memset(&bp_circuit, 0, sizeof(bp_circuit));
1433 memset(commit_points, 0, sizeof(commit_points));
1434 if (circuit == NULL || value_gen33 == NULL || proof == NULL) {
1435 return 0;
1436 }
1437 if (extra_commit == NULL && extra_commit_len != 0) {
1438 return 0;
1439 }
1440 if (!purify_build_bulletproof_circuit(circuit, &bp_circuit)) {
1441 goto done;
1442 }
1443 if (resources == NULL) {
1444 resources = purify_bulletproof_backend_resources_create(context, bp_circuit.n_gates);
1445 if (resources == NULL) {
1446 goto done;
1447 }
1448 owns_resources = 1;
1449 }
1450 if (resources->n_gates < bp_circuit.n_gates || resources->ctx == NULL || resources->scratch == NULL || resources->gens == NULL) {
1451 goto done;
1452 }
1453 if (!purify_bulletproof_reserve_circuit_scratch(resources, &bp_circuit)) {
1454 goto done;
1455 }
1456 ctx = resources->ctx;
1457 scratch = resources->scratch;
1458 gens = resources->gens;
1459 if (!purify_bridge_checked_mul_size(2u, bp_circuit.n_gates, &required_generators) ||
1460 gens->n < required_generators) {
1461 goto done;
1462 }
1463 if (!purify_parse_generator_as_ge(ctx, value_gen33, &value_gen)) {
1464 goto done;
1465 }
1466
1467 n_commits = bp_circuit.n_commits;
1468 if (n_commits == 1) {
1469 if (commitment33 == NULL || !purify_parse_point_as_ge(commitment33, &commit_points[0])) {
1470 goto done;
1471 }
1472 commit_ptr = commit_points;
1473 } else if (n_commits > 1) {
1474 goto done;
1475 }
1476
1477 circuit_ptr = &bp_circuit;
1478 proof_ptr = proof;
1479 extra_commit_ptr = extra_commit;
1481 &ctx->error_callback,
1482 scratch,
1483 &proof_ptr,
1484 1,
1485 proof_len,
1486 n_commits == 0 ? NULL : &commit_ptr,
1487 n_commits == 0 ? NULL : &n_commits,
1488 &value_gen,
1489 &circuit_ptr,
1490 gens,
1491 &extra_commit_ptr,
1492 &extra_commit_len
1493 );
1494
1495done:
1497 if (owns_resources) {
1499 }
1500 return ok;
1501}
1502
1504 const purify_bulletproof_circuit_view* circuit,
1505 const unsigned char commitment33[33],
1506 const unsigned char value_gen33[33],
1507 const unsigned char* extra_commit,
1508 size_t extra_commit_len,
1509 const unsigned char* proof,
1510 size_t proof_len) {
1511 return purify_bulletproof_verify_circuit_impl(NULL, resources, circuit, commitment33, value_gen33,
1512 extra_commit, extra_commit_len, proof, proof_len);
1513}
1514
1515static int purify_norm_arg_commit_initial_data(secp256k1_sha256* transcript, const secp256k1_scalar* rho,
1516 const secp256k1_bppp_generators* gens_vec, size_t g_len,
1517 const secp256k1_scalar* c_vec, size_t c_vec_len,
1518 const secp256k1_ge* commit) {
1519 unsigned char ser_commit[33], ser_scalar[32], ser_le64[8];
1520 size_t i;
1521 secp256k1_ge comm = *commit;
1522
1523 secp256k1_bppp_sha256_tagged_commitment_init(transcript);
1524 /* The bridge's norm statement uses finite compressed points. Reject a
1525 * representation the transcript cannot serialize before hashing it. */
1526 if (!purify_serialize_point(ser_commit, &comm)) {
1527 return 0;
1528 }
1529 secp256k1_sha256_write(transcript, ser_commit, sizeof(ser_commit));
1530 secp256k1_scalar_get_b32(ser_scalar, rho);
1531 secp256k1_sha256_write(transcript, ser_scalar, sizeof(ser_scalar));
1532 secp256k1_bppp_le64(ser_le64, g_len);
1533 secp256k1_sha256_write(transcript, ser_le64, sizeof(ser_le64));
1534 secp256k1_bppp_le64(ser_le64, gens_vec->n);
1535 secp256k1_sha256_write(transcript, ser_le64, sizeof(ser_le64));
1536 for (i = 0; i < gens_vec->n; ++i) {
1537 secp256k1_ge gen = gens_vec->gens[i];
1538 if (!purify_serialize_point(ser_commit, &gen)) {
1539 return 0;
1540 }
1541 secp256k1_sha256_write(transcript, ser_commit, sizeof(ser_commit));
1542 }
1543 secp256k1_bppp_le64(ser_le64, c_vec_len);
1544 secp256k1_sha256_write(transcript, ser_le64, sizeof(ser_le64));
1545 for (i = 0; i < c_vec_len; ++i) {
1546 secp256k1_scalar_get_b32(ser_scalar, &c_vec[i]);
1547 secp256k1_sha256_write(transcript, ser_scalar, sizeof(ser_scalar));
1548 }
1549 return 1;
1550}
1551
1552static int purify_copy_vectors_into_scratch(const secp256k1_context* ctx, secp256k1_scratch_space* scratch,
1553 secp256k1_scalar** ns, secp256k1_scalar** ls, secp256k1_scalar** cs,
1554 secp256k1_ge** gs, const secp256k1_scalar* n_vec,
1555 const secp256k1_scalar* l_vec, const secp256k1_scalar* c_vec,
1556 const secp256k1_ge* gens_vec, size_t g_len, size_t h_len) {
1557 size_t scalar_g_bytes = 0;
1558 size_t scalar_h_bytes = 0;
1559 size_t ge_count = 0;
1560 size_t ge_bytes = 0;
1561
1562 if (!purify_bridge_checked_mul_size(g_len, sizeof(secp256k1_scalar), &scalar_g_bytes) ||
1563 !purify_bridge_checked_mul_size(h_len, sizeof(secp256k1_scalar), &scalar_h_bytes) ||
1564 !purify_bridge_checked_add_size(g_len, h_len, &ge_count) ||
1565 !purify_bridge_checked_mul_size(ge_count, sizeof(secp256k1_ge), &ge_bytes)) {
1566 return 0;
1567 }
1568
1569 *ns = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, scalar_g_bytes);
1570 *ls = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, scalar_h_bytes);
1571 *cs = (secp256k1_scalar*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, scalar_h_bytes);
1572 *gs = (secp256k1_ge*)secp256k1_scratch_alloc(&ctx->error_callback, scratch, ge_bytes);
1573 if (*ns == NULL || *ls == NULL || *cs == NULL || *gs == NULL) {
1574 return 0;
1575 }
1576 memcpy(*ns, n_vec, scalar_g_bytes);
1577 memcpy(*ls, l_vec, scalar_h_bytes);
1578 memcpy(*cs, c_vec, scalar_h_bytes);
1579 memcpy(*gs, gens_vec, ge_bytes);
1580 return 1;
1581}
1582
1583static int purify_bppp_rangeproof_norm_product_prove_const(const secp256k1_context* ctx, secp256k1_scratch_space* scratch,
1584 unsigned char* proof, size_t* proof_len,
1585 secp256k1_sha256* transcript, const secp256k1_scalar* rho,
1586 const secp256k1_ge* g_vec, size_t g_vec_len,
1587 const secp256k1_scalar* n_vec, size_t n_vec_len,
1588 const secp256k1_scalar* l_vec, size_t l_vec_len,
1589 const secp256k1_scalar* c_vec, size_t c_vec_len) {
1590 secp256k1_scalar *ns = NULL, *ls = NULL, *cs = NULL;
1591 secp256k1_ge *gs = NULL;
1592 size_t checkpoint = secp256k1_scratch_checkpoint(&ctx->error_callback, scratch);
1593 int result = 0;
1594
1595 if (purify_copy_vectors_into_scratch(ctx, scratch, &ns, &ls, &cs, &gs, n_vec, l_vec, c_vec, g_vec, n_vec_len, l_vec_len)) {
1596 result = secp256k1_bppp_rangeproof_norm_product_prove(ctx, scratch, proof, proof_len, transcript, rho,
1597 gs, g_vec_len, ns, n_vec_len, ls, l_vec_len, cs, c_vec_len);
1598 }
1599 secp256k1_scratch_apply_checkpoint(&ctx->error_callback, scratch, checkpoint);
1600 return result;
1601}
1602
1603size_t purify_bppp_required_proof_size(size_t n_vec_len, size_t c_vec_len) {
1604 size_t log_g_len, log_h_len, max_log_len, proof_size = 0;
1605 if (n_vec_len == 0 || c_vec_len == 0 || n_vec_len < c_vec_len) {
1606 return 0;
1607 }
1608 log_g_len = secp256k1_bppp_log2(n_vec_len);
1609 log_h_len = secp256k1_bppp_log2(c_vec_len);
1610 max_log_len = log_g_len > log_h_len ? log_g_len : log_h_len;
1611 if (!purify_bridge_checked_mul_size(65u, max_log_len, &proof_size) ||
1612 !purify_bridge_checked_add_size(proof_size, 64u, &proof_size)) {
1613 return 0;
1614 }
1615 return proof_size;
1616}
1617
1618int purify_bppp_base_generator(purify_secp_context* context, unsigned char out33[33]) {
1619 secp256k1_context* ctx = purify_context_handle(context);
1620 secp256k1_generator generator;
1621 secp256k1_ge ge = secp256k1_ge_const_g;
1622 int ok;
1623
1624 if (ctx == NULL || out33 == NULL) {
1625 return 0;
1626 }
1627 secp256k1_generator_save(&generator, &ge);
1628 ok = secp256k1_generator_serialize(ctx, out33, &generator);
1629 return ok;
1630}
1631
1632int purify_bppp_value_generator_h(purify_secp_context* context, unsigned char out33[33]) {
1633 secp256k1_context* ctx = purify_context_handle(context);
1634 int ok;
1635
1636 if (ctx == NULL || out33 == NULL) {
1637 return 0;
1638 }
1639 ok = secp256k1_generator_serialize(ctx, out33, secp256k1_generator_h);
1640 return ok;
1641}
1642
1643int purify_bppp_create_generators(purify_secp_context* context, size_t count, unsigned char* out, size_t* out_len) {
1644 secp256k1_context* ctx = purify_context_handle(context);
1645 secp256k1_bppp_generators* gens = NULL;
1646 size_t required = 0;
1647 int ok = 0;
1648
1649 if (ctx == NULL || out_len == NULL) {
1650 return 0;
1651 }
1652 if (!purify_bridge_checked_mul_size(count, 33u, &required)) {
1653 *out_len = 0;
1654 return 0;
1655 }
1656 if (*out_len < required || out == NULL) {
1657 *out_len = required;
1658 return 0;
1659 }
1660 gens = secp256k1_bppp_generators_create(ctx, count);
1661 if (gens == NULL) {
1662 return 0;
1663 }
1664 ok = secp256k1_bppp_generators_serialize(ctx, gens, out, out_len);
1665 secp256k1_bppp_generators_destroy(ctx, gens);
1666 return ok;
1667}
1668
1670 const unsigned char blind32[32], const unsigned char value32[32],
1671 const unsigned char value_gen33[33], const unsigned char blind_gen33[33],
1672 unsigned char commitment_out33[33]) {
1673 secp256k1_context* ctx = purify_context_handle(context);
1674 secp256k1_scalar blind_scalar, value_scalar;
1675 secp256k1_generator value_generator, blind_generator;
1676 secp256k1_ge value_ge, blind_ge, commit_ge;
1677 secp256k1_gej blind_part, value_part, total;
1678 int ok = 0;
1679
1680 if (ctx == NULL || blind32 == NULL || value32 == NULL || value_gen33 == NULL || blind_gen33 == NULL || commitment_out33 == NULL) {
1681 return 0;
1682 }
1683 if (!purify_parse_scalar(blind32, &blind_scalar, 0) || !purify_parse_scalar(value32, &value_scalar, 0)) {
1684 return 0;
1685 }
1686 if (!secp256k1_generator_parse(ctx, &value_generator, value_gen33) || !secp256k1_generator_parse(ctx, &blind_generator, blind_gen33)) {
1687 return 0;
1688 }
1689 secp256k1_generator_load(&value_ge, &value_generator);
1690 secp256k1_generator_load(&blind_ge, &blind_generator);
1691 secp256k1_ecmult_const(&blind_part, &blind_ge, &blind_scalar);
1692 secp256k1_ecmult_const(&value_part, &value_ge, &value_scalar);
1693 secp256k1_gej_add_var(&total, &blind_part, &value_part, NULL);
1694 if (!secp256k1_gej_is_infinity(&total)) {
1695 secp256k1_ge_set_gej(&commit_ge, &total);
1696 ok = purify_serialize_point(commitment_out33, &commit_ge);
1697 }
1698 return ok;
1699}
1700
1703 const unsigned char rho32[32], const unsigned char* generators33,
1704 size_t generators_count, const unsigned char* n_vec32, size_t n_vec_len,
1705 const unsigned char* l_vec32, size_t l_vec_len,
1706 const unsigned char* c_vec32, size_t c_vec_len,
1707 unsigned char commitment_out33[33]) {
1708 secp256k1_context* ctx = resources != NULL ? resources->ctx : purify_context_handle(context);
1709 secp256k1_scratch_space* scratch = resources != NULL ? resources->scratch : NULL;
1710 secp256k1_bppp_generators* gens = resources != NULL ? resources->gens : NULL;
1711 secp256k1_scalar rho, mu;
1712 secp256k1_scalar *n_vec = NULL, *l_vec = NULL, *c_vec = NULL;
1713 secp256k1_ge commit;
1714 size_t expected_generators = 0;
1715 size_t serialized_generators_len = 0;
1716 int ok = 0;
1717
1718 if (ctx == NULL || rho32 == NULL || (resources == NULL && generators33 == NULL) || n_vec32 == NULL || l_vec32 == NULL || c_vec32 == NULL ||
1719 commitment_out33 == NULL) {
1720 return 0;
1721 }
1722 if (n_vec_len == 0 || l_vec_len == 0 || c_vec_len == 0 || l_vec_len != c_vec_len) {
1723 return 0;
1724 }
1725 if (!secp256k1_is_power_of_two(n_vec_len) || !secp256k1_is_power_of_two(c_vec_len) ||
1726 !purify_bridge_checked_add_size(n_vec_len, l_vec_len, &expected_generators) ||
1727 generators_count != expected_generators ||
1728 !purify_bridge_checked_mul_size(generators_count, 33u, &serialized_generators_len)) {
1729 return 0;
1730 }
1731 if (resources != NULL && resources->generators_count != generators_count) {
1732 return 0;
1733 }
1734 if (!purify_parse_scalar(rho32, &rho, 1)) {
1735 return 0;
1736 }
1737 if (resources == NULL) {
1738 scratch = secp256k1_scratch_space_create(ctx, 1u << 24);
1739 gens = secp256k1_bppp_generators_parse(ctx, generators33, serialized_generators_len);
1740 }
1741 n_vec = (secp256k1_scalar*)purify_malloc_array(n_vec_len, sizeof(*n_vec));
1742 l_vec = (secp256k1_scalar*)purify_malloc_array(l_vec_len, sizeof(*l_vec));
1743 c_vec = (secp256k1_scalar*)purify_malloc_array(c_vec_len, sizeof(*c_vec));
1744 if (scratch == NULL || gens == NULL || n_vec == NULL || l_vec == NULL || c_vec == NULL) {
1745 goto cleanup;
1746 }
1747 if (!purify_parse_scalar_array(n_vec32, n_vec_len, n_vec) ||
1748 !purify_parse_scalar_array(l_vec32, l_vec_len, l_vec) ||
1749 !purify_parse_scalar_array(c_vec32, c_vec_len, c_vec)) {
1750 goto cleanup;
1751 }
1752 secp256k1_scalar_sqr(&mu, &rho);
1753 if (!secp256k1_bppp_commit(ctx, scratch, &commit, gens, n_vec, n_vec_len, l_vec, l_vec_len, c_vec, c_vec_len, &mu)) {
1754 goto cleanup;
1755 }
1756 ok = purify_serialize_point(commitment_out33, &commit);
1757
1758cleanup:
1759 if (n_vec != NULL) free(n_vec);
1760 if (l_vec != NULL) free(l_vec);
1761 if (c_vec != NULL) free(c_vec);
1762 if (resources == NULL) {
1763 if (gens != NULL) secp256k1_bppp_generators_destroy(ctx, gens);
1764 if (scratch != NULL) secp256k1_scratch_space_destroy(ctx, scratch);
1765 }
1766 return ok;
1767}
1768
1770 const unsigned char rho32[32], const unsigned char* generators33, size_t generators_count,
1771 const unsigned char* n_vec32, size_t n_vec_len, const unsigned char* l_vec32,
1772 size_t l_vec_len, const unsigned char* c_vec32, size_t c_vec_len,
1773 unsigned char commitment_out33[33]) {
1774 return purify_bppp_commit_norm_arg_impl(context, NULL, rho32, generators33, generators_count, n_vec32, n_vec_len,
1775 l_vec32, l_vec_len, c_vec32, c_vec_len, commitment_out33);
1776}
1777
1779 const unsigned char rho32[32],
1780 const unsigned char* n_vec32, size_t n_vec_len,
1781 const unsigned char* l_vec32, size_t l_vec_len,
1782 const unsigned char* c_vec32, size_t c_vec_len,
1783 unsigned char commitment_out33[33]) {
1784 return purify_bppp_commit_norm_arg_impl(NULL, resources, rho32, NULL,
1785 resources != NULL ? resources->generators_count : 0,
1786 n_vec32, n_vec_len, l_vec32, l_vec_len, c_vec32, c_vec_len,
1787 commitment_out33);
1788}
1789
1792 const unsigned char* generators33, size_t generators_count,
1793 const unsigned char* n_vec32, size_t n_vec_len,
1794 const unsigned char* l_vec32, size_t l_vec_len,
1795 unsigned char commitment_out33[33]) {
1796 secp256k1_context* ctx = resources != NULL ? resources->ctx : purify_context_handle(context);
1797 secp256k1_scratch_space* scratch = resources != NULL ? resources->scratch : NULL;
1798 secp256k1_bppp_generators* gens = resources != NULL ? resources->gens : NULL;
1799 secp256k1_scalar zero;
1800 secp256k1_scalar *n_vec = NULL, *l_vec = NULL;
1801 secp256k1_ge commit;
1802 secp256k1_gej commitj;
1803 ecmult_bp_commit_cb_data data;
1804 size_t expected_generators = 0;
1805 size_t serialized_generators_len = 0;
1806 int ok = 0;
1807
1808 if (ctx == NULL || (resources == NULL && generators33 == NULL) || n_vec32 == NULL || l_vec32 == NULL || commitment_out33 == NULL) {
1809 return 0;
1810 }
1811 if (n_vec_len == 0 || l_vec_len == 0 ||
1812 !purify_bridge_checked_add_size(n_vec_len, l_vec_len, &expected_generators) ||
1813 generators_count != expected_generators ||
1814 !purify_bridge_checked_mul_size(generators_count, 33u, &serialized_generators_len)) {
1815 return 0;
1816 }
1817 if (resources != NULL && resources->generators_count != generators_count) {
1818 return 0;
1819 }
1820
1821 if (resources == NULL) {
1822 scratch = secp256k1_scratch_space_create(ctx, 1u << 24);
1823 gens = secp256k1_bppp_generators_parse(ctx, generators33, serialized_generators_len);
1824 }
1825 n_vec = (secp256k1_scalar*)purify_malloc_array(n_vec_len, sizeof(*n_vec));
1826 l_vec = (secp256k1_scalar*)purify_malloc_array(l_vec_len, sizeof(*l_vec));
1827 if (scratch == NULL || gens == NULL || n_vec == NULL || l_vec == NULL) {
1828 goto cleanup;
1829 }
1830 if (!purify_parse_scalar_array(n_vec32, n_vec_len, n_vec) ||
1831 !purify_parse_scalar_array(l_vec32, l_vec_len, l_vec)) {
1832 goto cleanup;
1833 }
1834
1835 secp256k1_scalar_set_int(&zero, 0);
1836 data.g = gens->gens;
1837 data.n = n_vec;
1838 data.l = l_vec;
1839 data.g_len = n_vec_len;
1840 if (!secp256k1_ecmult_multi_var(&ctx->error_callback, scratch, &commitj, &zero, ecmult_bp_commit_cb,
1841 (void*)&data, expected_generators)) {
1842 goto cleanup;
1843 }
1844 if (secp256k1_gej_is_infinity(&commitj)) {
1845 goto cleanup;
1846 }
1847 secp256k1_ge_set_gej_var(&commit, &commitj);
1848 ok = purify_serialize_point(commitment_out33, &commit);
1849
1850cleanup:
1851 if (n_vec != NULL) free(n_vec);
1852 if (l_vec != NULL) free(l_vec);
1853 if (resources == NULL) {
1854 if (gens != NULL) secp256k1_bppp_generators_destroy(ctx, gens);
1855 if (scratch != NULL) secp256k1_scratch_space_destroy(ctx, scratch);
1856 }
1857 return ok;
1858}
1859
1861 const unsigned char* generators33, size_t generators_count,
1862 const unsigned char* n_vec32, size_t n_vec_len, const unsigned char* l_vec32,
1863 size_t l_vec_len, unsigned char commitment_out33[33]) {
1864 return purify_bppp_commit_witness_only_impl(context, NULL, generators33, generators_count,
1865 n_vec32, n_vec_len, l_vec32, l_vec_len, commitment_out33);
1866}
1867
1869 const unsigned char* n_vec32, size_t n_vec_len,
1870 const unsigned char* l_vec32, size_t l_vec_len,
1871 unsigned char commitment_out33[33]) {
1872 return purify_bppp_commit_witness_only_impl(NULL, resources, NULL,
1873 resources != NULL ? resources->generators_count : 0,
1874 n_vec32, n_vec_len, l_vec32, l_vec_len, commitment_out33);
1875}
1876
1878 const unsigned char commitment33[33], const unsigned char scalar32[32],
1879 unsigned char commitment_out33[33]) {
1880 secp256k1_context* ctx = purify_context_handle(context);
1881 secp256k1_scalar scalar;
1882 secp256k1_ge commitment_ge, result_ge;
1883 secp256k1_gej commitment_j, offset_j, result_j;
1884 int ok = 0;
1885
1886 if (ctx == NULL || commitment33 == NULL || scalar32 == NULL || commitment_out33 == NULL) {
1887 return 0;
1888 }
1889 if (!purify_parse_point_as_ge(commitment33, &commitment_ge) ||
1890 !purify_parse_scalar(scalar32, &scalar, 0)) {
1891 return 0;
1892 }
1893
1894 secp256k1_gej_set_ge(&commitment_j, &commitment_ge);
1895 secp256k1_ecmult_const(&offset_j, &secp256k1_ge_const_g, &scalar);
1896 secp256k1_gej_add_var(&result_j, &commitment_j, &offset_j, NULL);
1897 if (!secp256k1_gej_is_infinity(&result_j)) {
1898 secp256k1_ge_set_gej(&result_ge, &result_j);
1899 ok = purify_serialize_point(commitment_out33, &result_ge);
1900 }
1901 return ok;
1902}
1903
1905 const unsigned char point33[33], const unsigned char scalar32[32],
1906 unsigned char out33[33]) {
1907 secp256k1_context* ctx = purify_context_handle(context);
1908 secp256k1_scalar scalar;
1909 secp256k1_ge point_ge, result_ge;
1910 secp256k1_gej result_j;
1911 int ok = 0;
1912
1913 if (ctx == NULL || point33 == NULL || scalar32 == NULL || out33 == NULL) {
1914 return 0;
1915 }
1916 if (!purify_parse_point_as_ge(point33, &point_ge) ||
1917 !purify_parse_scalar(scalar32, &scalar, 0)) {
1918 return 0;
1919 }
1920
1921 secp256k1_ecmult_const(&result_j, &point_ge, &scalar);
1922 if (!secp256k1_gej_is_infinity(&result_j)) {
1923 secp256k1_ge_set_gej(&result_ge, &result_j);
1924 ok = purify_serialize_point(out33, &result_ge);
1925 }
1926 return ok;
1927}
1928
1930 const unsigned char lhs33[33], const unsigned char rhs33[33],
1931 unsigned char out33[33]) {
1932 secp256k1_context* ctx = purify_context_handle(context);
1933 secp256k1_ge lhs_ge, rhs_ge, result_ge;
1934 secp256k1_gej lhs_j, result_j;
1935 int ok = 0;
1936
1937 if (ctx == NULL || lhs33 == NULL || rhs33 == NULL || out33 == NULL) {
1938 return 0;
1939 }
1940 if (!purify_parse_point_as_ge(lhs33, &lhs_ge) ||
1941 !purify_parse_point_as_ge(rhs33, &rhs_ge)) {
1942 return 0;
1943 }
1944
1945 secp256k1_gej_set_ge(&lhs_j, &lhs_ge);
1946 secp256k1_gej_add_ge_var(&result_j, &lhs_j, &rhs_ge, NULL);
1947 if (!secp256k1_gej_is_infinity(&result_j)) {
1948 secp256k1_ge_set_gej(&result_ge, &result_j);
1949 ok = purify_serialize_point(out33, &result_ge);
1950 }
1951 return ok;
1952}
1953
1956 const unsigned char rho32[32], const unsigned char* generators33,
1957 size_t generators_count, const unsigned char* n_vec32, size_t n_vec_len,
1958 const unsigned char* l_vec32, size_t l_vec_len,
1959 const unsigned char* c_vec32, size_t c_vec_len,
1960 unsigned char commitment_out33[33], unsigned char* proof_out,
1961 size_t* proof_len) {
1962 secp256k1_context* ctx = resources != NULL ? resources->ctx : purify_context_handle(context);
1963 secp256k1_scratch_space* scratch = resources != NULL ? resources->scratch : NULL;
1964 secp256k1_bppp_generators* gens = NULL;
1965 secp256k1_scalar rho, mu;
1966 secp256k1_scalar *n_vec = NULL, *l_vec = NULL, *c_vec = NULL;
1967 secp256k1_ge commit;
1968 secp256k1_sha256 transcript;
1969 size_t required = purify_bppp_required_proof_size(n_vec_len, c_vec_len);
1970 size_t expected_generators = 0;
1971 size_t serialized_generators_len = 0;
1972 int ok = 0;
1973 purify_bppp_mutable_generators_guard gens_guard = {0};
1974
1975 if (ctx == NULL || rho32 == NULL || (resources == NULL && generators33 == NULL) || n_vec32 == NULL || l_vec32 == NULL || c_vec32 == NULL ||
1976 commitment_out33 == NULL || proof_out == NULL || proof_len == NULL) {
1977 return 0;
1978 }
1979 if (n_vec_len == 0 || l_vec_len == 0 || c_vec_len == 0 || l_vec_len != c_vec_len ||
1980 n_vec_len < c_vec_len) {
1981 return 0;
1982 }
1983 if (!secp256k1_is_power_of_two(n_vec_len) || !secp256k1_is_power_of_two(c_vec_len) ||
1984 !purify_bridge_checked_add_size(n_vec_len, l_vec_len, &expected_generators) ||
1985 generators_count != expected_generators ||
1986 !purify_bridge_checked_mul_size(generators_count, 33u, &serialized_generators_len)) {
1987 return 0;
1988 }
1989 if (resources != NULL && resources->generators_count != generators_count) {
1990 return 0;
1991 }
1992 if (*proof_len < required) {
1993 *proof_len = required;
1994 return 0;
1995 }
1996 if (!purify_parse_scalar(rho32, &rho, 1)) {
1997 return 0;
1998 }
1999 if (resources != NULL) {
2000 gens = purify_bppp_backend_resources_acquire_scratch_gens(resources, &gens_guard);
2001 if (gens == NULL) {
2002 return 0;
2003 }
2004 } else {
2005 scratch = secp256k1_scratch_space_create(ctx, 1u << 24);
2006 gens = secp256k1_bppp_generators_parse(ctx, generators33, serialized_generators_len);
2007 }
2008 n_vec = (secp256k1_scalar*)purify_malloc_array(n_vec_len, sizeof(*n_vec));
2009 l_vec = (secp256k1_scalar*)purify_malloc_array(l_vec_len, sizeof(*l_vec));
2010 c_vec = (secp256k1_scalar*)purify_malloc_array(c_vec_len, sizeof(*c_vec));
2011 if (scratch == NULL || gens == NULL || n_vec == NULL || l_vec == NULL || c_vec == NULL) {
2012 goto cleanup;
2013 }
2014 if (!purify_parse_scalar_array(n_vec32, n_vec_len, n_vec) ||
2015 !purify_parse_scalar_array(l_vec32, l_vec_len, l_vec) ||
2016 !purify_parse_scalar_array(c_vec32, c_vec_len, c_vec)) {
2017 goto cleanup;
2018 }
2019 secp256k1_scalar_sqr(&mu, &rho);
2020 if (!secp256k1_bppp_commit(ctx, scratch, &commit, gens, n_vec, n_vec_len, l_vec, l_vec_len, c_vec, c_vec_len, &mu)) {
2021 goto cleanup;
2022 }
2023 if (!purify_serialize_point(commitment_out33, &commit)) {
2024 goto cleanup;
2025 }
2026 if (!purify_norm_arg_commit_initial_data(&transcript, &rho, gens, n_vec_len, c_vec, c_vec_len, &commit)) {
2027 goto cleanup;
2028 }
2029 ok = purify_bppp_rangeproof_norm_product_prove_const(ctx, scratch, proof_out, proof_len, &transcript, &rho,
2030 gens->gens, gens->n, n_vec, n_vec_len, l_vec, l_vec_len, c_vec, c_vec_len);
2031
2032cleanup:
2033 if (n_vec != NULL) free(n_vec);
2034 if (l_vec != NULL) free(l_vec);
2035 if (c_vec != NULL) free(c_vec);
2037 if (resources == NULL) {
2038 if (gens != NULL) secp256k1_bppp_generators_destroy(ctx, gens);
2039 if (scratch != NULL) secp256k1_scratch_space_destroy(ctx, scratch);
2040 }
2041 return ok;
2042}
2043
2045 const unsigned char rho32[32], const unsigned char* generators33, size_t generators_count,
2046 const unsigned char* n_vec32, size_t n_vec_len, const unsigned char* l_vec32,
2047 size_t l_vec_len, const unsigned char* c_vec32, size_t c_vec_len,
2048 unsigned char commitment_out33[33], unsigned char* proof_out, size_t* proof_len) {
2049 return purify_bppp_prove_norm_arg_impl(context, NULL, rho32, generators33, generators_count, n_vec32, n_vec_len,
2050 l_vec32, l_vec_len, c_vec32, c_vec_len, commitment_out33,
2051 proof_out, proof_len);
2052}
2053
2055 const unsigned char rho32[32],
2056 const unsigned char* n_vec32, size_t n_vec_len,
2057 const unsigned char* l_vec32, size_t l_vec_len,
2058 const unsigned char* c_vec32, size_t c_vec_len,
2059 unsigned char commitment_out33[33], unsigned char* proof_out,
2060 size_t* proof_len) {
2061 return purify_bppp_prove_norm_arg_impl(NULL, resources, rho32, NULL,
2062 resources != NULL ? resources->generators_count : 0,
2063 n_vec32, n_vec_len, l_vec32, l_vec_len, c_vec32, c_vec_len,
2064 commitment_out33, proof_out, proof_len);
2065}
2066
2069 const unsigned char rho32[32], const unsigned char* generators33,
2070 size_t generators_count, const unsigned char* n_vec32,
2071 size_t n_vec_len, const unsigned char* l_vec32,
2072 size_t l_vec_len, const unsigned char* c_vec32,
2073 size_t c_vec_len, const unsigned char commitment33[33],
2074 unsigned char* proof_out, size_t* proof_len) {
2075 secp256k1_context* ctx = resources != NULL ? resources->ctx : purify_context_handle(context);
2076 secp256k1_scratch_space* scratch = resources != NULL ? resources->scratch : NULL;
2077 secp256k1_bppp_generators* gens = NULL;
2078 secp256k1_scalar rho, mu;
2079 secp256k1_scalar *n_vec = NULL, *l_vec = NULL, *c_vec = NULL;
2080 secp256k1_ge expected_commit, commitment_ge;
2081 secp256k1_sha256 transcript;
2082 unsigned char expected_commitment33[33];
2083 size_t required = purify_bppp_required_proof_size(n_vec_len, c_vec_len);
2084 size_t expected_generators = 0;
2085 size_t serialized_generators_len = 0;
2086 int ok = 0;
2087 purify_bppp_mutable_generators_guard gens_guard = {0};
2088
2089 memset(expected_commitment33, 0, sizeof(expected_commitment33));
2090 if (ctx == NULL || rho32 == NULL || (resources == NULL && generators33 == NULL) || n_vec32 == NULL || l_vec32 == NULL || c_vec32 == NULL ||
2091 commitment33 == NULL || proof_out == NULL || proof_len == NULL) {
2092 return 0;
2093 }
2094 if (n_vec_len == 0 || l_vec_len == 0 || c_vec_len == 0 || l_vec_len != c_vec_len ||
2095 n_vec_len < c_vec_len) {
2096 return 0;
2097 }
2098 if (!secp256k1_is_power_of_two(n_vec_len) || !secp256k1_is_power_of_two(c_vec_len) ||
2099 !purify_bridge_checked_add_size(n_vec_len, l_vec_len, &expected_generators) ||
2100 generators_count != expected_generators ||
2101 !purify_bridge_checked_mul_size(generators_count, 33u, &serialized_generators_len)) {
2102 return 0;
2103 }
2104 if (resources != NULL && resources->generators_count != generators_count) {
2105 return 0;
2106 }
2107 if (*proof_len < required) {
2108 *proof_len = required;
2109 return 0;
2110 }
2111 if (!purify_parse_scalar(rho32, &rho, 1) || !purify_parse_point_as_ge(commitment33, &commitment_ge)) {
2112 return 0;
2113 }
2114 if (resources != NULL) {
2115 gens = purify_bppp_backend_resources_acquire_scratch_gens(resources, &gens_guard);
2116 if (gens == NULL) {
2117 return 0;
2118 }
2119 } else {
2120 scratch = secp256k1_scratch_space_create(ctx, 1u << 24);
2121 gens = secp256k1_bppp_generators_parse(ctx, generators33, serialized_generators_len);
2122 }
2123 n_vec = (secp256k1_scalar*)purify_malloc_array(n_vec_len, sizeof(*n_vec));
2124 l_vec = (secp256k1_scalar*)purify_malloc_array(l_vec_len, sizeof(*l_vec));
2125 c_vec = (secp256k1_scalar*)purify_malloc_array(c_vec_len, sizeof(*c_vec));
2126 if (scratch == NULL || gens == NULL || n_vec == NULL || l_vec == NULL || c_vec == NULL) {
2127 goto cleanup;
2128 }
2129 if (!purify_parse_scalar_array(n_vec32, n_vec_len, n_vec) ||
2130 !purify_parse_scalar_array(l_vec32, l_vec_len, l_vec) ||
2131 !purify_parse_scalar_array(c_vec32, c_vec_len, c_vec)) {
2132 goto cleanup;
2133 }
2134 secp256k1_scalar_sqr(&mu, &rho);
2135 if (!secp256k1_bppp_commit(ctx, scratch, &expected_commit, gens, n_vec, n_vec_len, l_vec, l_vec_len, c_vec, c_vec_len, &mu)) {
2136 goto cleanup;
2137 }
2138 if (!purify_serialize_point(expected_commitment33, &expected_commit)) {
2139 goto cleanup;
2140 }
2141 if (secp256k1_memcmp_var(expected_commitment33, commitment33, sizeof(expected_commitment33)) != 0) {
2142 goto cleanup;
2143 }
2144 if (!purify_norm_arg_commit_initial_data(&transcript, &rho, gens, n_vec_len, c_vec, c_vec_len, &commitment_ge)) {
2145 goto cleanup;
2146 }
2147 ok = purify_bppp_rangeproof_norm_product_prove_const(ctx, scratch, proof_out, proof_len, &transcript, &rho,
2148 gens->gens, gens->n, n_vec, n_vec_len, l_vec, l_vec_len, c_vec, c_vec_len);
2149
2150cleanup:
2151 if (n_vec != NULL) free(n_vec);
2152 if (l_vec != NULL) free(l_vec);
2153 if (c_vec != NULL) free(c_vec);
2155 if (resources == NULL) {
2156 if (gens != NULL) secp256k1_bppp_generators_destroy(ctx, gens);
2157 if (scratch != NULL) secp256k1_scratch_space_destroy(ctx, scratch);
2158 }
2159 return ok;
2160}
2161
2163 const unsigned char rho32[32], const unsigned char* generators33, size_t generators_count,
2164 const unsigned char* n_vec32, size_t n_vec_len, const unsigned char* l_vec32,
2165 size_t l_vec_len, const unsigned char* c_vec32, size_t c_vec_len,
2166 const unsigned char commitment33[33], unsigned char* proof_out, size_t* proof_len) {
2167 return purify_bppp_prove_norm_arg_to_commitment_impl(context, NULL, rho32, generators33, generators_count,
2168 n_vec32, n_vec_len, l_vec32, l_vec_len, c_vec32, c_vec_len,
2169 commitment33, proof_out, proof_len);
2170}
2171
2173 const unsigned char rho32[32],
2174 const unsigned char* n_vec32, size_t n_vec_len,
2175 const unsigned char* l_vec32, size_t l_vec_len,
2176 const unsigned char* c_vec32, size_t c_vec_len,
2177 const unsigned char commitment33[33],
2178 unsigned char* proof_out, size_t* proof_len) {
2179 return purify_bppp_prove_norm_arg_to_commitment_impl(NULL, resources, rho32, NULL,
2180 resources != NULL ? resources->generators_count : 0,
2181 n_vec32, n_vec_len, l_vec32, l_vec_len, c_vec32, c_vec_len,
2182 commitment33, proof_out, proof_len);
2183}
2184
2187 const unsigned char rho32[32], const unsigned char* generators33,
2188 size_t generators_count, const unsigned char* c_vec32, size_t c_vec_len,
2189 size_t n_vec_len, const unsigned char commitment33[33],
2190 const unsigned char* proof, size_t proof_len) {
2191 secp256k1_context* ctx = resources != NULL ? resources->ctx : purify_context_handle(context);
2192 secp256k1_scratch_space* scratch = resources != NULL ? resources->scratch : NULL;
2193 secp256k1_bppp_generators* gens = resources != NULL ? resources->gens : NULL;
2194 secp256k1_scalar rho, *c_vec = NULL;
2195 secp256k1_ge commit;
2196 secp256k1_sha256 transcript;
2197 size_t expected_generators = 0;
2198 size_t serialized_generators_len = 0;
2199 int ok = 0;
2200
2201 if (ctx == NULL || rho32 == NULL || (resources == NULL && generators33 == NULL) || c_vec32 == NULL || commitment33 == NULL || proof == NULL) {
2202 return 0;
2203 }
2204 /* Four-fork norm extraction requires a paired norm coordinate in every
2205 * nonterminal round. A linear-only tail does not preserve that relation. */
2206 if (n_vec_len == 0 || c_vec_len == 0 || n_vec_len < c_vec_len ||
2207 !purify_bridge_checked_add_size(n_vec_len, c_vec_len, &expected_generators) ||
2208 generators_count != expected_generators ||
2209 !purify_bridge_checked_mul_size(generators_count, 33u, &serialized_generators_len)) {
2210 return 0;
2211 }
2212 if (!secp256k1_is_power_of_two(n_vec_len) || !secp256k1_is_power_of_two(c_vec_len)) {
2213 return 0;
2214 }
2215 if (resources != NULL && resources->generators_count != generators_count) {
2216 return 0;
2217 }
2218 if (!purify_parse_scalar(rho32, &rho, 1) || !secp256k1_ge_parse_ext(&commit, commitment33)) {
2219 return 0;
2220 }
2221 if (resources == NULL) {
2222 scratch = secp256k1_scratch_space_create(ctx, 1u << 24);
2223 gens = secp256k1_bppp_generators_parse(ctx, generators33, serialized_generators_len);
2224 }
2225 c_vec = (secp256k1_scalar*)purify_malloc_array(c_vec_len, sizeof(*c_vec));
2226 if (scratch == NULL || gens == NULL || c_vec == NULL) {
2227 goto cleanup;
2228 }
2229 if (!purify_parse_scalar_array(c_vec32, c_vec_len, c_vec)) {
2230 goto cleanup;
2231 }
2232 if (!purify_norm_arg_commit_initial_data(&transcript, &rho, gens, n_vec_len, c_vec, c_vec_len, &commit)) {
2233 goto cleanup;
2234 }
2235 ok = secp256k1_bppp_rangeproof_norm_product_verify(ctx, scratch, proof, proof_len, &transcript, &rho,
2236 gens, n_vec_len, c_vec, c_vec_len, &commit);
2237
2238cleanup:
2239 if (c_vec != NULL) free(c_vec);
2240 if (resources == NULL) {
2241 if (gens != NULL) secp256k1_bppp_generators_destroy(ctx, gens);
2242 if (scratch != NULL) secp256k1_scratch_space_destroy(ctx, scratch);
2243 }
2244 return ok;
2245}
2246
2248 const unsigned char rho32[32], const unsigned char* generators33, size_t generators_count,
2249 const unsigned char* c_vec32, size_t c_vec_len, size_t n_vec_len,
2250 const unsigned char commitment33[33], const unsigned char* proof, size_t proof_len) {
2251 return purify_bppp_verify_norm_arg_impl(context, NULL, rho32, generators33, generators_count,
2252 c_vec32, c_vec_len, n_vec_len, commitment33, proof, proof_len);
2253}
2254
2256 const unsigned char rho32[32],
2257 const unsigned char* c_vec32, size_t c_vec_len, size_t n_vec_len,
2258 const unsigned char commitment33[33], const unsigned char* proof,
2259 size_t proof_len) {
2260 return purify_bppp_verify_norm_arg_impl(NULL, resources, rho32, NULL,
2261 resources != NULL ? resources->generators_count : 0,
2262 c_vec32, c_vec_len, n_vec_len, commitment33, proof, proof_len);
2263}
static secp256k1_scalar * purify_scalar_cast(purify_scalar *scalar)
static int purify_bulletproof_add_scratch_frame(size_t *total, size_t bytes, size_t objects)
static void purify_bulletproof_circuit_evaluate_sum_row(secp256k1_scalar *acc, const secp256k1_bulletproof_wmatrix_row *row, const secp256k1_scalar *assn)
void purify_scalar_mul(purify_scalar *out, const purify_scalar *lhs, const purify_scalar *rhs)
Multiplies two scalars modulo the backend field.
int purify_bppp_prove_norm_arg_to_commitment_with_resources(purify_bppp_backend_resources *resources, const unsigned char rho32[32], const unsigned char *n_vec32, size_t n_vec_len, const unsigned char *l_vec32, size_t l_vec_len, const unsigned char *c_vec32, size_t c_vec_len, const unsigned char commitment33[33], unsigned char *proof_out, size_t *proof_len)
static int purify_parse_scalar_array(const unsigned char *input32, size_t count, secp256k1_scalar *out)
static int purify_bppp_backend_resources_reset_scratch_gens(purify_bppp_backend_resources *resources)
static int purify_bppp_rangeproof_norm_product_prove_const(const secp256k1_context *ctx, secp256k1_scratch_space *scratch, unsigned char *proof, size_t *proof_len, secp256k1_sha256 *transcript, const secp256k1_scalar *rho, const secp256k1_ge *g_vec, size_t g_vec_len, const secp256k1_scalar *n_vec, size_t n_vec_len, const secp256k1_scalar *l_vec, size_t l_vec_len, const secp256k1_scalar *c_vec, size_t c_vec_len)
static int purify_build_row_family(secp256k1_bulletproof_wmatrix_row *out_rows, size_t row_count, const purify_bulletproof_row_view *in_rows, size_t n_constraints, secp256k1_bulletproof_wmatrix_entry *entries, size_t *offset)
int purify_bulletproof_prove_circuit(purify_secp_context *context, const purify_bulletproof_circuit_view *circuit, const purify_bulletproof_assignment_view *assignment, const unsigned char *blind32, const unsigned char value_gen33[33], const unsigned char nonce32[32], const unsigned char *extra_commit, size_t extra_commit_len, unsigned char commitment_out33[33], unsigned char *proof_out, size_t *proof_len)
static secp256k1_bppp_generators * purify_bppp_backend_resources_acquire_scratch_gens(purify_bppp_backend_resources *resources, purify_bppp_mutable_generators_guard *guard)
static int purify_parse_scalar(const unsigned char input32[32], secp256k1_scalar *scalar, int reject_zero)
static int purify_build_bulletproof_assignment(const purify_bulletproof_assignment_view *view, secp256k1_bulletproof_circuit_assignment *out)
int purify_bppp_commit_witness_only_with_resources(purify_bppp_backend_resources *resources, const unsigned char *n_vec32, size_t n_vec_len, const unsigned char *l_vec32, size_t l_vec_len, unsigned char commitment_out33[33])
int purify_bulletproof_prove_circuit_with_resources(purify_bulletproof_backend_resources *resources, const purify_bulletproof_circuit_view *circuit, const purify_bulletproof_assignment_view *assignment, const unsigned char *blind32, const unsigned char value_gen33[33], const unsigned char nonce32[32], const unsigned char *extra_commit, size_t extra_commit_len, unsigned char commitment_out33[33], unsigned char *proof_out, size_t *proof_len)
int purify_scalar_is_zero(const purify_scalar *value)
Returns nonzero when the scalar is zero.
int purify_bip340_xonly_from_point(purify_secp_context *context, const unsigned char point33[33], unsigned char xonly32[32], int *parity_out)
Converts a compressed secp256k1 point into its x-only public key encoding.
int purify_bulletproof_verify_circuit(purify_secp_context *context, const purify_bulletproof_circuit_view *circuit, const unsigned char commitment33[33], const unsigned char value_gen33[33], const unsigned char *extra_commit, size_t extra_commit_len, const unsigned char *proof, size_t proof_len)
int purify_bulletproof_prove_circuit_assume_valid(purify_secp_context *context, const purify_bulletproof_circuit_view *circuit, const purify_bulletproof_assignment_view *assignment, const unsigned char *blind32, const unsigned char value_gen33[33], const unsigned char nonce32[32], const unsigned char *extra_commit, size_t extra_commit_len, unsigned char commitment_out33[33], unsigned char *proof_out, size_t *proof_len)
int purify_bip340_key_from_seckey(purify_secp_context *context, unsigned char seckey32[32], unsigned char xonly_pubkey32[32])
Canonicalizes a valid secp256k1 secret key for BIP340 and derives its x-only public key.
int purify_scalar_add(purify_scalar *out, const purify_scalar *lhs, const purify_scalar *rhs)
Adds two scalars modulo the backend field.
int purify_bip340_nonce_from_scalar(purify_secp_context *context, unsigned char scalar32[32], unsigned char xonly_nonce32[32])
Canonicalizes a valid secp256k1 nonce scalar for BIP340 and derives its x-only public nonce.
int purify_point_scale(purify_secp_context *context, const unsigned char point33[33], const unsigned char scalar32[32], unsigned char out33[33])
void purify_scalar_set_int(purify_scalar *out, unsigned int value)
Initializes a scalar from an unsigned integer.
void purify_scalar_cmov(purify_scalar *dst, const purify_scalar *src, int flag)
Conditionally assigns src into dst when flag is nonzero.
static int purify_copy_vectors_into_scratch(const secp256k1_context *ctx, secp256k1_scratch_space *scratch, secp256k1_scalar **ns, secp256k1_scalar **ls, secp256k1_scalar **cs, secp256k1_ge **gs, const secp256k1_scalar *n_vec, const secp256k1_scalar *l_vec, const secp256k1_scalar *c_vec, const secp256k1_ge *gens_vec, size_t g_len, size_t h_len)
static int purify_bridge_checked_mul_size(size_t lhs, size_t rhs, size_t *out)
static int purify_bulletproof_circuit_scratch_size(const secp256k1_bulletproof_circuit *circuit, size_t *out)
int purify_bulletproof_verify_circuit_with_resources(purify_bulletproof_backend_resources *resources, const purify_bulletproof_circuit_view *circuit, const unsigned char commitment33[33], const unsigned char value_gen33[33], const unsigned char *extra_commit, size_t extra_commit_len, const unsigned char *proof, size_t proof_len)
static int purify_parse_point_as_ge(const unsigned char point33[33], secp256k1_ge *out)
int purify_bip340_sign_with_fixed_nonce(purify_secp_context *context, unsigned char sig64[64], const unsigned char *msg, size_t msglen, const unsigned char seckey32[32], const unsigned char nonce32[32])
Signs a message with a caller-supplied BIP340 nonce scalar.
static int purify_bulletproof_verify_circuit_impl(purify_secp_context *context, purify_bulletproof_backend_resources *resources, const purify_bulletproof_circuit_view *circuit, const unsigned char commitment33[33], const unsigned char value_gen33[33], const unsigned char *extra_commit, size_t extra_commit_len, const unsigned char *proof, size_t proof_len)
static int purify_bulletproof_reserve_circuit_scratch(purify_bulletproof_backend_resources *resources, const secp256k1_bulletproof_circuit *circuit)
int purify_scalar_is_even(const purify_scalar *value)
Returns nonzero when the scalar is even.
static void purify_free_bulletproof_circuit(secp256k1_bulletproof_circuit *circuit)
static int purify_bppp_commit_norm_arg_impl(purify_secp_context *context, purify_bppp_backend_resources *resources, const unsigned char rho32[32], const unsigned char *generators33, size_t generators_count, const unsigned char *n_vec32, size_t n_vec_len, const unsigned char *l_vec32, size_t l_vec_len, const unsigned char *c_vec32, size_t c_vec_len, unsigned char commitment_out33[33])
static int purify_fixed_nonce_function(unsigned char *nonce32, const unsigned char *msg, size_t msglen, const unsigned char *key32, const unsigned char *xonly_pk32, const unsigned char *algo, size_t algolen, void *data)
int purify_sha256_many(unsigned char output32[32], const unsigned char *const *items, const size_t *item_lens, size_t items_count)
Computes SHA-256 over a set of byte strings.
int purify_bppp_prove_norm_arg(purify_secp_context *context, const unsigned char rho32[32], const unsigned char *generators33, size_t generators_count, const unsigned char *n_vec32, size_t n_vec_len, const unsigned char *l_vec32, size_t l_vec_len, const unsigned char *c_vec32, size_t c_vec_len, unsigned char commitment_out33[33], unsigned char *proof_out, size_t *proof_len)
Produces a standalone BPPP norm argument.
static int purify_bppp_prove_norm_arg_to_commitment_impl(purify_secp_context *context, purify_bppp_backend_resources *resources, const unsigned char rho32[32], const unsigned char *generators33, size_t generators_count, const unsigned char *n_vec32, size_t n_vec_len, const unsigned char *l_vec32, size_t l_vec_len, const unsigned char *c_vec32, size_t c_vec_len, const unsigned char commitment33[33], unsigned char *proof_out, size_t *proof_len)
int purify_bppp_commit_witness_only(purify_secp_context *context, const unsigned char *generators33, size_t generators_count, const unsigned char *n_vec32, size_t n_vec_len, const unsigned char *l_vec32, size_t l_vec_len, unsigned char commitment_out33[33])
int purify_bppp_commit_norm_arg(purify_secp_context *context, const unsigned char rho32[32], const unsigned char *generators33, size_t generators_count, const unsigned char *n_vec32, size_t n_vec_len, const unsigned char *l_vec32, size_t l_vec_len, const unsigned char *c_vec32, size_t c_vec_len, unsigned char commitment_out33[33])
static void purify_free_bulletproof_assignment(secp256k1_bulletproof_circuit_assignment *assignment)
void purify_sha256(unsigned char output32[32], const unsigned char *data, size_t data_len)
Computes SHA-256 over a byte string.
static int purify_bppp_verify_norm_arg_impl(purify_secp_context *context, purify_bppp_backend_resources *resources, const unsigned char rho32[32], const unsigned char *generators33, size_t generators_count, const unsigned char *c_vec32, size_t c_vec_len, size_t n_vec_len, const unsigned char commitment33[33], const unsigned char *proof, size_t proof_len)
int purify_scalar_is_one(const purify_scalar *value)
Returns nonzero when the scalar is one.
static size_t purify_total_row_entries(const purify_bulletproof_row_view *rows, size_t count, int *ok)
int purify_bip340_validate_signature(purify_secp_context *context, const unsigned char sig64[64])
Returns nonzero when the 64-byte BIP340 signature has a syntactically valid encoding.
void purify_secp_context_destroy(purify_secp_context *context)
Destroys a context returned by purify_secp_context_create.
Definition bppp_bridge.c:69
void purify_bppp_backend_resources_destroy(purify_bppp_backend_resources *resources)
void purify_scalar_inverse(purify_scalar *out, const purify_scalar *value)
Computes the multiplicative inverse of a scalar in constant time.
int purify_bppp_value_generator_h(purify_secp_context *context, unsigned char out33[33])
Serializes the alternate value generator used by Pedersen commitments.
int purify_bppp_create_generators(purify_secp_context *context, size_t count, unsigned char *out, size_t *out_len)
Expands the generator list required by the BPPP prover and verifier.
purify_bppp_backend_resources * purify_bppp_backend_resources_clone(const purify_bppp_backend_resources *resources)
static const secp256k1_scalar * purify_scalar_cast_const(const purify_scalar *scalar)
static int purify_bulletproof_circuit_evaluate(const secp256k1_bulletproof_circuit *circuit, const secp256k1_bulletproof_circuit_assignment *assignment)
int purify_point_add(purify_secp_context *context, const unsigned char lhs33[33], const unsigned char rhs33[33], unsigned char out33[33])
int purify_bppp_verify_norm_arg(purify_secp_context *context, const unsigned char rho32[32], const unsigned char *generators33, size_t generators_count, const unsigned char *c_vec32, size_t c_vec_len, size_t n_vec_len, const unsigned char commitment33[33], const unsigned char *proof, size_t proof_len)
Verifies a standalone BPPP norm argument.
static const size_t purify_bulletproof_msm_scratch_size
int purify_pedersen_commit_char(purify_secp_context *context, const unsigned char blind32[32], const unsigned char value32[32], const unsigned char value_gen33[33], const unsigned char blind_gen33[33], unsigned char commitment_out33[33])
Computes a Pedersen commitment to an arbitrary 32-byte scalar value.
void purify_scalar_inverse_var(purify_scalar *out, const purify_scalar *value)
Computes the multiplicative inverse of a scalar.
int purify_bip340_validate_xonly_pubkey(purify_secp_context *context, const unsigned char xonly_pubkey32[32])
Returns nonzero when the x-only public key encoding parses successfully.
static secp256k1_context * purify_context_handle(const purify_secp_context *context)
Definition bppp_bridge.c:42
static void purify_bridge_secure_clear(void *data, size_t size)
Definition bppp_bridge.c:79
int purify_bppp_prove_norm_arg_with_resources(purify_bppp_backend_resources *resources, const unsigned char rho32[32], const unsigned char *n_vec32, size_t n_vec_len, const unsigned char *l_vec32, size_t l_vec_len, const unsigned char *c_vec32, size_t c_vec_len, unsigned char commitment_out33[33], unsigned char *proof_out, size_t *proof_len)
int purify_scalar_eq(const purify_scalar *lhs, const purify_scalar *rhs)
Returns nonzero when two scalars are equal.
int purify_bulletproof_prove_circuit_assume_valid_with_resources(purify_bulletproof_backend_resources *resources, const purify_bulletproof_circuit_view *circuit, const purify_bulletproof_assignment_view *assignment, const unsigned char *blind32, const unsigned char value_gen33[33], const unsigned char nonce32[32], const unsigned char *extra_commit, size_t extra_commit_len, unsigned char commitment_out33[33], unsigned char *proof_out, size_t *proof_len)
static int purify_parse_generator_as_ge(const secp256k1_context *ctx, const unsigned char generator33[33], secp256k1_ge *out)
int purify_bppp_offset_commitment(purify_secp_context *context, const unsigned char commitment33[33], const unsigned char scalar32[32], unsigned char commitment_out33[33])
static int purify_parse_fast_scalar(const unsigned char input32[32], secp256k1_fast_scalar *scalar)
void purify_scalar_negate(purify_scalar *out, const purify_scalar *value)
Computes the additive inverse of a scalar.
void purify_bulletproof_backend_resources_destroy(purify_bulletproof_backend_resources *resources)
purify_secp_context * purify_secp_context_create(void)
Creates one reusable secp256k1 context for the Purify bridge and public APIs.
Definition bppp_bridge.c:46
int purify_bppp_prove_norm_arg_to_commitment(purify_secp_context *context, const unsigned char rho32[32], const unsigned char *generators33, size_t generators_count, const unsigned char *n_vec32, size_t n_vec_len, const unsigned char *l_vec32, size_t l_vec_len, const unsigned char *c_vec32, size_t c_vec_len, const unsigned char commitment33[33], unsigned char *proof_out, size_t *proof_len)
purify_bulletproof_backend_resources * purify_bulletproof_backend_resources_create(purify_secp_context *context, size_t n_gates)
static int purify_norm_arg_commit_initial_data(secp256k1_sha256 *transcript, const secp256k1_scalar *rho, const secp256k1_bppp_generators *gens_vec, size_t g_len, const secp256k1_scalar *c_vec, size_t c_vec_len, const secp256k1_ge *commit)
purify_bulletproof_backend_resources * purify_bulletproof_backend_resources_clone(const purify_bulletproof_backend_resources *resources)
int purify_bppp_base_generator(purify_secp_context *context, unsigned char out33[33])
Serializes the secp256k1 base generator into compressed form.
int purify_bppp_commit_norm_arg_with_resources(purify_bppp_backend_resources *resources, const unsigned char rho32[32], const unsigned char *n_vec32, size_t n_vec_len, const unsigned char *l_vec32, size_t l_vec_len, const unsigned char *c_vec32, size_t c_vec_len, unsigned char commitment_out33[33])
size_t purify_bppp_required_proof_size(size_t n_vec_len, size_t c_vec_len)
Computes the maximum serialized size of a BPPP norm proof.
static secp256k1_bppp_generators * purify_bppp_generators_clone(const secp256k1_bppp_generators *generators)
static int purify_bppp_prove_norm_arg_impl(purify_secp_context *context, purify_bppp_backend_resources *resources, const unsigned char rho32[32], const unsigned char *generators33, size_t generators_count, const unsigned char *n_vec32, size_t n_vec_len, const unsigned char *l_vec32, size_t l_vec_len, const unsigned char *c_vec32, size_t c_vec_len, unsigned char commitment_out33[33], unsigned char *proof_out, size_t *proof_len)
static void * purify_malloc_array(size_t count, size_t elem_size)
void purify_scalar_set_u64(purify_scalar *out, uint64_t value)
Initializes a scalar from a 64-bit unsigned integer.
void purify_hmac_sha256(unsigned char output32[32], const unsigned char *key, size_t key_len, const unsigned char *data, size_t data_len)
Computes HMAC-SHA256 over a byte string.
static void purify_bppp_backend_resources_release_scratch_gens(purify_bppp_mutable_generators_guard *guard)
void purify_scalar_get_b32(unsigned char output32[32], const purify_scalar *value)
Serializes a scalar as 32 big-endian bytes.
size_t purify_bulletproof_required_proof_size(size_t n_gates)
purify_bppp_backend_resources * purify_bppp_backend_resources_create(purify_secp_context *context, const unsigned char *generators33, size_t generators_count)
static int purify_bulletproof_prove_circuit_impl(const purify_bulletproof_circuit_view *circuit, const purify_bulletproof_assignment_view *assignment, const unsigned char *blind32, const unsigned char value_gen33[33], const unsigned char nonce32[32], const unsigned char *extra_commit, size_t extra_commit_len, unsigned char commitment_out33[33], unsigned char *proof_out, size_t *proof_len, int require_valid_assignment, purify_secp_context *context, purify_bulletproof_backend_resources *resources)
static int purify_bridge_checked_add_size(size_t lhs, size_t rhs, size_t *out)
int purify_bip340_verify(purify_secp_context *context, const unsigned char sig64[64], const unsigned char *msg, size_t msglen, const unsigned char xonly_pubkey32[32])
Verifies a BIP340 signature against a serialized x-only public key.
static void * purify_calloc_array(size_t count, size_t elem_size)
static int purify_serialize_point(unsigned char out33[33], secp256k1_ge *point)
int purify_bppp_verify_norm_arg_with_resources(purify_bppp_backend_resources *resources, const unsigned char rho32[32], const unsigned char *c_vec32, size_t c_vec_len, size_t n_vec_len, const unsigned char commitment33[33], const unsigned char *proof, size_t proof_len)
void purify_scalar_set_b32(purify_scalar *out, const unsigned char input32[32], int *overflow)
Parses a big-endian 32-byte scalar.
static int purify_build_bulletproof_circuit(const purify_bulletproof_circuit_view *view, secp256k1_bulletproof_circuit *out)
static int purify_bppp_commit_witness_only_impl(purify_secp_context *context, purify_bppp_backend_resources *resources, const unsigned char *generators33, size_t generators_count, const unsigned char *n_vec32, size_t n_vec_len, const unsigned char *l_vec32, size_t l_vec_len, unsigned char commitment_out33[33])
C ABI bridging Purify C++ code to secp256k1-zkp BPPP functionality.
static void secp256k1_fast_scalar_mul(secp256k1_scalar *r, const secp256k1_fast_scalar *a, const secp256k1_scalar *b)
static int secp256k1_bulletproof_relation66_verify_impl(const secp256k1_ecmult_context *ecmult_ctx, secp256k1_scratch *scratch, const unsigned char *const *proof, size_t n_proofs, size_t plen, const secp256k1_ge *const *commitp, size_t *nc, const secp256k1_ge *value_gen, const secp256k1_bulletproof_circuit *const *circ, const secp256k1_bulletproof_generators *gens, const unsigned char **extra_commit, size_t *extra_commit_len)
static int secp256k1_bulletproof_relation66_prove_impl(const secp256k1_ecmult_context *ecmult_ctx, secp256k1_scratch *scratch, unsigned char *proof, size_t *plen, const secp256k1_bulletproof_circuit_assignment *assn, const secp256k1_ge *commitp, const secp256k1_scalar *blinds, size_t nc, const secp256k1_ge *value_gen, const secp256k1_bulletproof_circuit *circ, const secp256k1_bulletproof_generators *gens, const unsigned char *nonce, const unsigned char *extra_commit, size_t extra_commit_len)
size_t secp256k1_bulletproof_innerproduct_proof_length(size_t n)
static secp256k1_bulletproof_generators * secp256k1_bulletproof_generators_create(const secp256k1_context *ctx, const secp256k1_generator *blinding_gen, size_t n, size_t precomp_n)
Definition core.h:97
static void secp256k1_bulletproof_generators_destroy(const secp256k1_context *ctx, secp256k1_bulletproof_generators *gen)
Definition core.h:169
#define secp256k1_scratch_alloc(scratch, size)
Definition core.h:26
static SECP256K1_INLINE void secp256k1_pedersen_ecmult_scalar(secp256k1_gej *rj, const secp256k1_scalar *sec, const secp256k1_scalar *value, const secp256k1_ge *value_gen, const secp256k1_ge *blind_gen)
Definition core.h:220
Public C core for Purify key validation, key derivation, key generation, and evaluation.
purify_error_code purify_fill_secure_random(unsigned char *bytes, size_t bytes_len)
Fills a caller-owned buffer with secure operating-system randomness.
Definition core.c:260
@ PURIFY_ERROR_OK
Definition purify.h:29
Narrow C ABI exposing secp256k1 scalar and HMAC helpers to the C++ headers.
secp256k1_scratch_space * scratch
Definition bppp_bridge.c:96
secp256k1_context * ctx
Definition bppp_bridge.c:95
secp256k1_bppp_generators * gens
Definition bppp_bridge.c:97
secp256k1_bppp_generators gens_scratch
Definition bppp_bridge.c:98
purify_bppp_backend_resources * resources
secp256k1_scratch_space * scratch
Definition bppp_bridge.c:89
secp256k1_bulletproof_generators * gens
Definition bppp_bridge.c:90
const purify_bulletproof_row_view * wr
const purify_bulletproof_row_view * wo
const purify_bulletproof_row_view * wl
const purify_bulletproof_row_view * wv
const unsigned char * c32
Opaque scalar storage compatible with secp256k1-zkp internal scalar storage.
Definition secp_bridge.h:22
secp256k1_context * ctx
Definition bppp_bridge.c:37
secp256k1_bulletproof_wmatrix_row * wl
Definition core.h:52
secp256k1_bulletproof_wmatrix_row * wv
Definition core.h:55
secp256k1_bulletproof_wmatrix_entry * entries
Definition core.h:57
secp256k1_bulletproof_wmatrix_row * wo
Definition core.h:54
secp256k1_bulletproof_wmatrix_row * wr
Definition core.h:53
secp256k1_fast_scalar * c
Definition core.h:56
secp256k1_ge * blinding_gen
Definition core.h:72
Definition core.h:37
secp256k1_fast_scalar scal
Definition core.h:39
size_t idx
Definition core.h:38
secp256k1_bulletproof_wmatrix_entry * entry
Definition core.h:44
secp256k1_scalar scal
Definition core.h:30
static SECP256K1_INLINE size_t secp256k1_floor_lg(size_t n)
Definition util.h:85