diff options
Diffstat (limited to 'utils/nwztools')
-rw-r--r-- | utils/nwztools/upgtools/Makefile | 2 | ||||
-rw-r--r-- | utils/nwztools/upgtools/keysig_search.c | 344 | ||||
-rw-r--r-- | utils/nwztools/upgtools/keysig_search.h | 10 | ||||
-rw-r--r-- | utils/nwztools/upgtools/mg.cpp | 13 | ||||
-rw-r--r-- | utils/nwztools/upgtools/misc.c | 1 | ||||
-rw-r--r-- | utils/nwztools/upgtools/misc.h | 2 | ||||
-rw-r--r-- | utils/nwztools/upgtools/upgtool.c | 33 |
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++ | |||
5 | PROFILE= | 5 | PROFILE= |
6 | CFLAGS=-g $(PROFILE) -std=c99 -W -Wall $(DEFINES) `pkg-config --cflags openssl` `pkg-config --cflags libcrypto++` | 6 | CFLAGS=-g $(PROFILE) -std=c99 -W -Wall $(DEFINES) `pkg-config --cflags openssl` `pkg-config --cflags libcrypto++` |
7 | CXXFLAGS=-g $(PROFILE) -W -Wall $(DEFINES) `pkg-config --cflags openssl` `pkg-config --cflags libcrypto++` | 7 | CXXFLAGS=-g $(PROFILE) -W -Wall $(DEFINES) `pkg-config --cflags openssl` `pkg-config --cflags libcrypto++` |
8 | LDFLAGS=$(PROFILE) `pkg-config --libs openssl` `pkg-config --libs libcrypto++` -lcrypt | 8 | LDFLAGS=$(PROFILE) `pkg-config --libs openssl` `pkg-config --libs libcrypto++` -lcrypt -lpthread |
9 | BINS=upgtool | 9 | BINS=upgtool |
10 | 10 | ||
11 | all: $(BINS) | 11 | all: $(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. */ | ||
33 | static 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 */ | ||
44 | static 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 */ | ||
55 | static 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 */ | ||
86 | static 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 */ | ||
98 | static 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 | |||
125 | static 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 | ||
44 | static uint8_t g_cipher[8]; | 156 | static struct |
45 | static keysig_notify_fn_t g_notify; | 157 | { |
46 | static uint8_t g_key[8]; | 158 | pthread_mutex_t mutex; |
47 | static 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 | |||
48 | static bool is_hex[256]; | 166 | static bool is_hex[256]; |
49 | static bool is_init = false; | 167 | static bool is_init = false; |
50 | static uint64_t g_tot_count; | ||
51 | static uint64_t g_cur_count; | ||
52 | static int g_last_percent; | ||
53 | static int g_last_subpercent; | ||
54 | 168 | ||
55 | static void keysig_search_init() | 169 | static 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 | ||
74 | static inline bool check_stupid() | 188 | struct 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) | 195 | static 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 | ||
101 | static bool search_stupid_rec(int rem_digit, int rem_letter, int pos) | 218 | /** Hex search */ |
219 | |||
220 | struct 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 | |||
228 | static 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 | ||
126 | static bool search_stupid(int rem_digit, int rem_letter) | 273 | static 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 | ||
137 | bool keysig_search_ascii_stupid(uint8_t *cipher, keysig_notify_fn_t notify, void *user) | 286 | static 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:"); | 298 | void *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 | ||
165 | bool keysig_search_ascii_brute(uint8_t *cipher, keysig_notify_fn_t notify, void *user) | 316 | typedef void *(*routine_t)(void *); |
317 | |||
318 | bool 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 | ||
175 | struct keysig_search_desc_t keysig_search_desc[KEYSIG_SEARCH_LAST] = | 353 | struct 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 | ||
28 | enum keysig_search_method_t | 29 | enum 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 */ |
38 | typedef bool (*keysig_notify_fn_t)(void *user, uint8_t key[NWZ_KEY_SIZE], | 38 | typedef 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 */ | ||
41 | typedef bool (*keysig_search_fn_t)(uint8_t *cipher, keysig_notify_fn_t notify, void *user); | ||
42 | 40 | ||
43 | struct keysig_search_desc_t | 41 | struct 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 | ||
50 | struct keysig_search_desc_t keysig_search_desc[KEYSIG_SEARCH_LAST]; | 47 | struct keysig_search_desc_t keysig_search_desc[KEYSIG_SEARCH_LAST]; |
51 | 48 | ||
49 | bool 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 @@ | |||
28 | using namespace CryptoPP; | 28 | using namespace CryptoPP; |
29 | namespace | 29 | namespace |
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' }; | |||
31 | char GREEN[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '2', 0x6d, '\0' }; | 31 | char GREEN[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '2', 0x6d, '\0' }; |
32 | char YELLOW[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '3', 0x6d, '\0' }; | 32 | char YELLOW[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '3', 0x6d, '\0' }; |
33 | char BLUE[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '4', 0x6d, '\0' }; | 33 | char BLUE[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '4', 0x6d, '\0' }; |
34 | char WHITE[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '7', 0x6d, '\0' }; | ||
35 | 34 | ||
36 | static bool g_color_enable = true; | 35 | static 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 | ||
35 | typedef char color_t[]; | 35 | typedef char color_t[]; |
36 | 36 | ||
37 | extern color_t OFF, GREY, RED, GREEN, YELLOW, BLUE, WHITE; | 37 | extern color_t OFF, GREY, RED, GREEN, YELLOW, BLUE; |
38 | void *xmalloc(size_t s); | 38 | void *xmalloc(size_t s); |
39 | void color(color_t c); | 39 | void color(color_t c); |
40 | void enable_color(bool enable); | 40 | void 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; | |||
47 | static char *g_kas = NULL; | 47 | static char *g_kas = NULL; |
48 | static char *g_key = NULL; | 48 | static char *g_key = NULL; |
49 | static char *g_sig = NULL; | 49 | static char *g_sig = NULL; |
50 | static int g_nr_threads = 1; | ||
50 | 51 | ||
51 | enum keysig_search_method_t g_keysig_search = KEYSIG_SEARCH_NONE; | 52 | enum 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 | |||
77 | struct upg_md5_t | 90 | struct 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 | ||
82 | struct upg_header_t | 95 | struct 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 | ||
171 | static int digit_value(char c) | 185 | static 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 | } |