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

542 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>
#ifdef __bswap_constant_32
# undef __bswap_constant_32
#endif
#define __bswap_constant_32(x) \
((((uint32_t)(x) & 0xff000000u) >> 24) | \
(((uint32_t)(x) & 0x00ff0000u) >> 8) | \
(((uint32_t)(x) & 0x0000ff00u) << 8) | \
(((uint32_t)(x) & 0x000000ffu) << 24))
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
# define n2l(x) (x)
# define l2n(x) (x)
#else
# define n2l(x) __bswap_constant_32(x)
# define l2n(x) __bswap_constant_32(x)
#endif
DEFINE_STACK_OF(EC_POINT)
static int stack_of_point_encode(STACK_OF(EC_POINT) *sk, unsigned char *out,
point_conversion_form_t form,
const EC_GROUP *group, BN_CTX *bn_ctx)
{
int i, n, *q;
size_t point_len;
unsigned char *p = out;
EC_POINT *P;
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_EC_POINT_num(sk);
if (out == NULL)
return 1 + sizeof(n) + n * point_len;
*p++ = form == POINT_CONVERSION_COMPRESSED ? 0x1 : 0;
q = (int *)p;
*q++ = l2n((int)n);
p = (unsigned char *)q;
for (i = 0; i < n; i++) {
P = sk_EC_POINT_value(sk, i);
if (P == NULL)
goto end;
if (EC_POINT_point2oct(group, P, form, p, point_len, bn_ctx) == 0)
goto end;
p += point_len;
}
end:
return p - out;
}
static STACK_OF(EC_POINT) *stack_of_point_decode(const unsigned char *in,
int *len,
const EC_GROUP *group,
BN_CTX *bn_ctx)
{
unsigned char *p = (unsigned char *)in;
int *q, n, i;
size_t point_len;
EC_POINT *P = NULL;
STACK_OF(EC_POINT) *ret = NULL;
point_conversion_form_t form;
if (in == NULL || group == NULL)
return 0;
form = *p == 0x1 ? POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED;
p++;
q = (int *)p;
point_len = EC_POINT_point2oct(group, EC_GROUP_get0_generator(group),
form, NULL, 0, bn_ctx);
n = (int)n2l(*q);
q++;
p = (unsigned char *)q;
if (n < 0) {
return NULL;
}
if (!(ret = sk_EC_POINT_new_reserve(NULL, n)))
return NULL;
for (i = 0; i < n; i++) {
if (!(P = EC_POINT_new(group)))
goto err;
if (!EC_POINT_oct2point(group, P, p, point_len, bn_ctx))
goto err;
if (sk_EC_POINT_push(ret, P) <= 0)
goto err;
p += point_len;
}
if (len != NULL)
*len = p - in;
return ret;
err:
EC_POINT_free(P);
sk_EC_POINT_pop_free(ret, EC_POINT_free);
return NULL;
}
/** Creates a new EC_ELGAMAL_CIPHERTEXT object for EC-ELGAMAL oparations
* \param ctx EC_ELGAMAL_CTX object
* \return newly created EC_ELGAMAL_CIPHERTEXT object or NULL in case of an error
*/
EC_ELGAMAL_CIPHERTEXT *EC_ELGAMAL_CIPHERTEXT_new(EC_ELGAMAL_CTX *ctx)
{
EC_POINT *C1 = NULL, *C2 = NULL;
EC_ELGAMAL_CIPHERTEXT *ciphertext;
ciphertext = OPENSSL_zalloc(sizeof(*ciphertext));
if (ciphertext == NULL) {
ERR_raise(ERR_LIB_EC, ERR_R_MALLOC_FAILURE);
return NULL;
}
C1 = EC_POINT_new(ctx->key->group);
if (C1 == NULL)
goto err;
C2 = EC_POINT_new(ctx->key->group);
if (C2 == NULL)
goto err;
ciphertext->C1 = C1;
ciphertext->C2 = C2;
return ciphertext;
err:
EC_POINT_free(C1);
EC_POINT_free(C2);
OPENSSL_free(ciphertext);
return NULL;
}
EC_ELGAMAL_CIPHERTEXT *EC_ELGAMAL_CIPHERTEXT_dup(const EC_ELGAMAL_CIPHERTEXT *ct,
const EC_GROUP *group)
{
EC_ELGAMAL_CIPHERTEXT *ret;
if (ct == NULL || group == NULL)
return NULL;
ret = OPENSSL_zalloc(sizeof(*ret));
if (ret == NULL) {
ERR_raise(ERR_LIB_EC, ERR_R_MALLOC_FAILURE);
return NULL;
}
ret->C1 = EC_POINT_dup(ct->C1, group);
ret->C2 = EC_POINT_dup(ct->C2, group);
if (ret->C1 == NULL || ret->C2 == NULL)
goto err;
return ret;
err:
EC_ELGAMAL_CIPHERTEXT_free(ret);
return NULL;
}
/** Frees a EC_ELGAMAL_CIPHERTEXT object
* \param ciphertext EC_ELGAMAL_CIPHERTEXT object to be freed
*/
void EC_ELGAMAL_CIPHERTEXT_free(EC_ELGAMAL_CIPHERTEXT *ciphertext)
{
if (ciphertext == NULL)
return;
EC_POINT_free(ciphertext->C1);
EC_POINT_free(ciphertext->C2);
OPENSSL_free(ciphertext);
}
/** Encodes EC_ELGAMAL_CIPHERTEXT to binary
* \param ctx EC_ELGAMAL_CTX 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 ciphertext EC_ELGAMAL_CIPHERTEXT object
* \param compressed Whether to compress the encoding (either 0 or 1)
* \return the length of the encoded octet string or 0 if an error occurred
*/
size_t EC_ELGAMAL_CIPHERTEXT_encode(EC_ELGAMAL_CTX *ctx, unsigned char *out,
size_t size,
const EC_ELGAMAL_CIPHERTEXT *ciphertext,
int compressed)
{
size_t point_len, ret = 0, len, plen;
unsigned char *p = out;
point_conversion_form_t form = compressed ? POINT_CONVERSION_COMPRESSED :
POINT_CONVERSION_UNCOMPRESSED;
BN_CTX *bn_ctx = NULL;
if (ctx == NULL || ctx->key == NULL || ciphertext == NULL ||
ciphertext->C1 == NULL || ciphertext->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 end;
}
point_len = EC_POINT_point2oct(ctx->key->group,
EC_GROUP_get0_generator(ctx->key->group),
form, NULL, 0, bn_ctx);
len = point_len * 2;
if (out == NULL) {
ret = len;
goto end;
}
if (size < len)
goto end;
memset(out, 0, size);
plen = EC_POINT_point2oct(ctx->key->group, ciphertext->C1, form, p,
point_len, bn_ctx);
if (plen == 0)
goto end;
p += point_len;
plen = EC_POINT_point2oct(ctx->key->group, ciphertext->C2, form, p,
point_len, bn_ctx);
if (plen == 0)
goto end;
ret = len;
end:
BN_CTX_free(bn_ctx);
return ret;
}
/** Decodes binary to EC_ELGAMAL_CIPHERTEXT
* \param ctx EC_ELGAMAL_CTX object
* \param r the resulting ciphertext
* \param in Memory buffer with the encoded EC_ELGAMAL_CIPHERTEXT
* object
* \param size The memory size of the in pointer object
* \return 1 on success and 0 otherwise
*/
int EC_ELGAMAL_CIPHERTEXT_decode(EC_ELGAMAL_CTX *ctx, EC_ELGAMAL_CIPHERTEXT *r,
unsigned char *in, size_t size)
{
int ret = 0;
size_t point_len;
unsigned char *p = in, zero[128];
BN_CTX *bn_ctx = NULL;
if (ctx == NULL || ctx->key == NULL || r == NULL || r->C1 == NULL ||
r->C2 == NULL || size % 2 != 0 || in == 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;
}
point_len = size / 2;
memset(zero, 0, sizeof(zero));
if (!EC_POINT_oct2point(ctx->key->group, r->C1, p, point_len, bn_ctx)) {
if (memcmp(p, zero, point_len) != 0 ||
!EC_POINT_set_to_infinity(ctx->key->group, r->C1))
goto err;
}
p += point_len;
if (!EC_POINT_oct2point(ctx->key->group, r->C2, p, point_len, bn_ctx)) {
if (memcmp(p, zero, point_len) != 0 ||
!EC_POINT_set_to_infinity(ctx->key->group, r->C2))
goto err;
}
ret = 1;
err:
BN_CTX_free(bn_ctx);
return ret;
}
/** Creates a new EC_ELGAMAL_MR_CIPHERTEXT object for EC-ELGAMAL oparations
* \param ctx EC_ELGAMAL_MR_CTX object
* \return newly created EC_ELGAMAL_MR_CIPHERTEXT object or NULL in case of an error
*/
EC_ELGAMAL_MR_CIPHERTEXT *EC_ELGAMAL_MR_CIPHERTEXT_new(EC_ELGAMAL_MR_CTX *ctx)
{
STACK_OF(EC_POINT) *sk_C1 = NULL;
EC_ELGAMAL_MR_CIPHERTEXT *ciphertext;
EC_POINT *C2 = NULL;
ciphertext = OPENSSL_zalloc(sizeof(*ciphertext));
if (ciphertext == NULL) {
ERR_raise(ERR_LIB_EC, ERR_R_MALLOC_FAILURE);
return NULL;
}
sk_C1 = sk_EC_POINT_new_null();
if (sk_C1 == NULL)
goto err;
C2 = EC_POINT_new(ctx->group);
if (C2 == NULL)
goto err;
ciphertext->sk_C1 = sk_C1;
ciphertext->C2 = C2;
return ciphertext;
err:
sk_EC_POINT_free(sk_C1);
EC_POINT_free(C2);
OPENSSL_free(ciphertext);
return NULL;
}
EC_ELGAMAL_MR_CIPHERTEXT *EC_ELGAMAL_MR_CIPHERTEXT_dup(const EC_ELGAMAL_MR_CIPHERTEXT *ct,
const EC_GROUP *group)
{
int i;
STACK_OF(EC_POINT) *sk_C1 = NULL;
EC_ELGAMAL_MR_CIPHERTEXT *ret;
EC_POINT *P = NULL, *C1, *C2 = NULL;
if (ct == NULL || group == NULL)
return NULL;
ret = OPENSSL_zalloc(sizeof(*ret));
if (ret == NULL) {
ERR_raise(ERR_LIB_EC, ERR_R_MALLOC_FAILURE);
return NULL;
}
sk_C1 = sk_EC_POINT_new_reserve(NULL, sk_EC_POINT_num(ct->sk_C1));
if (sk_C1 == NULL)
goto err;
for (i = 0; i < sk_EC_POINT_num(ct->sk_C1); i++) {
C1 = sk_EC_POINT_value(ct->sk_C1, i);
if (!(P = EC_POINT_dup(C1, group)))
goto err;
if (sk_EC_POINT_push(sk_C1, P) <= 0)
goto err;
}
C2 = EC_POINT_dup(ct->C2, group);
if (C2 == NULL)
goto err;
ret->sk_C1 = sk_C1;
ret->C2 = C2;
return ret;
err:
sk_EC_POINT_pop_free(sk_C1, EC_POINT_free);
EC_POINT_free(P);
EC_POINT_free(C2);
OPENSSL_free(ret);
return NULL;
}
/** Frees a EC_ELGAMAL_MR_CIPHERTEXT object
* \param ciphertext EC_ELGAMAL_MR_CIPHERTEXT object to be freed
*/
void EC_ELGAMAL_MR_CIPHERTEXT_free(EC_ELGAMAL_MR_CIPHERTEXT *ciphertext)
{
if (ciphertext == NULL)
return;
sk_EC_POINT_pop_free(ciphertext->sk_C1, EC_POINT_free);
EC_POINT_free(ciphertext->C2);
OPENSSL_free(ciphertext);
}
/** Encodes EC_ELGAMAL_MR_CIPHERTEXT to binary
* \param ctx EC_ELGAMAL_MR_CTX 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 ciphertext EC_ELGAMAL_MR_CIPHERTEXT object
* \param compressed Whether to compress the encoding (either 0 or 1)
* \return the length of the encoded octet string or 0 if an error occurred
*/
size_t EC_ELGAMAL_MR_CIPHERTEXT_encode(EC_ELGAMAL_MR_CTX *ctx, unsigned char *out,
size_t size,
const EC_ELGAMAL_MR_CIPHERTEXT *ciphertext,
int compressed)
{
size_t point_len, ret = 0, len, plen;
unsigned char *p = out;
point_conversion_form_t form = compressed ? POINT_CONVERSION_COMPRESSED :
POINT_CONVERSION_UNCOMPRESSED;
BN_CTX *bn_ctx = NULL;
if (ctx == NULL || ctx->group == NULL || ciphertext == NULL ||
ciphertext->sk_C1 == NULL || ciphertext->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 end;
}
point_len = EC_POINT_point2oct(ctx->group,
EC_GROUP_get0_generator(ctx->group),
form, NULL, 0, bn_ctx);
len = stack_of_point_encode(ciphertext->sk_C1, NULL, form, ctx->group, bn_ctx);
len += point_len;
if (out == NULL) {
ret = len;
goto end;
}
if (size < len)
goto end;
memset(out, 0, size);
len = stack_of_point_encode(ciphertext->sk_C1, p, form, ctx->group, bn_ctx);
if (len <= 0)
goto end;
p += len;
plen = EC_POINT_point2oct(ctx->group, ciphertext->C2, form, p,
point_len, bn_ctx);
if (plen == 0)
goto end;
ret = len;
end:
BN_CTX_free(bn_ctx);
return ret;
}
/** Decodes binary to EC_ELGAMAL_MR_CIPHERTEXT
* \param ctx EC_ELGAMAL_MR_CTX object
* \param r the resulting ciphertext
* \param in Memory buffer with the encoded EC_ELGAMAL_MR_CIPHERTEXT
* object
* \param size The memory size of the in pointer object
* \return 1 on success and 0 otherwise
*/
int EC_ELGAMAL_MR_CIPHERTEXT_decode(EC_ELGAMAL_MR_CTX *ctx, EC_ELGAMAL_MR_CIPHERTEXT *r,
unsigned char *in, size_t size)
{
int ret = 0, len = 0;
size_t point_len;
unsigned char *p = in, zero[128];
BN_CTX *bn_ctx = NULL;
STACK_OF(EC_POINT) *sk_C1 = NULL;
point_conversion_form_t form;
if (ctx == NULL || ctx->group == NULL || r == NULL || r->C2 == NULL || in == NULL) {
ERR_raise(ERR_LIB_EC, ERR_R_PASSED_NULL_PARAMETER);
return ret;
}
memset(zero, 0, sizeof(zero));
bn_ctx = BN_CTX_new();
if (bn_ctx == NULL) {
ERR_raise(ERR_LIB_EC, ERR_R_MALLOC_FAILURE);
goto err;
}
form = *p == 0x1 ? POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED;
point_len = EC_POINT_point2oct(ctx->group, EC_GROUP_get0_generator(ctx->group),
form, NULL, 0, bn_ctx);
sk_C1 = stack_of_point_decode(p, &len, ctx->group, bn_ctx);
p += len;
if (!EC_POINT_oct2point(ctx->group, r->C2, p, point_len, bn_ctx)) {
if (memcmp(p, zero, point_len) != 0 ||
!EC_POINT_set_to_infinity(ctx->group, r->C2))
goto err;
}
if (r->sk_C1 != NULL) {
sk_EC_POINT_pop_free(r->sk_C1, EC_POINT_free);
}
r->sk_C1 = sk_C1;
sk_C1 = NULL;
ret = 1;
err:
sk_EC_POINT_pop_free(sk_C1, EC_POINT_free);
BN_CTX_free(bn_ctx);
return ret;
}