fix little endian vs big endian in the macros... again... but this time correct
[RRG-proxmark3.git] / client / src / emv / emv_pk.c
blob1a783ef127220452ee5eecafc4d7952260eb47a1
1 /*
2 * libopenemv - a library to work with EMV family of smart cards
3 * Copyright (C) 2012, 2015 Dmitry Eremin-Solenikov
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.
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.
16 #ifdef HAVE_CONFIG_H
17 #include <config.h>
18 #endif
20 /* For asprintf */
21 #define _GNU_SOURCE
23 #include "emv_pk.h"
25 #include <stdio.h>
26 #include <string.h>
27 #include <stdlib.h>
29 #include "ui.h"
30 #include "crypto.h"
31 #include "proxmark3.h"
32 #include "fileutils.h"
33 #include "pm3_cmd.h"
35 #define BCD(c) (((c) >= '0' && (c) <= '9') ? ((c) - '0') : -1)
37 #define HEX(c) (((c) >= '0' && (c) <= '9') ? ((c) - '0') : \
38 ((c) >= 'A' && (c) <= 'F') ? ((c) - 'A' + 10) : \
39 ((c) >= 'a' && (c) <= 'f') ? ((c) - 'a' + 10) : \
40 -1)
42 #define TOHEX(v) ((v) < 10 ? (v) + '0' : (v) - 10 + 'a')
44 static ssize_t emv_pk_read_bin(char *buf, size_t buflen, unsigned char *bin, size_t size, size_t *read) {
46 if (buf == NULL)
47 return 0;
49 size_t left = size;
50 char *p = buf;
51 while ((*p == ' ') && (p < (buf + buflen - 1)))
52 p++;
54 while (left > 0) {
55 int c1, c2;
56 c1 = HEX(*p);
57 if (c1 == -1)
58 return -(p - buf);
59 if (p == (buf + buflen - 1))
60 return -(p - buf);
61 p++;
62 c2 = HEX(*p);
63 if (c2 == -1)
64 return -(p - buf);
65 if (p == (buf + buflen - 1))
66 return -(p - buf);
67 p++;
68 *bin = (c1 * 16 + c2);
69 bin ++;
70 left --;
71 if ((*p == ':') && (p < (buf + buflen - 1)))
72 p++;
73 else if (read) {
74 *read = (size - left);
75 break;
76 } else if (left == 0)
77 break;
78 else
79 return -(p - buf);
82 while ((*p == ' ') && (p < (buf + buflen - 1)))
83 p++;
85 p--;
87 return (p - buf);
90 static ssize_t emv_pk_read_ymv(char *buf, size_t buflen, unsigned *ymv) {
92 if (buf == NULL)
93 return 0;
95 unsigned char temp[3];
96 char *p = buf;
98 *ymv = 0;
100 while ((*p == ' ') && (p < (buf + buflen - 1)))
101 p++;
103 for (int i = 0; i < 3; i++) {
104 int c1, c2;
105 c1 = BCD(*p);
106 if (c1 == -1)
107 return -(p - buf);
108 if (p == (buf + buflen - 1))
109 return -(p - buf);
110 p++;
111 c2 = BCD(*p);
112 if (c2 == -1)
113 return -(p - buf);
114 if (p == (buf + buflen - 1))
115 return -(p - buf);
116 p++;
117 temp[i] = (c1 * 16 + c2);
120 while ((*p == ' ') && (p < (buf + buflen - 1)))
121 p++;
123 p--;
125 if (temp[1] > 0x12 || temp[2] > 0x31)
126 return -(p - buf);
128 *ymv = (temp[0] * 0x10000 + temp[1] * 0x100 + temp[2]);
130 return (p - buf);
133 static ssize_t emv_pk_read_string(char *buf, size_t buflen, char *str, size_t size) {
135 if (buf == NULL)
136 return 0;
138 char *p = buf;
139 while ((*p == ' ') && (p < (buf + buflen - 1)))
140 p++;
142 while (size > 1) {
143 if (*p == ' ')
144 break;
145 else if (*p < 0x20 || *p >= 0x7f)
146 return -(p - buf);
147 *str = *p;
148 if (p == (buf + buflen - 1))
149 return -(p - buf);
150 p++;
151 str ++;
152 size --;
155 *str = 0;
157 while ((*p == ' ') && (p < (buf + buflen - 1)))
158 p++;
160 p--;
162 return (p - buf);
165 struct emv_pk *emv_pk_parse_pk(char *buf, size_t buflen) {
166 struct emv_pk *r = calloc(1, sizeof(*r));
167 ssize_t l;
168 char temp[10];
170 l = emv_pk_read_bin(buf, buflen, r->rid, 5, NULL);
171 if (l <= 0)
172 goto out;
173 buf += l;
175 l = emv_pk_read_bin(buf, buflen, &r->index, 1, NULL);
176 if (l <= 0)
177 goto out;
178 buf += l;
180 l = emv_pk_read_ymv(buf, buflen, &r->expire);
181 if (l <= 0)
182 goto out;
183 buf += l;
185 l = emv_pk_read_string(buf, buflen, temp, sizeof(temp));
186 if (l <= 0)
187 goto out;
188 buf += l;
190 if (!strcmp(temp, "rsa"))
191 r->pk_algo = PK_RSA;
192 else
193 goto out;
195 l = emv_pk_read_bin(buf, buflen, r->exp, sizeof(r->exp), &r->elen);
196 if (l <= 0)
197 goto out;
198 buf += l;
200 r->modulus = malloc(2048 / 8);
201 l = emv_pk_read_bin(buf, buflen, r->modulus, 2048 / 8, &r->mlen);
202 if (l <= 0)
203 goto out2;
204 buf += l;
206 l = emv_pk_read_string(buf, buflen, temp, sizeof(temp));
207 if (l <= 0)
208 goto out2;
209 buf += l;
211 if (!strcmp(temp, "sha1"))
212 r->hash_algo = HASH_SHA_1;
213 else
214 goto out2;
216 l = emv_pk_read_bin(buf, buflen, r->hash, 20, NULL);
217 if (l <= 0)
218 goto out2;
220 return r;
222 out2:
223 free(r->modulus);
224 out:
225 free(r);
226 return NULL;
229 static size_t emv_pk_write_bin(char *out, size_t outlen, const unsigned char *buf, size_t len) {
230 int i;
231 size_t pos = 0;
233 if (len == 0)
234 return 0;
235 if (outlen < len * 3)
236 return 0;
238 out[pos++] = TOHEX(buf[0] >> 4);
239 out[pos++] = TOHEX(buf[0] & 0xf);
240 for (i = 1; i < len; i++) {
241 out[pos++] = ':';
242 out[pos++] = TOHEX(buf[i] >> 4);
243 out[pos++] = TOHEX(buf[i] & 0xf);
245 out[pos++] = ' ';
247 return pos;
250 static size_t emv_pk_write_str(char *out, size_t outlen, const char *str) {
251 size_t len = strlen(str);
253 if (len == 0)
254 return 0;
255 if (outlen < len)
256 return 0;
258 memcpy(out, str, len);
260 return len;
263 char *emv_pk_dump_pk(const struct emv_pk *pk) {
264 size_t outpos = 0;
265 size_t outsize = 1024; /* should be enough */
266 char *out = malloc(outsize); /* should be enough */
267 if (!out)
268 return NULL;
270 size_t rc = emv_pk_write_bin(out + outpos, outsize - outpos, pk->rid, 5);
271 if (rc == 0)
272 goto err;
274 outpos += rc;
276 rc = emv_pk_write_bin(out + outpos, outsize - outpos, &pk->index, 1);
277 if (rc == 0)
278 goto err;
280 outpos += rc;
282 if (outpos + 7 > outsize)
283 goto err;
284 out[outpos++] = TOHEX((pk->expire >> 20) & 0xf);
285 out[outpos++] = TOHEX((pk->expire >> 16) & 0xf);
286 out[outpos++] = TOHEX((pk->expire >> 12) & 0xf);
287 out[outpos++] = TOHEX((pk->expire >> 8) & 0xf);
288 out[outpos++] = TOHEX((pk->expire >> 4) & 0xf);
289 out[outpos++] = TOHEX((pk->expire >> 0) & 0xf);
290 out[outpos++] = ' ';
292 if (pk->pk_algo == PK_RSA) {
293 rc = emv_pk_write_str(out + outpos, outsize - outpos, "rsa");
294 if (rc == 0)
295 goto err;
296 outpos += rc;
297 out[outpos++] = ' ';
298 } else {
299 if (outpos + 4 > outsize)
300 goto err;
301 out[outpos++] = '?';
302 out[outpos++] = '?';
303 out[outpos++] = TOHEX(pk->pk_algo >> 4);
304 out[outpos++] = TOHEX(pk->pk_algo & 0xf);
307 rc = emv_pk_write_bin(out + outpos, outsize - outpos, pk->exp, pk->elen);
308 if (rc == 0)
309 goto err;
310 outpos += rc;
312 rc = emv_pk_write_bin(out + outpos, outsize - outpos, pk->modulus, pk->mlen);
313 if (rc == 0)
314 goto err;
315 outpos += rc;
317 if (pk->hash_algo == HASH_SHA_1) {
318 rc = emv_pk_write_str(out + outpos, outsize - outpos, "sha1");
319 if (rc == 0)
320 goto err;
321 outpos += rc;
322 out[outpos++] = ' ';
323 } else {
324 if (outpos + 4 > outsize)
325 goto err;
326 out[outpos++] = '?';
327 out[outpos++] = '?';
328 out[outpos++] = TOHEX(pk->pk_algo >> 4);
329 out[outpos++] = TOHEX(pk->pk_algo & 0xf);
333 rc = emv_pk_write_bin(out + outpos, outsize - outpos, pk->hash, 20);
334 if (rc == 0)
335 goto err;
336 outpos += rc;
338 out[outpos - 1] = '\0';
340 return out;
342 err:
343 free(out);
344 return NULL;
347 bool emv_pk_verify(const struct emv_pk *pk) {
348 struct crypto_hash *ch = crypto_hash_open(pk->hash_algo);
349 if (!ch)
350 return false;
352 crypto_hash_write(ch, pk->rid, sizeof(pk->rid));
353 crypto_hash_write(ch, &pk->index, 1);
354 crypto_hash_write(ch, pk->modulus, pk->mlen);
355 crypto_hash_write(ch, pk->exp, pk->elen);
357 unsigned char *h = crypto_hash_read(ch);
358 if (!h) {
359 crypto_hash_close(ch);
360 return false;
363 size_t hsize = crypto_hash_get_size(ch);
364 bool r = hsize && !memcmp(h, pk->hash, hsize) ? true : false;
366 crypto_hash_close(ch);
368 return r;
371 struct emv_pk *emv_pk_new(size_t modlen, size_t explen) {
372 struct emv_pk *pk;
374 /* Not supported ATM */
375 if (explen > 3)
376 return NULL;
378 pk = calloc(1, sizeof(*pk));
379 if (!pk)
380 return NULL;
382 pk->mlen = modlen;
383 pk->elen = explen;
385 pk->modulus = calloc(modlen, 1);
386 if (!pk->modulus) {
387 free(pk);
388 pk = NULL;
391 return pk;
394 void emv_pk_free(struct emv_pk *pk) {
395 if (!pk)
396 return;
398 free(pk->modulus);
399 free(pk);
402 static struct emv_pk *emv_pk_get_ca_pk_from_file(const char *fname,
403 const unsigned char *rid,
404 unsigned char idx) {
405 if (!fname)
406 return NULL;
408 FILE *f = fopen(fname, "r");
409 if (!f) {
410 PrintAndLogEx(ERR, "Error: can't open file %s.", fname);
411 return NULL;
414 while (!feof(f)) {
415 char buf[2048];
416 if (fgets(buf, sizeof(buf), f) == NULL)
417 break;
419 struct emv_pk *pk = emv_pk_parse_pk(buf, sizeof(buf));
420 if (!pk)
421 continue;
423 if (memcmp(pk->rid, rid, 5) || pk->index != idx) {
424 emv_pk_free(pk);
425 continue;
428 fclose(f);
429 return pk;
432 fclose(f);
433 return NULL;
436 char *emv_pk_get_ca_pk_file(const char *dirname, const unsigned char *rid, unsigned char idx) {
437 if (!dirname)
438 dirname = ".";//openemv_config_get_str("capk.dir", NULL);
440 char *filename;
441 int ret = asprintf(&filename, "%s/%02hhx%02hhx%02hhx%02hhx%02hhx_%02hhx.0",
442 dirname,
443 rid[0],
444 rid[1],
445 rid[2],
446 rid[3],
447 rid[4],
448 idx);
450 if (ret <= 0)
451 return NULL;
453 return filename;
456 char *emv_pk_get_ca_pk_rid_file(const char *dirname, const unsigned char *rid) {
457 if (!dirname)
458 dirname = "."; //openemv_config_get_str("capk.dir", NULL);
460 char *filename;
461 int ret = asprintf(&filename, "%s/%02hhx%02hhx%02hhx%02hhx%02hhx.pks",
462 dirname,
463 rid[0],
464 rid[1],
465 rid[2],
466 rid[3],
467 rid[4]);
469 if (ret <= 0)
470 return NULL;
472 return filename;
475 struct emv_pk *emv_pk_get_ca_pk(const unsigned char *rid, unsigned char idx) {
476 struct emv_pk *pk = NULL;
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);
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);
494 char *path;
495 if (searchFile(&path, RESOURCES_SUBDIR, "capk", ".txt", false) != PM3_SUCCESS) {
496 return NULL;
498 pk = emv_pk_get_ca_pk_from_file(path, rid, idx);
499 free(path);
501 if (!pk)
502 return NULL;
504 bool isok = emv_pk_verify(pk);
506 PrintAndLogEx(INFO, "Verifying CA PK for %02hhx:%02hhx:%02hhx:%02hhx:%02hhx IDX %02hhx %zu bits. ( %s )",
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 (isok) ? _GREEN_("ok") : _RED_("failed")
517 if (isok) {
518 return pk;
521 emv_pk_free(pk);
522 return NULL;