summaryrefslogtreecommitdiff
path: root/utils/nwztools/upgtools/upg.c
diff options
context:
space:
mode:
Diffstat (limited to 'utils/nwztools/upgtools/upg.c')
-rw-r--r--utils/nwztools/upgtools/upg.c260
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
27struct 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
44static 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
52static char hex_digit(unsigned v)
53{
54 return (v < 10) ? v + '0' : (v < 16) ? v - 10 + 'a' : 'x';
55}
56
57int 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
76void 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
91struct 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
173void *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
236struct 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
243void 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
251void 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}