• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 = &params[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