1 /*
2 * Copyright (C) 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 /* AES benchmark using directly linked BoringSSL. */
18
19 #define TLOG_TAG "swaes_bench"
20 #include <assert.h>
21 #include <stdbool.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include <trusty_benchmark.h>
26 #include <trusty_unittest.h>
27 #include <uapi/err.h>
28
29 #include <openssl/evp.h>
30
31 #include "vectors.h"
32
33 /*
34 * Define to verify crypto operation output matches the expected test vectors.
35 * This adds overhead (memcmp()) to the benchmark, so is not normally desired.
36 * However, it may be useful to verify that the correct cipher operation in
37 * the benchmark.
38 */
39 #define CHECK_RESULTS
40
41 /* Number of times to run the benchmark function with each parameter */
42 #define RUNS 40
43
44 /* test state */
45 struct crypto_swaes_state {
46 const struct crypto_param* param;
47
48 /* Encryption and decrypt context */
49 EVP_CIPHER_CTX evp_ctx;
50
51 /* Cipher to use, which combines mode and size */
52 const EVP_CIPHER* cipher;
53
54 /* Output buffer */
55 void* buf;
56
57 /* Tag buffer for GCM mode */
58 uint8_t* tag;
59 };
60
61 static struct crypto_swaes_state* _state = NULL;
62
63 static struct crypto_param params[] = {
64 /* Key and input sizes are given in bits
65 * mode, key, input, direction:
66 */
67 AES_CRYPT_ARGS(CBC, 128, 256, ENCRYPT), /* 32 bytes */
68 AES_CRYPT_ARGS(CBC, 128, 8192, ENCRYPT), /* 1Kbytes */
69 AES_CRYPT_ARGS(CBC, 128, 16384, ENCRYPT), /* 2Kbytes */
70 AES_CRYPT_ARGS(CBC, 128, 32768, ENCRYPT), /* 4Kbytes */
71 AES_CRYPT_ARGS(CBC, 128, 65536, ENCRYPT), /* 8Kbytes */
72 AES_CRYPT_ARGS(CBC, 128, 131072, ENCRYPT), /* 16Kbytes */
73
74 AES_CRYPT_ARGS(CBC, 128, 256, DECRYPT), /* 32 bytes */
75 AES_CRYPT_ARGS(CBC, 128, 8192, DECRYPT), /* 1Kbytes */
76 AES_CRYPT_ARGS(CBC, 128, 16384, DECRYPT), /* 2Kbytes */
77 AES_CRYPT_ARGS(CBC, 128, 32768, DECRYPT), /* 4Kbytes */
78 AES_CRYPT_ARGS(CBC, 128, 65536, DECRYPT), /* 8Kbytes */
79 AES_CRYPT_ARGS(CBC, 128, 131072, DECRYPT), /* 16Kbytes */
80
81 AES_CRYPT_ARGS(CBC, 256, 256, ENCRYPT), /* 32 bytes */
82 AES_CRYPT_ARGS(CBC, 256, 8192, ENCRYPT), /* 1Kbytes */
83 AES_CRYPT_ARGS(CBC, 256, 16384, ENCRYPT), /* 2Kbytes */
84 AES_CRYPT_ARGS(CBC, 256, 32768, ENCRYPT), /* 4Kbytes */
85 AES_CRYPT_ARGS(CBC, 256, 65536, ENCRYPT), /* 8Kbytes */
86 AES_CRYPT_ARGS(CBC, 256, 131072, ENCRYPT), /* 16Kbytes */
87
88 AES_CRYPT_ARGS(CBC, 256, 256, DECRYPT), /* 32 bytes */
89 AES_CRYPT_ARGS(CBC, 256, 8192, DECRYPT), /* 1Kbytes */
90 AES_CRYPT_ARGS(CBC, 256, 16384, DECRYPT), /* 2Kbytes */
91 AES_CRYPT_ARGS(CBC, 256, 32768, DECRYPT), /* 4Kbytes */
92 AES_CRYPT_ARGS(CBC, 256, 65536, DECRYPT), /* 8Kbytes */
93 AES_CRYPT_ARGS(CBC, 256, 131072, DECRYPT), /* 16Kbytes */
94
95 AES_CRYPT_ARGS(GCM, 128, 256, ENCRYPT), /* 32 bytes */
96 AES_CRYPT_ARGS(GCM, 128, 8192, ENCRYPT), /* 1Kbytes */
97 AES_CRYPT_ARGS(GCM, 128, 16384, ENCRYPT), /* 2Kbytes */
98 AES_CRYPT_ARGS(GCM, 128, 32768, ENCRYPT), /* 4Kbytes */
99 AES_CRYPT_ARGS(GCM, 128, 65536, ENCRYPT), /* 8Kbytes */
100 AES_CRYPT_ARGS(GCM, 128, 131072, ENCRYPT), /* 16Kbytes */
101
102 AES_CRYPT_ARGS(GCM, 128, 256, DECRYPT), /* 32 bytes */
103 AES_CRYPT_ARGS(GCM, 128, 8192, DECRYPT), /* 1Kbytes */
104 AES_CRYPT_ARGS(GCM, 128, 16384, DECRYPT), /* 2Kbytes */
105 AES_CRYPT_ARGS(GCM, 128, 32768, DECRYPT), /* 4Kbytes */
106 AES_CRYPT_ARGS(GCM, 128, 65536, DECRYPT), /* 8Kbytes */
107 AES_CRYPT_ARGS(GCM, 128, 131072, DECRYPT), /* 16Kbytes */
108
109 AES_CRYPT_ARGS(GCM, 256, 256, ENCRYPT), /* 32 bytes */
110 AES_CRYPT_ARGS(GCM, 256, 8192, ENCRYPT), /* 1Kbytes */
111 AES_CRYPT_ARGS(GCM, 256, 16384, ENCRYPT), /* 2Kbytes */
112 AES_CRYPT_ARGS(GCM, 256, 32768, ENCRYPT), /* 4Kbytes */
113 AES_CRYPT_ARGS(GCM, 256, 65536, ENCRYPT), /* 8Kbytes */
114 AES_CRYPT_ARGS(GCM, 256, 131072, ENCRYPT), /* 16Kbytes */
115
116 AES_CRYPT_ARGS(GCM, 256, 256, DECRYPT), /* 32 bytes */
117 AES_CRYPT_ARGS(GCM, 256, 8192, DECRYPT), /* 1Kbytes */
118 AES_CRYPT_ARGS(GCM, 256, 16384, DECRYPT), /* 2Kbytes */
119 AES_CRYPT_ARGS(GCM, 256, 32768, DECRYPT), /* 4Kbytes */
120 AES_CRYPT_ARGS(GCM, 256, 65536, DECRYPT), /* 8Kbytes */
121 AES_CRYPT_ARGS(GCM, 256, 131072, DECRYPT), /* 16Kbytes */
122 };
123
get_param_name_cb(char * buf,size_t buf_size,size_t param_idx)124 static void get_param_name_cb(char* buf, size_t buf_size, size_t param_idx) {
125 // TODO(b/330059594): param_idx goes out of bounds
126 uint8_t cpu_idx = param_idx / countof(params);
127 param_idx = param_idx % countof(params);
128 snprintf(buf, buf_size, "cpu%u|%s%sK%zu_%zu", cpu_idx,
129 params[param_idx].encrypt ? "ENC_" : "DEC_",
130 params[param_idx].mode == AES_MODE_CBC ? "CBC_" : "GCM_",
131 params[param_idx].key_size * 8, params[param_idx].input_size);
132 }
133
BENCH_SETUP(crypto)134 BENCH_SETUP(crypto) {
135 const struct crypto_param* param = ¶ms[bench_get_param_idx()];
136
137 trusty_bench_get_param_name_cb = &get_param_name_cb;
138
139 _state = calloc(sizeof(struct crypto_swaes_state), 1);
140 ASSERT_NE(_state, NULL);
141
142 _state->param = param;
143
144 _state->buf = calloc(param->output_size, 1);
145 ASSERT_NE(_state->buf, NULL);
146
147 EVP_CIPHER_CTX_init(&_state->evp_ctx);
148
149 switch (param->mode) {
150 case AES_MODE_CBC:
151 if (_state->param->key_size * 8 == 128) {
152 _state->cipher = EVP_aes_128_cbc();
153 } else if (_state->param->key_size * 8 == 256) {
154 _state->cipher = EVP_aes_256_cbc();
155 }
156 break;
157 case AES_MODE_GCM:
158 if (_state->param->key_size * 8 == 128) {
159 _state->cipher = EVP_aes_128_gcm();
160 } else if (_state->param->key_size * 8 == 256) {
161 _state->cipher = EVP_aes_256_gcm();
162 }
163
164 _state->tag = calloc(_state->param->tag_size, 1);
165 ASSERT_NE(_state->tag, NULL);
166 break;
167 }
168
169 ASSERT_NE(NULL, _state->cipher, "invalid cipher mode or size\n");
170
171 /* Check the cipher parameters match the cipher */
172 ASSERT_EQ(EVP_CIPHER_key_length(_state->cipher), param->key_size);
173 ASSERT_EQ(EVP_CIPHER_iv_length(_state->cipher), param->iv_size);
174
175 return NO_ERROR;
176 test_abort:
177 return ERR_GENERIC;
178 }
179
BENCH_TEARDOWN(crypto)180 BENCH_TEARDOWN(crypto) {
181 EVP_CIPHER_CTX_cleanup(&_state->evp_ctx);
182 if (_state->tag) {
183 free(_state->tag);
184 }
185 free(_state->buf);
186 free(_state);
187 }
188
encrypt(const struct crypto_param * param)189 static int encrypt(const struct crypto_param* param) {
190 int rc, total_len, out_len = 0;
191
192 rc = EVP_EncryptInit_ex(&_state->evp_ctx, _state->cipher, NULL, param->key,
193 param->iv);
194 ASSERT_NE(0, rc, "EVP_EncryptInit_ex() failed\n");
195
196 rc = EVP_CIPHER_CTX_set_padding(&_state->evp_ctx, 0);
197 ASSERT_NE(0, rc, "EVP_CIPHER_CTX_set_padding() failed\n");
198
199 if (param->mode == AES_MODE_GCM) {
200 rc = EVP_EncryptUpdate(&_state->evp_ctx, NULL, &out_len, aad,
201 sizeof(aad));
202 ASSERT_NE(0, rc, "EVP_EncryptUpdate(aad) failed\n");
203 ASSERT_EQ(sizeof(aad), out_len);
204 }
205
206 rc = EVP_EncryptUpdate(&_state->evp_ctx, _state->buf, &out_len,
207 param->input, param->input_size);
208 ASSERT_NE(0, rc, "EVP_EncryptUpdate failed\n");
209
210 total_len = out_len;
211
212 rc = EVP_EncryptFinal_ex(&_state->evp_ctx, _state->buf + total_len,
213 &out_len);
214 ASSERT_NE(0, rc, "EVP_EncryptFinal_ex failed\n");
215
216 total_len += out_len;
217 ASSERT_EQ(total_len, param->output_size);
218
219 #ifdef CHECK_RESULTS
220 ASSERT_EQ(0, memcmp(_state->buf, param->output, param->output_size),
221 "ciphertext mismatch\n");
222
223 if (param->mode == AES_MODE_GCM) {
224 rc = EVP_CIPHER_CTX_ctrl(&_state->evp_ctx, EVP_CTRL_AEAD_GET_TAG,
225 param->tag_size, _state->tag);
226 ASSERT_NE(0, rc, "EVP_CIPHER_CTX_ctrl() failed\n");
227 ASSERT_EQ(0, memcmp(_state->tag, param->tag, param->tag_size),
228 "tag mismatch\n");
229 }
230 #endif
231 EVP_CIPHER_CTX_reset(&_state->evp_ctx);
232
233 return NO_ERROR;
234 test_abort:
235 EVP_CIPHER_CTX_reset(&_state->evp_ctx);
236 return ERR_GENERIC;
237 }
238
decrypt(const struct crypto_param * param)239 static int decrypt(const struct crypto_param* param) {
240 int rc, total_len, out_len = 0;
241
242 rc = EVP_DecryptInit_ex(&_state->evp_ctx, _state->cipher, NULL, param->key,
243 param->iv);
244
245 ASSERT_NE(0, rc, "EVP_DecryptInit_ex() failed\n");
246
247 rc = EVP_CIPHER_CTX_set_padding(&_state->evp_ctx, 0);
248 ASSERT_NE(0, rc, "EVP_CIPHER_CTX_set_padding() failed\n");
249
250 if (param->mode == AES_MODE_GCM) {
251 rc = EVP_DecryptUpdate(&_state->evp_ctx, NULL, &out_len, aad,
252 sizeof(aad));
253 ASSERT_NE(0, rc, "EVP_DecryptUpdate(aad) failed\n");
254 ASSERT_EQ(sizeof(aad), out_len);
255
256 rc = EVP_CIPHER_CTX_ctrl(&_state->evp_ctx, EVP_CTRL_AEAD_SET_TAG,
257 param->tag_size, (void*)param->tag);
258 ASSERT_NE(0, rc, "EVP_CIPHER_CTX_ctrl() failed\n");
259 }
260
261 rc = EVP_DecryptUpdate(&_state->evp_ctx, _state->buf, &out_len,
262 param->input, param->input_size);
263 ASSERT_NE(0, rc, "EVP_DecryptUpdate failed\n");
264
265 total_len = out_len;
266
267 rc = EVP_DecryptFinal_ex(&_state->evp_ctx, _state->buf + out_len, &out_len);
268 ASSERT_NE(0, rc, "EVP_DecryptFinal_ex failed\n");
269
270 total_len += out_len;
271 ASSERT_EQ(total_len, param->output_size);
272
273 #ifdef CHECK_RESULTS
274 EXPECT_EQ(0, memcmp(_state->buf, param->output, param->output_size),
275 "plaintext mismatch\n");
276 #endif
277
278 EVP_CIPHER_CTX_reset(&_state->evp_ctx);
279
280 return NO_ERROR;
281 test_abort:
282 EVP_CIPHER_CTX_reset(&_state->evp_ctx);
283 return ERR_GENERIC;
284 }
285
BENCH_ALL_CPU(crypto,swaes,RUNS,params)286 BENCH_ALL_CPU(crypto, swaes, RUNS, params) {
287 const struct crypto_param* param = _state->param;
288
289 return param->encrypt ? encrypt(param) : decrypt(param);
290 }
291
BENCH_RESULT(crypto,swaes,Mbit_s)292 BENCH_RESULT(crypto, swaes, Mbit_s) {
293 return (8000 * _state->param->input_size) / bench_get_duration_ns();
294 }
295
296 PORT_TEST(hwaes, "com.android.trusty.swaes.bench")
297