416 lines
14 KiB
C
Executable File
416 lines
14 KiB
C
Executable File
/**
|
|
* Copyright (C) 2015-2016 Virgil Security Inc.
|
|
*
|
|
* Lead Maintainer: Virgil Security Inc. <support@virgilsecurity.com>
|
|
*
|
|
* 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) Neither the name of the copyright holder nor the names of its
|
|
* contributors may be used to endorse or promote products derived from
|
|
* this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS 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 AUTHOR 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.
|
|
*
|
|
* This file is part of extension to mbed TLS (https://tls.mbed.org)
|
|
*/
|
|
/**
|
|
* Implementation is based on the standard ISO 18033-2.
|
|
*/
|
|
|
|
#if !defined(MBEDTLS_CONFIG_FILE)
|
|
#include "mbedtls/config.h"
|
|
#else
|
|
#include MBEDTLS_CONFIG_FILE
|
|
#endif
|
|
|
|
#if defined(MBEDTLS_ECIES_C)
|
|
|
|
#include "mbedtls/ecies.h"
|
|
#include "mbedtls/ecies_internal.h"
|
|
#include "mbedtls/ecies_envelope.h"
|
|
|
|
#include "mbedtls/cipher.h"
|
|
#include "mbedtls/md.h"
|
|
#include "mbedtls/kdf.h"
|
|
|
|
#if defined(MBEDTLS_PLATFORM_C)
|
|
#include "mbedtls/platform.h"
|
|
#else
|
|
#include <stdlib.h>
|
|
#define mbedtls_calloc calloc
|
|
#define mbedtls_free free
|
|
#endif
|
|
|
|
#define INVOKE_AND_CHECK(result,invocation) \
|
|
if ((result = invocation) < 0) goto exit;
|
|
|
|
#define ACCUMULATE_AND_CHECK(result, len, invocation) \
|
|
do { \
|
|
if ((result = invocation) < 0) { \
|
|
goto exit; \
|
|
} else { \
|
|
len += result; \
|
|
result = 0; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define ECIES_OCTET_SIZE 8
|
|
#define ECIES_SIZE_TO_OCTETS(size) ((size + 7) / ECIES_OCTET_SIZE)
|
|
|
|
#define ECIES_ENVELOPE_VERSION 0
|
|
|
|
int mbedtls_ecies_encrypt(void *key, const mbedtls_ecies_info_t* info,
|
|
const unsigned char *input, size_t ilen,
|
|
unsigned char *output, size_t *olen, size_t osize,
|
|
int (*f_rng)(void *, unsigned char *, size_t), void *p_rng)
|
|
{
|
|
int result = 0;
|
|
|
|
const mbedtls_md_info_t *md_info = NULL;
|
|
const mbedtls_kdf_info_t *kdf_info = NULL;
|
|
const mbedtls_md_info_t *hmac_info = NULL;
|
|
|
|
void* ephemeral_key = NULL; // MUST be released
|
|
|
|
unsigned char *shared_key = NULL; // MUST be released
|
|
size_t shared_key_len = 0;
|
|
|
|
unsigned char *kdf_value = NULL; // MUST be released
|
|
size_t hmac_len = 0;
|
|
|
|
unsigned char *hmac = NULL; // MUST be released
|
|
size_t kdf_len = 0;
|
|
|
|
unsigned char *cipher_key = NULL; // pointer inside data: kdf_value
|
|
size_t cipher_key_len = 0;
|
|
|
|
unsigned char *cipher_iv = NULL; // MUST be released
|
|
size_t cipher_iv_len = 0;
|
|
|
|
unsigned char *hmac_key = NULL; // pointer inside data: kdf_value
|
|
size_t hmac_key_len = 0;
|
|
|
|
mbedtls_cipher_context_t cipher_ctx;
|
|
size_t cipher_block_size = 0;
|
|
size_t cipher_enc_data_len = 0;
|
|
size_t cipher_enc_header_len = 0;
|
|
unsigned char *cipher_enc_data = NULL; // pointer inside data: output
|
|
|
|
if (key == NULL || info == NULL || input == NULL || output == NULL || olen == NULL) {
|
|
return MBEDTLS_ERR_ECIES_BAD_INPUT_DATA;
|
|
}
|
|
|
|
// Init structures.
|
|
*olen = 0;
|
|
|
|
md_info = mbedtls_md_info_from_type(MBEDTLS_ECIES_MD_TYPE);
|
|
kdf_info = mbedtls_kdf_info_from_type(MBEDTLS_ECIES_KDF_TYPE);
|
|
hmac_info = mbedtls_md_info_from_type(MBEDTLS_ECIES_HMAC_TYPE);
|
|
|
|
mbedtls_cipher_init(&cipher_ctx);
|
|
INVOKE_AND_CHECK(result,
|
|
mbedtls_cipher_setup(&cipher_ctx, mbedtls_cipher_info_from_type(MBEDTLS_ECIES_CIPHER_TYPE))
|
|
);
|
|
|
|
cipher_iv_len = mbedtls_cipher_get_iv_size(&cipher_ctx);
|
|
cipher_key_len = ECIES_SIZE_TO_OCTETS(mbedtls_cipher_get_key_bitlen(&cipher_ctx));
|
|
hmac_len = mbedtls_md_get_size(hmac_info);
|
|
hmac_key_len = hmac_len;
|
|
kdf_len = cipher_key_len + hmac_key_len;
|
|
|
|
kdf_value = mbedtls_calloc(1, kdf_len);
|
|
if (kdf_value == NULL) {
|
|
INVOKE_AND_CHECK(result, MBEDTLS_ERR_ECIES_ALLOC_FAILED)
|
|
}
|
|
|
|
cipher_key = kdf_value;
|
|
hmac_key = kdf_value + cipher_key_len;
|
|
|
|
// 1. Generate ephemeral keypair.
|
|
ephemeral_key = info->key_alloc_func();
|
|
if (ephemeral_key == NULL) {
|
|
INVOKE_AND_CHECK(result, MBEDTLS_ERR_ECIES_ALLOC_FAILED)
|
|
}
|
|
INVOKE_AND_CHECK(result,
|
|
info->key_gen_ephemeral_func(key, ephemeral_key, f_rng, p_rng)
|
|
);
|
|
// 2. Compute shared secret key.
|
|
shared_key_len = info->key_get_shared_len_func(key);
|
|
shared_key = mbedtls_calloc(1, shared_key_len);
|
|
if (shared_key == NULL) {
|
|
INVOKE_AND_CHECK(result, MBEDTLS_ERR_ECIES_ALLOC_FAILED)
|
|
}
|
|
INVOKE_AND_CHECK(result,
|
|
info->key_compute_shared_func(key, ephemeral_key, shared_key, shared_key_len, f_rng, p_rng)
|
|
);
|
|
// 3. Derive keys (encryption key and hmac key).
|
|
INVOKE_AND_CHECK(result,
|
|
mbedtls_kdf(kdf_info, md_info, shared_key, shared_key_len,
|
|
kdf_value, kdf_len)
|
|
);
|
|
// 4. Encrypt given message.
|
|
cipher_iv = mbedtls_calloc(1, cipher_iv_len);
|
|
if (cipher_iv == NULL) {
|
|
INVOKE_AND_CHECK(result, MBEDTLS_ERR_ECIES_ALLOC_FAILED)
|
|
}
|
|
INVOKE_AND_CHECK(result,
|
|
f_rng(p_rng, cipher_iv, cipher_iv_len)
|
|
);
|
|
INVOKE_AND_CHECK(result,
|
|
mbedtls_cipher_setkey(&cipher_ctx, cipher_key,
|
|
cipher_key_len * ECIES_OCTET_SIZE, MBEDTLS_ENCRYPT)
|
|
);
|
|
INVOKE_AND_CHECK(result,
|
|
mbedtls_cipher_set_padding_mode(&cipher_ctx, MBEDTLS_ECIES_CIPHER_PADDING)
|
|
);
|
|
INVOKE_AND_CHECK(result,
|
|
mbedtls_cipher_reset(&cipher_ctx)
|
|
);
|
|
cipher_block_size = mbedtls_cipher_get_block_size(&cipher_ctx);
|
|
cipher_enc_data_len = ilen + cipher_block_size;
|
|
if (osize < cipher_enc_data_len) {
|
|
result = MBEDTLS_ERR_ECIES_OUTPUT_TOO_SMALL;
|
|
goto exit;
|
|
}
|
|
cipher_enc_data = output + osize - cipher_enc_data_len;
|
|
INVOKE_AND_CHECK(result,
|
|
mbedtls_cipher_crypt(&cipher_ctx, cipher_iv, cipher_iv_len, input, ilen,
|
|
cipher_enc_data, &cipher_enc_data_len)
|
|
);
|
|
// 5. Get HMAC for encrypted message.
|
|
hmac = mbedtls_calloc(1, hmac_len);
|
|
if (hmac == NULL) {
|
|
INVOKE_AND_CHECK(result, MBEDTLS_ERR_ECIES_ALLOC_FAILED)
|
|
}
|
|
INVOKE_AND_CHECK(result,
|
|
mbedtls_md_hmac(hmac_info, hmac_key, hmac_key_len,
|
|
cipher_enc_data, cipher_enc_data_len, hmac)
|
|
);
|
|
// 6. Write envelope.
|
|
cipher_enc_header_len = 0;
|
|
ACCUMULATE_AND_CHECK(result, cipher_enc_header_len,
|
|
mbedtls_ecies_write_content_info(&cipher_enc_data, output, MBEDTLS_ECIES_CIPHER_TYPE,
|
|
cipher_iv, cipher_iv_len, cipher_enc_data_len)
|
|
);
|
|
ACCUMULATE_AND_CHECK(result, cipher_enc_header_len,
|
|
mbedtls_ecies_write_hmac(&cipher_enc_data, output, mbedtls_md_get_type(hmac_info),
|
|
hmac, hmac_len)
|
|
);
|
|
ACCUMULATE_AND_CHECK(result, cipher_enc_header_len,
|
|
mbedtls_ecies_write_kdf(&cipher_enc_data, output, mbedtls_kdf_get_type(kdf_info),
|
|
mbedtls_md_get_type(md_info))
|
|
);
|
|
ACCUMULATE_AND_CHECK(result, cipher_enc_header_len,
|
|
info->key_write_pub_asn1_func(&cipher_enc_data, output, ephemeral_key)
|
|
);
|
|
ACCUMULATE_AND_CHECK(result, cipher_enc_header_len,
|
|
mbedtls_ecies_write_version(&cipher_enc_data, output, ECIES_ENVELOPE_VERSION)
|
|
);
|
|
ACCUMULATE_AND_CHECK(result, cipher_enc_header_len,
|
|
mbedtls_ecies_write_envelope(&cipher_enc_data, output, cipher_enc_header_len)
|
|
);
|
|
memmove(output, cipher_enc_data, cipher_enc_header_len);
|
|
memset(output + cipher_enc_header_len, 0, osize - cipher_enc_header_len);
|
|
exit:
|
|
*olen = cipher_enc_header_len;
|
|
info->key_free_func(ephemeral_key);
|
|
mbedtls_cipher_free(&cipher_ctx);
|
|
if (shared_key != NULL) {
|
|
mbedtls_free(shared_key);
|
|
}
|
|
if (kdf_value != NULL) {
|
|
mbedtls_free(kdf_value);
|
|
}
|
|
if (cipher_iv != NULL) {
|
|
mbedtls_free(cipher_iv);
|
|
}
|
|
if (hmac != NULL) {
|
|
mbedtls_free(hmac);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
int mbedtls_ecies_decrypt(void *key, const mbedtls_ecies_info_t* info,
|
|
const unsigned char *input, size_t ilen,
|
|
unsigned char *output, size_t *olen, size_t osize,
|
|
int (*f_rng)(void *, unsigned char *, size_t), void *p_rng)
|
|
{
|
|
int result = 0;
|
|
int version = 0;
|
|
|
|
void *ephemeral_key = NULL; // MUST be released
|
|
|
|
mbedtls_md_type_t md_type = MBEDTLS_MD_NONE;
|
|
mbedtls_kdf_type_t kdf_type = MBEDTLS_KDF_NONE;
|
|
mbedtls_md_type_t hmac_type = MBEDTLS_MD_NONE;
|
|
|
|
unsigned char *shared_key = NULL; // MUST be released
|
|
size_t shared_key_len = 0;
|
|
|
|
unsigned char *kdf_value = NULL; // MUST be released
|
|
size_t kdf_len = 0;
|
|
|
|
unsigned char *hmac_base = NULL; // pointer inside data: input
|
|
size_t hmac_base_len = 0;
|
|
|
|
unsigned char *hmac = NULL; // MUST be released
|
|
size_t hmac_len = 0;
|
|
|
|
unsigned char *cipher_key = NULL; // pointer inside data: kdf_value
|
|
size_t cipher_key_len = 0;
|
|
|
|
unsigned char *hmac_key = NULL; // pointer inside data: kdf_value
|
|
size_t hmac_key_len = 0;
|
|
|
|
unsigned char *cipher_iv = NULL; // pointer inside data: input
|
|
size_t cipher_iv_len = 0;
|
|
|
|
mbedtls_cipher_type_t cipher_type = MBEDTLS_CIPHER_NONE;
|
|
mbedtls_cipher_context_t cipher_ctx;
|
|
size_t cipher_enc_data_len = 0;
|
|
size_t cipher_enc_header_len = 0;
|
|
unsigned char *cipher_enc_data = NULL; // pointer inside data: input
|
|
unsigned char *cipher_enc_header = NULL; // pointer inside data: input
|
|
|
|
if (key == NULL || info == NULL || input == NULL || output == NULL || olen == NULL) {
|
|
return MBEDTLS_ERR_ECIES_BAD_INPUT_DATA;
|
|
}
|
|
|
|
// Init structures.
|
|
*olen = 0;
|
|
mbedtls_cipher_init(&cipher_ctx);
|
|
cipher_enc_header = (unsigned char *)input;
|
|
|
|
ephemeral_key = info->key_alloc_func();
|
|
if (ephemeral_key == NULL) {
|
|
INVOKE_AND_CHECK(result, MBEDTLS_ERR_ECIES_ALLOC_FAILED)
|
|
}
|
|
|
|
// Read envelope.
|
|
INVOKE_AND_CHECK(result,
|
|
mbedtls_ecies_read_envelope(&cipher_enc_header, input + ilen,
|
|
&cipher_enc_header_len)
|
|
);
|
|
INVOKE_AND_CHECK(result,
|
|
mbedtls_ecies_read_version(&cipher_enc_header, input + ilen, &version)
|
|
);
|
|
if (version != ECIES_ENVELOPE_VERSION) {
|
|
result = MBEDTLS_ERR_ECIES_MALFORMED_DATA;
|
|
goto exit;
|
|
}
|
|
INVOKE_AND_CHECK(result,
|
|
info->key_read_pub_asn1_func(&cipher_enc_header, input + ilen, ephemeral_key)
|
|
);
|
|
INVOKE_AND_CHECK(result,
|
|
mbedtls_ecies_read_kdf(&cipher_enc_header, input + ilen, &kdf_type, &md_type)
|
|
);
|
|
INVOKE_AND_CHECK(result,
|
|
mbedtls_ecies_read_hmac(&cipher_enc_header, input + ilen, &hmac_type,
|
|
&hmac_base, &hmac_base_len)
|
|
);
|
|
INVOKE_AND_CHECK(result,
|
|
mbedtls_ecies_read_content_info(&cipher_enc_header, input + ilen, &cipher_type,
|
|
&cipher_iv, &cipher_iv_len, &cipher_enc_data,
|
|
&cipher_enc_data_len)
|
|
);
|
|
|
|
INVOKE_AND_CHECK(result,
|
|
mbedtls_cipher_setup(&cipher_ctx, mbedtls_cipher_info_from_type(cipher_type))
|
|
);
|
|
cipher_key_len = ECIES_SIZE_TO_OCTETS(mbedtls_cipher_get_key_bitlen(&cipher_ctx));
|
|
hmac_len = mbedtls_md_get_size(mbedtls_md_info_from_type(hmac_type));
|
|
hmac_key_len = hmac_len;
|
|
kdf_len = cipher_key_len + hmac_key_len;
|
|
kdf_value = mbedtls_calloc(1, kdf_len);
|
|
if (kdf_value == NULL) {
|
|
INVOKE_AND_CHECK(result, MBEDTLS_ERR_ECIES_ALLOC_FAILED)
|
|
}
|
|
cipher_key = kdf_value;
|
|
hmac_key = kdf_value + cipher_key_len;
|
|
hmac = mbedtls_calloc(1, hmac_len);
|
|
if (hmac == NULL) {
|
|
INVOKE_AND_CHECK(result, MBEDTLS_ERR_ECIES_ALLOC_FAILED)
|
|
}
|
|
|
|
// 1. Compute shared secret key.
|
|
shared_key_len = info->key_get_shared_len_func(key);
|
|
shared_key = mbedtls_calloc(1, shared_key_len);
|
|
if (shared_key == NULL) {
|
|
INVOKE_AND_CHECK(result, MBEDTLS_ERR_ECIES_ALLOC_FAILED)
|
|
}
|
|
INVOKE_AND_CHECK(result,
|
|
info->key_compute_shared_func(ephemeral_key, key, shared_key, shared_key_len, f_rng, p_rng)
|
|
);
|
|
// 2. Derive keys (encryption key and hmac key).
|
|
INVOKE_AND_CHECK(result,
|
|
mbedtls_kdf(mbedtls_kdf_info_from_type(kdf_type), mbedtls_md_info_from_type(md_type),
|
|
shared_key, shared_key_len, kdf_value, kdf_len)
|
|
);
|
|
// 3. Get HMAC for encrypted message and compare it.
|
|
INVOKE_AND_CHECK(result,
|
|
mbedtls_md_hmac(mbedtls_md_info_from_type(hmac_type), hmac_key, hmac_key_len,
|
|
cipher_enc_data, cipher_enc_data_len, hmac)
|
|
);
|
|
if (hmac_base_len != hmac_len || memcmp(hmac_base, hmac, hmac_len) != 0) {
|
|
result = MBEDTLS_ERR_ECIES_MALFORMED_DATA;
|
|
goto exit;
|
|
}
|
|
// 4. Decrypt given message.
|
|
INVOKE_AND_CHECK(result,
|
|
mbedtls_cipher_setkey(&cipher_ctx, cipher_key,
|
|
cipher_key_len * ECIES_OCTET_SIZE, MBEDTLS_DECRYPT)
|
|
);
|
|
INVOKE_AND_CHECK(result,
|
|
mbedtls_cipher_set_padding_mode(&cipher_ctx, MBEDTLS_ECIES_CIPHER_PADDING)
|
|
);
|
|
INVOKE_AND_CHECK(result,
|
|
mbedtls_cipher_reset(&cipher_ctx)
|
|
);
|
|
if (osize < cipher_enc_data_len) {
|
|
result = MBEDTLS_ERR_ECIES_OUTPUT_TOO_SMALL;
|
|
goto exit;
|
|
}
|
|
INVOKE_AND_CHECK(result,
|
|
mbedtls_cipher_crypt(&cipher_ctx, cipher_iv, cipher_iv_len, cipher_enc_data,
|
|
cipher_enc_data_len, output, olen)
|
|
);
|
|
exit:
|
|
mbedtls_cipher_free(&cipher_ctx);
|
|
info->key_free_func(ephemeral_key);
|
|
if (shared_key != NULL) {
|
|
mbedtls_free(shared_key);
|
|
}
|
|
if (kdf_value != NULL) {
|
|
mbedtls_free(kdf_value);
|
|
}
|
|
if (hmac != NULL) {
|
|
mbedtls_free(hmac);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
#endif /* defined(MBEDTLS_ECIES_C) */
|