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