mirror of
https://github.com/guanzhi/GmSSL.git
synced 2026-05-07 08:56:17 +08:00
769 lines
20 KiB
C
769 lines
20 KiB
C
/* ====================================================================
|
|
* Copyright (c) 2007 - 2017 The GmSSL Project. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in
|
|
* the documentation and/or other materials provided with the
|
|
* distribution.
|
|
*
|
|
* 3. All advertising materials mentioning features or use of this
|
|
* software must display the following acknowledgment:
|
|
* "This product includes software developed by the GmSSL Project.
|
|
* (http://gmssl.org/)"
|
|
*
|
|
* 4. The name "GmSSL Project" must not be used to endorse or promote
|
|
* products derived from this software without prior written
|
|
* permission. For written permission, please contact
|
|
* guanzhi1980@gmail.com.
|
|
*
|
|
* 5. Products derived from this software may not be called "GmSSL"
|
|
* nor may "GmSSL" appear in their names without prior written
|
|
* permission of the GmSSL Project.
|
|
*
|
|
* 6. Redistributions of any form whatsoever must retain the following
|
|
* acknowledgment:
|
|
* "This product includes software developed by the GmSSL Project
|
|
* (http://gmssl.org/)"
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE GmSSL PROJECT ``AS IS'' AND ANY
|
|
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE GmSSL PROJECT OR
|
|
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
|
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
* ====================================================================
|
|
*
|
|
*/
|
|
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <openssl/opensslconf.h>
|
|
|
|
#ifndef OPENSSL_NO_AES
|
|
# include <openssl/aes.h>
|
|
#endif
|
|
#include <openssl/hmac.h>
|
|
#ifndef OPENSSL_NO_CMAC
|
|
# include <openssl/cmac.h>
|
|
#endif
|
|
#include <openssl/rand.h>
|
|
#include <openssl/ecdh.h>
|
|
#include <openssl/kdf2.h>
|
|
#include <openssl/ecies.h>
|
|
#include "internal/o_str.h"
|
|
#include "ecies_lcl.h"
|
|
|
|
#define ECIES_ENC_RANDOM_IV 1
|
|
|
|
int ECIES_PARAMS_init_with_type(ECIES_PARAMS *params, int type)
|
|
{
|
|
if (!params) {
|
|
ECerr(EC_F_ECIES_PARAMS_INIT_WITH_TYPE, ERR_R_PASSED_NULL_PARAMETER);
|
|
return 0;
|
|
}
|
|
|
|
switch (type) {
|
|
#ifndef OPENSSL_NO_SHA
|
|
case NID_ecies_with_x9_63_sha1_xor_hmac:
|
|
params->kdf_nid = NID_x9_63_kdf;
|
|
params->kdf_md = EVP_sha1();
|
|
params->enc_nid = NID_xor_in_ecies;
|
|
params->mac_nid = NID_hmac_full_ecies;
|
|
params->hmac_md = EVP_sha1();
|
|
break;
|
|
# ifndef OPENSSL_NO_SHA256
|
|
case NID_ecies_with_x9_63_sha256_xor_hmac:
|
|
params->kdf_nid = NID_x9_63_kdf;
|
|
params->kdf_md = EVP_sha256();
|
|
params->enc_nid = NID_xor_in_ecies;
|
|
params->mac_nid = NID_hmac_full_ecies;
|
|
params->hmac_md = EVP_sha256();
|
|
break;
|
|
# endif
|
|
#endif
|
|
default:
|
|
ECerr(EC_F_ECIES_PARAMS_INIT_WITH_TYPE, EC_R_INVALID_ECIES_PARAMS);
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int ECIES_PARAMS_init_with_recommended(ECIES_PARAMS *param)
|
|
{
|
|
if (!param) {
|
|
ECerr(EC_F_ECIES_PARAMS_INIT_WITH_RECOMMENDED,
|
|
ERR_R_PASSED_NULL_PARAMETER);
|
|
return 0;
|
|
}
|
|
|
|
memset(param, 0, sizeof(*param));
|
|
#ifndef OPENSSL_NO_SHA
|
|
param->kdf_nid = NID_x9_63_kdf;
|
|
param->kdf_md = EVP_sha256();
|
|
param->enc_nid = NID_xor_in_ecies;
|
|
param->mac_nid = NID_hmac_full_ecies;
|
|
param->hmac_md = EVP_sha256();
|
|
// we should return error when sha256 disabled
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
KDF_FUNC ECIES_PARAMS_get_kdf(const ECIES_PARAMS *param)
|
|
{
|
|
if (!param || !param->kdf_md) {
|
|
ECerr(EC_F_ECIES_PARAMS_GET_KDF, ERR_R_PASSED_NULL_PARAMETER);
|
|
return NULL;
|
|
}
|
|
|
|
switch (param->kdf_nid) {
|
|
case NID_x9_63_kdf:
|
|
return KDF_get_x9_63(param->kdf_md);
|
|
case NID_nist_concatenation_kdf:
|
|
case NID_tls_kdf:
|
|
case NID_ikev2_kdf:
|
|
ECerr(EC_F_ECIES_PARAMS_GET_KDF, EC_R_NOT_IMPLEMENTED);
|
|
return NULL;
|
|
}
|
|
|
|
ECerr(EC_F_ECIES_PARAMS_GET_KDF, EC_R_INVALID_ECIES_PARAMETERS);
|
|
return NULL;
|
|
}
|
|
|
|
int ECIES_PARAMS_get_enc(const ECIES_PARAMS *param, size_t inlen,
|
|
const EVP_CIPHER **enc_cipher, size_t *enckeylen, size_t *ciphertextlen)
|
|
{
|
|
const EVP_CIPHER *cipher = NULL;
|
|
size_t keylen;
|
|
size_t outlen;
|
|
|
|
if (!param || !enc_cipher || !enckeylen || !ciphertextlen) {
|
|
ECerr(EC_F_ECIES_PARAMS_GET_ENC, ERR_R_PASSED_NULL_PARAMETER);
|
|
return 0;
|
|
}
|
|
|
|
switch (param->enc_nid) {
|
|
case NID_xor_in_ecies:
|
|
cipher = NULL;
|
|
keylen = inlen;
|
|
break;
|
|
#ifndef OPENSSL_NO_DES
|
|
case NID_tdes_cbc_in_ecies:
|
|
cipher = EVP_des_ede_cbc();
|
|
break;
|
|
#endif
|
|
#ifndef OPENSSL_NO_AES
|
|
case NID_aes128_cbc_in_ecies:
|
|
cipher = EVP_aes_128_cbc();
|
|
break;
|
|
case NID_aes192_cbc_in_ecies:
|
|
cipher = EVP_aes_192_cbc();
|
|
break;
|
|
case NID_aes256_cbc_in_ecies:
|
|
cipher = EVP_aes_256_cbc();
|
|
break;
|
|
case NID_aes128_ctr_in_ecies:
|
|
cipher = EVP_aes_128_ctr();
|
|
break;
|
|
case NID_aes192_ctr_in_ecies:
|
|
cipher = EVP_aes_192_ctr();
|
|
break;
|
|
case NID_aes256_ctr_in_ecies:
|
|
cipher = EVP_aes_256_ctr();
|
|
break;
|
|
#endif
|
|
default:
|
|
ECerr(EC_F_ECIES_PARAMS_GET_ENC, EC_R_INVALID_ECIES_PARAMETERS);
|
|
return 0;
|
|
}
|
|
|
|
outlen = inlen;
|
|
if (cipher) {
|
|
int blocksize = EVP_CIPHER_block_size(cipher);
|
|
keylen = EVP_CIPHER_key_length(cipher);
|
|
if (ECIES_ENC_RANDOM_IV) {
|
|
outlen += blocksize;
|
|
}
|
|
if (EVP_CIPHER_mode(cipher) == EVP_CIPH_CBC_MODE) {
|
|
outlen += blocksize - inlen % blocksize;
|
|
}
|
|
}
|
|
|
|
*enc_cipher = cipher;
|
|
*enckeylen = keylen;
|
|
*ciphertextlen = outlen;
|
|
|
|
return 1;
|
|
}
|
|
|
|
int ECIES_PARAMS_get_mac(const ECIES_PARAMS *param,
|
|
const EVP_MD **hmac_md, const EVP_CIPHER **cmac_cipher,
|
|
unsigned int *mackeylen, unsigned int *maclen)
|
|
{
|
|
const EVP_CIPHER *cipher = NULL;
|
|
const EVP_MD *md = NULL;
|
|
unsigned int keylen;
|
|
unsigned int outlen;
|
|
|
|
if (!param || !hmac_md || !cmac_cipher || !mackeylen || !maclen) {
|
|
ECerr(EC_F_ECIES_PARAMS_GET_MAC, ERR_R_PASSED_NULL_PARAMETER);
|
|
return 0;
|
|
}
|
|
|
|
switch (param->mac_nid) {
|
|
case NID_hmac_full_ecies:
|
|
if (!(md = param->hmac_md)) {
|
|
ECerr(EC_F_ECIES_PARAMS_GET_MAC,
|
|
EC_R_INVALID_ECIES_PARAMETERS);
|
|
return 0;
|
|
}
|
|
keylen = EVP_MD_size(md);
|
|
outlen = EVP_MD_size(md);
|
|
break;
|
|
case NID_hmac_half_ecies:
|
|
if (!(md = param->hmac_md)) {
|
|
ECerr(EC_F_ECIES_PARAMS_GET_MAC,
|
|
EC_R_INVALID_ECIES_PARAMETERS);
|
|
return 0;
|
|
}
|
|
keylen = EVP_MD_size(md);
|
|
outlen = EVP_MD_size(md)/2;
|
|
break;
|
|
#ifndef OPENSSL_NO_AES
|
|
case NID_cmac_aes128_ecies:
|
|
cipher = EVP_aes_128_ecb();
|
|
break;
|
|
case NID_cmac_aes192_ecies:
|
|
cipher = EVP_aes_192_ecb();
|
|
break;
|
|
case NID_cmac_aes256_ecies:
|
|
cipher = EVP_aes_256_ecb();
|
|
break;
|
|
#endif
|
|
default:
|
|
ECerr(EC_F_ECIES_PARAMS_GET_MAC,
|
|
EC_R_INVALID_ECIES_PARAMETERS);
|
|
return 0;
|
|
}
|
|
|
|
if (cipher) {
|
|
keylen = EVP_CIPHER_key_length(cipher);
|
|
outlen = EVP_CIPHER_block_size(cipher);
|
|
}
|
|
|
|
*hmac_md = md;
|
|
*cmac_cipher = cipher;
|
|
*mackeylen = keylen;
|
|
*maclen = outlen;
|
|
|
|
return 1;
|
|
}
|
|
|
|
int ECIES_CIPHERTEXT_VALUE_ciphertext_length(const ECIES_CIPHERTEXT_VALUE *a)
|
|
{
|
|
return ASN1_STRING_length(a->ciphertext);
|
|
}
|
|
|
|
ECIES_CIPHERTEXT_VALUE *ECIES_do_encrypt(const ECIES_PARAMS *param,
|
|
const unsigned char *in, size_t inlen, EC_KEY *ec_key)
|
|
{
|
|
int e = 1;
|
|
KDF_FUNC kdf_func;
|
|
const EVP_CIPHER *enc_cipher = NULL;
|
|
size_t enckeylen, ciphertextlen;
|
|
const EVP_MD *hmac_md = NULL;
|
|
const EVP_CIPHER *mac_cipher = NULL;
|
|
unsigned int mackeylen, maclen;
|
|
ECIES_CIPHERTEXT_VALUE *ret = NULL;
|
|
const EC_GROUP *group = EC_KEY_get0_group(ec_key);
|
|
EC_KEY *ephem_key = NULL;
|
|
int point_form = POINT_CONVERSION_COMPRESSED;
|
|
unsigned char *sharekey = NULL;
|
|
unsigned int sharekeylen;
|
|
unsigned char *enckey, *mackey;
|
|
unsigned char mac[EVP_MAX_MD_SIZE];
|
|
size_t len;
|
|
|
|
if (!param || !in || !inlen || !ec_key || !group) {
|
|
ECerr(EC_F_ECIES_DO_ENCRYPT, ERR_R_PASSED_NULL_PARAMETER);
|
|
return 0;
|
|
}
|
|
|
|
/* parse parameters */
|
|
if (!(kdf_func = ECIES_PARAMS_get_kdf(param))) {
|
|
ECerr(EC_F_ECIES_DO_ENCRYPT, EC_R_INVALID_ECIES_PARAMETERS);
|
|
goto end;
|
|
}
|
|
if (!ECIES_PARAMS_get_enc(param, inlen, &enc_cipher, &enckeylen, &ciphertextlen)) {
|
|
ECerr(EC_F_ECIES_DO_ENCRYPT, EC_R_INVALID_ECIES_PARAMETERS);
|
|
goto end;
|
|
}
|
|
if (!ECIES_PARAMS_get_mac(param, &hmac_md, &mac_cipher, &mackeylen, &maclen)) {
|
|
ECerr(EC_F_ECIES_DO_ENCRYPT, EC_R_INVALID_ECIES_PARAMETERS);
|
|
goto end;
|
|
}
|
|
|
|
/* malloc ciphertext value */
|
|
if (!(ret = ECIES_CIPHERTEXT_VALUE_new())) {
|
|
ECerr(EC_F_ECIES_DO_ENCRYPT, ERR_R_MALLOC_FAILURE);
|
|
return 0;
|
|
}
|
|
|
|
/* generate ephem keypair */
|
|
if (!(ephem_key = EC_KEY_new())) {
|
|
ECerr(EC_F_ECIES_DO_ENCRYPT, ERR_R_MALLOC_FAILURE);
|
|
goto end;
|
|
}
|
|
if (!EC_KEY_set_group(ephem_key, group)) {
|
|
ECerr(EC_F_ECIES_DO_ENCRYPT, ERR_R_EC_LIB);
|
|
goto end;
|
|
}
|
|
if (!EC_KEY_generate_key(ephem_key)) {
|
|
ECerr(EC_F_ECIES_DO_ENCRYPT, ERR_R_EC_LIB);
|
|
goto end;
|
|
}
|
|
|
|
/* output ephem_point */
|
|
len = EC_POINT_point2oct(group, EC_KEY_get0_public_key(ephem_key),
|
|
point_form, NULL, 0, NULL);
|
|
if (!ASN1_OCTET_STRING_set(ret->ephem_point, NULL, len)) {
|
|
ECerr(EC_F_ECIES_DO_ENCRYPT, ERR_R_ASN1_LIB);
|
|
goto end;
|
|
}
|
|
if (EC_POINT_point2oct(group, EC_KEY_get0_public_key(ephem_key),
|
|
point_form, ret->ephem_point->data, len, NULL) <= 0) {
|
|
ECerr(EC_F_ECIES_DO_ENCRYPT, ERR_R_EC_LIB);
|
|
goto end;
|
|
}
|
|
|
|
/* ecdh to generate enckey and mackey */
|
|
sharekeylen = enckeylen + mackeylen;
|
|
if (!(sharekey = OPENSSL_malloc(sharekeylen))) {
|
|
ECerr(EC_F_ECIES_DO_ENCRYPT, ERR_R_MALLOC_FAILURE);
|
|
goto end;
|
|
}
|
|
if (!ECDH_compute_key(sharekey, sharekeylen,
|
|
EC_KEY_get0_public_key(ec_key), ephem_key,
|
|
kdf_func)) {
|
|
ECerr(EC_F_ECIES_DO_ENCRYPT, EC_R_ECDH_FAILED);
|
|
goto end;
|
|
}
|
|
enckey = sharekey;
|
|
mackey = sharekey + enckeylen;
|
|
|
|
/* encrypt */
|
|
if (!ASN1_OCTET_STRING_set(ret->ciphertext, NULL, ciphertextlen)) {
|
|
ECerr(EC_F_ECIES_DO_ENCRYPT, ERR_R_MALLOC_FAILURE);
|
|
goto end;
|
|
}
|
|
if (enc_cipher) {
|
|
EVP_CIPHER_CTX *cipher_ctx = NULL;
|
|
unsigned char ivbuf[EVP_MAX_IV_LENGTH];
|
|
unsigned char *iv, *pout;
|
|
unsigned int ivlen, len;
|
|
|
|
ivlen = EVP_CIPHER_iv_length(enc_cipher);
|
|
if (ECIES_ENC_RANDOM_IV) {
|
|
iv = ret->ciphertext->data;
|
|
pout = ret->ciphertext->data + ivlen;
|
|
RAND_bytes(iv, ivlen);
|
|
} else {
|
|
iv = ivbuf;
|
|
pout = ret->ciphertext->data;
|
|
memset(iv, 0, ivlen);
|
|
}
|
|
|
|
if (!(cipher_ctx = EVP_CIPHER_CTX_new())) {
|
|
ECerr(EC_F_ECIES_DO_ENCRYPT, ERR_R_MALLOC_FAILURE);
|
|
goto end;
|
|
}
|
|
if (!EVP_EncryptInit(cipher_ctx, enc_cipher, enckey, iv)) {
|
|
ECerr(EC_F_ECIES_DO_ENCRYPT, EC_R_ENCRYPT_FAILED);
|
|
EVP_CIPHER_CTX_free(cipher_ctx);
|
|
goto end;
|
|
}
|
|
if (!EVP_EncryptUpdate(cipher_ctx, pout, (int *)&len, in, inlen)) {
|
|
ECerr(EC_F_ECIES_DO_ENCRYPT, EC_R_ENCRYPT_FAILED);
|
|
EVP_CIPHER_CTX_free(cipher_ctx);
|
|
goto end;
|
|
}
|
|
pout += len;
|
|
if (!EVP_EncryptFinal(cipher_ctx, pout, (int *)&len)) {
|
|
ECerr(EC_F_ECIES_DO_ENCRYPT, EC_R_ENCRYPT_FAILED);
|
|
goto end;
|
|
}
|
|
pout += len;
|
|
|
|
OPENSSL_assert(pout - ret->ciphertext->data == ciphertextlen);
|
|
|
|
} else {
|
|
unsigned int i;
|
|
for (i = 0; i < ret->ciphertext->length; i++) {
|
|
ret->ciphertext->data[i] = in[i] ^ enckey[i];
|
|
}
|
|
}
|
|
|
|
/* generate mac */
|
|
if (mac_cipher) {
|
|
CMAC_CTX *cmac_ctx;
|
|
if (!(cmac_ctx = CMAC_CTX_new())) {
|
|
ECerr(EC_F_ECIES_DO_ENCRYPT, ERR_R_MALLOC_FAILURE);
|
|
goto end;
|
|
}
|
|
if (!CMAC_Init(cmac_ctx, mackey, mackeylen, mac_cipher, NULL)) {
|
|
ECerr(EC_F_ECIES_DO_ENCRYPT, EC_R_CMAC_INIT_FAILURE);
|
|
CMAC_CTX_free(cmac_ctx);
|
|
goto end;
|
|
}
|
|
if (!CMAC_Update(cmac_ctx, ret->ciphertext->data, ret->ciphertext->length)) {
|
|
ECerr(EC_F_ECIES_DO_ENCRYPT, EC_R_CMAC_UPDATE_FAILURE);
|
|
CMAC_CTX_free(cmac_ctx);
|
|
goto end;
|
|
}
|
|
len = sizeof(mac);
|
|
if (!CMAC_Final(cmac_ctx, mac, &len)) {
|
|
ECerr(EC_F_ECIES_DO_ENCRYPT, EC_R_CMAC_FINAL_FAILURE);
|
|
CMAC_CTX_free(cmac_ctx);
|
|
goto end;
|
|
}
|
|
OPENSSL_assert(len == maclen);
|
|
CMAC_CTX_free(cmac_ctx);
|
|
|
|
} else {
|
|
len = sizeof(mac);
|
|
if (!HMAC(param->hmac_md, mackey, mackeylen,
|
|
ret->ciphertext->data, ret->ciphertext->length,
|
|
mac, &maclen)) {
|
|
ECerr(EC_F_ECIES_DO_ENCRYPT, EC_R_HMAC_FAILURE);
|
|
goto end;
|
|
}
|
|
OPENSSL_assert(len == maclen || len/2 == maclen);
|
|
}
|
|
|
|
if (!ASN1_OCTET_STRING_set(ret->mactag, mac, maclen)) {
|
|
ECerr(EC_F_ECIES_DO_ENCRYPT, ERR_R_MALLOC_FAILURE);
|
|
goto end;
|
|
}
|
|
|
|
|
|
e = 0;
|
|
end:
|
|
EC_KEY_free(ephem_key);
|
|
OPENSSL_free(sharekey);
|
|
if (e && ret) {
|
|
ECIES_CIPHERTEXT_VALUE_free(ret);
|
|
ret = NULL;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int ECIES_do_decrypt(const ECIES_PARAMS *param, const ECIES_CIPHERTEXT_VALUE *in,
|
|
unsigned char *out, size_t *outlen, EC_KEY *ec_key)
|
|
{
|
|
int ret = 0;
|
|
KDF_FUNC kdf_func;
|
|
const EVP_CIPHER *enc_cipher = NULL;
|
|
size_t enckeylen, ciphertextlen;
|
|
const EVP_MD *hmac_md = NULL;
|
|
const EVP_CIPHER *mac_cipher = NULL;
|
|
unsigned int mackeylen, maclen;
|
|
const EC_GROUP *group = EC_KEY_get0_group(ec_key);
|
|
EC_POINT *ephem_point = NULL;
|
|
unsigned char *sharekey = NULL;
|
|
unsigned int sharekeylen;
|
|
unsigned char *enckey, *mackey;
|
|
unsigned char mac[EVP_MAX_MD_SIZE];
|
|
size_t len;
|
|
|
|
if (!param || !in || !outlen || !ec_key || !group) {
|
|
ECerr(EC_F_ECIES_DO_DECRYPT, ERR_R_PASSED_NULL_PARAMETER);
|
|
return 0;
|
|
}
|
|
|
|
if (!out) {
|
|
*outlen = in->ciphertext->length;
|
|
return 1;
|
|
}
|
|
if (*outlen < in->ciphertext->length) {
|
|
ECerr(EC_F_ECIES_DO_DECRYPT, EC_R_BUFFER_TOO_SMALL);
|
|
return 0;
|
|
}
|
|
|
|
/* parse parameters */
|
|
if (!(kdf_func = ECIES_PARAMS_get_kdf(param))) {
|
|
ECerr(EC_F_ECIES_DO_DECRYPT, EC_R_INVALID_ECIES_PARAMETERS);
|
|
goto end;
|
|
}
|
|
if (!ECIES_PARAMS_get_enc(param, in->ciphertext->length,
|
|
&enc_cipher, &enckeylen, &ciphertextlen)) {
|
|
ECerr(EC_F_ECIES_DO_DECRYPT, EC_R_INVALID_ECIES_PARAMETERS);
|
|
goto end;
|
|
}
|
|
if (!ECIES_PARAMS_get_mac(param, &hmac_md, &mac_cipher, &mackeylen, &maclen)) {
|
|
ECerr(EC_F_ECIES_DO_DECRYPT, EC_R_INVALID_ECIES_PARAMETERS);
|
|
goto end;
|
|
}
|
|
|
|
/* parse ephem_point */
|
|
if (!in->ephem_point || !in->ephem_point->data || in->ephem_point->length <= 0) {
|
|
ECerr(EC_F_ECIES_DO_DECRYPT, EC_R_INVALID_ECIES_CIPHERTEXT);
|
|
goto end;
|
|
}
|
|
if (!(ephem_point = EC_POINT_new(group))) {
|
|
ECerr(EC_F_ECIES_DO_DECRYPT, ERR_R_MALLOC_FAILURE);
|
|
goto end;
|
|
}
|
|
if (!EC_POINT_oct2point(group, ephem_point,
|
|
in->ephem_point->data, in->ephem_point->length, NULL)) {
|
|
ECerr(EC_F_ECIES_DO_DECRYPT, EC_R_INVALID_ECIES_CIPHERTEXT);
|
|
goto end;
|
|
}
|
|
|
|
/* compute ecdh, get enckey and mackey */
|
|
sharekeylen = enckeylen + mackeylen;
|
|
if (!(sharekey = OPENSSL_malloc(sharekeylen))) {
|
|
ECerr(EC_F_ECIES_DO_DECRYPT, ERR_R_MALLOC_FAILURE);
|
|
goto end;
|
|
}
|
|
if (!ECDH_compute_key(sharekey, sharekeylen,
|
|
ephem_point, ec_key, kdf_func)) {
|
|
ECerr(EC_F_ECIES_DO_DECRYPT, EC_R_ECDH_FAILURE);
|
|
goto end;
|
|
}
|
|
enckey = sharekey;
|
|
mackey = sharekey + enckeylen;
|
|
|
|
/* generate and verify mac */
|
|
if (!in->mactag || !in->mactag->data) {
|
|
ECerr(EC_F_ECIES_DO_DECRYPT, EC_R_INVALID_ECIES_CIPHERTEXT);
|
|
goto end;
|
|
}
|
|
|
|
if (mac_cipher) {
|
|
CMAC_CTX *cmac_ctx;
|
|
if (!(cmac_ctx = CMAC_CTX_new())) {
|
|
ECerr(EC_F_ECIES_DO_DECRYPT, ERR_R_MALLOC_FAILURE);
|
|
goto end;
|
|
}
|
|
if (!CMAC_Init(cmac_ctx, mackey, mackeylen, mac_cipher, NULL)) {
|
|
ECerr(EC_F_ECIES_DO_DECRYPT, EC_R_CMAC_INIT_FAILURE);
|
|
CMAC_CTX_free(cmac_ctx);
|
|
goto end;
|
|
}
|
|
if (!CMAC_Update(cmac_ctx, in->ciphertext->data, in->ciphertext->length)) {
|
|
ECerr(EC_F_ECIES_DO_DECRYPT, EC_R_CMAC_UPDATE_FAILURE);
|
|
CMAC_CTX_free(cmac_ctx);
|
|
goto end;
|
|
}
|
|
len = sizeof(mac);
|
|
if (!CMAC_Final(cmac_ctx, mac, &len)) {
|
|
ECerr(EC_F_ECIES_DO_DECRYPT, EC_R_CMAC_FINAL_FAILURE);
|
|
CMAC_CTX_free(cmac_ctx);
|
|
goto end;
|
|
}
|
|
OPENSSL_assert(len == maclen);
|
|
CMAC_CTX_free(cmac_ctx);
|
|
|
|
} else {
|
|
unsigned int ulen;
|
|
ulen = sizeof(mac);
|
|
if (!HMAC(param->hmac_md, mackey, mackeylen,
|
|
in->ciphertext->data, in->ciphertext->length,
|
|
mac, &ulen)) {
|
|
ECerr(EC_F_ECIES_DO_DECRYPT, EC_R_GEN_MAC_FAILED);
|
|
goto end;
|
|
}
|
|
len = (size_t)ulen;
|
|
OPENSSL_assert(len == maclen || len/2 == maclen);
|
|
}
|
|
|
|
if (maclen != in->mactag->length) {
|
|
ECerr(EC_F_ECIES_DO_DECRYPT, EC_R_ECIES_VERIFY_MAC_FAILURE);
|
|
goto end;
|
|
}
|
|
if (OPENSSL_memcmp(in->mactag->data, mac, maclen)) {
|
|
ECerr(EC_F_ECIES_DO_DECRYPT, EC_R_ECIES_VERIFY_MAC_FAILURE);
|
|
goto end;
|
|
}
|
|
|
|
/* decrypt */
|
|
if (enc_cipher) {
|
|
EVP_CIPHER_CTX *cipher_ctx = NULL;
|
|
unsigned char ivbuf[EVP_MAX_IV_LENGTH];
|
|
unsigned char *iv, *pin, *pout;
|
|
unsigned int ivlen, inlen;
|
|
int ilen;
|
|
|
|
/* prepare iv */
|
|
ivlen = EVP_CIPHER_iv_length(enc_cipher);
|
|
if (ECIES_ENC_RANDOM_IV) {
|
|
iv = in->ciphertext->data;
|
|
pin = in->ciphertext->data + ivlen;
|
|
if (in->ciphertext->length < ivlen) {
|
|
ECerr(EC_F_ECIES_DO_DECRYPT, EC_R_INVALID_ECIES_CIPHERTEXT);
|
|
goto end;
|
|
}
|
|
inlen = in->ciphertext->length - ivlen;
|
|
} else {
|
|
/* use fixed all-zero iv */
|
|
memset(ivbuf, 0, ivlen);
|
|
iv = ivbuf;
|
|
pin = in->ciphertext->data;
|
|
if (in->ciphertext->length <= 0) {
|
|
ECerr(EC_F_ECIES_DO_DECRYPT, EC_R_INVALID_ECIES_CIPHERTEXT);
|
|
goto end;
|
|
}
|
|
inlen = in->ciphertext->length;
|
|
}
|
|
|
|
/* decrypt */
|
|
if (!(cipher_ctx = EVP_CIPHER_CTX_new())) {
|
|
ECerr(EC_F_ECIES_DO_DECRYPT, ERR_R_MALLOC_FAILURE);
|
|
goto end;
|
|
}
|
|
|
|
if (!EVP_DecryptInit(cipher_ctx, enc_cipher, enckey, iv)) {
|
|
ECerr(EC_F_ECIES_DO_DECRYPT, EC_R_ECIES_DECRYPT_INIT_FAILURE);
|
|
EVP_CIPHER_CTX_free(cipher_ctx);
|
|
goto end;
|
|
}
|
|
pout = out;
|
|
ilen = (int)*outlen; //FIXME: do we need to check it?
|
|
if (!EVP_DecryptUpdate(cipher_ctx, pout, &ilen, pin, inlen)) {
|
|
ECerr(EC_F_ECIES_DO_DECRYPT, EC_R_DECRYPT_FAILED);
|
|
EVP_CIPHER_CTX_free(cipher_ctx);
|
|
goto end;
|
|
}
|
|
pout += ilen;
|
|
if (!EVP_DecryptFinal(cipher_ctx, pout, &ilen)) {
|
|
ECerr(EC_F_ECIES_DO_DECRYPT, EC_R_DECRYPT_FAILED);
|
|
EVP_CIPHER_CTX_free(cipher_ctx);
|
|
goto end;
|
|
}
|
|
pout += ilen;
|
|
EVP_CIPHER_CTX_free(cipher_ctx);
|
|
*outlen = pout - out;
|
|
|
|
} else {
|
|
unsigned int i;
|
|
for (i = 0; i < in->ciphertext->length; i++) {
|
|
out[i] = in->ciphertext->data[i] ^ enckey[i];
|
|
}
|
|
*outlen = in->ciphertext->length;
|
|
}
|
|
|
|
ret = 1;
|
|
end:
|
|
OPENSSL_free(sharekey);
|
|
EC_POINT_free(ephem_point);
|
|
return ret;
|
|
}
|
|
|
|
int ECIES_encrypt(int type, const unsigned char *in, size_t inlen,
|
|
unsigned char *out, size_t *outlen, EC_KEY *ec_key)
|
|
{
|
|
int ret = 0;
|
|
ECIES_PARAMS param;
|
|
ECIES_CIPHERTEXT_VALUE *cv = NULL;
|
|
unsigned char *p = out;
|
|
int len;
|
|
|
|
if (!ECIES_PARAMS_init_with_type(¶m, type)) {
|
|
ECerr(EC_F_ECIES_ENCRYPT, EC_R_INVALID_ENC_PARAM);
|
|
return 0;
|
|
}
|
|
|
|
RAND_seed(in, inlen);
|
|
if (!(cv = ECIES_do_encrypt(¶m, in, inlen, ec_key))) {
|
|
ECerr(EC_F_ECIES_ENCRYPT, EC_R_ENCRYPT_FAILED);
|
|
return 0;
|
|
}
|
|
|
|
if ((len = i2d_ECIES_CIPHERTEXT_VALUE(cv, NULL)) <= 0) {
|
|
ECerr(EC_F_ECIES_ENCRYPT, EC_R_ENCRYPT_FAILED);
|
|
goto end;
|
|
}
|
|
|
|
if (!out) {
|
|
*outlen = (size_t)len;
|
|
ret = 1;
|
|
goto end;
|
|
}
|
|
|
|
if (*outlen < len) {
|
|
ECerr(EC_F_ECIES_ENCRYPT, EC_R_ENCRYPT_FAILED);
|
|
*outlen = (size_t)len;
|
|
goto end;
|
|
}
|
|
|
|
if ((len = i2d_ECIES_CIPHERTEXT_VALUE(cv, &p)) <= 0) {
|
|
ECerr(EC_F_ECIES_ENCRYPT, EC_R_ENCRYPT_FAILED);
|
|
goto end;
|
|
}
|
|
|
|
*outlen = (size_t)len;
|
|
ret = 1;
|
|
|
|
end:
|
|
ECIES_CIPHERTEXT_VALUE_free(cv);
|
|
return ret;
|
|
}
|
|
|
|
int ECIES_decrypt(int type, const unsigned char *in, size_t inlen,
|
|
unsigned char *out, size_t *outlen, EC_KEY *ec_key)
|
|
{
|
|
int ret = 0;
|
|
ECIES_PARAMS param;
|
|
ECIES_CIPHERTEXT_VALUE *cv = NULL;
|
|
|
|
if (!in) {
|
|
ECerr(EC_F_ECIES_DECRYPT, ERR_R_PASSED_NULL_PARAMETER);
|
|
return 0;
|
|
}
|
|
|
|
if (inlen <= 0 || inlen > INT_MAX) {
|
|
ECerr(EC_F_ECIES_DECRYPT, EC_R_INVALID_INPUT_LENGTH);
|
|
return 0;
|
|
}
|
|
|
|
if (!ECIES_PARAMS_init_with_type(¶m, type)) {
|
|
ECerr(EC_F_ECIES_DECRYPT, EC_R_INVALID_ENC_PARAM);
|
|
return 0;
|
|
}
|
|
|
|
if (!(cv = d2i_ECIES_CIPHERTEXT_VALUE(NULL, &in, (long)inlen))) {
|
|
ECerr(EC_F_ECIES_DECRYPT, EC_R_INVALID_ECIES_CIPHERTEXT);
|
|
return 0;
|
|
}
|
|
|
|
if (inlen != i2d_ECIES_CIPHERTEXT_VALUE(cv, NULL)) {
|
|
ECerr(EC_F_ECIES_DECRYPT, EC_R_INVALID_ECIES_CIPHERTEXT);
|
|
goto end;
|
|
}
|
|
|
|
if (!ECIES_do_decrypt(¶m, cv, out, outlen, ec_key)) {
|
|
ECerr(EC_F_ECIES_DECRYPT, EC_R_ENCRYPT_FAILURE);
|
|
goto end;
|
|
}
|
|
|
|
ret = 1;
|
|
end:
|
|
ECIES_CIPHERTEXT_VALUE_free(cv);
|
|
return ret;
|
|
}
|