715 lines
19 KiB
C
715 lines
19 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 "ec_elgamal.h"
|
|
#include <openssl/err.h>
|
|
#include <string.h>
|
|
|
|
DEFINE_STACK_OF(EC_KEY)
|
|
DEFINE_STACK_OF(EC_POINT)
|
|
|
|
/** Creates a new EC_ELGAMAL_CTX object
|
|
* \param key EC_KEY to use
|
|
* \param h EC_POINT object pointer
|
|
* \param flag flag of ctx
|
|
* \return newly created EC_ELGAMAL_CTX object or NULL in case of an error
|
|
*/
|
|
EC_ELGAMAL_CTX *EC_ELGAMAL_CTX_new(EC_KEY *key, const EC_POINT *h, int32_t flag)
|
|
{
|
|
#ifndef OPENSSL_NO_TWISTED_EC_ELGAMAL
|
|
size_t len;
|
|
unsigned char *buf = NULL;
|
|
BN_CTX *bn_ctx = NULL;
|
|
#endif
|
|
EC_ELGAMAL_CTX *ctx = NULL;
|
|
|
|
if (key == NULL) {
|
|
ERR_raise(ERR_LIB_EC, ERR_R_PASSED_NULL_PARAMETER);
|
|
return NULL;
|
|
}
|
|
|
|
ctx = OPENSSL_zalloc(sizeof(*ctx));
|
|
if (ctx == NULL) {
|
|
ERR_raise(ERR_LIB_EC, ERR_R_MALLOC_FAILURE);
|
|
return NULL;
|
|
}
|
|
|
|
#ifndef OPENSSL_NO_TWISTED_EC_ELGAMAL
|
|
if (flag == EC_ELGAMAL_FLAG_TWISTED) {
|
|
bn_ctx = BN_CTX_new();
|
|
if (bn_ctx == NULL) {
|
|
ERR_raise(ERR_LIB_EC, ERR_R_MALLOC_FAILURE);
|
|
goto err;
|
|
}
|
|
|
|
if (h != NULL) {
|
|
if (!(ctx->h = EC_POINT_dup(h, key->group)))
|
|
return 0;
|
|
} else {
|
|
ctx->h = EC_POINT_new(key->group);
|
|
if (ctx->h == NULL) {
|
|
ERR_raise(ERR_LIB_EC, ERR_R_MALLOC_FAILURE);
|
|
goto err;
|
|
}
|
|
|
|
len = EC_POINT_point2oct(key->group, EC_GROUP_get0_generator(key->group),
|
|
POINT_CONVERSION_COMPRESSED, NULL, 0, bn_ctx);
|
|
if (len <= 0)
|
|
goto err;
|
|
|
|
buf = OPENSSL_zalloc(len);
|
|
if (buf == NULL)
|
|
goto err;
|
|
|
|
if (!EC_POINT_point2oct(key->group, EC_GROUP_get0_generator(key->group),
|
|
POINT_CONVERSION_COMPRESSED, buf, len, bn_ctx))
|
|
goto err;
|
|
|
|
if (!EC_POINT_from_string(key->group, ctx->h, buf, len))
|
|
goto err;
|
|
}
|
|
|
|
if (key->priv_key) {
|
|
ctx->pk_inv = BN_new();
|
|
if (ctx->pk_inv == NULL) {
|
|
ERR_raise(ERR_LIB_EC, ERR_R_MALLOC_FAILURE);
|
|
goto err;
|
|
}
|
|
|
|
if (!BN_mod_inverse(ctx->pk_inv, key->priv_key,
|
|
EC_GROUP_get0_order(key->group), bn_ctx))
|
|
goto err;
|
|
}
|
|
|
|
OPENSSL_free(buf);
|
|
BN_CTX_free(bn_ctx);
|
|
}
|
|
#endif
|
|
|
|
EC_KEY_up_ref(key);
|
|
ctx->key = key;
|
|
ctx->flag = flag;
|
|
|
|
return ctx;
|
|
#ifndef OPENSSL_NO_TWISTED_EC_ELGAMAL
|
|
err:
|
|
OPENSSL_free(buf);
|
|
BN_CTX_free(bn_ctx);
|
|
EC_ELGAMAL_CTX_free(ctx);
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
EC_ELGAMAL_CTX *EC_ELGAMAL_CTX_dup(EC_ELGAMAL_CTX *ctx)
|
|
{
|
|
#ifndef OPENSSL_NO_TWISTED_EC_ELGAMAL
|
|
return EC_ELGAMAL_CTX_new(ctx->key, ctx->h, ctx->flag);
|
|
#else
|
|
return EC_ELGAMAL_CTX_new(ctx->key, NULL, ctx->flag);
|
|
#endif
|
|
}
|
|
|
|
/** Frees a EC_ELGAMAL_CTX object
|
|
* \param ctx EC_ELGAMAL_CTX object to be freed
|
|
*/
|
|
void EC_ELGAMAL_CTX_free(EC_ELGAMAL_CTX *ctx)
|
|
{
|
|
if (ctx == NULL)
|
|
return;
|
|
|
|
EC_KEY_free(ctx->key);
|
|
EC_ELGAMAL_DECRYPT_TABLE_free(ctx->decrypt_table);
|
|
#ifndef OPENSSL_NO_TWISTED_EC_ELGAMAL
|
|
EC_POINT_free(ctx->h);
|
|
BN_free(ctx->pk_inv);
|
|
#endif
|
|
OPENSSL_free(ctx);
|
|
}
|
|
|
|
/** Creates a new EC_ELGAMAL_MR_CTX object
|
|
* \param key EC_KEY to use
|
|
* \param flag flag of ctx
|
|
* \return newly created EC_ELGAMAL_MR_CTX object or NULL in case of an error
|
|
*/
|
|
EC_ELGAMAL_MR_CTX *EC_ELGAMAL_MR_CTX_new(STACK_OF(EC_KEY) *keys, const EC_POINT *h,
|
|
int32_t flag)
|
|
{
|
|
#ifndef OPENSSL_NO_TWISTED_EC_ELGAMAL
|
|
size_t len;
|
|
unsigned char *buf = NULL;
|
|
BN_CTX *bn_ctx = NULL;
|
|
#endif
|
|
int i;
|
|
EC_KEY *key;
|
|
EC_GROUP *group;
|
|
EC_ELGAMAL_MR_CTX *ctx = NULL;
|
|
|
|
if (keys == NULL) {
|
|
ERR_raise(ERR_LIB_EC, ERR_R_PASSED_NULL_PARAMETER);
|
|
return NULL;
|
|
}
|
|
|
|
if (sk_EC_KEY_num(keys) == 0) {
|
|
ERR_raise(ERR_LIB_EC, ERR_R_PASSED_INVALID_ARGUMENT);
|
|
return NULL;
|
|
}
|
|
|
|
key = sk_EC_KEY_value(keys, 0);
|
|
group = key->group;
|
|
|
|
ctx = OPENSSL_zalloc(sizeof(*ctx));
|
|
if (ctx == NULL) {
|
|
ERR_raise(ERR_LIB_EC, ERR_R_MALLOC_FAILURE);
|
|
return NULL;
|
|
}
|
|
|
|
if (!(ctx->group = EC_GROUP_dup(group)))
|
|
goto err;
|
|
|
|
ctx->sk_key = sk_EC_KEY_dup(keys);
|
|
if (ctx->sk_key == NULL) {
|
|
ERR_raise(ERR_LIB_EC, ERR_R_MALLOC_FAILURE);
|
|
goto err;
|
|
}
|
|
|
|
for (i = 0; i < sk_EC_KEY_num(keys); i++) {
|
|
key = sk_EC_KEY_value(keys, i);
|
|
if (!ec_point_is_compat(key->pub_key, group))
|
|
goto err;
|
|
|
|
if (!EC_KEY_up_ref(key))
|
|
goto err;
|
|
}
|
|
|
|
ctx->flag = flag;
|
|
|
|
#ifndef OPENSSL_NO_TWISTED_EC_ELGAMAL
|
|
if (flag == EC_ELGAMAL_FLAG_TWISTED) {
|
|
bn_ctx = BN_CTX_new();
|
|
if (bn_ctx == NULL) {
|
|
ERR_raise(ERR_LIB_EC, ERR_R_MALLOC_FAILURE);
|
|
goto err;
|
|
}
|
|
|
|
if (h != NULL) {
|
|
if (!(ctx->h = EC_POINT_dup(h, ctx->group)))
|
|
return 0;
|
|
} else {
|
|
ctx->h = EC_POINT_new(group);
|
|
if (ctx->h == NULL) {
|
|
ERR_raise(ERR_LIB_EC, ERR_R_MALLOC_FAILURE);
|
|
goto err;
|
|
}
|
|
|
|
len = EC_POINT_point2oct(group, EC_GROUP_get0_generator(group),
|
|
POINT_CONVERSION_COMPRESSED, NULL, 0, bn_ctx);
|
|
if (len <= 0)
|
|
goto err;
|
|
|
|
buf = OPENSSL_zalloc(len);
|
|
if (buf == NULL)
|
|
goto err;
|
|
|
|
if (!EC_POINT_point2oct(group, EC_GROUP_get0_generator(group),
|
|
POINT_CONVERSION_COMPRESSED, buf, len, bn_ctx))
|
|
goto err;
|
|
|
|
if (!EC_POINT_from_string(group, ctx->h, buf, len))
|
|
goto err;
|
|
}
|
|
|
|
if (key->priv_key) {
|
|
ctx->pk_inv = BN_new();
|
|
if (ctx->pk_inv == NULL) {
|
|
ERR_raise(ERR_LIB_EC, ERR_R_MALLOC_FAILURE);
|
|
goto err;
|
|
}
|
|
|
|
if (!BN_mod_inverse(ctx->pk_inv, key->priv_key,
|
|
EC_GROUP_get0_order(group), bn_ctx))
|
|
goto err;
|
|
}
|
|
|
|
OPENSSL_free(buf);
|
|
BN_CTX_free(bn_ctx);
|
|
}
|
|
#endif
|
|
|
|
return ctx;
|
|
err:
|
|
#ifndef OPENSSL_NO_TWISTED_EC_ELGAMAL
|
|
OPENSSL_free(buf);
|
|
BN_CTX_free(bn_ctx);
|
|
#endif
|
|
EC_ELGAMAL_MR_CTX_free(ctx);
|
|
return NULL;
|
|
}
|
|
|
|
/** Frees a EC_ELGAMAL_MR_CTX object
|
|
* \param ctx EC_ELGAMAL_MR_CTX object to be freed
|
|
*/
|
|
void EC_ELGAMAL_MR_CTX_free(EC_ELGAMAL_MR_CTX *ctx)
|
|
{
|
|
if (ctx == NULL)
|
|
return;
|
|
|
|
EC_GROUP_free(ctx->group);
|
|
sk_EC_KEY_pop_free(ctx->sk_key, EC_KEY_free);
|
|
EC_ELGAMAL_DECRYPT_TABLE_free(ctx->decrypt_table);
|
|
#ifndef OPENSSL_NO_TWISTED_EC_ELGAMAL
|
|
EC_POINT_free(ctx->h);
|
|
BN_free(ctx->pk_inv);
|
|
#endif
|
|
OPENSSL_free(ctx);
|
|
}
|
|
|
|
/** Encrypts an Integer with additadive homomorphic EC-ElGamal
|
|
* \param ctx EC_ELGAMAL_CTX object.
|
|
* \param r EC_ELGAMAL_CIPHERTEXT object that stores the result of
|
|
* the encryption
|
|
* \param plaintext The plaintext integer to be encrypted
|
|
* \return 1 on success and 0 otherwise
|
|
*/
|
|
int EC_ELGAMAL_encrypt(EC_ELGAMAL_CTX *ctx, EC_ELGAMAL_CIPHERTEXT *r, int32_t plaintext)
|
|
{
|
|
int ret = 0;
|
|
BIGNUM *bn_plain = NULL;
|
|
|
|
if (ctx == NULL || ctx->key == NULL || ctx->key->pub_key == NULL || r == NULL) {
|
|
ERR_raise(ERR_LIB_EC, ERR_R_PASSED_NULL_PARAMETER);
|
|
return ret;
|
|
}
|
|
|
|
bn_plain = BN_new();
|
|
if (bn_plain == NULL)
|
|
return ret;
|
|
|
|
BN_set_word(bn_plain, (BN_ULONG)(plaintext > 0 ? plaintext : -(int64_t)plaintext));
|
|
BN_set_negative(bn_plain, plaintext < 0 ? 1 : 0);
|
|
|
|
ret = EC_ELGAMAL_bn_encrypt(ctx, r, bn_plain, NULL);
|
|
|
|
BN_free(bn_plain);
|
|
return ret;
|
|
}
|
|
|
|
int EC_ELGAMAL_bn_encrypt(EC_ELGAMAL_CTX *ctx, EC_ELGAMAL_CIPHERTEXT *r,
|
|
const BIGNUM *plaintext, const BIGNUM *rand)
|
|
{
|
|
int ret = 0;
|
|
BN_CTX *bn_ctx = NULL;
|
|
BIGNUM *random = NULL;
|
|
|
|
if (ctx == NULL || ctx->key == NULL || ctx->key->pub_key == NULL
|
|
|| r == NULL || plaintext == NULL) {
|
|
ERR_raise(ERR_LIB_EC, ERR_R_PASSED_NULL_PARAMETER);
|
|
return ret;
|
|
}
|
|
|
|
bn_ctx = BN_CTX_new();
|
|
if (bn_ctx == NULL) {
|
|
ERR_raise(ERR_LIB_EC, ERR_R_MALLOC_FAILURE);
|
|
return ret;
|
|
}
|
|
|
|
BN_CTX_start(bn_ctx);
|
|
random = BN_CTX_get(bn_ctx);
|
|
if (random == NULL) {
|
|
ERR_raise(ERR_LIB_EC, ERR_R_MALLOC_FAILURE);
|
|
goto err;
|
|
}
|
|
|
|
if (r->C1 == NULL) {
|
|
r->C1 = EC_POINT_new(ctx->key->group);
|
|
if (r->C1 == NULL)
|
|
goto err;
|
|
}
|
|
|
|
if (r->C2 == NULL) {
|
|
r->C2 = EC_POINT_new(ctx->key->group);
|
|
if (r->C2 == NULL)
|
|
goto err;
|
|
}
|
|
|
|
if (rand == NULL)
|
|
BN_rand_range(random, EC_GROUP_get0_order(ctx->key->group));
|
|
else
|
|
random = (BIGNUM *)rand;
|
|
|
|
#ifndef OPENSSL_NO_TWISTED_EC_ELGAMAL
|
|
if (ctx->flag == EC_ELGAMAL_FLAG_TWISTED) {
|
|
if (!EC_POINT_mul(ctx->key->group, r->C1, NULL, ctx->key->pub_key,
|
|
random, bn_ctx))
|
|
goto err;
|
|
|
|
if (!EC_POINT_mul(ctx->key->group, r->C2, random, ctx->h,
|
|
plaintext, bn_ctx))
|
|
goto err;
|
|
} else {
|
|
#endif
|
|
if (!EC_POINT_mul(ctx->key->group, r->C1, random, NULL, NULL, bn_ctx))
|
|
goto err;
|
|
|
|
if (!EC_POINT_mul(ctx->key->group, r->C2, plaintext, ctx->key->pub_key,
|
|
random, bn_ctx))
|
|
goto err;
|
|
#ifndef OPENSSL_NO_TWISTED_EC_ELGAMAL
|
|
}
|
|
#endif
|
|
|
|
ret = 1;
|
|
|
|
err:
|
|
BN_CTX_end(bn_ctx);
|
|
BN_CTX_free(bn_ctx);
|
|
|
|
if (!ret) {
|
|
EC_POINT_free(r->C1);
|
|
EC_POINT_free(r->C2);
|
|
r->C1 = NULL;
|
|
r->C2 = NULL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/** Encryption with one plaintext for multiple recipients.
|
|
* \param ctx EC_ELGAMAL_CTX object.
|
|
* \param r EC_ELGAMAL_CIPHERTEXT_MR object that stores the result of
|
|
* the encryption
|
|
* \param plaintext The plaintext BIGNUM object to be encrypted
|
|
* \return 1 on success and 0 otherwise
|
|
*/
|
|
int EC_ELGAMAL_MR_encrypt(EC_ELGAMAL_MR_CTX *ctx, EC_ELGAMAL_MR_CIPHERTEXT *r,
|
|
const BIGNUM *plaintext, BIGNUM *rand)
|
|
{
|
|
int ret = 0, i;
|
|
EC_KEY *key;
|
|
BN_CTX *bn_ctx = NULL;
|
|
BIGNUM *random = NULL;
|
|
EC_POINT *C1 = NULL;
|
|
|
|
if (ctx == NULL || ctx->sk_key == NULL || r == NULL || plaintext == NULL) {
|
|
ERR_raise(ERR_LIB_EC, ERR_R_PASSED_NULL_PARAMETER);
|
|
return ret;
|
|
}
|
|
|
|
bn_ctx = BN_CTX_new();
|
|
if (bn_ctx == NULL) {
|
|
ERR_raise(ERR_LIB_EC, ERR_R_MALLOC_FAILURE);
|
|
return ret;
|
|
}
|
|
|
|
BN_CTX_start(bn_ctx);
|
|
random = BN_CTX_get(bn_ctx);
|
|
if (random == NULL) {
|
|
ERR_raise(ERR_LIB_EC, ERR_R_MALLOC_FAILURE);
|
|
goto err;
|
|
}
|
|
|
|
if (r->sk_C1 && sk_EC_POINT_num(r->sk_C1) != 0) {
|
|
sk_EC_POINT_pop_free(r->sk_C1, EC_POINT_free);
|
|
r->sk_C1 = NULL;
|
|
}
|
|
|
|
if (r->sk_C1 == NULL) {
|
|
r->sk_C1 = sk_EC_POINT_new_null();
|
|
if (r->sk_C1 == NULL)
|
|
goto err;
|
|
}
|
|
|
|
if (r->C2 == NULL) {
|
|
r->C2 = EC_POINT_new(ctx->group);
|
|
if (r->C2 == NULL)
|
|
goto err;
|
|
}
|
|
|
|
if (rand == NULL) {
|
|
BN_rand_range(random, EC_GROUP_get0_order(ctx->group));
|
|
rand = random;
|
|
}
|
|
|
|
#ifndef OPENSSL_NO_TWISTED_EC_ELGAMAL
|
|
if (ctx->flag == EC_ELGAMAL_FLAG_TWISTED) {
|
|
for (i = 0; i < sk_EC_KEY_num(ctx->sk_key); i++) {
|
|
key = sk_EC_KEY_value(ctx->sk_key, i);
|
|
|
|
C1 = EC_POINT_new(ctx->group);
|
|
if (C1 == NULL)
|
|
goto err;
|
|
|
|
if (!EC_POINT_mul(ctx->group, C1, NULL, key->pub_key, rand, bn_ctx))
|
|
goto err;
|
|
|
|
if (sk_EC_POINT_push(r->sk_C1, C1) <= 0)
|
|
goto err;
|
|
|
|
C1 = NULL;
|
|
}
|
|
|
|
if (!EC_POINT_mul(ctx->group, r->C2, rand, ctx->h, plaintext, bn_ctx))
|
|
goto err;
|
|
} else {
|
|
#endif
|
|
for (i = 0; i < sk_EC_KEY_num(ctx->sk_key); i++) {
|
|
key = sk_EC_KEY_value(ctx->sk_key, i);
|
|
|
|
C1 = EC_POINT_new(ctx->group);
|
|
if (C1 == NULL)
|
|
goto err;
|
|
|
|
if (!EC_POINT_mul(ctx->group, C1, plaintext, key->pub_key, rand, bn_ctx))
|
|
goto err;
|
|
|
|
if (sk_EC_POINT_push(r->sk_C1, C1) <= 0)
|
|
goto err;
|
|
|
|
C1 = NULL;
|
|
}
|
|
|
|
if (!EC_POINT_mul(ctx->group, r->C2, rand, NULL, NULL, bn_ctx))
|
|
goto err;
|
|
#ifndef OPENSSL_NO_TWISTED_EC_ELGAMAL
|
|
}
|
|
#endif
|
|
|
|
ret = 1;
|
|
|
|
err:
|
|
BN_CTX_end(bn_ctx);
|
|
BN_CTX_free(bn_ctx);
|
|
|
|
if (!ret) {
|
|
EC_POINT_free(C1);
|
|
EC_POINT_free(r->C2);
|
|
sk_EC_POINT_pop_free(r->sk_C1, EC_POINT_free);
|
|
r->sk_C1 = NULL;
|
|
r->C2 = NULL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/** Decrypts the ciphertext
|
|
* \param ctx EC_ELGAMAL_CTX object
|
|
* \param r The resulting plaintext integer
|
|
* \param cihpertext EC_ELGAMAL_CIPHERTEXT object to be decrypted
|
|
* \return 1 on success and 0 otherwise
|
|
*/
|
|
int EC_ELGAMAL_decrypt(EC_ELGAMAL_CTX *ctx, int32_t *r,
|
|
const EC_ELGAMAL_CIPHERTEXT *ciphertext)
|
|
{
|
|
int ret = 0;
|
|
int32_t plaintext = 0;
|
|
EC_POINT *M = NULL;
|
|
BN_CTX *bn_ctx = NULL;
|
|
|
|
if (ctx == NULL || ctx->key == NULL || ctx->key->priv_key == NULL || r == NULL) {
|
|
ERR_raise(ERR_LIB_EC, ERR_R_PASSED_NULL_PARAMETER);
|
|
return ret;
|
|
}
|
|
|
|
M = EC_POINT_new(ctx->key->group);
|
|
if (M == NULL)
|
|
goto err;
|
|
|
|
bn_ctx = BN_CTX_new();
|
|
if (bn_ctx == NULL) {
|
|
ERR_raise(ERR_LIB_EC, ERR_R_MALLOC_FAILURE);
|
|
goto err;
|
|
}
|
|
|
|
BN_CTX_start(bn_ctx);
|
|
#ifndef OPENSSL_NO_TWISTED_EC_ELGAMAL
|
|
if (ctx->flag == EC_ELGAMAL_FLAG_TWISTED) {
|
|
if (!EC_POINT_mul(ctx->key->group, M, NULL, ciphertext->C1,
|
|
ctx->pk_inv, bn_ctx))
|
|
goto err;
|
|
} else {
|
|
#endif
|
|
if (!EC_POINT_mul(ctx->key->group, M, NULL, ciphertext->C1,
|
|
ctx->key->priv_key, bn_ctx))
|
|
goto err;
|
|
#ifndef OPENSSL_NO_TWISTED_EC_ELGAMAL
|
|
}
|
|
#endif
|
|
|
|
if (!EC_POINT_invert(ctx->key->group, M, bn_ctx))
|
|
goto err;
|
|
|
|
if (!EC_POINT_add(ctx->key->group, M, ciphertext->C2, M, bn_ctx))
|
|
goto err;
|
|
|
|
if (ctx->decrypt_table != NULL) {
|
|
if (!EC_ELGAMAL_dlog_bsgs(ctx, &plaintext, M))
|
|
goto err;
|
|
} else {
|
|
if (!EC_ELGAMAL_dlog_brute(ctx, &plaintext, M))
|
|
goto err;
|
|
}
|
|
|
|
*r = plaintext;
|
|
|
|
ret = 1;
|
|
|
|
err:
|
|
BN_CTX_end(bn_ctx);
|
|
BN_CTX_free(bn_ctx);
|
|
EC_POINT_free(M);
|
|
return ret;
|
|
}
|
|
|
|
/** Adds two EC-Elgamal ciphertext and stores it in r (r = c1 + c2).
|
|
* \param ctx EC_ELGAMAL_CTX object
|
|
* \param r The EC_ELGAMAL_CIPHERTEXT object that stores the addition
|
|
* result
|
|
* \param c1 EC_ELGAMAL_CIPHERTEXT object
|
|
* \param c2 EC_ELGAMAL_CIPHERTEXT object
|
|
* \return 1 on success and 0 otherwise
|
|
*/
|
|
int EC_ELGAMAL_add(EC_ELGAMAL_CTX *ctx, EC_ELGAMAL_CIPHERTEXT *r,
|
|
const EC_ELGAMAL_CIPHERTEXT *c1,
|
|
const EC_ELGAMAL_CIPHERTEXT *c2)
|
|
{
|
|
int ret = 0;
|
|
BN_CTX *bn_ctx = NULL;
|
|
|
|
if (ctx == NULL || ctx->key == NULL || r == NULL || c1 == NULL || c2 == NULL) {
|
|
ERR_raise(ERR_LIB_EC, ERR_R_PASSED_NULL_PARAMETER);
|
|
return ret;
|
|
}
|
|
|
|
bn_ctx = BN_CTX_new();
|
|
if (bn_ctx == NULL) {
|
|
ERR_raise(ERR_LIB_EC, ERR_R_MALLOC_FAILURE);
|
|
goto err;
|
|
}
|
|
|
|
if (!EC_POINT_add(ctx->key->group, r->C1, c1->C1, c2->C1, bn_ctx))
|
|
goto err;
|
|
|
|
if (!EC_POINT_add(ctx->key->group, r->C2, c1->C2, c2->C2, bn_ctx))
|
|
goto err;
|
|
|
|
ret = 1;
|
|
|
|
err:
|
|
BN_CTX_free(bn_ctx);
|
|
return ret;
|
|
}
|
|
|
|
/** Substracts two EC-Elgamal ciphertext and stores it in r (r = c1 - c2).
|
|
* \param ctx EC_ELGAMAL_CTX object
|
|
* \param r The EC_ELGAMAL_CIPHERTEXT object that stores the
|
|
* subtraction result
|
|
* \param c1 EC_ELGAMAL_CIPHERTEXT object
|
|
* \param c2 EC_ELGAMAL_CIPHERTEXT object
|
|
* \return 1 on success and 0 otherwise
|
|
*/
|
|
int EC_ELGAMAL_sub(EC_ELGAMAL_CTX *ctx, EC_ELGAMAL_CIPHERTEXT *r,
|
|
const EC_ELGAMAL_CIPHERTEXT *c1,
|
|
const EC_ELGAMAL_CIPHERTEXT *c2)
|
|
{
|
|
int ret = 0;
|
|
BN_CTX *bn_ctx = NULL;
|
|
EC_POINT *C1_inv = NULL, *C2_inv = NULL;
|
|
|
|
if (ctx == NULL || ctx->key == NULL || r == NULL || c1 == NULL || c2 == NULL) {
|
|
ERR_raise(ERR_LIB_EC, ERR_R_PASSED_NULL_PARAMETER);
|
|
return ret;
|
|
}
|
|
|
|
bn_ctx = BN_CTX_new();
|
|
if (bn_ctx == NULL) {
|
|
ERR_raise(ERR_LIB_EC, ERR_R_MALLOC_FAILURE);
|
|
goto err;
|
|
}
|
|
|
|
if ((C1_inv = EC_POINT_dup(c2->C1, ctx->key->group)) == NULL)
|
|
goto err;
|
|
|
|
if ((C2_inv = EC_POINT_dup(c2->C2, ctx->key->group)) == NULL)
|
|
goto err;
|
|
|
|
if (!EC_POINT_invert(ctx->key->group, C1_inv, bn_ctx))
|
|
goto err;
|
|
|
|
if (!EC_POINT_invert(ctx->key->group, C2_inv, bn_ctx))
|
|
goto err;
|
|
|
|
if (!EC_POINT_add(ctx->key->group, r->C1, c1->C1, C1_inv, bn_ctx))
|
|
goto err;
|
|
|
|
if (!EC_POINT_add(ctx->key->group, r->C2, c1->C2, C2_inv, bn_ctx))
|
|
goto err;
|
|
|
|
ret = 1;
|
|
|
|
err:
|
|
EC_POINT_free(C1_inv);
|
|
EC_POINT_free(C2_inv);
|
|
BN_CTX_free(bn_ctx);
|
|
return ret;
|
|
}
|
|
|
|
/** Ciphertext multiplication, computes r = c * m
|
|
* \param ctx EC_ELGAMAL_CTX object
|
|
* \param r The EC_ELGAMAL_CIPHERTEXT object that stores the
|
|
* multiplication result
|
|
* \param c1 EC_ELGAMAL_CIPHERTEXT object
|
|
* \param c2 EC_ELGAMAL_CIPHERTEXT object
|
|
* \return 1 on success and 0 otherwise
|
|
*/
|
|
int EC_ELGAMAL_mul(EC_ELGAMAL_CTX *ctx, EC_ELGAMAL_CIPHERTEXT *r,
|
|
const EC_ELGAMAL_CIPHERTEXT *c, int32_t m)
|
|
{
|
|
int ret = 0;
|
|
BIGNUM *bn_m;
|
|
BN_CTX *bn_ctx = NULL;
|
|
|
|
if (ctx == NULL || ctx->key == NULL || r == NULL || c == NULL) {
|
|
ERR_raise(ERR_LIB_EC, ERR_R_PASSED_NULL_PARAMETER);
|
|
return ret;
|
|
}
|
|
|
|
bn_ctx = BN_CTX_new();
|
|
if (bn_ctx == NULL) {
|
|
ERR_raise(ERR_LIB_EC, ERR_R_MALLOC_FAILURE);
|
|
return ret;
|
|
}
|
|
|
|
BN_CTX_start(bn_ctx);
|
|
|
|
if (m == 0) {
|
|
ret = EC_ELGAMAL_encrypt(ctx, r, 0);
|
|
goto end;
|
|
}
|
|
|
|
bn_m = BN_CTX_get(bn_ctx);
|
|
if (bn_m == NULL) {
|
|
ERR_raise(ERR_LIB_EC, ERR_R_MALLOC_FAILURE);
|
|
goto end;
|
|
}
|
|
BN_set_word(bn_m, (BN_ULONG)(m > 0 ? m : -(int64_t)m));
|
|
BN_set_negative(bn_m, m < 0 ? 1 : 0);
|
|
|
|
if (!EC_POINT_mul(ctx->key->group, r->C1, NULL, c->C1, bn_m, bn_ctx))
|
|
goto end;
|
|
|
|
if (!EC_POINT_mul(ctx->key->group, r->C2, NULL, c->C2, bn_m, bn_ctx))
|
|
goto end;
|
|
|
|
ret = 1;
|
|
|
|
end:
|
|
BN_CTX_end(bn_ctx);
|
|
BN_CTX_free(bn_ctx);
|
|
return ret;
|
|
}
|