1 /*
2  * This file is part of the MicroPython project, http://micropython.org/
3  *
4  * The MIT License (MIT)
5  *
6  * Copyright (c) 2020-2021 Damien P. George
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a copy
9  * of this software and associated documentation files (the "Software"), to deal
10  * in the Software without restriction, including without limitation the rights
11  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12  * copies of the Software, and to permit persons to whom the Software is
13  * furnished to do so, subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be included in
16  * all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24  * THE SOFTWARE.
25  */
26 
27 #include <string.h>
28 
29 #include "dfu.h"
30 #include "gzstream.h"
31 #include "mboot.h"
32 #include "pack.h"
33 
34 #if MBOOT_ENABLE_PACKING
35 
36 // Keys provided externally by the board, will be built into mboot flash.
37 #include MBOOT_PACK_KEYS_FILE
38 
39 // Encrypted dfu files using gzip require a decompress buffer. Larger can be faster.
40 // This setting is independent to the incoming encrypted/signed/compressed DFU file.
41 #ifndef MBOOT_PACK_GZIP_BUFFER_SIZE
42 #define MBOOT_PACK_GZIP_BUFFER_SIZE (2048)
43 #endif
44 
45 // State to manage automatic flash erasure.
46 static uint32_t erased_base_addr;
47 static uint32_t erased_top_addr;
48 
49 // DFU chunk buffer, used to cache incoming blocks of data from USB.
50 static uint32_t firmware_chunk_base_addr;
51 static mboot_pack_chunk_buf_t firmware_chunk_buf;
52 
53 // Temporary buffer for decrypted data.
54 static uint8_t decrypted_buf[MBOOT_PACK_DFU_CHUNK_BUF_SIZE] __attribute__((aligned(8)));
55 
56 // Temporary buffer for uncompressing.
57 static uint8_t uncompressed_buf[MBOOT_PACK_GZIP_BUFFER_SIZE] __attribute__((aligned(8)));
58 
59 // Buffer to hold the start of the firmware, which is only written once the
60 // entire firmware is validated.  This is 8 bytes due to STM32WB MCUs requiring
61 // that a double-word write to flash can only be done once (due to ECC).
62 static uint8_t firmware_head[8];
63 
64 // Flag to indicate that firmware_head contains valid data.
65 static bool firmware_head_valid;
66 
mboot_pack_init(void)67 void mboot_pack_init(void) {
68     erased_base_addr = 0;
69     erased_top_addr = 0;
70     firmware_chunk_base_addr = 0;
71     firmware_head_valid = false;
72 }
73 
74 // In encrypted mode the erase is automatically managed.
75 // Note: this scheme requires blocks be written in sequence, which is the case.
mboot_pack_erase(uint32_t addr,size_t len)76 static int mboot_pack_erase(uint32_t addr, size_t len) {
77     while (!(erased_base_addr <= addr && addr + len <= erased_top_addr)) {
78         uint32_t erase;
79         if (erased_base_addr <= addr && addr < erased_top_addr) {
80             erase = erased_top_addr;
81         } else {
82             erase = addr;
83             erased_base_addr = addr;
84         }
85         uint32_t next_addr;
86         int ret = hw_page_erase(erase, &next_addr);
87         if (ret != 0) {
88             return ret;
89         }
90         erased_top_addr = next_addr;
91     }
92     return 0;
93 }
94 
95 // Commit an unencrypted and uncompressed chunk of firmware to the flash.
mboot_pack_commit_chunk(uint32_t addr,uint8_t * data,size_t len)96 static int mboot_pack_commit_chunk(uint32_t addr, uint8_t *data, size_t len) {
97     // Erase any required sectors before writing.
98     int ret = mboot_pack_erase(addr, len);
99     if (ret != 0) {
100         return ret;
101     }
102 
103     if (addr == APPLICATION_ADDR) {
104         // Don't write the very start of the firmware, just copy it into a temporary buffer.
105         // It will be written only if the full firmware passes the checksum/signature.
106         memcpy(firmware_head, data, sizeof(firmware_head));
107         addr += sizeof(firmware_head);
108         data += sizeof(firmware_head);
109         len -= sizeof(firmware_head);
110         firmware_head_valid = true;
111     }
112 
113     // Commit this piece of the firmware.
114     return hw_write(addr, data, len);
115 }
116 
117 // Handle a chunk with the full firmware signature.
mboot_pack_handle_full_sig(void)118 static int mboot_pack_handle_full_sig(void) {
119     if (firmware_chunk_buf.header.length < hydro_sign_BYTES) {
120         return -MBOOT_ERRNO_PACK_INVALID_CHUNK;
121     }
122 
123     uint8_t *full_sig = &firmware_chunk_buf.data[firmware_chunk_buf.header.length - hydro_sign_BYTES];
124     uint32_t *region_data = (uint32_t *)&firmware_chunk_buf.data[0];
125     size_t num_regions = (full_sig - (uint8_t *)region_data) / sizeof(uint32_t) / 2;
126 
127     uint8_t *buf = decrypted_buf;
128     const size_t buf_alloc = sizeof(decrypted_buf);
129 
130     // Compute the signature of the full firmware.
131     hydro_sign_state sign_state;
132     hydro_sign_init(&sign_state, MBOOT_PACK_HYDRO_CONTEXT);
133     for (size_t region = 0; region < num_regions; ++region) {
134         uint32_t addr = region_data[2 * region];
135         uint32_t len = region_data[2 * region + 1];
136         while (len) {
137             uint32_t l = len <= buf_alloc ? len : buf_alloc;
138             hw_read(addr, l, buf);
139             if (addr == APPLICATION_ADDR && firmware_head_valid) {
140                 // The start of the firmware was not yet written to flash so copy
141                 // it out of the temporary buffer to compute the full signature.
142                 memcpy(buf, firmware_head, sizeof(firmware_head));
143             }
144             int ret = hydro_sign_update(&sign_state, buf, l);
145             if (ret != 0) {
146                 return -MBOOT_ERRNO_PACK_SIGN_FAILED;
147             }
148             addr += l;
149             len -= l;
150         }
151     }
152 
153     // Verify the signature of the full firmware.
154     int ret = hydro_sign_final_verify(&sign_state, full_sig, mboot_pack_sign_public_key);
155     if (ret != 0) {
156         dfu_context.status = DFU_STATUS_ERROR_VERIFY;
157         dfu_context.error = MBOOT_ERROR_STR_INVALID_SIG_IDX;
158         return -MBOOT_ERRNO_PACK_SIGN_FAILED;
159     }
160 
161     // Full firmware passed the signature check.
162 
163     if (firmware_head_valid) {
164         // Write the start of the firmware so it boots.
165         ret = hw_write(APPLICATION_ADDR, firmware_head, sizeof(firmware_head));
166     }
167 
168     return ret;
169 }
170 
171 // Handle a chunk with firmware data.
mboot_pack_handle_firmware(void)172 static int mboot_pack_handle_firmware(void) {
173     const uint8_t *fw_data = &firmware_chunk_buf.data[0];
174     const size_t fw_len = firmware_chunk_buf.header.length;
175 
176     // Decrypt the chunk.
177     if (hydro_secretbox_decrypt(decrypted_buf, fw_data, fw_len, 0, MBOOT_PACK_HYDRO_CONTEXT, mboot_pack_secretbox_key) != 0) {
178         dfu_context.status = DFU_STATUS_ERROR_VERIFY;
179         dfu_context.error = MBOOT_ERROR_STR_INVALID_SIG_IDX;
180         return -MBOOT_ERRNO_PACK_DECRYPT_FAILED;
181     }
182 
183     // Use the decrypted message contents going formward.
184     size_t len = fw_len - hydro_secretbox_HEADERBYTES;
185     uint32_t addr = firmware_chunk_buf.header.address;
186 
187     if (firmware_chunk_buf.header.format == MBOOT_PACK_CHUNK_FW_GZIP) {
188         // Decompress chunk data.
189         gz_stream_init_from_raw_data(decrypted_buf, len);
190         for (;;) {
191             int read = gz_stream_read(sizeof(uncompressed_buf), uncompressed_buf);
192             if (read == 0) {
193                 return 0; // finished decompressing
194             } else if (read < 0) {
195                 return -MBOOT_ERRNO_GUNZIP_FAILED; // error reading
196             }
197             int ret = mboot_pack_commit_chunk(addr, uncompressed_buf, read);
198             if (ret != 0) {
199                 return ret;
200             }
201             addr += read;
202         }
203     } else {
204         // Commit chunk data directly.
205         return mboot_pack_commit_chunk(addr, decrypted_buf, len);
206     }
207 }
208 
mboot_pack_write(uint32_t addr,const uint8_t * src8,size_t len)209 int mboot_pack_write(uint32_t addr, const uint8_t *src8, size_t len) {
210     if (addr == APPLICATION_ADDR) {
211         // Base address of main firmware, reset any previous state
212         firmware_chunk_base_addr = 0;
213     }
214 
215     if (firmware_chunk_base_addr == 0) {
216         // First piece of data starting a new chunk, so set the base address.
217         firmware_chunk_base_addr = addr;
218     }
219 
220     if (addr < firmware_chunk_base_addr) {
221         // Address out of range.
222         firmware_chunk_base_addr = 0;
223         return -MBOOT_ERRNO_PACK_INVALID_ADDR;
224     }
225 
226     size_t offset = addr - firmware_chunk_base_addr;
227     if (offset + len > sizeof(firmware_chunk_buf)) {
228         // Address/length out of range.
229         firmware_chunk_base_addr = 0;
230         return -MBOOT_ERRNO_PACK_INVALID_ADDR;
231     }
232 
233     // Copy in the new data piece into the chunk buffer.
234     memcpy((uint8_t *)&firmware_chunk_buf + offset, src8, len);
235 
236     if (offset + len < sizeof(firmware_chunk_buf.header)) {
237         // Don't have the header yet.
238         return 0;
239     }
240 
241     if (firmware_chunk_buf.header.header_vers != MBOOT_PACK_HEADER_VERSION) {
242         // Chunk header has the wrong version.
243         dfu_context.status = DFU_STATUS_ERROR_FILE;
244         dfu_context.error = MBOOT_ERROR_STR_INVALID_SIG_IDX;
245         return -MBOOT_ERRNO_PACK_INVALID_VERSION;
246     }
247 
248     if (firmware_chunk_buf.header.address != firmware_chunk_base_addr) {
249         // Chunk address doesn't agree with dfu address, abort.
250         dfu_context.status = DFU_STATUS_ERROR_ADDRESS;
251         dfu_context.error = MBOOT_ERROR_STR_INVALID_SIG_IDX;
252         return -MBOOT_ERRNO_PACK_INVALID_ADDR;
253     }
254 
255     if (offset + len < sizeof(firmware_chunk_buf.header) + firmware_chunk_buf.header.length + sizeof(firmware_chunk_buf.signature)) {
256         // Don't have the full chunk yet.
257         return 0;
258     }
259 
260     // Have the full chunk in firmware_chunk_buf, process it now.
261 
262     // Reset the chunk base address for the next chunk that comes in.
263     firmware_chunk_base_addr = 0;
264 
265     // Verify the signature of the chunk.
266     const size_t fw_len = firmware_chunk_buf.header.length;
267     const uint8_t *sig = &firmware_chunk_buf.data[0] + fw_len;
268     if (hydro_sign_verify(sig, &firmware_chunk_buf, sizeof(firmware_chunk_buf.header) + fw_len,
269         MBOOT_PACK_HYDRO_CONTEXT, mboot_pack_sign_public_key) != 0) {
270         // Signature failed
271         dfu_context.status = DFU_STATUS_ERROR_VERIFY;
272         dfu_context.error = MBOOT_ERROR_STR_INVALID_SIG_IDX;
273         return -MBOOT_ERRNO_PACK_SIGN_FAILED;
274     }
275 
276     // Signature passed, we have valid chunk.
277 
278     if (firmware_chunk_buf.header.format == MBOOT_PACK_CHUNK_META) {
279         // Ignore META chunks.
280         return 0;
281     } else if (firmware_chunk_buf.header.format == MBOOT_PACK_CHUNK_FULL_SIG) {
282         return mboot_pack_handle_full_sig();
283     } else if (firmware_chunk_buf.header.format == MBOOT_PACK_CHUNK_FW_RAW
284         || firmware_chunk_buf.header.format == MBOOT_PACK_CHUNK_FW_GZIP) {
285         return mboot_pack_handle_firmware();
286     } else {
287         // Unsupported contents.
288         return -MBOOT_ERRNO_PACK_INVALID_CHUNK;
289     }
290 }
291 
292 #endif // MBOOT_ENABLE_PACKING
293