blob: 7ca314a05b267b10512c3a7a4e63333c17853d4c [file]
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* AES benchmark using directly linked BoringSSL. */
#define TLOG_TAG "swaes_bench"
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <trusty_benchmark.h>
#include <trusty_unittest.h>
#include <uapi/err.h>
#include <openssl/evp.h>
#include "vectors.h"
/*
* Define to verify crypto operation output matches the expected test vectors.
* This adds overhead (memcmp()) to the benchmark, so is not normally desired.
* However, it may be useful to verify that the correct cipher operation in
* the benchmark.
*/
#define CHECK_RESULTS
/* Number of times to run the benchmark function with each parameter */
#define RUNS 40
/* test state */
struct crypto_swaes_state {
const struct crypto_param* param;
/* Encryption and decrypt context */
EVP_CIPHER_CTX evp_ctx;
/* Cipher to use, which combines mode and size */
const EVP_CIPHER* cipher;
/* Output buffer */
void* buf;
/* Tag buffer for GCM mode */
uint8_t* tag;
};
static struct crypto_swaes_state* _state = NULL;
static struct crypto_param params[] = {
/* Key and input sizes are given in bits
* mode, key, input, direction:
*/
AES_CRYPT_ARGS(CBC, 128, 256, ENCRYPT), /* 32 bytes */
AES_CRYPT_ARGS(CBC, 128, 8192, ENCRYPT), /* 1Kbytes */
AES_CRYPT_ARGS(CBC, 128, 16384, ENCRYPT), /* 2Kbytes */
AES_CRYPT_ARGS(CBC, 128, 32768, ENCRYPT), /* 4Kbytes */
AES_CRYPT_ARGS(CBC, 128, 65536, ENCRYPT), /* 8Kbytes */
AES_CRYPT_ARGS(CBC, 128, 131072, ENCRYPT), /* 16Kbytes */
AES_CRYPT_ARGS(CBC, 128, 256, DECRYPT), /* 32 bytes */
AES_CRYPT_ARGS(CBC, 128, 8192, DECRYPT), /* 1Kbytes */
AES_CRYPT_ARGS(CBC, 128, 16384, DECRYPT), /* 2Kbytes */
AES_CRYPT_ARGS(CBC, 128, 32768, DECRYPT), /* 4Kbytes */
AES_CRYPT_ARGS(CBC, 128, 65536, DECRYPT), /* 8Kbytes */
AES_CRYPT_ARGS(CBC, 128, 131072, DECRYPT), /* 16Kbytes */
AES_CRYPT_ARGS(CBC, 256, 256, ENCRYPT), /* 32 bytes */
AES_CRYPT_ARGS(CBC, 256, 8192, ENCRYPT), /* 1Kbytes */
AES_CRYPT_ARGS(CBC, 256, 16384, ENCRYPT), /* 2Kbytes */
AES_CRYPT_ARGS(CBC, 256, 32768, ENCRYPT), /* 4Kbytes */
AES_CRYPT_ARGS(CBC, 256, 65536, ENCRYPT), /* 8Kbytes */
AES_CRYPT_ARGS(CBC, 256, 131072, ENCRYPT), /* 16Kbytes */
AES_CRYPT_ARGS(CBC, 256, 256, DECRYPT), /* 32 bytes */
AES_CRYPT_ARGS(CBC, 256, 8192, DECRYPT), /* 1Kbytes */
AES_CRYPT_ARGS(CBC, 256, 16384, DECRYPT), /* 2Kbytes */
AES_CRYPT_ARGS(CBC, 256, 32768, DECRYPT), /* 4Kbytes */
AES_CRYPT_ARGS(CBC, 256, 65536, DECRYPT), /* 8Kbytes */
AES_CRYPT_ARGS(CBC, 256, 131072, DECRYPT), /* 16Kbytes */
AES_CRYPT_ARGS(GCM, 128, 256, ENCRYPT), /* 32 bytes */
AES_CRYPT_ARGS(GCM, 128, 8192, ENCRYPT), /* 1Kbytes */
AES_CRYPT_ARGS(GCM, 128, 16384, ENCRYPT), /* 2Kbytes */
AES_CRYPT_ARGS(GCM, 128, 32768, ENCRYPT), /* 4Kbytes */
AES_CRYPT_ARGS(GCM, 128, 65536, ENCRYPT), /* 8Kbytes */
AES_CRYPT_ARGS(GCM, 128, 131072, ENCRYPT), /* 16Kbytes */
AES_CRYPT_ARGS(GCM, 128, 256, DECRYPT), /* 32 bytes */
AES_CRYPT_ARGS(GCM, 128, 8192, DECRYPT), /* 1Kbytes */
AES_CRYPT_ARGS(GCM, 128, 16384, DECRYPT), /* 2Kbytes */
AES_CRYPT_ARGS(GCM, 128, 32768, DECRYPT), /* 4Kbytes */
AES_CRYPT_ARGS(GCM, 128, 65536, DECRYPT), /* 8Kbytes */
AES_CRYPT_ARGS(GCM, 128, 131072, DECRYPT), /* 16Kbytes */
AES_CRYPT_ARGS(GCM, 256, 256, ENCRYPT), /* 32 bytes */
AES_CRYPT_ARGS(GCM, 256, 8192, ENCRYPT), /* 1Kbytes */
AES_CRYPT_ARGS(GCM, 256, 16384, ENCRYPT), /* 2Kbytes */
AES_CRYPT_ARGS(GCM, 256, 32768, ENCRYPT), /* 4Kbytes */
AES_CRYPT_ARGS(GCM, 256, 65536, ENCRYPT), /* 8Kbytes */
AES_CRYPT_ARGS(GCM, 256, 131072, ENCRYPT), /* 16Kbytes */
AES_CRYPT_ARGS(GCM, 256, 256, DECRYPT), /* 32 bytes */
AES_CRYPT_ARGS(GCM, 256, 8192, DECRYPT), /* 1Kbytes */
AES_CRYPT_ARGS(GCM, 256, 16384, DECRYPT), /* 2Kbytes */
AES_CRYPT_ARGS(GCM, 256, 32768, DECRYPT), /* 4Kbytes */
AES_CRYPT_ARGS(GCM, 256, 65536, DECRYPT), /* 8Kbytes */
AES_CRYPT_ARGS(GCM, 256, 131072, DECRYPT), /* 16Kbytes */
};
static void get_param_name_cb(char* buf, size_t buf_size, size_t param_idx) {
// TODO(b/330059594): param_idx goes out of bounds
uint8_t cpu_idx = param_idx / countof(params);
param_idx = param_idx % countof(params);
snprintf(buf, buf_size, "cpu%u|%s%sK%zu_%zu", cpu_idx,
params[param_idx].encrypt ? "ENC_" : "DEC_",
params[param_idx].mode == AES_MODE_CBC ? "CBC_" : "GCM_",
params[param_idx].key_size * 8, params[param_idx].input_size);
}
BENCH_SETUP(crypto) {
const struct crypto_param* param = &params[bench_get_param_idx()];
trusty_bench_get_param_name_cb = &get_param_name_cb;
_state = calloc(sizeof(struct crypto_swaes_state), 1);
ASSERT_NE(_state, NULL);
_state->param = param;
_state->buf = calloc(param->output_size, 1);
ASSERT_NE(_state->buf, NULL);
EVP_CIPHER_CTX_init(&_state->evp_ctx);
switch (param->mode) {
case AES_MODE_CBC:
if (_state->param->key_size * 8 == 128) {
_state->cipher = EVP_aes_128_cbc();
} else if (_state->param->key_size * 8 == 256) {
_state->cipher = EVP_aes_256_cbc();
}
break;
case AES_MODE_GCM:
if (_state->param->key_size * 8 == 128) {
_state->cipher = EVP_aes_128_gcm();
} else if (_state->param->key_size * 8 == 256) {
_state->cipher = EVP_aes_256_gcm();
}
_state->tag = calloc(_state->param->tag_size, 1);
ASSERT_NE(_state->tag, NULL);
break;
}
ASSERT_NE(NULL, _state->cipher, "invalid cipher mode or size\n");
/* Check the cipher parameters match the cipher */
ASSERT_EQ(EVP_CIPHER_key_length(_state->cipher), param->key_size);
ASSERT_EQ(EVP_CIPHER_iv_length(_state->cipher), param->iv_size);
return NO_ERROR;
test_abort:
return ERR_GENERIC;
}
BENCH_TEARDOWN(crypto) {
EVP_CIPHER_CTX_cleanup(&_state->evp_ctx);
if (_state->tag) {
free(_state->tag);
}
free(_state->buf);
free(_state);
}
static int encrypt(const struct crypto_param* param) {
int rc, total_len, out_len = 0;
rc = EVP_EncryptInit_ex(&_state->evp_ctx, _state->cipher, NULL, param->key,
param->iv);
ASSERT_NE(0, rc, "EVP_EncryptInit_ex() failed\n");
rc = EVP_CIPHER_CTX_set_padding(&_state->evp_ctx, 0);
ASSERT_NE(0, rc, "EVP_CIPHER_CTX_set_padding() failed\n");
if (param->mode == AES_MODE_GCM) {
rc = EVP_EncryptUpdate(&_state->evp_ctx, NULL, &out_len, aad,
sizeof(aad));
ASSERT_NE(0, rc, "EVP_EncryptUpdate(aad) failed\n");
ASSERT_EQ(sizeof(aad), out_len);
}
rc = EVP_EncryptUpdate(&_state->evp_ctx, _state->buf, &out_len,
param->input, param->input_size);
ASSERT_NE(0, rc, "EVP_EncryptUpdate failed\n");
total_len = out_len;
rc = EVP_EncryptFinal_ex(&_state->evp_ctx, _state->buf + total_len,
&out_len);
ASSERT_NE(0, rc, "EVP_EncryptFinal_ex failed\n");
total_len += out_len;
ASSERT_EQ(total_len, param->output_size);
#ifdef CHECK_RESULTS
ASSERT_EQ(0, memcmp(_state->buf, param->output, param->output_size),
"ciphertext mismatch\n");
if (param->mode == AES_MODE_GCM) {
rc = EVP_CIPHER_CTX_ctrl(&_state->evp_ctx, EVP_CTRL_AEAD_GET_TAG,
param->tag_size, _state->tag);
ASSERT_NE(0, rc, "EVP_CIPHER_CTX_ctrl() failed\n");
ASSERT_EQ(0, memcmp(_state->tag, param->tag, param->tag_size),
"tag mismatch\n");
}
#endif
EVP_CIPHER_CTX_reset(&_state->evp_ctx);
return NO_ERROR;
test_abort:
EVP_CIPHER_CTX_reset(&_state->evp_ctx);
return ERR_GENERIC;
}
static int decrypt(const struct crypto_param* param) {
int rc, total_len, out_len = 0;
rc = EVP_DecryptInit_ex(&_state->evp_ctx, _state->cipher, NULL, param->key,
param->iv);
ASSERT_NE(0, rc, "EVP_DecryptInit_ex() failed\n");
rc = EVP_CIPHER_CTX_set_padding(&_state->evp_ctx, 0);
ASSERT_NE(0, rc, "EVP_CIPHER_CTX_set_padding() failed\n");
if (param->mode == AES_MODE_GCM) {
rc = EVP_DecryptUpdate(&_state->evp_ctx, NULL, &out_len, aad,
sizeof(aad));
ASSERT_NE(0, rc, "EVP_DecryptUpdate(aad) failed\n");
ASSERT_EQ(sizeof(aad), out_len);
rc = EVP_CIPHER_CTX_ctrl(&_state->evp_ctx, EVP_CTRL_AEAD_SET_TAG,
param->tag_size, (void*)param->tag);
ASSERT_NE(0, rc, "EVP_CIPHER_CTX_ctrl() failed\n");
}
rc = EVP_DecryptUpdate(&_state->evp_ctx, _state->buf, &out_len,
param->input, param->input_size);
ASSERT_NE(0, rc, "EVP_DecryptUpdate failed\n");
total_len = out_len;
rc = EVP_DecryptFinal_ex(&_state->evp_ctx, _state->buf + out_len, &out_len);
ASSERT_NE(0, rc, "EVP_DecryptFinal_ex failed\n");
total_len += out_len;
ASSERT_EQ(total_len, param->output_size);
#ifdef CHECK_RESULTS
EXPECT_EQ(0, memcmp(_state->buf, param->output, param->output_size),
"plaintext mismatch\n");
#endif
EVP_CIPHER_CTX_reset(&_state->evp_ctx);
return NO_ERROR;
test_abort:
EVP_CIPHER_CTX_reset(&_state->evp_ctx);
return ERR_GENERIC;
}
BENCH_ALL_CPU(crypto, swaes, RUNS, params) {
const struct crypto_param* param = _state->param;
return param->encrypt ? encrypt(param) : decrypt(param);
}
BENCH_RESULT(crypto, swaes, Mbit_s) {
return (8000 * _state->param->input_size) / bench_get_duration_ns();
}
PORT_TEST(hwaes, "com.android.trusty.swaes.bench")