summaryrefslogtreecommitdiff
path: root/utils/nwztools/upgtools/keysig_search.c
diff options
context:
space:
mode:
Diffstat (limited to 'utils/nwztools/upgtools/keysig_search.c')
-rw-r--r--utils/nwztools/upgtools/keysig_search.c344
1 files changed, 257 insertions, 87 deletions
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};