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

492 lines
15 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 EC_ELGAMAL_MSG_BITS 32
#define EC_ELGAMAL_ECDLP_BABY_BITS 11
#define EC_ELGAMAL_ECDLP_GIANT_BITS (EC_ELGAMAL_MSG_BITS-EC_ELGAMAL_ECDLP_BABY_BITS)
static EC_ELGAMAL_dec_tbl_entry *EC_ELGAMAL_dec_tbl_entry_new(EC_ELGAMAL_CTX *ctx,
EC_POINT *point,
int32_t value);
static void EC_ELGAMAL_dec_tbl_entry_free(EC_ELGAMAL_dec_tbl_entry *entry);
static unsigned long EC_ELGAMAL_dec_tbl_entry_hash(const EC_ELGAMAL_dec_tbl_entry *e)
{
int i = e->key_len;
unsigned char *p = e->key;
while (*p == 0 && i-- > 0)
p++;
return ossl_lh_strcasehash((const char *)p);
}
static int EC_ELGAMAL_dec_tbl_entry_cmp(const EC_ELGAMAL_dec_tbl_entry *a,
const EC_ELGAMAL_dec_tbl_entry *b)
{
if (a->key_len != b->key_len)
return -1;
return memcmp(a->key, b->key, a->key_len);
}
/** Finds the value r with brute force s.t. M=rG
* \param ctx EC_ELGAMAL_CTX object
* \param r The resulting integer
* \param M EC_POINT object
* \return 1 on success and 0 otherwise
*/
int EC_ELGAMAL_dlog_brute(EC_ELGAMAL_CTX *ctx, int32_t *r, EC_POINT *M)
{
int ret = 0;
int64_t i = 1, max = 1LL << EC_ELGAMAL_MAX_BITS;
const EC_POINT *G;
EC_POINT *P = NULL;
BN_CTX *bn_ctx = NULL;
if (EC_POINT_is_at_infinity(ctx->key->group, M)) {
ERR_raise(ERR_LIB_EC, EC_R_POINT_AT_INFINITY);
goto err;
}
bn_ctx = BN_CTX_new();
if (bn_ctx == NULL) {
ERR_raise(ERR_LIB_EC, ERR_R_MALLOC_FAILURE);
goto err;
}
P = EC_POINT_new(ctx->key->group);
if (P == NULL) {
ERR_raise(ERR_LIB_EC, ERR_R_MALLOC_FAILURE);
goto err;
}
G = EC_GROUP_get0_generator(ctx->key->group);
EC_POINT_set_to_infinity(ctx->key->group, P);
for (; i < max; i++) {
if (!EC_POINT_add(ctx->key->group, P, P, G, bn_ctx))
goto err;
if (EC_POINT_cmp(ctx->key->group, P, M, bn_ctx) == 0)
break;
}
if (i >= max) {
ERR_raise(ERR_LIB_EC, EC_R_ELGAMAL_DLOG_FAILED);
goto err;
}
*r = (int32_t)i;
ret = 1;
err:
EC_POINT_free(P);
BN_CTX_free(bn_ctx);
return ret;
}
static
int EC_ELGAMAL_dec_tbl_entries_dlog(EC_ELGAMAL_CTX *ctx, int32_t *r,
LHASH_OF(EC_ELGAMAL_dec_tbl_entry) *entries,
EC_POINT *M, EC_POINT *Q, BN_CTX *bn_ctx)
{
int ret = 0;
int64_t i, max;
EC_POINT *P = NULL;
EC_ELGAMAL_dec_tbl_entry *entry = NULL, *entry_res = NULL;
max = 1L << ctx->decrypt_table->baby_step_bits;
if ((P = EC_POINT_dup(M, ctx->key->group)) == NULL)
goto err;
for (i = 0; i < max; i++) {
entry = EC_ELGAMAL_dec_tbl_entry_new(ctx, P, (int32_t)i);
if (entry == NULL)
goto err;
entry_res = lh_EC_ELGAMAL_dec_tbl_entry_retrieve(entries, entry);
if (entry_res != NULL) {
ret = 1;
*r = (int32_t)(i * ctx->decrypt_table->size + entry_res->value);
break;
}
if (!EC_POINT_add(ctx->key->group, P, P, Q, bn_ctx))
goto err;
EC_ELGAMAL_dec_tbl_entry_free(entry);
entry = NULL;
}
err:
EC_ELGAMAL_dec_tbl_entry_free(entry);
EC_POINT_free(P);
return ret;
}
/** Finds the value r with ecdlp bsgs hashtable.
* \param ctx EC_ELGAMAL_CTX object
* \param r The resulting integer
* \param M EC_POINT object
* \return 1 on success and 0 otherwise
*/
int EC_ELGAMAL_dlog_bsgs(EC_ELGAMAL_CTX *ctx, int32_t *r, EC_POINT *M)
{
int ret = 0;
int32_t result = 0;
EC_POINT *Q = NULL;
EC_ELGAMAL_DECRYPT_TABLE *table = ctx->decrypt_table;
BN_CTX *bn_ctx = NULL;
bn_ctx = BN_CTX_new();
if (bn_ctx == NULL) {
ERR_raise(ERR_LIB_EC, ERR_R_MALLOC_FAILURE);
return ret;
}
if (table->flag == 0 || (table->flag & EC_ELGAMAL_DECRYPT_TABLE_FLAG_NEGATIVE))
ret = EC_ELGAMAL_dec_tbl_entries_dlog(ctx, &result, table->positive_entries,
M, table->mG_inv, bn_ctx);
if (ret) {
*r = result;
} else if (table->flag & (EC_ELGAMAL_DECRYPT_TABLE_FLAG_NEGATIVE |
EC_ELGAMAL_DECRYPT_TABLE_FLAG_NEGATIVE_FIRST |
EC_ELGAMAL_DECRYPT_TABLE_FLAG_NEGATIVE_ONLY)) {
if ((Q = EC_POINT_dup(table->mG_inv, ctx->key->group)) == NULL)
goto err;
if (!EC_POINT_invert(ctx->key->group, Q, bn_ctx))
goto err;
ret = EC_ELGAMAL_dec_tbl_entries_dlog(ctx, &result,
table->negative_entries, M, Q,
bn_ctx);
if (!ret && (table->flag & EC_ELGAMAL_DECRYPT_TABLE_FLAG_NEGATIVE_FIRST))
ret = EC_ELGAMAL_dec_tbl_entries_dlog(ctx, &result, table->positive_entries,
M, table->mG_inv, bn_ctx);
if (ret)
*r = -result;
else
ERR_raise(ERR_LIB_EC, EC_R_ELGAMAL_DLOG_FAILED);
}
err:
EC_POINT_free(Q);
BN_CTX_free(bn_ctx);
return ret;
}
/** Creates a new EC_ELGAMAL_dec_tbl_entry object
* \param ctx EC_ELGAMAL_CTX object
* \param point EC_POINT object
* \return newly created EC_ELGAMAL_dec_tbl_entry object or NULL in case of an error
*/
static EC_ELGAMAL_dec_tbl_entry *EC_ELGAMAL_dec_tbl_entry_new(EC_ELGAMAL_CTX *ctx,
EC_POINT *point,
int32_t value)
{
EC_ELGAMAL_dec_tbl_entry *entry = NULL;
size_t point_size = 0, len = 0;
unsigned char *point_key = NULL;
BN_CTX *bn_ctx = NULL;
bn_ctx = BN_CTX_new();
if (bn_ctx == NULL) {
ERR_raise(ERR_LIB_EC, ERR_R_MALLOC_FAILURE);
goto err;
}
point_size = EC_POINT_point2oct(ctx->key->group, point,
POINT_CONVERSION_COMPRESSED, NULL, 0,
bn_ctx);
if (point_size <= 0)
goto err;
entry = OPENSSL_zalloc(sizeof(*entry));
if (entry == NULL) {
ERR_raise(ERR_LIB_EC, ERR_R_MALLOC_FAILURE);
goto err;
}
point_key = OPENSSL_zalloc(point_size + 1);
if (point_key == NULL) {
ERR_raise(ERR_LIB_EC, ERR_R_MALLOC_FAILURE);
goto err;
}
if ((len = EC_POINT_point2oct(ctx->key->group, point,
POINT_CONVERSION_COMPRESSED, point_key,
point_size, bn_ctx)) != point_size)
goto err;
entry->key_len = (int)point_size;
entry->key = point_key;
entry->value = value;
BN_CTX_free(bn_ctx);
return entry;
err:
OPENSSL_free(point_key);
OPENSSL_free(entry);
BN_CTX_free(bn_ctx);
return NULL;
}
/** Frees a EC_ELGAMAL_dec_tbl_entry object
* \param entry EC_ELGAMAL_dec_tbl_entry object to be freed
*/
static void EC_ELGAMAL_dec_tbl_entry_free(EC_ELGAMAL_dec_tbl_entry *entry)
{
if (entry == NULL)
return;
OPENSSL_free(entry->key);
OPENSSL_free(entry);
}
static int EC_ELGAMAL_dec_table_entries_init(EC_ELGAMAL_CTX *ctx,
LHASH_OF(EC_ELGAMAL_dec_tbl_entry) *entries,
int32_t size, const EC_POINT *G,
BN_CTX *bn_ctx)
{
int ret = 0;
int32_t i;
EC_POINT *P;
EC_GROUP *group;
EC_ELGAMAL_dec_tbl_entry *entry = NULL, *entry_old = NULL;
if (ctx == NULL || ctx->key == NULL) {
ERR_raise(ERR_LIB_EC, ERR_R_PASSED_NULL_PARAMETER);
return ret;
}
group = ctx->key->group;
P = EC_POINT_new(group);
if (P == NULL)
return ret;
EC_POINT_set_to_infinity(group, P);
for (i = 0; i < size; i++) {
entry = EC_ELGAMAL_dec_tbl_entry_new(ctx, P, i);
if (entry == NULL)
goto err;
entry_old = lh_EC_ELGAMAL_dec_tbl_entry_insert(entries, entry);
if (lh_EC_ELGAMAL_dec_tbl_entry_error(entries) && entry_old == NULL)
goto err;
if (entry_old != NULL)
EC_ELGAMAL_dec_tbl_entry_free(entry_old);
entry = NULL;
if (!EC_POINT_add(group, P, P, G, bn_ctx))
goto err;
}
ret = 1;
err:
EC_ELGAMAL_dec_tbl_entry_free(entry);
EC_POINT_free(P);
return ret;
}
/** Creates a new EC_ELGAMAL_DECRYPT_TABLE object
* \param ctx EC_ELGAMAL_CTX object
* \param decrypt_negative Whether negative numbers can be decrypted (1 or 0)
* \return newly created EC_ELGAMAL_DECRYPT_TABLE object or NULL in case of an error
*/
EC_ELGAMAL_DECRYPT_TABLE *EC_ELGAMAL_DECRYPT_TABLE_new(EC_ELGAMAL_CTX *ctx,
int32_t decrypt_negative)
{
return EC_ELGAMAL_DECRYPT_TABLE_new_ex(ctx, EC_ELGAMAL_DECRYPT_TABLE_FLAG_NEGATIVE,
EC_ELGAMAL_ECDLP_BABY_BITS,
EC_ELGAMAL_ECDLP_GIANT_BITS);
}
/** Creates a new EC_ELGAMAL_DECRYPT_TABLE object with some extra paramers
* \param ctx EC_ELGAMAL_CTX object
* \param flag the flag of decrypt table
* \param baby_step_bits baby step exponent/bits
* \param giant_step_bits giant step exponent/bits
* \return newly created EC_ELGAMAL_DECRYPT_TABLE object or NULL in case of an error
*/
EC_ELGAMAL_DECRYPT_TABLE *EC_ELGAMAL_DECRYPT_TABLE_new_ex(EC_ELGAMAL_CTX *ctx,
int32_t flag,
uint32_t baby_step_bits,
uint32_t giant_step_bits)
{
int32_t size;
EC_ELGAMAL_DECRYPT_TABLE *table = NULL;
LHASH_OF(EC_ELGAMAL_dec_tbl_entry) *positive_entries = NULL, *negative_entries = NULL;
EC_GROUP *group;
EC_POINT *mG_inv = NULL, *Q = NULL;
const EC_POINT *G;
BIGNUM *bn_size = NULL;
BN_CTX *bn_ctx = NULL;
if (ctx == NULL || ctx->key == NULL) {
ERR_raise(ERR_LIB_EC, ERR_R_PASSED_NULL_PARAMETER);
return NULL;
}
if (giant_step_bits > 32 || baby_step_bits > 32 ||
(giant_step_bits + baby_step_bits) > 32)
return NULL;
size = 1L << giant_step_bits;
group = ctx->key->group;
bn_ctx = BN_CTX_new();
if (bn_ctx == NULL) {
ERR_raise(ERR_LIB_EC, ERR_R_MALLOC_FAILURE);
goto err;
}
table = OPENSSL_zalloc(sizeof(*table));
if (table == NULL) {
ERR_raise(ERR_LIB_EC, ERR_R_MALLOC_FAILURE);
goto err;
}
table->size = size;
bn_size = BN_CTX_get(bn_ctx);
if (bn_size == NULL) {
ERR_raise(ERR_LIB_EC, ERR_R_MALLOC_FAILURE);
goto err;
}
BN_set_word(bn_size, (BN_ULONG)size);
BN_set_negative(bn_size, 0);
G = EC_GROUP_get0_generator(group);
mG_inv = EC_POINT_new(group);
if (mG_inv == NULL)
goto err;
#ifndef OPENSSL_NO_TWISTED_EC_ELGAMAL
if (ctx->flag == EC_ELGAMAL_FLAG_TWISTED) {
if (!EC_POINT_mul(group, mG_inv, NULL, ctx->h, bn_size, bn_ctx))
goto err;
G = ctx->h;
} else
#endif
if (!EC_POINT_mul(group, mG_inv, bn_size, NULL, NULL, bn_ctx))
goto err;
if (!EC_POINT_invert(group, mG_inv, bn_ctx))
goto err;
positive_entries = lh_EC_ELGAMAL_dec_tbl_entry_new(EC_ELGAMAL_dec_tbl_entry_hash,
EC_ELGAMAL_dec_tbl_entry_cmp);
if (positive_entries == NULL
|| !EC_ELGAMAL_dec_table_entries_init(ctx, positive_entries, size, G, bn_ctx))
goto err;
if (flag & (EC_ELGAMAL_DECRYPT_TABLE_FLAG_NEGATIVE |
EC_ELGAMAL_DECRYPT_TABLE_FLAG_NEGATIVE_FIRST |
EC_ELGAMAL_DECRYPT_TABLE_FLAG_NEGATIVE_ONLY)) {
negative_entries = lh_EC_ELGAMAL_dec_tbl_entry_new(EC_ELGAMAL_dec_tbl_entry_hash,
EC_ELGAMAL_dec_tbl_entry_cmp);
if (negative_entries == NULL)
goto err;
if ((Q = EC_POINT_dup(G, group)) == NULL)
goto err;
if (!EC_POINT_invert(group, Q, bn_ctx))
goto err;
if (!EC_ELGAMAL_dec_table_entries_init(ctx, negative_entries, size, Q, bn_ctx))
goto err;
EC_POINT_free(Q);
table->flag |= EC_ELGAMAL_DECRYPT_TABLE_FLAG_NEGATIVE;
}
table->mG_inv = mG_inv;
table->positive_entries = positive_entries;
table->negative_entries = negative_entries;
table->flag = flag;
table->baby_step_bits = baby_step_bits;
table->giant_step_bits = giant_step_bits;
table->references = 1;
table->lock = CRYPTO_THREAD_lock_new();
BN_CTX_free(bn_ctx);
return table;
err:
lh_EC_ELGAMAL_dec_tbl_entry_doall(positive_entries, EC_ELGAMAL_dec_tbl_entry_free);
lh_EC_ELGAMAL_dec_tbl_entry_free(positive_entries);
lh_EC_ELGAMAL_dec_tbl_entry_doall(negative_entries, EC_ELGAMAL_dec_tbl_entry_free);
lh_EC_ELGAMAL_dec_tbl_entry_free(negative_entries);
EC_POINT_free(Q);
EC_POINT_free(mG_inv);
OPENSSL_free(table);
BN_CTX_free(bn_ctx);
return NULL;
}
/** Frees a EC_ELGAMAL_DECRYPT_TABLE object
* \param table EC_ELGAMAL_DECRYPT_TABLE object to be freed
*/
void EC_ELGAMAL_DECRYPT_TABLE_free(EC_ELGAMAL_DECRYPT_TABLE *table)
{
int i;
if (table == NULL)
return;
CRYPTO_DOWN_REF(&table->references, &i, table->lock);
if (i > 0)
return;
lh_EC_ELGAMAL_dec_tbl_entry_doall(table->positive_entries, EC_ELGAMAL_dec_tbl_entry_free);
lh_EC_ELGAMAL_dec_tbl_entry_free(table->positive_entries);
lh_EC_ELGAMAL_dec_tbl_entry_doall(table->negative_entries, EC_ELGAMAL_dec_tbl_entry_free);
lh_EC_ELGAMAL_dec_tbl_entry_free(table->negative_entries);
EC_POINT_free(table->mG_inv);
CRYPTO_THREAD_lock_free(table->lock);
OPENSSL_free(table);
}
/** Sets a EC_ELGAMAL_DECRYPT_TABLE object for decryption.
* \param ctx EC_ELGAMAL_CTX object
* \param table EC_ELGAMAL_DECRYPT_TABLE object
*/
void EC_ELGAMAL_CTX_set_decrypt_table(EC_ELGAMAL_CTX *ctx,
EC_ELGAMAL_DECRYPT_TABLE *table)
{
int i;
ctx->decrypt_table = table;
CRYPTO_UP_REF(&table->references, &i, table->lock);
}