static void build_cap(struct tlvdb *db) { const struct tlv *ipb = tlvdb_get(db, 0x9f56, NULL); ipb_dol_value[sizeof(ipb_dol_value) - 1] = ipb->len - (1 + 1 + 2 + 8); struct tlv *ipb_data_tlv = dol_process(&ipb_dol, db, 0); if (!ipb_data_tlv) return; dump_buffer(ipb_data_tlv->value, ipb_data_tlv->len, stdout); dump_buffer(ipb->value, ipb->len, stdout); if (ipb_data_tlv->len < ipb->len) { free(ipb_data_tlv); return; } unsigned char buf[ipb->len]; int i, j, k = 0; unsigned char c = 0; for (i = ipb->len; i > 0; i--) { for (j = 0; j < 8; j++) { if ((ipb->value[i-1] & (1 << j)) == 0) continue; c |= ((ipb_data_tlv->value[i-1] >> j) & 1) << (k % 8); k++; if (k % 8 != 0) continue; buf[k / 8 - 1] = c; c = 0; } } free(ipb_data_tlv); if (k % 8 != 0) { k += 8 - (k % 8); buf[k / 8 - 1] = c; } k /= 8; unsigned long data = 0; for (i = 0; i < k; i++) data |= ((unsigned long) buf[i]) << (8 * i); dump_buffer(buf, k, stdout); fprintf(stdout, "CAP data: %lu\n", data); }
struct tlv *dol_process(const struct tlv *tlv, const struct tlvdb *tlvdb, tlv_tag_t tag) { size_t res_len; if (!tlv || !(res_len = dol_calculate_len(tlv, 0))) { struct tlv *res_tlv = malloc(sizeof(*res_tlv)); res_tlv->tag = tag; res_tlv->len = 0; res_tlv->value = NULL; return res_tlv; } struct tlv *res_tlv = malloc(sizeof(*res_tlv) + res_len); if (!res_tlv) return NULL; const unsigned char *buf = tlv->value; size_t left = tlv->len; unsigned char *res = (unsigned char *)(res_tlv + 1); size_t pos = 0; while (left) { struct tlv cur_tlv; if (!tlv_parse_tl(&buf, &left, &cur_tlv) || pos + cur_tlv.len > res_len) { free(res_tlv); return NULL; } const struct tlv *tag_tlv = tlvdb_get(tlvdb, cur_tlv.tag, NULL); if (!tag_tlv) { memset(res + pos, 0, cur_tlv.len); } else if (tag_tlv->len > cur_tlv.len) { memcpy(res + pos, tag_tlv->value, cur_tlv.len); } else { // FIXME: cn data should be padded with 0xFF !!! memcpy(res + pos, tag_tlv->value, tag_tlv->len); memset(res + pos + tag_tlv->len, 0, cur_tlv.len - tag_tlv->len); } pos += cur_tlv.len; } res_tlv->tag = tag; res_tlv->len = res_len; res_tlv->value = res; return res_tlv; }
unsigned char *dol_process(const struct tlv *tlv, const struct tlvdb *tlvdb, size_t *len) { if (!tlv) { *len = 0; return NULL; } const unsigned char *buf = tlv->value; size_t left = tlv->len; size_t res_len = dol_calculate_len(tlv, 0); unsigned char *res; size_t pos = 0; if (!res_len) { *len = 0; return NULL; } res = malloc(res_len); while (left) { struct tlv cur_tlv; if (!tlv_parse_tl(&buf, &left, &cur_tlv) || pos + cur_tlv.len > res_len) { free(res); return NULL; } const struct tlv *tag_tlv = tlvdb_get(tlvdb, cur_tlv.tag, NULL); if (!tag_tlv) { memset(res + pos, 0, cur_tlv.len); } else if (tag_tlv->len > cur_tlv.len) { memcpy(res + pos, tag_tlv->value, cur_tlv.len); } else { // FIXME: cn data should be padded with 0xFF !!! memcpy(res + pos, tag_tlv->value, tag_tlv->len); memset(res + pos + tag_tlv->len, 0, cur_tlv.len - tag_tlv->len); } pos += cur_tlv.len; } *len = pos; return res; }
static int sda_test_pk(void) { const struct emv_pk *pk = &vsdc_01; struct tlvdb *db; db = tlvdb_external(0x90, sizeof(issuer_cert), issuer_cert); tlvdb_add(db, tlvdb_external(0x9f32, sizeof(issuer_exp), issuer_exp)); tlvdb_add(db, tlvdb_external(0x92, sizeof(issuer_rem), issuer_rem)); struct emv_pk *ipk = emv_pki_recover_issuer_cert(pk, db); if (!ipk) { fprintf(stderr, "Could not recover Issuer certificate!\n"); tlvdb_free(db); return 2; } tlvdb_add(db, tlvdb_external(0x93, sizeof(ssad_cr), ssad_cr)); struct tlvdb *dacdb = emv_pki_recover_dac(ipk, db, ssd1, sizeof(ssd1)); if (!dacdb) { fprintf(stderr, "Could not recover DAC!\n"); emv_pk_free(ipk); tlvdb_free(db); return 2; } const struct tlv *dac = tlvdb_get(dacdb, 0x9f45, NULL); if (!dac) { fprintf(stderr, "DAC not found!\n"); tlvdb_free(dacdb); emv_pk_free(ipk); tlvdb_free(db); return 2; } dump_buffer(dac->value, dac->len, stdout); tlvdb_free(dacdb); emv_pk_free(ipk); tlvdb_free(db); return 0; }
int main(void) { int i; struct sc *sc; sc = scard_init(NULL); if (!sc) { printf("Cannot init scard\n"); return 1; } scard_connect(sc, openemv_config_get_int("scard.reader", 0)); if (scard_is_error(sc)) { printf("%s\n", scard_error(sc)); return 1; } struct tlvdb *s; struct tlvdb *t; for (i = 0, s = NULL; apps[i].name_len != 0; i++) { const struct tlv aid_tlv = { .len = apps[i].name_len, .value = apps[i].name, }; s = emv_select(sc, &aid_tlv); if (s) break; } if (!s) return 1; struct tlv *pdol_data_tlv = dol_process(tlvdb_get(s, 0x9f38, NULL), s, 0x83); if (!pdol_data_tlv) return 1; t = emv_gpo(sc, pdol_data_tlv); free(pdol_data_tlv); if (!t) return 1; tlvdb_add(s, t); struct tlv *sda_tlv = emv_read_records(sc, s); if (!sda_tlv) return 1; /* Only PTC read should happen before VERIFY */ tlvdb_add(s, emv_get_data(sc, 0x9f17)); verify_offline_clear(s, sc); #define TAG(tag, len, value...) tlvdb_add(s, tlvdb_fixed(tag, len, (unsigned char[]){value})) // TAG(0x9f02, 6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); // TAG(0x9f1a, 2, 0x06, 0x43); TAG(0x95, 5, 0x80, 0x00, 0x00, 0x00, 0x00); // TAG(0x5f2a, 2, 0x06, 0x43); // TAG(0x9a, 3, 0x14, 0x09, 0x25); // TAG(0x9c, 1, 0x50); // TAG(0x9f37, 4, 0x12, 0x34, 0x57, 0x79); TAG(0x9f35, 1, 0x34); TAG(0x9f34, 3, 0x01, 0x00, 0x02); #undef TAG /* Generate ARQC */ struct tlv *crm_tlv = dol_process(tlvdb_get(s, 0x8c, NULL), s, 0); if (!crm_tlv) return 1; t = emv_generate_ac(sc, 0x80, crm_tlv); free(crm_tlv); tlvdb_add(s, t); build_cap(s); #define TAG(tag, len, value...) tlvdb_add(s, tlvdb_fixed(tag, len, (unsigned char[]){value})) TAG(0x8a, 2, 'Z', '3'); #undef TAG /* Generate AC asking for AAC */ crm_tlv = dol_process(tlvdb_get(s, 0x8d, NULL), s, 0); if (!crm_tlv) return 1; t = emv_generate_ac(sc, 0x00, crm_tlv); free(crm_tlv); tlvdb_add(s, t); tlvdb_visit(s, print_cb, NULL); tlvdb_free(s); scard_disconnect(sc); if (scard_is_error(sc)) { printf("%s\n", scard_error(sc)); return 1; } scard_shutdown(sc); return 0; }
bool emv_read_records(struct sc *sc, struct tlvdb *db, unsigned char **pdata, size_t *plen) { *pdata = NULL; *plen = 0; const struct tlv *afl = tlvdb_get(db, 0x94, NULL); if (!afl) return 1; unsigned char *sda_data = NULL; size_t sda_len = 0; int i; for (i = 0; i < afl->len; i += 4) { unsigned char p2 = afl->value[i + 0]; unsigned char first = afl->value[i + 1]; unsigned char last = afl->value[i + 2]; unsigned char sdarec = afl->value[i + 3]; unsigned char sfi = p2 >> 3; if (sfi == 0 || sfi == 31 || first == 0 || first > last) return false; for (; first <= last; first ++) { unsigned short sw; size_t outlen; unsigned char *outbuf; struct tlvdb *t; outbuf = emv_read_record(sc, sfi, first, &sw, &outlen); if (!outbuf) return false; if (sw == 0x9000) { t = tlvdb_parse(outbuf, outlen); if (!t) return false; } else return false; if (sdarec) { const unsigned char *data; size_t data_len; if (sfi < 11) { const struct tlv *e = tlvdb_get(t, 0x70, NULL); if (!e) return false; data = e->value; data_len = e->len; } else { data = outbuf; data_len = outlen; } sda_data = realloc(sda_data, sda_len + data_len); memcpy(sda_data + sda_len, data, data_len); sda_len += data_len; sdarec --; } free(outbuf); tlvdb_add(db, t); } } const struct tlv *sdatl_tlv = tlvdb_get(db, 0x9f4a, NULL); if (sdatl_tlv) { const struct tlv *aip_tlv = tlvdb_get(db, 0x82, NULL); if (sdatl_tlv->len == 1 && sdatl_tlv->value[0] == 0x82 && aip_tlv) { sda_data = realloc(sda_data, sda_len + aip_tlv->len); memcpy(sda_data + sda_len, aip_tlv->value, aip_tlv->len); sda_len += aip_tlv->len; } else { /* Error!! */ free(sda_data); sda_data = NULL; sda_len = 0; } } *pdata = sda_data; *plen = sda_len; return true; }
static struct emu_df *read_df(FILE *f, struct sc *sc, const unsigned char *name, size_t name_len) { struct emu_df *df; int i, j; struct tlvdb *s; unsigned short sw; size_t outlen; unsigned char *outbuf; struct tlv pdol_data_tlv; size_t pdol_data_len; unsigned char *pdol_data; outbuf = sc_command(sc, 0x00, 0xa4, 0x04, 0x00, name_len, name, &sw, &outlen); if (sw != 0x9000) return NULL; s = tlvdb_parse(outbuf, outlen); if (!s) return NULL; df = emu_df_new(); pdol_data_tlv.tag = 0x83; pdol_data_tlv.value = dol_process(tlvdb_get(s, 0x9f38, NULL), s, &pdol_data_tlv.len); pdol_data = tlv_encode(&pdol_data_tlv, &pdol_data_len); if (!pdol_data) return NULL; free((unsigned char *)pdol_data_tlv.value); tlvdb_free(s); emu_df_append(df, emu_property_new("name", emu_value_new_buf(name, name_len))); emu_df_append(df, emu_property_new("fci", emu_value_new_buf(outbuf, outlen))); free(outbuf); outbuf = sc_command(sc, 0x80, 0xa8, 0x00, 0x00, pdol_data_len, pdol_data, &sw, &outlen); free(pdol_data); if (sw == 0x9000) { emu_df_append(df, emu_property_new("gpo", emu_value_new_buf(outbuf, outlen))); free(outbuf); } for (i = 1; i < 31; i++) { int last = 0; struct emu_value *value = NULL; char buf[7]; snprintf(buf, sizeof(buf), "sfi%d", i); for (j = 1; j < 256; j++) { outbuf = sc_command(sc, 0x00, 0xb2, j, (i << 3) | 4, 0, NULL, &sw, &outlen); if (sw == 0x6985) continue; else if (sw != 0x9000) break; for (; last < j - 1; last++) value = emu_value_append(value, ""); value = emu_value_append_buf(value, outbuf, outlen); last ++; free(outbuf); } if (value) emu_df_append(df, emu_property_new(buf, value)); } for (i = 0; card_data[i]; i++) { char buf[10]; tlv_tag_t tag = card_data[i]; outbuf = sc_command(sc, 0x80, 0xca, tag >> 8, tag & 0xff, 0, NULL, &sw, &outlen); if (sw != 0x9000) continue; snprintf(buf, sizeof(buf), "data%x", tag); emu_df_append(df, emu_property_new(buf, emu_value_new_buf(outbuf, outlen))); free(outbuf); } return df; }
int main(void) { int i; struct sc *sc; sc = scard_init(NULL); if (!sc) { printf("Cannot init scard\n"); return 1; } scard_connect(sc, 0); if (scard_is_error(sc)) { printf("%s\n", scard_error(sc)); return 1; } struct tlvdb *s; struct tlvdb *t; for (i = 0, s = NULL; apps[i].name_len != 0; i++) { s = emv_select(sc, apps[i].name, apps[i].name_len); if (s) break; } if (!s) return 1; size_t pdol_data_len; unsigned char *pdol_data = dol_process(tlvdb_get(s, 0x9f38, NULL), s, &pdol_data_len); struct tlv pdol_data_tlv = { .tag = 0x83, .len = pdol_data_len, .value = pdol_data }; size_t pdol_data_tlv_data_len; unsigned char *pdol_data_tlv_data = tlv_encode(&pdol_data_tlv, &pdol_data_tlv_data_len); free(pdol_data); if (!pdol_data_tlv_data) return 1; t = emv_gpo(sc, pdol_data_tlv_data, pdol_data_tlv_data_len); free(pdol_data_tlv_data); if (!t) return 1; tlvdb_add(s, t); unsigned char *sda_data = NULL; size_t sda_len = 0; bool ok = emv_read_records(sc, s, &sda_data, &sda_len); if (!ok) return 1; free(sda_data); /* Generate AC asking for AAC */ size_t crm_data_len; unsigned char *crm_data = dol_process(tlvdb_get(s, 0x8c, NULL), s, &crm_data_len); t = emv_generate_ac(sc, 0x00, crm_data, crm_data_len); free(crm_data); tlvdb_add(s, t); tlvdb_add(s, emv_get_data(sc, 0x9f36)); tlvdb_add(s, emv_get_data(sc, 0x9f13)); tlvdb_add(s, emv_get_data(sc, 0x9f17)); tlvdb_add(s, emv_get_data(sc, 0x9f4f)); tlvdb_visit(s, print_cb, NULL); const struct tlv *logent_tlv = tlvdb_get(s, 0x9f4d, NULL); const struct tlv *logent_dol = tlvdb_get(s, 0x9f4f, NULL); if (logent_tlv && logent_tlv->len == 2 && logent_dol) { for (i = 1; i <= logent_tlv->value[1]; i++) { unsigned short sw; size_t log_len; unsigned char *log = emv_read_record(sc, logent_tlv->value[0], i, &sw, &log_len); if (!log) continue; if (sw == 0x9000) { printf("Log #%d\n", i); struct tlvdb *log_db = dol_parse(logent_dol, log, log_len); tlvdb_visit(log_db, print_cb, NULL); tlvdb_free(log_db); } free(log); } } tlvdb_free(s); scard_disconnect(sc); if (scard_is_error(sc)) { printf("%s\n", scard_error(sc)); return 1; } scard_shutdown(sc); return 0; }