Software APIs
aes_functest.c
1 // Copyright lowRISC contributors (OpenTitan project).
2 // Licensed under the Apache License, Version 2.0, see LICENSE for details.
3 // SPDX-License-Identifier: Apache-2.0
4 
6 #include "sw/device/lib/crypto/drivers/entropy.h"
7 #include "sw/device/lib/crypto/impl/integrity.h"
8 #include "sw/device/lib/crypto/impl/keyblob.h"
11 #include "sw/device/lib/testing/test_framework/check.h"
13 #include "sw/device/tests/crypto/aes_testvectors.h"
14 
15 // Module ID for status codes.
16 #define MODULE_ID MAKE_MODULE_ID('t', 's', 't')
17 
18 // Random mask for testing.
19 static const uint32_t kKeyMask[8] = {
20  0x1b81540c, 0x220733c9, 0x8bf85383, 0x05ab50b4,
21  0x8acdcb7e, 0x15e76440, 0x8459b2ce, 0xdc2110cc,
22 };
23 
24 // Global pointer to the current test.
25 static const aes_test_t *test = NULL;
26 
27 enum {
28  kAesBlockBytes = 128 / 8,
29  kAesBlockWords = kAesBlockBytes / sizeof(uint32_t),
30 };
31 
32 static otcrypto_key_config_t make_key_config(const aes_test_t *test) {
33  otcrypto_key_mode_t key_mode;
34  switch (test->mode) {
35  case kOtcryptoAesModeEcb:
36  LOG_INFO("Mode: ECB");
37  key_mode = kOtcryptoKeyModeAesEcb;
38  break;
39  case kOtcryptoAesModeCbc:
40  LOG_INFO("Mode: CBC");
41  key_mode = kOtcryptoKeyModeAesCbc;
42  break;
43  case kOtcryptoAesModeCfb:
44  LOG_INFO("Mode: CFB");
45  key_mode = kOtcryptoKeyModeAesCfb;
46  break;
47  case kOtcryptoAesModeOfb:
48  LOG_INFO("Mode: OFB");
49  key_mode = kOtcryptoKeyModeAesOfb;
50  break;
51  case kOtcryptoAesModeCtr:
52  LOG_INFO("Mode: CTR");
53  key_mode = kOtcryptoKeyModeAesCtr;
54  break;
55  default:
56  // Should be unreachable.
57  CHECK(false, "Invalid block cipher mode.");
58  };
59 
60  return (otcrypto_key_config_t){
61  .version = kOtcryptoLibVersion1,
62  .key_mode = key_mode,
63  .key_length = test->key_len,
64  .hw_backed = kHardenedBoolFalse,
65  .security_level = kOtcryptoKeySecurityLevelLow,
66  };
67 }
68 
69 /**
70  * Run AES encryption for the given test vector.
71  *
72  * If `streaming` is true, we will process the plaintext in small chunks to
73  * ensure that the IV is updating correctly.
74  *
75  * @param test Test vector to run.
76  * @param streaming Whether to run in streaming mode.
77  */
78 static status_t run_encrypt(const aes_test_t *test, bool streaming) {
79  // Determine the key configuration.
80  otcrypto_key_config_t config = make_key_config(test);
81 
82  // Construct blinded key from the key and testing mask.
83  uint32_t keyblob[keyblob_num_words(config)];
84  TRY(keyblob_from_key_and_mask(test->key, kKeyMask, config, keyblob));
86  .config = config,
87  .keyblob_length = sizeof(keyblob),
88  .keyblob = keyblob,
89  };
90  key.checksum = integrity_blinded_checksum(&key);
91 
92  // Construct a buffer to hold the IV.
93  uint32_t iv_data[kAesBlockWords];
94  memcpy(iv_data, test->iv, kAesBlockBytes);
96  .data = iv_data,
97  .len = kAesBlockWords,
98  };
99 
100  // Calculate the size of the padded plaintext.
101  size_t padded_len_bytes;
102  TRY(otcrypto_aes_padded_plaintext_length(test->plaintext_len, test->padding,
103  &padded_len_bytes));
104 
105  // Create buffer for ciphertext.
106  uint32_t ciphertext_data[padded_len_bytes / sizeof(uint32_t)];
107 
108  // If in streaming mode, encrypt one block at a time with null padding until
109  // there is at most 1 block of input remaining.
110  size_t plaintext_len = test->plaintext_len;
111  size_t ciphertext_len = sizeof(ciphertext_data);
112  const unsigned char *plaintext = (const unsigned char *)test->plaintext;
113  unsigned char *ciphertext = (unsigned char *)ciphertext_data;
114  if (streaming) {
115  while (plaintext_len > kAesBlockBytes) {
116  otcrypto_const_byte_buf_t plaintext_block = {.data = plaintext,
117  .len = kAesBlockBytes};
118  otcrypto_byte_buf_t ciphertext_block = {.data = ciphertext,
119  .len = kAesBlockBytes};
120  TRY(otcrypto_aes(&key, iv, test->mode, kOtcryptoAesOperationEncrypt,
121  plaintext_block, kOtcryptoAesPaddingNull,
122  ciphertext_block));
123  plaintext += kAesBlockBytes;
124  ciphertext += kAesBlockBytes;
125  plaintext_len -= kAesBlockBytes;
126  ciphertext_len -= kAesBlockBytes;
127  }
128  }
129 
130  // Encrypt the remaining input in one shot with the requested padding.
131  otcrypto_const_byte_buf_t plaintext_buf = {.data = plaintext,
132  .len = plaintext_len};
133  otcrypto_byte_buf_t ciphertext_buf = {.data = ciphertext,
134  .len = ciphertext_len};
135  TRY(otcrypto_aes(&key, iv, test->mode, kOtcryptoAesOperationEncrypt,
136  plaintext_buf, test->padding, ciphertext_buf));
137 
138  TRY_CHECK_ARRAYS_EQ(ciphertext_data, test->exp_ciphertext,
139  ARRAYSIZE(ciphertext_data));
140  return OK_STATUS();
141 }
142 
143 /**
144  * Run AES decryption for the given test vector.
145  *
146  * If `streaming` is true, we will process the ciphertext in small chunks to
147  * ensure that the IV is updating correctly.
148  *
149  * @param test Test vector to run.
150  * @param streaming Whether to run in streaming mode.
151  */
152 static status_t run_decrypt(const aes_test_t *test, bool streaming) {
153  // Determine the key configuration.
154  otcrypto_key_config_t config = make_key_config(test);
155 
156  // Construct blinded key from the key and testing mask.
157  uint32_t keyblob[keyblob_num_words(config)];
158  TRY(keyblob_from_key_and_mask(test->key, kKeyMask, config, keyblob));
159  otcrypto_blinded_key_t key = {
160  .config = config,
161  .keyblob_length = sizeof(keyblob),
162  .keyblob = keyblob,
163  };
164  key.checksum = integrity_blinded_checksum(&key);
165 
166  // Construct a buffer to hold the IV.
167  uint32_t iv_data[kAesBlockWords];
168  memcpy(iv_data, test->iv, kAesBlockBytes);
169  otcrypto_word32_buf_t iv = {
170  .data = iv_data,
171  .len = kAesBlockWords,
172  };
173 
174  // Calculate the size of the padded plaintext.
175  size_t padded_len_bytes;
176  TRY(otcrypto_aes_padded_plaintext_length(test->plaintext_len, test->padding,
177  &padded_len_bytes));
178 
179  // Construct a buffer for the recovered plaintext.
180  TRY_CHECK(padded_len_bytes % sizeof(uint32_t) == 0);
181  size_t padded_len_words = padded_len_bytes / sizeof(uint32_t);
182  uint32_t recovered_plaintext_data[padded_len_words];
183  memset(recovered_plaintext_data, 0, sizeof(recovered_plaintext_data));
184 
185  // If in streaming mode, decrypt one block at a time with null padding until
186  // there is only 1 block of input remaining.
187  size_t len = sizeof(recovered_plaintext_data);
188  const unsigned char *ciphertext = (const unsigned char *)test->exp_ciphertext;
189  unsigned char *recovered_plaintext =
190  (unsigned char *)recovered_plaintext_data;
191  if (streaming) {
192  while (len > kAesBlockBytes) {
193  otcrypto_const_byte_buf_t ciphertext_block = {.data = ciphertext,
194  .len = kAesBlockBytes};
195  otcrypto_byte_buf_t recovered_plaintext_block = {
196  .data = recovered_plaintext, .len = kAesBlockBytes};
197  TRY(otcrypto_aes(&key, iv, test->mode, kOtcryptoAesOperationDecrypt,
198  ciphertext_block, kOtcryptoAesPaddingNull,
199  recovered_plaintext_block));
200  ciphertext += kAesBlockBytes;
201  recovered_plaintext += kAesBlockBytes;
202  len -= kAesBlockBytes;
203  }
204  // Expect that the length is now exactly one block, since the ciphertext
205  // length should be a multiple of the block size.
206  TRY_CHECK(len == kAesBlockBytes);
207  }
208 
209  // Decrypt the remaining input in one shot.
210  otcrypto_const_byte_buf_t ciphertext_buf = {.data = ciphertext, .len = len};
211  otcrypto_byte_buf_t recovered_plaintext_buf = {.data = recovered_plaintext,
212  .len = len};
213  TRY(otcrypto_aes(&key, iv, test->mode, kOtcryptoAesOperationDecrypt,
214  ciphertext_buf, test->padding, recovered_plaintext_buf));
215 
216  // Check the result (not including padding).
217  TRY_CHECK_ARRAYS_EQ((unsigned char *)recovered_plaintext_data,
218  (unsigned char *)test->plaintext, test->plaintext_len);
219 
220  return OK_STATUS();
221 }
222 
223 /**
224  * Test one-shot AES encryption.
225  */
226 static status_t encrypt_test(void) {
227  return run_encrypt(test, /*streaming=*/false);
228 }
229 
230 /**
231  * Test one-shot AES decryption.
232  */
233 static status_t decrypt_test(void) {
234  return run_decrypt(test, /*streaming=*/false);
235 }
236 
237 /**
238  * Test streaming AES encryption.
239  */
240 static status_t encrypt_streaming_test(void) {
241  return run_encrypt(test, /*streaming=*/true);
242 }
243 
244 /**
245  * Test streaming AES decryption.
246  */
247 static status_t decrypt_streaming_test(void) {
248  return run_decrypt(test, /*streaming=*/true);
249 }
250 
251 OTTF_DEFINE_TEST_CONFIG();
252 
253 bool test_main(void) {
254  status_t result = OK_STATUS();
255 
256  // Start the entropy complex.
257  CHECK_STATUS_OK(entropy_complex_init());
258 
259  for (size_t i = 0; i < ARRAYSIZE(kAesTests); i++) {
260  LOG_INFO("Starting AES test %d of %d...", i + 1, ARRAYSIZE(kAesTests));
261  test = &kAesTests[i];
262  EXECUTE_TEST(result, encrypt_test);
263  EXECUTE_TEST(result, decrypt_test);
264  EXECUTE_TEST(result, encrypt_streaming_test);
265  EXECUTE_TEST(result, decrypt_streaming_test);
266  LOG_INFO("Finished AES test %d.", i + 1);
267  }
268 
269  return status_ok(result);
270 }