gateway2/Tongsuo-8.4.0/crypto/zkp/gadget/zkp_range_proof.c
2026-07-11 13:40:57 +08:00

624 lines
17 KiB
C

/*
* 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 <openssl/err.h>
#include <openssl/zkperr.h>
#include <openssl/bulletproofs.h>
#include <openssl/nizk.h>
#include <openssl/zkp_gadget.h>
#include <crypto/ec.h>
#include <crypto/ec/ec_elgamal.h>
#include <crypto/zkp/bulletproofs/bulletproofs.h>
#include <crypto/zkp/nizk/nizk.h>
#include <crypto/zkp/nizk/nizk_plaintext_knowledge.h>
#include <crypto/zkp/common/zkp_util.h>
#include "zkp_range_proof.h"
DEFINE_STACK_OF(BIGNUM)
DEFINE_STACK_OF(BP_VARIABLE)
static int zkp_bp_range_witness_adjust(BP_WITNESS *witness, int left_bound_bits,
int right_bound_bits, int range_bits,
int is_prove, const EC_GROUP *group)
{
int ret = 0;
BIGNUM *bn1, *bn2, *bn_left_bound, *bn_right_bound, *bn_range, *bn_delta, *v0, *v1;
BN_CTX *bn_ctx = NULL;
BP_VARIABLE *var0, *var1;
EC_POINT *P = NULL;
zkp_poly_points_t *poly = NULL;
bn_ctx = BN_CTX_new();
if (bn_ctx == NULL)
return 0;
bn1 = BN_CTX_get(bn_ctx);
bn2 = BN_CTX_get(bn_ctx);
bn_left_bound = BN_CTX_get(bn_ctx);
bn_right_bound = BN_CTX_get(bn_ctx);
bn_range = BN_CTX_get(bn_ctx);
bn_delta = BN_CTX_get(bn_ctx);
if (bn_delta == NULL)
goto err;
BN_one(bn1);
BN_set_word(bn2, 2);
BN_set_word(bn_left_bound, left_bound_bits);
BN_set_word(bn_right_bound, right_bound_bits);
BN_set_word(bn_range, range_bits);
if (!BN_exp(bn_left_bound, bn2, bn_left_bound, bn_ctx)
|| !BN_exp(bn_right_bound, bn2, bn_right_bound, bn_ctx)
|| !BN_exp(bn_range, bn2, bn_range, bn_ctx)
|| !BN_sub(bn_delta, bn_range, bn_right_bound))
goto err;
if (is_prove == 1) {
v0 = sk_BIGNUM_value(witness->sk_v, 0);
v1 = sk_BIGNUM_value(witness->sk_v, 1);
if (!BN_sub(v0, v0, bn_left_bound) || !BN_add(v1, v1, bn_delta))
goto err;
}
P = EC_POINT_new(group);
if (P == NULL)
goto err;
var0 = sk_BP_VARIABLE_value(witness->sk_V, 0);
var1 = sk_BP_VARIABLE_value(witness->sk_V, 1);
BN_set_negative(bn_left_bound, 1);
if (!(poly = zkp_poly_points_new(2)))
goto err;
if (!zkp_poly_points_append(poly, var0->point, bn1)
|| !zkp_poly_points_append(poly, witness->H, bn_left_bound))
goto err;
if (!zkp_poly_points_mul(poly, var0->point, NULL, group, bn_ctx))
goto err;
zkp_poly_points_reset(poly);
if (!zkp_poly_points_append(poly, var1->point, bn1)
|| !zkp_poly_points_append(poly, witness->H, bn_delta))
goto err;
if (!zkp_poly_points_mul(poly, var1->point, NULL, group, bn_ctx))
goto err;
ret = 1;
err:
zkp_poly_points_free(poly);
EC_POINT_free(P);
BN_CTX_free(bn_ctx);
return ret;
}
ZKP_RANGE_PUB_PARAM *ZKP_RANGE_PUB_PARAM_raw_new(BP_PUB_PARAM *bp_pp)
{
ZKP_RANGE_PUB_PARAM *pp = NULL;
if (bp_pp == NULL) {
ERR_raise(ERR_LIB_ZKP, ERR_R_PASSED_NULL_PARAMETER);
return NULL;
}
pp = OPENSSL_zalloc(sizeof(*pp));
if (pp == NULL) {
ERR_raise(ERR_LIB_ZKP, ERR_R_MALLOC_FAILURE);
return NULL;
}
if (!BP_PUB_PARAM_up_ref(bp_pp))
goto err;
pp->bp_pp = bp_pp;
pp->references = 1;
if ((pp->lock = CRYPTO_THREAD_lock_new()) == NULL) {
ERR_raise(ERR_LIB_ZKP_BP, ERR_R_MALLOC_FAILURE);
goto err;
}
return pp;
err:
ZKP_RANGE_PUB_PARAM_free(pp);
return NULL;
}
ZKP_RANGE_PUB_PARAM *ZKP_RANGE_PUB_PARAM_new(const EC_GROUP *group, int max_bits)
{
BP_PUB_PARAM *bp_pp = NULL;
ZKP_RANGE_PUB_PARAM *pp = NULL;
if (group == NULL || max_bits > 64) {
ERR_raise(ERR_LIB_ZKP, ERR_R_PASSED_INVALID_ARGUMENT);
return NULL;
}
if (!(bp_pp = BP_PUB_PARAM_new(group, max_bits, 2)))
return NULL;
pp = ZKP_RANGE_PUB_PARAM_raw_new(bp_pp);
BP_PUB_PARAM_free(bp_pp);
return pp;
}
void ZKP_RANGE_PUB_PARAM_free(ZKP_RANGE_PUB_PARAM *pp)
{
int ref;
if (pp == NULL)
return;
CRYPTO_DOWN_REF(&pp->references, &ref, pp->lock);
REF_PRINT_COUNT("ZKP_RANGE_PUB_PARAM", pp);
if (ref > 0)
return;
REF_ASSERT_ISNT(ref < 0);
BP_PUB_PARAM_down_ref(pp->bp_pp);
OPENSSL_clear_free((void *)pp, sizeof(*pp));
}
int ZKP_RANGE_PUB_PARAM_up_ref(ZKP_RANGE_PUB_PARAM *pp)
{
int ref;
if (pp == NULL)
return 0;
if (CRYPTO_UP_REF(&pp->references, &ref, pp->lock) <= 0)
return 0;
REF_PRINT_COUNT("ZKP_RANGE_PUB_PARAM", pp);
REF_ASSERT_ISNT(ref < 2);
return ((ref > 1) ? 1 : 0);
}
int ZKP_RANGE_PUB_PARAM_down_ref(ZKP_RANGE_PUB_PARAM *pp)
{
int ref;
if (pp == NULL)
return 0;
if (CRYPTO_DOWN_REF(&pp->references, &ref, pp->lock) <= 0)
return 0;
REF_PRINT_COUNT("ZKP_RANGE_PUB_PARAM", pp);
REF_ASSERT_ISNT(ref < 0);
return ((ref > 0) ? 1 : 0);
}
ZKP_RANGE_WITNESS *ZKP_RANGE_WITNESS_new(const ZKP_RANGE_PUB_PARAM *pp,
const BIGNUM *r, const BIGNUM *v)
{
ZKP_RANGE_WITNESS *witness = NULL;
if (pp == NULL) {
ERR_raise(ERR_LIB_ZKP, ERR_R_PASSED_NULL_PARAMETER);
return NULL;
}
if (!(witness = OPENSSL_zalloc(sizeof(*witness)))) {
ERR_raise(ERR_LIB_ZKP, ERR_R_MALLOC_FAILURE);
return NULL;
}
if (!(witness->order = BN_dup(EC_GROUP_get0_order(pp->bp_pp->group)))
|| !(witness->r = BN_new())
|| !(witness->v = BN_new())) {
ERR_raise(ERR_LIB_ZKP, ERR_R_MALLOC_FAILURE);
goto err;
}
if (r != NULL) {
if (!BN_copy(witness->r, r))
goto err;
} else {
zkp_rand_range(witness->r, witness->order);
}
if (v != NULL && !BN_copy(witness->v, v))
goto err;
witness->references = 1;
if ((witness->lock = CRYPTO_THREAD_lock_new()) == NULL) {
ERR_raise(ERR_LIB_ZKP, ERR_R_MALLOC_FAILURE);
goto err;
}
return witness;
err:
ZKP_RANGE_WITNESS_free(witness);
return NULL;
}
void ZKP_RANGE_WITNESS_free(ZKP_RANGE_WITNESS *witness)
{
int ref;
if (witness == NULL)
return;
CRYPTO_DOWN_REF(&witness->references, &ref, witness->lock);
REF_PRINT_COUNT("ZKP_RANGE_WITNESS", witness);
if (ref > 0)
return;
REF_ASSERT_ISNT(ref < 0);
BN_free(witness->order);
BN_free(witness->r);
BN_free(witness->v);
CRYPTO_THREAD_lock_free(witness->lock);
OPENSSL_free(witness);
}
int ZKP_RANGE_WITNESS_up_ref(ZKP_RANGE_WITNESS *witness)
{
int ref;
if (witness == NULL)
return 0;
if (CRYPTO_UP_REF(&witness->references, &ref, witness->lock) <= 0)
return 0;
REF_PRINT_COUNT("ZKP_RANGE_WITNESS", witness);
REF_ASSERT_ISNT(ref < 2);
return ((ref > 1) ? 1 : 0);
}
int ZKP_RANGE_WITNESS_down_ref(ZKP_RANGE_WITNESS *witness)
{
int ref;
if (witness == NULL)
return 0;
if (CRYPTO_DOWN_REF(&witness->references, &ref, witness->lock) <= 0)
return 0;
REF_PRINT_COUNT("ZKP_RANGE_WITNESS", witness);
REF_ASSERT_ISNT(ref < 0);
return ((ref > 0) ? 1 : 0);
}
ZKP_RANGE_CTX *ZKP_RANGE_CTX_raw_new(ZKP_TRANSCRIPT *transcript,
ZKP_RANGE_PUB_PARAM *pp,
ZKP_RANGE_WITNESS *witness,
const EC_POINT *pk,
EC_ELGAMAL_CTX *enc_ctx,
EC_ELGAMAL_CIPHERTEXT *enc_ct)
{
ZKP_RANGE_CTX *ctx = NULL;
if (transcript == NULL || pp == NULL || witness == NULL || pk == NULL
|| enc_ct == NULL || enc_ctx == NULL) {
ERR_raise(ERR_LIB_ZKP, ERR_R_PASSED_NULL_PARAMETER);
return NULL;
}
if (enc_ctx->flag != EC_ELGAMAL_FLAG_TWISTED) {
ERR_raise(ERR_LIB_ZKP, ERR_R_PASSED_INVALID_ARGUMENT);
return NULL;
}
ctx = OPENSSL_zalloc(sizeof(*ctx));
if (ctx == NULL) {
ERR_raise(ERR_LIB_ZKP, ERR_R_MALLOC_FAILURE);
return NULL;
}
ctx->transcript = transcript;
if (!ZKP_RANGE_PUB_PARAM_up_ref(pp))
goto err;
ctx->pp = pp;
if (!ZKP_RANGE_WITNESS_up_ref(witness))
goto err;
ctx->witness = witness;
ctx->PK = EC_POINT_dup(pk, pp->bp_pp->group);
if (ctx->PK == NULL)
goto err;
ctx->enc_ctx = EC_ELGAMAL_CTX_dup(enc_ctx);
ctx->enc_ct = EC_ELGAMAL_CIPHERTEXT_dup(enc_ct, pp->bp_pp->group);
if (ctx->enc_ctx == NULL || ctx->enc_ct == NULL)
goto err;
return ctx;
err:
ZKP_RANGE_CTX_free(ctx);
return NULL;
}
ZKP_RANGE_CTX *ZKP_RANGE_CTX_new(ZKP_TRANSCRIPT *transcript,
ZKP_RANGE_PUB_PARAM *pp,
ZKP_RANGE_WITNESS *witness,
EC_KEY *key)
{
ZKP_RANGE_CTX *ret = NULL;
EC_ELGAMAL_CTX *enc_ctx = NULL;
EC_ELGAMAL_CIPHERTEXT *enc_ct = NULL;
if (transcript == NULL || pp == NULL || witness == NULL || key == NULL) {
ERR_raise(ERR_LIB_ZKP, ERR_R_PASSED_NULL_PARAMETER);
return NULL;
}
if (!(enc_ctx = EC_ELGAMAL_CTX_new(key, pp->bp_pp->H, EC_ELGAMAL_FLAG_TWISTED)))
goto err;
if (!(enc_ct = EC_ELGAMAL_CIPHERTEXT_new(enc_ctx)))
goto err;
if (!EC_ELGAMAL_bn_encrypt(enc_ctx, enc_ct, witness->v, witness->r))
goto err;
ret = ZKP_RANGE_CTX_raw_new(transcript, pp, witness, key->pub_key, enc_ctx, enc_ct);
err:
EC_ELGAMAL_CIPHERTEXT_free(enc_ct);
EC_ELGAMAL_CTX_free(enc_ctx);
return ret;
}
void ZKP_RANGE_CTX_free(ZKP_RANGE_CTX *ctx)
{
if (ctx == NULL)
return;
ZKP_RANGE_PUB_PARAM_down_ref(ctx->pp);
ZKP_RANGE_WITNESS_down_ref(ctx->witness);
EC_ELGAMAL_CIPHERTEXT_free(ctx->enc_ct);
EC_ELGAMAL_CTX_free(ctx->enc_ctx);
OPENSSL_clear_free((void *)ctx, sizeof(*ctx));
}
ZKP_RANGE_PROOF *ZKP_RANGE_PROOF_new(void)
{
ZKP_RANGE_PROOF *proof = NULL;
proof = OPENSSL_zalloc(sizeof(*proof));
if (proof == NULL) {
ERR_raise(ERR_LIB_ZKP, ERR_R_MALLOC_FAILURE);
return NULL;
}
return proof;
}
void ZKP_RANGE_PROOF_free(ZKP_RANGE_PROOF *proof)
{
if (proof == NULL)
return;
NIZK_PLAINTEXT_KNOWLEDGE_PROOF_free(proof->ptke_proof);
BP_RANGE_PROOF_free(proof->bp_proof);
}
ZKP_RANGE_PROOF *ZKP_RANGE_PROOF_prove(ZKP_RANGE_CTX *ctx, int left_bound_bits,
int right_bound_bits)
{
EC_GROUP *group;
const EC_POINT *G, *H;
ZKP_RANGE_PUB_PARAM *pp;
ZKP_RANGE_WITNESS *witness;
ZKP_RANGE_PROOF *proof = NULL, *ret = NULL;
NIZK_PUB_PARAM *nizk_pp = NULL;
NIZK_WITNESS *nizk_witness = NULL;
NIZK_PLAINTEXT_KNOWLEDGE_CTX *ptke_ctx = NULL;
BIGNUM *v1 = NULL, *v2 = NULL;
BP_WITNESS *bp_witness = NULL;
BP_VARIABLE *bp_var1 = NULL, *bp_var2 = NULL;
BP_RANGE_CTX *bp_ctx = NULL;
if (ctx == NULL || ctx->pp == NULL || ctx->pp->bp_pp == NULL) {
ERR_raise(ERR_LIB_ZKP, ERR_R_PASSED_NULL_PARAMETER);
return NULL;
}
pp = ctx->pp;
witness = ctx->witness;
group = pp->bp_pp->group;
G = EC_GROUP_get0_generator(group);
H = pp->bp_pp->H;
if (left_bound_bits < 0
|| right_bound_bits < 0
|| left_bound_bits > right_bound_bits
|| right_bound_bits > pp->bp_pp->gens_capacity) {
ERR_raise(ERR_LIB_ZKP, ERR_R_PASSED_INVALID_ARGUMENT);
return NULL;
}
proof = ZKP_RANGE_PROOF_new();
if (proof == NULL)
return NULL;
nizk_pp = NIZK_PUB_PARAM_new(group, G, H);
if (nizk_pp == NULL)
goto err;
nizk_witness = NIZK_WITNESS_new(nizk_pp, witness->r, witness->v);
if (nizk_witness == NULL)
goto err;
ptke_ctx = NIZK_PLAINTEXT_KNOWLEDGE_CTX_new(ctx->transcript, nizk_pp,
nizk_witness, ctx->PK, ctx->enc_ct);
if (ptke_ctx == NULL)
goto err;
proof->ptke_proof = NIZK_PLAINTEXT_KNOWLEDGE_PROOF_prove(ptke_ctx);
if (proof->ptke_proof == NULL) {
ERR_raise(ERR_LIB_ZKP, ZKP_R_RANGE_PROVE_FAILED);
goto err;
}
bp_witness = BP_WITNESS_new(pp->bp_pp);
if (bp_witness == NULL)
goto err;
bp_var1 = BP_VARIABLE_new(NULL, ctx->enc_ct->C2, group);
bp_var2 = BP_VARIABLE_new(NULL, ctx->enc_ct->C2, group);
if (bp_var1 == NULL || bp_var2 == NULL)
goto err;
v1 = BN_dup(ctx->witness->v);
v2 = BN_dup(ctx->witness->v);
if (v1 == NULL || v2 == NULL)
goto err;
if (sk_BIGNUM_push(bp_witness->sk_r, ctx->witness->r) <= 0
|| sk_BIGNUM_push(bp_witness->sk_r, ctx->witness->r) <= 1
|| sk_BIGNUM_push(bp_witness->sk_v, v1) <= 0
|| sk_BIGNUM_push(bp_witness->sk_v, v2) <= 1
|| sk_BP_VARIABLE_push(bp_witness->sk_V, bp_var1) <= 0
|| sk_BP_VARIABLE_push(bp_witness->sk_V, bp_var2) <= 1)
goto err;
if (!zkp_bp_range_witness_adjust(bp_witness, left_bound_bits, right_bound_bits,
pp->bp_pp->gens_capacity, 1, group))
goto err;
bp_ctx = BP_RANGE_CTX_new(pp->bp_pp, bp_witness, ctx->transcript);
if (bp_ctx == NULL)
goto err;
proof->bp_proof = BP_RANGE_PROOF_new_prove(bp_ctx);
if (proof->bp_proof == NULL) {
ERR_raise(ERR_LIB_ZKP, ZKP_R_RANGE_PROVE_FAILED);
goto err;
}
ret = proof;
proof = NULL;
err:
BP_RANGE_CTX_free(bp_ctx);
BN_free(v1);
BN_free(v2);
BP_VARIABLE_free(bp_var1);
BP_VARIABLE_free(bp_var2);
if (bp_witness != NULL) {
sk_BIGNUM_zero(bp_witness->sk_r);
sk_BIGNUM_zero(bp_witness->sk_v);
sk_BP_VARIABLE_zero(bp_witness->sk_V);
BP_WITNESS_free(bp_witness);
}
NIZK_PLAINTEXT_KNOWLEDGE_CTX_free(ptke_ctx);
NIZK_WITNESS_free(nizk_witness);
NIZK_PUB_PARAM_free(nizk_pp);
ZKP_RANGE_PROOF_free(proof);
return ret;
}
int ZKP_RANGE_PROOF_verify(ZKP_RANGE_CTX *ctx, ZKP_RANGE_PROOF *proof,
int left_bound_bits, int right_bound_bits)
{
int ret = 0;
EC_GROUP *group;
const EC_POINT *G, *H;
ZKP_RANGE_PUB_PARAM *pp;
NIZK_PUB_PARAM *nizk_pp = NULL;
NIZK_WITNESS *nizk_witness = NULL;
NIZK_PLAINTEXT_KNOWLEDGE_CTX *ptke_ctx = NULL;
BP_WITNESS *bp_witness = NULL;
BP_VARIABLE *bp_var1 = NULL, *bp_var2 = NULL;
BP_RANGE_CTX *bp_ctx = NULL;
if (ctx == NULL || ctx->pp == NULL || ctx->pp->bp_pp == NULL
|| proof->ptke_proof == NULL || proof->bp_proof == NULL) {
ERR_raise(ERR_LIB_ZKP, ERR_R_PASSED_NULL_PARAMETER);
return ret;
}
pp = ctx->pp;
group = pp->bp_pp->group;
G = EC_GROUP_get0_generator(group);
H = pp->bp_pp->H;
if (left_bound_bits < 0
|| right_bound_bits < 0
|| left_bound_bits > right_bound_bits
|| right_bound_bits > pp->bp_pp->gens_capacity) {
ERR_raise(ERR_LIB_ZKP, ERR_R_PASSED_INVALID_ARGUMENT);
return ret;
}
nizk_pp = NIZK_PUB_PARAM_new(group, G, H);
if (nizk_pp == NULL)
goto err;
ptke_ctx = NIZK_PLAINTEXT_KNOWLEDGE_CTX_new(ctx->transcript, nizk_pp, NULL,
ctx->PK, ctx->enc_ct);
if (ptke_ctx == NULL)
goto err;
if (!NIZK_PLAINTEXT_KNOWLEDGE_PROOF_verify(ptke_ctx, proof->ptke_proof)) {
ERR_raise(ERR_LIB_ZKP, ZKP_R_RANGE_VERIFY_FAILED);
goto err;
}
bp_witness = BP_WITNESS_new(pp->bp_pp);
if (bp_witness == NULL)
goto err;
bp_var1 = BP_VARIABLE_new(NULL, ctx->enc_ct->C2, group);
if (bp_var1 == NULL)
goto err;
bp_var2 = BP_VARIABLE_new(NULL, ctx->enc_ct->C2, group);
if (bp_var2 == NULL)
goto err;
if (sk_BP_VARIABLE_push(bp_witness->sk_V, bp_var1) <= 0
|| sk_BP_VARIABLE_push(bp_witness->sk_V, bp_var2) <= 1)
goto err;
if (!zkp_bp_range_witness_adjust(bp_witness, left_bound_bits, right_bound_bits,
pp->bp_pp->gens_capacity, 0, group))
goto err;
bp_ctx = BP_RANGE_CTX_new(pp->bp_pp, bp_witness, ctx->transcript);
if (bp_ctx == NULL)
goto err;
if (!BP_RANGE_PROOF_verify(bp_ctx, proof->bp_proof)) {
ERR_raise(ERR_LIB_ZKP, ZKP_R_RANGE_VERIFY_FAILED);
goto err;
}
ret = 1;
err:
BP_RANGE_CTX_free(bp_ctx);
BP_VARIABLE_free(bp_var1);
BP_VARIABLE_free(bp_var2);
if (bp_witness != NULL) {
sk_BP_VARIABLE_zero(bp_witness->sk_V);
BP_WITNESS_free(bp_witness);
}
NIZK_PLAINTEXT_KNOWLEDGE_CTX_free(ptke_ctx);
NIZK_WITNESS_free(nizk_witness);
NIZK_PUB_PARAM_free(nizk_pp);
return ret;
}