Software APIs
hkdf.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('h', 'k', 'd')
16 
17 /**
18  * Infer the digest length in 32-bit words for the given hash function.
19  *
20  * @param key_mode HMAC key mode.
21  * @param[out] digest_words Number of words in the hash digest.
22  * @return OK or error.
23  */
24 static status_t digest_num_words_from_key_mode(otcrypto_key_mode_t key_mode,
25  size_t *digest_words) {
26  *digest_words = 0;
27  switch (launder32(key_mode)) {
28  case kOtcryptoKeyModeHmacSha256:
29  HARDENED_CHECK_EQ(key_mode, kOtcryptoKeyModeHmacSha256);
30  *digest_words = 256 / 32;
31  break;
32  case kOtcryptoKeyModeHmacSha384:
33  HARDENED_CHECK_EQ(key_mode, kOtcryptoKeyModeHmacSha384);
34  *digest_words = 384 / 32;
35  break;
36  case kOtcryptoKeyModeHmacSha512:
37  HARDENED_CHECK_EQ(key_mode, kOtcryptoKeyModeHmacSha512);
38  *digest_words = 512 / 32;
39  break;
40  default:
41  return OTCRYPTO_BAD_ARGS;
42  }
43  HARDENED_CHECK_NE(*digest_words, 0);
44  return OTCRYPTO_OK;
45 }
46 
51  // Infer the digest length.
52  size_t digest_wordlen;
53  HARDENED_TRY(
54  digest_num_words_from_key_mode(ikm.config.key_mode, &digest_wordlen));
55  size_t digest_bytelen = digest_wordlen * sizeof(uint32_t);
56 
57  // Construct a blinded key struct for the intermediate key.
58  otcrypto_key_config_t prk_config = {
59  .version = kOtcryptoLibVersion1,
60  .key_mode = ikm.config.key_mode,
61  .key_length = digest_bytelen,
62  .hw_backed = kHardenedBoolFalse,
63  .exportable = kHardenedBoolFalse,
64  .security_level = kOtcryptoKeySecurityLevelLow,
65  };
66  size_t keyblob_wordlen = keyblob_num_words(prk_config);
67  uint32_t keyblob[keyblob_wordlen];
69  .config = prk_config,
70  .keyblob = keyblob,
71  .keyblob_length = sizeof(keyblob),
72  };
73 
74  // Call extract and expand.
75  HARDENED_TRY(otcrypto_hkdf_extract(ikm, salt, &prk));
76  return otcrypto_hkdf_expand(prk, info, okm);
77 }
78 
79 /**
80  * Check that an HKDF pseudo-random key is correctly configured.
81  *
82  * Ensures that the key mode, length, and allocated keyblob length are suitable
83  * for an HKDF pseudo-random key (i.e. the input for extract and the output for
84  * expand). Does not dereference the keyblob, so it is safe to call this before
85  * the keyblob is initialized.
86  *
87  * @param digest_words Length of the hash digest in 32-bit words.
88  * @param prk Pseudo-random key struct to check.
89  * @return OK if the PRK is acceptable, otherwise OTCRYPTO_BAD_ARGS.
90  */
91 static status_t hkdf_check_prk(size_t digest_words,
92  const otcrypto_blinded_key_t *prk) {
93  if (launder32(prk->config.key_mode) >> 16 != kOtcryptoKeyTypeHmac) {
94  return OTCRYPTO_BAD_ARGS;
95  }
96  HARDENED_CHECK_EQ(prk->config.key_mode >> 16, kOtcryptoKeyTypeHmac);
97 
98  // PRK should be the same length as the digest.
99  size_t digest_bytelen = digest_words * sizeof(uint32_t);
100  if (launder32(prk->config.key_length) != digest_bytelen) {
101  return OTCRYPTO_BAD_ARGS;
102  }
103  HARDENED_CHECK_EQ(prk->config.key_length, digest_bytelen);
104 
105  // Check the keyblob length.
106  size_t keyblob_bytelen = keyblob_num_words(prk->config) * sizeof(uint32_t);
107  if (launder32(prk->keyblob_length) != keyblob_bytelen) {
108  return OTCRYPTO_BAD_ARGS;
109  }
110  HARDENED_CHECK_EQ(prk->keyblob_length, keyblob_bytelen);
111 
112  // Ensure that the PRK is a symmetric key masked with XOR and is not supposed
113  // to be hardware-backed.
114  HARDENED_TRY(keyblob_ensure_xor_masked(prk->config));
115 
116  return OTCRYPTO_OK;
117 }
118 
121  otcrypto_blinded_key_t *prk) {
122  // Check for null pointers.
123  if (ikm.keyblob == NULL || prk == NULL || prk->keyblob == NULL) {
124  return OTCRYPTO_BAD_ARGS;
125  }
126  if (salt.data == NULL && salt.len != 0) {
127  return OTCRYPTO_BAD_ARGS;
128  }
129 
130  // Check the private key checksum.
131  if (integrity_blinded_key_check(&ikm) != kHardenedBoolTrue) {
132  return OTCRYPTO_BAD_ARGS;
133  }
134 
135  if (launder32(ikm.config.security_level) != kOtcryptoKeySecurityLevelLow ||
136  launder32(prk->config.security_level) != kOtcryptoKeySecurityLevelLow) {
137  // The underlying HMAC implementation is not currently hardened.
138  return OTCRYPTO_NOT_IMPLEMENTED;
139  }
140  HARDENED_CHECK_EQ(ikm.config.security_level, kOtcryptoKeySecurityLevelLow);
141  HARDENED_CHECK_EQ(prk->config.security_level, kOtcryptoKeySecurityLevelLow);
142 
143  // Ensure the key modes match.
144  if (launder32(prk->config.key_mode) != launder32(ikm.config.key_mode)) {
145  return OTCRYPTO_BAD_ARGS;
146  }
147  HARDENED_CHECK_EQ(prk->config.key_mode, ikm.config.key_mode);
148 
149  // Infer the digest size. This step also ensures that the key mode is
150  // supported.
151  size_t digest_words = 0;
152  HARDENED_TRY(
153  digest_num_words_from_key_mode(ikm.config.key_mode, &digest_words));
154 
155  // Validate the PRK configuration.
156  HARDENED_TRY(hkdf_check_prk(digest_words, prk));
157 
158  // Copy the salt into a 32-bit aligned buffer. If the salt is empty, replace
159  // it with a string of `hashLen` zeroes as specified in RFC 5869.
160  size_t salt_bytelen =
161  (salt.len == 0) ? digest_words * sizeof(uint32_t) : salt.len;
162  size_t salt_wordlen = ceil_div(salt_bytelen, sizeof(uint32_t));
163  uint32_t salt_aligned_data[salt_wordlen];
164  memset(salt_aligned_data, 0, sizeof(salt_aligned_data));
165  if (salt.len > 0) {
166  memcpy(salt_aligned_data, salt.data, salt.len);
167  }
168 
169  // The extract stage uses `salt` as the key and the input key as the message.
170  // We therefore need to unmask the key and package the salt in a blinded key
171  // struct.
172 
173  // Unmask the input key.
174  uint32_t *ikm_share0;
175  uint32_t *ikm_share1;
176  HARDENED_TRY(keyblob_to_shares(&ikm, &ikm_share0, &ikm_share1));
177  uint32_t unmasked_ikm_data[keyblob_share_num_words(ikm.config)];
178  for (size_t i = 0; i < ARRAYSIZE(unmasked_ikm_data); i++) {
179  unmasked_ikm_data[i] = ikm_share0[i] ^ ikm_share1[i];
180  }
181  otcrypto_const_byte_buf_t unmasked_ikm = {
182  .data = (unsigned char *)unmasked_ikm_data,
183  .len = ikm.config.key_length,
184  };
185 
186  // Package the salt value in a blinded key, using an all-zero mask because
187  // the salt is not actually secret.
188  uint32_t salt_mask[ARRAYSIZE(salt_aligned_data)];
189  memset(salt_mask, 0, sizeof(salt_mask));
190  otcrypto_key_config_t salt_key_config = {
191  .version = kOtcryptoLibVersion1,
192  .key_mode = ikm.config.key_mode,
193  .key_length = salt_bytelen,
194  .hw_backed = kHardenedBoolFalse,
195  .exportable = kHardenedBoolFalse,
196  .security_level = kOtcryptoKeySecurityLevelLow,
197  };
198  uint32_t salt_keyblob[keyblob_num_words(salt_key_config)];
199  TRY(keyblob_from_key_and_mask(salt_aligned_data, salt_mask, salt_key_config,
200  salt_keyblob));
201  otcrypto_blinded_key_t salt_key = {
202  .config = salt_key_config,
203  .keyblob = salt_keyblob,
204  .keyblob_length = sizeof(salt_keyblob),
205  };
206 
207  // Call HMAC(salt, IKM).
208  uint32_t tag_data[digest_words];
209  otcrypto_word32_buf_t tag = {.data = tag_data, .len = ARRAYSIZE(tag_data)};
210  HARDENED_TRY(otcrypto_hmac(&salt_key, unmasked_ikm, tag));
211 
212  // Construct the blinded keyblob for PRK (with an all-zero mask for now
213  // because HMAC is unhardened anyway).
214  uint32_t prk_mask[digest_words];
215  memset(prk_mask, 0, sizeof(prk_mask));
216  HARDENED_TRY(
217  keyblob_from_key_and_mask(tag_data, prk_mask, prk->config, prk->keyblob));
218  prk->checksum = integrity_blinded_checksum(prk);
219  return OTCRYPTO_OK;
220 }
221 
224  otcrypto_blinded_key_t *okm) {
225  if (okm == NULL || okm->keyblob == NULL || prk.keyblob == NULL) {
226  return OTCRYPTO_BAD_ARGS;
227  }
228  if (info.data == NULL && info.len != 0) {
229  return OTCRYPTO_BAD_ARGS;
230  }
231 
232  if (launder32(okm->config.security_level) != kOtcryptoKeySecurityLevelLow ||
233  launder32(prk.config.security_level) != kOtcryptoKeySecurityLevelLow) {
234  // The underlying HMAC implementation is not currently hardened.
235  return OTCRYPTO_NOT_IMPLEMENTED;
236  }
237 
238  // Infer the digest size.
239  size_t digest_words = 0;
240  HARDENED_TRY(
241  digest_num_words_from_key_mode(prk.config.key_mode, &digest_words));
242 
243  // Check the PRK configuration.
244  HARDENED_TRY(hkdf_check_prk(digest_words, &prk));
245 
246  // Ensure that the derived key is a symmetric key masked with XOR and is not
247  // supposed to be hardware-backed.
248  HARDENED_TRY(keyblob_ensure_xor_masked(okm->config));
249 
250  // Check the keyblob length.
251  size_t keyblob_bytelen = keyblob_num_words(okm->config) * sizeof(uint32_t);
252  if (launder32(okm->keyblob_length) != keyblob_bytelen) {
253  return OTCRYPTO_BAD_ARGS;
254  }
255  HARDENED_CHECK_EQ(okm->keyblob_length, keyblob_bytelen);
256 
257  // Check that the unmasked key length is not too large for HKDF (see RFC
258  // 5869, section 2.3).
259  size_t okm_bytelen = okm->config.key_length;
260  size_t okm_wordlen = ceil_div(okm_bytelen, sizeof(uint32_t));
261  size_t num_iterations = ceil_div(okm_wordlen, digest_words);
262  if (launder32(num_iterations) > 255) {
263  return OTCRYPTO_BAD_ARGS;
264  }
265  HARDENED_CHECK_LE(num_iterations, 255);
266 
267  // Create a buffer that holds `info` and a one-byte counter.
268  uint8_t info_and_counter_data[info.len + 1];
269  memcpy(info_and_counter_data, info.data, info.len);
270  info_and_counter_data[info.len] = 0x00;
271  otcrypto_const_byte_buf_t info_and_counter = {
272  .data = info_and_counter_data,
273  .len = sizeof(info_and_counter_data),
274  };
275 
276  // Repeatedly call HMAC to generate the derived key (see RFC 5869, section
277  // 2.3):
278  uint32_t okm_data[okm_wordlen];
279  uint32_t *t_data = okm_data;
280  for (uint8_t i = 0; i < num_iterations; i++) {
281  info_and_counter_data[info.len] = i + 1;
283  HARDENED_TRY(otcrypto_hmac_init(&ctx, &prk));
284  if (launder32(i) != 0) {
285  otcrypto_const_byte_buf_t t_bytes = {
286  .data = (unsigned char *)t_data,
287  .len = digest_words * sizeof(uint32_t),
288  };
289  HARDENED_TRY(otcrypto_hmac_update(&ctx, t_bytes));
290  t_data += digest_words;
291  }
292  HARDENED_TRY(otcrypto_hmac_update(&ctx, info_and_counter));
293  otcrypto_word32_buf_t t_words = {
294  .data = t_data,
295  .len = digest_words,
296  };
297  HARDENED_TRY(otcrypto_hmac_final(&ctx, t_words));
298  }
299 
300  // Generate a mask (all-zero for now, since HMAC is unhardened anyway).
301  uint32_t mask[digest_words];
302  memset(mask, 0, sizeof(mask));
303 
304  // Construct a blinded key.
305  HARDENED_TRY(
306  keyblob_from_key_and_mask(okm_data, mask, okm->config, okm->keyblob));
307  okm->checksum = integrity_blinded_checksum(okm);
308  return OTCRYPTO_OK;
309 }