Software APIs
aes_gcm_testutils.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 
5 #include "sw/device/tests/crypto/aes_gcm_testutils.h"
6 
9 #include "sw/device/lib/base/status.h"
10 #include "sw/device/lib/crypto/impl/integrity.h"
11 #include "sw/device/lib/crypto/impl/keyblob.h"
15 #include "sw/device/lib/testing/profile.h"
16 #include "sw/device/lib/testing/test_framework/check.h"
17 
18 #define MODULE_ID MAKE_MODULE_ID('a', 'g', 't')
19 
20 /**
21  * Static mask to use for testing.
22  */
23 static const uint32_t kKeyMask[8] = {
24  0x01234567, 0x89abcdef, 0x00010203, 0x04050607,
25  0x08090a0b, 0x0c0d0e0f, 0x10111213, 0x14151617,
26 };
27 
28 enum {
29  /**
30  * Byte-length of AAD chunks for streaming interface tests.
31  */
32  kStreamingAadChunkBytes = 10,
33  /**
34  * Byte-length of message chunks for streaming interface tests.
35  */
36  kStreamingMessageChunkBytes = 20,
37 };
38 
39 /**
40  * Get the enum value for a given tag length.
41  */
42 static otcrypto_aes_gcm_tag_len_t get_tag_length(size_t tag_len_bytes) {
43  switch (tag_len_bytes) {
44  case (128 / 8):
45  return kOtcryptoAesGcmTagLen128;
46  case (96 / 8):
47  return kOtcryptoAesGcmTagLen96;
48  case (64 / 8):
49  return kOtcryptoAesGcmTagLen64;
50  case (32 / 8):
51  return kOtcryptoAesGcmTagLen32;
52  default:
53  // Should not get here.
54  CHECK(false);
55  }
56  // Should not get here.
57  CHECK(false);
58  return 0;
59 }
60 
61 /**
62  * Stream an AES-GCM operation (encrypt or decrypt).
63  *
64  * The caller should call `otcrypto_aes_gcm_*_init()` before this operation and
65  * `otcrypto_aes_gcm_*_final()` after.
66  *
67  * @param ctx AES-GCM context, modified in-place.
68  * @param aad Associated data.
69  * @param input Plaintext for encryption, or ciphertext for decryption.
70  * @param[out] output Ciphertext for encryption, or plaintext for decryption.
71  * @param[out] output_bytes_written Number of output bytes written.
72  * @return OK or error.
73  */
74 static status_t stream_gcm(otcrypto_aes_gcm_context_t *ctx,
77  otcrypto_byte_buf_t output,
78  size_t *output_bytes_written) {
79  // Stream the AAD. The last chunk may have a different length than the others.
80  if (aad.len > 0) {
81  size_t num_chunks = ceil_div(aad.len, kStreamingAadChunkBytes);
82  for (size_t i = 0; i < num_chunks; i++) {
83  size_t offset = i * kStreamingAadChunkBytes;
84  size_t chunk_len = kStreamingAadChunkBytes;
85  if (offset + chunk_len > aad.len) {
86  chunk_len = aad.len - offset;
87  }
88  otcrypto_const_byte_buf_t aad_chunk = {
89  .data = aad.data + offset,
90  .len = chunk_len,
91  };
92  TRY(otcrypto_aes_gcm_update_aad(ctx, aad_chunk));
93  }
94  }
95 
96  // Stream the input, similary to the AAD except that this time we need to
97  // accumulate incremental parts of the output.
98  *output_bytes_written = 0;
99  if (input.len > 0) {
100  size_t num_chunks = ceil_div(input.len, kStreamingMessageChunkBytes);
101  for (size_t i = 0; i < num_chunks; i++) {
102  size_t offset = i * kStreamingMessageChunkBytes;
103  size_t chunk_len = kStreamingMessageChunkBytes;
104  if (offset + chunk_len > input.len) {
105  chunk_len = input.len - offset;
106  }
107  otcrypto_const_byte_buf_t input_chunk = {
108  .data = input.data + offset,
109  .len = chunk_len,
110  };
111  otcrypto_byte_buf_t output_with_offset = {
112  .data = output.data + *output_bytes_written,
113  .len = output.len - *output_bytes_written,
114  };
115  size_t bytes_written_for_chunk = 0;
117  ctx, input_chunk, output_with_offset, &bytes_written_for_chunk));
118  *output_bytes_written += bytes_written_for_chunk;
119  }
120  }
121  return OTCRYPTO_OK;
122 }
123 
124 status_t aes_gcm_testutils_encrypt(const aes_gcm_test_t *test, bool streaming,
125  uint32_t *cycles) {
126  // Construct the blinded key configuration.
127  otcrypto_key_config_t config = {
128  .version = kOtcryptoLibVersion1,
129  .key_mode = kOtcryptoKeyModeAesGcm,
130  .key_length = test->key_len * sizeof(uint32_t),
131  .hw_backed = kHardenedBoolFalse,
132  .security_level = kOtcryptoKeySecurityLevelLow,
133  };
134 
135  // Construct blinded key from the key and testing mask.
136  uint32_t keyblob[keyblob_num_words(config)];
137  TRY(keyblob_from_key_and_mask(test->key, kKeyMask, config, keyblob));
138 
139  // Construct the blinded key.
140  otcrypto_blinded_key_t key = {
141  .config = config,
142  .keyblob_length = sizeof(keyblob),
143  .keyblob = keyblob,
144  .checksum = 0,
145  };
146 
147  // Set the checksum.
148  key.checksum = integrity_blinded_checksum(&key);
149 
150  size_t iv_num_words =
151  (test->iv_len + sizeof(uint32_t) - 1) / sizeof(uint32_t);
152  uint32_t iv_data[iv_num_words];
153  memcpy(iv_data, test->iv, test->iv_len);
155  .data = iv_data,
156  .len = iv_num_words,
157  };
158  otcrypto_const_byte_buf_t plaintext = {
159  .data = test->plaintext,
160  .len = test->plaintext_len,
161  };
163  .data = test->aad,
164  .len = test->aad_len,
165  };
166 
167  size_t tag_num_words =
168  (test->tag_len + sizeof(uint32_t) - 1) / sizeof(uint32_t);
169  uint32_t actual_tag_data[tag_num_words];
170  otcrypto_word32_buf_t actual_tag = {
171  .data = actual_tag_data,
172  .len = tag_num_words,
173  };
174 
175  size_t ciphertext_blocks = ceil_div(test->plaintext_len, kAesBlockNumBytes);
176  uint8_t actual_ciphertext_data[ciphertext_blocks * kAesBlockNumBytes];
177  otcrypto_byte_buf_t actual_ciphertext = {
178  .data = actual_ciphertext_data,
179  .len = test->plaintext_len,
180  };
181 
182  otcrypto_aes_gcm_tag_len_t tag_len = get_tag_length(test->tag_len);
183 
184  if (streaming) {
185  uint64_t t_start = profile_start();
187  TRY(otcrypto_aes_gcm_encrypt_init(&key, iv, &ctx));
188  size_t ciphertext_bytes_written;
189  TRY(stream_gcm(&ctx, aad, plaintext, actual_ciphertext,
190  &ciphertext_bytes_written));
191  otcrypto_byte_buf_t final_ciphertext = {
192  .data = actual_ciphertext.data + ciphertext_bytes_written,
193  .len = test->plaintext_len - ciphertext_bytes_written,
194  };
195  TRY(otcrypto_aes_gcm_encrypt_final(&ctx, tag_len, final_ciphertext,
196  &ciphertext_bytes_written, actual_tag));
197  *cycles = profile_end(t_start);
198  } else {
199  // Call encrypt() with a cycle count timing profile.
200  uint64_t t_start = profile_start();
202  &key, plaintext, iv, aad, tag_len, actual_ciphertext, actual_tag);
203  *cycles = profile_end(t_start);
204 
205  // Check for errors.
206  TRY(err);
207  }
208 
209  // Check that the tag and plaintext match expected values.
210  if (test->plaintext_len > 0) {
211  TRY_CHECK_ARRAYS_EQ(actual_ciphertext_data, test->ciphertext,
212  test->plaintext_len);
213  }
214  TRY_CHECK_ARRAYS_EQ((unsigned char *)actual_tag_data, test->tag,
215  test->tag_len);
216 
217  return OK_STATUS();
218 }
219 
220 status_t aes_gcm_testutils_decrypt(const aes_gcm_test_t *test,
221  hardened_bool_t *tag_valid, bool streaming,
222  uint32_t *cycles) {
223  // Construct the blinded key configuration.
224  otcrypto_key_config_t config = {
225  .version = kOtcryptoLibVersion1,
226  .key_mode = kOtcryptoKeyModeAesGcm,
227  .key_length = test->key_len * sizeof(uint32_t),
228  .hw_backed = kHardenedBoolFalse,
229  .security_level = kOtcryptoKeySecurityLevelLow,
230  };
231 
232  // Construct blinded key from the key and testing mask.
233  uint32_t keyblob[keyblob_num_words(config)];
234  TRY(keyblob_from_key_and_mask(test->key, kKeyMask, config, keyblob));
235 
236  // Construct the blinded key.
237  otcrypto_blinded_key_t key = {
238  .config = config,
239  .keyblob_length = sizeof(keyblob),
240  .keyblob = keyblob,
241  .checksum = 0,
242  };
243 
244  // Set the checksum.
245  key.checksum = integrity_blinded_checksum(&key);
246 
247  size_t iv_num_words =
248  (test->iv_len + sizeof(uint32_t) - 1) / sizeof(uint32_t);
249  uint32_t iv_data[iv_num_words];
250  memcpy(iv_data, test->iv, test->iv_len);
252  .data = iv_data,
253  .len = iv_num_words,
254  };
255  otcrypto_const_byte_buf_t ciphertext = {
256  .data = test->ciphertext,
257  .len = test->plaintext_len,
258  };
260  .data = test->aad,
261  .len = test->aad_len,
262  };
263  size_t tag_num_words =
264  (test->tag_len + sizeof(uint32_t) - 1) / sizeof(uint32_t);
265  uint32_t tag_data[tag_num_words];
266  memcpy(tag_data, test->tag, test->tag_len);
268  .data = tag_data,
269  .len = tag_num_words,
270  };
271 
272  size_t ciphertext_blocks = ceil_div(test->plaintext_len, kAesBlockNumBytes);
273  uint8_t actual_plaintext_data[ciphertext_blocks * kAesBlockNumBytes];
274  otcrypto_byte_buf_t actual_plaintext = {
275  .data = actual_plaintext_data,
276  .len = test->plaintext_len,
277  };
278 
279  otcrypto_aes_gcm_tag_len_t tag_len = get_tag_length(test->tag_len);
280 
281  if (streaming) {
283  uint64_t t_start = profile_start();
284  TRY(otcrypto_aes_gcm_decrypt_init(&key, iv, &ctx));
285  size_t plaintext_bytes_written;
286  TRY(stream_gcm(&ctx, aad, ciphertext, actual_plaintext,
287  &plaintext_bytes_written));
288  otcrypto_byte_buf_t final_plaintext = {
289  .data = actual_plaintext.data + plaintext_bytes_written,
290  .len = actual_plaintext.len - plaintext_bytes_written,
291  };
292  size_t final_plaintext_bytes_written;
293  TRY(otcrypto_aes_gcm_decrypt_final(&ctx, tag, tag_len, final_plaintext,
294  &final_plaintext_bytes_written,
295  tag_valid));
296  *cycles = profile_end(t_start);
297  } else {
298  // Call decrypt() with a cycle count timing profile.
300  uint64_t t_start = profile_start();
302  &key, ciphertext, iv, aad, tag_len, tag, actual_plaintext, tag_valid);
303  *cycles = profile_end(t_start);
305 
306  // Check for errors.
307  TRY(err);
308  }
309 
310  // Check the results.
311  if (test->plaintext_len > 0 && *tag_valid == kHardenedBoolTrue) {
312  TRY_CHECK_ARRAYS_EQ(actual_plaintext_data, test->plaintext,
313  test->plaintext_len);
314  }
315 
316  return OK_STATUS();
317 }