diff options
author | Amaury Pouly <amaury.pouly@gmail.com> | 2020-08-10 23:09:46 +0200 |
---|---|---|
committer | Amaury Pouly <amaury.pouly@gmail.com> | 2020-10-11 13:08:03 +0200 |
commit | 8ce60c54f762b2f1392bee586dc8292170c87696 (patch) | |
tree | d979e6ebe4a54b07c57a9995cf2973118504e799 | |
parent | 7cba59910669062b320eb57d831f3a9c50efb364 (diff) | |
download | rockbox-8ce60c54f762b2f1392bee586dc8292170c87696.tar.gz rockbox-8ce60c54f762b2f1392bee586dc8292170c87696.zip |
nwztools/upgtool: add support for MD5
When compressing, it is possible to tell the tool to add an entry to the MD5
file (index 1), it is still necessary to give an empty file for that index.
To do so, pass the option "-z idx,name" insteas of "-z idx". This will create
an entry of the form "size md5 name". For instance "-z 6,system.img".
When decompressing, if one passes "-z idx,name" instead of "-z idx", the tool
will decompress and check against the value in the MD5 file.
Change-Id: Ifb945f6121644ae9105265d2d83ce6067301c5b2
-rw-r--r-- | utils/nwztools/upgtools/mg.cpp | 18 | ||||
-rw-r--r-- | utils/nwztools/upgtools/mg.h | 4 | ||||
-rw-r--r-- | utils/nwztools/upgtools/upgtool.c | 141 |
3 files changed, 159 insertions, 4 deletions
diff --git a/utils/nwztools/upgtools/mg.cpp b/utils/nwztools/upgtools/mg.cpp index df2dbbfd4f..a4d06cd77f 100644 --- a/utils/nwztools/upgtools/mg.cpp +++ b/utils/nwztools/upgtools/mg.cpp | |||
@@ -215,6 +215,24 @@ void MD5_CalculateDigest(void *digest, const void *input, size_t length) | |||
215 | MD5().CalculateDigest((byte *)digest, (const byte *)input, length); | 215 | MD5().CalculateDigest((byte *)digest, (const byte *)input, length); |
216 | } | 216 | } |
217 | 217 | ||
218 | void *md5_start() | ||
219 | { | ||
220 | return new MD5; | ||
221 | } | ||
222 | |||
223 | void md5_update(void *md5_obj, const void *input, size_t length) | ||
224 | { | ||
225 | MD5 *md5 = reinterpret_cast<MD5 *>(md5_obj); | ||
226 | md5->Update(reinterpret_cast<const uint8_t *>(input), length); | ||
227 | } | ||
228 | |||
229 | void md5_final(void *md5_obj, void *digest) | ||
230 | { | ||
231 | MD5 *md5 = reinterpret_cast<MD5 *>(md5_obj); | ||
232 | md5->Final(reinterpret_cast<uint8_t *>(digest)); | ||
233 | delete md5; | ||
234 | } | ||
235 | |||
218 | void mg_decrypt_fw(void *in, int size, void *out, uint8_t *key) | 236 | void mg_decrypt_fw(void *in, int size, void *out, uint8_t *key) |
219 | { | 237 | { |
220 | ECB_Mode< DES >::Decryption dec; | 238 | ECB_Mode< DES >::Decryption dec; |
diff --git a/utils/nwztools/upgtools/mg.h b/utils/nwztools/upgtools/mg.h index 3f7de2cd9b..a2de5952fd 100644 --- a/utils/nwztools/upgtools/mg.h +++ b/utils/nwztools/upgtools/mg.h | |||
@@ -30,6 +30,10 @@ extern "C" { | |||
30 | 30 | ||
31 | /* Compute the MD5 digest of a buffer */ | 31 | /* Compute the MD5 digest of a buffer */ |
32 | void MD5_CalculateDigest(void *digest, const void *input, size_t length); | 32 | void MD5_CalculateDigest(void *digest, const void *input, size_t length); |
33 | /* Compute MD5 in more than one step */ | ||
34 | void *md5_start(); /* return an opaque pointer */ | ||
35 | void md5_update(void *md5_obj, const void *input, size_t length); | ||
36 | void md5_final(void *md5_obj, void *digest); /* destroys the MD5 object */ | ||
33 | 37 | ||
34 | /* size must be a multiple of 8, this function is thread-safe */ | 38 | /* size must be a multiple of 8, this function is thread-safe */ |
35 | void mg_decrypt_fw(void *in, int size, void *out, uint8_t key[8]); | 39 | void mg_decrypt_fw(void *in, int size, void *out, uint8_t key[8]); |
diff --git a/utils/nwztools/upgtools/upgtool.c b/utils/nwztools/upgtools/upgtool.c index 8a9194ebe4..03d7d706d7 100644 --- a/utils/nwztools/upgtools/upgtool.c +++ b/utils/nwztools/upgtools/upgtool.c | |||
@@ -49,6 +49,7 @@ static char *g_sig = NULL; | |||
49 | static int g_nr_threads = 1; | 49 | static int g_nr_threads = 1; |
50 | #define MAX_NR_FILES 32 | 50 | #define MAX_NR_FILES 32 |
51 | bool g_compress[MAX_NR_FILES] = {false}; | 51 | bool g_compress[MAX_NR_FILES] = {false}; |
52 | const char *g_md5name[MAX_NR_FILES] = {NULL}; | ||
52 | 53 | ||
53 | enum keysig_search_method_t g_keysig_search = KEYSIG_SEARCH_NONE; | 54 | enum keysig_search_method_t g_keysig_search = KEYSIG_SEARCH_NONE; |
54 | 55 | ||
@@ -148,6 +149,93 @@ static int get_key_and_sig(bool is_extract, void *buf) | |||
148 | return 0; | 149 | return 0; |
149 | } | 150 | } |
150 | 151 | ||
152 | static unsigned xdigit2val(char c) | ||
153 | { | ||
154 | if('0' <= c && c <= '9') | ||
155 | return c - '0'; | ||
156 | if('a' <= c && c <= 'f') | ||
157 | return c - 'a' + 10; | ||
158 | if('A' <= c && c <= 'F') | ||
159 | return c - 'A' + 10; | ||
160 | return 0; | ||
161 | } | ||
162 | |||
163 | static bool find_md5_entry(struct upg_file_t *file, const char *name, size_t *out_size, uint8_t *md5) | ||
164 | { | ||
165 | char *content = file->files[1].data; | ||
166 | size_t size = file->files[1].size; | ||
167 | /* we expect the file to have a terminating zero because it is padded with zeroes, if not, add one */ | ||
168 | if(content[size - 1] != 0) | ||
169 | { | ||
170 | content = file->files[1].data = realloc(content, size + 1); | ||
171 | content[size] = 0; | ||
172 | size++; | ||
173 | } | ||
174 | /* now we can parse safely by stopping t the first 0 */ | ||
175 | size_t pos = 0; | ||
176 | while(true) | ||
177 | { | ||
178 | /* format of each line: filesize md5 name */ | ||
179 | char *end; | ||
180 | if(content[pos] == 0) | ||
181 | break; /* stop on zero */ | ||
182 | if(!isdigit(content[pos])) | ||
183 | goto Lskipline; | ||
184 | /* parse size */ | ||
185 | *out_size = strtoul(content + pos, &end, 0); | ||
186 | pos = end - content; | ||
187 | while(content[pos] == ' ') | ||
188 | pos++; | ||
189 | /* parse md5 */ | ||
190 | for(int i = 0; i < NWZ_MD5_SIZE; i++) | ||
191 | { | ||
192 | if(!isxdigit(content[pos])) | ||
193 | goto Lskipline; | ||
194 | if(!isxdigit(content[pos + 1])) | ||
195 | goto Lskipline; | ||
196 | md5[i] = xdigit2val(content[pos]) << 4 | xdigit2val(content[pos + 1]); | ||
197 | pos += 2; | ||
198 | } | ||
199 | /* parse name: this is a stupid comparison, no trimming */ | ||
200 | while(content[pos] == ' ') | ||
201 | pos++; | ||
202 | size_t name_begin = pos; | ||
203 | while(content[pos] != 0 && content[pos] != '\n') | ||
204 | pos++; | ||
205 | if(strlen(name) == pos - name_begin && !memcmp(content + name_begin, name, pos - name_begin)) | ||
206 | return true; | ||
207 | /* fallthrough: eat end of line */ | ||
208 | Lskipline: | ||
209 | while(content[pos] != 0 && content[pos] != '\n') | ||
210 | pos++; | ||
211 | if(content[pos] == '\n') | ||
212 | pos++; | ||
213 | } | ||
214 | return false; | ||
215 | } | ||
216 | |||
217 | static void compare_md5(struct upg_file_t *file, int idx, size_t filesize, uint8_t *md5) | ||
218 | { | ||
219 | if(g_md5name[idx] == NULL) | ||
220 | return; | ||
221 | size_t expected_size; | ||
222 | uint8_t expected_md5[NWZ_MD5_SIZE * 2]; | ||
223 | bool found = find_md5_entry(file, g_md5name[idx], &expected_size, expected_md5); | ||
224 | cprintf(BLUE, "File %d\n", idx); | ||
225 | cprintf_field(" Name: ", "%s ", g_md5name[idx]); | ||
226 | cprintf(RED, found ? "Found" : " Not found"); | ||
227 | printf("\n"); | ||
228 | cprintf_field(" Size: ", "%lu", filesize); | ||
229 | cprintf(RED, " %s", !found ? "Cannot check" : filesize == expected_size ? "Ok" : "Mismatch"); | ||
230 | printf("\n"); | ||
231 | cprintf_field(" MD5:", " "); | ||
232 | for(int i = 0; i < NWZ_MD5_SIZE; i++) | ||
233 | printf("%02x", md5[i]); | ||
234 | bool ok_md5 = !memcmp(md5, expected_md5, NWZ_MD5_SIZE); | ||
235 | cprintf(RED, " %s", !found ? "Cannot check" : ok_md5 ? "Ok" : "Mismatch"); | ||
236 | printf("\n"); | ||
237 | } | ||
238 | |||
151 | static int do_upg(void *buf, long size) | 239 | static int do_upg(void *buf, long size) |
152 | { | 240 | { |
153 | int ret = get_key_and_sig(true, buf); | 241 | int ret = get_key_and_sig(true, buf); |
@@ -171,8 +259,11 @@ static int do_upg(void *buf, long size) | |||
171 | continue; | 259 | continue; |
172 | } | 260 | } |
173 | free(str); | 261 | free(str); |
262 | /* we will compute the MD5 during writing/decompress */ | ||
174 | if(g_compress[i]) | 263 | if(g_compress[i]) |
175 | { | 264 | { |
265 | void *md5_obj = md5_start(); | ||
266 | uint8_t md5[NWZ_MD5_SIZE]; | ||
176 | void *buf = file->files[i].data; | 267 | void *buf = file->files[i].data; |
177 | int size = file->files[i].size; | 268 | int size = file->files[i].size; |
178 | int pos = 0; | 269 | int pos = 0; |
@@ -182,6 +273,7 @@ static int do_upg(void *buf, long size) | |||
182 | void *chunk = malloc(max_chunk_size); | 273 | void *chunk = malloc(max_chunk_size); |
183 | if(g_debug) | 274 | if(g_debug) |
184 | cprintf(GREY, "decompressing file %d with chunk size %d...\n", i, max_chunk_size); | 275 | cprintf(GREY, "decompressing file %d with chunk size %d...\n", i, max_chunk_size); |
276 | size_t total_size = 0; | ||
185 | while(pos + 4 <= size) | 277 | while(pos + 4 <= size) |
186 | { | 278 | { |
187 | int compressed_chunk_size = *(uint32_t *)(buf + pos); | 279 | int compressed_chunk_size = *(uint32_t *)(buf + pos); |
@@ -206,11 +298,15 @@ static int do_upg(void *buf, long size) | |||
206 | break; | 298 | break; |
207 | } | 299 | } |
208 | pos += 4 + compressed_chunk_size; | 300 | pos += 4 + compressed_chunk_size; |
301 | md5_update(md5_obj, chunk, chunk_size); | ||
209 | fwrite(chunk, 1, chunk_size, f); | 302 | fwrite(chunk, 1, chunk_size, f); |
303 | total_size += chunk_size; | ||
210 | } | 304 | } |
211 | free(chunk); | 305 | free(chunk); |
212 | if(g_debug) | 306 | if(g_debug) |
213 | cprintf(GREY, "done."); | 307 | cprintf(GREY, "done."); |
308 | md5_final(md5_obj, md5); | ||
309 | compare_md5(file, i, total_size, md5); | ||
214 | } | 310 | } |
215 | else | 311 | else |
216 | { | 312 | { |
@@ -297,6 +393,9 @@ static int create_upg(int argc, char **argv) | |||
297 | struct upg_file_t *upg = upg_new(); | 393 | struct upg_file_t *upg = upg_new(); |
298 | int nr_files = argc - 1; | 394 | int nr_files = argc - 1; |
299 | 395 | ||
396 | char *md5_prepend = malloc(1); | ||
397 | md5_prepend[0] = 0; | ||
398 | size_t md5_prepend_sz = 0; | ||
300 | for(int i = 0; i < nr_files; i++) | 399 | for(int i = 0; i < nr_files; i++) |
301 | { | 400 | { |
302 | FILE *f = fopen(argv[1 + i], "rb"); | 401 | FILE *f = fopen(argv[1 + i], "rb"); |
@@ -316,9 +415,24 @@ static int create_upg(int argc, char **argv) | |||
316 | return 1; | 415 | return 1; |
317 | } | 416 | } |
318 | fclose(f); | 417 | fclose(f); |
418 | /* add the MD5 of files *before* any kind of treatment. Does nothing if not resquested, | ||
419 | * which is important on v1 where the second file might not be the md5 file */ | ||
420 | if(g_md5name[i]) | ||
421 | { | ||
422 | uint8_t md5[NWZ_MD5_SIZE]; | ||
423 | MD5_CalculateDigest(md5, buf, size); | ||
424 | size_t inc_sz = 16 + NWZ_MD5_SIZE * 2 + strlen(g_md5name[i]); | ||
425 | md5_prepend = realloc(md5_prepend, md5_prepend_sz + inc_sz); | ||
426 | md5_prepend_sz += sprintf(md5_prepend + md5_prepend_sz, "%lu ", size); | ||
427 | for(int i = 0; i < NWZ_MD5_SIZE; i++) | ||
428 | md5_prepend_sz += sprintf(md5_prepend + md5_prepend_sz, "%02x", md5[i]); | ||
429 | md5_prepend_sz += sprintf(md5_prepend + md5_prepend_sz, " %s\n", g_md5name[i]); | ||
430 | } | ||
319 | if(g_compress[i]) | 431 | if(g_compress[i]) |
320 | { | 432 | { |
321 | int out_buf_max_sz = size; /* we expect that the output will not take more space */ | 433 | /* in the worst case, maybe the compressor will double the size, also we always need |
434 | * at least 4 bytes to write the size of a block */ | ||
435 | int out_buf_max_sz = 4 + 2 * size; | ||
322 | void *out_buf = malloc(out_buf_max_sz); | 436 | void *out_buf = malloc(out_buf_max_sz); |
323 | int out_buf_pos = 0, in_buf_pos = 0; | 437 | int out_buf_pos = 0, in_buf_pos = 0; |
324 | int max_chunk_size = 4096; /* the OF encoder/decoder expect that */ | 438 | int max_chunk_size = 4096; /* the OF encoder/decoder expect that */ |
@@ -329,7 +443,7 @@ static int create_upg(int argc, char **argv) | |||
329 | int zres = compress(out_buf + out_buf_pos + 4, &dest_len, buf + in_buf_pos, chunk_size); | 443 | int zres = compress(out_buf + out_buf_pos + 4, &dest_len, buf + in_buf_pos, chunk_size); |
330 | if(zres == Z_BUF_ERROR) | 444 | if(zres == Z_BUF_ERROR) |
331 | { | 445 | { |
332 | cprintf(RED, "the compresser produced a file greater than its input, I can't handle that\n"); | 446 | cprintf(RED, "the compresser produced a file much greater than its input, I can't handle that\n"); |
333 | return 1; | 447 | return 1; |
334 | } | 448 | } |
335 | else if(zres != Z_OK) | 449 | else if(zres != Z_OK) |
@@ -352,6 +466,11 @@ static int create_upg(int argc, char **argv) | |||
352 | upg_append(upg, buf, size); | 466 | upg_append(upg, buf, size); |
353 | } | 467 | } |
354 | 468 | ||
469 | /* modify md5 file (if any) */ | ||
470 | upg->files[1].data = realloc(upg->files[1].data, upg->files[1].size + md5_prepend_sz); | ||
471 | memmove(upg->files[1].data + md5_prepend_sz, upg->files[1].data, upg->files[1].size); | ||
472 | memcpy(upg->files[1].data, md5_prepend, md5_prepend_sz); | ||
473 | |||
355 | size_t size = 0; | 474 | size_t size = 0; |
356 | void *buf = upg_write_memory(upg, g_key, g_sig, &size, NULL, generic_std_printf); | 475 | void *buf = upg_write_memory(upg, g_key, g_sig, &size, NULL, generic_std_printf); |
357 | upg_free(upg); | 476 | upg_free(upg); |
@@ -398,7 +517,11 @@ static void usage(void) | |||
398 | printf(" -e/--extract\t\tExtract a UPG archive\n"); | 517 | printf(" -e/--extract\t\tExtract a UPG archive\n"); |
399 | printf(" -c/--create\t\tCreate a UPG archive\n"); | 518 | printf(" -c/--create\t\tCreate a UPG archive\n"); |
400 | printf(" -z/--compress <idx>\t\t(De)compress file <idx> (starts at 0)\n"); | 519 | printf(" -z/--compress <idx>\t\t(De)compress file <idx> (starts at 0)\n"); |
401 | printf("keysig search method:\n"); | 520 | printf(" -z/--compress <idx>,<md5name>\t\t(De)compress file <idx> and add it to the MD5 file\n"); |
521 | printf("When using -z <idx>,<md5name>, the file file size and MD5 prior to compression will\n"); | ||
522 | printf("be prepended to the contect of the second file (index 1). The name can be arbitrary and\n"); | ||
523 | printf("has meaning only the script, e.g. \"-z 6,system.img\".\n"); | ||
524 | printf("Keysig search method:\n"); | ||
402 | for(int i = KEYSIG_SEARCH_FIRST; i < KEYSIG_SEARCH_LAST; i++) | 525 | for(int i = KEYSIG_SEARCH_FIRST; i < KEYSIG_SEARCH_LAST; i++) |
403 | printf(" %-10s\t%s\n", keysig_search_desc[i].name, keysig_search_desc[i].comment); | 526 | printf(" %-10s\t%s\n", keysig_search_desc[i].name, keysig_search_desc[i].comment); |
404 | exit(1); | 527 | exit(1); |
@@ -494,13 +617,23 @@ int main(int argc, char **argv) | |||
494 | break; | 617 | break; |
495 | case 'z': | 618 | case 'z': |
496 | { | 619 | { |
497 | int idx = strtol(optarg, NULL, 0); | 620 | char *end; |
621 | int idx = strtol(optarg, &end, 0); | ||
498 | if(idx < 0 || idx >= MAX_NR_FILES) | 622 | if(idx < 0 || idx >= MAX_NR_FILES) |
499 | { | 623 | { |
500 | cprintf(GREY, "Invalid file index\n"); | 624 | cprintf(GREY, "Invalid file index\n"); |
501 | return 1; | 625 | return 1; |
502 | } | 626 | } |
503 | g_compress[idx] = true; | 627 | g_compress[idx] = true; |
628 | /* distinguish betwen -z <idx> and -z <idx>,<md5name> */ | ||
629 | if(*end == 0) | ||
630 | break; | ||
631 | if(*end != ',') | ||
632 | { | ||
633 | cprintf(GREY, "Invalid file index\n"); | ||
634 | return 1; | ||
635 | } | ||
636 | g_md5name[idx] = end + 1; | ||
504 | break; | 637 | break; |
505 | } | 638 | } |
506 | default: | 639 | default: |