Software APIs
kdf_ctr.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 
8 #include "sw/device/lib/crypto/impl/integrity.h"
9 #include "sw/device/lib/crypto/impl/keyblob.h"
10 #include "sw/device/lib/crypto/impl/status.h"
13 
14 // Module ID for status codes.
15 #define MODULE_ID MAKE_MODULE_ID('c', 'k', 'd')
16 
17 /**
18  * Check if the string contains a 0x00 byte.
19  *
20  * @param buffer Inspected string.
21  * @return OK or error.
22  */
23 static status_t check_zero_byte(const otcrypto_const_byte_buf_t buffer) {
24  for (size_t i = 0; i < buffer.len; i++) {
25  if (buffer.data[i] == 0x00) {
26  return OTCRYPTO_BAD_ARGS;
27  }
28  }
29  return OTCRYPTO_OK;
30 }
31 
32 /**
33  * Infer the digest length in 32-bit words for the given hash function.
34  *
35  * @param key_mode HMAC key mode.
36  * @param[out] digest_words Number of words in the hash digest.
37  * @return OK or error.
38  */
39 static status_t digest_num_words_from_key_mode(otcrypto_key_mode_t key_mode,
40  size_t *digest_words) {
41  *digest_words = 0;
42  switch (launder32(key_mode)) {
43  case kOtcryptoKeyModeHmacSha256:
44  HARDENED_CHECK_EQ(key_mode, kOtcryptoKeyModeHmacSha256);
45  *digest_words = 256 / 32;
46  break;
47  case kOtcryptoKeyModeHmacSha384:
48  HARDENED_CHECK_EQ(key_mode, kOtcryptoKeyModeHmacSha384);
49  *digest_words = 384 / 32;
50  break;
51  case kOtcryptoKeyModeHmacSha512:
52  HARDENED_CHECK_EQ(key_mode, kOtcryptoKeyModeHmacSha512);
53  *digest_words = 512 / 32;
54  break;
55  default:
56  return OTCRYPTO_BAD_ARGS;
57  }
58  HARDENED_CHECK_NE(*digest_words, 0);
59  return OTCRYPTO_OK;
60 }
61 
63  const otcrypto_blinded_key_t key_derivation_key,
64  const otcrypto_const_byte_buf_t label,
65  const otcrypto_const_byte_buf_t context,
66  otcrypto_blinded_key_t *output_key_material) {
67  // Check NULL pointers.
68  if (output_key_material == NULL || output_key_material->keyblob == NULL ||
69  key_derivation_key.keyblob == NULL) {
70  return OTCRYPTO_BAD_ARGS;
71  }
72 
73  if (launder32(output_key_material->config.security_level) !=
74  kOtcryptoKeySecurityLevelLow ||
75  launder32(key_derivation_key.config.security_level) !=
76  kOtcryptoKeySecurityLevelLow) {
77  // The underlying HMAC implementation is not currently hardened.
78  return OTCRYPTO_NOT_IMPLEMENTED;
79  }
80 
81  // Check for null label with nonzero length.
82  if (label.data == NULL && label.len != 0) {
83  return OTCRYPTO_BAD_ARGS;
84  }
85 
86  // Check for null context with nonzero length.
87  if (context.data == NULL && context.len != 0) {
88  return OTCRYPTO_BAD_ARGS;
89  }
90 
91  // Check the private key checksum.
92  if (integrity_blinded_key_check(&key_derivation_key) != kHardenedBoolTrue) {
93  return OTCRYPTO_BAD_ARGS;
94  }
95 
96  // Check non-zero length for output_key_material.
97  if (output_key_material->config.key_length == 0) {
98  return OTCRYPTO_BAD_ARGS;
99  }
100 
101  // Infer the digest size.
102  size_t digest_word_len = 0;
103  HARDENED_TRY(digest_num_words_from_key_mode(
104  key_derivation_key.config.key_mode, &digest_word_len));
105 
106  // Ensure that the derived key is a symmetric key masked with XOR and is not
107  // supposed to be hardware-backed.
108  HARDENED_TRY(keyblob_ensure_xor_masked(output_key_material->config));
109 
110  // Check `output_key_material` key length.
111  if (output_key_material->config.hw_backed == kHardenedBoolTrue) {
112  // The case where `output_key_material` is hw_backed is addressed by
113  // `otcrypto_hw_backed_key` function in `key_transport.h`.
114  return OTCRYPTO_BAD_ARGS;
115  } else if (output_key_material->config.hw_backed == kHardenedBoolFalse) {
116  if (output_key_material->keyblob_length !=
117  keyblob_num_words(output_key_material->config) * sizeof(uint32_t)) {
118  return OTCRYPTO_BAD_ARGS;
119  }
120  } else {
121  return OTCRYPTO_BAD_ARGS;
122  }
123 
124  // Check that the unmasked key length is not too large for HMAC CTR
125  // (see NIST SP 800-108r1, section 4.1)
126  size_t required_byte_len = output_key_material->config.key_length;
127  size_t required_word_len = ceil_div(required_byte_len, sizeof(uint32_t));
128  size_t num_iterations = ceil_div(required_word_len, digest_word_len);
129  if (launder32(num_iterations) > UINT32_MAX ||
130  launder32(required_byte_len) > UINT32_MAX / 8) {
131  return OTCRYPTO_BAD_ARGS;
132  }
133  HARDENED_CHECK_LE(num_iterations, UINT32_MAX);
134  HARDENED_CHECK_LE(required_byte_len, UINT32_MAX / 8);
135  uint32_t required_bit_len = __builtin_bswap32(required_byte_len * 8);
136 
137  // Check if label or context contain 0x00 bytes
138  // Since 0x00 is used as the delimiter between label and context
139  // there shouldn't be multiple instances of them in input data
140  HARDENED_TRY(check_zero_byte(label));
141  HARDENED_TRY(check_zero_byte(context));
142 
143  // Setup
144  uint8_t zero = 0x00;
145 
146  // Repeatedly call HMAC to generate the derived key based on input data:
147  // [i]_2 || Label || 0x00 || Context || [L]_2
148  // (see NIST SP 800-108r1, section 4.1)
149  // [i]_2 is the binary representation of the counter value
150  // [L]_2 is the binary representation of the required bit length
151  // The counter value is updated within the loop
152 
153  uint32_t output_key_material_len =
154  required_word_len + digest_word_len - required_word_len % digest_word_len;
155  uint32_t output_key_material_data[output_key_material_len];
156 
157  for (uint32_t i = 0; i < num_iterations; i++) {
159  HARDENED_TRY(otcrypto_hmac_init(&ctx, &key_derivation_key));
160  uint32_t counter_be = __builtin_bswap32(i + 1);
161  HARDENED_TRY(otcrypto_hmac_update(
163  .data = (const unsigned char *const)&counter_be,
164  .len = sizeof(counter_be)}));
165  HARDENED_TRY(otcrypto_hmac_update(&ctx, label));
166  HARDENED_TRY(otcrypto_hmac_update(
167  &ctx,
168  (otcrypto_const_byte_buf_t){.data = (const unsigned char *const)&zero,
169  .len = sizeof(zero)}));
170  HARDENED_TRY(otcrypto_hmac_update(&ctx, context));
171  HARDENED_TRY(otcrypto_hmac_update(
173  .data = (const unsigned char *const)&required_bit_len,
174  .len = sizeof(required_bit_len)}));
175  uint32_t *tag_dest = output_key_material_data + i * digest_word_len;
176  HARDENED_TRY(otcrypto_hmac_final(
177  &ctx,
178  (otcrypto_word32_buf_t){.data = tag_dest, .len = digest_word_len}));
179  }
180 
181  // Generate a mask (all-zero for now, since HMAC is unhardened anyway).
182  uint32_t mask[digest_word_len];
183  memset(mask, 0, sizeof(mask));
184 
185  // Construct a blinded key.
186  HARDENED_TRY(keyblob_from_key_and_mask(output_key_material_data, mask,
187  output_key_material->config,
188  output_key_material->keyblob));
189 
190  output_key_material->checksum =
191  integrity_blinded_checksum(output_key_material);
192 
193  return OTCRYPTO_OK;
194 }