Software APIs
sha256.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/sha2/sha256.h"
6 
11 #include "sw/device/lib/crypto/drivers/otbn.h"
12 #include "sw/device/lib/crypto/impl/status.h"
13 
14 // Module ID for status codes.
15 #define MODULE_ID MAKE_MODULE_ID('s', '2', '2')
16 
17 enum {
18  /**
19  * Maximum number of message chunks that the OTBN app can accept.
20  *
21  * This number is based on the DMEM size limit and usage by the SHA-256 app
22  * itself; see `run_sha256.s` for the detailed calculation.
23  */
24  kSha256MaxMessageChunksPerOtbnRun = 41,
25 };
26 
27 /**
28  * A type to hold message blocks.
29  */
30 typedef struct sha256_message_block {
31  uint32_t data[kSha256MessageBlockWords];
33 
34 /**
35  * Context object for the OTBN message buffer.
36  */
37 typedef struct sha256_otbn_ctx {
38  /**
39  * Number of message blocks currently loaded.
40  */
41  size_t num_blocks;
43 
44 // Initial state for SHA-256 (see FIPS 180-4, section 5.3.3). The SHA-256 OTBN
45 // app represents the state with little-endian words and in reverse word-order
46 // compared with FIPS 180-4.
47 static const uint32_t kSha256InitialState[kSha256StateWords] = {
48  0x5be0cd19, 0x1f83d9ab, 0x9b05688c, 0x510e527f,
49  0xa54ff53a, 0x3c6ef372, 0xbb67ae85, 0x6a09e667,
50 };
51 static_assert(sizeof(kSha256InitialState) == kSha256StateBytes,
52  "Initial state for SHA-256 has an unexpected size.");
53 
54 OTBN_DECLARE_APP_SYMBOLS(run_sha256); // The OTBN SHA-256 app.
55 OTBN_DECLARE_SYMBOL_ADDR(run_sha256, state); // Hash state.
56 OTBN_DECLARE_SYMBOL_ADDR(run_sha256, msg); // Input message.
57 OTBN_DECLARE_SYMBOL_ADDR(run_sha256,
58  num_msg_chunks); // Message length in blocks.
59 
60 static const otbn_app_t kOtbnAppSha256 = OTBN_APP_T_INIT(run_sha256);
61 static const otbn_addr_t kOtbnVarSha256State =
62  OTBN_ADDR_T_INIT(run_sha256, state);
63 static const otbn_addr_t kOtbnVarSha256Msg = OTBN_ADDR_T_INIT(run_sha256, msg);
64 static const otbn_addr_t kOtbnVarSha256NumMsgChunks =
65  OTBN_ADDR_T_INIT(run_sha256, num_msg_chunks);
66 
67 void sha256_init(sha256_state_t *state) {
68  // Set the initial state.
69  hardened_memcpy(state->H, kSha256InitialState, kSha256StateWords);
70  // Set the partial block to 0 (the value is ignored).
71  memset(state->partial_block, 0, kSha256MessageBlockBytes);
72  // Set the message length so far to 0.
73  state->total_len = 0ull;
74 }
75 
76 /**
77  * Run OTBN to process the data currently in DMEM.
78  *
79  * @param ctx OTBN message buffer context information (updated in place).
80  * @return Result of the operation.
81  */
82 static status_t process_message_buffer(sha256_otbn_ctx_t *ctx) {
83  // Write the number of blocks to DMEM.
84  HARDENED_TRY(
85  otbn_dmem_write(1, &ctx->num_blocks, kOtbnVarSha256NumMsgChunks));
86 
87  // Run the OTBN program.
88  HARDENED_TRY(otbn_execute());
89  HARDENED_TRY(otbn_busy_wait_for_done());
90 
91  // Reset the message buffer counter.
92  ctx->num_blocks = 0;
93  return OTCRYPTO_OK;
94 }
95 
96 /**
97  * Add a single message block to the processing buffer.
98  *
99  * Runs OTBN if the maximum number of message blocks has been reached.
100  *
101  * @param ctx OTBN message buffer context information (updated in place).
102  * @param block Block to write.
103  * @return Result of the operation.
104  */
105 static status_t process_block(sha256_otbn_ctx_t *ctx,
106  const sha256_message_block_t *block) {
107  // Calculate the offset within the message buffer.
108  size_t offset = ctx->num_blocks * kSha256MessageBlockBytes;
109  otbn_addr_t dst = kOtbnVarSha256Msg + offset;
110 
111  // Copy the message block into DMEM.
112  otbn_dmem_write(kSha256MessageBlockWords, block->data, dst);
113  ctx->num_blocks += 1;
114 
115  // If we've reached the maximum number of message chunks for a single run,
116  // then run the OTBN program to update the state in-place. Note that there
117  // is no need to read back and then re-write the state; it'll stay updated
118  // in DMEM for the next run.
119  if (ctx->num_blocks == kSha256MaxMessageChunksPerOtbnRun) {
120  HARDENED_TRY(process_message_buffer(ctx));
121  }
122  return OTCRYPTO_OK;
123 }
124 
125 /**
126  * Pad the block as described in FIPS 180-4, section 5.1.1.
127  *
128  * Padding fills the current block and may require one additional block. This
129  * function calls `process_block` to load the padded block(s) into OTBN.
130  *
131  * The length of real data in the partial block should be the byte-length of
132  * the message so far (total_len >> 3) modulo `kSha256MessageBlockBytes`.
133  *
134  * @param ctx OTBN message buffer context information (updated in place).
135  * @param total_len Total length of message so far.
136  * @param block Current (partial) block.
137  * @return Result of the operation.
138  */
139 static status_t process_padding(sha256_otbn_ctx_t *ctx,
140  const uint64_t total_len,
141  sha256_message_block_t *block) {
142  size_t partial_block_len = (total_len >> 3) % kSha256MessageBlockBytes;
143 
144  // Get a byte-sized pointer to the end of the real data within the block.
145  unsigned char *data_end = (unsigned char *)block->data + partial_block_len;
146 
147  // Fill the remainder of the block with zeroes.
148  size_t padding_len = kSha256MessageBlockBytes - partial_block_len;
149  memset(data_end, 0, padding_len);
150 
151  // Set the last byte after the message to 0x80. There must be at least one
152  // unfilled byte in the partial block, since partial_block_len is always <
153  // kSha256MessageBlockBytes.
154  memset(data_end, 0x80, 1);
155 
156  if (partial_block_len + 1 + sizeof(total_len) > kSha256MessageBlockBytes) {
157  // We need to use the additional block. The first block is already
158  // complete, so process it and then zero out the block data again to
159  // prepare the next one.
160  HARDENED_TRY(process_block(ctx, block));
161  memset(block, 0, kSha256MessageBlockBytes);
162  }
163 
164  // Set the last 64 bits of the final block to the bit-length in big-endian
165  // form.
166  block->data[kSha256MessageBlockWords - 1] =
167  __builtin_bswap32(total_len & UINT32_MAX);
168  block->data[kSha256MessageBlockWords - 2] =
169  __builtin_bswap32(total_len >> 32);
170 
171  // Process the last block.
172  return process_block(ctx, block);
173 }
174 
175 /**
176  * Update the hash state to include new data, optionally adding padding.
177  *
178  * @param state Context object.
179  * @param msg Input message.
180  * @param msg_len Input message length in bytes.
181  * @param padding_needed Whether to pad the message.
182  * @return Result of the operation.
183  */
184 static status_t process_message(sha256_state_t *state, const uint8_t *msg,
185  size_t msg_len,
186  hardened_bool_t padding_needed) {
187  // Load the SHA-256 app. Fails if OTBN is non-idle.
188  HARDENED_TRY(otbn_load_app(kOtbnAppSha256));
189 
190  // Check the message length. SHA-256 messages must be less than 2^64 bits
191  // long in total.
192  uint64_t msg_bits = ((uint64_t)msg_len) << 3;
193  uint64_t max_msg_bits = UINT64_MAX - state->total_len;
194  if (msg_bits > max_msg_bits) {
195  return OTCRYPTO_BAD_ARGS;
196  }
197 
198  // Calculate the new value of state->total_len. Do NOT update the state yet
199  // (because if we get an OTBN error, it would become out of sync).
200  sha256_state_t new_state;
201  new_state.total_len = state->total_len + msg_bits;
202 
203  // Set the initial state if at least one block has been received before now.
204  if (state->total_len >= kSha256MessageBlockBytes) {
205  HARDENED_TRY(
206  otbn_dmem_write(kSha256StateWords, state->H, kOtbnVarSha256State));
207  }
208 
209  // Start computing the first block for the hash computation by simply copying
210  // the partial block. We won't use the partial block directly to avoid
211  // contaminating the context object if this operation fails later.
213  size_t partial_block_len = (state->total_len >> 3) % kSha256MessageBlockBytes;
214  memcpy(block.data, state->partial_block, partial_block_len);
215 
216  // Initialize the context for the OTBN message buffer.
217  sha256_otbn_ctx_t ctx = {.num_blocks = 0};
218 
219  // Process the message one block at a time, including partial data if it is
220  // present (which is only possible on the first iteration).
221  while (msg_len >= kSha256MessageBlockBytes - partial_block_len) {
222  size_t available_len = kSha256MessageBlockBytes - partial_block_len;
223  memcpy((unsigned char *)block.data + partial_block_len, msg, available_len);
224  msg += available_len;
225  msg_len -= available_len;
226  HARDENED_TRY(process_block(&ctx, &block));
227  partial_block_len = 0;
228  }
229 
230  // Copy remaining mesage data into the working block (after partial data, if
231  // it is present). Because of the loop condition above, this must not be a
232  // full block.
233  memcpy((unsigned char *)block.data + partial_block_len, msg, msg_len);
234 
235  // Add padding if necessary.
236  if (padding_needed == kHardenedBoolTrue) {
237  HARDENED_TRY(process_padding(&ctx, new_state.total_len, &block));
238  }
239 
240  // If there are any unprocessed blocks currently in DMEM, run the program one
241  // final time.
242  if (ctx.num_blocks > 0) {
243  HARDENED_TRY(process_message_buffer(&ctx));
244  }
245 
246  // Read the final state from OTBN dmem.
247  HARDENED_TRY(
248  otbn_dmem_read(kSha256StateWords, kOtbnVarSha256State, new_state.H));
249 
250  // Clear OTBN's memory.
251  HARDENED_TRY(otbn_dmem_sec_wipe());
252 
253  // At this point, no more errors are possible; it is safe to update the
254  // context object.
255  hardened_memcpy(state->H, new_state.H, kSha256StateWords);
256  hardened_memcpy(state->partial_block, block.data, kSha256MessageBlockWords);
257  state->total_len = new_state.total_len;
258  return OTCRYPTO_OK;
259 }
260 
261 status_t sha256_update(sha256_state_t *state, const uint8_t *msg,
262  const size_t msg_len) {
263  // Process new data with no padding.
264  return process_message(state, msg, msg_len, kHardenedBoolFalse);
265 }
266 
267 /**
268  * Replace sensitive data in a SHA-256 context with non-sensitive values.
269  *
270  * @param state The context object to shred.
271  */
272 static void state_shred(sha256_state_t *state) {
273  hardened_memshred(state->H, kSha256StateWords);
274  hardened_memshred(state->partial_block, kSha256MessageBlockWords);
275  state->total_len = 0;
276 }
277 
278 /**
279  * Copy the final digest as a byte-string.
280  *
281  * SHA-256 intermediate computations use words in little-endian format, but the
282  * FIPS 180-4 spec requires big-endian words (see section 3.1). Therefore, to
283  * get the right final byte-order, we need to reverse each word in the state.
284  *
285  * Also, in the SHA-256 computation, the order of the words is reversed
286  * compared to FIPS 180-4, so we switch the word order as well.
287  *
288  * @param state Context object.
289  * @param[out] digest Destination buffer for digest.
290  */
291 static void digest_get(sha256_state_t *state, uint32_t *digest) {
292  for (size_t i = 0; i < kSha256StateWords / 2; i++) {
293  uint32_t tmp = __builtin_bswap32(state->H[i]);
294  state->H[i] = __builtin_bswap32(state->H[kSha256StateWords - 1 - i]);
295  state->H[kSha256StateWords - 1 - i] = tmp;
296  }
297  hardened_memcpy(digest, state->H, kSha256StateWords);
298 }
299 
300 status_t sha256_final(sha256_state_t *state, uint32_t *digest) {
301  // Construct padding.
302  HARDENED_TRY(process_message(state, NULL, 0, kHardenedBoolTrue));
303 
304  // Retrieve the final digest and destroy the state.
305  digest_get(state, digest);
306  state_shred(state);
307  return OTCRYPTO_OK;
308 }
309 
310 status_t sha256(const uint8_t *msg, const size_t msg_len, uint32_t *digest) {
311  sha256_state_t state;
312  sha256_init(&state);
313 
314  // Process data with padding enabled.
315  HARDENED_TRY(process_message(&state, msg, msg_len, kHardenedBoolTrue));
316 
317  // Retrieve the final digest and destroy the state.
318  digest_get(&state, digest);
319  state_shred(&state);
320  return OTCRYPTO_OK;
321 }