blob: 2f234d635852d276c912f5df727f508ffeb31b48 [file] [log] [blame]
Amaury Poulycb09e362012-11-03 02:16:01 +01001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2012 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 "keysig_search.h"
22#include "misc.h"
23#include "mg.h"
24#include <string.h>
Amaury Poulycf82f202016-08-30 17:19:30 +100025#include <stdio.h>
Amaury Pouly37f95f62016-10-27 23:06:16 +020026#include <stdlib.h>
27#include <pthread.h>
Amaury Pouly5cfd4a52017-01-04 16:35:38 +010028#include <stdbool.h>
29
30#define cprintf(col, ...) do {color(col); printf(__VA_ARGS__); }while(0)
Amaury Pouly37f95f62016-10-27 23:06:16 +020031
32/** Generic search code */
33
34/* The generator sends chunks to the workers. The exact type of chunks depends
35 * on the method used. */
36static struct
37{
38 pthread_mutex_t mutex; /* mutex for the whole structure */
39 pthread_cond_t avail_cond; /* condition to signal available or stop */
40 pthread_cond_t req_cond; /* condition to signal request or stop */
41 bool stop; /* if true, stop searcg */
42 void *chunk; /* pointer to chunk (NULL if not available) */
43 size_t chunk_sz; /* chunk size */
44}g_producer;
45
46/* init producer */
47static void producer_init(void)
48{
49 pthread_cond_init(&g_producer.avail_cond, NULL);
50 pthread_cond_init(&g_producer.req_cond, NULL);
51 pthread_mutex_init(&g_producer.mutex, NULL);
52 g_producer.stop = false;
53 g_producer.chunk = NULL;
54 g_producer.chunk_sz = 0;
55}
56
57/* consumer get: called by worker to get a new chunk, return NULL to stop */
58static void *consumer_get(size_t *sz)
59{
60 pthread_mutex_lock(&g_producer.mutex);
61 /* loop until stop or new chunk */
62 while(true)
63 {
64 /* stop if requested */
65 if(g_producer.stop)
66 {
67 pthread_mutex_unlock(&g_producer.mutex);
68 return NULL;
69 }
70 if(g_producer.chunk)
71 break;
72 /* request a new chunk */
73 pthread_cond_signal(&g_producer.req_cond);
74 /* wait for availability */
75 pthread_cond_wait(&g_producer.avail_cond, &g_producer.mutex);
76 }
77 void *c = g_producer.chunk;
78 if(sz)
79 *sz = g_producer.chunk_sz;
80 g_producer.chunk = NULL;
81 pthread_mutex_unlock(&g_producer.mutex);
82 /* request a new chunk, so that if other consumers are waiting, the producer
83 * will also wake them up */
84 pthread_cond_signal(&g_producer.req_cond);
85 return c;
86}
87
88/* stop: called by worker to stop the search */
89static void consumer_stop(void)
90{
91 pthread_mutex_lock(&g_producer.mutex);
92 /* set stop */
93 g_producer.stop = true;
94 /* wake up everyone */
95 pthread_cond_broadcast(&g_producer.req_cond);
96 pthread_cond_broadcast(&g_producer.avail_cond);
97 pthread_mutex_unlock(&g_producer.mutex);
98}
99
100/* producer yield: called by generator to give a new chunk, return true to stop */
101static bool producer_yield(void *chunk, size_t sz)
102{
103 pthread_mutex_lock(&g_producer.mutex);
104 /* wait until stop or request */
105 while(true)
106 {
107 /* stop if requested */
108 if(g_producer.stop)
109 {
110 pthread_mutex_unlock(&g_producer.mutex);
111 return true;
112 }
113 /* if the chunk is empty, fill it */
114 if(g_producer.chunk == NULL)
115 break;
116 /* otherwise wait for request */
117 pthread_cond_wait(&g_producer.req_cond, &g_producer.mutex);
118 }
119 g_producer.chunk = malloc(sz);
120 memcpy(g_producer.chunk, chunk, sz);
121 g_producer.chunk_sz = sz;
122 /* signal availability */
123 pthread_cond_signal(&g_producer.avail_cond);
124 pthread_mutex_unlock(&g_producer.mutex);
125 return false;
126}
127
128static void producer_stop(void)
129{
130 pthread_mutex_lock(&g_producer.mutex);
131 /* if we are not already stopping and there is a chunk still waiting to
132 * be consumed, wait until next request */
133 if(!g_producer.stop && g_producer.chunk)
134 pthread_cond_wait(&g_producer.req_cond, &g_producer.mutex);
135 /* set stop */
136 g_producer.stop = true;
137 /* wake up everyone */
138 pthread_cond_broadcast(&g_producer.avail_cond);
139 pthread_mutex_unlock(&g_producer.mutex);
140}
Amaury Poulycb09e362012-11-03 02:16:01 +0100141
Amaury Poulycf82f202016-08-30 17:19:30 +1000142/* Key search methods
143 *
144 * This code tries to find the key and signature of a device using an upgrade
145 * file. It more or less relies on brute force and makes the following assumptions.
146 * It assumes the key and the signature are hexadecimal strings (it appears to be
147 * true thus far). The code lists all possible keys and decrypts the first
148 * 8 bytes of the file. If the decrypted signature happens to be an hex string,
149 * the code reports the key and signature as potentially valid. Note that some
150 * key/sig pairs may not be valid but since the likelyhood of decrypting a
151 * random 8-byte sequence using an hex string key and to produce an hex string
152 * is very small, there should be almost no false positive.
153 *
154 * Since the key is supposedly random, the code starts by looking at "balanced"
155 * keys: keys with slightly more digits (0-9) than letters (a-f) and then moving
156 * towards very unbalanced strings (only digits or only letters).
157 */
Amaury Poulycb09e362012-11-03 02:16:01 +0100158
Amaury Pouly37f95f62016-10-27 23:06:16 +0200159static struct
160{
161 pthread_mutex_t mutex;
162 uint8_t *enc_buf;
163 size_t enc_buf_sz;
164 bool found_keysig;
165 uint8_t key[NWZ_KEY_SIZE]; /* result */
166 uint8_t sig[NWZ_SIG_SIZE]; /* result */
167}g_keysig_search;
168
Amaury Poulycb09e362012-11-03 02:16:01 +0100169static bool is_hex[256];
Amaury Pouly5cfd4a52017-01-04 16:35:38 +0100170static bool is_alnum[256];
Amaury Poulycb09e362012-11-03 02:16:01 +0100171static bool is_init = false;
Amaury Poulycb09e362012-11-03 02:16:01 +0100172
173static void keysig_search_init()
174{
175 if(is_init) return;
176 is_init = true;
177 memset(is_hex, 0, sizeof(is_hex));
178 for(int i = '0'; i <= '9'; i++)
Amaury Pouly5cfd4a52017-01-04 16:35:38 +0100179 {
180 is_alnum[i] = true;
Amaury Poulycb09e362012-11-03 02:16:01 +0100181 is_hex[i] = true;
Amaury Pouly5cfd4a52017-01-04 16:35:38 +0100182 }
Amaury Poulycb09e362012-11-03 02:16:01 +0100183 for(int i = 'a'; i <= 'f'; i++)
Amaury Poulycb09e362012-11-03 02:16:01 +0100184 is_hex[i] = true;
Amaury Pouly5cfd4a52017-01-04 16:35:38 +0100185 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;
Amaury Poulycb09e362012-11-03 02:16:01 +0100191}
192
Amaury Pouly5cfd4a52017-01-04 16:35:38 +0100193static bool hex_validate_sig(uint8_t *arr)
Amaury Poulycb09e362012-11-03 02:16:01 +0100194{
195 for(int i = 0; i < 8; i++)
196 if(!is_hex[arr[i]])
197 return false;
198 return true;
199}
200
Amaury Pouly5cfd4a52017-01-04 16:35:38 +0100201static 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
Amaury Pouly37f95f62016-10-27 23:06:16 +0200209struct upg_header_t
Amaury Poulycb09e362012-11-03 02:16:01 +0100210{
Amaury Pouly37f95f62016-10-27 23:06:16 +0200211 uint8_t sig[NWZ_SIG_SIZE];
212 uint32_t nr_files;
213 uint32_t pad; // make sure structure size is a multiple of 8
214} __attribute__((packed));
Amaury Poulycf82f202016-08-30 17:19:30 +1000215
Amaury Pouly5cfd4a52017-01-04 16:35:38 +0100216typedef bool (*sig_validate_fn_t)(uint8_t *key);
217
218static bool check_key(uint8_t key[NWZ_KEY_SIZE], sig_validate_fn_t validate)
Amaury Poulycb09e362012-11-03 02:16:01 +0100219{
Amaury Pouly37f95f62016-10-27 23:06:16 +0200220 struct upg_header_t hdr;
221 mg_decrypt_fw(g_keysig_search.enc_buf, sizeof(hdr.sig), (void *)&hdr, key);
Amaury Pouly5cfd4a52017-01-04 16:35:38 +0100222 if(validate(hdr.sig))
Amaury Poulycb09e362012-11-03 02:16:01 +0100223 {
Amaury Pouly37f95f62016-10-27 23:06:16 +0200224 /* the signature looks correct, so decrypt the header futher to be sure */
225 mg_decrypt_fw(g_keysig_search.enc_buf, sizeof(hdr), (void *)&hdr, key);
226 /* we expect the number of files to be small and the padding to be 0 */
227 if(hdr.nr_files == 0 || hdr.nr_files > 10 || hdr.pad != 0)
228 return false;
229 cprintf(RED, " Found key: %.8s (sig=%.8s, nr_files=%u)\n", key, hdr.sig, (unsigned)hdr.nr_files);
230 pthread_mutex_lock(&g_keysig_search.mutex);
231 g_keysig_search.found_keysig = true;
232 memcpy(g_keysig_search.key, key, NWZ_KEY_SIZE);
233 memcpy(g_keysig_search.sig, hdr.sig, NWZ_SIG_SIZE);
234 pthread_mutex_unlock(&g_keysig_search.mutex);
235 consumer_stop();
236 return true;
Amaury Poulycb09e362012-11-03 02:16:01 +0100237 }
238 return false;
239}
240
Amaury Pouly37f95f62016-10-27 23:06:16 +0200241/** Hex search */
242
243struct hex_chunk_t
Amaury Poulycb09e362012-11-03 02:16:01 +0100244{
Amaury Pouly37f95f62016-10-27 23:06:16 +0200245 uint8_t key[NWZ_KEY_SIZE]; /* partially pre-filled key */
Amaury Pouly5cfd4a52017-01-04 16:35:38 +0100246 bool upper_case; /* allow upper case in letters */
Amaury Pouly37f95f62016-10-27 23:06:16 +0200247 int pos;
248 int rem_letters;
249 int rem_digits;
250};
251
252static bool hex_rec(bool producer, struct hex_chunk_t *ch)
253{
254 /* we list the first 4 pos in generator, and remaining 4 in workers */
255 if(producer && ch->pos == 4)
256 {
257 //printf("yield(%.8s,%d,%d,%d)\n", ch->key, ch->pos, ch->rem_digits, ch->rem_letters);
258 return producer_yield(ch, sizeof(struct hex_chunk_t));
259 }
260 /* filled the key ? */
261 if(!producer && ch->pos == NWZ_KEY_SIZE)
Amaury Pouly5cfd4a52017-01-04 16:35:38 +0100262 return check_key(ch->key, hex_validate_sig);
Amaury Pouly37f95f62016-10-27 23:06:16 +0200263 /* list next possibilities
264 *
265 * NOTE (42) Since the cipher is DES, the key is actually 56-bit: the least
266 * significant bit of each byte is an (unused) parity bit. We thus only
267 * generate keys where the least significant bit is 0. */
268 int p = ch->pos++;
Amaury Pouly99cc8f82017-09-19 15:30:37 +0200269 int step = (p % 2) ? 2 : 1; // skip significant bit at positions 1, 3, 5 and 7
Amaury Pouly37f95f62016-10-27 23:06:16 +0200270 if(ch->rem_digits > 0)
271 {
272 ch->rem_digits--;
273 /* NOTE (42) */
Amaury Pouly99cc8f82017-09-19 15:30:37 +0200274 for(int i = '0'; i <= '9'; i += step)
Amaury Pouly37f95f62016-10-27 23:06:16 +0200275 {
276 ch->key[p] = i;
277 if(hex_rec(producer, ch))
278 return true;
279 }
280 ch->rem_digits++;
281 }
282 if(ch->rem_letters > 0)
283 {
284 ch->rem_letters--;
285 /* NOTE (42) */
Amaury Pouly99cc8f82017-09-19 15:30:37 +0200286 for(int i = 'a'; i <= 'f'; i += step)
Amaury Pouly37f95f62016-10-27 23:06:16 +0200287 {
288 ch->key[p] = i;
289 if(hex_rec(producer, ch))
290 return true;
291 }
Amaury Pouly5cfd4a52017-01-04 16:35:38 +0100292 if(ch->upper_case)
293 {
Amaury Pouly99cc8f82017-09-19 15:30:37 +0200294 for(int i = 'A'; i <= 'F'; i += step)
Amaury Pouly5cfd4a52017-01-04 16:35:38 +0100295 {
296 ch->key[p] = i;
297 if(hex_rec(producer, ch))
298 return true;
299 }
300 }
Amaury Pouly37f95f62016-10-27 23:06:16 +0200301 ch->rem_letters++;
302 }
303 ch->pos--;
304 return false;
Amaury Poulycb09e362012-11-03 02:16:01 +0100305}
306
Amaury Pouly37f95f62016-10-27 23:06:16 +0200307static void *hex_worker(void *arg)
Amaury Poulycb09e362012-11-03 02:16:01 +0100308{
Amaury Pouly37f95f62016-10-27 23:06:16 +0200309 (void) arg;
310 while(true)
311 {
312 struct hex_chunk_t *ch = consumer_get(NULL);
313 if(ch == NULL)
314 break;
315 hex_rec(false, ch);
316 }
317 return NULL;
318}
319
Amaury Pouly5cfd4a52017-01-04 16:35:38 +0100320static bool hex_producer_list(bool upper_case, int nr_digits, int nr_letters)
Amaury Pouly37f95f62016-10-27 23:06:16 +0200321{
322 struct hex_chunk_t ch;
323 cprintf(BLUE, " Listing keys with %d letters and %d digits\n", nr_letters,
324 nr_digits);
325 memset(ch.key, ' ', 8);
326 ch.pos = 0;
Amaury Pouly5cfd4a52017-01-04 16:35:38 +0100327 ch.upper_case = upper_case;
Amaury Pouly37f95f62016-10-27 23:06:16 +0200328 ch.rem_letters = nr_letters;
329 ch.rem_digits = nr_digits;
330 return hex_rec(true, &ch);
331}
332
333void *hex_producer(void *arg)
334{
335 (void) arg;
Amaury Poulycf82f202016-08-30 17:19:30 +1000336 // sorted by probability:
Amaury Pouly5cfd4a52017-01-04 16:35:38 +0100337 bool stop = hex_producer_list(false, 5, 3) // 5 digits, 3 letters: 0.281632
338 || hex_producer_list(false, 6, 2) // 6 digits, 2 letters: 0.234693
339 || hex_producer_list(false, 4, 4) // 4 digits, 4 letters: 0.211224
340 || hex_producer_list(false, 7, 1) // 7 digits, 1 letters: 0.111759
341 || hex_producer_list(false, 3, 5) // 3 digits, 5 letters: 0.101388
342 || hex_producer_list(false, 2, 6) // 2 digits, 6 letters: 0.030416
343 || hex_producer_list(false, 8, 0) // 8 digits, 0 letters: 0.023283
344 || hex_producer_list(false, 1, 7) // 1 digits, 7 letters: 0.005214
345 || hex_producer_list(false, 0, 8);// 0 digits, 8 letters: 0.000391
Amaury Pouly37f95f62016-10-27 23:06:16 +0200346 if(!stop)
347 producer_stop();
348 return NULL;
Amaury Poulycb09e362012-11-03 02:16:01 +0100349}
350
Amaury Pouly5cfd4a52017-01-04 16:35:38 +0100351void *hex_producer_up(void *arg)
352{
353 (void) arg;
354 // sorted by probability:
355 // TODO sort
356 bool stop = hex_producer_list(true, 5, 3) // 5 digits, 3 letters: 0.281632
357 || hex_producer_list(true, 6, 2) // 6 digits, 2 letters: 0.234693
358 || hex_producer_list(true, 4, 4) // 4 digits, 4 letters: 0.211224
359 || hex_producer_list(true, 7, 1) // 7 digits, 1 letters: 0.111759
360 || hex_producer_list(true, 3, 5) // 3 digits, 5 letters: 0.101388
361 || hex_producer_list(true, 2, 6) // 2 digits, 6 letters: 0.030416
362 || hex_producer_list(true, 8, 0) // 8 digits, 0 letters: 0.023283
363 || hex_producer_list(true, 1, 7) // 1 digits, 7 letters: 0.005214
364 || hex_producer_list(true, 0, 8);// 0 digits, 8 letters: 0.000391
365 if(!stop)
366 producer_stop();
367 return NULL;
368}
369
370/** Alphanumeric search */
371
372struct alnum_chunk_t
373{
374 uint8_t key[NWZ_KEY_SIZE]; /* partially pre-filled key */
375 int pos;
376};
377
378static bool alnum_rec(bool producer, struct alnum_chunk_t *ch)
379{
380 /* we list the first 5 pos in generator, and remaining 3 in workers */
381 if(producer && ch->pos == 4)
382 {
Amaury Pouly99cc8f82017-09-19 15:30:37 +0200383 //printf("yield(%.8s,%d)\n", ch->key, ch->pos);
Amaury Pouly5cfd4a52017-01-04 16:35:38 +0100384 return producer_yield(ch, sizeof(struct alnum_chunk_t));
385 }
386 /* filled the key ? */
387 if(!producer && ch->pos == NWZ_KEY_SIZE)
388 return check_key(ch->key, alnum_validate_sig);
389 /* list next possibilities
390 *
391 * NOTE (42) Since the cipher is DES, the key is actually 56-bit: the least
392 * significant bit of each byte is an (unused) parity bit. We thus only
393 * generate keys where the least significant bit is 0. */
394 int p = ch->pos++;
395 /* NOTE (42) */
Amaury Pouly99cc8f82017-09-19 15:30:37 +0200396 int step = (p % 2) ? 2 : 1; // skip significant bit at positions 1, 3, 5 and 7
397 for(int i = '0'; i <= '9'; i += step)
Amaury Pouly5cfd4a52017-01-04 16:35:38 +0100398 {
399 ch->key[p] = i;
400 if(alnum_rec(producer, ch))
401 return true;
402 }
403 /* NOTE (42) */
Amaury Pouly99cc8f82017-09-19 15:30:37 +0200404 for(int i = 'a'; i <= 'z'; i += step)
Amaury Pouly5cfd4a52017-01-04 16:35:38 +0100405 {
406 ch->key[p] = i;
407 if(alnum_rec(producer, ch))
408 return true;
409 }
410 ch->pos--;
411 return false;
412}
413
414static void *alnum_worker(void *arg)
415{
416 (void) arg;
417 while(true)
418 {
419 struct alnum_chunk_t *ch = consumer_get(NULL);
420 if(ch == NULL)
421 break;
422 alnum_rec(false, ch);
423 }
424 return NULL;
425}
426
427void *alnum_producer(void *arg)
428{
429 (void) arg;
430 struct alnum_chunk_t ch;
431 cprintf(BLUE, " Listing alphanumeric keys\n");
432 memset(ch.key, ' ', 8);
433 ch.pos = 0;
434 if(!alnum_rec(true, &ch))
435 producer_stop();
436 return NULL;
437}
438
Amaury Pouly37f95f62016-10-27 23:06:16 +0200439typedef void *(*routine_t)(void *);
440
441bool keysig_search(int method, uint8_t *enc_buf, size_t buf_sz,
442 keysig_notify_fn_t notify, void *user, int nr_threads)
Amaury Poulycb09e362012-11-03 02:16:01 +0100443{
Amaury Pouly37f95f62016-10-27 23:06:16 +0200444 /* init producer */
445 producer_init();
446 /* init search */
Amaury Poulycb09e362012-11-03 02:16:01 +0100447 keysig_search_init();
Amaury Pouly37f95f62016-10-27 23:06:16 +0200448 pthread_mutex_init(&g_keysig_search.mutex, NULL);
449 g_keysig_search.enc_buf = enc_buf;
450 g_keysig_search.enc_buf_sz = buf_sz;
451 g_keysig_search.found_keysig = false;
452 /* get methods */
453 routine_t worker_fn = NULL;
454 routine_t producer_fn = NULL;
Amaury Pouly5cfd4a52017-01-04 16:35:38 +0100455 if(method == KEYSIG_SEARCH_XDIGITS)
Amaury Pouly37f95f62016-10-27 23:06:16 +0200456 {
457 worker_fn = hex_worker;
458 producer_fn = hex_producer;
459 }
Amaury Pouly5cfd4a52017-01-04 16:35:38 +0100460 else if(method == KEYSIG_SEARCH_XDIGITS_UP)
461 {
462 worker_fn = hex_worker;
463 producer_fn = hex_producer_up;
464 }
465 else if(method == KEYSIG_SEARCH_ALNUM)
466 {
467 worker_fn = alnum_worker;
468 producer_fn = alnum_producer;
469 }
470 else
471 {
472 printf("Invalid method\n");
473 return false;
474 }
Amaury Pouly37f95f62016-10-27 23:06:16 +0200475 /* create workers */
476 pthread_t *worker = malloc(sizeof(pthread_t) * nr_threads);
477 pthread_t producer;
478 for(int i = 0; i < nr_threads; i++)
479 pthread_create(&worker[i], NULL, worker_fn, NULL);
480 pthread_create(&producer, NULL, producer_fn, NULL);
481 /* wait for all threads */
482 pthread_join(producer, NULL);
483 for(int i = 0; i < nr_threads; i++)
484 pthread_join(worker[i], NULL);
485 free(worker);
486 if(g_keysig_search.found_keysig)
487 notify(user, g_keysig_search.key, g_keysig_search.sig);
488 return g_keysig_search.found_keysig;
Amaury Poulycb09e362012-11-03 02:16:01 +0100489}
490
491struct keysig_search_desc_t keysig_search_desc[KEYSIG_SEARCH_LAST] =
492{
493 [KEYSIG_SEARCH_NONE] =
494 {
495 .name = "none",
Amaury Poulycb09e362012-11-03 02:16:01 +0100496 .comment = "don't use",
497 },
Amaury Pouly5cfd4a52017-01-04 16:35:38 +0100498 [KEYSIG_SEARCH_XDIGITS] =
Amaury Poulycb09e362012-11-03 02:16:01 +0100499 {
Amaury Pouly5cfd4a52017-01-04 16:35:38 +0100500 .name = "xdigits",
501 .comment = "Try to find an hexadecimal string keysig"
502 },
503 [KEYSIG_SEARCH_XDIGITS_UP] =
504 {
505 .name = "xdigits-up",
506 .comment = "Try to find an hexadecimal string keysig, including upper case"
507 },
508 [KEYSIG_SEARCH_ALNUM] =
509 {
510 .name = "alnum",
511 .comment = "Try to find an alphanumeric string keysig"
Amaury Poulycb09e362012-11-03 02:16:01 +0100512 },
Amaury Poulycb09e362012-11-03 02:16:01 +0100513};