diff options
Diffstat (limited to 'utils')
-rw-r--r-- | utils/nwztools/upgtools/keysig_search.c | 182 | ||||
-rw-r--r-- | utils/nwztools/upgtools/keysig_search.h | 4 | ||||
-rw-r--r-- | utils/nwztools/upgtools/upgtool.c | 4 |
3 files changed, 169 insertions, 21 deletions
diff --git a/utils/nwztools/upgtools/keysig_search.c b/utils/nwztools/upgtools/keysig_search.c index 2740dc1461..51a04bb6f9 100644 --- a/utils/nwztools/upgtools/keysig_search.c +++ b/utils/nwztools/upgtools/keysig_search.c | |||
@@ -25,6 +25,9 @@ | |||
25 | #include <stdio.h> | 25 | #include <stdio.h> |
26 | #include <stdlib.h> | 26 | #include <stdlib.h> |
27 | #include <pthread.h> | 27 | #include <pthread.h> |
28 | #include <stdbool.h> | ||
29 | |||
30 | #define cprintf(col, ...) do {color(col); printf(__VA_ARGS__); }while(0) | ||
28 | 31 | ||
29 | /** Generic search code */ | 32 | /** Generic search code */ |
30 | 33 | ||
@@ -164,6 +167,7 @@ static struct | |||
164 | }g_keysig_search; | 167 | }g_keysig_search; |
165 | 168 | ||
166 | static bool is_hex[256]; | 169 | static bool is_hex[256]; |
170 | static bool is_alnum[256]; | ||
167 | static bool is_init = false; | 171 | static bool is_init = false; |
168 | 172 | ||
169 | static void keysig_search_init() | 173 | static void keysig_search_init() |
@@ -172,12 +176,21 @@ static void keysig_search_init() | |||
172 | is_init = true; | 176 | is_init = true; |
173 | memset(is_hex, 0, sizeof(is_hex)); | 177 | memset(is_hex, 0, sizeof(is_hex)); |
174 | for(int i = '0'; i <= '9'; i++) | 178 | for(int i = '0'; i <= '9'; i++) |
179 | { | ||
180 | is_alnum[i] = true; | ||
175 | is_hex[i] = true; | 181 | is_hex[i] = true; |
182 | } | ||
176 | for(int i = 'a'; i <= 'f'; i++) | 183 | for(int i = 'a'; i <= 'f'; i++) |
177 | is_hex[i] = true; | 184 | is_hex[i] = true; |
185 | for(int i = 'A'; i <= 'F'; i++) | ||
186 | is_hex[i] = true; | ||
187 | for(int i = 'a'; i <= 'z'; i++) | ||
188 | is_alnum[i] = true; | ||
189 | for(int i = 'A'; i <= 'Z'; i++) | ||
190 | is_alnum[i] = true; | ||
178 | } | 191 | } |
179 | 192 | ||
180 | static inline bool is_full_ascii(uint8_t *arr) | 193 | static bool hex_validate_sig(uint8_t *arr) |
181 | { | 194 | { |
182 | for(int i = 0; i < 8; i++) | 195 | for(int i = 0; i < 8; i++) |
183 | if(!is_hex[arr[i]]) | 196 | if(!is_hex[arr[i]]) |
@@ -185,6 +198,14 @@ static inline bool is_full_ascii(uint8_t *arr) | |||
185 | return true; | 198 | return true; |
186 | } | 199 | } |
187 | 200 | ||
201 | static bool alnum_validate_sig(uint8_t *arr) | ||
202 | { | ||
203 | for(int i = 0; i < 8; i++) | ||
204 | if(!is_alnum[arr[i]]) | ||
205 | return false; | ||
206 | return true; | ||
207 | } | ||
208 | |||
188 | struct upg_header_t | 209 | struct upg_header_t |
189 | { | 210 | { |
190 | uint8_t sig[NWZ_SIG_SIZE]; | 211 | uint8_t sig[NWZ_SIG_SIZE]; |
@@ -192,11 +213,13 @@ struct upg_header_t | |||
192 | uint32_t pad; // make sure structure size is a multiple of 8 | 213 | uint32_t pad; // make sure structure size is a multiple of 8 |
193 | } __attribute__((packed)); | 214 | } __attribute__((packed)); |
194 | 215 | ||
195 | static bool check_key(uint8_t key[NWZ_KEY_SIZE]) | 216 | typedef bool (*sig_validate_fn_t)(uint8_t *key); |
217 | |||
218 | static bool check_key(uint8_t key[NWZ_KEY_SIZE], sig_validate_fn_t validate) | ||
196 | { | 219 | { |
197 | struct upg_header_t hdr; | 220 | struct upg_header_t hdr; |
198 | mg_decrypt_fw(g_keysig_search.enc_buf, sizeof(hdr.sig), (void *)&hdr, key); | 221 | mg_decrypt_fw(g_keysig_search.enc_buf, sizeof(hdr.sig), (void *)&hdr, key); |
199 | if(is_full_ascii(hdr.sig)) | 222 | if(validate(hdr.sig)) |
200 | { | 223 | { |
201 | /* the signature looks correct, so decrypt the header futher to be sure */ | 224 | /* the signature looks correct, so decrypt the header futher to be sure */ |
202 | mg_decrypt_fw(g_keysig_search.enc_buf, sizeof(hdr), (void *)&hdr, key); | 225 | mg_decrypt_fw(g_keysig_search.enc_buf, sizeof(hdr), (void *)&hdr, key); |
@@ -220,6 +243,7 @@ static bool check_key(uint8_t key[NWZ_KEY_SIZE]) | |||
220 | struct hex_chunk_t | 243 | struct hex_chunk_t |
221 | { | 244 | { |
222 | uint8_t key[NWZ_KEY_SIZE]; /* partially pre-filled key */ | 245 | uint8_t key[NWZ_KEY_SIZE]; /* partially pre-filled key */ |
246 | bool upper_case; /* allow upper case in letters */ | ||
223 | int pos; | 247 | int pos; |
224 | int rem_letters; | 248 | int rem_letters; |
225 | int rem_digits; | 249 | int rem_digits; |
@@ -235,7 +259,7 @@ static bool hex_rec(bool producer, struct hex_chunk_t *ch) | |||
235 | } | 259 | } |
236 | /* filled the key ? */ | 260 | /* filled the key ? */ |
237 | if(!producer && ch->pos == NWZ_KEY_SIZE) | 261 | if(!producer && ch->pos == NWZ_KEY_SIZE) |
238 | return check_key(ch->key); | 262 | return check_key(ch->key, hex_validate_sig); |
239 | /* list next possibilities | 263 | /* list next possibilities |
240 | * | 264 | * |
241 | * NOTE (42) Since the cipher is DES, the key is actually 56-bit: the least | 265 | * NOTE (42) Since the cipher is DES, the key is actually 56-bit: the least |
@@ -264,6 +288,15 @@ static bool hex_rec(bool producer, struct hex_chunk_t *ch) | |||
264 | if(hex_rec(producer, ch)) | 288 | if(hex_rec(producer, ch)) |
265 | return true; | 289 | return true; |
266 | } | 290 | } |
291 | if(ch->upper_case) | ||
292 | { | ||
293 | for(int i = 'A'; i <= 'F'; i += 2) | ||
294 | { | ||
295 | ch->key[p] = i; | ||
296 | if(hex_rec(producer, ch)) | ||
297 | return true; | ||
298 | } | ||
299 | } | ||
267 | ch->rem_letters++; | 300 | ch->rem_letters++; |
268 | } | 301 | } |
269 | ch->pos--; | 302 | ch->pos--; |
@@ -283,13 +316,14 @@ static void *hex_worker(void *arg) | |||
283 | return NULL; | 316 | return NULL; |
284 | } | 317 | } |
285 | 318 | ||
286 | static bool hex_producer_list(int nr_digits, int nr_letters) | 319 | static bool hex_producer_list(bool upper_case, int nr_digits, int nr_letters) |
287 | { | 320 | { |
288 | struct hex_chunk_t ch; | 321 | struct hex_chunk_t ch; |
289 | cprintf(BLUE, " Listing keys with %d letters and %d digits\n", nr_letters, | 322 | cprintf(BLUE, " Listing keys with %d letters and %d digits\n", nr_letters, |
290 | nr_digits); | 323 | nr_digits); |
291 | memset(ch.key, ' ', 8); | 324 | memset(ch.key, ' ', 8); |
292 | ch.pos = 0; | 325 | ch.pos = 0; |
326 | ch.upper_case = upper_case; | ||
293 | ch.rem_letters = nr_letters; | 327 | ch.rem_letters = nr_letters; |
294 | ch.rem_digits = nr_digits; | 328 | ch.rem_digits = nr_digits; |
295 | return hex_rec(true, &ch); | 329 | return hex_rec(true, &ch); |
@@ -299,20 +333,107 @@ void *hex_producer(void *arg) | |||
299 | { | 333 | { |
300 | (void) arg; | 334 | (void) arg; |
301 | // sorted by probability: | 335 | // sorted by probability: |
302 | bool stop = hex_producer_list(5, 3) // 5 digits, 3 letters: 0.281632 | 336 | bool stop = hex_producer_list(false, 5, 3) // 5 digits, 3 letters: 0.281632 |
303 | || hex_producer_list(6, 2) // 6 digits, 2 letters: 0.234693 | 337 | || hex_producer_list(false, 6, 2) // 6 digits, 2 letters: 0.234693 |
304 | || hex_producer_list(4, 4) // 4 digits, 4 letters: 0.211224 | 338 | || hex_producer_list(false, 4, 4) // 4 digits, 4 letters: 0.211224 |
305 | || hex_producer_list(7, 1) // 7 digits, 1 letters: 0.111759 | 339 | || hex_producer_list(false, 7, 1) // 7 digits, 1 letters: 0.111759 |
306 | || hex_producer_list(3, 5) // 3 digits, 5 letters: 0.101388 | 340 | || hex_producer_list(false, 3, 5) // 3 digits, 5 letters: 0.101388 |
307 | || hex_producer_list(2, 6) // 2 digits, 6 letters: 0.030416 | 341 | || hex_producer_list(false, 2, 6) // 2 digits, 6 letters: 0.030416 |
308 | || hex_producer_list(8, 0) // 8 digits, 0 letters: 0.023283 | 342 | || hex_producer_list(false, 8, 0) // 8 digits, 0 letters: 0.023283 |
309 | || hex_producer_list(1, 7) // 1 digits, 7 letters: 0.005214 | 343 | || hex_producer_list(false, 1, 7) // 1 digits, 7 letters: 0.005214 |
310 | || hex_producer_list(0, 8);// 0 digits, 8 letters: 0.000391 | 344 | || hex_producer_list(false, 0, 8);// 0 digits, 8 letters: 0.000391 |
311 | if(!stop) | 345 | if(!stop) |
312 | producer_stop(); | 346 | producer_stop(); |
313 | return NULL; | 347 | return NULL; |
314 | } | 348 | } |
315 | 349 | ||
350 | void *hex_producer_up(void *arg) | ||
351 | { | ||
352 | (void) arg; | ||
353 | // sorted by probability: | ||
354 | // TODO sort | ||
355 | bool stop = hex_producer_list(true, 5, 3) // 5 digits, 3 letters: 0.281632 | ||
356 | || hex_producer_list(true, 6, 2) // 6 digits, 2 letters: 0.234693 | ||
357 | || hex_producer_list(true, 4, 4) // 4 digits, 4 letters: 0.211224 | ||
358 | || hex_producer_list(true, 7, 1) // 7 digits, 1 letters: 0.111759 | ||
359 | || hex_producer_list(true, 3, 5) // 3 digits, 5 letters: 0.101388 | ||
360 | || hex_producer_list(true, 2, 6) // 2 digits, 6 letters: 0.030416 | ||
361 | || hex_producer_list(true, 8, 0) // 8 digits, 0 letters: 0.023283 | ||
362 | || hex_producer_list(true, 1, 7) // 1 digits, 7 letters: 0.005214 | ||
363 | || hex_producer_list(true, 0, 8);// 0 digits, 8 letters: 0.000391 | ||
364 | if(!stop) | ||
365 | producer_stop(); | ||
366 | return NULL; | ||
367 | } | ||
368 | |||
369 | /** Alphanumeric search */ | ||
370 | |||
371 | struct alnum_chunk_t | ||
372 | { | ||
373 | uint8_t key[NWZ_KEY_SIZE]; /* partially pre-filled key */ | ||
374 | int pos; | ||
375 | }; | ||
376 | |||
377 | static bool alnum_rec(bool producer, struct alnum_chunk_t *ch) | ||
378 | { | ||
379 | /* we list the first 5 pos in generator, and remaining 3 in workers */ | ||
380 | if(producer && ch->pos == 4) | ||
381 | { | ||
382 | printf("yield(%.8s,%d)\n", ch->key, ch->pos); | ||
383 | return producer_yield(ch, sizeof(struct alnum_chunk_t)); | ||
384 | } | ||
385 | /* filled the key ? */ | ||
386 | if(!producer && ch->pos == NWZ_KEY_SIZE) | ||
387 | return check_key(ch->key, alnum_validate_sig); | ||
388 | /* list next possibilities | ||
389 | * | ||
390 | * NOTE (42) Since the cipher is DES, the key is actually 56-bit: the least | ||
391 | * significant bit of each byte is an (unused) parity bit. We thus only | ||
392 | * generate keys where the least significant bit is 0. */ | ||
393 | int p = ch->pos++; | ||
394 | /* NOTE (42) */ | ||
395 | for(int i = '0'; i <= '9'; i += 2) | ||
396 | { | ||
397 | ch->key[p] = i; | ||
398 | if(alnum_rec(producer, ch)) | ||
399 | return true; | ||
400 | } | ||
401 | /* NOTE (42) */ | ||
402 | for(int i = 'a'; i <= 'z'; i += 2) | ||
403 | { | ||
404 | ch->key[p] = i; | ||
405 | if(alnum_rec(producer, ch)) | ||
406 | return true; | ||
407 | } | ||
408 | ch->pos--; | ||
409 | return false; | ||
410 | } | ||
411 | |||
412 | static void *alnum_worker(void *arg) | ||
413 | { | ||
414 | (void) arg; | ||
415 | while(true) | ||
416 | { | ||
417 | struct alnum_chunk_t *ch = consumer_get(NULL); | ||
418 | if(ch == NULL) | ||
419 | break; | ||
420 | alnum_rec(false, ch); | ||
421 | } | ||
422 | return NULL; | ||
423 | } | ||
424 | |||
425 | void *alnum_producer(void *arg) | ||
426 | { | ||
427 | (void) arg; | ||
428 | struct alnum_chunk_t ch; | ||
429 | cprintf(BLUE, " Listing alphanumeric keys\n"); | ||
430 | memset(ch.key, ' ', 8); | ||
431 | ch.pos = 0; | ||
432 | if(!alnum_rec(true, &ch)) | ||
433 | producer_stop(); | ||
434 | return NULL; | ||
435 | } | ||
436 | |||
316 | typedef void *(*routine_t)(void *); | 437 | typedef void *(*routine_t)(void *); |
317 | 438 | ||
318 | bool keysig_search(int method, uint8_t *enc_buf, size_t buf_sz, | 439 | bool keysig_search(int method, uint8_t *enc_buf, size_t buf_sz, |
@@ -329,11 +450,26 @@ bool keysig_search(int method, uint8_t *enc_buf, size_t buf_sz, | |||
329 | /* get methods */ | 450 | /* get methods */ |
330 | routine_t worker_fn = NULL; | 451 | routine_t worker_fn = NULL; |
331 | routine_t producer_fn = NULL; | 452 | routine_t producer_fn = NULL; |
332 | if(method == KEYSIG_SEARCH_ASCII_HEX) | 453 | if(method == KEYSIG_SEARCH_XDIGITS) |
333 | { | 454 | { |
334 | worker_fn = hex_worker; | 455 | worker_fn = hex_worker; |
335 | producer_fn = hex_producer; | 456 | producer_fn = hex_producer; |
336 | } | 457 | } |
458 | else if(method == KEYSIG_SEARCH_XDIGITS_UP) | ||
459 | { | ||
460 | worker_fn = hex_worker; | ||
461 | producer_fn = hex_producer_up; | ||
462 | } | ||
463 | else if(method == KEYSIG_SEARCH_ALNUM) | ||
464 | { | ||
465 | worker_fn = alnum_worker; | ||
466 | producer_fn = alnum_producer; | ||
467 | } | ||
468 | else | ||
469 | { | ||
470 | printf("Invalid method\n"); | ||
471 | return false; | ||
472 | } | ||
337 | /* create workers */ | 473 | /* create workers */ |
338 | pthread_t *worker = malloc(sizeof(pthread_t) * nr_threads); | 474 | pthread_t *worker = malloc(sizeof(pthread_t) * nr_threads); |
339 | pthread_t producer; | 475 | pthread_t producer; |
@@ -357,9 +493,19 @@ struct keysig_search_desc_t keysig_search_desc[KEYSIG_SEARCH_LAST] = | |||
357 | .name = "none", | 493 | .name = "none", |
358 | .comment = "don't use", | 494 | .comment = "don't use", |
359 | }, | 495 | }, |
360 | [KEYSIG_SEARCH_ASCII_HEX] = | 496 | [KEYSIG_SEARCH_XDIGITS] = |
497 | { | ||
498 | .name = "xdigits", | ||
499 | .comment = "Try to find an hexadecimal string keysig" | ||
500 | }, | ||
501 | [KEYSIG_SEARCH_XDIGITS_UP] = | ||
502 | { | ||
503 | .name = "xdigits-up", | ||
504 | .comment = "Try to find an hexadecimal string keysig, including upper case" | ||
505 | }, | ||
506 | [KEYSIG_SEARCH_ALNUM] = | ||
361 | { | 507 | { |
362 | .name = "ascii-hex", | 508 | .name = "alnum", |
363 | .comment = "Try to find an hexadecimal ascii string keysig" | 509 | .comment = "Try to find an alphanumeric string keysig" |
364 | }, | 510 | }, |
365 | }; | 511 | }; |
diff --git a/utils/nwztools/upgtools/keysig_search.h b/utils/nwztools/upgtools/keysig_search.h index f419e2621c..877da2f89c 100644 --- a/utils/nwztools/upgtools/keysig_search.h +++ b/utils/nwztools/upgtools/keysig_search.h | |||
@@ -30,7 +30,9 @@ enum keysig_search_method_t | |||
30 | { | 30 | { |
31 | KEYSIG_SEARCH_NONE = 0, | 31 | KEYSIG_SEARCH_NONE = 0, |
32 | KEYSIG_SEARCH_FIRST, | 32 | KEYSIG_SEARCH_FIRST, |
33 | KEYSIG_SEARCH_ASCII_HEX = KEYSIG_SEARCH_FIRST, | 33 | KEYSIG_SEARCH_XDIGITS = KEYSIG_SEARCH_FIRST, |
34 | KEYSIG_SEARCH_XDIGITS_UP, | ||
35 | KEYSIG_SEARCH_ALNUM, | ||
34 | KEYSIG_SEARCH_LAST | 36 | KEYSIG_SEARCH_LAST |
35 | }; | 37 | }; |
36 | 38 | ||
diff --git a/utils/nwztools/upgtools/upgtool.c b/utils/nwztools/upgtools/upgtool.c index 8f75f93024..833321ec12 100644 --- a/utils/nwztools/upgtools/upgtool.c +++ b/utils/nwztools/upgtools/upgtool.c | |||
@@ -319,7 +319,7 @@ static int get_key_and_sig(bool is_extract, void *encrypted_hdr) | |||
319 | cprintf(GREY, "- select a model with a known KAS\n"); | 319 | cprintf(GREY, "- select a model with a known KAS\n"); |
320 | cprintf(GREY, "- specify an explicit KAS or key+sig\n"); | 320 | cprintf(GREY, "- specify an explicit KAS or key+sig\n"); |
321 | if(is_extract) | 321 | if(is_extract) |
322 | cprintf(GREY, "- let me try to find the keysig(slow !)\n"); | 322 | cprintf(GREY, "- let me try to find the keysig by brute force\n"); |
323 | return 1; | 323 | return 1; |
324 | } | 324 | } |
325 | 325 | ||
@@ -604,7 +604,7 @@ static void usage(void) | |||
604 | printf(" -c/--create\t\tCreate a UPG archive\n"); | 604 | printf(" -c/--create\t\tCreate a UPG archive\n"); |
605 | printf("keysig search method:\n"); | 605 | printf("keysig search method:\n"); |
606 | for(int i = KEYSIG_SEARCH_FIRST; i < KEYSIG_SEARCH_LAST; i++) | 606 | for(int i = KEYSIG_SEARCH_FIRST; i < KEYSIG_SEARCH_LAST; i++) |
607 | printf(" %s\t%s\n", keysig_search_desc[i].name, keysig_search_desc[i].comment); | 607 | printf(" %-10s\t%s\n", keysig_search_desc[i].name, keysig_search_desc[i].comment); |
608 | exit(1); | 608 | exit(1); |
609 | } | 609 | } |
610 | 610 | ||