1 /*
2 * Copyright (c) 2024 Raspberry Pi (Trading) Ltd.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 #include <stdio.h>
8 #include <string.h>
9 #include <inttypes.h>
10
11 #include "hardware/sha256.h"
12 #include "pico/bootrom/lock.h"
13 #include "pico/sha256.h"
14 #include "pico/time.h"
15
16 // We add one 0x80 byte, then 8 bytes for the size
17 #define SHA256_PADDING_DATA_BYTES 9
18 #define SHA256_BLOCK_SIZE_BYTES 64
19
pico_sha256_lock(pico_sha256_state_t * state)20 bool __weak pico_sha256_lock(pico_sha256_state_t *state) {
21 if (!bootrom_try_acquire_lock(BOOTROM_LOCK_SHA_256))
22 return false;
23 state->locked = true;
24 return true;
25 }
26
pico_sha256_unlock(pico_sha256_state_t * state)27 void __weak pico_sha256_unlock(pico_sha256_state_t *state) {
28 assert(state->locked);
29 bootrom_release_lock(BOOTROM_LOCK_SHA_256);
30 state->locked = false;
31 }
32
pico_sha256_try_start(pico_sha256_state_t * state,enum sha256_endianness endianness,bool use_dma)33 int pico_sha256_try_start(pico_sha256_state_t *state, enum sha256_endianness endianness, bool use_dma) {
34 memset(state, 0, sizeof(*state));
35 if (!pico_sha256_lock(state)) return PICO_ERROR_RESOURCE_IN_USE;
36 state->endianness = endianness;
37 if (use_dma) {
38 state->channel = (int8_t)dma_claim_unused_channel(false);
39 if (state->channel < 0) {
40 pico_sha256_unlock(state);
41 return PICO_ERROR_INSUFFICIENT_RESOURCES;
42 }
43 state->config = dma_channel_get_default_config(state->channel);
44 channel_config_set_transfer_data_size(&state->config, DMA_SIZE_8);
45 channel_config_set_read_increment(&state->config, true);
46 channel_config_set_write_increment(&state->config, false);
47 channel_config_set_dreq(&state->config, DREQ_SHA256);
48 sha256_set_dma_size(1);
49 } else {
50 state->channel = -1;
51 }
52 sha256_err_not_ready_clear();
53 sha256_set_bswap(endianness == SHA256_BIG_ENDIAN);
54 sha256_start();
55 state->total_data_size = 0;
56 return PICO_OK;
57 }
58
pico_sha256_start_blocking_until(pico_sha256_state_t * state,enum sha256_endianness endianness,bool use_dma,absolute_time_t until)59 int pico_sha256_start_blocking_until(pico_sha256_state_t *state, enum sha256_endianness endianness, bool use_dma, absolute_time_t until) {
60 int rc;
61 do {
62 rc = pico_sha256_try_start(state, endianness, use_dma);
63 if (rc != PICO_ERROR_RESOURCE_IN_USE) break;
64 if (time_reached(until)) {
65 rc = PICO_ERROR_TIMEOUT;
66 break;
67 }
68 } while (true);
69 return rc;
70 }
71
write_to_hardware(pico_sha256_state_t * state,const uint8_t * data,size_t data_size_bytes)72 static void write_to_hardware(pico_sha256_state_t *state, const uint8_t *data, size_t data_size_bytes) {
73 if (state->channel >= 0) {
74 dma_channel_wait_for_finish_blocking(state->channel);
75 assert(!sha256_err_not_ready());
76 sha256_wait_ready_blocking();
77 dma_channel_configure(
78 state->channel,
79 &state->config,
80 sha256_get_write_addr(),
81 data,
82 data_size_bytes,
83 true
84 );
85 } else {
86 if (!state->cache_used && !(((uintptr_t)data)&3u)) {
87 GCC_Like_Pragma("GCC diagnostic ignored \"-Wcast-align\"")
88 const uint32_t *data32 = (const uint32_t *)data;
89 // aligned writes
90 while (data_size_bytes >= 4) {
91 // write a whole word
92 sha256_wait_ready_blocking();
93 sha256_put_word(*data32++);
94 data_size_bytes -= 4;
95 }
96 data = (const uint8_t *)data32;
97 }
98 while (data_size_bytes--) {
99 state->cache.bytes[state->cache_used++] = *data++;
100 if (state->cache_used == 4) {
101 state->cache_used = 0;
102 sha256_wait_ready_blocking();
103 sha256_put_word(state->cache.word);
104 }
105 }
106 }
107 }
108
update_internal(pico_sha256_state_t * state,const uint8_t * data,size_t data_size_bytes)109 static void update_internal(pico_sha256_state_t *state, const uint8_t *data, size_t data_size_bytes) {
110 assert(state->locked);
111 // must finish off the last 64 byte block first or else sha256_err_not_ready will be true
112 size_t bytes_left = ((state->total_data_size + (SHA256_BLOCK_SIZE_BYTES - 1)) & ~(SHA256_BLOCK_SIZE_BYTES - 1)) - state->total_data_size;
113 if (bytes_left > data_size_bytes) bytes_left = data_size_bytes;
114 if (bytes_left > 0) {
115 write_to_hardware(state, data, bytes_left);
116 state->total_data_size += bytes_left;
117 data_size_bytes -= bytes_left;
118 data += bytes_left;
119 }
120 // Write the rest of the data
121 if (data_size_bytes > 0) {
122 write_to_hardware(state, data, data_size_bytes);
123 state->total_data_size += data_size_bytes;
124 }
125 }
126
add_zero_bytes(pico_sha256_state_t * state,size_t data_size_bytes)127 static void add_zero_bytes(pico_sha256_state_t *state, size_t data_size_bytes) {
128 uint32_t zero = 0;
129 // todo: can be done a bit more efficiently with dma?
130 assert(data_size_bytes < INT32_MAX);
131 while((int32_t)data_size_bytes > 0) {
132 update_internal(state, (uint8_t *)&zero, MIN(4, data_size_bytes));
133 data_size_bytes -= 4;
134 }
135 }
136
pico_sha256_update(pico_sha256_state_t * state,const uint8_t * data,size_t data_size_bytes)137 void pico_sha256_update(pico_sha256_state_t *state, const uint8_t *data, size_t data_size_bytes) {
138 update_internal(state, data, data_size_bytes);
139 }
140
pico_sha256_update_blocking(pico_sha256_state_t * state,const uint8_t * data,size_t data_size_bytes)141 void pico_sha256_update_blocking(pico_sha256_state_t *state, const uint8_t *data, size_t data_size_bytes) {
142 update_internal(state, data, data_size_bytes);
143 if (state->channel >= 0) {
144 dma_channel_wait_for_finish_blocking(state->channel);
145 }
146 }
147
148 // write the SHA-256 padding to hardware
write_padding(pico_sha256_state_t * state)149 static void write_padding(pico_sha256_state_t *state) {
150 // Has to be a multiple of 64 bytes
151 uint64_t size = (state->total_data_size + SHA256_PADDING_DATA_BYTES + (SHA256_BLOCK_SIZE_BYTES - 1)) & ~(SHA256_BLOCK_SIZE_BYTES - 1);
152 const size_t user_data_size = state->total_data_size;
153 const size_t padding_size_bytes = size - state->total_data_size;
154
155 // append a single '1' bit
156 const uint8_t one_bit = 0x80;
157 update_internal(state, &one_bit, 1);
158
159 // Zero unused padding
160 add_zero_bytes(state, padding_size_bytes - SHA256_PADDING_DATA_BYTES);
161
162 // Add size in bits, big endian
163 size = __builtin_bswap64(user_data_size * 8);
164 update_internal(state, (uint8_t*)&size, sizeof(uint64_t)); // last write
165 }
166
pico_sha256_finish(pico_sha256_state_t * state,sha256_result_t * out)167 void pico_sha256_finish(pico_sha256_state_t *state, sha256_result_t *out) {
168 assert(state->locked);
169 // pass NULL to abandon the current hash in case of an error
170 if (out) {
171 write_padding(state);
172 if (state->channel >= 0) {
173 dma_channel_wait_for_finish_blocking(state->channel);
174 assert(!sha256_err_not_ready());
175 }
176 sha256_wait_valid_blocking();
177 sha256_get_result(out, state->endianness);
178 }
179 if (state->channel >= 0) {
180 dma_channel_cleanup(state->channel);
181 dma_channel_unclaim(state->channel);
182 state->channel = -1;
183 }
184 pico_sha256_unlock(state);
185 }
186