]>
Commit | Line | Data |
---|---|---|
d03fb293 OM |
1 | /* |
2 | * libopenemv - a library to work with EMV family of smart cards | |
3 | * Copyright (C) 2012, 2015 Dmitry Eremin-Solenikov | |
4 | * | |
5 | * This library is free software; you can redistribute it and/or | |
6 | * modify it under the terms of the GNU Lesser General Public | |
7 | * License as published by the Free Software Foundation; either | |
8 | * version 2.1 of the License, or (at your option) any later version. | |
9 | * | |
10 | * This library is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | * Lesser General Public License for more details. | |
14 | */ | |
15 | ||
16 | #ifdef HAVE_CONFIG_H | |
17 | #include <config.h> | |
18 | #endif | |
19 | ||
20 | /* For asprintf */ | |
21 | #define _GNU_SOURCE | |
22 | #include <stdio.h> | |
23 | ||
24 | #include "emv_pk.h" | |
25 | #include "crypto.h" | |
26 | #include "proxmark3.h" | |
27 | ||
28 | #include <stdbool.h> | |
29 | #include <string.h> | |
30 | #include <stdlib.h> | |
31 | #include <sys/types.h> | |
32 | ||
33 | #define BCD(c) (((c) >= '0' && (c) <= '9') ? ((c) - '0') : \ | |
34 | -1) | |
35 | ||
36 | #define HEX(c) (((c) >= '0' && (c) <= '9') ? ((c) - '0') : \ | |
37 | ((c) >= 'A' && (c) <= 'F') ? ((c) - 'A' + 10) : \ | |
38 | ((c) >= 'a' && (c) <= 'f') ? ((c) - 'a' + 10) : \ | |
39 | -1) | |
40 | ||
41 | #define TOHEX(v) ((v) < 10 ? (v) + '0' : (v) - 10 + 'a') | |
42 | ||
43 | static ssize_t emv_pk_read_bin(char *buf, unsigned char *bin, size_t size, size_t *read) | |
44 | { | |
45 | size_t left = size; | |
46 | char *p = buf; | |
47 | while (*p && *p == ' ') | |
48 | p++; | |
49 | ||
50 | while (left > 0) { | |
51 | int c1, c2; | |
52 | c1 = HEX(*p); | |
53 | if (c1 == -1) | |
54 | return -(p - buf); | |
55 | p++; | |
56 | c2 = HEX(*p); | |
57 | if (c2 == -1) | |
58 | return -(p - buf); | |
59 | p++; | |
60 | *bin = (c1 * 16 + c2); | |
61 | bin ++; | |
62 | left --; | |
63 | if (*p == ':') | |
64 | p++; | |
65 | else if (read) { | |
66 | *read = (size - left); | |
67 | break; | |
68 | } else if (left == 0) | |
69 | break; | |
70 | else | |
71 | return -(p - buf); | |
72 | } | |
73 | ||
74 | while (*p && *p == ' ') | |
75 | p++; | |
76 | ||
77 | p--; | |
78 | ||
79 | return (p - buf); | |
80 | } | |
81 | ||
82 | static ssize_t emv_pk_read_ymv(char *buf, unsigned *ymv) | |
83 | { | |
84 | int i; | |
85 | unsigned char temp[3]; | |
86 | char *p = buf; | |
87 | ||
88 | *ymv = 0; | |
89 | ||
90 | while (*p && *p == ' ') | |
91 | p++; | |
92 | ||
93 | for (i = 0; i < 3; i++) { | |
94 | int c1, c2; | |
95 | c1 = BCD(*p); | |
96 | if (c1 == -1) | |
97 | return -(p - buf); | |
98 | p++; | |
99 | c2 = BCD(*p); | |
100 | if (c2 == -1) | |
101 | return -(p - buf); | |
102 | p++; | |
103 | temp[i] = (c1 * 16 + c2); | |
104 | } | |
105 | ||
106 | while (*p && *p == ' ') | |
107 | p++; | |
108 | ||
109 | p--; | |
110 | ||
111 | if (temp[1] > 0x12 || temp[2] > 0x31) | |
112 | return -(p - buf); | |
113 | ||
114 | *ymv = (temp[0] * 0x10000 + temp[1] * 0x100 + temp[2]); | |
115 | ||
116 | return (p - buf); | |
117 | } | |
118 | ||
119 | static ssize_t emv_pk_read_string(char *buf, char *str, size_t size) | |
120 | { | |
121 | char *p = buf; | |
122 | while (*p && *p == ' ') | |
123 | p++; | |
124 | ||
125 | while (size > 1) { | |
126 | if (*p == ' ') | |
127 | break; | |
128 | else if (*p < 0x20 || *p >= 0x7f) | |
129 | return -(p - buf); | |
130 | *str = *p; | |
131 | p++; | |
132 | str ++; | |
133 | size --; | |
134 | } | |
135 | ||
136 | *str = 0; | |
137 | ||
138 | while (*p && *p == ' ') | |
139 | p++; | |
140 | ||
141 | p--; | |
142 | ||
143 | return (p - buf); | |
144 | } | |
145 | ||
146 | ||
147 | struct emv_pk *emv_pk_parse_pk(char *buf) | |
148 | { | |
149 | struct emv_pk *r = calloc(1, sizeof(*r)); | |
150 | ssize_t l; | |
151 | char temp[10]; | |
152 | ||
153 | l = emv_pk_read_bin(buf, r->rid, 5, NULL); | |
154 | if (l <= 0) | |
155 | goto out; | |
156 | buf += l; | |
157 | ||
158 | l = emv_pk_read_bin(buf, &r->index, 1, NULL); | |
159 | if (l <= 0) | |
160 | goto out; | |
161 | buf += l; | |
162 | ||
163 | l = emv_pk_read_ymv(buf, &r->expire); | |
164 | if (l <= 0) | |
165 | goto out; | |
166 | buf += l; | |
167 | ||
168 | l = emv_pk_read_string(buf, temp, sizeof(temp)); | |
169 | if (l <= 0) | |
170 | goto out; | |
171 | buf += l; | |
172 | ||
173 | if (!strcmp(temp, "rsa")) | |
174 | r->pk_algo = PK_RSA; | |
175 | else | |
176 | goto out; | |
177 | ||
178 | l = emv_pk_read_bin(buf, r->exp, sizeof(r->exp), &r->elen); | |
179 | if (l <= 0) | |
180 | goto out; | |
181 | buf += l; | |
182 | ||
183 | r->modulus = malloc(2048/8); | |
184 | l = emv_pk_read_bin(buf, r->modulus, 2048/8, &r->mlen); | |
185 | if (l <= 0) | |
186 | goto out2; | |
187 | buf += l; | |
188 | ||
189 | l = emv_pk_read_string(buf, temp, sizeof(temp)); | |
190 | if (l <= 0) | |
191 | goto out2; | |
192 | buf += l; | |
193 | ||
194 | if (!strcmp(temp, "sha1")) | |
195 | r->hash_algo = HASH_SHA_1; | |
196 | else | |
197 | goto out2; | |
198 | ||
199 | l = emv_pk_read_bin(buf, r->hash, 20, NULL); | |
200 | if (l <= 0) | |
201 | goto out2; | |
202 | ||
203 | return r; | |
204 | ||
205 | out2: | |
206 | free(r->modulus); | |
207 | out: | |
208 | free(r); | |
209 | return NULL; | |
210 | } | |
211 | ||
212 | static size_t emv_pk_write_bin(char *out, size_t outlen, const unsigned char *buf, size_t len) | |
213 | { | |
214 | int i; | |
215 | size_t pos = 0; | |
216 | ||
217 | if (len == 0) | |
218 | return 0; | |
219 | if (outlen < len * 3) | |
220 | return 0; | |
221 | ||
222 | out[pos++] = TOHEX(buf[0] >> 4); | |
223 | out[pos++] = TOHEX(buf[0] & 0xf); | |
224 | for (i = 1; i < len; i++) { | |
225 | out[pos++] = ':'; | |
226 | out[pos++] = TOHEX(buf[i] >> 4); | |
227 | out[pos++] = TOHEX(buf[i] & 0xf); | |
228 | } | |
229 | out[pos++] = ' '; | |
230 | ||
231 | return pos; | |
232 | } | |
233 | ||
234 | static size_t emv_pk_write_str(char *out, size_t outlen, const char *str) | |
235 | { | |
236 | size_t len = strlen(str); | |
237 | ||
238 | if (len == 0) | |
239 | return 0; | |
240 | if (outlen < len) | |
241 | return 0; | |
242 | ||
243 | memcpy(out, str, len); | |
244 | ||
245 | return len; | |
246 | } | |
247 | ||
248 | char *emv_pk_dump_pk(const struct emv_pk *pk) | |
249 | { | |
250 | size_t outsize = 1024; /* should be enough */ | |
251 | char *out = malloc(outsize); /* should be enough */ | |
252 | size_t outpos = 0; | |
253 | size_t rc; | |
254 | ||
255 | if (!out) | |
256 | return NULL; | |
257 | ||
258 | rc = emv_pk_write_bin(out + outpos, outsize - outpos, pk->rid, 5); | |
259 | if (rc == 0) | |
260 | goto err; | |
261 | outpos += rc; | |
262 | ||
263 | rc = emv_pk_write_bin(out + outpos, outsize - outpos, &pk->index, 1); | |
264 | if (rc == 0) | |
265 | goto err; | |
266 | outpos += rc; | |
267 | ||
268 | if (outpos + 7 > outsize) | |
269 | goto err; | |
270 | out[outpos++] = TOHEX((pk->expire >> 20) & 0xf); | |
271 | out[outpos++] = TOHEX((pk->expire >> 16) & 0xf); | |
272 | out[outpos++] = TOHEX((pk->expire >> 12) & 0xf); | |
273 | out[outpos++] = TOHEX((pk->expire >> 8 ) & 0xf); | |
274 | out[outpos++] = TOHEX((pk->expire >> 4 ) & 0xf); | |
275 | out[outpos++] = TOHEX((pk->expire >> 0 ) & 0xf); | |
276 | out[outpos++] = ' '; | |
277 | ||
278 | if (pk->pk_algo == PK_RSA) { | |
279 | rc = emv_pk_write_str(out + outpos, outsize - outpos, "rsa"); | |
280 | if (rc == 0) | |
281 | goto err; | |
282 | outpos += rc; | |
283 | out[outpos++] = ' '; | |
284 | } else { | |
285 | if (outpos + 4 > outsize) | |
286 | goto err; | |
287 | out[outpos++] = '?'; | |
288 | out[outpos++] = '?'; | |
289 | out[outpos++] = TOHEX(pk->pk_algo >> 4); | |
290 | out[outpos++] = TOHEX(pk->pk_algo & 0xf); | |
291 | } | |
292 | ||
293 | rc = emv_pk_write_bin(out + outpos, outsize - outpos, pk->exp, pk->elen); | |
294 | if (rc == 0) | |
295 | goto err; | |
296 | outpos += rc; | |
297 | ||
298 | rc = emv_pk_write_bin(out + outpos, outsize - outpos, pk->modulus, pk->mlen); | |
299 | if (rc == 0) | |
300 | goto err; | |
301 | outpos += rc; | |
302 | ||
303 | if (pk->hash_algo == HASH_SHA_1) { | |
304 | rc = emv_pk_write_str(out + outpos, outsize - outpos, "sha1"); | |
305 | if (rc == 0) | |
306 | goto err; | |
307 | outpos += rc; | |
308 | out[outpos++] = ' '; | |
309 | } else { | |
310 | if (outpos + 4 > outsize) | |
311 | goto err; | |
312 | out[outpos++] = '?'; | |
313 | out[outpos++] = '?'; | |
314 | out[outpos++] = TOHEX(pk->pk_algo >> 4); | |
315 | out[outpos++] = TOHEX(pk->pk_algo & 0xf); | |
316 | } | |
317 | ||
318 | ||
319 | rc = emv_pk_write_bin(out + outpos, outsize - outpos, pk->hash, 20); | |
320 | if (rc == 0) | |
321 | goto err; | |
322 | outpos += rc; | |
323 | ||
324 | out[outpos-1] = '\0'; | |
325 | ||
326 | return out; | |
327 | ||
328 | err: | |
329 | free(out); | |
330 | return NULL; | |
331 | } | |
332 | ||
333 | bool emv_pk_verify(const struct emv_pk *pk) | |
334 | { | |
335 | struct crypto_hash *ch = crypto_hash_open(pk->hash_algo); | |
336 | if (!ch) | |
337 | return false; | |
338 | ||
339 | crypto_hash_write(ch, pk->rid, sizeof(pk->rid)); | |
340 | crypto_hash_write(ch, &pk->index, 1); | |
341 | crypto_hash_write(ch, pk->modulus, pk->mlen); | |
342 | crypto_hash_write(ch, pk->exp, pk->elen); | |
343 | ||
344 | unsigned char *h = crypto_hash_read(ch); | |
345 | if (!h) { | |
346 | crypto_hash_close(ch); | |
347 | return false; | |
348 | } | |
349 | ||
350 | size_t hsize = crypto_hash_get_size(ch); | |
351 | bool r = hsize && !memcmp(h, pk->hash, hsize) ? true : false; | |
352 | ||
353 | crypto_hash_close(ch); | |
354 | ||
355 | return r; | |
356 | } | |
357 | ||
358 | struct emv_pk *emv_pk_new(size_t modlen, size_t explen) | |
359 | { | |
360 | struct emv_pk *pk; | |
361 | ||
362 | /* Not supported ATM */ | |
363 | if (explen > 3) | |
364 | return NULL; | |
365 | ||
366 | pk = calloc(1, sizeof(*pk)); | |
367 | if (!pk) | |
368 | return NULL; | |
369 | ||
370 | pk->mlen = modlen; | |
371 | pk->elen = explen; | |
372 | ||
373 | pk->modulus = calloc(modlen, 1); | |
374 | if (!pk->modulus) { | |
375 | free(pk); | |
376 | pk = NULL; | |
377 | } | |
378 | ||
379 | return pk; | |
380 | } | |
381 | ||
382 | void emv_pk_free(struct emv_pk *pk) | |
383 | { | |
384 | if (!pk) | |
385 | return; | |
386 | ||
387 | free(pk->modulus); | |
388 | free(pk); | |
389 | } | |
390 | ||
391 | static struct emv_pk *emv_pk_get_ca_pk_from_file(const char *fname, | |
392 | const unsigned char *rid, | |
393 | unsigned char idx) | |
394 | { | |
395 | if (!fname) | |
396 | return NULL; | |
397 | ||
398 | FILE *f = fopen(fname, "r"); | |
399 | if (!f) { | |
400 | perror("fopen"); | |
401 | return NULL; | |
402 | } | |
403 | ||
404 | while (!feof(f)) { | |
405 | char buf[2048]; | |
406 | if (fgets(buf, sizeof(buf), f) == NULL) | |
407 | break; | |
408 | ||
409 | struct emv_pk *pk = emv_pk_parse_pk(buf); | |
410 | if (!pk) | |
411 | continue; | |
412 | if (memcmp(pk->rid, rid, 5) || pk->index != idx) { | |
413 | emv_pk_free(pk); | |
414 | continue; | |
415 | } | |
416 | ||
417 | fclose(f); | |
418 | ||
419 | return pk; | |
420 | } | |
421 | ||
422 | fclose(f); | |
423 | ||
424 | return NULL; | |
425 | } | |
426 | ||
427 | char *emv_pk_get_ca_pk_file(const char *dirname, const unsigned char *rid, unsigned char idx) | |
428 | { | |
429 | if (!dirname) | |
430 | dirname = ".";//openemv_config_get_str("capk.dir", NULL); | |
431 | ||
432 | if (!dirname) | |
433 | return NULL; | |
434 | ||
435 | char *filename; | |
436 | int ret = asprintf(&filename, "%s/%02hhx%02hhx%02hhx%02hhx%02hhx_%02hhx.0", | |
437 | dirname, | |
438 | rid[0], | |
439 | rid[1], | |
440 | rid[2], | |
441 | rid[3], | |
442 | rid[4], | |
443 | idx); | |
444 | ||
445 | if (ret <= 0) | |
446 | return NULL; | |
447 | ||
448 | return filename; | |
449 | } | |
450 | ||
451 | char *emv_pk_get_ca_pk_rid_file(const char *dirname, const unsigned char *rid) | |
452 | { | |
453 | if (!dirname) | |
454 | dirname = "."; //openemv_config_get_str("capk.dir", NULL); | |
455 | ||
456 | if (!dirname) | |
457 | return NULL; | |
458 | ||
459 | char *filename; | |
460 | int ret = asprintf(&filename, "%s/%02hhx%02hhx%02hhx%02hhx%02hhx.pks", | |
461 | dirname, | |
462 | rid[0], | |
463 | rid[1], | |
464 | rid[2], | |
465 | rid[3], | |
466 | rid[4]); | |
467 | ||
468 | if (ret <= 0) | |
469 | return NULL; | |
470 | ||
471 | return filename; | |
472 | } | |
473 | ||
474 | struct emv_pk *emv_pk_get_ca_pk(const unsigned char *rid, unsigned char idx) | |
475 | { | |
476 | struct emv_pk *pk = NULL; | |
477 | ||
478 | /* if (!pk) { | |
479 | char *fname = emv_pk_get_ca_pk_file(NULL, rid, idx); | |
480 | if (fname) { | |
481 | pk = emv_pk_get_ca_pk_from_file(fname, rid, idx); | |
482 | free(fname); | |
483 | } | |
484 | } | |
485 | ||
486 | if (!pk) { | |
487 | char *fname = emv_pk_get_ca_pk_rid_file(NULL, rid); | |
488 | if (fname) { | |
489 | pk = emv_pk_get_ca_pk_from_file(fname, rid, idx); | |
490 | free(fname); | |
491 | } | |
492 | } | |
493 | */ | |
494 | if (!pk) { | |
495 | const char *relfname = "emv/capk.txt"; | |
496 | ||
497 | char fname[strlen(get_my_executable_directory()) + strlen(relfname) + 1]; | |
498 | strcpy(fname, get_my_executable_directory()); | |
499 | strcat(fname, relfname); | |
500 | ||
501 | pk = emv_pk_get_ca_pk_from_file(fname, rid, idx); | |
502 | } | |
503 | if (!pk) | |
504 | return NULL; | |
505 | ||
506 | printf("Verifying CA PK for %02hhx:%02hhx:%02hhx:%02hhx:%02hhx IDX %02hhx %zd bits...", | |
507 | pk->rid[0], | |
508 | pk->rid[1], | |
509 | pk->rid[2], | |
510 | pk->rid[3], | |
511 | pk->rid[4], | |
512 | pk->index, | |
513 | pk->mlen * 8); | |
514 | if (emv_pk_verify(pk)) { | |
515 | printf("OK\n"); | |
516 | ||
517 | return pk; | |
518 | } | |
519 | ||
520 | printf("Failed!\n"); | |
521 | emv_pk_free(pk); | |
522 | ||
523 | return NULL; | |
524 | } |