diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:45:59 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:45:59 +0000 |
commit | 19fcec84d8d7d21e796c7624e521b60d28ee21ed (patch) | |
tree | 42d26aa27d1e3f7c0b8bd3fd14e7d7082f5008dc /src/spdk/intel-ipsec-mb/LibTestApp/ipsec_xvalid.c | |
parent | Initial commit. (diff) | |
download | ceph-19fcec84d8d7d21e796c7624e521b60d28ee21ed.tar.xz ceph-19fcec84d8d7d21e796c7624e521b60d28ee21ed.zip |
Adding upstream version 16.2.11+ds.upstream/16.2.11+dsupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/spdk/intel-ipsec-mb/LibTestApp/ipsec_xvalid.c')
-rw-r--r-- | src/spdk/intel-ipsec-mb/LibTestApp/ipsec_xvalid.c | 2055 |
1 files changed, 2055 insertions, 0 deletions
diff --git a/src/spdk/intel-ipsec-mb/LibTestApp/ipsec_xvalid.c b/src/spdk/intel-ipsec-mb/LibTestApp/ipsec_xvalid.c new file mode 100644 index 000000000..c1f0467a1 --- /dev/null +++ b/src/spdk/intel-ipsec-mb/LibTestApp/ipsec_xvalid.c @@ -0,0 +1,2055 @@ +/********************************************************************** + Copyright(c) 2019, Intel Corporation All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * 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. + * Neither the name of Intel Corporation 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 COPYRIGHT HOLDERS AND CONTRIBUTORS + "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 COPYRIGHT + OWNER OR 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 <inttypes.h> +#include <string.h> +#include <errno.h> +#include <malloc.h> /* memalign() or _aligned_malloc()/aligned_free() */ +#include "misc.h" + +#ifdef _WIN32 +#include <intrin.h> +#define strdup _strdup +#define __forceinline static __forceinline +#define BSWAP64 _byteswap_uint64 +#else +#include <x86intrin.h> +#define __forceinline static inline __attribute__((always_inline)) +#define BSWAP64 __builtin_bswap64 +#endif + +#include <intel-ipsec-mb.h> + +/* maximum size of a test buffer */ +#define JOB_SIZE_TOP (16 * 1024) +/* min size of a buffer when testing range of buffers */ +#define DEFAULT_JOB_SIZE_MIN 16 +/* max size of a buffer when testing range of buffers */ +#define DEFAULT_JOB_SIZE_MAX (2 * 1024) +/* number of bytes to increase buffer size when testing range of buffers */ +#define DEFAULT_JOB_SIZE_STEP 16 + +#define DEFAULT_JOB_ITER 10 + +#define AAD_SIZE 12 +#define MAX_IV_SIZE 16 + +/* Maximum key and digest size for SHA-512 */ +#define MAX_KEY_SIZE SHA_512_BLOCK_SIZE +#define MAX_DIGEST_SIZE SHA512_DIGEST_SIZE_IN_BYTES + +#define DIM(x) (sizeof(x)/sizeof(x[0])) + +#define SEED 0xdeadcafe +#define PT_PATTERN 0x44444444 +#define KEY_PATTERN 0x66666666 +#define TAG_PATTERN 0x77777777 +#define STACK_DEPTH 8192 + +enum arch_type_e { + ARCH_SSE = 0, + ARCH_AESNI_EMU, + ARCH_AVX, + ARCH_AVX2, + ARCH_AVX512, + NUM_ARCHS +}; + +/* Struct storing cipher parameters */ +struct params_s { + JOB_CIPHER_MODE cipher_mode; /* CBC, CNTR, DES, GCM etc. */ + JOB_HASH_ALG hash_alg; /* SHA-1 or others... */ + uint32_t key_size; + uint32_t buf_size; + uint64_t aad_size; + uint32_t num_sizes; +}; + +/* Struct storing all expanded keys */ +struct cipher_auth_keys { + uint8_t temp_buf[SHA_512_BLOCK_SIZE]; + DECLARE_ALIGNED(uint32_t dust[15 * 4], 16); + uint8_t ipad[SHA512_DIGEST_SIZE_IN_BYTES]; + uint8_t opad[SHA512_DIGEST_SIZE_IN_BYTES]; + DECLARE_ALIGNED(uint32_t k1_expanded[15 * 4], 16); + DECLARE_ALIGNED(uint8_t k2[16], 16); + DECLARE_ALIGNED(uint8_t k3[16], 16); + DECLARE_ALIGNED(uint32_t enc_keys[15 * 4], 16); + DECLARE_ALIGNED(uint32_t dec_keys[15 * 4], 16); + DECLARE_ALIGNED(struct gcm_key_data gdata_key, 64); +}; + +/* Struct storing all necessary data for crypto operations */ +struct data { + uint8_t test_buf[JOB_SIZE_TOP]; + uint8_t src_dst_buf[JOB_SIZE_TOP]; + uint8_t aad[AAD_SIZE]; + uint8_t in_digest[MAX_DIGEST_SIZE]; + uint8_t out_digest[MAX_DIGEST_SIZE]; + uint8_t iv[MAX_IV_SIZE]; + uint8_t key[MAX_KEY_SIZE]; + struct cipher_auth_keys enc_keys; + struct cipher_auth_keys dec_keys; +}; + +struct custom_job_params { + JOB_CIPHER_MODE cipher_mode; /* CBC, CNTR, DES, GCM etc. */ + JOB_HASH_ALG hash_alg; /* SHA-1 or others... */ + uint32_t key_size; +}; + +union params { + enum arch_type_e arch_type; + struct custom_job_params job_params; +}; + +struct str_value_mapping { + const char *name; + union params values; +}; + +struct str_value_mapping arch_str_map[] = { + {.name = "SSE", .values.arch_type = ARCH_SSE }, + {.name = "AESNI_EMU", .values.arch_type = ARCH_AESNI_EMU }, + {.name = "AVX", .values.arch_type = ARCH_AVX }, + {.name = "AVX2", .values.arch_type = ARCH_AVX2 }, + {.name = "AVX512", .values.arch_type = ARCH_AVX512 } +}; + +struct str_value_mapping cipher_algo_str_map[] = { + { + .name = "aes-cbc-128", + .values.job_params = { + .cipher_mode = CBC, + .key_size = AES_128_BYTES + } + }, + { + .name = "aes-cbc-192", + .values.job_params = { + .cipher_mode = CBC, + .key_size = AES_192_BYTES + } + }, + { + .name = "aes-cbc-256", + .values.job_params = { + .cipher_mode = CBC, + .key_size = AES_256_BYTES + } + }, + { + .name = "aes-ctr-128", + .values.job_params = { + .cipher_mode = CNTR, + .key_size = AES_128_BYTES + } + }, + { + .name = "aes-ctr-192", + .values.job_params = { + .cipher_mode = CNTR, + .key_size = AES_192_BYTES + } + }, + { + .name = "aes-ctr-256", + .values.job_params = { + .cipher_mode = CNTR, + .key_size = AES_256_BYTES + } + }, + { + .name = "aes-ctr-bit-128", + .values.job_params = { + .cipher_mode = CNTR_BITLEN, + .key_size = AES_128_BYTES + } + }, + { + .name = "aes-ctr-bit-192", + .values.job_params = { + .cipher_mode = CNTR_BITLEN, + .key_size = AES_192_BYTES + } + }, + { + .name = "aes-ctr-bit-256", + .values.job_params = { + .cipher_mode = CNTR_BITLEN, + .key_size = AES_256_BYTES + } + }, + { + .name = "aes-ecb-128", + .values.job_params = { + .cipher_mode = ECB, + .key_size = AES_128_BYTES + } + }, + { + .name = "aes-ecb-192", + .values.job_params = { + .cipher_mode = ECB, + .key_size = AES_192_BYTES + } + }, + { + .name = "aes-ecb-256", + .values.job_params = { + .cipher_mode = ECB, + .key_size = AES_256_BYTES + } + }, + { + .name = "aes-docsis", + .values.job_params = { + .cipher_mode = DOCSIS_SEC_BPI, + .key_size = AES_128_BYTES + } + }, + { + .name = "des-docsis", + .values.job_params = { + .cipher_mode = DOCSIS_DES, + .key_size = 8 + } + }, + { + .name = "des-cbc", + .values.job_params = { + .cipher_mode = DES, + .key_size = 8 + } + }, + { + .name = "3des-cbc", + .values.job_params = { + .cipher_mode = DES3, + .key_size = 24 + } + }, + { + .name = "null", + .values.job_params = { + .cipher_mode = NULL_CIPHER, + .key_size = 0 + } + } +}; + +struct str_value_mapping hash_algo_str_map[] = { + { + .name = "sha1-hmac", + .values.job_params = { + .hash_alg = SHA1 + } + }, + { + .name = "sha224-hmac", + .values.job_params = { + .hash_alg = SHA_224 + } + }, + { + .name = "sha256-hmac", + .values.job_params = { + .hash_alg = SHA_256 + } + }, + { + .name = "sha384-hmac", + .values.job_params = { + .hash_alg = SHA_384 + } + }, + { + .name = "sha512-hmac", + .values.job_params = { + .hash_alg = SHA_512 + } + }, + { + .name = "aes-xcbc", + .values.job_params = { + .hash_alg = AES_XCBC + } + }, + { + .name = "md5-hmac", + .values.job_params = { + .hash_alg = MD5 + } + }, + { + .name = "aes-cmac", + .values.job_params = { + .hash_alg = AES_CMAC + } + }, + { + .name = "null", + .values.job_params = { + .hash_alg = NULL_HASH + } + }, + { + .name = "aes-cmac-bitlen", + .values.job_params = { + .hash_alg = AES_CMAC_BITLEN + } + }, + { + .name = "sha1", + .values.job_params = { + .hash_alg = PLAIN_SHA1 + } + }, + { + .name = "sha224", + .values.job_params = { + .hash_alg = PLAIN_SHA_224 + } + }, + { + .name = "sha256", + .values.job_params = { + .hash_alg = PLAIN_SHA_256 + } + }, + { + .name = "sha384", + .values.job_params = { + .hash_alg = PLAIN_SHA_384 + } + }, + { + .name = "sha512", + .values.job_params = { + .hash_alg = PLAIN_SHA_512 + } + }, +}; + +struct str_value_mapping aead_algo_str_map[] = { + { + .name = "aes-gcm-128", + .values.job_params = { + .cipher_mode = GCM, + .hash_alg = AES_GMAC, + .key_size = AES_128_BYTES + } + }, + { + .name = "aes-gcm-192", + .values.job_params = { + .cipher_mode = GCM, + .hash_alg = AES_GMAC, + .key_size = AES_192_BYTES + } + }, + { + .name = "aes-gcm-256", + .values.job_params = { + .cipher_mode = GCM, + .hash_alg = AES_GMAC, + .key_size = AES_256_BYTES + } + }, + { + .name = "aes-ccm-128", + .values.job_params = { + .cipher_mode = CCM, + .hash_alg = AES_CCM, + .key_size = AES_128_BYTES + } + }, + { + .name = "pon-128", + .values.job_params = { + .cipher_mode = PON_AES_CNTR, + .hash_alg = PON_CRC_BIP, + .key_size = AES_128_BYTES + } + }, + { + .name = "pon-128-no-ctr", + .values.job_params = { + .cipher_mode = PON_AES_CNTR, + .hash_alg = PON_CRC_BIP, + .key_size = 0 + } + }, +}; + +/* This struct stores all information about performed test case */ +struct variant_s { + uint32_t arch; + struct params_s params; + uint64_t *avg_times; +}; + +const uint8_t auth_tag_length_bytes[19] = { + 12, /* SHA1 */ + 14, /* SHA_224 */ + 16, /* SHA_256 */ + 24, /* SHA_384 */ + 32, /* SHA_512 */ + 12, /* AES_XCBC */ + 12, /* MD5 */ + 0, /* NULL_HASH */ +#ifndef NO_GCM + 16, /* AES_GMAC */ +#endif + 0, /* CUSTOM HASH */ + 16, /* AES_CCM */ + 16, /* AES_CMAC */ + 20, /* PLAIN_SHA1 */ + 28, /* PLAIN_SHA_224 */ + 32, /* PLAIN_SHA_256 */ + 48, /* PLAIN_SHA_384 */ + 64, /* PLAIN_SHA_512 */ + 4, /* AES_CMAC_BITLEN (3GPP) */ + 8, /* PON */ +}; + +/* Minimum, maximum and step values of key sizes */ +const uint8_t key_sizes[13][3] = { + {16, 32, 8}, /* CBC */ + {16, 32, 8}, /* CNTR */ + {0, 0, 1}, /* NULL */ + {16, 16, 1}, /* DOCSIS_SEC_BPI */ +#ifndef NO_GCM + {16, 32, 8}, /* GCM */ +#endif + {0, 0, 1}, /* CUSTOM_CIPHER */ + {8, 8, 1}, /* DES */ + {8, 8, 1}, /* DOCSIS_DES */ + {16, 16, 1}, /* CCM */ + {24, 24, 1}, /* DES3 */ + {16, 16, 1}, /* PON_AES_CNTR */ + {16, 32, 8}, /* ECB */ + {16, 32, 8}, /* CNTR_BITLEN */ +}; + +uint8_t custom_test = 0; +uint8_t verbose = 0; + +enum range { + RANGE_MIN = 0, + RANGE_STEP, + RANGE_MAX, + NUM_RANGE +}; + +uint32_t job_sizes[NUM_RANGE] = {DEFAULT_JOB_SIZE_MIN, + DEFAULT_JOB_SIZE_STEP, + DEFAULT_JOB_SIZE_MAX}; +uint32_t job_iter = DEFAULT_JOB_ITER; + +struct custom_job_params custom_job_params = { + .cipher_mode = NULL_CIPHER, + .hash_alg = NULL_HASH, + .key_size = 0 +}; + +/* AESNI_EMU disabled by default */ +uint8_t enc_archs[NUM_ARCHS] = {1, 0, 1, 1, 1}; +uint8_t dec_archs[NUM_ARCHS] = {1, 0, 1, 1, 1}; + +uint64_t flags = 0; /* flags passed to alloc_mb_mgr() */ + +/** Generate random buffer */ +static void +generate_random_buf(uint8_t *buf, const uint32_t length) +{ + uint32_t i; + + for (i = 0; i < length; i++) + buf[i] = (uint8_t) rand(); +} + +/* + * Searches across a block of memory if a pattern is present + * (indicating there is some left over sensitive data) + * + * Returns 0 if pattern is present or -1 if not present + */ +static int +search_patterns(const void *ptr, const size_t mem_size) +{ + const uint8_t *ptr8 = (const uint8_t *) ptr; + size_t i; + + if (mem_size < 4) + return -1; + + for (i = 0; i <= (mem_size - 4); i++) { + const uint32_t string = ((const uint32_t *) ptr8)[0]; + int ret = -1; + + if (string == KEY_PATTERN) { + fprintf(stderr, "Part of KEY is present\n"); + ret = 0; + } + if (string == TAG_PATTERN) { + fprintf(stderr, "Part of TAG is present\n"); + ret = 0; + } + if (string == PT_PATTERN) { + fprintf(stderr, + "Part of plain/ciphertext is present\n"); + ret = 0; + } + if (ret == 0) { + fprintf(stderr, "Offset = %zu\n", i); + return 0; + } + ptr8++; + } + + return -1; +} + +static void +byte_hexdump(const char *message, const uint8_t *ptr, const uint32_t len) +{ + uint32_t ctr; + + printf("%s:\n", message); + for (ctr = 0; ctr < len; ctr++) { + printf("0x%02X ", ptr[ctr] & 0xff); + if (!((ctr + 1) % 16)) + printf("\n"); + } + printf("\n"); + printf("\n"); +}; + +static void +print_algo_info(const struct params_s *params) +{ + struct custom_job_params *job_params; + uint32_t i; + + for (i = 0; i < DIM(aead_algo_str_map); i++) { + job_params = &aead_algo_str_map[i].values.job_params; + if (job_params->cipher_mode == params->cipher_mode && + job_params->hash_alg == params->hash_alg && + job_params->key_size == params->key_size) { + printf("AEAD algo = %s\n", aead_algo_str_map[i].name); + return; + } + } + + for (i = 0; i < DIM(cipher_algo_str_map); i++) { + job_params = &cipher_algo_str_map[i].values.job_params; + if (job_params->cipher_mode == params->cipher_mode && + job_params->key_size == params->key_size) { + printf("Cipher algo = %s ", + cipher_algo_str_map[i].name); + break; + } + } + for (i = 0; i < DIM(hash_algo_str_map); i++) { + job_params = &hash_algo_str_map[i].values.job_params; + if (job_params->hash_alg == params->hash_alg) { + printf("Hash algo = %s\n", hash_algo_str_map[i].name); + break; + } + } +} + +static void +print_arch_info(const enum arch_type_e arch) +{ + uint32_t i; + + for (i = 0; i < DIM(arch_str_map); i++) { + if (arch_str_map[i].values.arch_type == arch) + printf("Architecture = %s\n", + arch_str_map[i].name); + } +} + +static int +fill_job(JOB_AES_HMAC *job, const struct params_s *params, + uint8_t *buf, uint8_t *digest, const uint8_t *aad, + const uint32_t buf_size, const uint8_t tag_size, + JOB_CIPHER_DIRECTION cipher_dir, + struct cipher_auth_keys *keys, uint8_t *iv) +{ + static const void *ks_ptr[3]; + uint32_t *k1_expanded = keys->k1_expanded; + uint8_t *k2 = keys->k2; + uint8_t *k3 = keys->k3; + uint32_t *enc_keys = keys->enc_keys; + uint32_t *dec_keys = keys->dec_keys; + uint8_t *ipad = keys->ipad; + uint8_t *opad = keys->opad; + struct gcm_key_data *gdata_key = &keys->gdata_key; + + /* Force partial byte, by substracting 3 bits from the full length */ + if (params->cipher_mode == CNTR_BITLEN) + job->msg_len_to_cipher_in_bits = buf_size * 8 - 3; + else + job->msg_len_to_cipher_in_bytes = buf_size; + + job->msg_len_to_hash_in_bytes = buf_size; + job->hash_start_src_offset_in_bytes = 0; + job->cipher_start_src_offset_in_bytes = 0; + job->iv = iv; + + if (params->cipher_mode == PON_AES_CNTR) { + /* Substract XGEM header */ + job->msg_len_to_cipher_in_bytes -= 8; + job->cipher_start_src_offset_in_bytes = 8; + /* If no crypto needed, set msg_len_to_cipher to 0 */ + if (params->key_size == 0) + job->msg_len_to_cipher_in_bytes = 0; + } + + /* In-place operation */ + job->src = buf; + job->dst = buf + job->cipher_start_src_offset_in_bytes; + job->auth_tag_output = digest; + + job->hash_alg = params->hash_alg; + switch (params->hash_alg) { + case AES_XCBC: + job->u.XCBC._k1_expanded = k1_expanded; + job->u.XCBC._k2 = k2; + job->u.XCBC._k3 = k3; + break; + case AES_CMAC: + job->u.CMAC._key_expanded = k1_expanded; + job->u.CMAC._skey1 = k2; + job->u.CMAC._skey2 = k3; + break; + case AES_CMAC_BITLEN: + job->u.CMAC._key_expanded = k1_expanded; + job->u.CMAC._skey1 = k2; + job->u.CMAC._skey2 = k3; + /* + * CMAC bit level version is done in bits (length is + * converted to bits and it is decreased by 4 bits, + * to force the CMAC bitlen path) + */ + job->msg_len_to_hash_in_bits = + (job->msg_len_to_hash_in_bytes * 8) - 4; + break; + case SHA1: + case SHA_224: + case SHA_256: + case SHA_384: + case SHA_512: + case MD5: + /* HMAC hash alg is SHA1 or MD5 */ + job->u.HMAC._hashed_auth_key_xor_ipad = + (uint8_t *) ipad; + job->u.HMAC._hashed_auth_key_xor_opad = + (uint8_t *) opad; + break; + case PON_CRC_BIP: + case NULL_HASH: + case AES_GMAC: + case AES_CCM: + case PLAIN_SHA1: + case PLAIN_SHA_224: + case PLAIN_SHA_256: + case PLAIN_SHA_384: + case PLAIN_SHA_512: + /* No operation needed */ + break; + default: + printf("Unsupported hash algorithm\n"); + return -1; + } + + job->auth_tag_output_len_in_bytes = tag_size; + + job->cipher_direction = cipher_dir; + + if (params->cipher_mode == NULL_CIPHER) { + job->chain_order = HASH_CIPHER; + } else if (params->cipher_mode == CCM) { + if (job->cipher_direction == ENCRYPT) + job->chain_order = HASH_CIPHER; + else + job->chain_order = CIPHER_HASH; + } else { + if (job->cipher_direction == ENCRYPT) + job->chain_order = CIPHER_HASH; + else + job->chain_order = HASH_CIPHER; + } + + /* Translating enum to the API's one */ + job->cipher_mode = params->cipher_mode; + job->aes_key_len_in_bytes = params->key_size; + + switch (job->cipher_mode) { + case CBC: + case DOCSIS_SEC_BPI: + job->aes_enc_key_expanded = enc_keys; + job->aes_dec_key_expanded = dec_keys; + job->iv_len_in_bytes = 16; + break; + case PON_AES_CNTR: + case CNTR: + case CNTR_BITLEN: + job->aes_enc_key_expanded = enc_keys; + job->aes_dec_key_expanded = enc_keys; + job->iv_len_in_bytes = 16; + break; + case GCM: + job->aes_enc_key_expanded = gdata_key; + job->aes_dec_key_expanded = gdata_key; + job->u.GCM.aad_len_in_bytes = params->aad_size; + job->u.GCM.aad = aad; + job->iv_len_in_bytes = 12; + break; + case CCM: + job->msg_len_to_cipher_in_bytes = buf_size; + job->msg_len_to_hash_in_bytes = buf_size; + job->hash_start_src_offset_in_bytes = 0; + job->cipher_start_src_offset_in_bytes = 0; + job->u.CCM.aad_len_in_bytes = params->aad_size; + job->u.CCM.aad = aad; + job->aes_enc_key_expanded = enc_keys; + job->aes_dec_key_expanded = enc_keys; + job->iv_len_in_bytes = 13; + break; + case DES: + case DOCSIS_DES: + job->aes_enc_key_expanded = enc_keys; + job->aes_dec_key_expanded = enc_keys; + job->iv_len_in_bytes = 8; + break; + case DES3: + ks_ptr[0] = ks_ptr[1] = ks_ptr[2] = enc_keys; + job->aes_enc_key_expanded = ks_ptr; + job->aes_dec_key_expanded = ks_ptr; + job->iv_len_in_bytes = 8; + break; + case ECB: + job->aes_enc_key_expanded = enc_keys; + job->aes_dec_key_expanded = dec_keys; + job->iv_len_in_bytes = 0; + break; + case NULL_CIPHER: + /* No operation needed */ + break; + default: + printf("Unsupported cipher mode\n"); + return -1; + } + + return 0; +} + +static int +prepare_keys(MB_MGR *mb_mgr, struct cipher_auth_keys *keys, + const uint8_t *key, const struct params_s *params, + const unsigned int force_pattern) +{ + uint8_t *buf = keys->temp_buf; + uint32_t *dust = keys->dust; + uint32_t *k1_expanded = keys->k1_expanded; + uint8_t *k2 = keys->k2; + uint8_t *k3 = keys->k3; + uint32_t *enc_keys = keys->enc_keys; + uint32_t *dec_keys = keys->dec_keys; + uint8_t *ipad = keys->ipad; + uint8_t *opad = keys->opad; + struct gcm_key_data *gdata_key = &keys->gdata_key; + uint8_t i; + + /* Set all expanded keys to KEY_PATTERN if flag is set */ + if (force_pattern) { + switch (params->hash_alg) { + case AES_XCBC: + memset(k1_expanded, KEY_PATTERN, + sizeof(keys->k1_expanded)); + break; + case AES_CMAC: + case AES_CMAC_BITLEN: + memset(k1_expanded, KEY_PATTERN, + sizeof(keys->k1_expanded)); + memset(k2, KEY_PATTERN, sizeof(keys->k2)); + memset(k3, KEY_PATTERN, sizeof(keys->k3)); + break; + case SHA1: + case SHA_224: + case SHA_256: + case SHA_384: + case SHA_512: + case MD5: + memset(ipad, KEY_PATTERN, sizeof(keys->ipad)); + memset(opad, KEY_PATTERN, sizeof(keys->opad)); + break; + case AES_CCM: + case AES_GMAC: + case NULL_HASH: + case PLAIN_SHA1: + case PLAIN_SHA_224: + case PLAIN_SHA_256: + case PLAIN_SHA_384: + case PLAIN_SHA_512: + case PON_CRC_BIP: + /* No operation needed */ + break; + default: + fprintf(stderr, "Unsupported hash algo\n"); + return -1; + } + + switch (params->cipher_mode) { + case GCM: + memset(gdata_key, KEY_PATTERN, sizeof(keys->gdata_key)); + break; + case PON_AES_CNTR: + case CBC: + case CCM: + case CNTR: + case CNTR_BITLEN: + case DOCSIS_SEC_BPI: + case ECB: + memset(enc_keys, KEY_PATTERN, sizeof(keys->enc_keys)); + memset(dec_keys, KEY_PATTERN, sizeof(keys->dec_keys)); + break; + case DES: + case DES3: + case DOCSIS_DES: + memset(enc_keys, KEY_PATTERN, sizeof(keys->enc_keys)); + break; + case NULL_CIPHER: + /* No operation needed */ + break; + default: + fprintf(stderr, "Unsupported cipher mode\n"); + return -1; + } + + return 0; + } + + switch (params->hash_alg) { + case AES_XCBC: + IMB_AES_XCBC_KEYEXP(mb_mgr, key, k1_expanded, k2, k3); + break; + case AES_CMAC: + case AES_CMAC_BITLEN: + IMB_AES_KEYEXP_128(mb_mgr, key, k1_expanded, dust); + IMB_AES_CMAC_SUBKEY_GEN_128(mb_mgr, k1_expanded, k2, k3); + break; + case SHA1: + /* compute ipad hash */ + memset(buf, 0x36, SHA1_BLOCK_SIZE); + for (i = 0; i < SHA1_BLOCK_SIZE; i++) + buf[i] ^= key[i]; + IMB_SHA1_ONE_BLOCK(mb_mgr, buf, ipad); + + /* compute opad hash */ + memset(buf, 0x5c, SHA1_BLOCK_SIZE); + for (i = 0; i < SHA1_BLOCK_SIZE; i++) + buf[i] ^= key[i]; + IMB_SHA1_ONE_BLOCK(mb_mgr, buf, opad); + + break; + case SHA_224: + /* compute ipad hash */ + memset(buf, 0x36, SHA_256_BLOCK_SIZE); + for (i = 0; i < SHA_256_BLOCK_SIZE; i++) + buf[i] ^= key[i]; + IMB_SHA224_ONE_BLOCK(mb_mgr, buf, ipad); + + /* compute opad hash */ + memset(buf, 0x5c, SHA_256_BLOCK_SIZE); + for (i = 0; i < SHA_256_BLOCK_SIZE; i++) + buf[i] ^= key[i]; + IMB_SHA224_ONE_BLOCK(mb_mgr, buf, opad); + + break; + case SHA_256: + /* compute ipad hash */ + memset(buf, 0x36, SHA_256_BLOCK_SIZE); + for (i = 0; i < SHA_256_BLOCK_SIZE; i++) + buf[i] ^= key[i]; + IMB_SHA256_ONE_BLOCK(mb_mgr, buf, ipad); + + /* compute opad hash */ + memset(buf, 0x5c, SHA_256_BLOCK_SIZE); + for (i = 0; i < SHA_256_BLOCK_SIZE; i++) + buf[i] ^= key[i]; + IMB_SHA256_ONE_BLOCK(mb_mgr, buf, opad); + + break; + case SHA_384: + /* compute ipad hash */ + memset(buf, 0x36, SHA_384_BLOCK_SIZE); + for (i = 0; i < SHA_384_BLOCK_SIZE; i++) + buf[i] ^= key[i]; + IMB_SHA384_ONE_BLOCK(mb_mgr, buf, ipad); + + /* compute opad hash */ + memset(buf, 0x5c, SHA_384_BLOCK_SIZE); + for (i = 0; i < SHA_384_BLOCK_SIZE; i++) + buf[i] ^= key[i]; + IMB_SHA384_ONE_BLOCK(mb_mgr, buf, opad); + + break; + case SHA_512: + /* compute ipad hash */ + memset(buf, 0x36, SHA_512_BLOCK_SIZE); + for (i = 0; i < SHA_512_BLOCK_SIZE; i++) + buf[i] ^= key[i]; + IMB_SHA512_ONE_BLOCK(mb_mgr, buf, ipad); + + /* compute opad hash */ + memset(buf, 0x5c, SHA_512_BLOCK_SIZE); + for (i = 0; i < SHA_512_BLOCK_SIZE; i++) + buf[i] ^= key[i]; + IMB_SHA512_ONE_BLOCK(mb_mgr, buf, opad); + + break; + case MD5: + /* compute ipad hash */ + memset(buf, 0x36, 64); + for (i = 0; i < 64; i++) + buf[i] ^= key[i]; + IMB_MD5_ONE_BLOCK(mb_mgr, buf, ipad); + + /* compute opad hash */ + memset(buf, 0x5c, 64); + for (i = 0; i < 64; i++) + buf[i] ^= key[i]; + IMB_MD5_ONE_BLOCK(mb_mgr, buf, opad); + + break; + case AES_CCM: + case AES_GMAC: + case NULL_HASH: + case PLAIN_SHA1: + case PLAIN_SHA_224: + case PLAIN_SHA_256: + case PLAIN_SHA_384: + case PLAIN_SHA_512: + case PON_CRC_BIP: + /* No operation needed */ + break; + default: + fprintf(stderr, "Unsupported hash algo\n"); + return -1; + } + + switch (params->cipher_mode) { + case GCM: + switch (params->key_size) { + case AES_128_BYTES: + IMB_AES128_GCM_PRE(mb_mgr, key, gdata_key); + break; + case AES_192_BYTES: + IMB_AES192_GCM_PRE(mb_mgr, key, gdata_key); + break; + case AES_256_BYTES: + IMB_AES256_GCM_PRE(mb_mgr, key, gdata_key); + break; + default: + fprintf(stderr, "Wrong key size\n"); + return -1; + } + break; + case PON_AES_CNTR: + switch (params->key_size) { + case 16: + IMB_AES_KEYEXP_128(mb_mgr, key, enc_keys, dec_keys); + break; + case 0: + break; + default: + fprintf(stderr, "Wrong key size\n"); + return -1; + } + break; + case CBC: + case CCM: + case CNTR: + case CNTR_BITLEN: + case DOCSIS_SEC_BPI: + case ECB: + switch (params->key_size) { + case AES_128_BYTES: + IMB_AES_KEYEXP_128(mb_mgr, key, enc_keys, dec_keys); + break; + case AES_192_BYTES: + IMB_AES_KEYEXP_192(mb_mgr, key, enc_keys, dec_keys); + break; + case AES_256_BYTES: + IMB_AES_KEYEXP_256(mb_mgr, key, enc_keys, dec_keys); + break; + default: + fprintf(stderr, "Wrong key size\n"); + return -1; + } + break; + case DES: + case DES3: + case DOCSIS_DES: + des_key_schedule((uint64_t *) enc_keys, key); + break; + case NULL_CIPHER: + /* No operation needed */ + break; + default: + fprintf(stderr, "Unsupported cipher mode\n"); + return -1; + } + + return 0; +} + +/* Modify the test buffer to set the HEC value and CRC, so the final + * decrypted message can be compared against the test buffer */ +static int +modify_pon_test_buf(uint8_t *test_buf, const struct params_s *params, + const JOB_AES_HMAC *job, const uint64_t xgem_hdr) +{ + /* Set plaintext CRC in test buffer for PON */ + uint32_t *buf32 = (uint32_t *) &test_buf[8 + params->buf_size - 4]; + uint64_t *buf64 = (uint64_t *) test_buf; + const uint32_t *tag32 = (uint32_t *) job->auth_tag_output; + const uint64_t hec_mask = BSWAP64(0xfffffffffffe000); + const uint64_t xgem_hdr_out = ((const uint64_t *)job->src)[0]; + + if (params->buf_size >= 5) + buf32[0] = tag32[1]; + + /* Check if any bits apart from HEC are modified */ + if ((xgem_hdr_out & hec_mask) != (xgem_hdr & hec_mask)) { + fprintf(stderr, "XGEM header overwritten outside HEC\n"); + fprintf(stderr, "Original XGEM header: %"PRIx64"\n", + xgem_hdr & hec_mask ); + fprintf(stderr, "Output XGEM header: %"PRIx64"\n", + xgem_hdr_out & hec_mask); + return -1; + } + + /* Modify original XGEM header to include calculated HEC */ + buf64[0] = xgem_hdr_out; + + return 0; +} + +/* + * Checks for sensitive information in registers, stack and MB_MGR + * (in this order, to try to minimize pollution of the data left out + * after the job completion, due to these actual checks). + * + * Returns -1 if sensitive information was found or 0 if not. + */ +static int +perform_safe_checks(MB_MGR *mgr, const enum arch_type_e arch, + const char *dir) +{ + uint8_t *rsp_ptr; + uint32_t simd_size = 0; + + dump_gps(); + switch (arch) { + case ARCH_SSE: + case ARCH_AESNI_EMU: + dump_xmms_sse(); + simd_size = XMM_MEM_SIZE; + break; + case ARCH_AVX: + dump_xmms_avx(); + simd_size = XMM_MEM_SIZE; + break; + case ARCH_AVX2: + dump_ymms(); + simd_size = YMM_MEM_SIZE; + break; + case ARCH_AVX512: + dump_zmms(); + simd_size = ZMM_MEM_SIZE; + break; + default: + fprintf(stderr, + "Error getting the architecture\n"); + return -1; + } + if (search_patterns(gps, GP_MEM_SIZE) == 0) { + fprintf(stderr, "Pattern found in GP registers " + "after %s data\n", dir); + return -1; + } + if (search_patterns(simd_regs, simd_size) == 0) { + fprintf(stderr, "Pattern found in SIMD " + "registers after %s data\n", dir); + return -1; + } + rsp_ptr = rdrsp(); + if (search_patterns((rsp_ptr - STACK_DEPTH), + STACK_DEPTH) == 0) { + fprintf(stderr, "Pattern found in stack after " + "%s data\n", dir); + return -1; + } + if (search_patterns(mgr, sizeof(MB_MGR)) == 0) { + fprintf(stderr, "Pattern found in MB_MGR after " + "%s data\n", dir); + return -1; + } + + return 0; +} + +static void +clear_scratch_simd(const enum arch_type_e arch) +{ + switch (arch) { + case ARCH_SSE: + case ARCH_AESNI_EMU: + clear_scratch_xmms_sse(); + break; + case ARCH_AVX: + clear_scratch_xmms_avx(); + break; + case ARCH_AVX2: + clear_scratch_ymms(); + break; + case ARCH_AVX512: + clear_scratch_zmms(); + break; + default: + fprintf(stderr, "Invalid architecture\n"); + exit(EXIT_FAILURE); + } +} + +/* Performs test using AES_HMAC or DOCSIS */ +static int +do_test(MB_MGR *enc_mb_mgr, const enum arch_type_e enc_arch, + MB_MGR *dec_mb_mgr, const enum arch_type_e dec_arch, + const struct params_s *params, struct data *data, + const unsigned safe_check) +{ + JOB_AES_HMAC *job; + uint32_t i; + int ret = -1; + uint32_t buf_size = params->buf_size; + uint8_t tag_size = auth_tag_length_bytes[params->hash_alg - 1]; + uint64_t xgem_hdr = 0; + uint8_t tag_size_to_check = 0; + struct cipher_auth_keys *enc_keys = &data->enc_keys; + struct cipher_auth_keys *dec_keys = &data->dec_keys; + uint8_t *aad = data->aad; + uint8_t *iv = data->iv; + uint8_t *in_digest = data->in_digest; + uint8_t *out_digest = data->out_digest; + uint8_t *test_buf = data->test_buf; + uint8_t *src_dst_buf = data->src_dst_buf; + uint8_t *key = data->key; + + if (params->hash_alg == PON_CRC_BIP) { + /* Buf size is XGEM payload, including CRC, + * allocate space for XGEM header and padding */ + buf_size = buf_size + 8; + if (buf_size % 8) + buf_size = (buf_size + 8) & 0xfffffff8; + /* Only first 4 bytes are checked, corresponding to BIP */ + tag_size_to_check = 4; + } + + /* If performing a test searching for sensitive information, + * set keys and plaintext to known values, + * so they can be searched later on in the MB_MGR structure and stack. + * Otherwise, just randomize the data */ + if (safe_check) { + memset(test_buf, PT_PATTERN, buf_size); + memset(key, KEY_PATTERN, MAX_KEY_SIZE); + } else { + generate_random_buf(test_buf, buf_size); + generate_random_buf(key, MAX_KEY_SIZE); + generate_random_buf(iv, MAX_IV_SIZE); + generate_random_buf(aad, AAD_SIZE); + } + + /* For PON, construct the XGEM header, setting valid PLI */ + if (params->hash_alg == PON_CRC_BIP) { + /* create XGEM header template */ + const uint64_t pli = ((params->buf_size) << 2) & 0xffff; + uint64_t *p_src = (uint64_t *)test_buf; + + xgem_hdr = ((pli >> 8) & 0xff) | ((pli & 0xff) << 8); + p_src[0] = xgem_hdr; + } + + /* + * Expand/schedule keys. + * If checking for sensitive information, first use actual + * key expansion functions and check the stack for left over + * information and then set a pattern in the expanded key memory + * to search for later on. + * If not checking for sensitive information, just use the key + * expansion functions. + */ + if (safe_check) { + uint8_t *rsp_ptr; + + /* Clear scratch registers before expanding keys to prevent + * other functions from storing sensitive data in stack */ + clear_scratch_simd(enc_arch); + if (prepare_keys(enc_mb_mgr, enc_keys, key, params, 0) < 0) + goto exit; + + rsp_ptr = rdrsp(); + if (search_patterns((rsp_ptr - STACK_DEPTH), + STACK_DEPTH) == 0) { + fprintf(stderr, "Pattern found in stack after " + "expanding encryption keys\n"); + goto exit; + } + + if (prepare_keys(dec_mb_mgr, dec_keys, key, params, 0) < 0) + goto exit; + + rsp_ptr = rdrsp(); + if (search_patterns((rsp_ptr - STACK_DEPTH), + STACK_DEPTH) == 0) { + fprintf(stderr, "Pattern found in stack after " + "expanding decryption keys\n"); + goto exit; + } + + if (prepare_keys(enc_mb_mgr, enc_keys, key, params, 1) < 0) + goto exit; + + if (prepare_keys(enc_mb_mgr, dec_keys, key, params, 1) < 0) + goto exit; + } else { + if (prepare_keys(enc_mb_mgr, enc_keys, key, params, 0) < 0) + goto exit; + + if (prepare_keys(enc_mb_mgr, dec_keys, key, params, 0) < 0) + goto exit; + } + + for (i = 0; i < job_iter; i++) { + job = IMB_GET_NEXT_JOB(enc_mb_mgr); + /* + * Encrypt + generate digest from encrypted message + * using architecture under test + */ + memcpy(src_dst_buf, test_buf, buf_size); + if (fill_job(job, params, src_dst_buf, in_digest, aad, + buf_size, tag_size, ENCRYPT, enc_keys, iv) < 0) + goto exit; + + /* Randomize memory for input digest */ + generate_random_buf(in_digest, tag_size); + + /* Clear scratch registers before submitting job to prevent + * other functions from storing sensitive data in stack */ + if (safe_check) + clear_scratch_simd(enc_arch); + job = IMB_SUBMIT_JOB(enc_mb_mgr); + + if (!job) + job = IMB_FLUSH_JOB(enc_mb_mgr); + + if (!job) { + fprintf(stderr, "job not returned\n"); + goto exit; + } + + /* Check that the registers, stack and MB_MGR do not contain any + * sensitive information after job is returned */ + if (safe_check) + if (perform_safe_checks(enc_mb_mgr, enc_arch, + "encrypting") < 0) + goto exit; + + if (job->status != STS_COMPLETED) { + fprintf(stderr, "failed job, status:%d\n", + job->status); + goto exit; + } + + if (params->hash_alg == PON_CRC_BIP) { + if (modify_pon_test_buf(test_buf, params, job, + xgem_hdr) < 0) + goto exit; + } + + job = IMB_GET_NEXT_JOB(dec_mb_mgr); + + /* Randomize memory for input digest */ + generate_random_buf(out_digest, tag_size); + + /* + * Generate digest from encrypted message and decrypt + * using reference architecture + */ + if (fill_job(job, params, src_dst_buf, out_digest, aad, + buf_size, tag_size, DECRYPT, dec_keys, iv) < 0) + goto exit; + + /* Clear scratch registers before submitting job to prevent + * other functions from storing sensitive data in stack */ + if (safe_check) + clear_scratch_simd(dec_arch); + job = IMB_SUBMIT_JOB(dec_mb_mgr); + + if (!job) + job = IMB_FLUSH_JOB(dec_mb_mgr); + + /* Check that the registers, stack and MB_MGR do not contain any + * sensitive information after job is returned */ + if (safe_check) + if (perform_safe_checks(dec_mb_mgr, dec_arch, + "decrypting") < 0) + goto exit; + + if (!job) { + fprintf(stderr, "job not returned\n"); + goto exit; + } + + if (job->status != STS_COMPLETED) { + fprintf(stderr, "failed job, status:%d\n", + job->status); + goto exit; + } + + if (params->hash_alg != NULL_HASH && + memcmp(in_digest, out_digest, tag_size_to_check) != 0) { + fprintf(stderr, + "\nInput and output tags don't match\n"); + byte_hexdump("Input digest", in_digest, + tag_size_to_check); + byte_hexdump("Output digest", out_digest, + tag_size_to_check); + goto exit; + } + + if (params->cipher_mode != NULL_CIPHER && + memcmp(src_dst_buf, test_buf, buf_size) != 0) { + fprintf(stderr, + "\nDecrypted text and plaintext don't match\n"); + byte_hexdump("Plaintext (orig)", test_buf, buf_size); + byte_hexdump("Decrypted msg", src_dst_buf, buf_size); + goto exit; + } + + if (params->hash_alg == PON_CRC_BIP && params->buf_size > 4) { + const uint64_t plen = params->buf_size - 4; + + if (memcmp(src_dst_buf + 8 + plen, + out_digest + 4, 4) != 0) { + fprintf(stderr, "\nDecrypted CRC and calculated" + " CRC don't match\n"); + byte_hexdump("Decrypted CRC", + src_dst_buf + 8 + plen, 4); + byte_hexdump("Calculated CRC", + out_digest + 4, 4); + goto exit; + } + } + } + + ret = 0; + +exit: + if (ret < 0) { + printf("Failures in\n"); + print_algo_info(params); + printf("Encrypting "); + print_arch_info(enc_arch); + printf("Decrypting "); + print_arch_info(dec_arch); + printf("Buffer size = %u\n", params->buf_size); + printf("Key size = %u\n", params->key_size); + printf("Tag size = %u\n", tag_size); + } + + return ret; +} + +/* Runs test for each buffer size */ +static void +process_variant(MB_MGR *enc_mgr, const enum arch_type_e enc_arch, + MB_MGR *dec_mgr, const enum arch_type_e dec_arch, + struct params_s *params, struct data *variant_data, + const unsigned int safe_check) +{ + const uint32_t sizes = params->num_sizes; + uint32_t sz; + + if (verbose) { + printf("Testing "); + print_algo_info(params); + } + + /* Reset the variant data */ + memset(variant_data, 0, sizeof(struct data)); + + for (sz = 0; sz < sizes; sz++) { + const uint32_t buf_size = job_sizes[RANGE_MIN] + + (sz * job_sizes[RANGE_STEP]); + params->aad_size = AAD_SIZE; + + params->buf_size = buf_size; + + /* + * CBC and ECB operation modes do not support lengths which are + * non-multiple of block size + */ + if (params->cipher_mode == CBC || params->cipher_mode == ECB) + if ((buf_size % AES_BLOCK_SIZE) != 0) + continue; + + if (params->cipher_mode == DES || params->cipher_mode == DES3) + if ((buf_size % DES_BLOCK_SIZE) != 0) + continue; + + /* Check for sensitive data first, then normal cross + * architecture validation */ + if (safe_check && do_test(enc_mgr, enc_arch, dec_mgr, dec_arch, + params, variant_data, 1) < 0) + exit(EXIT_FAILURE); + + if (do_test(enc_mgr, enc_arch, dec_mgr, dec_arch, + params, variant_data, 0) < 0) + exit(EXIT_FAILURE); + + } +} + +/* Sets cipher direction and key size */ +static void +run_test(const enum arch_type_e enc_arch, const enum arch_type_e dec_arch, + struct params_s *params, struct data *variant_data, + const unsigned int safe_check) +{ + MB_MGR *enc_mgr = NULL; + MB_MGR *dec_mgr = NULL; + + if (enc_arch == ARCH_AESNI_EMU) + enc_mgr = alloc_mb_mgr(flags | IMB_FLAG_AESNI_OFF); + else + enc_mgr = alloc_mb_mgr(flags); + + if (enc_mgr == NULL) { + fprintf(stderr, "MB MGR could not be allocated\n"); + exit(EXIT_FAILURE); + } + + /* Reset the MB MGR structure in case it is allocated with + * memory containing the patterns that will be searched later on */ + if (safe_check) + memset(enc_mgr, 0, sizeof(MB_MGR)); + + switch (enc_arch) { + case ARCH_SSE: + case ARCH_AESNI_EMU: + init_mb_mgr_sse(enc_mgr); + break; + case ARCH_AVX: + init_mb_mgr_avx(enc_mgr); + break; + case ARCH_AVX2: + init_mb_mgr_avx2(enc_mgr); + break; + case ARCH_AVX512: + init_mb_mgr_avx512(enc_mgr); + break; + default: + fprintf(stderr, "Invalid architecture\n"); + exit(EXIT_FAILURE); + } + + if (dec_arch == ARCH_AESNI_EMU) + dec_mgr = alloc_mb_mgr(flags | IMB_FLAG_AESNI_OFF); + else + dec_mgr = alloc_mb_mgr(flags); + + if (dec_mgr == NULL) { + fprintf(stderr, "MB MGR could not be allocated\n"); + exit(EXIT_FAILURE); + } + + /* Reset the MB MGR structure in case it is allocated with + * memory containing the patterns that will be searched later on */ + if (safe_check) + memset(dec_mgr, 0, sizeof(MB_MGR)); + + switch (dec_arch) { + case ARCH_SSE: + case ARCH_AESNI_EMU: + init_mb_mgr_sse(dec_mgr); + break; + case ARCH_AVX: + init_mb_mgr_avx(dec_mgr); + break; + case ARCH_AVX2: + init_mb_mgr_avx2(dec_mgr); + break; + case ARCH_AVX512: + init_mb_mgr_avx512(dec_mgr); + break; + default: + fprintf(stderr, "Invalid architecture\n"); + exit(EXIT_FAILURE); + } + + if (custom_test) { + params->key_size = custom_job_params.key_size; + params->cipher_mode = custom_job_params.cipher_mode; + params->hash_alg = custom_job_params.hash_alg; + process_variant(enc_mgr, enc_arch, dec_mgr, dec_arch, params, + variant_data, safe_check); + goto exit; + } + + JOB_HASH_ALG hash_alg; + JOB_CIPHER_MODE c_mode; + + for (c_mode = CBC; c_mode <= CNTR_BITLEN; c_mode++) { + /* Skip CUSTOM_CIPHER */ + if (c_mode == CUSTOM_CIPHER) + continue; + params->cipher_mode = c_mode; + uint8_t min_sz = key_sizes[c_mode - 1][0]; + uint8_t max_sz = key_sizes[c_mode - 1][1]; + uint8_t step_sz = key_sizes[c_mode - 1][2]; + uint8_t key_sz; + + for (key_sz = min_sz; key_sz <= max_sz; key_sz += step_sz) { + params->key_size = key_sz; + for (hash_alg = SHA1; hash_alg <= PON_CRC_BIP; + hash_alg++) { + /* Skip CUSTOM_HASH */ + if (hash_alg == CUSTOM_HASH) + continue; + + /* Skip not supported combinations */ + if ((c_mode == GCM && hash_alg != AES_GMAC) || + (c_mode != GCM && hash_alg == AES_GMAC)) + continue; + if ((c_mode == CCM && hash_alg != AES_CCM) || + (c_mode != CCM && hash_alg == AES_CCM)) + continue; + if ((c_mode == PON_AES_CNTR && + hash_alg != PON_CRC_BIP) || + (c_mode != PON_AES_CNTR && + hash_alg == PON_CRC_BIP)) + continue; + + params->hash_alg = hash_alg; + process_variant(enc_mgr, enc_arch, dec_mgr, + dec_arch, params, variant_data, + safe_check); + } + } + } + +exit: + free_mb_mgr(enc_mgr); + free_mb_mgr(dec_mgr); +} + +/* Prepares data structure for test variants storage, + * sets test configuration + */ +static void +run_tests(const unsigned int safe_check) +{ + struct params_s params; + struct data *variant_data = NULL; + enum arch_type_e enc_arch, dec_arch; + const uint32_t min_size = job_sizes[RANGE_MIN]; + const uint32_t max_size = job_sizes[RANGE_MAX]; + const uint32_t step_size = job_sizes[RANGE_STEP]; + + params.num_sizes = ((max_size - min_size) / step_size) + 1; + + variant_data = malloc(sizeof(struct data)); + + if (variant_data == NULL) { + fprintf(stderr, "Test data could not be allocated\n"); + exit(EXIT_FAILURE); + } + + if (verbose) { + if (min_size == max_size) + printf("Testing buffer size = %u bytes\n", min_size); + else + printf("Testing buffer sizes from %u to %u " + "in steps of %u bytes\n", + min_size, max_size, step_size); + } + /* Performing tests for each selected architecture */ + for (enc_arch = ARCH_SSE; enc_arch < NUM_ARCHS; enc_arch++) { + if (enc_archs[enc_arch] == 0) + continue; + printf("\nEncrypting with "); + print_arch_info(enc_arch); + + for (dec_arch = ARCH_SSE; dec_arch < NUM_ARCHS; dec_arch++) { + if (dec_archs[dec_arch] == 0) + continue; + printf("\tDecrypting with "); + print_arch_info(dec_arch); + run_test(enc_arch, dec_arch, ¶ms, variant_data, + safe_check); + } + + } /* end for run */ + + free(variant_data); +} + +static void usage(void) +{ + fprintf(stderr, "Usage: exhaustive_test [args], " + "where args are zero or more\n" + "-h: print this message\n" + "-v: verbose, prints extra information\n" + "--enc-arch: encrypting with architecture " + "(AESNI_EMU/SSE/AVX/AVX2/AVX512)\n" + "--dec-arch: decrypting with architecture " + "(AESNI_EMU/SSE/AVX/AVX2/AVX512)\n" + "--cipher-algo: Select cipher algorithm to run on the custom " + "test\n" + "--hash-algo: Select hash algorithm to run on the custom test\n" + "--aead-algo: Select AEAD algorithm to run on the custom test\n" + "--no-avx512: Don't do AVX512\n" + "--no-avx2: Don't do AVX2\n" + "--no-avx: Don't do AVX\n" + "--no-sse: Don't do SSE\n" + "--aesni-emu: Do AESNI_EMU (disabled by default)\n" + "--shani-on: use SHA extensions, default: auto-detect\n" + "--shani-off: don't use SHA extensions\n" + "--job-size: size of the cipher & MAC job in bytes. " + "It can be:\n" + " - single value: test single size\n" + " - range: test multiple sizes with following format" + " min:step:max (e.g. 16:16:256)\n" + " (-o still applies for MAC)\n" + "--job-iter: number of tests iterations for each job size\n" + "--safe-check: check if keys, IVs, plaintext or tags " + "get cleared from MB_MGR upon job completion (off by default; " + "requires library compiled with SAFE_DATA)\n"); +} + +static int +get_next_num_arg(const char * const *argv, const int index, const int argc, + void *dst, const size_t dst_size) +{ + char *endptr = NULL; + uint64_t val; + + if (dst == NULL || argv == NULL || index < 0 || argc < 0) { + fprintf(stderr, "%s() internal error!\n", __func__); + exit(EXIT_FAILURE); + } + + if (index >= (argc - 1)) { + fprintf(stderr, "'%s' requires an argument!\n", argv[index]); + exit(EXIT_FAILURE); + } + +#ifdef _WIN32 + val = _strtoui64(argv[index + 1], &endptr, 0); +#else + val = strtoull(argv[index + 1], &endptr, 0); +#endif + if (endptr == argv[index + 1] || (endptr != NULL && *endptr != '\0')) { + fprintf(stderr, "Error converting '%s' as value for '%s'!\n", + argv[index + 1], argv[index]); + exit(EXIT_FAILURE); + } + + switch (dst_size) { + case (sizeof(uint8_t)): + *((uint8_t *)dst) = (uint8_t) val; + break; + case (sizeof(uint16_t)): + *((uint16_t *)dst) = (uint16_t) val; + break; + case (sizeof(uint32_t)): + *((uint32_t *)dst) = (uint32_t) val; + break; + case (sizeof(uint64_t)): + *((uint64_t *)dst) = val; + break; + default: + fprintf(stderr, "%s() invalid dst_size %u!\n", + __func__, (unsigned) dst_size); + exit(EXIT_FAILURE); + break; + } + + return index + 1; +} + +static int +detect_arch(unsigned int arch_support[NUM_ARCHS]) +{ + const uint64_t detect_sse = + IMB_FEATURE_SSE4_2 | IMB_FEATURE_CMOV | IMB_FEATURE_AESNI; + const uint64_t detect_avx = + IMB_FEATURE_AVX | IMB_FEATURE_CMOV | IMB_FEATURE_AESNI; + const uint64_t detect_avx2 = IMB_FEATURE_AVX2 | detect_avx; + const uint64_t detect_avx512 = IMB_FEATURE_AVX512_SKX | detect_avx2; + MB_MGR *p_mgr = NULL; + enum arch_type_e arch_id; + + if (arch_support == NULL) { + fprintf(stderr, "Array not passed correctly\n"); + return -1; + } + + for (arch_id = ARCH_SSE; arch_id < NUM_ARCHS; arch_id++) + arch_support[arch_id] = 1; + + p_mgr = alloc_mb_mgr(0); + if (p_mgr == NULL) { + fprintf(stderr, "Architecture detect error!\n"); + return -1; + } + + if ((p_mgr->features & detect_avx512) != detect_avx512) + arch_support[ARCH_AVX512] = 0; + + if ((p_mgr->features & detect_avx2) != detect_avx2) + arch_support[ARCH_AVX2] = 0; + + if ((p_mgr->features & detect_avx) != detect_avx) + arch_support[ARCH_AVX] = 0; + + if ((p_mgr->features & detect_sse) != detect_sse) { + arch_support[ARCH_SSE] = 0; + arch_support[ARCH_AESNI_EMU] = 0; + } + + free_mb_mgr(p_mgr); + + return 0; +} + +/* + * Check string argument is supported and if it is, return values associated + * with it. + */ +static const union params * +check_string_arg(const char *param, const char *arg, + const struct str_value_mapping *map, + const unsigned int num_avail_opts) +{ + unsigned int i; + + if (arg == NULL) { + fprintf(stderr, "%s requires an argument\n", param); + goto exit; + } + + for (i = 0; i < num_avail_opts; i++) + if (strcmp(arg, map[i].name) == 0) + return &(map[i].values); + + /* Argument is not listed in the available options */ + fprintf(stderr, "Invalid argument for %s\n", param); +exit: + fprintf(stderr, "Accepted arguments: "); + for (i = 0; i < num_avail_opts; i++) + fprintf(stderr, "%s ", map[i].name); + fprintf(stderr, "\n"); + + return NULL; +} + +static int +parse_range(const char * const *argv, const int index, const int argc, + uint32_t range_values[NUM_RANGE]) +{ + char *token; + uint32_t number; + unsigned int i; + + + if (range_values == NULL || argv == NULL || index < 0 || argc < 0) { + fprintf(stderr, "%s() internal error!\n", __func__); + exit(EXIT_FAILURE); + } + + if (index >= (argc - 1)) { + fprintf(stderr, "'%s' requires an argument!\n", argv[index]); + exit(EXIT_FAILURE); + } + + char *copy_arg = strdup(argv[index + 1]); + + if (copy_arg == NULL) { + fprintf(stderr, "%s() internal error!\n", __func__); + exit(EXIT_FAILURE); + } + + errno = 0; + token = strtok(copy_arg, ":"); + + /* Try parsing range (minimum, step and maximum values) */ + for (i = 0; i < NUM_RANGE; i++) { + if (token == NULL) + goto no_range; + + number = strtoul(token, NULL, 10); + + if (errno != 0) + goto no_range; + + range_values[i] = number; + token = strtok(NULL, ":"); + } + + if (token != NULL) + goto no_range; + + if (range_values[RANGE_MAX] < range_values[RANGE_MIN]) { + fprintf(stderr, "Maximum value of range cannot be lower " + "than minimum value\n"); + exit(EXIT_FAILURE); + } + + if (range_values[RANGE_STEP] == 0) { + fprintf(stderr, "Step value in range cannot be 0\n"); + exit(EXIT_FAILURE); + } + + goto end_range; +no_range: + /* Try parsing as single value */ + get_next_num_arg(argv, index, argc, &job_sizes[RANGE_MIN], + sizeof(job_sizes[RANGE_MIN])); + + job_sizes[RANGE_MAX] = job_sizes[RANGE_MIN]; + +end_range: + free(copy_arg); + return (index + 1); + +} + +int main(int argc, char *argv[]) +{ + int i; + unsigned int arch_id; + unsigned int arch_support[NUM_ARCHS]; + const union params *values; + unsigned int cipher_algo_set = 0; + unsigned int hash_algo_set = 0; + unsigned int aead_algo_set = 0; + unsigned int safe_check = 0; + + for (i = 1; i < argc; i++) + if (strcmp(argv[i], "-h") == 0) { + usage(); + return EXIT_SUCCESS; + } else if (strcmp(argv[i], "-v") == 0) { + verbose = 1; + } else if (strcmp(argv[i], "--no-avx512") == 0) { + enc_archs[ARCH_AVX512] = 0; + dec_archs[ARCH_AVX512] = 0; + } else if (strcmp(argv[i], "--no-avx2") == 0) { + enc_archs[ARCH_AVX2] = 0; + dec_archs[ARCH_AVX2] = 0; + } else if (strcmp(argv[i], "--no-avx") == 0) { + enc_archs[ARCH_AVX] = 0; + dec_archs[ARCH_AVX] = 0; + } else if (strcmp(argv[i], "--no-sse") == 0) { + enc_archs[ARCH_SSE] = 0; + dec_archs[ARCH_SSE] = 0; + } else if (strcmp(argv[i], "--aesni-emu") == 0) { + enc_archs[ARCH_AESNI_EMU] = 1; + dec_archs[ARCH_AESNI_EMU] = 1; + } else if (strcmp(argv[i], "--shani-on") == 0) { + flags &= (~IMB_FLAG_SHANI_OFF); + } else if (strcmp(argv[i], "--shani-off") == 0) { + flags |= IMB_FLAG_SHANI_OFF; + } else if (strcmp(argv[i], "--enc-arch") == 0) { + values = check_string_arg(argv[i], argv[i+1], + arch_str_map, + DIM(arch_str_map)); + if (values == NULL) + return EXIT_FAILURE; + + /* + * Disable all the other architectures + * and enable only the specified + */ + memset(enc_archs, 0, sizeof(enc_archs)); + enc_archs[values->arch_type] = 1; + i++; + } else if (strcmp(argv[i], "--dec-arch") == 0) { + values = check_string_arg(argv[i], argv[i+1], + arch_str_map, + DIM(arch_str_map)); + if (values == NULL) + return EXIT_FAILURE; + + /* + * Disable all the other architectures + * and enable only the specified + */ + memset(dec_archs, 0, sizeof(dec_archs)); + dec_archs[values->arch_type] = 1; + i++; + } else if (strcmp(argv[i], "--cipher-algo") == 0) { + values = check_string_arg(argv[i], argv[i+1], + cipher_algo_str_map, + DIM(cipher_algo_str_map)); + if (values == NULL) + return EXIT_FAILURE; + + custom_job_params.cipher_mode = + values->job_params.cipher_mode; + custom_job_params.key_size = + values->job_params.key_size; + custom_test = 1; + cipher_algo_set = 1; + i++; + } else if (strcmp(argv[i], "--hash-algo") == 0) { + values = check_string_arg(argv[i], argv[i+1], + hash_algo_str_map, + DIM(hash_algo_str_map)); + if (values == NULL) + return EXIT_FAILURE; + + custom_job_params.hash_alg = + values->job_params.hash_alg; + custom_test = 1; + hash_algo_set = 1; + i++; + } else if (strcmp(argv[i], "--aead-algo") == 0) { + values = check_string_arg(argv[i], argv[i+1], + aead_algo_str_map, + DIM(aead_algo_str_map)); + if (values == NULL) + return EXIT_FAILURE; + + custom_job_params.cipher_mode = + values->job_params.cipher_mode; + custom_job_params.key_size = + values->job_params.key_size; + custom_job_params.hash_alg = + values->job_params.hash_alg; + custom_test = 1; + aead_algo_set = 1; + i++; + } else if (strcmp(argv[i], "--job-size") == 0) { + /* Try parsing the argument as a range first */ + i = parse_range((const char * const *)argv, i, argc, + job_sizes); + if (job_sizes[RANGE_MAX] > JOB_SIZE_TOP) { + fprintf(stderr, + "Invalid job size %u (max %u)\n", + (unsigned) job_sizes[RANGE_MAX], + JOB_SIZE_TOP); + return EXIT_FAILURE; + } + } else if (strcmp(argv[i], "--job-iter") == 0) { + i = get_next_num_arg((const char * const *)argv, i, + argc, &job_iter, sizeof(job_iter)); + } else if (strcmp(argv[i], "--safe-check") == 0) { + safe_check = 1; + } else { + usage(); + return EXIT_FAILURE; + } + + if (custom_test) { + if (aead_algo_set && (cipher_algo_set || hash_algo_set)) { + fprintf(stderr, "AEAD algorithm cannot be used " + "combined with another cipher/hash " + "algorithm\n"); + return EXIT_FAILURE; + } + } + + if (job_sizes[RANGE_MIN] == 0) { + fprintf(stderr, "Buffer size cannot be 0 unless only " + "an AEAD algorithm is tested\n"); + return EXIT_FAILURE; + } + + if (detect_arch(arch_support) < 0) + return EXIT_FAILURE; + + /* disable tests depending on instruction sets supported */ + for (arch_id = 0; arch_id < NUM_ARCHS; arch_id++) { + if (arch_support[arch_id] == 0) { + enc_archs[arch_id] = 0; + dec_archs[arch_id] = 0; + fprintf(stderr, + "%s not supported. Disabling %s tests\n", + arch_str_map[arch_id].name, + arch_str_map[arch_id].name); + } + } + + MB_MGR *p_mgr = alloc_mb_mgr(flags); + if (p_mgr == NULL) { + fprintf(stderr, "Error allocating MB_MGR structure!\n"); + return EXIT_FAILURE; + } + + if (safe_check && ((p_mgr->features & IMB_FEATURE_SAFE_DATA) == 0)) { + fprintf(stderr, "Library needs to be compiled with SAFE_DATA " + "if --safe-check is enabled\n"); + free_mb_mgr(p_mgr); + return EXIT_FAILURE; + } + if (enc_archs[ARCH_SSE] || dec_archs[ARCH_SSE]) { + init_mb_mgr_sse(p_mgr); + fprintf(stderr, "%s SHA extensions (shani) for SSE arch\n", + (p_mgr->features & IMB_FEATURE_SHANI) ? + "Using" : "Not using"); + } + free_mb_mgr(p_mgr); + + srand(SEED); + + run_tests(safe_check); + + return EXIT_SUCCESS; +} |