/* * 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 #include #include #include #include #include #include #include #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 = ¶ms[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")