gateway2/Tongsuo-8.4.0/crypto/paillier/paillier_crypt.c
2026-07-11 13:40:57 +08:00

344 lines
9.3 KiB
C

/*
* Copyright 2022 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 "paillier_local.h"
/** Encrypts an Integer with additadive homomorphic Paillier
* \param ctx PAILLIER_CTX object.
* \param r PAILLIER_CIPHERTEXT object that stores the result of
* the encryption
* \param m The plaintext integer to be encrypted
* \return 1 on success and 0 otherwise
*/
int PAILLIER_encrypt(PAILLIER_CTX *ctx, PAILLIER_CIPHERTEXT *out, int32_t m)
{
int ret = 0;
PAILLIER_KEY *key;
BN_CTX *bn_ctx = NULL;
BIGNUM *r, *r_exp_n, *g_exp_m, *bn_plain;
if (ctx == NULL || out == NULL) {
ERR_raise(ERR_LIB_PAILLIER, ERR_R_PASSED_NULL_PARAMETER);
return ret;
}
bn_ctx = BN_CTX_new();
if (bn_ctx == NULL)
goto err;
BN_CTX_start(bn_ctx);
#if !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_BN_METHOD)
if (ctx->engine != NULL && !BN_CTX_set_engine(bn_ctx, ctx->engine))
goto err;
#endif
key = ctx->key;
bn_plain = BN_CTX_get(bn_ctx);
r = BN_CTX_get(bn_ctx);
r_exp_n = BN_CTX_get(bn_ctx);
g_exp_m = BN_CTX_get(bn_ctx);
if (g_exp_m == NULL)
goto err;
BN_set_word(bn_plain, (BN_ULONG)(m > 0 ? m : -m));
BN_set_negative(bn_plain, m < 0 ? 1 : 0);
if (!BN_rand_range(r, key->n))
goto err;
if (!BN_mod_exp(r_exp_n, r, key->n, key->n_square, bn_ctx))
goto err;
if (key->flag & PAILLIER_FLAG_G_OPTIMIZE) {
if (!BN_mul(g_exp_m, bn_plain, key->n, bn_ctx))
goto err;
if (!BN_add_word(g_exp_m, (BN_ULONG)1))
goto err;
if (!BN_mod(g_exp_m, g_exp_m, key->n_square, bn_ctx))
goto err;
} else {
if (!BN_mod_exp(g_exp_m, key->g, bn_plain, key->n_square, bn_ctx))
goto err;
if (m < 0 && !BN_mod_inverse(g_exp_m, g_exp_m, key->n_square, bn_ctx))
goto err;
}
if (!BN_mod_mul(out->data, g_exp_m, r_exp_n, key->n_square, bn_ctx))
goto err;
ret = 1;
err:
BN_CTX_end(bn_ctx);
BN_CTX_free(bn_ctx);
return ret;
}
/** Decrypts the ciphertext
* \param ctx PAILLIER_CTX object
* \param r The resulting plaintext integer
* \param c PAILLIER_CIPHERTEXT object to be decrypted
* \return 1 on success and 0 otherwise
*/
int PAILLIER_decrypt(PAILLIER_CTX *ctx, int32_t *out, PAILLIER_CIPHERTEXT *c)
{
int ret = 0;
int32_t result;
char *p = NULL;
PAILLIER_KEY *key;
BN_CTX *bn_ctx = NULL;
BIGNUM *c_exp_lambda, *l_ret, *bn_out;
if (ctx == NULL || out == NULL || c == NULL) {
ERR_raise(ERR_LIB_PAILLIER, ERR_R_PASSED_NULL_PARAMETER);
return ret;
}
bn_ctx = BN_CTX_new();
if (bn_ctx == NULL)
goto err;
BN_CTX_start(bn_ctx);
#if !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_BN_METHOD)
if (ctx->engine != NULL && !BN_CTX_set_engine(bn_ctx, ctx->engine))
goto err;
#endif
key = ctx->key;
bn_out = BN_CTX_get(bn_ctx);
c_exp_lambda = BN_CTX_get(bn_ctx);
l_ret = BN_CTX_get(bn_ctx);
if (l_ret == NULL)
goto err;
if (!BN_mod_exp(c_exp_lambda, c->data, key->lambda, key->n_square, bn_ctx))
goto err;
if (!paillier_l_func(l_ret, c_exp_lambda, key->n, bn_ctx))
goto err;
if (!BN_mod_mul(bn_out, l_ret, key->u, key->n, bn_ctx))
goto err;
if (BN_cmp(bn_out, ctx->threshold) == 1) {
if (!BN_sub(bn_out, bn_out, key->n))
goto err;
}
p = BN_bn2dec(bn_out);
if (p == NULL)
goto err;
result = atoi(p);
if (result == 0 && *p != '0')
goto err;
*out = result;
ret = 1;
err:
OPENSSL_free(p);
BN_CTX_end(bn_ctx);
BN_CTX_free(bn_ctx);
return ret;
}
/** Adds two paillier ciphertext and stores it in r:
* E(r) = E(c1 + c2) = E(c1) * E(c2)
* \param ctx PAILLIER_CTX object
* \param r The PAILLIER_CIPHERTEXT object that stores the addition
* result
* \param c1 PAILLIER_CIPHERTEXT object
* \param c2 PAILLIER_CIPHERTEXT object
* \return 1 on success and 0 otherwise
*/
int PAILLIER_add(PAILLIER_CTX *ctx, PAILLIER_CIPHERTEXT *r,
PAILLIER_CIPHERTEXT *c1, PAILLIER_CIPHERTEXT *c2)
{
int ret = 0;
BN_CTX *bn_ctx = NULL;
if (ctx == NULL || r == NULL || c1 == NULL || c2 == NULL) {
ERR_raise(ERR_LIB_PAILLIER, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
bn_ctx = BN_CTX_new();
if (bn_ctx == NULL)
return 0;
#if !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_BN_METHOD)
if (ctx->engine != NULL && !BN_CTX_set_engine(bn_ctx, ctx->engine)) {
BN_CTX_free(bn_ctx);
return ret;
}
#endif
ret = BN_mod_mul(r->data, c1->data, c2->data, ctx->key->n_square, bn_ctx);
BN_CTX_free(bn_ctx);
return ret;
}
/** Add a paillier ciphertext to a plaintext, and stores it in r:
* E(r) = E(c1 + m) = E(c1) * g^m
* \param ctx PAILLIER_CTX object
* \param r The PAILLIER_CIPHERTEXT object that stores the addition
* result
* \param c1 PAILLIER_CIPHERTEXT object
* \param m The plaintext integer to be added
* \return 1 on success and 0 otherwise
*/
int PAILLIER_add_plain(PAILLIER_CTX *ctx, PAILLIER_CIPHERTEXT *r,
PAILLIER_CIPHERTEXT *c, int32_t m)
{
int ret = 0;
BN_CTX *bn_ctx = NULL;
BIGNUM *g_exp_p, *bn_plain;
if (ctx == NULL || r == NULL || c == NULL) {
ERR_raise(ERR_LIB_PAILLIER, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
bn_ctx = BN_CTX_new();
if (bn_ctx == NULL)
goto err;
BN_CTX_start(bn_ctx);
#if !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_BN_METHOD)
if (ctx->engine != NULL && !BN_CTX_set_engine(bn_ctx, ctx->engine))
goto err;
#endif
bn_plain = BN_CTX_get(bn_ctx);
g_exp_p = BN_CTX_get(bn_ctx);
if (g_exp_p == NULL)
goto err;
BN_set_word(bn_plain, (BN_ULONG)(m > 0 ? m : -m));
//BN_set_negative(bn_plain, m < 0 ? 1 : 0);
if (!BN_mod_exp(g_exp_p, ctx->key->g, bn_plain, ctx->key->n_square, bn_ctx))
goto err;
if (m < 0 && !BN_mod_inverse(g_exp_p, g_exp_p, ctx->key->n_square, bn_ctx))
goto err;
ret = BN_mod_mul(r->data, c->data, g_exp_p, ctx->key->n_square, bn_ctx);
err:
BN_CTX_end(bn_ctx);
BN_CTX_free(bn_ctx);
return ret;
}
/** Substracts two paillier ciphertext and stores it in r:
* E(r) = E(c1 - c2) = E(c1) * E(-c2) = E(c1) / E(c2)
* \param ctx PAILLIER_CTX object
* \param r The PAILLIER_CIPHERTEXT object that stores the
* subtraction result
* \param c1 PAILLIER_CIPHERTEXT object
* \param c2 PAILLIER_CIPHERTEXT object
* \return 1 on success and 0 otherwise
*/
int PAILLIER_sub(PAILLIER_CTX *ctx, PAILLIER_CIPHERTEXT *r,
PAILLIER_CIPHERTEXT *c1, PAILLIER_CIPHERTEXT *c2)
{
int ret = 0;
BN_CTX *bn_ctx = NULL;
BIGNUM *inv;
if (ctx == NULL || r == NULL || c1 == NULL || c2 == NULL) {
ERR_raise(ERR_LIB_PAILLIER, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
bn_ctx = BN_CTX_new();
if (bn_ctx == NULL)
return 0;
BN_CTX_start(bn_ctx);
#if !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_BN_METHOD)
if (ctx->engine != NULL && !BN_CTX_set_engine(bn_ctx, ctx->engine))
goto err;
#endif
inv = BN_CTX_get(bn_ctx);
if (inv == NULL)
goto err;
if (!BN_mod_inverse(inv, c2->data, ctx->key->n_square, bn_ctx))
goto err;
ret = BN_mod_mul(r->data, c1->data, inv, ctx->key->n_square, bn_ctx);
err:
BN_CTX_end(bn_ctx);
BN_CTX_free(bn_ctx);
return ret;
}
/** Ciphertext multiplication, computes E(r) = E(c * m) = E(c) ^ m
* \param ctx PAILLIER_CTX object
* \param r The PAILLIER_CIPHERTEXT object that stores the
* multiplication result
* \param c1 PAILLIER_CIPHERTEXT object
* \param m The plaintext integer to be multiplied
* \return 1 on success and 0 otherwise
*/
int PAILLIER_mul(PAILLIER_CTX *ctx, PAILLIER_CIPHERTEXT *r,
PAILLIER_CIPHERTEXT *c, int32_t m)
{
int ret = 0;
BN_CTX *bn_ctx = NULL;
BIGNUM *bn_plain;
if (ctx == NULL || r == NULL || c == NULL) {
ERR_raise(ERR_LIB_PAILLIER, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
bn_ctx = BN_CTX_new();
if (bn_ctx == NULL)
goto err;
BN_CTX_start(bn_ctx);
#if !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_BN_METHOD)
if (ctx->engine != NULL && !BN_CTX_set_engine(bn_ctx, ctx->engine))
goto err;
#endif
bn_plain = BN_CTX_get(bn_ctx);
if (bn_plain == NULL)
goto err;
BN_set_word(bn_plain, (BN_ULONG)(m > 0 ? m : -m));
ret = BN_mod_exp(r->data, c->data, bn_plain, ctx->key->n_square, bn_ctx);
if (m < 0)
ret = BN_mod_inverse(r->data, r->data, ctx->key->n_square, bn_ctx) != NULL;
err:
BN_CTX_end(bn_ctx);
BN_CTX_free(bn_ctx);
return ret;
}