summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAmaury Pouly <amaury.pouly@gmail.com>2016-10-27 23:06:16 +0200
committerAmaury Pouly <amaury.pouly@gmail.com>2016-10-27 23:06:16 +0200
commit37f95f67fec2b2460903ffa5255b1beeba1731fd (patch)
tree6a932718139104406ab576ba89065c53f8dd20e7
parent794104dd17a28a2db09ca1ed44ba7dfb18a1f0ca (diff)
downloadrockbox-37f95f67fec2b2460903ffa5255b1beeba1731fd.tar.gz
rockbox-37f95f67fec2b2460903ffa5255b1beeba1731fd.zip
nwztools/upgtools: rewrite keysig brute force search
The new search has two new features: - it takes advantage of the fact that DES keys are only 56-bit long (and not 64) - it is now multithreaded As a proof of concept, I ran it on the A10 series firmware upgrade and was able to find the key in a few seconds using 4 threads. The search is still limited to ascii hex passwords (seems to work on all devices I have tried thus far). Change-Id: Ied080286d2bbdc493a6ceaecaaadba802b429666
-rw-r--r--utils/nwztools/upgtools/Makefile2
-rw-r--r--utils/nwztools/upgtools/keysig_search.c344
-rw-r--r--utils/nwztools/upgtools/keysig_search.h10
-rw-r--r--utils/nwztools/upgtools/mg.cpp13
-rw-r--r--utils/nwztools/upgtools/misc.c1
-rw-r--r--utils/nwztools/upgtools/misc.h2
-rw-r--r--utils/nwztools/upgtools/upgtool.c33
7 files changed, 299 insertions, 106 deletions
diff --git a/utils/nwztools/upgtools/Makefile b/utils/nwztools/upgtools/Makefile
index ad83c0e4c6..35d5bfabff 100644
--- a/utils/nwztools/upgtools/Makefile
+++ b/utils/nwztools/upgtools/Makefile
@@ -5,7 +5,7 @@ LD=g++
5PROFILE= 5PROFILE=
6CFLAGS=-g $(PROFILE) -std=c99 -W -Wall $(DEFINES) `pkg-config --cflags openssl` `pkg-config --cflags libcrypto++` 6CFLAGS=-g $(PROFILE) -std=c99 -W -Wall $(DEFINES) `pkg-config --cflags openssl` `pkg-config --cflags libcrypto++`
7CXXFLAGS=-g $(PROFILE) -W -Wall $(DEFINES) `pkg-config --cflags openssl` `pkg-config --cflags libcrypto++` 7CXXFLAGS=-g $(PROFILE) -W -Wall $(DEFINES) `pkg-config --cflags openssl` `pkg-config --cflags libcrypto++`
8LDFLAGS=$(PROFILE) `pkg-config --libs openssl` `pkg-config --libs libcrypto++` -lcrypt 8LDFLAGS=$(PROFILE) `pkg-config --libs openssl` `pkg-config --libs libcrypto++` -lcrypt -lpthread
9BINS=upgtool 9BINS=upgtool
10 10
11all: $(BINS) 11all: $(BINS)
diff --git a/utils/nwztools/upgtools/keysig_search.c b/utils/nwztools/upgtools/keysig_search.c
index 7652efa233..2740dc1461 100644
--- a/utils/nwztools/upgtools/keysig_search.c
+++ b/utils/nwztools/upgtools/keysig_search.c
@@ -23,6 +23,118 @@
23#include "mg.h" 23#include "mg.h"
24#include <string.h> 24#include <string.h>
25#include <stdio.h> 25#include <stdio.h>
26#include <stdlib.h>
27#include <pthread.h>
28
29/** Generic search code */
30
31/* The generator sends chunks to the workers. The exact type of chunks depends
32 * on the method used. */
33static struct
34{
35 pthread_mutex_t mutex; /* mutex for the whole structure */
36 pthread_cond_t avail_cond; /* condition to signal available or stop */
37 pthread_cond_t req_cond; /* condition to signal request or stop */
38 bool stop; /* if true, stop searcg */
39 void *chunk; /* pointer to chunk (NULL if not available) */
40 size_t chunk_sz; /* chunk size */
41}g_producer;
42
43/* init producer */
44static void producer_init(void)
45{
46 pthread_cond_init(&g_producer.avail_cond, NULL);
47 pthread_cond_init(&g_producer.req_cond, NULL);
48 pthread_mutex_init(&g_producer.mutex, NULL);
49 g_producer.stop = false;
50 g_producer.chunk = NULL;
51 g_producer.chunk_sz = 0;
52}
53
54/* consumer get: called by worker to get a new chunk, return NULL to stop */
55static void *consumer_get(size_t *sz)
56{
57 pthread_mutex_lock(&g_producer.mutex);
58 /* loop until stop or new chunk */
59 while(true)
60 {
61 /* stop if requested */
62 if(g_producer.stop)
63 {
64 pthread_mutex_unlock(&g_producer.mutex);
65 return NULL;
66 }
67 if(g_producer.chunk)
68 break;
69 /* request a new chunk */
70 pthread_cond_signal(&g_producer.req_cond);
71 /* wait for availability */
72 pthread_cond_wait(&g_producer.avail_cond, &g_producer.mutex);
73 }
74 void *c = g_producer.chunk;
75 if(sz)
76 *sz = g_producer.chunk_sz;
77 g_producer.chunk = NULL;
78 pthread_mutex_unlock(&g_producer.mutex);
79 /* request a new chunk, so that if other consumers are waiting, the producer
80 * will also wake them up */
81 pthread_cond_signal(&g_producer.req_cond);
82 return c;
83}
84
85/* stop: called by worker to stop the search */
86static void consumer_stop(void)
87{
88 pthread_mutex_lock(&g_producer.mutex);
89 /* set stop */
90 g_producer.stop = true;
91 /* wake up everyone */
92 pthread_cond_broadcast(&g_producer.req_cond);
93 pthread_cond_broadcast(&g_producer.avail_cond);
94 pthread_mutex_unlock(&g_producer.mutex);
95}
96
97/* producer yield: called by generator to give a new chunk, return true to stop */
98static bool producer_yield(void *chunk, size_t sz)
99{
100 pthread_mutex_lock(&g_producer.mutex);
101 /* wait until stop or request */
102 while(true)
103 {
104 /* stop if requested */
105 if(g_producer.stop)
106 {
107 pthread_mutex_unlock(&g_producer.mutex);
108 return true;
109 }
110 /* if the chunk is empty, fill it */
111 if(g_producer.chunk == NULL)
112 break;
113 /* otherwise wait for request */
114 pthread_cond_wait(&g_producer.req_cond, &g_producer.mutex);
115 }
116 g_producer.chunk = malloc(sz);
117 memcpy(g_producer.chunk, chunk, sz);
118 g_producer.chunk_sz = sz;
119 /* signal availability */
120 pthread_cond_signal(&g_producer.avail_cond);
121 pthread_mutex_unlock(&g_producer.mutex);
122 return false;
123}
124
125static void producer_stop(void)
126{
127 pthread_mutex_lock(&g_producer.mutex);
128 /* if we are not already stopping and there is a chunk still waiting to
129 * be consumed, wait until next request */
130 if(!g_producer.stop && g_producer.chunk)
131 pthread_cond_wait(&g_producer.req_cond, &g_producer.mutex);
132 /* set stop */
133 g_producer.stop = true;
134 /* wake up everyone */
135 pthread_cond_broadcast(&g_producer.avail_cond);
136 pthread_mutex_unlock(&g_producer.mutex);
137}
26 138
27/* Key search methods 139/* Key search methods
28 * 140 *
@@ -41,16 +153,18 @@
41 * towards very unbalanced strings (only digits or only letters). 153 * towards very unbalanced strings (only digits or only letters).
42 */ 154 */
43 155
44static uint8_t g_cipher[8]; 156static struct
45static keysig_notify_fn_t g_notify; 157{
46static uint8_t g_key[8]; 158 pthread_mutex_t mutex;
47static void *g_user; 159 uint8_t *enc_buf;
160 size_t enc_buf_sz;
161 bool found_keysig;
162 uint8_t key[NWZ_KEY_SIZE]; /* result */
163 uint8_t sig[NWZ_SIG_SIZE]; /* result */
164}g_keysig_search;
165
48static bool is_hex[256]; 166static bool is_hex[256];
49static bool is_init = false; 167static bool is_init = false;
50static uint64_t g_tot_count;
51static uint64_t g_cur_count;
52static int g_last_percent;
53static int g_last_subpercent;
54 168
55static void keysig_search_init() 169static void keysig_search_init()
56{ 170{
@@ -71,105 +185,169 @@ static inline bool is_full_ascii(uint8_t *arr)
71 return true; 185 return true;
72} 186}
73 187
74static inline bool check_stupid() 188struct upg_header_t
75{ 189{
76 uint8_t res[8]; 190 uint8_t sig[NWZ_SIG_SIZE];
77 // display progress 191 uint32_t nr_files;
78 g_cur_count++; 192 uint32_t pad; // make sure structure size is a multiple of 8
79 int percent = (g_cur_count * 100ULL) / g_tot_count; 193} __attribute__((packed));
80 int subpercent = ((g_cur_count * 1000ULL) / g_tot_count) % 10; 194
81 if(percent != g_last_percent) 195static bool check_key(uint8_t key[NWZ_KEY_SIZE])
82 { 196{
83 cprintf(RED, "%d%%", percent); 197 struct upg_header_t hdr;
84 fflush(stdout); 198 mg_decrypt_fw(g_keysig_search.enc_buf, sizeof(hdr.sig), (void *)&hdr, key);
85 g_last_subpercent = 0; 199 if(is_full_ascii(hdr.sig))
86 }
87 if(subpercent != g_last_subpercent)
88 { 200 {
89 cprintf(WHITE, "."); 201 /* the signature looks correct, so decrypt the header futher to be sure */
90 fflush(stdout); 202 mg_decrypt_fw(g_keysig_search.enc_buf, sizeof(hdr), (void *)&hdr, key);
203 /* we expect the number of files to be small and the padding to be 0 */
204 if(hdr.nr_files == 0 || hdr.nr_files > 10 || hdr.pad != 0)
205 return false;
206 cprintf(RED, " Found key: %.8s (sig=%.8s, nr_files=%u)\n", key, hdr.sig, (unsigned)hdr.nr_files);
207 pthread_mutex_lock(&g_keysig_search.mutex);
208 g_keysig_search.found_keysig = true;
209 memcpy(g_keysig_search.key, key, NWZ_KEY_SIZE);
210 memcpy(g_keysig_search.sig, hdr.sig, NWZ_SIG_SIZE);
211 pthread_mutex_unlock(&g_keysig_search.mutex);
212 consumer_stop();
213 return true;
91 } 214 }
92 g_last_percent = percent;
93 g_last_subpercent = subpercent;
94
95 mg_decrypt_fw(g_cipher, 8, res, g_key);
96 if(is_full_ascii(res))
97 return g_notify(g_user, g_key, res);
98 return false; 215 return false;
99} 216}
100 217
101static bool search_stupid_rec(int rem_digit, int rem_letter, int pos) 218/** Hex search */
219
220struct hex_chunk_t
102{ 221{
103 if(pos == 8) 222 uint8_t key[NWZ_KEY_SIZE]; /* partially pre-filled key */
104 return check_stupid(); 223 int pos;
105 if(rem_digit > 0) 224 int rem_letters;
225 int rem_digits;
226};
227
228static bool hex_rec(bool producer, struct hex_chunk_t *ch)
229{
230 /* we list the first 4 pos in generator, and remaining 4 in workers */
231 if(producer && ch->pos == 4)
232 {
233 //printf("yield(%.8s,%d,%d,%d)\n", ch->key, ch->pos, ch->rem_digits, ch->rem_letters);
234 return producer_yield(ch, sizeof(struct hex_chunk_t));
235 }
236 /* filled the key ? */
237 if(!producer && ch->pos == NWZ_KEY_SIZE)
238 return check_key(ch->key);
239 /* list next possibilities
240 *
241 * NOTE (42) Since the cipher is DES, the key is actually 56-bit: the least
242 * significant bit of each byte is an (unused) parity bit. We thus only
243 * generate keys where the least significant bit is 0. */
244 int p = ch->pos++;
245 if(ch->rem_digits > 0)
106 { 246 {
107 for(int i = '0'; i <= '9'; i++) 247 ch->rem_digits--;
248 /* NOTE (42) */
249 for(int i = '0'; i <= '9'; i += 2)
108 { 250 {
109 g_key[pos] = i; 251 ch->key[p] = i;
110 if(search_stupid_rec(rem_digit - 1, rem_letter, pos + 1)) 252 if(hex_rec(producer, ch))
111 return true; 253 return true;
112 } 254 }
255 ch->rem_digits++;
113 } 256 }
114 if(rem_letter > 0) 257 if(ch->rem_letters > 0)
115 { 258 {
116 for(int i = 'a' - 1; i <= 'f'; i++) 259 ch->rem_letters--;
260 /* NOTE (42) */
261 for(int i = 'a'; i <= 'f'; i += 2)
117 { 262 {
118 g_key[pos] = i; 263 ch->key[p] = i;
119 if(search_stupid_rec(rem_digit, rem_letter - 1, pos + 1)) 264 if(hex_rec(producer, ch))
120 return true; 265 return true;
121 } 266 }
267 ch->rem_letters++;
122 } 268 }
269 ch->pos--;
123 return false; 270 return false;
124} 271}
125 272
126static bool search_stupid(int rem_digit, int rem_letter) 273static void *hex_worker(void *arg)
127{ 274{
128 cprintf(WHITE, "\n Looking for keys with "); 275 (void) arg;
129 cprintf(RED, "%d", rem_digit); 276 while(true)
130 cprintf(WHITE, " digits and "); 277 {
131 cprintf(RED, "%d", rem_letter); 278 struct hex_chunk_t *ch = consumer_get(NULL);
132 cprintf(WHITE, " letters..."); 279 if(ch == NULL)
133 fflush(stdout); 280 break;
134 return search_stupid_rec(rem_digit, rem_letter, 0); 281 hex_rec(false, ch);
282 }
283 return NULL;
135} 284}
136 285
137bool keysig_search_ascii_stupid(uint8_t *cipher, keysig_notify_fn_t notify, void *user) 286static bool hex_producer_list(int nr_digits, int nr_letters)
138{ 287{
139 keysig_search_init(); 288 struct hex_chunk_t ch;
140 memcpy(g_cipher, cipher, 8); 289 cprintf(BLUE, " Listing keys with %d letters and %d digits\n", nr_letters,
141 g_notify = notify; 290 nr_digits);
142 g_user = user; 291 memset(ch.key, ' ', 8);
143 // compute number of possibilities 292 ch.pos = 0;
144 g_cur_count = 0; 293 ch.rem_letters = nr_letters;
145 g_tot_count = 1; 294 ch.rem_digits = nr_digits;
146 g_last_percent = -1; 295 return hex_rec(true, &ch);
147 for(int i = 0; i < 8; i++) 296}
148 g_tot_count *= 16ULL; 297
149 cprintf(WHITE, " Search space:"); 298void *hex_producer(void *arg)
150 cprintf(RED, " %llu", (unsigned long long)g_tot_count); 299{
300 (void) arg;
151 // sorted by probability: 301 // sorted by probability:
152 bool ret = search_stupid(5, 3) // 5 digits, 3 letters: 0.281632 302 bool stop = hex_producer_list(5, 3) // 5 digits, 3 letters: 0.281632
153 || search_stupid(6, 2) // 6 digits, 2 letters: 0.234693 303 || hex_producer_list(6, 2) // 6 digits, 2 letters: 0.234693
154 || search_stupid(4, 4) // 4 digits, 4 letters: 0.211224 304 || hex_producer_list(4, 4) // 4 digits, 4 letters: 0.211224
155 || search_stupid(7, 1) // 7 digits, 1 letters: 0.111759 305 || hex_producer_list(7, 1) // 7 digits, 1 letters: 0.111759
156 || search_stupid(3, 5) // 3 digits, 5 letters: 0.101388 306 || hex_producer_list(3, 5) // 3 digits, 5 letters: 0.101388
157 || search_stupid(2, 6) // 2 digits, 6 letters: 0.030416 307 || hex_producer_list(2, 6) // 2 digits, 6 letters: 0.030416
158 || search_stupid(8, 0) // 8 digits, 0 letters: 0.023283 308 || hex_producer_list(8, 0) // 8 digits, 0 letters: 0.023283
159 || search_stupid(1, 7) // 1 digits, 7 letters: 0.005214 309 || hex_producer_list(1, 7) // 1 digits, 7 letters: 0.005214
160 || search_stupid(0, 8);// 0 digits, 8 letters: 0.000391 310 || hex_producer_list(0, 8);// 0 digits, 8 letters: 0.000391
161 cprintf(OFF, "\n"); 311 if(!stop)
162 return ret; 312 producer_stop();
313 return NULL;
163} 314}
164 315
165bool keysig_search_ascii_brute(uint8_t *cipher, keysig_notify_fn_t notify, void *user) 316typedef void *(*routine_t)(void *);
317
318bool keysig_search(int method, uint8_t *enc_buf, size_t buf_sz,
319 keysig_notify_fn_t notify, void *user, int nr_threads)
166{ 320{
167 (void) cipher; 321 /* init producer */
168 (void) notify; 322 producer_init();
169 (void) user; 323 /* init search */
170 keysig_search_init(); 324 keysig_search_init();
171 cprintf(RED, "Unimplemented\n"); 325 pthread_mutex_init(&g_keysig_search.mutex, NULL);
172 return false; 326 g_keysig_search.enc_buf = enc_buf;
327 g_keysig_search.enc_buf_sz = buf_sz;
328 g_keysig_search.found_keysig = false;
329 /* get methods */
330 routine_t worker_fn = NULL;
331 routine_t producer_fn = NULL;
332 if(method == KEYSIG_SEARCH_ASCII_HEX)
333 {
334 worker_fn = hex_worker;
335 producer_fn = hex_producer;
336 }
337 /* create workers */
338 pthread_t *worker = malloc(sizeof(pthread_t) * nr_threads);
339 pthread_t producer;
340 for(int i = 0; i < nr_threads; i++)
341 pthread_create(&worker[i], NULL, worker_fn, NULL);
342 pthread_create(&producer, NULL, producer_fn, NULL);
343 /* wait for all threads */
344 pthread_join(producer, NULL);
345 for(int i = 0; i < nr_threads; i++)
346 pthread_join(worker[i], NULL);
347 free(worker);
348 if(g_keysig_search.found_keysig)
349 notify(user, g_keysig_search.key, g_keysig_search.sig);
350 return g_keysig_search.found_keysig;
173} 351}
174 352
175struct keysig_search_desc_t keysig_search_desc[KEYSIG_SEARCH_LAST] = 353struct keysig_search_desc_t keysig_search_desc[KEYSIG_SEARCH_LAST] =
@@ -177,19 +355,11 @@ struct keysig_search_desc_t keysig_search_desc[KEYSIG_SEARCH_LAST] =
177 [KEYSIG_SEARCH_NONE] = 355 [KEYSIG_SEARCH_NONE] =
178 { 356 {
179 .name = "none", 357 .name = "none",
180 .fn = NULL,
181 .comment = "don't use", 358 .comment = "don't use",
182 }, 359 },
183 [KEYSIG_SEARCH_ASCII_STUPID] = 360 [KEYSIG_SEARCH_ASCII_HEX] =
184 { 361 {
185 .name = "ascii-hex", 362 .name = "ascii-hex",
186 .fn = keysig_search_ascii_stupid,
187 .comment = "Try to find an hexadecimal ascii string keysig" 363 .comment = "Try to find an hexadecimal ascii string keysig"
188 }, 364 },
189 [KEYSIG_SEARCH_ASCII_BRUTE] =
190 {
191 .name = "ascii-brute",
192 .fn = keysig_search_ascii_brute,
193 .comment = "Brute force all ASCII keys"
194 },
195}; 365};
diff --git a/utils/nwztools/upgtools/keysig_search.h b/utils/nwztools/upgtools/keysig_search.h
index 9009a73284..f419e2621c 100644
--- a/utils/nwztools/upgtools/keysig_search.h
+++ b/utils/nwztools/upgtools/keysig_search.h
@@ -23,30 +23,30 @@
23 23
24#include <stdbool.h> 24#include <stdbool.h>
25#include <stdint.h> 25#include <stdint.h>
26#include <stddef.h>
26#include "fwp.h" 27#include "fwp.h"
27 28
28enum keysig_search_method_t 29enum keysig_search_method_t
29{ 30{
30 KEYSIG_SEARCH_NONE = 0, 31 KEYSIG_SEARCH_NONE = 0,
31 KEYSIG_SEARCH_FIRST, 32 KEYSIG_SEARCH_FIRST,
32 KEYSIG_SEARCH_ASCII_STUPID = KEYSIG_SEARCH_FIRST, 33 KEYSIG_SEARCH_ASCII_HEX = KEYSIG_SEARCH_FIRST,
33 KEYSIG_SEARCH_ASCII_BRUTE,
34 KEYSIG_SEARCH_LAST 34 KEYSIG_SEARCH_LAST
35}; 35};
36 36
37/* notify returns true if the key seems ok */ 37/* notify returns true if the key seems ok */
38typedef bool (*keysig_notify_fn_t)(void *user, uint8_t key[NWZ_KEY_SIZE], 38typedef bool (*keysig_notify_fn_t)(void *user, uint8_t key[NWZ_KEY_SIZE],
39 uint8_t sig[NWZ_SIG_SIZE]); 39 uint8_t sig[NWZ_SIG_SIZE]);
40/* returns true if a key was accepted by notify */
41typedef bool (*keysig_search_fn_t)(uint8_t *cipher, keysig_notify_fn_t notify, void *user);
42 40
43struct keysig_search_desc_t 41struct keysig_search_desc_t
44{ 42{
45 const char *name; 43 const char *name;
46 const char *comment; 44 const char *comment;
47 keysig_search_fn_t fn;
48}; 45};
49 46
50struct keysig_search_desc_t keysig_search_desc[KEYSIG_SEARCH_LAST]; 47struct keysig_search_desc_t keysig_search_desc[KEYSIG_SEARCH_LAST];
51 48
49bool keysig_search(int method, uint8_t *enc_buf, size_t buf_sz,
50 keysig_notify_fn_t notify, void *user, int nr_threads);
51
52#endif /* __keysig_search_h__ */ 52#endif /* __keysig_search_h__ */
diff --git a/utils/nwztools/upgtools/mg.cpp b/utils/nwztools/upgtools/mg.cpp
index 21659ff3cf..f02b67375a 100644
--- a/utils/nwztools/upgtools/mg.cpp
+++ b/utils/nwztools/upgtools/mg.cpp
@@ -28,24 +28,23 @@
28using namespace CryptoPP; 28using namespace CryptoPP;
29namespace 29namespace
30{ 30{
31 ECB_Mode< DES >::Decryption g_dec;
32 ECB_Mode< DES >::Encryption g_enc;
33
34 inline int dec_des_ecb(void *in, int size, void *out, uint8_t *key) 31 inline int dec_des_ecb(void *in, int size, void *out, uint8_t *key)
35 { 32 {
33 ECB_Mode< DES >::Decryption dec;
36 if(size % 8) 34 if(size % 8)
37 return 42; 35 return 42;
38 g_dec.SetKey(key, 8); 36 dec.SetKey(key, 8);
39 g_dec.ProcessData((byte*)out, (byte*)in, size); 37 dec.ProcessData((byte*)out, (byte*)in, size);
40 return 0; 38 return 0;
41 } 39 }
42 40
43 inline int enc_des_ecb(void *in, int size, void *out, uint8_t *key) 41 inline int enc_des_ecb(void *in, int size, void *out, uint8_t *key)
44 { 42 {
43 ECB_Mode< DES >::Encryption enc;
45 if(size % 8) 44 if(size % 8)
46 return 42; 45 return 42;
47 g_enc.SetKey(key, 8); 46 enc.SetKey(key, 8);
48 g_enc.ProcessData((byte*)out, (byte*)in, size); 47 enc.ProcessData((byte*)out, (byte*)in, size);
49 return 0; 48 return 0;
50 } 49 }
51} 50}
diff --git a/utils/nwztools/upgtools/misc.c b/utils/nwztools/upgtools/misc.c
index 00832cd585..108235e7fd 100644
--- a/utils/nwztools/upgtools/misc.c
+++ b/utils/nwztools/upgtools/misc.c
@@ -31,7 +31,6 @@ char RED[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '1', 0x6d, '\0' };
31char GREEN[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '2', 0x6d, '\0' }; 31char GREEN[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '2', 0x6d, '\0' };
32char YELLOW[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '3', 0x6d, '\0' }; 32char YELLOW[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '3', 0x6d, '\0' };
33char BLUE[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '4', 0x6d, '\0' }; 33char BLUE[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '4', 0x6d, '\0' };
34char WHITE[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '7', 0x6d, '\0' };
35 34
36static bool g_color_enable = true; 35static bool g_color_enable = true;
37 36
diff --git a/utils/nwztools/upgtools/misc.h b/utils/nwztools/upgtools/misc.h
index 4e8294f1ee..96666a2eff 100644
--- a/utils/nwztools/upgtools/misc.h
+++ b/utils/nwztools/upgtools/misc.h
@@ -34,7 +34,7 @@
34 34
35typedef char color_t[]; 35typedef char color_t[];
36 36
37extern color_t OFF, GREY, RED, GREEN, YELLOW, BLUE, WHITE; 37extern color_t OFF, GREY, RED, GREEN, YELLOW, BLUE;
38void *xmalloc(size_t s); 38void *xmalloc(size_t s);
39void color(color_t c); 39void color(color_t c);
40void enable_color(bool enable); 40void enable_color(bool enable);
diff --git a/utils/nwztools/upgtools/upgtool.c b/utils/nwztools/upgtools/upgtool.c
index 065cede63c..3a8cf6174b 100644
--- a/utils/nwztools/upgtools/upgtool.c
+++ b/utils/nwztools/upgtools/upgtool.c
@@ -47,6 +47,7 @@ static int g_model_index = -1;
47static char *g_kas = NULL; 47static char *g_kas = NULL;
48static char *g_key = NULL; 48static char *g_key = NULL;
49static char *g_sig = NULL; 49static char *g_sig = NULL;
50static int g_nr_threads = 1;
50 51
51enum keysig_search_method_t g_keysig_search = KEYSIG_SEARCH_NONE; 52enum keysig_search_method_t g_keysig_search = KEYSIG_SEARCH_NONE;
52 53
@@ -74,6 +75,18 @@ struct nwz_model_t
74 char *sig; 75 char *sig;
75}; 76};
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
77struct upg_md5_t 90struct upg_md5_t
78{ 91{
79 uint8_t md5[16]; 92 uint8_t md5[16];
@@ -81,7 +94,7 @@ struct upg_md5_t
81 94
82struct upg_header_t 95struct upg_header_t
83{ 96{
84 char sig[NWZ_SIG_SIZE]; 97 uint8_t sig[NWZ_SIG_SIZE];
85 uint32_t nr_files; 98 uint32_t nr_files;
86 uint32_t pad; // make sure structure size is a multiple of 8 99 uint32_t pad; // make sure structure size is a multiple of 8
87} __attribute__((packed)); 100} __attribute__((packed));
@@ -166,6 +179,7 @@ struct nwz_model_t g_model_list[] =
166 /* The following keys were obtained by brute forcing firmware upgrades, 179 /* The following keys were obtained by brute forcing firmware upgrades,
167 * someone with a device needs to confirm that they work */ 180 * someone with a device needs to confirm that they work */
168 { "nw-a82x", HAS_KEY | HAS_SIG, "", "4df06482", "07fa0b6e" }, 181 { "nw-a82x", HAS_KEY | HAS_SIG, "", "4df06482", "07fa0b6e" },
182 { "nwz-a1x", HAS_KEY | HAS_SIG, "", "ec2888e2", "f62ced8a" },
169}; 183};
170 184
171static int digit_value(char c) 185static int digit_value(char c)
@@ -286,7 +300,8 @@ static int get_key_and_sig(bool is_extract, void *encrypted_hdr)
286 { 300 {
287 cprintf(BLUE, "keysig Search\n"); 301 cprintf(BLUE, "keysig Search\n");
288 cprintf_field(" Method: ", "%s\n", keysig_search_desc[g_keysig_search].name); 302 cprintf_field(" Method: ", "%s\n", keysig_search_desc[g_keysig_search].name);
289 bool ok = keysig_search_desc[g_keysig_search].fn(encrypted_hdr, &upg_notify_keysig, keysig); 303 bool ok = keysig_search(g_keysig_search, encrypted_hdr, 8,
304 &upg_notify_keysig, keysig, g_nr_threads);
290 cprintf(GREEN, " Result: "); 305 cprintf(GREEN, " Result: ");
291 cprintf(ok ? YELLOW : RED, "%s\n", ok ? "Key found" : "No key found"); 306 cprintf(ok ? YELLOW : RED, "%s\n", ok ? "Key found" : "No key found");
292 if(!ok) 307 if(!ok)
@@ -576,6 +591,7 @@ static void usage(void)
576 printf(" -c/--no-color\t\tDisable color output\n"); 591 printf(" -c/--no-color\t\tDisable color output\n");
577 printf(" -m/--model <model>\tSelect model (or ? to list them)\n"); 592 printf(" -m/--model <model>\tSelect model (or ? to list them)\n");
578 printf(" -l/--search <method>\tTry to find the keysig (implies -e)\n"); 593 printf(" -l/--search <method>\tTry to find the keysig (implies -e)\n");
594 printf(" -t/--threads <nr>\tSpecify number of threads to find the keysig\n");
579 printf(" -a/--kas <kas>\tForce KAS\n"); 595 printf(" -a/--kas <kas>\tForce KAS\n");
580 printf(" -k/--key <key>\tForce key\n"); 596 printf(" -k/--key <key>\tForce key\n");
581 printf(" -s/--sig <sig>\tForce sig\n"); 597 printf(" -s/--sig <sig>\tForce sig\n");
@@ -594,7 +610,7 @@ int main(int argc, char **argv)
594 610
595 if(argc <= 1) 611 if(argc <= 1)
596 usage(); 612 usage();
597 613
598 while(1) 614 while(1)
599 { 615 {
600 static struct option long_options[] = 616 static struct option long_options[] =
@@ -610,10 +626,11 @@ int main(int argc, char **argv)
610 {"sig", required_argument, 0, 's'}, 626 {"sig", required_argument, 0, 's'},
611 {"extract", no_argument, 0, 'e'}, 627 {"extract", no_argument, 0, 'e'},
612 {"create", no_argument, 0 ,'c'}, 628 {"create", no_argument, 0 ,'c'},
629 {"threads", required_argument, 0, 't'},
613 {0, 0, 0, 0} 630 {0, 0, 0, 0}
614 }; 631 };
615 632
616 int c = getopt_long(argc, argv, "?dnfo:m:l:a:k:s:ec", long_options, NULL); 633 int c = getopt_long(argc, argv, "?dnfo:m:l:a:k:s:ect:", long_options, NULL);
617 if(c == -1) 634 if(c == -1)
618 break; 635 break;
619 switch(c) 636 switch(c)
@@ -665,6 +682,14 @@ int main(int argc, char **argv)
665 case 'c': 682 case 'c':
666 create = true; 683 create = true;
667 break; 684 break;
685 case 't':
686 g_nr_threads = strtol(optarg, NULL, 0);
687 if(g_nr_threads < 1 || g_nr_threads > 128)
688 {
689 cprintf(GREY, "Invalid number of threads\n");
690 return 1;
691 }
692 break;
668 default: 693 default:
669 abort(); 694 abort();
670 } 695 }