corecrypto/ccecies/crypto_test/crypto_test_ecies.c

919 lines
41 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* Copyright (c) (2014-2020) Apple Inc. All rights reserved.
*
* corecrypto is licensed under Apple Inc.s Internal Use License Agreement (which
* is contained in the License.txt file distributed with corecrypto) and only to
* people who accept that license. IMPORTANT: Any license rights granted to you by
* Apple Inc. (if any) are limited to internal use within your organization only on
* devices and computers you own or control, for the sole purpose of verifying the
* security characteristics and correct functioning of the Apple Software. You may
* not, directly or indirectly, redistribute the Apple Software or any portions thereof.
*/
#include "testmore.h"
#include "testbyteBuffer.h"
#include "testccnBuffer.h"
#include <corecrypto/cc_macros.h>
#if (CCECIES == 0)
entryPoint(ccies, "ccies")
#else
#include "crypto_test_ecies.h"
#include "../ccec/crypto_test/crypto_test_ec.h"
#include <corecrypto/ccec.h>
#include <corecrypto/ccec_priv.h>
#include "ccec_internal.h"
#include <corecrypto/ccecies.h>
#include <corecrypto/ccecies_priv.h>
#include "ccecies_internal.h"
#include <corecrypto/ccaes.h>
#include <corecrypto/ccrng_test.h>
#include <corecrypto/ccrng_ecfips_test.h>
#include <corecrypto/ccsha1.h>
#include <corecrypto/ccsha2.h>
static const int verbose = 1;
static const uint8_t shared_info1[] = "test1";
static const uint8_t shared_info2[] = "test2";
static const uint8_t shared_info3[] = "test3";
#define di_SHA1 &ccsha1_eay_di
#define di_SHA224 &ccsha224_ltc_di
#define di_SHA256 &ccsha256_ltc_di
#define di_SHA384 &ccsha384_ltc_di
#define di_SHA512 &ccsha512_ltc_di
const struct ccecies_vector ccecies_aes_gcm_vectors[] = {
#include "../test_vectors/ecies_aes_gcm.inc"
#include "../test_vectors/ecies_aes_gcm_legacy.inc"
// End
{
.di = NULL,
}
};
static int is_zero(size_t nbytes, uint8_t *p)
{
int rc = 0;
for (size_t i = 0; i < nbytes; i++) {
rc |= p[i];
}
return (rc == 0);
}
// Superset of the inputs necessary for ccecies_encrypt_gcm and ccecies_encrypt_gcm_from_shared_secret
// Execute both and verify they behave the same (error or valid cases)
static int test_ccecies_encrypt_gcm(ccec_pub_ctx_t public_key,
const ccecies_gcm_t ecies,
ccec_pub_ctx_t ephemeral_public_key,
size_t shared_secret_nbytes,
const uint8_t *shared_secret,
size_t plaintext_nbytes,
const uint8_t *plaintext,
size_t sharedinfo1_nbytes,
const void *sharedinfo1,
size_t sharedinfo2_nbytes,
const void *sharedinfo2,
size_t *encrypted_blob_nbytes,
uint8_t *encrypted_blob)
{
int status1;
int status2;
// shadow output parameter
byteBuffer encrypted_blob1 = mallocByteBuffer(*encrypted_blob_nbytes);
byteBuffer encrypted_blob2 = mallocByteBuffer(*encrypted_blob_nbytes);
size_t encrypted_blob_nbytes1 = *encrypted_blob_nbytes;
size_t encrypted_blob_nbytes2 = *encrypted_blob_nbytes;
status1 = ccecies_encrypt_gcm(public_key,
ecies,
plaintext_nbytes,
plaintext,
sharedinfo1_nbytes,
sharedinfo1,
sharedinfo2_nbytes,
sharedinfo2,
&encrypted_blob_nbytes1,
encrypted_blob1->bytes);
status2 = ccecies_encrypt_gcm_from_shared_secret(public_key,
ecies,
ephemeral_public_key,
shared_secret_nbytes,
shared_secret,
plaintext_nbytes,
plaintext,
sharedinfo1_nbytes,
sharedinfo1,
sharedinfo2_nbytes,
sharedinfo2,
&encrypted_blob_nbytes2,
encrypted_blob2->bytes);
if (ecies->options & (ECIES_EPH_PUBKEY_IN_SHAREDINFO1 | ECIES_EPH_PUBKEY_AND_SHAREDINFO1)) {
is(status1, status2, "Encrypt same error code");
is(encrypted_blob_nbytes1, encrypted_blob_nbytes2, "Encrypt same output size");
ok_memcmp(encrypted_blob1->bytes, encrypted_blob2->bytes, encrypted_blob2->len, "Encrypt same output value");
}
memcpy(encrypted_blob, encrypted_blob1->bytes, encrypted_blob1->len);
*encrypted_blob_nbytes = encrypted_blob_nbytes1;
return status1;
}
// Superset of the inputs necessary for ccecies_decrypt_gcm and ccecies_decrypt_gcm_from_shared_secret
// Execute both and verify they behave the same (error or valid cases)
static int test_ccecies_decrypt_gcm(ccec_full_ctx_t full_key,
const ccecies_gcm_t ecies,
size_t shared_secret_nbytes,
const uint8_t *shared_secret,
size_t encrypted_blob_nbytes,
const uint8_t *encrypted_blob,
size_t sharedinfo1_nbytes,
const void *sharedinfo1,
size_t sharedinfo2_nbytes,
const void *sharedinfo2,
size_t *plaintext_nbytes,
uint8_t *plaintext)
{
int status1;
int status2;
// shadow output parameter
byteBuffer plaintext1 = mallocByteBuffer(*plaintext_nbytes);
byteBuffer plaintext2 = mallocByteBuffer(*plaintext_nbytes);
size_t plaintext_nbytes1 = *plaintext_nbytes;
size_t plaintext_nbytes2 = *plaintext_nbytes;
status1 = ccecies_decrypt_gcm(full_key,
ecies,
encrypted_blob_nbytes,
encrypted_blob,
sharedinfo1_nbytes,
sharedinfo1,
sharedinfo2_nbytes,
sharedinfo2,
&plaintext_nbytes1,
plaintext1->bytes);
status2 = ccecies_decrypt_gcm_from_shared_secret(ccec_ctx_cp(full_key),
ecies,
shared_secret_nbytes,
shared_secret,
encrypted_blob_nbytes,
encrypted_blob,
sharedinfo1_nbytes,
sharedinfo1,
sharedinfo2_nbytes,
sharedinfo2,
&plaintext_nbytes2,
plaintext2->bytes);
if (ecies->options & (ECIES_EPH_PUBKEY_IN_SHAREDINFO1 | ECIES_EPH_PUBKEY_AND_SHAREDINFO1)) {
is(!!status1, !!status2, "Same error code"); // slight variation of error code since elliptic curve operations are not
// performed in the case of ccecies_decrypt_gcm_from_shared_secret
is(plaintext_nbytes1, plaintext_nbytes2, "Decrypt Same output size");
ok_memcmp(plaintext1->bytes, plaintext2->bytes, plaintext2->len, "Decrypt Same output value");
}
memcpy(plaintext, plaintext1->bytes, plaintext1->len);
*plaintext_nbytes = plaintext_nbytes1;
return status1;
}
// Negative testing based on KATs
static int ccecies_gcm_kat_negative_test(const struct ccecies_vector *test, int test_counter)
{
int status = 0; // fail
byteBuffer plaintext = hexStringToBytes(test->message);
byteBuffer expectedCiphertext = hexStringToBytes(
((test->options & ECIES_EXPORT_PUB_STANDARD) == ECIES_EXPORT_PUB_STANDARD) ? test->cipher : test->compact_cipher);
size_t sharedInfo1_size = strlen(test->sharedInfo1);
size_t sharedInfo2_size = strlen(test->sharedInfo2);
byteBuffer eph_priv_key = hexStringToBytes(test->eph_priv_key);
byteBuffer dec_priv_key = hexStringToBytes(test->dec_priv_key);
byteBuffer shared_secret = hexStringToBytes(test->Z);
struct ccrng_ecfips_test_state rng;
struct ccecies_gcm ecies_enc;
struct ccecies_gcm ecies_dec;
ccec_const_cp_t cp = test->curve();
size_t fake_size;
size_t output_size;
ccec_full_ctx_decl_cp(cp, remote_key);
ccec_full_ctx_decl_cp(cp, ephemeral_key);
// Buffer for outputs
byteBuffer ciphertext = NULL;
byteBuffer plaintext_bis = NULL;
// Generate "remote" public key from private key
ok_or_goto(
ccec_recover_full_key(cp, dec_priv_key->len, dec_priv_key->bytes, remote_key) == 0, "Generated private Key", errout);
ok_or_goto(
ccec_recover_full_key(cp, eph_priv_key->len, eph_priv_key->bytes, ephemeral_key) == 0, "Generated ephemeral Key", errout);
// Set RNG to control ephemeral key
ccrng_ecfips_test_init(&rng, eph_priv_key->len, eph_priv_key->bytes);
// Invalid setup
is(ccecies_encrypt_gcm_setup(
&ecies_enc, test->di, (struct ccrng_state *)&rng, ccaes_gcm_encrypt_mode(), 12, test->mac_nbytes, test->options),
CCERR_CRYPTO_CONFIG,
"Negative test encrypt setup");
is(ccecies_decrypt_gcm_setup(&ecies_dec, test->di, ccaes_gcm_decrypt_mode(), 13, test->mac_nbytes, test->options),
CCERR_CRYPTO_CONFIG,
"Negative test decrypt setup");
is(ccecies_encrypt_gcm_setup(
&ecies_enc, test->di, (struct ccrng_state *)&rng, ccaes_gcm_encrypt_mode(), test->key_nbytes, 3, test->options),
CCERR_CRYPTO_CONFIG,
"Negative test encrypt setup");
is(ccecies_decrypt_gcm_setup(&ecies_dec, test->di, ccaes_gcm_decrypt_mode(), test->key_nbytes, 7, test->options),
CCERR_CRYPTO_CONFIG,
"Negative test decrypt setup");
// Actual setup
is(ccecies_encrypt_gcm_setup(&ecies_enc,
test->di,
(struct ccrng_state *)&rng,
ccaes_gcm_encrypt_mode(),
test->key_nbytes,
test->mac_nbytes,
test->options),
0,
"Negative test encrypt setup");
is(ccecies_decrypt_gcm_setup(
&ecies_dec, test->di, ccaes_gcm_decrypt_mode(), test->key_nbytes, test->mac_nbytes, test->options),
0,
"Negative test decrypt setup");
ccec_pub_ctx_t pub_remote_key = ccec_ctx_pub(remote_key);
ciphertext = mallocByteBuffer(ccecies_encrypt_gcm_ciphertext_size(pub_remote_key, &ecies_enc, plaintext->len));
plaintext_bis = mallocByteBuffer(ccecies_decrypt_gcm_plaintext_size_cp(cp, &ecies_dec, ciphertext->len));
// Valid encrypted value
ok_or_goto(test_ccecies_encrypt_gcm(pub_remote_key,
&ecies_enc,
ccec_ctx_pub(ephemeral_key),
shared_secret->len,
shared_secret->bytes,
plaintext->len,
plaintext->bytes,
sharedInfo1_size,
test->sharedInfo1,
sharedInfo2_size,
test->sharedInfo2,
&ciphertext->len,
ciphertext->bytes) == 0,
"Encrypt",
errout);
// ------------------------------
// Negative testing of decrypt
// ------------------------------
// Wrong size
is(ccecies_decrypt_gcm_plaintext_size(remote_key, &ecies_dec, 0), (size_t)0, "Error case");
// Corrupted public key
ciphertext->bytes[2] ^= 1;
memset(plaintext_bis->bytes, 0xAA, plaintext_bis->len);
output_size = plaintext_bis->len;
ok_or_goto(test_ccecies_decrypt_gcm(remote_key,
&ecies_dec,
shared_secret->len,
shared_secret->bytes,
ciphertext->len,
ciphertext->bytes,
sharedInfo1_size,
test->sharedInfo1,
sharedInfo2_size,
test->sharedInfo2,
&output_size,
plaintext_bis->bytes) != 0,
"Corrupted public key",
errout);
ok(is_zero(plaintext_bis->len, plaintext_bis->bytes), "Delete on error");
ciphertext->bytes[2] ^= 1;
// Corrupted encrypted data (first byte)
size_t b = ((test->options & ECIES_EXPORT_PUB_STANDARD) == ECIES_EXPORT_PUB_STANDARD)
? ccec_x963_export_size(0, pub_remote_key)
: ccec_compact_export_size(0, pub_remote_key);
ciphertext->bytes[b] ^= 1;
memset(plaintext_bis->bytes, 0xAA, plaintext_bis->len);
output_size = plaintext_bis->len;
ok_or_goto(test_ccecies_decrypt_gcm(remote_key,
&ecies_dec,
shared_secret->len,
shared_secret->bytes,
ciphertext->len,
ciphertext->bytes,
sharedInfo1_size,
test->sharedInfo1,
sharedInfo2_size,
test->sharedInfo2,
&output_size,
plaintext_bis->bytes) != 0,
"Corrupted encrypted data, first byte",
errout);
ok(is_zero(plaintext_bis->len, plaintext_bis->bytes), "Delete on error");
ciphertext->bytes[b] ^= 1;
// Corrupted encrypted data (last byte)
ciphertext->bytes[ciphertext->len - test->mac_nbytes - 1] ^= 1;
memset(plaintext_bis->bytes, 0xAA, plaintext_bis->len);
output_size = plaintext_bis->len;
ok_or_goto(test_ccecies_decrypt_gcm(remote_key,
&ecies_dec,
shared_secret->len,
shared_secret->bytes,
ciphertext->len,
ciphertext->bytes,
sharedInfo1_size,
test->sharedInfo1,
sharedInfo2_size,
test->sharedInfo2,
&output_size,
plaintext_bis->bytes) != 0,
"Corrupted encrypted data, last byte",
errout);
ok(is_zero(plaintext_bis->len, plaintext_bis->bytes), "Delete on error");
ciphertext->bytes[ciphertext->len - test->mac_nbytes - 1] ^= 1;
// Corrupted mac
ciphertext->bytes[ciphertext->len - test->mac_nbytes] ^= 1;
memset(plaintext_bis->bytes, 0xAA, plaintext_bis->len);
output_size = plaintext_bis->len;
ok_or_goto(test_ccecies_decrypt_gcm(remote_key,
&ecies_dec,
shared_secret->len,
shared_secret->bytes,
ciphertext->len,
ciphertext->bytes,
sharedInfo1_size,
test->sharedInfo1,
sharedInfo2_size,
test->sharedInfo2,
&output_size,
plaintext_bis->bytes) != 0,
"Corrupted mac",
errout);
ok(is_zero(plaintext_bis->len, plaintext_bis->bytes), "Delete on error");
ciphertext->bytes[ciphertext->len - test->mac_nbytes] ^= 1;
// Output buffer too small
fake_size = plaintext_bis->len - 1;
memset(plaintext_bis->bytes, 0xAA, plaintext_bis->len);
output_size = fake_size;
ok_or_goto(test_ccecies_decrypt_gcm(remote_key,
&ecies_dec,
shared_secret->len,
shared_secret->bytes,
ciphertext->len,
ciphertext->bytes,
sharedInfo1_size,
test->sharedInfo1,
sharedInfo2_size,
test->sharedInfo2,
&output_size,
plaintext_bis->bytes) != 0,
"Decrypt: output too small",
errout);
ok(is_zero(fake_size, plaintext_bis->bytes), "Delete on error");
memset(plaintext_bis->bytes, 0xAA, plaintext_bis->len);
output_size = plaintext_bis->len;
if (test->options & ECIES_EPH_PUBKEY_IN_SHAREDINFO1) {
// SharedInfo1 must not be passed
ok_or_goto(test_ccecies_decrypt_gcm(remote_key,
&ecies_dec,
shared_secret->len,
shared_secret->bytes,
ciphertext->len,
ciphertext->bytes,
sizeof(shared_info3),
shared_info3,
sharedInfo2_size,
test->sharedInfo2,
&output_size,
plaintext_bis->bytes) != 0,
"SharedInfo1 must not be passed",
errout);
ok(is_zero(plaintext_bis->len, plaintext_bis->bytes), "Delete on error");
} else if (test->options & ECIES_EPH_PUBKEY_AND_SHAREDINFO1) {
// SharedInfo1 must be passed
ok_or_goto(test_ccecies_decrypt_gcm(remote_key,
&ecies_dec,
shared_secret->len,
shared_secret->bytes,
ciphertext->len,
ciphertext->bytes,
0,
NULL,
sharedInfo2_size,
test->sharedInfo2,
&output_size,
plaintext_bis->bytes) != 0,
"SharedInfo1 must be passed",
errout);
ok(is_zero(plaintext_bis->len, plaintext_bis->bytes), "Delete on error");
}
if ((test->options & ECIES_EPH_PUBKEY_IN_SHAREDINFO1) == 0) {
// SharedInfo1 mismatch
ok_or_goto(test_ccecies_decrypt_gcm(remote_key,
&ecies_dec,
shared_secret->len,
shared_secret->bytes,
ciphertext->len,
ciphertext->bytes,
sizeof(shared_info3),
shared_info3,
sharedInfo2_size,
test->sharedInfo2,
&output_size,
plaintext_bis->bytes) != 0,
"SharedInfo1 mismatch",
errout);
ok(is_zero(plaintext_bis->len, plaintext_bis->bytes), "Delete on error");
}
// SharedInfo2 mismatch
memset(plaintext_bis->bytes, 0xAA, plaintext_bis->len);
output_size = plaintext_bis->len;
ok_or_goto(test_ccecies_decrypt_gcm(remote_key,
&ecies_dec,
shared_secret->len,
shared_secret->bytes,
ciphertext->len,
ciphertext->bytes,
sharedInfo1_size,
test->sharedInfo1,
sizeof(shared_info3),
shared_info3,
&output_size,
plaintext_bis->bytes) != 0,
"SharedInfo2 mismatch",
errout);
ok_or_goto(is_zero(plaintext_bis->len, plaintext_bis->bytes), "Delete on error", errout);
// ------------------------------
// Negative testing of encrypt
// ------------------------------
// Wrong size
struct ccecies_gcm ecies_enc_bogus;
ecies_enc_bogus = ecies_enc;
ecies_enc_bogus.options = 0;
is_or_goto(
ccecies_encrypt_gcm_ciphertext_size(pub_remote_key, &ecies_enc_bogus, plaintext->len), (size_t)0, "Error case", errout);
// Bad random, does not apply to "from shared secret version" since
// it does not generate ephemeral key
ccrng_ecfips_test_init(&rng, 0, NULL);
output_size = ciphertext->len;
ok_or_goto(ccecies_encrypt_gcm(pub_remote_key,
&ecies_enc,
plaintext->len,
plaintext->bytes,
sharedInfo1_size,
test->sharedInfo1,
sharedInfo2_size,
test->sharedInfo2,
&output_size,
ciphertext->bytes) != 0,
"Bad random",
errout);
// Output size too small
fake_size = ccecies_encrypt_gcm_ciphertext_size(pub_remote_key, &ecies_enc, plaintext->len) - 1;
ok_or_goto(test_ccecies_encrypt_gcm(pub_remote_key,
&ecies_enc,
ccec_ctx_pub(ephemeral_key),
shared_secret->len,
shared_secret->bytes,
plaintext->len,
plaintext->bytes,
sharedInfo1_size,
test->sharedInfo1,
sharedInfo2_size,
test->sharedInfo2,
&fake_size,
ciphertext->bytes) != 0,
"Encrypt: output too small",
errout);
status = 1; // pass
errout:
ok(status == 1, "Error in test %i", test_counter);
free(expectedCiphertext);
free(eph_priv_key);
free(dec_priv_key);
free(plaintext);
free(plaintext_bis);
free(ciphertext);
free(shared_secret);
ccec_full_ctx_clear_cp(cp, remote_key);
ccec_full_ctx_clear_cp(cp, ephemeral_key);
return status;
}
// Process one vector
static int ccecies_gcm_kat_test(const struct ccecies_vector *test, int test_counter)
{
int status = 0; // fail
byteBuffer plaintext = hexStringToBytes(test->message);
byteBuffer expectedCiphertext = hexStringToBytes(
((test->options & ECIES_EXPORT_PUB_STANDARD) == ECIES_EXPORT_PUB_STANDARD) ? test->cipher : test->compact_cipher);
size_t sharedInfo1_size = strlen(test->sharedInfo1);
size_t sharedInfo2_size = strlen(test->sharedInfo2);
byteBuffer eph_priv_key = hexStringToBytes(test->eph_priv_key);
byteBuffer dec_priv_key = hexStringToBytes(test->dec_priv_key);
byteBuffer shared_secret = hexStringToBytes(test->Z);
struct ccrng_ecfips_test_state rng;
struct ccecies_gcm ecies_enc;
struct ccecies_gcm ecies_dec;
ccec_const_cp_t cp = test->curve();
ccec_full_ctx_decl_cp(cp, remote_key);
ccec_full_ctx_decl_cp(cp, ephemeral_key);
// Buffer for outputs
byteBuffer ciphertext = NULL;
byteBuffer plaintext_bis = NULL;
// Generate "remote" public key from private key
ok_or_goto(ccec_recover_full_key(cp, dec_priv_key->len, dec_priv_key->bytes, remote_key) == 0, "Generated Key", errout);
ok_or_goto(ccec_recover_full_key(cp, eph_priv_key->len, eph_priv_key->bytes, ephemeral_key) == 0, "Generated Key", errout);
// Set RNG to control ephemeral key
ccrng_ecfips_test_init(&rng, eph_priv_key->len, eph_priv_key->bytes);
is(ccecies_encrypt_gcm_setup(&ecies_enc,
test->di,
(struct ccrng_state *)&rng,
ccaes_gcm_encrypt_mode(),
test->key_nbytes,
test->mac_nbytes,
test->options),
0,
"KAT Encrypt setup");
is(ccecies_decrypt_gcm_setup(
&ecies_dec, test->di, ccaes_gcm_decrypt_mode(), test->key_nbytes, test->mac_nbytes, test->options),
0,
"KAT Decrypt setup");
ccec_pub_ctx_t pub_remote_key = ccec_ctx_pub(remote_key);
ciphertext = mallocByteBuffer(ccecies_encrypt_gcm_ciphertext_size(pub_remote_key, &ecies_enc, plaintext->len));
plaintext_bis = mallocByteBuffer(ccecies_decrypt_gcm_plaintext_size_cp(cp, &ecies_dec, ciphertext->len));
is_or_goto(test_ccecies_encrypt_gcm(pub_remote_key,
&ecies_enc,
ccec_ctx_pub(ephemeral_key),
shared_secret->len,
shared_secret->bytes,
plaintext->len,
plaintext->bytes,
sharedInfo1_size,
test->sharedInfo1,
sharedInfo2_size,
test->sharedInfo2,
&ciphertext->len,
ciphertext->bytes),
0,
"Encrypt",
errout);
is_or_goto(test_ccecies_decrypt_gcm(remote_key,
&ecies_dec,
shared_secret->len,
shared_secret->bytes,
ciphertext->len,
ciphertext->bytes,
sharedInfo1_size,
test->sharedInfo1,
sharedInfo2_size,
test->sharedInfo2,
&plaintext_bis->len,
plaintext_bis->bytes),
0,
"Decrypt",
errout);
// Checks
is(ciphertext->len, expectedCiphertext->len, "Ciphertext size correct");
// Manually checks internal fields for debugging
// Point
size_t point_nbytes = expectedCiphertext->len - test->mac_nbytes - plaintext->len;
ok_memcmp(ciphertext->bytes, expectedCiphertext->bytes, point_nbytes, "Point as expected");
// Encrypted
size_t ciphertext_offset = (expectedCiphertext->len - test->mac_nbytes - plaintext->len);
ok_memcmp(ciphertext->bytes + ciphertext_offset,
expectedCiphertext->bytes + ciphertext_offset,
plaintext->len,
"Encrypted data as expected");
// Tag
size_t tag_offset = (expectedCiphertext->len - test->mac_nbytes);
ok_memcmp(ciphertext->bytes + tag_offset, expectedCiphertext->bytes + tag_offset, test->mac_nbytes, "Tag as expected");
is(plaintext->len, plaintext_bis->len, "Decrypted plaintext size correct");
ok_memcmp(plaintext->bytes, plaintext_bis->bytes, plaintext->len, "Recovered plaintext correct");
status = 1;
errout:
ok(status == 1, "Error in test %i", test_counter);
free(expectedCiphertext);
free(eph_priv_key);
free(dec_priv_key);
free(plaintext);
free(plaintext_bis);
free(ciphertext);
free(shared_secret);
ccec_full_ctx_clear_cp(cp, remote_key);
ccec_full_ctx_clear_cp(cp, ephemeral_key);
return status;
}
static int ecies_aes_gcm_crypt_decrypt(ccec_full_ctx_t key,
size_t msg_size,
const struct ccdigest_info *di,
struct ccrng_state *rng,
uint32_t options)
{
int status = 0; // fail
byteBuffer plaintext = mallocByteBuffer(msg_size);
struct ccecies_gcm ecies_enc;
struct ccecies_gcm ecies_dec;
ccrng_generate(rng, msg_size, plaintext->bytes);
is(ccecies_encrypt_gcm_setup(&ecies_enc, di, rng, ccaes_gcm_encrypt_mode(), 16, 16, options), 0, "Roundtrip encrypt setup");
is(ccecies_decrypt_gcm_setup(&ecies_dec, di, ccaes_gcm_decrypt_mode(), 16, 16, options), 0, "Roundtrip decrypt setup");
ccec_pub_ctx_t pub_key = ccec_ctx_pub(key);
byteBuffer ciphertext = mallocByteBuffer(ccecies_encrypt_gcm_ciphertext_size(pub_key, &ecies_enc, msg_size));
byteBuffer plaintext_bis = mallocByteBuffer(ccecies_decrypt_gcm_plaintext_size(key, &ecies_dec, ciphertext->len));
// With shared info
if ((options & ECIES_EPH_PUBKEY_IN_SHAREDINFO1) == 0) {
ok_or_goto(ccecies_encrypt_gcm(pub_key,
&ecies_enc,
plaintext->len,
plaintext->bytes,
sizeof(shared_info1),
shared_info1,
sizeof(shared_info2),
shared_info2,
&ciphertext->len,
ciphertext->bytes) == 0,
"Encrypt",
errout);
ok_or_goto(ccecies_decrypt_gcm(key,
&ecies_dec,
ciphertext->len,
ciphertext->bytes,
sizeof(shared_info1),
shared_info1,
sizeof(shared_info2),
shared_info2,
&plaintext_bis->len,
plaintext_bis->bytes) == 0,
"Decrypt",
errout);
ok_or_goto(plaintext->len == plaintext_bis->len, "Encrypt/Decrypt correct keysize", errout);
ok_or_goto(memcmp(plaintext->bytes, plaintext_bis->bytes, plaintext->len) == 0, "Shared secrets match", errout);
}
// Without shared info
if ((options & ECIES_EPH_PUBKEY_AND_SHAREDINFO1) == 0) {
ok_or_goto(
ccecies_encrypt_gcm(
pub_key, &ecies_enc, plaintext->len, plaintext->bytes, 0, NULL, 0, NULL, &ciphertext->len, ciphertext->bytes) ==
0,
"Encrypt",
errout);
ok_or_goto(ccecies_decrypt_gcm(key,
&ecies_dec,
ciphertext->len,
ciphertext->bytes,
0,
NULL,
0,
NULL,
&plaintext_bis->len,
plaintext_bis->bytes) == 0,
"Decrypt",
errout);
ok_or_goto(plaintext->len == plaintext_bis->len, "Encrypt/Decrypt correct keysize", errout);
ok_or_goto(memcmp(plaintext->bytes, plaintext_bis->bytes, plaintext->len) == 0, "Shared secrets match", errout);
}
status = 1; // Success
errout:
free(plaintext);
free(plaintext_bis);
free(ciphertext);
return status;
}
static int ecies_test(struct ccrng_state *rng, size_t expected_keysize, ccec_const_cp_t cp, uint32_t options)
{
int status = 0;
ccec_full_ctx_decl_cp(cp, full_key);
ok_or_fail(ccec_generate_key_fips(cp, rng, full_key) == 0, "Generated Key");
if (verbose)
diag("Test with keysize %u", expected_keysize);
ok_or_goto(ccec_ctx_bitlen(full_key) == expected_keysize, "Generated correct keysize", errout);
// SHA-1
ok_or_goto(ecies_aes_gcm_crypt_decrypt(full_key, 16, ccsha1_di(), rng, options), "ECIES AES GCM SHA1, Msg length 16", errout);
ok_or_goto(ecies_aes_gcm_crypt_decrypt(full_key, 1, ccsha1_di(), rng, options), "ECIES AES GCM SHA1, Msg length 1", errout);
ok_or_goto(
ecies_aes_gcm_crypt_decrypt(full_key, 4096, ccsha1_di(), rng, options), "ECIES AES GCM SHA1, Msg length 4096", errout);
ok_or_goto(
ecies_aes_gcm_crypt_decrypt(full_key, 4097, ccsha1_di(), rng, options), "ECIES AES GCM SHA1, Msg length 4097", errout);
// SHA-256
ok_or_goto(
ecies_aes_gcm_crypt_decrypt(full_key, 16, ccsha256_di(), rng, options), "ECIES AES GCM SHA256, Msg length 16", errout);
ok_or_goto(
ecies_aes_gcm_crypt_decrypt(full_key, 1, ccsha256_di(), rng, options), "ECIES AES GCM SHA256, Msg length 1", errout);
ok_or_goto(ecies_aes_gcm_crypt_decrypt(full_key, 4096, ccsha256_di(), rng, options),
"ECIES AES GCM SHA256, Msg length 4096",
errout);
ok_or_goto(ecies_aes_gcm_crypt_decrypt(full_key, 4097, ccsha256_di(), rng, options),
"ECIES AES GCM SHA256, Msg length 4097",
errout);
status = 1;
errout:
ccec_full_ctx_clear_cp(cp, full_key);
return status;
}
typedef int (*test_func_t)(const struct ccecies_vector *test, int test_counter);
static int ecies_gcm_vector_tests(test_func_t func, const struct ccecies_vector *test)
{
int test_counter = 0;
int test_status = 1;
const struct ccecies_vector *current_test = &test[test_counter++];
while (current_test->di != NULL && test_status) {
struct ccecies_vector ct = *current_test;
test_status = func(&ct,test_counter);
ct.options &= ~(unsigned int)ECIES_EXPORT_PUB_STANDARD; // kill this option
ct.options |= ECIES_EXPORT_PUB_COMPACT; // add this option instead
test_status &= func(&ct,test_counter); // and redo the test
current_test = &test[test_counter++];
}
return test_status;
}
static char *option_name(uint32_t option)
{
if (ECIES_EXPORT_PUB_STANDARD == (option & ECIES_EXPORT_PUB_STANDARD)) {
return "ECIES_EXPORT_PUB_STANDARD";
} else if (ECIES_EXPORT_PUB_COMPACT == (option & ECIES_EXPORT_PUB_COMPACT)) {
return "ECIES_EXPORT_PUB_COMPACT";
} else
return NULL;
}
static int options_test(struct ccrng_state *rng, uint32_t options)
{
if (verbose)
diag(option_name(options));
ok(ecies_test(rng, 192, ccec_cp_192(), options), "ECIES with 192 bit EC Key");
ok(ecies_test(rng, 224, ccec_cp_224(), options), "ECIES with 224 bit EC Key");
ok(ecies_test(rng, 256, ccec_cp_256(), options), "ECIES with 256 bit EC Key");
ok(ecies_test(rng, 384, ccec_cp_384(), options), "ECIES with 384 bit EC Key");
ok(ecies_test(rng, 521, ccec_cp_521(), options), "ECIES with 521 bit EC Key");
if (verbose)
diag_linereturn();
char buf[134 + 1];
snprintf(buf, 133, "%s | ECIES_EPH_PUBKEY_IN_SHAREDINFO1", option_name(options));
buf[133] = 0;
if (verbose) {
diag(buf);
}
ok(ecies_test(rng, 192, ccec_cp_192(), options | ECIES_EPH_PUBKEY_IN_SHAREDINFO1),
"ECIES with 192 bit EC Key, public key in sharedInfo1");
ok(ecies_test(rng, 224, ccec_cp_224(), options | ECIES_EPH_PUBKEY_IN_SHAREDINFO1),
"ECIES with 224 bit EC Key, public key in sharedInfo1");
ok(ecies_test(rng, 256, ccec_cp_256(), options | ECIES_EPH_PUBKEY_IN_SHAREDINFO1),
"ECIES with 256 bit EC Key, public key in sharedInfo1");
ok(ecies_test(rng, 384, ccec_cp_384(), options | ECIES_EPH_PUBKEY_IN_SHAREDINFO1),
"ECIES with 384 bit EC Key, public key in sharedInfo1");
ok(ecies_test(rng, 521, ccec_cp_521(), options | ECIES_EPH_PUBKEY_IN_SHAREDINFO1),
"ECIES with 521 bit EC Key, public key in sharedInfo1");
if (verbose) {
diag_linereturn();
}
snprintf(buf, 134, "%s | ECIES_EPH_PUBKEY_AND_SHAREDINFO1", option_name(options));
buf[134] = 0;
if (verbose) {
diag(buf);
}
ok(ecies_test(rng, 192, ccec_cp_192(), options | ECIES_EPH_PUBKEY_AND_SHAREDINFO1),
"ECIES with 192 bit EC Key, public key in sharedInfo1");
ok(ecies_test(rng, 224, ccec_cp_224(), options | ECIES_EPH_PUBKEY_AND_SHAREDINFO1),
"ECIES with 224 bit EC Key, public key in sharedInfo1");
ok(ecies_test(rng, 256, ccec_cp_256(), options | ECIES_EPH_PUBKEY_AND_SHAREDINFO1),
"ECIES with 256 bit EC Key, public key in sharedInfo1");
ok(ecies_test(rng, 384, ccec_cp_384(), options | ECIES_EPH_PUBKEY_AND_SHAREDINFO1),
"ECIES with 384 bit EC Key, public key in sharedInfo1");
ok(ecies_test(rng, 521, ccec_cp_521(), options | ECIES_EPH_PUBKEY_AND_SHAREDINFO1),
"ECIES with 521 bit EC Key, public key in sharedInfo1");
if (verbose) {
diag_linereturn();
}
return 0;
}
static int options_conflict_test(struct ccrng_state *rng)
{
const struct ccdigest_info *di = ccsha256_di();
ccec_const_cp_t cp = ccec_cp_256();
ccec_full_ctx_decl_cp(cp, key);
uint32_t options = ECIES_EPH_PUBKEY_IN_SHAREDINFO1 | ECIES_EPH_PUBKEY_AND_SHAREDINFO1;
size_t plaintext_len = 32;
uint8_t plaintext[plaintext_len];
ccrng_generate(rng, 32, plaintext);
struct ccecies_gcm ecies_enc;
struct ccecies_gcm ecies_dec;
is(ccecies_encrypt_gcm_setup(&ecies_enc, di, rng, ccaes_gcm_encrypt_mode(), 16, 16, options), 0, "Conflict encrypt setup");
is(ccecies_decrypt_gcm_setup(&ecies_dec, di, ccaes_gcm_decrypt_mode(), 16, 16, options), 0, "Conflict decrypt setup");
ok_or_fail(ccec_generate_key_fips(cp, rng, key) == 0, "Generated Key");
ccec_pub_ctx_t pub_key = ccec_ctx_pub(key);
size_t ciphertext_len = ccecies_encrypt_gcm_ciphertext_size(pub_key, &ecies_enc, 32);
uint8_t ciphertext[ciphertext_len];
ok_or_fail(ccecies_encrypt_gcm(pub_key,
&ecies_enc,
plaintext_len,
plaintext,
sizeof(shared_info1),
shared_info1,
sizeof(shared_info2),
shared_info2,
&ciphertext_len,
ciphertext) != 0,
"Setting both ECIES_EPH_PUBKEY_IN_SHAREDINFO1 and "
"ECIES_EPH_PUBKEY_AND_SHAREDINFO1 should fail");
ok_or_fail(ccecies_decrypt_gcm(key,
&ecies_dec,
ciphertext_len,
ciphertext,
sizeof(shared_info1),
NULL,
sizeof(shared_info2),
NULL,
&plaintext_len,
plaintext) != 0,
"Setting both ECIES_EPH_PUBKEY_IN_SHAREDINFO1 and "
"ECIES_EPH_PUBKEY_AND_SHAREDINFO1 should fail");
ccec_full_ctx_clear_cp(cp, key);
return 0;
}
int ccecies_tests(TM_UNUSED int argc, TM_UNUSED char *const *argv)
{
struct ccrng_state *rng = global_test_rng;
plan_tests(6961);
if (verbose)
diag("KATs");
ok(ecies_gcm_vector_tests(ccecies_gcm_kat_test, ccecies_aes_gcm_vectors), "AES GCM KAT");
if (verbose)
diag("Negative tests");
ok(ecies_gcm_vector_tests(ccecies_gcm_kat_negative_test, ccecies_aes_gcm_vectors), "AES GCM Negative tests");
options_test(rng, ECIES_EXPORT_PUB_STANDARD);
options_test(rng, ECIES_EXPORT_PUB_COMPACT);
options_conflict_test(rng);
return 0;
}
#endif