Software APIs
drbg.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 
9 #include "sw/device/lib/crypto/drivers/entropy.h"
10 #include "sw/device/lib/crypto/impl/status.h"
12 
13 // Module ID for status codes.
14 #define MODULE_ID MAKE_MODULE_ID('r', 'b', 'g')
15 
16 /**
17  * Construct seed material for the CSRNG.
18  *
19  * Returns `OTCRYPTO_BAD_ARGS` if the provided value is longer than the entropy
20  * complex can absorb. If the value is shorter, it will be padded with zeroes.
21  *
22  * This operation is not constant-time relative to `value.len`.
23  *
24  * @param value Seed data.
25  * @param[out] seed_material Resulting entropy complex seed.
26  * @return OK or error.
27  */
28 static otcrypto_status_t seed_material_construct(
29  otcrypto_const_byte_buf_t value, entropy_seed_material_t *seed_material) {
30  if (value.len > kEntropySeedBytes) {
31  return OTCRYPTO_BAD_ARGS;
32  }
33 
34  size_t nwords = ceil_div(value.len, sizeof(uint32_t));
35  seed_material->len = nwords;
36 
37  // Initialize the set words to zero.
38  memset(seed_material->data, 0, nwords * sizeof(uint32_t));
39 
40  if (value.len == 0) {
41  return OTCRYPTO_OK;
42  }
43 
44  // Copy seed data.
45  // TODO(#17711) Change to `hardened_memcpy`.
46  memcpy(seed_material->data, value.data, value.len);
47 
48  return OTCRYPTO_OK;
49 }
50 
51 /**
52  * XOR the entropy seed with another value.
53  *
54  * Returns `OTCRYPTO_BAD_ARGS` if the provided value is longer than the seed.
55  * If the value is shorter, only the prefix will be XOR'ed.
56  *
57  * This operation is not constant-time relative to `value.len`.
58  *
59  * @param value Value to XOR with seed data.
60  * @param seed_material Entropy complex seed, modified in-place.
61  * @return OK or error.
62  */
63 static otcrypto_status_t seed_material_xor(
64  otcrypto_const_byte_buf_t value, entropy_seed_material_t *seed_material) {
65  if (value.len > kEntropySeedBytes) {
66  return OTCRYPTO_BAD_ARGS;
67  }
68  if (value.len == 0) {
69  return OTCRYPTO_OK;
70  }
71 
72  // Copy into a word-aligned buffer. Using a word-wise XOR is slightly safer
73  // from a side channel perspective than byte-wise.
74  size_t nwords = ceil_div(value.len, sizeof(uint32_t));
75  uint32_t value_words[nwords];
76  value_words[nwords - 1] = 0;
77  memcpy(value_words, value.data, value.len);
78 
79  // XOR with seed value.
80  for (size_t i = 0; i < nwords; i++) {
81  seed_material->data[i] ^= value_words[i];
82  }
83 
84  return OTCRYPTO_OK;
85 }
86 
88  otcrypto_const_byte_buf_t perso_string) {
89  // Check for NULL pointers or bad length.
90  if (perso_string.len != 0 && perso_string.data == NULL) {
91  return OTCRYPTO_BAD_ARGS;
92  }
93 
94  entropy_seed_material_t seed_material;
95  seed_material_construct(perso_string, &seed_material);
96 
97  HARDENED_TRY(entropy_csrng_uninstantiate());
98  return entropy_csrng_instantiate(/*disable_trng_input=*/kHardenedBoolFalse,
99  &seed_material);
100 }
101 
103  otcrypto_const_byte_buf_t additional_input) {
104  // Check for NULL pointers or bad length.
105  if (additional_input.len != 0 && additional_input.data == NULL) {
106  return OTCRYPTO_BAD_ARGS;
107  }
108 
109  entropy_seed_material_t seed_material;
110  seed_material_construct(additional_input, &seed_material);
111 
112  return entropy_csrng_reseed(/*disable_trng_input=*/kHardenedBoolFalse,
113  &seed_material);
114 }
115 
118  // Check for NULL pointers or bad length.
119  if (perso_string.len != 0 && perso_string.data == NULL) {
120  return OTCRYPTO_BAD_ARGS;
121  }
122  if (entropy.data == NULL || entropy.len != kEntropySeedBytes) {
123  return OTCRYPTO_BAD_ARGS;
124  }
125 
126  entropy_seed_material_t seed_material;
127  seed_material_construct(entropy, &seed_material);
128  seed_material_xor(perso_string, &seed_material);
129 
130  HARDENED_CHECK_EQ(seed_material.len, kEntropySeedWords);
131 
132  return entropy_csrng_instantiate(/*disable_trng_input=*/kHardenedBoolTrue,
133  &seed_material);
134 }
135 
138  otcrypto_const_byte_buf_t additional_input) {
139  // Check for NULL pointers or bad length.
140  if (additional_input.len != 0 && additional_input.data == NULL) {
141  return OTCRYPTO_BAD_ARGS;
142  }
143  if (entropy.data == NULL || entropy.len != kEntropySeedBytes) {
144  return OTCRYPTO_BAD_ARGS;
145  }
146 
147  entropy_seed_material_t seed_material;
148  seed_material_construct(entropy, &seed_material);
149  seed_material_xor(additional_input, &seed_material);
150 
151  HARDENED_CHECK_EQ(seed_material.len, kEntropySeedWords);
152 
153  return entropy_csrng_reseed(/*disable_trng_input=*/kHardenedBoolTrue,
154  &seed_material);
155 }
156 
157 /**
158  * Common function for random-bit generation.
159  *
160  * Used for both `otcrypto_drbg_generate` and `otcrypto_drbg_manual_generate`,
161  * which have the same implementation except for one flag that determines
162  * whether we check that the flag for FIPS compatibility is true.
163  *
164  * @param additional_input Additional input to DRBG
165  * @param fips_check Whether to check FIPS hardware flags
166  * @param[out] drbg_output Buffer for output
167  * @return Result status; OK or error
168  */
169 static otcrypto_status_t generate(hardened_bool_t fips_check,
170  otcrypto_const_byte_buf_t additional_input,
171  otcrypto_word32_buf_t drbg_output) {
172  if (drbg_output.len == 0) {
173  // Nothing to do.
174  return OTCRYPTO_OK;
175  }
176  if ((additional_input.len != 0 && additional_input.data == NULL) ||
177  drbg_output.data == NULL) {
178  return OTCRYPTO_BAD_ARGS;
179  }
180 
181  entropy_seed_material_t seed_material;
182  seed_material_construct(additional_input, &seed_material);
183  HARDENED_TRY(entropy_csrng_generate(&seed_material, drbg_output.data,
184  drbg_output.len, fips_check));
185 
186  return OTCRYPTO_OK;
187 }
188 
190  otcrypto_const_byte_buf_t additional_input,
191  otcrypto_word32_buf_t drbg_output) {
192  return generate(/*fips_check=*/kHardenedBoolTrue, additional_input,
193  drbg_output);
194 }
195 
197  otcrypto_const_byte_buf_t additional_input,
198  otcrypto_word32_buf_t drbg_output) {
199  return generate(/*fips_check=*/kHardenedBoolFalse, additional_input,
200  drbg_output);
201 }
202 
204  return entropy_csrng_uninstantiate();
205 }