diff options
Diffstat (limited to 'utils/nwztools/upgtools/upg.c')
-rw-r--r-- | utils/nwztools/upgtools/upg.c | 260 |
1 files changed, 260 insertions, 0 deletions
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 | } | ||