/* * Copyright 2023 The Tongsuo Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy * in the file LICENSE in the source distribution or at * https://github.com/Tongsuo-Project/Tongsuo/blob/master/LICENSE.txt */ #include #include #include #include "internal/endian.h" #include "bulletproofs.h" #include "range_proof.h" #include "r1cs.h" DEFINE_STACK_OF(BIGNUM) DEFINE_STACK_OF(EC_POINT) DEFINE_STACK_OF(BP_VARIABLE) static point_conversion_form_t form = POINT_CONVERSION_COMPRESSED; static int bp_stack_of_variable_encode(STACK_OF(BP_VARIABLE) *sk, unsigned char *out, const EC_GROUP *group, BN_CTX *bn_ctx) { int i, n, *q, size; size_t point_len; unsigned char *p; BP_VARIABLE *V; if (sk == NULL || group == NULL) return 0; point_len = EC_POINT_point2oct(group, EC_GROUP_get0_generator(group), form, NULL, 0, bn_ctx); n = sk_BP_VARIABLE_num(sk); if (out == NULL) { size = sizeof(n) + n * point_len; for (i = 0; i < n; i++) { V = sk_BP_VARIABLE_value(sk, i); if (V == NULL) break; if (V->name != NULL) { size += strlen(V->name); } size += 1; } return size; } q = (int *)out; *q++ = zkp_l2n((int)n); p = (unsigned char *)q; for (i = 0; i < n; i++) { V = sk_BP_VARIABLE_value(sk, i); if (V == NULL) goto end; if (EC_POINT_point2oct(group, V->point, form, p, point_len, bn_ctx) == 0) goto end; p += point_len; if (V->name == NULL) { *p++ = '\0'; continue; } stpcpy((char *)p, V->name); p += strlen(V->name) + 1; } end: return p - out; } static STACK_OF(BP_VARIABLE) *bp_stack_of_variable_decode(const unsigned char *in, int *len, const EC_GROUP *group, BN_CTX *bn_ctx) { char *name; unsigned char *p; int *q = (int *)in, n, i; size_t point_len; EC_POINT *V = NULL; BP_VARIABLE *var = NULL; STACK_OF(BP_VARIABLE) *ret = NULL; if (in == NULL || group == NULL) return 0; point_len = EC_POINT_point2oct(group, EC_GROUP_get0_generator(group), form, NULL, 0, bn_ctx); n = (int)zkp_n2l(*q); q++; p = (unsigned char *)q; if (n < 0) { return NULL; } if (!(ret = sk_BP_VARIABLE_new_reserve(NULL, n))) return NULL; for (i = 0; i < n; i++) { if (!(V = EC_POINT_new(group))) goto err; if (!EC_POINT_oct2point(group, V, p, point_len, bn_ctx)) goto err; p += point_len; name = (char *)p; if (*name == '\0') { name = NULL; } else { p += strlen(name); } p += 1; if (!(var = BP_VARIABLE_new(name, V, group))) goto err; if (sk_BP_VARIABLE_push(ret, var) <= 0) goto err; EC_POINT_free(V); } if (len != NULL) *len = p - in; return ret; err: EC_POINT_free(V); BP_VARIABLE_free(var); sk_BP_VARIABLE_pop_free(ret, BP_VARIABLE_free); return NULL; } static int bp_inner_product_proof_encode(bp_inner_product_proof_t *ip_proof, unsigned char *out, const EC_GROUP *group, BN_CTX *bn_ctx) { int bn_len, sk_len, len; unsigned char *p = out; STACK_OF(BIGNUM) *sk_bn = NULL; if (ip_proof == NULL || group == NULL || bn_ctx == NULL) return 0; bn_len = BN_num_bytes(EC_GROUP_get0_order(group)); sk_bn = sk_BIGNUM_new_reserve(NULL, 2); if (sk_bn == NULL) goto end; if (sk_BIGNUM_push(sk_bn, ip_proof->a) <= 0 || sk_BIGNUM_push(sk_bn, ip_proof->b) <= 0) goto end; sk_len = zkp_stack_of_bignum_encode(sk_bn, NULL, bn_len); if (sk_len == 0) goto end; len = sk_len; sk_len = zkp_stack_of_point_encode(ip_proof->sk_L, NULL, group, bn_ctx); if (sk_len == 0) goto end; len += sk_len; sk_len = zkp_stack_of_point_encode(ip_proof->sk_R, NULL, group, bn_ctx); if (sk_len == 0) goto end; len += sk_len; if (out == NULL) return len; sk_len = zkp_stack_of_bignum_encode(sk_bn, p, bn_len); if (sk_len == 0) goto end; p += sk_len; sk_len = zkp_stack_of_point_encode(ip_proof->sk_L, p, group, bn_ctx); if (sk_len == 0) goto end; p += sk_len; sk_len = zkp_stack_of_point_encode(ip_proof->sk_R, p, group, bn_ctx); if (sk_len == 0) goto end; p += sk_len; end: sk_BIGNUM_free(sk_bn); return p != NULL ? p - out : 0; } static bp_inner_product_proof_t *bp_inner_product_proof_decode(const unsigned char *in, int *len, const EC_GROUP *group, BN_CTX *bn_ctx) { int bn_len, sk_len; unsigned char *p = (unsigned char *)in; STACK_OF(BIGNUM) *sk_bn = NULL; bp_inner_product_proof_t *ip_proof = NULL; if (in == NULL || group == NULL || bn_ctx == NULL) return NULL; bn_len = BN_num_bytes(EC_GROUP_get0_order(group)); if (!(ip_proof = bp_inner_product_proof_alloc(1))) goto err; sk_EC_POINT_free(ip_proof->sk_L); sk_EC_POINT_free(ip_proof->sk_R); ip_proof->sk_L = NULL; ip_proof->sk_R = NULL; if (!(sk_bn = zkp_stack_of_bignum_decode(p, &sk_len, bn_len))) goto err; if (sk_BIGNUM_num(sk_bn) != 2) goto err; if (!BN_copy(ip_proof->a, sk_BIGNUM_value(sk_bn, 0)) || !BN_copy(ip_proof->b, sk_BIGNUM_value(sk_bn, 1))) goto err; p += sk_len; if (!(ip_proof->sk_L = zkp_stack_of_point_decode(p, &sk_len, group, bn_ctx))) goto err; p += sk_len; if (!(ip_proof->sk_R = zkp_stack_of_point_decode(p, &sk_len, group, bn_ctx))) goto err; p += sk_len; if (len != NULL) *len = p - in; sk_BIGNUM_free(sk_bn); return ip_proof; err: sk_BIGNUM_pop_free(sk_bn, BN_free); bp_inner_product_proof_free(ip_proof); return NULL; } /** Encodes BP_PUB_PARAM to binary * \param pp BP_PUB_PARAM object * \param out the buffer for the result (if NULL the function returns * number of bytes needed). * \param size The memory size of the out pointer object * \return the length of the encoded octet string or 0 if an error occurred */ size_t BP_PUB_PARAM_encode(const BP_PUB_PARAM *pp, unsigned char *out, size_t size) { int *q, sk_len, curve_id; size_t point_len, ret = 0, len; unsigned char *p; BN_CTX *bn_ctx = NULL; EC_GROUP *group = NULL; if (pp == NULL) { ERR_raise(ERR_LIB_ZKP_BP, ERR_R_PASSED_NULL_PARAMETER); return ret; } group = pp->group; curve_id = EC_GROUP_get_curve_name(group); bn_ctx = BN_CTX_new(); if (bn_ctx == NULL) { ERR_raise(ERR_LIB_ZKP_BP, ERR_R_MALLOC_FAILURE); goto end; } point_len = EC_POINT_point2oct(group, EC_GROUP_get0_generator(group), form, NULL, 0, bn_ctx); sk_len = zkp_stack_of_point_encode(pp->sk_G, NULL, group, bn_ctx); if (sk_len == 0) goto end; len = sizeof(int) * 3 + point_len * 2 + sk_len * 2; if (out == NULL) { ret = len; goto end; } if (size < len) goto end; memset(out, 0, size); q = (int *)out; *q++ = zkp_l2n((int)curve_id); *q++ = zkp_l2n((int)pp->gens_capacity); *q++ = zkp_l2n((int)pp->party_capacity); p = (unsigned char *)q; if (EC_POINT_point2oct(group, pp->H, form, p, point_len, bn_ctx) == 0) goto end; p += point_len; if (EC_POINT_point2oct(group, pp->U, form, p, point_len, bn_ctx) == 0) goto end; p += point_len; sk_len = zkp_stack_of_point_encode(pp->sk_G, p, group, bn_ctx); if (sk_len == 0) goto end; p += sk_len; sk_len = zkp_stack_of_point_encode(pp->sk_H, p, group, bn_ctx); if (sk_len == 0) goto end; p += sk_len; ret = len; end: BN_CTX_free(bn_ctx); return ret; } /** Decodes binary to BP_PUB_PARAM * \param in Memory buffer with the encoded BP_PUB_PARAM * object * \param size The memory size of the in pointer object * \return BP_PUB_PARAM object pointer on success and NULL otherwise */ BP_PUB_PARAM *BP_PUB_PARAM_decode(const unsigned char *in, size_t size) { unsigned char *p; int curve_id, *q = (int *)in, sk_len; size_t point_len, gens_capacity, party_capacity, n; BP_PUB_PARAM *pp = NULL; BN_CTX *bn_ctx = NULL; EC_GROUP *group = NULL; if (in == NULL || size <= 12) { ERR_raise(ERR_LIB_ZKP_BP, ERR_R_PASSED_NULL_PARAMETER); return NULL; } bn_ctx = BN_CTX_new(); if (bn_ctx == NULL) { ERR_raise(ERR_LIB_ZKP_BP, ERR_R_MALLOC_FAILURE); goto err; } curve_id = zkp_n2l(*q); q++; gens_capacity = (size_t)zkp_n2l(*q); q++; party_capacity = (size_t)zkp_n2l(*q); q++; p = (unsigned char *)q; n = gens_capacity * party_capacity; group = EC_GROUP_new_by_curve_name_ex(NULL, NULL, curve_id); if (group == NULL) goto err; point_len = EC_POINT_point2oct(group, EC_GROUP_get0_generator(group), form, NULL, 0, bn_ctx); if (point_len <= 0) goto err; if (size < (sizeof(int) * 3 + point_len * (n * 2 + 2))) { ERR_raise(ERR_LIB_ZKP_BP, ERR_R_PASSED_INVALID_ARGUMENT); goto err; } pp = BP_PUB_PARAM_new(group, gens_capacity, party_capacity); if (pp == NULL) goto err; sk_EC_POINT_pop_free(pp->sk_G, EC_POINT_free); sk_EC_POINT_pop_free(pp->sk_H, EC_POINT_free); pp->sk_G = NULL; pp->sk_H = NULL; if (!EC_POINT_oct2point(group, pp->H, p, point_len, bn_ctx)) goto err; p += point_len; if (!EC_POINT_oct2point(group, pp->U, p, point_len, bn_ctx)) goto err; p += point_len; if (!(pp->sk_G = zkp_stack_of_point_decode(p, &sk_len, group, bn_ctx))) goto err; p += sk_len; if (!(pp->sk_H = zkp_stack_of_point_decode(p, &sk_len, group, bn_ctx))) goto err; p += sk_len; EC_GROUP_free(group); BN_CTX_free(bn_ctx); return pp; err: EC_GROUP_free(group); BP_PUB_PARAM_free(pp); BN_CTX_free(bn_ctx); return NULL; } /** Encodes BP_WITNESS to binary * \param pp BP_WITNESS object * \param out The buffer for the result (if NULL the function returns * number of bytes needed). * \param size The memory size of the out pointer object * \param flag The flag is an indicator for encoding random number 'r' * and plaintext 'v', with 1 indicating encoding and 0 * indicating no encoding. * \return the length of the encoded octet string or 0 if an error occurred */ size_t BP_WITNESS_encode(const BP_WITNESS *witness, unsigned char *out, size_t size, int flag) { int *q, curve_id, bn_len, sk_len; size_t ret = 0, len, n, point_len; unsigned char *p; BP_VARIABLE *V; BN_CTX *bn_ctx = NULL; EC_GROUP *group = NULL; if (witness == NULL || witness->sk_V == NULL) { ERR_raise(ERR_LIB_ZKP_BP, ERR_R_PASSED_NULL_PARAMETER); return ret; } n = sk_BP_VARIABLE_num(witness->sk_V); if (n == 0) { ERR_raise(ERR_LIB_ZKP_BP, ERR_R_PASSED_INVALID_ARGUMENT); return ret; } V = sk_BP_VARIABLE_value(witness->sk_V, 0); if ((curve_id = EC_POINT_get_curve_name(V->point)) == NID_undef) { ERR_raise(ERR_LIB_ZKP_BP, ERR_R_PASSED_INVALID_ARGUMENT); goto end; } group = EC_GROUP_new_by_curve_name_ex(NULL, NULL, curve_id); if (group == NULL) goto end; bn_len = BN_num_bytes(EC_GROUP_get0_order(group)); point_len = EC_POINT_point2oct(group, EC_GROUP_get0_generator(group), form, NULL, 0, bn_ctx); if (point_len <= 0) goto end; bn_ctx = BN_CTX_new(); if (bn_ctx == NULL) { ERR_raise(ERR_LIB_ZKP_BP, ERR_R_MALLOC_FAILURE); goto end; } if (!(sk_len = bp_stack_of_variable_encode(witness->sk_V, NULL, group, bn_ctx))) goto end; len = 4 + point_len + sk_len; if (!(sk_len = zkp_stack_of_bignum_encode(witness->sk_r, NULL, bn_len))) goto end; if (flag == 1) len += sk_len * 2; if (out == NULL) { ret = len; goto end; } if (size < len) goto end; memset(out, 0, size); q = (int *)out; *q++ = zkp_l2n((int)curve_id); p = (unsigned char *)q; if (EC_POINT_point2oct(group, witness->H, form, p, point_len, bn_ctx) == 0) goto end; p += point_len; if (!(sk_len = bp_stack_of_variable_encode(witness->sk_V, p, group, bn_ctx))) goto end; p += sk_len; if (flag == 1) { if (!(sk_len = zkp_stack_of_bignum_encode(witness->sk_r, p, bn_len))) goto end; p += sk_len; if (!(sk_len = zkp_stack_of_bignum_encode(witness->sk_v, p, bn_len))) goto end; p += sk_len; } ret = len; end: BN_CTX_free(bn_ctx); EC_GROUP_free(group); return ret; } /** Decodes binary to BP_WITNESS * \param in Memory buffer with the encoded BP_WITNESS * object * \param size The memory size of the in pointer object * \param flag The flag is an indicator for decoding random number 'r' * and plaintext 'v', with 1 indicating decoding and 0 * indicating no decoding. * \return BP_WITNESS object pointer on success and NULL otherwise */ BP_WITNESS *BP_WITNESS_decode(const unsigned char *in, size_t size, int flag) { unsigned char *p; int curve_id, *q = (int *)in, bn_len, sk_len; size_t point_len; BP_WITNESS *witness = NULL; BN_CTX *bn_ctx = NULL; EC_GROUP *group = NULL; if (in == NULL || size <= 12) { ERR_raise(ERR_LIB_ZKP_BP, ERR_R_PASSED_NULL_PARAMETER); return NULL; } bn_ctx = BN_CTX_new(); if (bn_ctx == NULL) { ERR_raise(ERR_LIB_ZKP_BP, ERR_R_MALLOC_FAILURE); goto err; } curve_id = zkp_n2l(*q); q++; p = (unsigned char *)q; group = EC_GROUP_new_by_curve_name_ex(NULL, NULL, curve_id); if (group == NULL) goto err; bn_len = BN_num_bytes(EC_GROUP_get0_order(group)); point_len = EC_POINT_point2oct(group, EC_GROUP_get0_generator(group), form, NULL, 0, bn_ctx); if (point_len <= 0) goto err; if (!(witness = OPENSSL_zalloc(sizeof(*witness)))) { goto err; } if (!(witness->H = EC_POINT_new(group))) goto err; if (!EC_POINT_oct2point(group, witness->H, p, point_len, bn_ctx)) goto err; p += point_len; if (!(witness->sk_V = bp_stack_of_variable_decode(p, &sk_len, group, bn_ctx))) goto err; p += sk_len; if (flag == 1) { if (!(witness->sk_r = zkp_stack_of_bignum_decode(p, &sk_len, bn_len))) goto err; p += sk_len; if (!(witness->sk_v = zkp_stack_of_bignum_decode(p, &sk_len, bn_len))) goto err; p += sk_len; } witness->group = group; witness->references = 1; if ((witness->lock = CRYPTO_THREAD_lock_new()) == NULL) goto err; BN_CTX_free(bn_ctx); return witness; err: EC_GROUP_free(group); BP_WITNESS_free(witness); BN_CTX_free(bn_ctx); return NULL; } /** Encodes BP_RANGE_PROOF to binary * \param proof BP_RANGE_PROOF object * \param out the buffer for the result (if NULL the function returns * number of bytes needed). * \param size The memory size of the out pointer object * \return the length of the encoded octet string or 0 if an error occurred */ size_t BP_RANGE_PROOF_encode(const BP_RANGE_PROOF *proof, unsigned char *out, size_t size) { int *q, curve_id, bn_len, ret = 0, sk_len; size_t len; unsigned char *p = NULL; bp_inner_product_proof_t *ip_proof; BN_CTX *bn_ctx = NULL; EC_GROUP *group = NULL; STACK_OF(EC_POINT) *sk_point = NULL; STACK_OF(BIGNUM) *sk_bn = NULL; if (proof == NULL) { ERR_raise(ERR_LIB_ZKP_BP, ERR_R_PASSED_NULL_PARAMETER); return 0; } sk_point = sk_EC_POINT_new_reserve(NULL, 4); sk_bn = sk_BIGNUM_new_reserve(NULL, 3); if (sk_point == NULL || sk_bn == NULL) { ERR_raise(ERR_LIB_ZKP_BP, ERR_R_MALLOC_FAILURE); return 0; } ip_proof = proof->ip_proof; if ((curve_id = EC_POINT_get_curve_name(proof->A)) == NID_undef || ip_proof == NULL) { ERR_raise(ERR_LIB_ZKP_BP, ERR_R_PASSED_INVALID_ARGUMENT); goto end; } group = EC_GROUP_new_by_curve_name_ex(NULL, NULL, curve_id); if (group == NULL) goto end; bn_ctx = BN_CTX_new(); if (bn_ctx == NULL) { ERR_raise(ERR_LIB_ZKP_BP, ERR_R_MALLOC_FAILURE); goto end; } bn_len = BN_num_bytes(EC_GROUP_get0_order(group)); len = sizeof(int); if (sk_EC_POINT_push(sk_point, proof->A) <= 0 || sk_EC_POINT_push(sk_point, proof->S) <= 0 || sk_EC_POINT_push(sk_point, proof->T1) <= 0 || sk_EC_POINT_push(sk_point, proof->T2) <= 0) goto end; sk_len = zkp_stack_of_point_encode(sk_point, NULL, group, bn_ctx); if (sk_len == 0) goto end; len += sk_len; if (sk_BIGNUM_push(sk_bn, proof->taux) <= 0 || sk_BIGNUM_push(sk_bn, proof->mu) <= 0 || sk_BIGNUM_push(sk_bn, proof->tx) <= 0) goto end; sk_len = zkp_stack_of_bignum_encode(sk_bn, NULL, bn_len); if (sk_len == 0) goto end; len += sk_len; sk_len = bp_inner_product_proof_encode(ip_proof, NULL, group, bn_ctx); if (sk_len == 0) goto end; len += sk_len; if (out == NULL) { ret = len; goto end; } if (size < len) goto end; memset(out, 0, size); /* encoding proof */ q = (int *)out; *q++ = zkp_l2n(curve_id); p = (unsigned char *)q; sk_len = zkp_stack_of_point_encode(sk_point, p, group, bn_ctx); if (sk_len == 0) goto end; p += sk_len; sk_len = zkp_stack_of_bignum_encode(sk_bn, p, bn_len); if (sk_len == 0) goto end; p += sk_len; /* encoding ip_proof */ len = bp_inner_product_proof_encode(ip_proof, p, group, bn_ctx); if (len == 0) goto end; p += len; ret = p - out; end: BN_CTX_free(bn_ctx); EC_GROUP_free(group); sk_BIGNUM_free(sk_bn); sk_EC_POINT_free(sk_point); return ret; } /** Decodes binary to BP_RANGE_PROOF * \param in Memory buffer with the encoded BP_RANGE_PROOF object * \param size The memory size of the in pointer object * \return BP_RANGE_PROOF object pointer on success and NULL otherwise */ BP_RANGE_PROOF *BP_RANGE_PROOF_decode(const unsigned char *in, size_t size) { unsigned char *p; int *q = (int *)in, curve_id, len; size_t point_len, bn_len, proof_len; BP_RANGE_PROOF *proof = NULL; bp_inner_product_proof_t *ip_proof = NULL; BN_CTX *bn_ctx = NULL; EC_GROUP *group = NULL; STACK_OF(EC_POINT) *sk_point = NULL; STACK_OF(BIGNUM) *sk_bn = NULL; if (in == NULL || size <= 8) { ERR_raise(ERR_LIB_ZKP_BP, ERR_R_PASSED_NULL_PARAMETER); return NULL; } curve_id = zkp_n2l(*q); q++; if (curve_id <= 0) { ERR_raise(ERR_LIB_ZKP_BP, ERR_R_PASSED_INVALID_ARGUMENT); goto err; } group = EC_GROUP_new_by_curve_name_ex(NULL, NULL, curve_id); if (group == NULL) goto err; bn_ctx = BN_CTX_new(); if (bn_ctx == NULL) { ERR_raise(ERR_LIB_ZKP_BP, ERR_R_MALLOC_FAILURE); goto err; } p = (unsigned char *)q; bn_len = BN_num_bytes(EC_GROUP_get0_order(group)); point_len = EC_POINT_point2oct(group, EC_GROUP_get0_generator(group), form, NULL, 0, bn_ctx); if (point_len <= 0) goto err; /* len(curve_id) + len(A+S+T1+T2) + len(taux+mu+tx) + len(a+b) */ proof_len = 4 + point_len * 4 + bn_len * 3 + bn_len * 2; if (size < proof_len) { ERR_raise(ERR_LIB_ZKP_BP, ERR_R_PASSED_INVALID_ARGUMENT); goto err; } proof = OPENSSL_zalloc(sizeof(*proof)); if (proof == NULL) goto err; proof->references = 1; if ((proof->lock = CRYPTO_THREAD_lock_new()) == NULL) { ERR_raise(ERR_LIB_ZKP_BP, ERR_R_MALLOC_FAILURE); goto err; } sk_point = zkp_stack_of_point_decode(p, &len, group, bn_ctx); if (sk_point == NULL) goto err; p += len; if (sk_EC_POINT_num(sk_point) < 4) goto err; proof->A = sk_EC_POINT_value(sk_point, 0); proof->S = sk_EC_POINT_value(sk_point, 1); proof->T1 = sk_EC_POINT_value(sk_point, 2); proof->T2 = sk_EC_POINT_value(sk_point, 3); sk_bn = zkp_stack_of_bignum_decode(p, &len, bn_len); if (sk_point == NULL) goto err; p += len; if (sk_BIGNUM_num(sk_bn) < 3) goto err; proof->taux = sk_BIGNUM_value(sk_bn, 0); proof->mu = sk_BIGNUM_value(sk_bn, 1); proof->tx = sk_BIGNUM_value(sk_bn, 2); ip_proof = bp_inner_product_proof_decode(p, &len, group, bn_ctx); if (ip_proof == NULL) goto err; p += len; proof->ip_proof = ip_proof; sk_BIGNUM_free(sk_bn); sk_EC_POINT_free(sk_point); BN_CTX_free(bn_ctx); EC_GROUP_free(group); return proof; err: sk_BIGNUM_pop_free(sk_bn, BN_free); sk_EC_POINT_pop_free(sk_point, EC_POINT_free); bp_inner_product_proof_free(ip_proof); OPENSSL_free(proof); BN_CTX_free(bn_ctx); EC_GROUP_free(group); return NULL; } /** Encodes BP_R1CS_PROOF to binary * \param proof BP_R1CS_PROOF object * \param out the buffer for the result (if NULL the function returns * number of bytes needed). * \param size The memory size of the out pointer object * \return the length of the encoded octet string or 0 if an error occurred */ size_t BP_R1CS_PROOF_encode(const BP_R1CS_PROOF *proof, unsigned char *out, size_t size) { int *q, curve_id, bn_len, ret = 0, sk_len; size_t len; unsigned char *p = NULL; bp_inner_product_proof_t *ip_proof; BN_CTX *bn_ctx = NULL; EC_GROUP *group = NULL; STACK_OF(EC_POINT) *sk_point = NULL; STACK_OF(BIGNUM) *sk_bn = NULL; if (proof == NULL) { ERR_raise(ERR_LIB_ZKP_BP, ERR_R_PASSED_NULL_PARAMETER); return 0; } sk_point = sk_EC_POINT_new_reserve(NULL, 11); sk_bn = sk_BIGNUM_new_reserve(NULL, 3); if (sk_point == NULL || sk_bn == NULL) { ERR_raise(ERR_LIB_ZKP_BP, ERR_R_MALLOC_FAILURE); return 0; } ip_proof = proof->ip_proof; if ((curve_id = EC_POINT_get_curve_name(proof->AI1)) == NID_undef || ip_proof == NULL) { ERR_raise(ERR_LIB_ZKP_BP, ERR_R_PASSED_INVALID_ARGUMENT); goto end; } group = EC_GROUP_new_by_curve_name_ex(NULL, NULL, curve_id); if (group == NULL) goto end; bn_ctx = BN_CTX_new(); if (bn_ctx == NULL) { ERR_raise(ERR_LIB_ZKP_BP, ERR_R_MALLOC_FAILURE); goto end; } bn_len = BN_num_bytes(EC_GROUP_get0_order(group)); len = sizeof(int); if (sk_EC_POINT_push(sk_point, proof->AI1) <= 0 || sk_EC_POINT_push(sk_point, proof->AO1) <= 0 || sk_EC_POINT_push(sk_point, proof->S1) <= 0 || sk_EC_POINT_push(sk_point, proof->T1) <= 0 || sk_EC_POINT_push(sk_point, proof->T3) <= 0 || sk_EC_POINT_push(sk_point, proof->T4) <= 0 || sk_EC_POINT_push(sk_point, proof->T5) <= 0 || sk_EC_POINT_push(sk_point, proof->T6) <= 0 #if 0 || sk_EC_POINT_push(sk_point, proof->AI2) <= 0 || sk_EC_POINT_push(sk_point, proof->AO2) <= 0 || sk_EC_POINT_push(sk_point, proof->S2) <= 0 #endif ) goto end; sk_len = zkp_stack_of_point_encode(sk_point, NULL, group, bn_ctx); if (sk_len == 0) goto end; len += sk_len; if (sk_BIGNUM_push(sk_bn, proof->taux) <= 0 || sk_BIGNUM_push(sk_bn, proof->mu) <= 0 || sk_BIGNUM_push(sk_bn, proof->tx) <= 0) goto end; sk_len = zkp_stack_of_bignum_encode(sk_bn, NULL, bn_len); if (sk_len == 0) goto end; len += sk_len; sk_len = bp_inner_product_proof_encode(ip_proof, NULL, group, bn_ctx); if (sk_len == 0) goto end; len += sk_len; if (out == NULL) { ret = len; goto end; } if (size < len) goto end; memset(out, 0, size); /* encoding proof */ q = (int *)out; *q++ = zkp_l2n(curve_id); p = (unsigned char *)q; sk_len = zkp_stack_of_point_encode(sk_point, p, group, bn_ctx); if (sk_len == 0) goto end; p += sk_len; sk_len = zkp_stack_of_bignum_encode(sk_bn, p, bn_len); if (sk_len == 0) goto end; p += sk_len; /* encoding ip_proof */ sk_len = bp_inner_product_proof_encode(ip_proof, p, group, bn_ctx); if (sk_len == 0) goto end; p += sk_len; ret = p - out; end: BN_CTX_free(bn_ctx); EC_GROUP_free(group); sk_BIGNUM_free(sk_bn); sk_EC_POINT_free(sk_point); return ret; } /** Decodes binary to BP_R1CS_PROOF * \param in Memory buffer with the encoded BP_R1CS_PROOF object * \param size The memory size of the in pointer object * \return BP_R1CS_PROOF object pointer on success and NULL otherwise */ BP_R1CS_PROOF *BP_R1CS_PROOF_decode(const unsigned char *in, size_t size) { unsigned char *p; int *q = (int *)in, curve_id, len; size_t point_len, bn_len, proof_len; BP_R1CS_PROOF *proof = NULL; bp_inner_product_proof_t *ip_proof = NULL; BN_CTX *bn_ctx = NULL; EC_GROUP *group = NULL; STACK_OF(EC_POINT) *sk_point = NULL; STACK_OF(BIGNUM) *sk_bn = NULL; if (in == NULL || size <= 8) { ERR_raise(ERR_LIB_ZKP_BP, ERR_R_PASSED_NULL_PARAMETER); return NULL; } curve_id = zkp_n2l(*q); q++; if (curve_id <= 0) { ERR_raise(ERR_LIB_ZKP_BP, ERR_R_PASSED_INVALID_ARGUMENT); goto err; } group = EC_GROUP_new_by_curve_name_ex(NULL, NULL, curve_id); if (group == NULL) goto err; bn_ctx = BN_CTX_new(); if (bn_ctx == NULL) { ERR_raise(ERR_LIB_ZKP_BP, ERR_R_MALLOC_FAILURE); goto err; } p = (unsigned char *)q; bn_len = BN_num_bytes(EC_GROUP_get0_order(group)); point_len = EC_POINT_point2oct(group, EC_GROUP_get0_generator(group), form, NULL, 0, bn_ctx); if (point_len <= 0) goto err; #if 1 proof_len = 4 + point_len * 8 + bn_len * 3 + bn_len * 2; #else proof_len = 4 + point_len * 11 + bn_len * 3 + bn_len * 2; #endif if (size < proof_len) { ERR_raise(ERR_LIB_ZKP_BP, ERR_R_PASSED_INVALID_ARGUMENT); goto err; } proof = OPENSSL_zalloc(sizeof(*proof)); if (proof == NULL) goto err; proof->references = 1; if ((proof->lock = CRYPTO_THREAD_lock_new()) == NULL) { ERR_raise(ERR_LIB_ZKP_BP, ERR_R_MALLOC_FAILURE); goto err; } sk_point = zkp_stack_of_point_decode(p, &len, group, bn_ctx); if (sk_point == NULL) goto err; p += len; if (sk_EC_POINT_num(sk_point) < 8) goto err; proof->AI1 = sk_EC_POINT_value(sk_point, 0); proof->AO1 = sk_EC_POINT_value(sk_point, 1); proof->S1 = sk_EC_POINT_value(sk_point, 2); proof->T1 = sk_EC_POINT_value(sk_point, 3); proof->T3 = sk_EC_POINT_value(sk_point, 4); proof->T4 = sk_EC_POINT_value(sk_point, 5); proof->T5 = sk_EC_POINT_value(sk_point, 6); proof->T6 = sk_EC_POINT_value(sk_point, 7); #if 1 proof->AI2 = EC_POINT_new(group); proof->AO2 = EC_POINT_new(group); proof->S2 = EC_POINT_new(group); if (proof->AI2 == NULL || proof->AO2 == NULL || proof->S2 == NULL) { ERR_raise(ERR_LIB_ZKP_BP, ERR_R_MALLOC_FAILURE); goto err; } EC_POINT_set_to_infinity(group, proof->AI2); EC_POINT_set_to_infinity(group, proof->AO2); EC_POINT_set_to_infinity(group, proof->S2); #else proof->AI2 = sk_EC_POINT_value(sk_point, 8); proof->AO2 = sk_EC_POINT_value(sk_point, 9); proof->S2 = sk_EC_POINT_value(sk_point, 10); #endif sk_bn = zkp_stack_of_bignum_decode(p, &len, bn_len); if (sk_point == NULL) goto err; p += len; if (sk_BIGNUM_num(sk_bn) < 3) goto err; proof->taux = sk_BIGNUM_value(sk_bn, 0); proof->mu = sk_BIGNUM_value(sk_bn, 1); proof->tx = sk_BIGNUM_value(sk_bn, 2); ip_proof = bp_inner_product_proof_decode(p, &len, group, bn_ctx); if (ip_proof == NULL) goto err; p += len; proof->ip_proof = ip_proof; sk_BIGNUM_free(sk_bn); sk_EC_POINT_free(sk_point); BN_CTX_free(bn_ctx); EC_GROUP_free(group); return proof; err: sk_BIGNUM_pop_free(sk_bn, BN_free); sk_EC_POINT_pop_free(sk_point, EC_POINT_free); bp_inner_product_proof_free(ip_proof); BP_R1CS_PROOF_free(proof); BN_CTX_free(bn_ctx); EC_GROUP_free(group); return NULL; }