Software APIs
hmac_multistream_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 
5 #include "sw/device/lib/crypto/impl/integrity.h"
10 #include "sw/device/lib/testing/rand_testutils.h"
11 #include "sw/device/lib/testing/test_framework/check.h"
13 
14 // The autogen rule that creates this header creates it in a directory named
15 // after the rule, then manipulates the include path in the
16 // cc_compilation_context to include that directory, so the compiler will find
17 // the version of this file matching the Bazel rule under test.
18 #include "hmac_testvectors.h"
19 
20 // Module ID for status codes.
21 #define MODULE_ID MAKE_MODULE_ID('h', 's', 't')
22 
23 // We need the following assertion, because we are using hash context struct
24 // also for hmac contexts.
25 static_assert(sizeof(otcrypto_hash_context_t) ==
27  "Hash and Hmac contexts are expected to be of the same length");
28 
29 /**
30  * This enum defines the different stages of a test vector during streaming
31  * or oneshot HMAC calls.
32  *
33  * Oneshot calls transition from `kHmacTestNotStarted` to `kHmacTestDone`.
34  *
35  * For streaming calls, the exact number of streaming segments can be up to 4.
36  * For fewer segments, a transition from `kHmacTestFeedSegmentX` to
37  * `kHmacTestFinalize` is allowed so long as all message bytes of the test is
38  * fed to HMAC core.
39  *
40  * The value of `kHmacTestFeedSegmentX` enum is arranged to match integer X,
41  * as it us used as an index to access `segment_idx` array defined in
42  * `hmac_extended_test_vector_t`.
43  */
44 typedef enum hmac_test_progress {
45  /* The oneshot or streaming test has not been initialized.*/
46  kHmacTestNotStarted = 0,
47  /* The streaming test is initialized, but no message segment is fed.*/
48  kHmacTestFeedSegment1 = 1,
49  /* The streaming test is initialized and the first message segment is fed.*/
50  kHmacTestFeedSegment2 = 2,
51  /* The streaming test is initialized and the first two message segments are
52  fed.*/
53  kHmacTestFeedSegment3 = 3,
54  /* The streaming test is initialized and the first three message segments are
55  fed.*/
56  kHmacTestFeedSegment4 = 4,
57  /* All message bytes are fed but the final is not called.*/
58  kHmacTestFinalize = 5,
59  /* The test is completed and the result is verified.*/
60  kHmacTestDone = 6,
61 } hmac_test_progress_t;
62 
63 /**
64  * Extend `hmac_test_vector_t` with metadata fields useful for streaming.
65  */
66 typedef struct hmac_extended_test_vector {
67  /* The pointer to the test vector coming from the autogenerated C header. */
68  hmac_test_vector_t *hmac_test_vector;
69  /* The original index of this test vector in the autogenerated C header. */
70  size_t original_idx;
71  /* The number of update calls for a streaming test. */
72  size_t segment_count;
73  /* Randomly generated indices to break down the test message to segments. */
74  size_t segment_idx[4];
75  /* `progess` keeps track of how many message segments are streamed so far. */
76  hmac_test_progress_t progress;
77  /* `hash_ctx` is used to store context during streaming. */
78  otcrypto_hash_context_t hash_ctx;
80 
82  kHmacExtendedTestVectors[2 * ARRAYSIZE(kHmacTestVectors)];
83 
84 /**
85  * Populate `kHmacExtendedTestVectors` array with tests such that:
86  * i) There are mixture of oneshot and streaming HMAC operations.
87  * Each vector is run in 2 duplicates, one as a streaming and another
88  * as a oneshot call.
89  * ii) For streaming operations, messages are split into [1,4]
90  * segments with randomly chosen indices.
91  * iii) The order of tests is randomized.
92  */
93 static void vectors_populate(void) {
94  hmac_extended_test_vector_t *cur_ext_vec;
95  hmac_test_vector_t *cur_vec;
96  // First add streaming calls to the test array.
97  for (size_t i = 0; i < ARRAYSIZE(kHmacTestVectors); i++) {
98  cur_ext_vec = &kHmacExtendedTestVectors[i];
99  cur_vec = &kHmacTestVectors[i];
100  cur_ext_vec->hmac_test_vector = cur_vec;
101  cur_ext_vec->original_idx = i;
102  cur_ext_vec->segment_count = rand_testutils_gen32_range(1, 4);
103  // Pick three indicies to break down message into 4 parts, but we only
104  // use the first `segment_count` of them. The indices of a message
105  // looks like below:
106  // ix[0] = 0 ... idx[1] ... idx[2] ... idx[3] ... message_len-1
107  cur_ext_vec->segment_idx[0] = 0;
108  cur_ext_vec->segment_idx[2] =
109  rand_testutils_gen32_range(0, cur_vec->message.len);
110  cur_ext_vec->segment_idx[1] =
111  rand_testutils_gen32_range(0, cur_ext_vec->segment_idx[2]);
112  cur_ext_vec->segment_idx[3] = rand_testutils_gen32_range(
113  cur_ext_vec->segment_idx[2], cur_vec->message.len);
114  cur_ext_vec->progress = kHmacTestNotStarted;
115  }
116 
117  // Add oneshot calls of the same vectors.
118  for (size_t i = 0; i < ARRAYSIZE(kHmacTestVectors); i++) {
119  cur_ext_vec = &kHmacExtendedTestVectors[ARRAYSIZE(kHmacTestVectors) + i];
120  cur_vec = &kHmacTestVectors[i];
121  cur_ext_vec->hmac_test_vector = cur_vec;
122  cur_ext_vec->original_idx = i;
123  cur_ext_vec->segment_count = 0;
124  cur_ext_vec->progress = kHmacTestNotStarted;
125  }
126 
127  // Now we shuffle the order of vectors and print
128  // how the new order maps to the original order from the test file.
129  rand_testutils_shuffle(kHmacExtendedTestVectors,
131  ARRAYSIZE(kHmacExtendedTestVectors));
132  for (size_t i = 0; i < ARRAYSIZE(kHmacExtendedTestVectors); i++) {
133  cur_ext_vec = &kHmacExtendedTestVectors[i];
134  cur_vec = cur_ext_vec->hmac_test_vector;
135  LOG_INFO("Vector order = %d, original_vector_id = %d", i,
136  cur_ext_vec->original_idx);
137  LOG_INFO(
138  "segment_count = %d, segment_idx = [%d, %d, %d, %d], message_len = %d",
139  cur_ext_vec->segment_count, cur_ext_vec->segment_idx[0],
140  cur_ext_vec->segment_idx[1], cur_ext_vec->segment_idx[2],
141  cur_ext_vec->segment_idx[3],
142  cur_ext_vec->hmac_test_vector->message.len);
143  }
144 }
145 
146 /**
147  * Determines `hash_mode` for given SHA-2 test vectors.
148  *
149  * Note that for HMAC operations, mode information is part of the key struct,
150  * hence this function is only used for hash vectors.
151  *
152  * @param test_vec The pointer to the test vector.
153  * @param[out] hash_mode The determined hash_mode of the given test vector.
154  */
155 static status_t get_hash_mode(hmac_test_vector_t *test_vec,
156  otcrypto_hash_mode_t *hash_mode) {
157  switch (test_vec->test_operation) {
158  case kHmacTestOperationSha256:
159  *hash_mode = kOtcryptoHashModeSha256;
160  return OTCRYPTO_OK;
161  case kHmacTestOperationSha384:
162  *hash_mode = kOtcryptoHashModeSha384;
163  return OTCRYPTO_OK;
164  case kHmacTestOperationSha512:
165  *hash_mode = kOtcryptoHashModeSha512;
166  return OTCRYPTO_OK;
167  default:
168  return OTCRYPTO_BAD_ARGS;
169  }
170 }
171 
172 /**
173  * Instantiate `hash_ctx` context object with hash/mac mode specified in
174  * `current_test_vector`.
175  *
176  * @param hash_ctx Corresponding context for given `current_test_vector`.
177  * @param current_test_vector Pointer to the hardcoded test vector.
178  */
179 static status_t ctx_init(otcrypto_hash_context_t *hash_ctx,
180  hmac_test_vector_t *current_test_vector) {
181  // Populate `checksum` and `config.security_level` fields.
182  current_test_vector->key.checksum =
183  integrity_blinded_checksum(&current_test_vector->key);
184 
185  otcrypto_hash_mode_t hash_mode;
186 
187  switch (current_test_vector->test_operation) {
188  case kHmacTestOperationSha256:
190  case kHmacTestOperationSha384:
192  case kHmacTestOperationSha512:
193  TRY(get_hash_mode(current_test_vector, &hash_mode));
194  TRY(otcrypto_hash_init(hash_ctx, hash_mode));
195  break;
196  case kHmacTestOperationHmacSha256:
198  case kHmacTestOperationHmacSha384:
200  case kHmacTestOperationHmacSha512:
202  &current_test_vector->key));
203  break;
204  default:
205  return OTCRYPTO_BAD_ARGS;
206  }
207  return OTCRYPTO_OK;
208 }
209 
210 /**
211  * Run the test given by `current_test_vector` as oneshot operation.
212  *
213  * @param current_test_vector Pointer to the test vector.
214  * @return The result of the operation.
215  */
216 static status_t hmac_oneshot(hmac_test_vector_t *current_test_vector) {
217  // Populate `checksum` and `config.security_level` fields.
218  current_test_vector->key.checksum =
219  integrity_blinded_checksum(&current_test_vector->key);
220 
221  // The test vectors already have the correct digest sizes hardcoded.
222  size_t digest_len = current_test_vector->digest.len;
223  // Allocate the buffer for the maximum digest size (which comes from SHA-512).
224  uint32_t act_tag[kSha512DigestWords];
225  otcrypto_word32_buf_t tag_buf = {
226  .data = act_tag,
227  .len = digest_len,
228  };
229  otcrypto_hash_digest_t hash_digest = {
230  // .mode is to be determined below in switch-case block.
231  .data = act_tag,
232  .len = digest_len,
233  };
234  switch (current_test_vector->test_operation) {
235  case kHmacTestOperationSha256:
237  case kHmacTestOperationSha384:
239  case kHmacTestOperationSha512:
240  TRY(get_hash_mode(current_test_vector, &hash_digest.mode));
241  TRY(otcrypto_hash(current_test_vector->message, hash_digest));
242  break;
243  case kHmacTestOperationHmacSha256:
245  case kHmacTestOperationHmacSha384:
247  case kHmacTestOperationHmacSha512:
248  TRY(otcrypto_hmac(&current_test_vector->key, current_test_vector->message,
249  tag_buf));
250  break;
251  default:
252  return OTCRYPTO_BAD_ARGS;
253  }
254  LOG_INFO("Comparing result for %s.", current_test_vector->vector_identifier);
255  CHECK_ARRAYS_EQ(act_tag, current_test_vector->digest.data, digest_len);
256  return OTCRYPTO_OK;
257 }
258 
259 /**
260  * Feed messages to the streaming hash/hmac operation with context `hash_ctx`.
261  * `current_test_vector` is used to determine message bytes to be used.
262  *
263  * This function feeds (approximately) the first or the second half of
264  * `current_test_vector->message`.
265  *
266  * @param hash_ctx Corresponding context for given `current_test_vector`.
267  * @param current_test_vector Pointer to the hardcoded test vector.
268  * @param segment_start The starting index of the chosen segment.
269  * @param segment_len The byte length of the chosen segment.
270  * @return The result of the operation.
271  */
272 static status_t feed_msg(otcrypto_hash_context_t *hash_ctx,
273  hmac_test_vector_t *current_test_vector,
274  size_t segment_start, size_t segment_len) {
276  .data = &current_test_vector->message.data[segment_start],
277  .len = segment_len,
278  };
279 
280  switch (current_test_vector->test_operation) {
281  case kHmacTestOperationSha256:
283  case kHmacTestOperationSha384:
285  case kHmacTestOperationSha512:
286  TRY(otcrypto_hash_update(hash_ctx, msg));
287  break;
288  case kHmacTestOperationHmacSha256:
290  case kHmacTestOperationHmacSha384:
292  case kHmacTestOperationHmacSha512:
293  TRY(otcrypto_hmac_update((otcrypto_hmac_context_t *)hash_ctx, msg));
294  break;
295  default:
296  return OTCRYPTO_BAD_ARGS;
297  }
298  return OTCRYPTO_OK;
299 }
300 
301 /**
302  * Finalize the hash/hmac operation with context `hash_ctx`. Further necessary
303  * parameters for the final call is reads from `current_test_vector`.
304  *
305  * @param hash_ctx Corresponding context for given `current_test_vector`.
306  * @param current_test_vector Pointer to the hardcoded test vector.
307  * @return The result of the operation.
308  */
309 static status_t hmac_finalize(otcrypto_hash_context_t *hash_ctx,
310  hmac_test_vector_t *current_test_vector) {
311  // The test vectors already have the correct digest sizes hardcoded.
312  size_t digest_len = current_test_vector->digest.len;
313  // Allocate the buffer for the maximum digest size (which comes from SHA-512).
314  uint32_t act_tag[kSha512DigestWords];
315  otcrypto_word32_buf_t tag_buf = {
316  .data = act_tag,
317  .len = digest_len,
318  };
319  otcrypto_hash_digest_t hash_digest = {
320  // .mode is to be determined below in switch-case block.
321  .data = act_tag,
322  .len = digest_len,
323  };
324  switch (current_test_vector->test_operation) {
325  case kHmacTestOperationSha256:
327  case kHmacTestOperationSha384:
329  case kHmacTestOperationSha512:
330  TRY(get_hash_mode(current_test_vector, &hash_digest.mode));
331  TRY(otcrypto_hash_final(hash_ctx, hash_digest));
332  break;
333  case kHmacTestOperationHmacSha256:
335  case kHmacTestOperationHmacSha384:
337  case kHmacTestOperationHmacSha512:
338  TRY(otcrypto_hmac_final((otcrypto_hmac_context_t *)hash_ctx, tag_buf));
339  break;
340  default:
341  return OTCRYPTO_BAD_ARGS;
342  }
343  LOG_INFO("Comparing result for %s.", current_test_vector->vector_identifier);
344  CHECK_ARRAYS_EQ(act_tag, current_test_vector->digest.data, digest_len);
345  return OTCRYPTO_OK;
346 }
347 
348 /**
349  * Process a message segment for the given test vector.
350  *
351  * If the given test vector is already run and completed, this function
352  * simply returns.
353  *
354  * If the given test vector is a oneshot call, then it is run and the
355  * result is compared. The process of the vector is already update to done.
356  *
357  * If the given test vector is a starming call, then this function calls
358  * the necessary init, update or final call. In the final call, the result
359  * is compared with the expected result.
360  *
361  * @param test_ext_vec The test vector to run.
362  * @return The result of the operation.
363  *
364  */
365 static status_t process_segment(hmac_extended_test_vector_t *test_ext_vec) {
366  // If `test_ext_vec` is done, simply return.
367  if (test_ext_vec->progress == kHmacTestDone) {
368  return OTCRYPTO_OK;
369  }
370 
371  // If `test_ext_vec` is one-shot, we need to call one-shot API.
372  if (test_ext_vec->segment_count == 0 &&
373  test_ext_vec->progress == kHmacTestNotStarted) {
374  LOG_INFO("Invoking oneshot HMAC for vector #%d",
375  test_ext_vec->original_idx);
376  hmac_oneshot(test_ext_vec->hmac_test_vector);
377  // Mark this test as complete
378  test_ext_vec->progress = kHmacTestDone;
379  return OTCRYPTO_OK;
380  }
381 
382  // A sanity check: oneshot calls should not arrive here and `segment_count`
383  // should be valid.
384  if (test_ext_vec->segment_count == 0 || test_ext_vec->segment_count > 4) {
385  return OTCRYPTO_BAD_ARGS;
386  }
387 
388  // If `test_ext_vec` is streaming, and this is the first call, then call init.
389  if (test_ext_vec->progress == kHmacTestNotStarted) {
390  LOG_INFO("Initializing HMAC stream for vector #%d",
391  test_ext_vec->original_idx);
392  ctx_init(&test_ext_vec->hash_ctx, test_ext_vec->hmac_test_vector);
393  test_ext_vec->progress = kHmacTestFeedSegment1;
394  return OTCRYPTO_OK;
395  }
396 
397  if (test_ext_vec->progress == kHmacTestFinalize) {
398  LOG_INFO("Finalizing HMAC stream for vector #%d",
399  test_ext_vec->original_idx);
400  hmac_finalize(&test_ext_vec->hash_ctx, test_ext_vec->hmac_test_vector);
401  test_ext_vec->progress = kHmacTestDone;
402  return OTCRYPTO_OK;
403  }
404 
405  // Handle the last message specially
406  if (test_ext_vec->progress == test_ext_vec->segment_count) {
407  size_t segment_start =
408  test_ext_vec->segment_idx[test_ext_vec->progress - 1];
409  size_t segment_len =
410  test_ext_vec->hmac_test_vector->message.len - segment_start;
411  // Sanity check
412  if (segment_start > test_ext_vec->hmac_test_vector->message.len) {
413  return OTCRYPTO_BAD_ARGS;
414  }
415  LOG_INFO("Streaming the last segment #%d of vector #%d.",
416  test_ext_vec->progress, test_ext_vec->original_idx);
417  feed_msg(&test_ext_vec->hash_ctx, test_ext_vec->hmac_test_vector,
418  segment_start, segment_len);
419  test_ext_vec->progress = kHmacTestFinalize;
420  return OTCRYPTO_OK;
421  }
422 
423  if (test_ext_vec->progress < test_ext_vec->segment_count) {
424  size_t segment_start =
425  test_ext_vec->segment_idx[test_ext_vec->progress - 1];
426  size_t segment_len =
427  test_ext_vec->segment_idx[test_ext_vec->progress] - segment_start;
428  LOG_INFO("Streaming segment #%d of vector #%d.", test_ext_vec->progress,
429  test_ext_vec->original_idx);
430  feed_msg(&test_ext_vec->hash_ctx, test_ext_vec->hmac_test_vector,
431  segment_start, segment_len);
432  test_ext_vec->progress++;
433  return OTCRYPTO_OK;
434  }
435  return OTCRYPTO_BAD_ARGS;
436 }
437 
438 /**
439  * Run all vectors specified in `kHmacExtendedTestVectors` in parallel step
440  * by step.
441  */
442 static status_t run_test(void) {
443  vectors_populate();
444 
445  for (size_t i = kHmacTestNotStarted; i < kHmacTestDone; i++) {
446  for (size_t j = 0; j < ARRAYSIZE(kHmacExtendedTestVectors); j++) {
447  TRY(process_segment(&kHmacExtendedTestVectors[j]));
448  }
449  }
450  return OTCRYPTO_OK;
451 }
452 
453 OTTF_DEFINE_TEST_CONFIG();
454 bool test_main(void) {
455  LOG_INFO("Testing cryptolib SHA-2/HMAC with parallel multiple streams.");
456  status_t test_result = OK_STATUS();
457  EXECUTE_TEST(test_result, run_test);
458  return status_ok(test_result);
459 }