diff options
author | Amaury Pouly <amaury.pouly@gmail.com> | 2017-01-04 16:36:27 +0100 |
---|---|---|
committer | Amaury Pouly <amaury.pouly@gmail.com> | 2017-01-04 17:04:38 +0100 |
commit | 92ecbd5fb8a7c8e939b1b4dde82cc6c9ba9d41af (patch) | |
tree | 5e187e031ab8648e2c2c58502f167abc72ec606c | |
parent | 5cfd4a5b8e551c23600c93838180487e91698814 (diff) | |
download | rockbox-92ecbd5fb8a7c8e939b1b4dde82cc6c9ba9d41af.tar.gz rockbox-92ecbd5fb8a7c8e939b1b4dde82cc6c9ba9d41af.zip |
nwztools/upg: move upg handling to its own file, completely rework kas handling
This was a huge mess, the new is much cleaner hopefully.
Change-Id: I43663d021dc8bc31662d3923e1c3da22d987ebf9
-rw-r--r-- | utils/nwztools/upgtools/Makefile | 2 | ||||
-rw-r--r-- | utils/nwztools/upgtools/fwp.c | 8 | ||||
-rw-r--r-- | utils/nwztools/upgtools/misc.c | 31 | ||||
-rw-r--r-- | utils/nwztools/upgtools/misc.h | 10 | ||||
-rw-r--r-- | utils/nwztools/upgtools/upg.c | 260 | ||||
-rw-r--r-- | utils/nwztools/upgtools/upgtool.c | 436 |
6 files changed, 361 insertions, 386 deletions
diff --git a/utils/nwztools/upgtools/Makefile b/utils/nwztools/upgtools/Makefile index 35d5bfabff..1030b1b849 100644 --- a/utils/nwztools/upgtools/Makefile +++ b/utils/nwztools/upgtools/Makefile | |||
@@ -16,7 +16,7 @@ all: $(BINS) | |||
16 | %.o: %.cpp | 16 | %.o: %.cpp |
17 | $(CXX) $(CXXFLAGS) -c -o $@ $< | 17 | $(CXX) $(CXXFLAGS) -c -o $@ $< |
18 | 18 | ||
19 | upgtool: upgtool.o misc.o fwp.o mg.o keysig_search.o | 19 | upgtool: upgtool.o upg.o misc.o fwp.o mg.o keysig_search.o |
20 | $(LD) -o $@ $^ $(LDFLAGS) | 20 | $(LD) -o $@ $^ $(LDFLAGS) |
21 | 21 | ||
22 | clean: | 22 | clean: |
diff --git a/utils/nwztools/upgtools/fwp.c b/utils/nwztools/upgtools/fwp.c index c300f42f34..34c55f6e5a 100644 --- a/utils/nwztools/upgtools/fwp.c +++ b/utils/nwztools/upgtools/fwp.c | |||
@@ -19,6 +19,7 @@ | |||
19 | * | 19 | * |
20 | ****************************************************************************/ | 20 | ****************************************************************************/ |
21 | #include <stdio.h> | 21 | #include <stdio.h> |
22 | #include <stdlib.h> | ||
22 | #include "fwp.h" | 23 | #include "fwp.h" |
23 | #include "misc.h" | 24 | #include "misc.h" |
24 | #include "mg.h" | 25 | #include "mg.h" |
@@ -53,9 +54,6 @@ int fwp_crypt(void *buf, int size, int mode) | |||
53 | size -= NWZ_KEY_SIZE; | 54 | size -= NWZ_KEY_SIZE; |
54 | } | 55 | } |
55 | if(size != 0) | 56 | if(size != 0) |
56 | { | 57 | abort(); |
57 | cprintf(GREY, "Cannot fwp_crypt non-multiple of 8!\n"); | ||
58 | return -1; | ||
59 | } | ||
60 | return 0; | 58 | return 0; |
61 | } \ No newline at end of file | 59 | } |
diff --git a/utils/nwztools/upgtools/misc.c b/utils/nwztools/upgtools/misc.c index 108235e7fd..ed1ba6c788 100644 --- a/utils/nwztools/upgtools/misc.c +++ b/utils/nwztools/upgtools/misc.c | |||
@@ -22,25 +22,19 @@ | |||
22 | #include <stdio.h> | 22 | #include <stdio.h> |
23 | #include <time.h> | 23 | #include <time.h> |
24 | #include <ctype.h> | 24 | #include <ctype.h> |
25 | #include <stdarg.h> | ||
25 | #include "misc.h" | 26 | #include "misc.h" |
26 | 27 | ||
27 | char OFF[] = { 0x1b, 0x5b, 0x31, 0x3b, '0', '0', 0x6d, '\0' }; | 28 | const char OFF[] = { 0x1b, 0x5b, 0x31, 0x3b, '0', '0', 0x6d, '\0' }; |
28 | 29 | ||
29 | char GREY[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '0', 0x6d, '\0' }; | 30 | const char GREY[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '0', 0x6d, '\0' }; |
30 | char RED[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '1', 0x6d, '\0' }; | 31 | const char RED[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '1', 0x6d, '\0' }; |
31 | char GREEN[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '2', 0x6d, '\0' }; | 32 | const char GREEN[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '2', 0x6d, '\0' }; |
32 | char YELLOW[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '3', 0x6d, '\0' }; | 33 | const char YELLOW[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '3', 0x6d, '\0' }; |
33 | char BLUE[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '4', 0x6d, '\0' }; | 34 | const char BLUE[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '4', 0x6d, '\0' }; |
34 | 35 | ||
35 | static bool g_color_enable = true; | 36 | static bool g_color_enable = true; |
36 | 37 | ||
37 | void *xmalloc(size_t s) | ||
38 | { | ||
39 | void * r = malloc(s); | ||
40 | if(!r) bugp("malloc"); | ||
41 | return r; | ||
42 | } | ||
43 | |||
44 | void enable_color(bool enable) | 38 | void enable_color(bool enable) |
45 | { | 39 | { |
46 | g_color_enable = enable; | 40 | g_color_enable = enable; |
@@ -51,3 +45,14 @@ void color(color_t c) | |||
51 | if(g_color_enable) | 45 | if(g_color_enable) |
52 | printf("%s", (char *)c); | 46 | printf("%s", (char *)c); |
53 | } | 47 | } |
48 | |||
49 | void generic_std_printf(void *u, bool err, color_t c, const char *f, ...) | ||
50 | { | ||
51 | (void)u; | ||
52 | (void)err; | ||
53 | va_list args; | ||
54 | va_start(args, f); | ||
55 | color(c); | ||
56 | vprintf(f, args); | ||
57 | va_end(args); | ||
58 | } | ||
diff --git a/utils/nwztools/upgtools/misc.h b/utils/nwztools/upgtools/misc.h index 96666a2eff..e493cc2019 100644 --- a/utils/nwztools/upgtools/misc.h +++ b/utils/nwztools/upgtools/misc.h | |||
@@ -27,20 +27,16 @@ | |||
27 | #define _STR(a) #a | 27 | #define _STR(a) #a |
28 | #define STR(a) _STR(a) | 28 | #define STR(a) _STR(a) |
29 | 29 | ||
30 | #define bug(...) do { fprintf(stderr,"["__FILE__":"STR(__LINE__)"]ERROR: "__VA_ARGS__); exit(1); } while(0) | ||
31 | #define bugp(...) do { fprintf(stderr, __VA_ARGS__); perror(" "); exit(1); } while(0) | ||
32 | |||
33 | #define ROUND_UP(val, round) ((((val) + (round) - 1) / (round)) * (round)) | 30 | #define ROUND_UP(val, round) ((((val) + (round) - 1) / (round)) * (round)) |
34 | 31 | ||
35 | typedef char color_t[]; | 32 | typedef const char color_t[]; |
36 | 33 | ||
37 | extern color_t OFF, GREY, RED, GREEN, YELLOW, BLUE; | 34 | extern color_t OFF, GREY, RED, GREEN, YELLOW, BLUE; |
38 | void *xmalloc(size_t s); | ||
39 | void color(color_t c); | 35 | void color(color_t c); |
40 | void enable_color(bool enable); | 36 | void enable_color(bool enable); |
41 | 37 | ||
42 | #define cprintf(col, ...) do {color(col); printf(__VA_ARGS__); }while(0) | 38 | typedef void (*generic_printf_t)(void *u, bool err, color_t c, const char *f, ...); |
43 | 39 | ||
44 | #define cprintf_field(str1, ...) do{ cprintf(GREEN, str1); cprintf(YELLOW, __VA_ARGS__); }while(0) | 40 | void generic_std_printf(void *u, bool err, color_t c, const char *f, ...); |
45 | 41 | ||
46 | #endif /* __MISC_H__ */ | 42 | #endif /* __MISC_H__ */ |
diff --git a/utils/nwztools/upgtools/upg.c b/utils/nwztools/upgtools/upg.c new file mode 100644 index 0000000000..44d3eca789 --- /dev/null +++ b/utils/nwztools/upgtools/upg.c | |||
@@ -0,0 +1,260 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2016 Amaury Pouly | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include "upg.h" | ||
22 | #include <stdlib.h> | ||
23 | #include <string.h> | ||
24 | #include <ctype.h> | ||
25 | #include <openssl/md5.h> | ||
26 | |||
27 | struct nwz_model_t g_model_list[] = | ||
28 | { | ||
29 | { "nwz-e450", true, "8a01b624bfbfde4a1662a1772220e3c5" }, | ||
30 | { "nwz-e460", true, "89d813f8f966efdebd9c9e0ea98156d2" }, | ||
31 | { "nwz-a860", true, "a7c4af6c28b8900a783f307c1ba538c5" }, | ||
32 | { "nwz-a850", true, "a2efb9168616c2e84d78291295c1aa5d" }, | ||
33 | { "nwz-e470", true, "e4144baaa2707913f17b5634034262c4" }, | ||
34 | { "nwz-e580", true, "6e25f79812eca7ceed04819d833e80af" }, | ||
35 | /* The following keys were obtained by brute forcing firmware upgrades, | ||
36 | * someone with a device needs to confirm that they work */ | ||
37 | { "nw-a820", false, "0c9869c268e0eaa6d1ba62daab09cebc" }, | ||
38 | { "nwz-a10", false, "a4605e0628c9c3baeb5142ce9cb834d6" }, | ||
39 | { "nwz-a20", false, "e9d7185e5ac183bf26e9a5b66f983c0b" }, | ||
40 | { "nwz-zx100", false, "2c0bf029804f73e073154388743f84d2" }, | ||
41 | { 0 } | ||
42 | }; | ||
43 | |||
44 | static int digit_value(char c) | ||
45 | { | ||
46 | if(c >= '0' && c <= '9') return c - '0'; | ||
47 | if(c >= 'a' && c <= 'f') return c - 'a' + 10; | ||
48 | if(c >= 'A' && c <= 'F') return c - 'A' + 10; | ||
49 | return -1; | ||
50 | } | ||
51 | |||
52 | static char hex_digit(unsigned v) | ||
53 | { | ||
54 | return (v < 10) ? v + '0' : (v < 16) ? v - 10 + 'a' : 'x'; | ||
55 | } | ||
56 | |||
57 | int decrypt_keysig(const char kas[NWZ_KAS_SIZE], char key[NWZ_KEY_SIZE], | ||
58 | char sig[NWZ_SIG_SIZE]) | ||
59 | { | ||
60 | uint8_t src[NWZ_KAS_SIZE / 2]; | ||
61 | for(int index = 0; index < NWZ_KAS_SIZE / 2; index++) | ||
62 | { | ||
63 | int a = digit_value(kas[index * 2]); | ||
64 | int b = digit_value(kas[index * 2 + 1]); | ||
65 | if(a < 0 || b < 0) | ||
66 | return -1; | ||
67 | src[index] = a << 4 | b; | ||
68 | } | ||
69 | fwp_setkey("ed295076"); | ||
70 | fwp_crypt(src, sizeof(src), 1); | ||
71 | memcpy(key, src, NWZ_KEY_SIZE); | ||
72 | memcpy(sig, src + NWZ_KEY_SIZE, NWZ_SIG_SIZE); | ||
73 | return 0; | ||
74 | } | ||
75 | |||
76 | void encrypt_keysig(char kas[NWZ_KEY_SIZE], | ||
77 | const char key[NWZ_SIG_SIZE], const char sig[NWZ_KAS_SIZE]) | ||
78 | { | ||
79 | uint8_t src[NWZ_KAS_SIZE / 2]; | ||
80 | fwp_setkey("ed295076"); | ||
81 | memcpy(src, key, NWZ_KEY_SIZE); | ||
82 | memcpy(src + NWZ_KEY_SIZE, sig, NWZ_SIG_SIZE); | ||
83 | fwp_crypt(src, sizeof(src), 0); | ||
84 | for(int i = 0; i < NWZ_KAS_SIZE / 2; i++) | ||
85 | { | ||
86 | kas[2 * i] = hex_digit((src[i] >> 4) & 0xf); | ||
87 | kas[2 * i + 1] = hex_digit(src[i] & 0xf); | ||
88 | } | ||
89 | } | ||
90 | |||
91 | struct upg_file_t *upg_read_memory(void *buf, size_t size, char key[NWZ_KEY_SIZE], | ||
92 | char *sig, void *u, generic_printf_t printf) | ||
93 | { | ||
94 | #define cprintf(col, ...) printf(u, false, col, __VA_ARGS__) | ||
95 | #define cprintf_field(str1, ...) do{ cprintf(GREEN, str1); cprintf(YELLOW, __VA_ARGS__); }while(0) | ||
96 | #define err_printf(col, ...) printf(u, true, col, __VA_ARGS__) | ||
97 | struct upg_md5_t *md5 = buf; | ||
98 | cprintf(BLUE, "Preliminary\n"); | ||
99 | cprintf(GREEN, " MD5: "); | ||
100 | for(int i = 0; i < MD5_DIGEST_LENGTH; i++) | ||
101 | cprintf(YELLOW, "%02x", md5->md5[i]); | ||
102 | cprintf(OFF, " "); | ||
103 | |||
104 | /* check MD5 */ | ||
105 | uint8_t actual_md5[MD5_DIGEST_LENGTH]; | ||
106 | { | ||
107 | MD5_CTX c; | ||
108 | MD5_Init(&c); | ||
109 | MD5_Update(&c, md5 + 1, size - sizeof(struct upg_header_t)); | ||
110 | MD5_Final(actual_md5, &c); | ||
111 | } | ||
112 | if(memcmp(actual_md5, md5->md5, MD5_DIGEST_LENGTH) != 0) | ||
113 | { | ||
114 | cprintf(RED, "Mismatch\n"); | ||
115 | err_printf(GREY, "MD5 Mismatch\n"); | ||
116 | return NULL; | ||
117 | } | ||
118 | cprintf(RED, "Ok\n"); | ||
119 | |||
120 | struct upg_header_t *hdr = (void *)(md5 + 1); | ||
121 | /* decrypt the whole file at once */ | ||
122 | fwp_read(hdr, size - sizeof(*md5), hdr, (void *)key); | ||
123 | |||
124 | cprintf(BLUE, "Header\n"); | ||
125 | cprintf_field(" Signature:", " "); | ||
126 | for(int i = 0; i < NWZ_SIG_SIZE; i++) | ||
127 | cprintf(YELLOW, "%c", isprint(hdr->sig[i]) ? hdr->sig[i] : '.'); | ||
128 | if(sig) | ||
129 | { | ||
130 | if(memcmp(hdr->sig, sig, NWZ_SIG_SIZE) != 0) | ||
131 | { | ||
132 | cprintf(RED, "Mismatch\n"); | ||
133 | err_printf(GREY, "Signature Mismatch\n"); | ||
134 | return NULL; | ||
135 | } | ||
136 | cprintf(RED, " Ok\n"); | ||
137 | } | ||
138 | else | ||
139 | cprintf(RED, " Can't check\n"); | ||
140 | cprintf_field(" Files: ", "%d\n", hdr->nr_files); | ||
141 | cprintf_field(" Pad: ", "0x%x\n", hdr->pad); | ||
142 | |||
143 | |||
144 | /* Do a first pass to decrypt in-place */ | ||
145 | cprintf(BLUE, "Files\n"); | ||
146 | struct upg_entry_t *entry = (void *)(hdr + 1); | ||
147 | for(unsigned i = 0; i < hdr->nr_files; i++, entry++) | ||
148 | { | ||
149 | cprintf(GREY, " File"); | ||
150 | cprintf(RED, " %d\n", i); | ||
151 | cprintf_field(" Offset: ", "0x%x\n", entry->offset); | ||
152 | cprintf_field(" Size: ", "0x%x\n", entry->size); | ||
153 | } | ||
154 | /* Do a second pass to create the file structure */ | ||
155 | /* create file */ | ||
156 | struct upg_file_t *file = malloc(sizeof(struct upg_file_t)); | ||
157 | memset(file, 0, sizeof(struct upg_file_t)); | ||
158 | file->nr_files = hdr->nr_files; | ||
159 | file->files = malloc(sizeof(struct upg_file_entry_t) * file->nr_files); | ||
160 | |||
161 | entry = (void *)(hdr + 1); | ||
162 | for(unsigned i = 0; i < hdr->nr_files; i++, entry++) | ||
163 | { | ||
164 | memset(&file->files[i], 0, sizeof(struct upg_file_entry_t)); | ||
165 | file->files[i].size = entry->size; | ||
166 | file->files[i].data = malloc(file->files[i].size); | ||
167 | memcpy(file->files[i].data, buf + entry->offset, entry->size); | ||
168 | } | ||
169 | |||
170 | return file; | ||
171 | } | ||
172 | |||
173 | void *upg_write_memory(struct upg_file_t *file, char key[NWZ_KEY_SIZE], | ||
174 | char sig[NWZ_SIG_SIZE], size_t *out_size, void *u, generic_printf_t printf) | ||
175 | { | ||
176 | if(file->nr_files == 0) | ||
177 | { | ||
178 | err_printf(GREY, "A UPG file must have at least one file\n"); | ||
179 | return NULL; | ||
180 | } | ||
181 | /* compute total size and create buffer */ | ||
182 | size_t tot_size = sizeof(struct upg_md5_t) + sizeof(struct upg_header_t) | ||
183 | + file->nr_files * sizeof(struct upg_entry_t); | ||
184 | for(int i = 0; i < file->nr_files; i++) | ||
185 | tot_size += ROUND_UP(file->files[i].size, 8); | ||
186 | /* allocate buffer */ | ||
187 | void *buf = malloc(tot_size); | ||
188 | |||
189 | /* create md5 context, we push data to the context as we create it */ | ||
190 | struct upg_md5_t *md5 = buf; | ||
191 | memset(md5, 0, sizeof(*md5)); | ||
192 | /* create the encrypted signature and header */ | ||
193 | struct upg_header_t *hdr = (void *)(md5 + 1); | ||
194 | memcpy(hdr->sig, sig, NWZ_SIG_SIZE); | ||
195 | hdr->nr_files = file->nr_files; | ||
196 | hdr->pad = 0; | ||
197 | |||
198 | /* create file headers */ | ||
199 | size_t offset = sizeof(*md5) + sizeof(*hdr) + file->nr_files * sizeof(struct upg_entry_t); | ||
200 | struct upg_entry_t *entry = (void *)(hdr + 1); | ||
201 | cprintf(BLUE, "Files\n"); | ||
202 | for(int i = 0; i < file->nr_files; i++) | ||
203 | { | ||
204 | entry[i].offset = offset; | ||
205 | entry[i].size = file->files[i].size; | ||
206 | offset += ROUND_UP(entry[i].size, 8); /* pad each file to a multiple of 8 for encryption */ | ||
207 | |||
208 | cprintf(GREY, " File"); | ||
209 | cprintf(RED, " %d\n", i); | ||
210 | cprintf_field(" Offset: ", "0x%lx\n", entry[i].offset); | ||
211 | cprintf_field(" Size: ", "0x%lx\n", entry[i].size); | ||
212 | } | ||
213 | |||
214 | /* add file data */ | ||
215 | for(int i = 0; i < file->nr_files; i++) | ||
216 | { | ||
217 | /* copy data to buffer, and then encrypt in-place */ | ||
218 | size_t r_size = ROUND_UP(file->files[i].size, 8); | ||
219 | void *data_ptr = (uint8_t *)buf + entry[i].offset; | ||
220 | memset(data_ptr, 0, r_size); /* the padding will be zero 0 */ | ||
221 | memcpy(data_ptr, file->files[i].data, file->files[i].size); | ||
222 | } | ||
223 | /* encrypt everything and hash everything */ | ||
224 | fwp_write(hdr, tot_size - sizeof(*md5), hdr, (void *)key); | ||
225 | /* write final MD5 */ | ||
226 | { | ||
227 | MD5_CTX c; | ||
228 | MD5_Init(&c); | ||
229 | MD5_Update(&c, (void *)hdr, tot_size - sizeof(*md5)); | ||
230 | MD5_Final(md5->md5, &c); | ||
231 | } | ||
232 | *out_size = tot_size; | ||
233 | return buf; | ||
234 | } | ||
235 | |||
236 | struct upg_file_t *upg_new(void) | ||
237 | { | ||
238 | struct upg_file_t *f = malloc(sizeof(struct upg_file_t)); | ||
239 | memset(f, 0, sizeof(struct upg_file_t)); | ||
240 | return f; | ||
241 | } | ||
242 | |||
243 | void upg_append(struct upg_file_t *file, void *data, size_t size) | ||
244 | { | ||
245 | file->files = realloc(file->files, (file->nr_files + 1) * sizeof(struct upg_file_entry_t)); | ||
246 | file->files[file->nr_files].data = data; | ||
247 | file->files[file->nr_files].size = size; | ||
248 | file->nr_files++; | ||
249 | } | ||
250 | |||
251 | void upg_free(struct upg_file_t *file) | ||
252 | { | ||
253 | if(file) | ||
254 | { | ||
255 | for(int i = 0; i < file->nr_files; i++) | ||
256 | free(file->files[i].data); | ||
257 | free(file->files); | ||
258 | } | ||
259 | free(file); | ||
260 | } | ||
diff --git a/utils/nwztools/upgtools/upgtool.c b/utils/nwztools/upgtools/upgtool.c index 833321ec12..0de46a4260 100644 --- a/utils/nwztools/upgtools/upgtool.c +++ b/utils/nwztools/upgtools/upgtool.c | |||
@@ -33,6 +33,7 @@ | |||
33 | #include "crypt.h" | 33 | #include "crypt.h" |
34 | #include "fwp.h" | 34 | #include "fwp.h" |
35 | #include "keysig_search.h" | 35 | #include "keysig_search.h" |
36 | #include "upg.h" | ||
36 | 37 | ||
37 | #ifndef MIN | 38 | #ifndef MIN |
38 | #define MIN(a,b) ((a) < (b) ? (a) : (b)) | 39 | #define MIN(a,b) ((a) < (b) ? (a) : (b)) |
@@ -59,183 +60,11 @@ enum keysig_search_method_t g_keysig_search = KEYSIG_SEARCH_NONE; | |||
59 | { cprintf(RED, str_bad); let_the_force_flow(__LINE__); } \ | 60 | { cprintf(RED, str_bad); let_the_force_flow(__LINE__); } \ |
60 | else { cprintf(RED, str_ok); } | 61 | else { cprintf(RED, str_ok); } |
61 | 62 | ||
62 | static void usage(void); | 63 | #define cprintf(col, ...) do {color(col); printf(__VA_ARGS__); }while(0) |
63 | |||
64 | #define HAS_KAS (1 << 0) | ||
65 | #define HAS_KEY (1 << 1) | ||
66 | #define HAS_SIG (1 << 2) | ||
67 | #define CONFIRMED (1 << 3) | ||
68 | |||
69 | struct nwz_model_t | ||
70 | { | ||
71 | const char *model; | ||
72 | unsigned flags; | ||
73 | char *kas; | ||
74 | char *key; | ||
75 | char *sig; | ||
76 | }; | ||
77 | |||
78 | /** Firmware format | ||
79 | * | ||
80 | * The firmware starts with the MD5 hash of the entire file (except the MD5 hash | ||
81 | * itself of course). This is used to check that the file was not corrupted. | ||
82 | * The remaining of the file is encrypted (using DES) with the model key. The | ||
83 | * encrypted part starts with a header containing the model signature and the | ||
84 | * number of files. Since the header is encrypted, decrypting the header with | ||
85 | * the key and finding the right signature serves to authenticate the firmware. | ||
86 | * The header is followed by N entries (where N is the number of files) giving | ||
87 | * the offset, within the file, and size of each file. Note that the files in | ||
88 | * the firmware have no name. */ | ||
89 | |||
90 | struct upg_md5_t | ||
91 | { | ||
92 | uint8_t md5[16]; | ||
93 | }__attribute__((packed)); | ||
94 | |||
95 | struct upg_header_t | ||
96 | { | ||
97 | uint8_t sig[NWZ_SIG_SIZE]; | ||
98 | uint32_t nr_files; | ||
99 | uint32_t pad; // make sure structure size is a multiple of 8 | ||
100 | } __attribute__((packed)); | ||
101 | |||
102 | struct upg_entry_t | ||
103 | { | ||
104 | uint32_t offset; | ||
105 | uint32_t size; | ||
106 | } __attribute__((packed)); | ||
107 | |||
108 | /** KAS / Key / Signature | ||
109 | * | ||
110 | * Since this is all very confusing, we need some terminology and notations: | ||
111 | * - [X, Y, Z] is a sequence of bytes, for example: | ||
112 | * [8, 0x89, 42] | ||
113 | * is a sequence of three bytes. | ||
114 | * - "abcdef" is a string: it is a sequences of bytes where each byte happens to | ||
115 | * be the ASCII encoding of a letter. So for example: | ||
116 | * "abc" = [97, 98, 99] | ||
117 | * because 'a' has ASCII encoding 97 and so one | ||
118 | * - HexString(Seq) refers to the string where each byte of the original sequence | ||
119 | * is represented in hexadecimal by two ASCII characters. For example: | ||
120 | * HexString([8, 0x89, 42]) = "08892a" | ||
121 | * because 8 = 0x08 so it represented by "08" and 42 = 0x2a. Note that the length | ||
122 | * of HexString(Seq) is always exactly twice the length of Seq. | ||
123 | * - DES(Seq,Pass) is the result of encrypting Seq with Pass using the DES cipher. | ||
124 | * Seq must be a sequence of 8 bytes (known as a block) and Pass must be a | ||
125 | * sequence of 8 bytes. The result is also a 8-byte sequence. | ||
126 | * - ECB_DES([Block0, Block1, ..., BlockN], Pass) | ||
127 | * = [DES(Block0,Pass), DES(Block1,Pass), ..., DES(BlockN,Pass)] | ||
128 | * where Blocki is a block (8 byte). | ||
129 | * | ||
130 | * | ||
131 | * A firmware upgrade file is always encrypted using a Key. To authenticate it, | ||
132 | * the upgrade file (before encryption) contains a Sig(nature). The pair (Key,Sig) | ||
133 | * is refered to as KeySig and is specific to each series. For example all | ||
134 | * NWZ-E46x use the same KeySig but the NWZ-E46x and NWZ-A86x use different KeySig. | ||
135 | * In the details, a Key is a sequence of 8 bytes and a Sig is also a sequence | ||
136 | * of 8 bytes. A KeySig is a simply the concatenation of the Key followed by | ||
137 | * the Sig, so it is a sequence of 16 bytes. Probably in an attempt to obfuscate | ||
138 | * things a little further, Sony never provides the KeySig directly but instead | ||
139 | * encrypts it using DES in ECB mode using a hardcoded password and provides | ||
140 | * the hexadecimal string of the result, known as the KAS, which is thus a string | ||
141 | * of 32 ASCII characters. | ||
142 | * Note that since DES works on blocks of 8 bytes and ECB encrypts blocks | ||
143 | * independently, it is the same to encrypt the KeySig as once or encrypt the Key | ||
144 | * and Sig separately. | ||
145 | * | ||
146 | * To summarize: | ||
147 | * Key = [K0, K1, K2, ..., K7] (8 bytes) (model specific) | ||
148 | * Sig = [S0, S1, S2, ..., S7] (8 bytes) (model specific) | ||
149 | * KeySig = [Key, Sig] = [K0, ... K7, S0, ..., S7] (16 bytes) | ||
150 | * FwpPass = "ed295076" (8 bytes) (never changes) | ||
151 | * EncKeySig = ECB_DES(KeySig, FwpPass) = [DES(Key, FwpPass), DES(Sig, FwpPass)] | ||
152 | * KAS = HexString(EncKeySig) (32 characters) | ||
153 | * | ||
154 | * In theory, the Key and Sig can be any 8-byte sequence. In practice, they always | ||
155 | * are strings, probably to make it easier to write them down. In many cases, the | ||
156 | * Key and Sig are even the hexadecimal string of 4-byte sequences but it is | ||
157 | * unclear if this is the result of pure luck, confused engineers, lazyness on | ||
158 | * Sony's part or by design. The following code assumes that Key and Sig are | ||
159 | * strings (though it could easily be fixed to work with anything if this is | ||
160 | * really needed). | ||
161 | * | ||
162 | * | ||
163 | * Here is a real example, from the NWZ-E46x Series: | ||
164 | * Key = "6173819e" (note that this is a string and even a hex string in this case) | ||
165 | * Sig = "30b82e5c" | ||
166 | * KeySig = [Key, Sig] = "6173819e30b82e5c" | ||
167 | * FwpPass = "ed295076" (never changes) | ||
168 | * EncKeySig = ECB_DES(KeySig, FwpPass) | ||
169 | * = [0x8a, 0x01, 0xb6, ..., 0xc5] (16 bytes) | ||
170 | * KAS = HexString(EncKeySig) = "8a01b624bfbfde4a1662a1772220e3c5" | ||
171 | * | ||
172 | */ | ||
173 | 64 | ||
174 | struct nwz_model_t g_model_list[] = | 65 | #define cprintf_field(str1, ...) do{ cprintf(GREEN, str1); cprintf(YELLOW, __VA_ARGS__); }while(0) |
175 | { | ||
176 | { "nwz-e450", HAS_KAS | CONFIRMED, "8a01b624bfbfde4a1662a1772220e3c5", "", "" }, | ||
177 | { "nwz-e460", HAS_KAS | CONFIRMED, "89d813f8f966efdebd9c9e0ea98156d2", "", "" }, | ||
178 | { "nwz-a860", HAS_KAS | CONFIRMED, "a7c4af6c28b8900a783f307c1ba538c5", "", "" }, | ||
179 | { "nwz-a850", HAS_KAS | CONFIRMED, "a2efb9168616c2e84d78291295c1aa5d", "", "" }, | ||
180 | { "nwz-e470", HAS_KAS | CONFIRMED, "e4144baaa2707913f17b5634034262c4", "", "" }, | ||
181 | /* The following keys were obtained by brute forcing firmware upgrades, | ||
182 | * someone with a device needs to confirm that they work */ | ||
183 | { "nw-a820", HAS_KEY | HAS_SIG, "", "4df06482", "07fa0b6e" }, | ||
184 | { "nwz-a10", HAS_KEY | HAS_SIG, "", "ec2888e2", "f62ced8a" }, | ||
185 | { "nwz-a20", HAS_KEY | HAS_SIG, "", "e8e204ee", "577614df" }, | ||
186 | { "nwz-zx100", HAS_KEY | HAS_SIG, "", "22e44606", "a9f95e90" }, | ||
187 | { "nwz-e580", HAS_KEY | HAS_SIG, "", "a60806ea", "97e8ce46" }, | ||
188 | }; | ||
189 | |||
190 | static int digit_value(char c) | ||
191 | { | ||
192 | if(c >= '0' && c <= '9') return c - '0'; | ||
193 | if(c >= 'a' && c <= 'f') return c - 'a' + 10; | ||
194 | if(c >= 'A' && c <= 'F') return c - 'A' + 10; | ||
195 | return -1; | ||
196 | } | ||
197 | 66 | ||
198 | static char hex_digit(unsigned v) | 67 | static void usage(void); |
199 | { | ||
200 | return (v < 10) ? v + '0' : (v < 16) ? v - 10 + 'a' : 'x'; | ||
201 | } | ||
202 | |||
203 | static int decrypt_keysig(const char kas[NWZ_KAS_SIZE], char key[NWZ_KEY_SIZE], | ||
204 | char sig[NWZ_SIG_SIZE]) | ||
205 | { | ||
206 | uint8_t src[NWZ_KAS_SIZE / 2]; | ||
207 | for(int index = 0; index < NWZ_KAS_SIZE / 2; index++) | ||
208 | { | ||
209 | int a = digit_value(kas[index * 2]); | ||
210 | int b = digit_value(kas[index * 2 + 1]); | ||
211 | if(a < 0 || b < 0) | ||
212 | { | ||
213 | cprintf(GREY, "Invalid KAS !\n"); | ||
214 | return -1; | ||
215 | } | ||
216 | src[index] = a << 4 | b; | ||
217 | } | ||
218 | fwp_setkey("ed295076"); | ||
219 | fwp_crypt(src, sizeof(src), 1); | ||
220 | memcpy(key, src, NWZ_KEY_SIZE); | ||
221 | memcpy(sig, src + NWZ_KEY_SIZE, NWZ_SIG_SIZE); | ||
222 | return 0; | ||
223 | } | ||
224 | |||
225 | static void encrypt_keysig(char kas[NWZ_KEY_SIZE], | ||
226 | const char key[NWZ_SIG_SIZE], const char sig[NWZ_KAS_SIZE]) | ||
227 | { | ||
228 | uint8_t src[NWZ_KAS_SIZE / 2]; | ||
229 | fwp_setkey("ed295076"); | ||
230 | memcpy(src, key, NWZ_KEY_SIZE); | ||
231 | memcpy(src + NWZ_KEY_SIZE, sig, NWZ_SIG_SIZE); | ||
232 | fwp_crypt(src, sizeof(src), 0); | ||
233 | for(int i = 0; i < NWZ_KAS_SIZE / 2; i++) | ||
234 | { | ||
235 | kas[2 * i] = hex_digit((src[i] >> 4) & 0xf); | ||
236 | kas[2 * i + 1] = hex_digit(src[i] & 0xf); | ||
237 | } | ||
238 | } | ||
239 | 68 | ||
240 | /* user needs to be pointer to a NWZ_KEYSIG_SIZE-byte buffer, on success g_key | 69 | /* user needs to be pointer to a NWZ_KEYSIG_SIZE-byte buffer, on success g_key |
241 | * and g_sig are updated to point to the key and sig in the buffer */ | 70 | * and g_sig are updated to point to the key and sig in the buffer */ |
@@ -249,20 +78,13 @@ static bool upg_notify_keysig(void *user, uint8_t key[NWZ_KEY_SIZE], | |||
249 | return true; | 78 | return true; |
250 | } | 79 | } |
251 | 80 | ||
252 | static int get_key_and_sig(bool is_extract, void *encrypted_hdr) | 81 | static int get_key_and_sig(bool is_extract, void *buf) |
253 | { | 82 | { |
254 | static char keysig[NWZ_KEYSIG_SIZE]; | 83 | static char keysig[NWZ_KEYSIG_SIZE]; |
255 | static char kas[NWZ_KAS_SIZE]; | 84 | static char kas[NWZ_KAS_SIZE]; |
256 | /* database lookup */ | 85 | /* database lookup */ |
257 | if(g_model_index != -1) | 86 | if(g_model_index != -1) |
258 | { | 87 | g_kas = g_model_list[g_model_index].kas; |
259 | if(g_model_list[g_model_index].flags & HAS_KAS) | ||
260 | g_kas = g_model_list[g_model_index].kas; | ||
261 | if(g_model_list[g_model_index].flags & HAS_KEY) | ||
262 | g_key = g_model_list[g_model_index].key; | ||
263 | if(g_model_list[g_model_index].flags & HAS_SIG) | ||
264 | g_sig = g_model_list[g_model_index].sig; | ||
265 | } | ||
266 | 88 | ||
267 | /* always prefer KAS because it contains everything */ | 89 | /* always prefer KAS because it contains everything */ |
268 | if(g_kas) | 90 | if(g_kas) |
@@ -276,33 +98,26 @@ static int get_key_and_sig(bool is_extract, void *encrypted_hdr) | |||
276 | g_sig = keysig + NWZ_KEY_SIZE; | 98 | g_sig = keysig + NWZ_KEY_SIZE; |
277 | decrypt_keysig(g_kas, g_key, g_sig); | 99 | decrypt_keysig(g_kas, g_key, g_sig); |
278 | } | 100 | } |
279 | /* fall back to key and signature otherwise. The signature is not required | 101 | /* Otherwise require key and signature */ |
280 | * when extracting but prevents from checking decryption */ | 102 | else if(g_key && g_sig) |
281 | else if(g_key && (is_extract || g_sig)) | ||
282 | { | 103 | { |
104 | /* check key and signature size */ | ||
283 | if(strlen(g_key) != 8) | 105 | if(strlen(g_key) != 8) |
284 | { | 106 | { |
285 | cprintf(GREY, "The specified key has wrong length (must be 8 hex digits)\n"); | 107 | cprintf(GREY, "The specified key has wrong length (must be 8 hex digits)\n"); |
286 | return 4; | 108 | return 4; |
287 | } | 109 | } |
288 | 110 | if(strlen(g_sig) != 8) | |
289 | /* if there is a signature, it must have the correct size */ | ||
290 | if(g_sig) | ||
291 | { | 111 | { |
292 | if(strlen(g_sig) != 8) | 112 | cprintf(GREY, "The specified sig has wrong length (must be 8 hex digits)\n"); |
293 | { | 113 | return 5; |
294 | cprintf(GREY, "The specified sig has wrong length (must be 8 hex digits)\n"); | ||
295 | return 5; | ||
296 | } | ||
297 | } | ||
298 | else | ||
299 | { | ||
300 | cprintf(GREY, "Warning: you have specified a key but no sig, I won't be able to do any checks\n"); | ||
301 | } | 114 | } |
302 | } | 115 | } |
303 | /* for extraction, we offer a brute force search method from the MD5 */ | 116 | /* for extraction, we offer a brute force search method from the MD5 */ |
304 | else if(is_extract && g_keysig_search != KEYSIG_SEARCH_NONE) | 117 | else if(is_extract && g_keysig_search != KEYSIG_SEARCH_NONE) |
305 | { | 118 | { |
119 | struct upg_md5_t *md5 = (void *)buf; | ||
120 | void *encrypted_hdr = (md5 + 1); | ||
306 | cprintf(BLUE, "keysig Search\n"); | 121 | cprintf(BLUE, "keysig Search\n"); |
307 | cprintf_field(" Method: ", "%s\n", keysig_search_desc[g_keysig_search].name); | 122 | cprintf_field(" Method: ", "%s\n", keysig_search_desc[g_keysig_search].name); |
308 | bool ok = keysig_search(g_keysig_search, encrypted_hdr, 8, | 123 | bool ok = keysig_search(g_keysig_search, encrypted_hdr, 8, |
@@ -329,13 +144,8 @@ static int get_key_and_sig(bool is_extract, void *encrypted_hdr) | |||
329 | * valid files anyway */ | 144 | * valid files anyway */ |
330 | if(!g_kas) | 145 | if(!g_kas) |
331 | { | 146 | { |
332 | if(!g_sig) | 147 | /* This is useful to print the KAS for the user when brute-forcing since |
333 | { | 148 | * the process will produce a key+sig and the database requires a KAS */ |
334 | /* if we extract and don't have a signature, just use a random | ||
335 | * one, we cannot check it anyway */ | ||
336 | g_sig = keysig; | ||
337 | memset(g_sig, '?', NWZ_SIG_SIZE); | ||
338 | } | ||
339 | g_kas = kas; | 149 | g_kas = kas; |
340 | encrypt_keysig(g_kas, g_key, g_sig); | 150 | encrypt_keysig(g_kas, g_key, g_sig); |
341 | } | 151 | } |
@@ -343,86 +153,38 @@ static int get_key_and_sig(bool is_extract, void *encrypted_hdr) | |||
343 | cprintf(BLUE, "Keys\n"); | 153 | cprintf(BLUE, "Keys\n"); |
344 | cprintf_field(" KAS: ", "%."STR(NWZ_KAS_SIZE)"s\n", g_kas); | 154 | cprintf_field(" KAS: ", "%."STR(NWZ_KAS_SIZE)"s\n", g_kas); |
345 | cprintf_field(" Key: ", "%."STR(NWZ_KEY_SIZE)"s\n", g_key); | 155 | cprintf_field(" Key: ", "%."STR(NWZ_KEY_SIZE)"s\n", g_key); |
346 | if(g_sig) | 156 | cprintf_field(" Sig: ", "%."STR(NWZ_SIG_SIZE)"s\n", g_sig); |
347 | cprintf_field(" Sig: ", "%."STR(NWZ_SIG_SIZE)"s\n", g_sig); | ||
348 | 157 | ||
349 | return 0; | 158 | return 0; |
350 | } | 159 | } |
351 | 160 | ||
352 | static int do_upg(void *buf, long size) | 161 | static int do_upg(void *buf, long size) |
353 | { | 162 | { |
354 | struct upg_md5_t *md5 = buf; | 163 | int ret = get_key_and_sig(true, buf); |
355 | cprintf(BLUE, "Preliminary\n"); | ||
356 | cprintf(GREEN, " MD5: "); | ||
357 | for(int i = 0; i < 16; i++) | ||
358 | cprintf(YELLOW, "%02x", md5->md5[i]); | ||
359 | printf(" "); | ||
360 | |||
361 | uint8_t actual_md5[MD5_DIGEST_LENGTH]; | ||
362 | { | ||
363 | MD5_CTX c; | ||
364 | MD5_Init(&c); | ||
365 | MD5_Update(&c, md5 + 1, size - sizeof(struct upg_header_t)); | ||
366 | MD5_Final(actual_md5, &c); | ||
367 | } | ||
368 | check_field(memcmp(actual_md5, md5->md5, 16), 0, "Ok\n", "Mismatch\n"); | ||
369 | |||
370 | int ret = get_key_and_sig(true, md5 + 1); | ||
371 | if(ret != 0) | 164 | if(ret != 0) |
372 | return ret; | 165 | return ret; |
373 | 166 | struct upg_file_t *file = upg_read_memory(buf, size, g_key, g_sig, NULL, | |
374 | struct upg_header_t *hdr = (void *)(md5 + 1); | 167 | generic_std_printf); |
375 | ret = fwp_read(hdr, sizeof(struct upg_header_t), hdr, (void *)g_key); | 168 | if(file == NULL) |
376 | if(ret) | ||
377 | return ret; | 169 | return ret; |
378 | 170 | for(int i = 0; i < file->nr_files; i++) | |
379 | cprintf(BLUE, "Header\n"); | ||
380 | cprintf_field(" Signature:", " "); | ||
381 | for(int i = 0; i < 8; i++) | ||
382 | cprintf(YELLOW, "%c", isprint(hdr->sig[i]) ? hdr->sig[i] : '.'); | ||
383 | if(g_sig) | ||
384 | { | 171 | { |
385 | check_field(memcmp(hdr->sig, g_sig, 8), 0, " OK\n", " Mismatch\n"); | 172 | if(!g_out_prefix) |
386 | } | 173 | continue; |
387 | else | 174 | char *str = malloc(strlen(g_out_prefix) + 32); |
388 | cprintf(RED, " Can't check\n"); | 175 | sprintf(str, "%s%d.bin", g_out_prefix, i); |
389 | cprintf_field(" Files: ", "%d\n", hdr->nr_files); | 176 | FILE *f = fopen(str, "wb"); |
390 | cprintf_field(" Pad: ", "0x%x\n", hdr->pad); | 177 | if(!f) |
391 | |||
392 | cprintf(BLUE, "Files\n"); | ||
393 | struct upg_entry_t *entry = (void *)(hdr + 1); | ||
394 | for(unsigned i = 0; i < hdr->nr_files; i++, entry++) | ||
395 | { | ||
396 | int ret = fwp_read(entry, sizeof(struct upg_entry_t), entry, (void *)g_key); | ||
397 | if(ret) | ||
398 | return ret; | ||
399 | cprintf(GREY, " File"); | ||
400 | cprintf(RED, " %d\n", i); | ||
401 | cprintf_field(" Offset: ", "0x%x\n", entry->offset); | ||
402 | cprintf_field(" Size: ", "0x%x\n", entry->size); | ||
403 | |||
404 | if(g_out_prefix) | ||
405 | { | 178 | { |
406 | char *str = malloc(strlen(g_out_prefix) + 32); | 179 | cprintf(GREY, "Cannot open '%s' for writing\n", str); |
407 | sprintf(str, "%s%d.bin", g_out_prefix, i); | 180 | free(str); |
408 | FILE *f = fopen(str, "wb"); | 181 | continue; |
409 | if(f) | ||
410 | { | ||
411 | // round up size, there is some padding done with random data | ||
412 | int crypt_size = ROUND_UP(entry->size, 8); | ||
413 | int ret = fwp_read(buf + entry->offset, crypt_size, | ||
414 | buf + entry->offset, (void *)g_key); | ||
415 | if(ret) | ||
416 | return ret; | ||
417 | // but write the *good* amount of data | ||
418 | fwrite(buf + entry->offset, 1, entry->size, f); | ||
419 | fclose(f); | ||
420 | } | ||
421 | else | ||
422 | cprintf(GREY, "Cannot open '%s' for writing\n", str); | ||
423 | } | 182 | } |
183 | free(str); | ||
184 | fwrite(file->files[i].data, 1, file->files[i].size, f); | ||
185 | fclose(f); | ||
424 | } | 186 | } |
425 | 187 | upg_free(file); | |
426 | return 0; | 188 | return 0; |
427 | } | 189 | } |
428 | 190 | ||
@@ -490,7 +252,7 @@ static int create_upg(int argc, char **argv) | |||
490 | { | 252 | { |
491 | if(argc == 0) | 253 | if(argc == 0) |
492 | { | 254 | { |
493 | printf("You must specify a firmware filename\n"); | 255 | cprintf(GREY, "You must specify a firmware filename\n"); |
494 | usage(); | 256 | usage(); |
495 | } | 257 | } |
496 | 258 | ||
@@ -498,89 +260,55 @@ static int create_upg(int argc, char **argv) | |||
498 | if(ret != 0) | 260 | if(ret != 0) |
499 | return ret; | 261 | return ret; |
500 | 262 | ||
501 | FILE *fout = fopen(argv[0], "wb"); | 263 | struct upg_file_t *upg = upg_new(); |
502 | if(fout == NULL) | ||
503 | { | ||
504 | printf("Cannot open output firmware file: %m\n"); | ||
505 | return 1; | ||
506 | } | ||
507 | |||
508 | int nr_files = argc - 1; | 264 | int nr_files = argc - 1; |
509 | FILE **files = malloc(nr_files * sizeof(FILE *)); | ||
510 | 265 | ||
511 | for(int i = 0; i < nr_files; i++) | 266 | for(int i = 0; i < nr_files; i++) |
512 | { | 267 | { |
513 | files[i] = fopen(argv[1 + i], "rb"); | 268 | FILE *f = fopen(argv[1 + i], "rb"); |
514 | if(files[i] == NULL) | 269 | if(f == NULL) |
270 | { | ||
271 | upg_free(upg); | ||
272 | printf(GREY, "Cannot open input file '%s': %m\n", argv[i + 1]); | ||
273 | return 1; | ||
274 | } | ||
275 | size_t size = filesize(f); | ||
276 | void *buf = malloc(size); | ||
277 | if(fread(buf, 1, size, f) != size) | ||
515 | { | 278 | { |
516 | printf("Cannot open input file '%s': %m\n", argv[i + 1]); | 279 | cprintf(GREY, "Cannot read input file '%s': %m\n", argv[i + 1]); |
280 | fclose(f); | ||
281 | upg_free(upg); | ||
517 | return 1; | 282 | return 1; |
518 | } | 283 | } |
284 | fclose(f); | ||
285 | upg_append(upg, buf, size); | ||
519 | } | 286 | } |
520 | 287 | ||
521 | struct upg_md5_t md5; | 288 | size_t size = 0; |
522 | memset(&md5, 0, sizeof(md5)); | 289 | void *buf = upg_write_memory(upg, g_key, g_sig, &size, NULL, generic_std_printf); |
523 | MD5_CTX c; | 290 | upg_free(upg); |
524 | MD5_Init(&c); | 291 | if(buf == NULL) |
525 | // output a dummy md5 sum | ||
526 | fwrite(&md5, 1, sizeof(md5), fout); | ||
527 | // output the encrypted signature | ||
528 | struct upg_header_t hdr; | ||
529 | memcpy(hdr.sig, g_sig, 8); | ||
530 | hdr.nr_files = nr_files; | ||
531 | hdr.pad = 0; | ||
532 | |||
533 | ret = fwp_write(&hdr, sizeof(hdr), &hdr, (void *)g_key); | ||
534 | if(ret) | ||
535 | return ret; | ||
536 | MD5_Update(&c, &hdr, sizeof(hdr)); | ||
537 | fwrite(&hdr, 1, sizeof(hdr), fout); | ||
538 | |||
539 | // output file headers | ||
540 | long offset = sizeof(md5) + sizeof(hdr) + nr_files * sizeof(struct upg_entry_t); | ||
541 | for(int i = 0; i < nr_files; i++) | ||
542 | { | 292 | { |
543 | struct upg_entry_t entry; | 293 | cprintf(GREY, "Error creating UPG file\n"); |
544 | entry.offset = offset; | 294 | return 1; |
545 | entry.size = filesize(files[i]); | ||
546 | offset += ROUND_UP(entry.size, 8); // do it before encryption !! | ||
547 | |||
548 | ret = fwp_write(&entry, sizeof(entry), &entry, (void *)g_key); | ||
549 | if(ret) | ||
550 | return ret; | ||
551 | MD5_Update(&c, &entry, sizeof(entry)); | ||
552 | fwrite(&entry, 1, sizeof(entry), fout); | ||
553 | } | 295 | } |
554 | 296 | FILE *fout = fopen(argv[0], "wb"); | |
555 | cprintf(BLUE, "Files\n"); | 297 | if(fout == NULL) |
556 | for(int i = 0; i < nr_files; i++) | ||
557 | { | 298 | { |
558 | long size = filesize(files[i]); | 299 | cprintf(GREY, "Cannot open output firmware file: %m\n"); |
559 | long r_size = ROUND_UP(size, 8); | ||
560 | cprintf(GREY, " File"); | ||
561 | cprintf(RED, " %d\n", i); | ||
562 | cprintf_field(" Offset: ", "0x%lx\n", ftell(fout)); | ||
563 | cprintf_field(" Size: ", "0x%lx\n", size); | ||
564 | |||
565 | void *buf = malloc(r_size); | ||
566 | memset(buf, 0, r_size); | ||
567 | fread(buf, 1, size, files[i]); | ||
568 | fclose(files[i]); | ||
569 | |||
570 | ret = fwp_write(buf, r_size, buf, (void *)g_key); | ||
571 | if(ret) | ||
572 | return ret; | ||
573 | MD5_Update(&c, buf, r_size); | ||
574 | fwrite(buf, 1, r_size, fout); | ||
575 | |||
576 | free(buf); | 300 | free(buf); |
301 | return 1; | ||
302 | } | ||
303 | if(fwrite(buf, 1, size, fout) != size) | ||
304 | { | ||
305 | cprintf(GREY, "Cannot write output file: %m\n"); | ||
306 | fclose(fout); | ||
307 | free(buf); | ||
308 | return 1; | ||
577 | } | 309 | } |
578 | |||
579 | fseek(fout, 0, SEEK_SET); | ||
580 | MD5_Final(md5.md5, &c); | ||
581 | fwrite(&md5, 1, sizeof(md5), fout); | ||
582 | fclose(fout); | 310 | fclose(fout); |
583 | 311 | free(buf); | |
584 | return 0; | 312 | return 0; |
585 | } | 313 | } |
586 | 314 | ||
@@ -703,25 +431,13 @@ int main(int argc, char **argv) | |||
703 | if(g_model && strcmp(g_model, "?") == 0) | 431 | if(g_model && strcmp(g_model, "?") == 0) |
704 | { | 432 | { |
705 | cprintf(BLUE, "Model list:\n"); | 433 | cprintf(BLUE, "Model list:\n"); |
706 | for(unsigned i = 0; i < sizeof(g_model_list) / sizeof(g_model_list[0]); i++) | 434 | for(unsigned i = 0; g_model_list[i].model; i++) |
707 | { | 435 | { |
708 | cprintf(GREEN, " %s:", g_model_list[i].model); | 436 | cprintf(GREEN, " %s:", g_model_list[i].model); |
709 | if(g_model_list[i].flags & HAS_KAS) | 437 | |
710 | { | 438 | cprintf(RED, " kas="); |
711 | cprintf(RED, " kas="); | 439 | cprintf(YELLOW, "%."STR(NWZ_KAS_SIZE)"s", g_model_list[i].kas); |
712 | cprintf(YELLOW, "%."STR(NWZ_KAS_SIZE)"s", g_model_list[i].kas); | 440 | if(g_model_list[i].confirmed) |
713 | } | ||
714 | if(g_model_list[i].flags & HAS_KEY) | ||
715 | { | ||
716 | cprintf(RED, " key="); | ||
717 | cprintf(YELLOW, "%.8s", g_model_list[i].key); | ||
718 | } | ||
719 | if(g_model_list[i].flags & HAS_SIG) | ||
720 | { | ||
721 | cprintf(RED, " sig="); | ||
722 | cprintf(YELLOW, "%.8s", g_model_list[i].sig); | ||
723 | } | ||
724 | if(g_model_list[i].flags & CONFIRMED) | ||
725 | cprintf(RED, " confirmed"); | 441 | cprintf(RED, " confirmed"); |
726 | else | 442 | else |
727 | cprintf(RED, " guessed"); | 443 | cprintf(RED, " guessed"); |
@@ -732,7 +448,7 @@ int main(int argc, char **argv) | |||
732 | 448 | ||
733 | if(g_model) | 449 | if(g_model) |
734 | { | 450 | { |
735 | for(unsigned i = 0; i < sizeof(g_model_list) / sizeof(g_model_list[0]); i++) | 451 | for(unsigned i = 0; g_model_list[i].model; i++) |
736 | if(strcmp(g_model, g_model_list[i].model) == 0) | 452 | if(strcmp(g_model, g_model_list[i].model) == 0) |
737 | g_model_index = i; | 453 | g_model_index = i; |
738 | if(g_model_index == -1) | 454 | if(g_model_index == -1) |