summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAmaury Pouly <amaury.pouly@gmail.com>2017-01-04 16:36:27 +0100
committerAmaury Pouly <amaury.pouly@gmail.com>2017-01-04 17:04:38 +0100
commit92ecbd5fb8a7c8e939b1b4dde82cc6c9ba9d41af (patch)
tree5e187e031ab8648e2c2c58502f167abc72ec606c
parent5cfd4a5b8e551c23600c93838180487e91698814 (diff)
downloadrockbox-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/Makefile2
-rw-r--r--utils/nwztools/upgtools/fwp.c8
-rw-r--r--utils/nwztools/upgtools/misc.c31
-rw-r--r--utils/nwztools/upgtools/misc.h10
-rw-r--r--utils/nwztools/upgtools/upg.c260
-rw-r--r--utils/nwztools/upgtools/upgtool.c436
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
19upgtool: upgtool.o misc.o fwp.o mg.o keysig_search.o 19upgtool: upgtool.o upg.o misc.o fwp.o mg.o keysig_search.o
20 $(LD) -o $@ $^ $(LDFLAGS) 20 $(LD) -o $@ $^ $(LDFLAGS)
21 21
22clean: 22clean:
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
27char OFF[] = { 0x1b, 0x5b, 0x31, 0x3b, '0', '0', 0x6d, '\0' }; 28const char OFF[] = { 0x1b, 0x5b, 0x31, 0x3b, '0', '0', 0x6d, '\0' };
28 29
29char GREY[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '0', 0x6d, '\0' }; 30const char GREY[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '0', 0x6d, '\0' };
30char RED[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '1', 0x6d, '\0' }; 31const char RED[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '1', 0x6d, '\0' };
31char GREEN[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '2', 0x6d, '\0' }; 32const char GREEN[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '2', 0x6d, '\0' };
32char YELLOW[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '3', 0x6d, '\0' }; 33const char YELLOW[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '3', 0x6d, '\0' };
33char BLUE[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '4', 0x6d, '\0' }; 34const char BLUE[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '4', 0x6d, '\0' };
34 35
35static bool g_color_enable = true; 36static bool g_color_enable = true;
36 37
37void *xmalloc(size_t s)
38{
39 void * r = malloc(s);
40 if(!r) bugp("malloc");
41 return r;
42}
43
44void enable_color(bool enable) 38void 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
49void 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
35typedef char color_t[]; 32typedef const char color_t[];
36 33
37extern color_t OFF, GREY, RED, GREEN, YELLOW, BLUE; 34extern color_t OFF, GREY, RED, GREEN, YELLOW, BLUE;
38void *xmalloc(size_t s);
39void color(color_t c); 35void color(color_t c);
40void enable_color(bool enable); 36void enable_color(bool enable);
41 37
42#define cprintf(col, ...) do {color(col); printf(__VA_ARGS__); }while(0) 38typedef 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) 40void 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
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}
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
62static 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
69struct 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
90struct upg_md5_t
91{
92 uint8_t md5[16];
93}__attribute__((packed));
94
95struct 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
102struct 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
174struct 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
190static 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
198static char hex_digit(unsigned v) 67static void usage(void);
199{
200 return (v < 10) ? v + '0' : (v < 16) ? v - 10 + 'a' : 'x';
201}
202
203static 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
225static 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
252static int get_key_and_sig(bool is_extract, void *encrypted_hdr) 81static 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
352static int do_upg(void *buf, long size) 161static 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)