hf mf: split HID and VIGIK decoding into client/src/parsers/

The shared viewer had the VIGIK sector assembly inlined, and the HID PACS
decode sat in hf mf mad's file branch where hf mf view and hf mf dump --ns
could not reach it. Neither scheme had a home of its own.

Give each one a parser next to parsehrt.c, same shape as that one - an
is_valid_x_card() detector and an x_parser_parse() that prints:

  parsers/parsehid.c    MAD aid 0x484d, PACS sector, Wiegand decode
  parsers/parsevigik.c  MAD aid 0x4910/0x4916, sector assembly

parsevigik.c also takes vigik_get_service(), vigik_verify() and
vigik_annotate() out of mifare/mifarehost.c, 306 lines that were VIGIK only
with a single caller.

mf_view_dump() is now two detector calls, so hf mf view -f and hf mf dump --ns
both decode a HID credential off a live card for the first time, and adding a
scheme is a new file plus two lines. hf mf mad -f keeps its HID decode through
the same parser.

The sector copy in the VIGIK path gains a bounds check; a MAD entry pointing
past the end of a short dump used to read past the buffer.

All three source lists get the new files: client/Makefile,
client/CMakeLists.txt and client/experimental_lib/CMakeLists.txt. The library
one matters because vigik_annotate() moved; without it anything linking
libpm3rrg_rdv4 loses the symbol.

hf mf mad against a card still cannot decode PACS. It authenticates with the
MAD key alone and never reads the application sector, so it has no credential
bytes to work with - unchanged here.

Co-Authored-By: Claude Opus 5 (1M context)
This commit is contained in:
iceman1001andClaude Opus 5 (1M context) committed 2026-09-13 08:37:17 +02:00
1 parent f0b569b507
commit a9105c5089
11 files changed
+568 -383

No files matched your search

+4
View File
@@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file.
This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log...
## [unreleased][unreleased]
- Added `parsehid.c` - HID PACS decoding for MIFARE Classic dumps
- Added `parsevigik.c` - VIGIK PACS decoding for MIFARE Classic dumps (@iceman1001)
- Changed `hf mf view` - now decode a HID PACS credential (@iceman1001)
- Changed `hf mf dump --ns` - now decode a HID PACS credential (@iceman1001)
- Fixed `fpga_compress -d` - a single bitstream archive was unpacked 2384 bytes short, the trailing partial interleave chunk was dropped (@iceman1001)
- Fixed `fpga_compress` - a build with only one FPGA bitstream (`SKIP_LF`, `SKIP_FELICA`, `SKIP_ISO15693`) packed it as a single 1 MB block the ARM could not decompress, `inflate returned: -13247` (@iceman1001)
- Fixed `FpgaDownloadAndGo` - a failed FPGA decompress leaked the ring buffer, leaving BigBuf 16 kB short for the rest of the session (@iceman1001)
+2
View File
@@ -507,6 +507,8 @@ set (TARGET_SOURCES
${PM3_ROOT}/client/src/jsonutils.c
${PM3_ROOT}/client/src/lua_bitlib.c
${PM3_ROOT}/client/src/parsers/parsehrt.c
${PM3_ROOT}/client/src/parsers/parsehid.c
${PM3_ROOT}/client/src/parsers/parsevigik.c
${PM3_ROOT}/client/src/parsers/hrtparser/hrtparser.c
${PM3_ROOT}/client/src/pla.c
${PM3_ROOT}/client/src/preferences.c
+2
View File
@@ -905,6 +905,8 @@ SRCS = mifare/aiddesfire.c \
mifare/gen4.c \
nfc/ndef.c \
parsers/parsehrt.c \
parsers/parsehid.c \
parsers/parsevigik.c \
parsers/hrtparser/hrtparser.c \
pm3.c \
pm3_binlib.c \
+2
View File
@@ -434,6 +434,8 @@ set (TARGET_SOURCES
${PM3_ROOT}/client/src/jsonutils.c
${PM3_ROOT}/client/src/lua_bitlib.c
${PM3_ROOT}/client/src/parsers/parsehrt.c
${PM3_ROOT}/client/src/parsers/parsehid.c
${PM3_ROOT}/client/src/parsers/parsevigik.c
${PM3_ROOT}/client/src/parsers/hrtparser/hrtparser.c
${PM3_ROOT}/client/src/pla.c
${PM3_ROOT}/client/src/preferences.c
+9 -74
View File
@@ -43,6 +43,8 @@
#include "proxendian.h"
#include "preferences.h"
#include "mifare/gen4.h"
#include "parsers/parsehid.h"
#include "parsers/parsevigik.h"
#include "generator.h" // keygens.
#include "fpga.h"
#include "mifare/mifarehost.h"
@@ -875,51 +877,14 @@ static int mf_view_dump(uint8_t *dump, size_t bytes_read, uint16_t block_cnt, bo
mf_save_keys_from_arr(block_cnt, dump);
}
const mad1_sector_t *vigik_s0 = (bytes_read >= sizeof(mad1_sector_t)) ? (const mad1_sector_t *)dump : NULL;
int sector = vigik_s0 ? DetectHID(vigik_s0, 0x4910) : -1;
if (sector < 0) {
return PM3_SUCCESS;
if (is_valid_hid_card(dump, bytes_read)) {
(void)hid_parser_parse(dump, bytes_read);
}
// decode it
PrintAndLogEx(INFO, "");
PrintAndLogEx(INFO, _CYAN_("VIGIK PACS detected"));
mad_entry_list_t mad_list = {0};
int res = MADDecode(vigik_s0, NULL, &mad_list, false, true);
if (res != PM3_SUCCESS) {
PrintAndLogEx(ERR, "can't decode MAD");
return res;
if (is_valid_vigik_card(dump, bytes_read)) {
(void)vigik_parser_parse(dump, bytes_read);
}
typedef union UDATA {
uint8_t *bytes;
mfc_vigik_t *vigik;
} UDATA;
UDATA d;
d.bytes = calloc(bytes_read, sizeof(uint8_t));
if (d.bytes == NULL) {
PrintAndLogEx(WARNING, "Failed to allocate memory");
return PM3_EMALLOC;
}
uint16_t dlen = 0;
// vigik structure sector 0
memcpy(d.bytes + dlen, dump, MFBLOCK_SIZE * 3);
dlen += MFBLOCK_SIZE * 3;
for (size_t i = 0; i < mad_list.len; i++) {
if (0x4910 == mad_list.entries[i].aid || 0x4916 == mad_list.entries[i].aid) {
uint8_t sno = mad_list.entries[i].sector;
uint32_t offset = sno * MFBLOCK_SIZE * 4;
memcpy(d.bytes + dlen, dump + offset, MFBLOCK_SIZE * 3);
dlen += MFBLOCK_SIZE * 3;
}
}
vigik_annotate(d.vigik);
free(d.bytes);
return PM3_SUCCESS;
}
@@ -6922,41 +6887,11 @@ static int CmdHF14AMfMAD(const char *Cmd) {
bool haveMAD2 = false;
MAD1DecodeAndPrint(s0, swapmad, verbose, &haveMAD2);
int sector = DetectHID(s0, 0x484d);
if (sector > -1) {
// decode it
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "------------------------- " _CYAN_("Wiegand") " ---------------------------");
PrintAndLogEx(INFO, _CYAN_("HID PACS detected"));
size_t pacs_off = mfFirstBlockOfSector(sector) * MFBLOCK_SIZE;
uint8_t pacs_sector[MFBLOCK_SIZE * 3] = {0};
if (pacs_off + sizeof(pacs_sector) <= bytes_read) {
memcpy(pacs_sector, dump + pacs_off, sizeof(pacs_sector));
}
if (pacs_sector[16] == 0x02) {
PrintAndLogEx(SUCCESS, "Raw...... " _GREEN_("%s"), sprint_hex_inrow(pacs_sector + 24, 8));
//todo: remove preamble/sentinel
uint32_t top = 0, mid = 0, bot = 0;
char hexstr[16 + 1] = {0};
hex_to_buffer((uint8_t *)hexstr, pacs_sector + 24, 8, sizeof(hexstr) - 1, 0, 0, true);
hexstring_to_u96(&top, &mid, &bot, hexstr);
char binstr[64 + 1];
hextobinstring(binstr, hexstr);
char *pbin = binstr;
while (strlen(pbin) && *(++pbin) == '0');
PrintAndLogEx(SUCCESS, "Binary... " _GREEN_("%s"), pbin);
decode_wiegand(top, mid, bot, 0);
}
if (is_valid_hid_card(dump, bytes_read)) {
(void)hid_parser_parse(dump, bytes_read);
}
int sector = -1;
sector = DetectHID(s0, 0x4910);
if (sector > -1) {
// decode it
-306
View File
@@ -1914,309 +1914,3 @@ int convert_mfc_2_arr(uint8_t *in, uint16_t ilen, uint8_t *out, uint16_t *olen)
}
return PM3_SUCCESS;
}
static const vigik_pk_t vigik_rsa_pk[] = {
{"La Poste Service Universel", 0x07AA, "AB9953CBFCCD9375B6C028ADBAB7584BED15B9CA037FADED9765996F9EA1AB983F3041C90DA3A198804FF90D5D872A96A4988F91F2243B821E01C5021E3ED4E1BA83B7CFECAB0E766D8563164DE0B2412AE4E6EA63804DF5C19C7AA78DC14F608294D732D7C8C67A88C6F84C0F2E3FAFAE34084349E11AB5953AC68729D07715"},
{"La Poste Service Universel", 0x07AA, "1577D02987C63A95B51AE149430834AEAF3F2E0F4CF8C6887AC6C8D732D79482604FC18DA77A9CC1F54D8063EAE6E42A41B2E04D1663856D760EABECCFB783BAE1D43E1E02C5011E823B24F2918F98A4962A875D0DF94F8098A1A30DC941303F98ABA19E6F996597EDAD7F03CAB915ED4B58B7BAAD28C0B67593CDFCCB5399AB"},
{"La Poste Autres Services", 0x07AB, "A6D99B8D902893B04F3F8DE56CB6BF24338FEE897C1BCE6DFD4EBD05B7B1A07FD2EB564BB4F7D35DBFE0A42966C2C137AD156E3DAB62904592BCA20C0BC7B8B1E261EF82D53F52D203843566305A49A22062DECC38C2FE3864CAD08E79219487651E2F79F1C9392B48CAFE1BFFAFF4802AE451E7A283E55A4026AD1E82DF1A15"},
{"La Poste Autres Services", 0x07AB, "151adf821ead26405ae583a2e751e42a80f4afff1bfeca482b39c9f1792f1e65879421798ed0ca6438fec238ccde6220a2495a3066358403d2523fd582ef61e2b1b8c70b0ca2bc92459062ab3d6e15ad37c1c26629a4e0bf5dd3f7b44b56ebd27fa0b1b705bd4efd6dce1b7c89ee8f3324bfb66ce58d3f4fb09328908d9bd9a6"},
{"France Telecom", 0x07AC, "C44DBCD92F9DCF42F4902A87335DBB35D2FF530CDB09814CFA1F4B95A1BD018D099BC6AB69F667B4922AE1ED826E72951AA3E0EAAA7D49A695F04F8CDAAE2D18D10D25BD529CBB05ABF070DC7C041EC35C2BA7F58CC4C349983CC6E11A5CBE828FB8ECBC26F08E1094A6B44C8953C8E1BAFD214DF3E69F430A98CCC75C03669D"},
{"France Telecom", 0x07AC, "9d66035cc7cc980a439fe6f34d21fdbae1c853894cb4a694108ef026bcecb88f82be5c1ae1c63c9849c3c48cf5a72b5cc31e047cdc70f0ab05bb9c52bd250dd1182daeda8c4ff095a6497daaeae0a31a95726e82ede12a92b467f669abc69b098d01bda1954b1ffa4c8109db0c53ffd235bb5d33872a90f442cf9d2fd9bc4dc4"},
{"EDF-GDF", 0x07AD, "B35193DBD2F88A21CDCFFF4BF84F7FC036A991A363DCB3E802407A5E5879DC2127EECFC520779E79E911394882482C87D09A88B0711CBC2973B77FFDAE40EA0001F595072708C558B484AB89D02BCBCB971FF1B80371C0BE30CB13661078078BB68EBCCA524B9DD55EBF7D47D9355AFC95511350CC1103A5DEE847868848B235"},
{"EDF-GDF", 0x07AD, "35b248888647e8dea50311cc50135195fc5a35d9477dbf5ed59d4b52cabc8eb68b0778106613cb30bec07103b8f11f97cbcb2bd089ab84b458c508270795f50100ea40aefd7fb77329bc1c71b0889ad0872c4882483911e9799e7720c5cfee2721dc79585e7a4002e8b3dc63a391a936c07f4ff84bffcfcd218af8d2db9351b3"},
{NULL, 0, NULL}
};
const char *vigik_get_service(uint16_t service_code) {
for (int i = 0; i < ARRAYLEN(vigik_rsa_pk); ++i)
if (service_code == vigik_rsa_pk[i].code)
return vigik_rsa_pk[i].desc;
//No match, return default
return vigik_rsa_pk[ARRAYLEN(vigik_rsa_pk) - 1].desc;
}
int vigik_verify(mfc_vigik_t *d) {
#define PUBLIC_VIGIK_KEYLEN 128
// iso9796
// Exponent V = 2
// n = The public modulus n is the product of the secret prime factors p and q. Its length is 1024 bits.
if (g_debugMode == DEBUG) {
PrintAndLogEx(INFO, "Raw");
print_hex_noascii_break((uint8_t *)d, sizeof(*d) - sizeof(d->rsa_signature), MFBLOCK_SIZE * 2);
PrintAndLogEx(INFO, "Raw signature");
print_hex_noascii_break(d->rsa_signature, sizeof(d->rsa_signature), MFBLOCK_SIZE * 2);
}
/*
int dl = 0;
param_gethex_to_eol("1C07D46DA3849326D24B3468BD76673F4F3C41827DC413E81E4F3C7804FAC727213059B21D047510D6432448643A92EBFC67FBEDDAB468D13D948B172F5EBC79A0E3FEFDFAF4E81FC7108E070F1E3CD0", 0, signature, PUBLIC_VIGIK_KEYLEN, &dl);
param_gethex_to_eol("1AB86FE0C17FFFFE4379D5E15A4B2FAFFEFCFA0F1F3F7FA03E7DDDF1E3C78FFFB1F0E23F7FFF51584771C5C18307FEA36CA74E60AA6B0409ACA66A9EC155F4E9112345708A2B8457E722608EE1157408", 0, signature, PUBLIC_VIGIK_KEYLEN, &dl);
signature_len = dl;
*/
uint8_t rev_sig[128];
reverse_array_copy(d->rsa_signature, sizeof(d->rsa_signature), rev_sig);
PrintAndLogEx(INFO, "Raw signature reverse");
print_hex_noascii_break(rev_sig, sizeof(d->rsa_signature), MFBLOCK_SIZE * 2);
// t = 0xBC = Implicitly known
// t = 0xCC = look at byte before to determine hash function
// uint8_t T[] = {0x33, 0xCC};
// Success decrypt would mean 0x4b BB ... BB BA padding
// padding, message, hash, 8 bits or 16 bits
// signature = h( C || M1 || h(M2) )
// 1024 - 786 - 160 - 16 -1
// salt C
// message M = 96 bytes, 768 bits
// sha1 hash H = 20 bytes, 160 bits
// padding = 20 bytes, 96 bits
uint8_t i;
bool is_valid = false;
for (i = 0; i < ARRAYLEN(vigik_rsa_pk); i++) {
if (vigik_rsa_pk[i].desc == NULL) {
break;
}
mbedtls_mpi RN, E;
mbedtls_mpi_init(&RN);
// exponent 2 = even
mbedtls_mpi_init(&E);
mbedtls_mpi_add_int(&E, &E, 2);
int dl = 0;
uint8_t n[PUBLIC_VIGIK_KEYLEN];
memset(n, 0, sizeof(n));
param_gethex_to_eol(vigik_rsa_pk[i].n, 0, n, PUBLIC_VIGIK_KEYLEN, &dl);
// convert
mbedtls_mpi N, s, sqr, res;
mbedtls_mpi_init(&N);
mbedtls_mpi_init(&s);
mbedtls_mpi_init(&sqr);
mbedtls_mpi_init(&res);
mbedtls_mpi_read_binary(&N, (const unsigned char *)n, PUBLIC_VIGIK_KEYLEN);
//mbedtls_mpi_read_binary(&s, (const unsigned char*)signature, signature_len);
mbedtls_mpi_read_binary(&s, (const unsigned char *)rev_sig, sizeof(d->rsa_signature));
// check is sign < (N/2)
mbedtls_mpi n_2;
mbedtls_mpi_init(&n_2);
mbedtls_mpi_copy(&n_2, &N);
mbedtls_mpi_shift_r(&n_2, 1);
bool is_less = (mbedtls_mpi_cmp_mpi(&s, &n_2) > 0) ? false : true;
PrintAndLogEx(DEBUG, "z < (N/2) ..... %s", (is_less) ? _GREEN_("YES") : _RED_("NO"));
mbedtls_mpi_free(&n_2);
if (is_less) {
mbedtls_mpi_exp_mod(&sqr, &s, &E, &N, &RN);
} else {
continue;
}
/*
if v is even and
⎯ if J* mod 8 = 1, then f* = n–J*.
⎯ if J* mod 8 = 4, then f* = J*,
⎯ if J* mod 8 = 6, then f* = 2J*,
⎯ if J* mod 8 = 7, then f* = 2(n–J*),
*/
uint8_t b2 = mbedtls_mpi_get_bit(&sqr, 2);
uint8_t b1 = mbedtls_mpi_get_bit(&sqr, 1);
uint8_t b0 = mbedtls_mpi_get_bit(&sqr, 0);
uint8_t lsb = (b2 << 2) | (b1 << 1) | b0;
/*
//1
mbedtls_mpi_sub_mpi(&res, &N, &sqr);
mbedtls_mpi_write_file( "[=] 1... ", &res, 16, NULL );
// 4
mbedtls_mpi_copy(&res, &sqr);
mbedtls_mpi_write_file( "[=] 4... ", &res, 16, NULL );
// 6
mbedtls_mpi_mul_int(&res, &sqr, 2);
mbedtls_mpi_write_file( "[=] 6... ", &res, 16, NULL );
// 7
mbedtls_mpi foo;
mbedtls_mpi_init(&foo);
mbedtls_mpi_sub_mpi(&foo, &N, &sqr);
mbedtls_mpi_mul_int(&res, &foo, 2);
mbedtls_mpi_free(&foo);
mbedtls_mpi_write_file( "[=] 7... ", &res, 16, NULL );
*/
switch (lsb) {
case 1: {
mbedtls_mpi_sub_mpi(&res, &N, &sqr);
break;
}
case 4: {
mbedtls_mpi_copy(&res, &sqr);
break;
}
case 6: {
mbedtls_mpi_mul_int(&res, &sqr, 2);
break;
}
case 7: {
mbedtls_mpi foo2;
mbedtls_mpi_init(&foo2);
mbedtls_mpi_sub_mpi(&foo2, &N, &sqr);
mbedtls_mpi_mul_int(&res, &foo2, 2);
mbedtls_mpi_free(&foo2);
break;
}
default: {
continue;
}
}
PrintAndLogEx(DEBUG, "LSB............ " _GREEN_("%u"), lsb);
if (g_debugMode == DEBUG) {
mbedtls_mpi_write_file("[=] N.............. ", &N, 16, NULL);
mbedtls_mpi_write_file("[=] signature...... ", &s, 16, NULL);
mbedtls_mpi_write_file("[=] square mod n... ", &sqr, 16, NULL);
mbedtls_mpi_write_file("[=] n-fs........... ", &res, 16, NULL);
}
uint8_t nfs[128] = {0};
mbedtls_mpi_write_binary(&res, nfs, sizeof(nfs));
// xor 0xDC01
int count_zero = 0;
for (int x = 0; x < sizeof(nfs); x += 2) {
nfs[x] ^= 0xDC;
nfs[x + 1] ^= 0x01;
if (nfs[x] == 0x00)
count_zero++;
if (nfs[x + 1] == 0x00)
count_zero++;
}
if (count_zero > 10) {
PrintAndLogEx(INFO, "");
PrintAndLogEx(INFO, "Message XORED");
print_hex_noascii_break(nfs, sizeof(nfs), 32);
PrintAndLogEx(INFO, "\n");
is_valid = true;
break;
}
/*
if (bar == 0) {
typedef struct vigik_rsa_s {
uint8_t rsa[127];
uint8_t hash;
} vigik_rsa_t;
vigik_rsa_t ts;
memcpy(&ts, nfs, sizeof(ts));
if ( ts.hash == 0xCC ) {
PrintAndLogEx(INFO, "Hash byte... 0x%02X", ts.hash);
switch(ts.rsa[126]) {
case 0x11:
PrintAndLogEx(INFO, "Hash algo ( 0x%02X ) - SHA1");
break;
case 0x22:
PrintAndLogEx(INFO, "Hash algo ( 0x%02X ) - RIPEMD");
break;
case 0x33:
PrintAndLogEx(INFO, "Hash algo ( 0x%02X ) - SHA1");
break;
default:
PrintAndLogEx(INFO, "Hash algo ( 0x%02X ) - " _RED_("err"));
break;
}
} else if ( ts.hash == 0xBC) {
PrintAndLogEx(INFO, "Hash byte... 0x%02X - " _GREEN_("implict"), ts.hash);
} else {
PrintAndLogEx(INFO, "Hash byte... 0x%02x - " _RED_("err"), ts.hash);
}
PrintAndLogEx(INFO, "Message w padding");
print_hex_noascii_break(ts.rsa, sizeof(ts.rsa) - 20, 32);
}
*/
mbedtls_mpi_free(&N);
mbedtls_mpi_free(&s);
mbedtls_mpi_free(&res);
mbedtls_mpi_free(&RN);
mbedtls_mpi_free(&E);
}
PrintAndLogEx(INFO, "");
PrintAndLogEx(INFO, "--- " _CYAN_("Tag Signature"));
PrintAndLogEx(INFO, "RSA: 1024bit");
if (is_valid == false || i == ARRAYLEN(vigik_rsa_pk)) {
PrintAndLogEx(INFO, "Signature:");
print_hex_noascii_break(d->rsa_signature, sizeof(d->rsa_signature), MFBLOCK_SIZE * 2);
PrintAndLogEx(SUCCESS, "Signature verification: " _RED_("failed"));
return PM3_ESOFT;
}
PrintAndLogEx(INFO, "Signature public key name: " _YELLOW_("%s"), vigik_rsa_pk[i].desc);
PrintAndLogEx(INFO, "Signature public key value:");
PrintAndLogEx(INFO, "%.64s", vigik_rsa_pk[i].n);
PrintAndLogEx(INFO, "%.64s", vigik_rsa_pk[i].n + 64);
PrintAndLogEx(INFO, "%.64s", vigik_rsa_pk[i].n + 128);
PrintAndLogEx(INFO, "%.64s", vigik_rsa_pk[i].n + 192);
PrintAndLogEx(INFO, "Signature:");
print_hex_noascii_break(d->rsa_signature, sizeof(d->rsa_signature), MFBLOCK_SIZE * 2);
PrintAndLogEx(SUCCESS, "Signature verification: " _GREEN_("successful"));
return PM3_SUCCESS;
}
int vigik_annotate(mfc_vigik_t *d) {
if (d == NULL)
return PM3_EINVARG;
PrintAndLogEx(INFO, "Manufacture......... %s", sprint_hex(d->b0, sizeof(d->b0)));
PrintAndLogEx(INFO, "MAD................. %s", sprint_hex(d->mad, sizeof(d->mad)));
PrintAndLogEx(INFO, "Counters............ %u", d->counters);
PrintAndLogEx(INFO, "rtf................. %s", sprint_hex(d->rtf, sizeof(d->rtf)));
PrintAndLogEx(INFO, "Service code........ 0x%08x / %u - " _YELLOW_("%s"), d->service_code, d->service_code, vigik_get_service(d->service_code));
PrintAndLogEx(INFO, "Info flag........... %u -", d->info_flag); // , sprint_bin(d->info_flag, 1));
PrintAndLogEx(INFO, "Key version......... %u", d->key_version);
PrintAndLogEx(INFO, "PTR Counter......... %u", d->ptr_counter);
PrintAndLogEx(INFO, "Counter num......... %u", d->counter_num);
PrintAndLogEx(INFO, "Slot access date.... %s", sprint_hex(d->slot_access_date, sizeof(d->slot_access_date)));
PrintAndLogEx(INFO, "Slot dst duration... %u", d->slot_dst_duration);
PrintAndLogEx(INFO, "Other Slots......... %s", sprint_hex(d->other_slots, sizeof(d->other_slots)));
PrintAndLogEx(INFO, "Services counter.... %u", d->services_counter);
PrintAndLogEx(INFO, "Loading date........ %s", sprint_hex(d->loading_date, sizeof(d->loading_date)));
PrintAndLogEx(INFO, "Reserved null....... %u", d->reserved_null);
PrintAndLogEx(INFO, "----------------------------------------------------------------");
PrintAndLogEx(INFO, "");
vigik_verify(d);
PrintAndLogEx(INFO, "----------------------------------------------------------------");
PrintAndLogEx(INFO, "");
return PM3_SUCCESS;
}
-3
View File
@@ -127,7 +127,4 @@ void mf_crypto1_decrypt(struct Crypto1State *pcs, uint8_t *data, int len, bool i
// remove all sector trailers in a MFC dump
int convert_mfc_2_arr(uint8_t *in, uint16_t ilen, uint8_t *out, uint16_t *olen);
const char *vigik_get_service(uint16_t service_code);
int vigik_verify(mfc_vigik_t *d);
int vigik_annotate(mfc_vigik_t *d);
#endif
+83
View File
@@ -0,0 +1,83 @@
//-----------------------------------------------------------------------------
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------
// HID PACS parser for MIFARE Classic dumps
//-----------------------------------------------------------------------------
#include "parsehid.h"
#include <string.h>
#include "commonutil.h"
#include "ui.h" // PrintAndLogEx
#include "util.h" // sprint_hex_inrow, hex_to_buffer
#include "protocols.h" // MFBLOCK_SIZE
#include "mifare/mad.h"
#include "mifare/mifare4.h" // mfFirstBlockOfSector
#include "wiegand_formats.h"
#include "wiegand_formatutils.h"
// The PACS sector holds the credential in its three data blocks, tagged by a
// 0x02 marker at the head of the second one.
#define HID_PACS_MARKER 0x02
bool is_valid_hid_card(const uint8_t *dump, size_t dumplen) {
if (dump == NULL || dumplen < sizeof(mad1_sector_t)) {
return false;
}
return (DetectHID((const mad1_sector_t *)dump, HID_MAD_AID) > -1);
}
int hid_parser_parse(const uint8_t *dump, size_t dumplen) {
if (is_valid_hid_card(dump, dumplen) == false) {
return PM3_EINVARG;
}
int sector = DetectHID((const mad1_sector_t *)dump, HID_MAD_AID);
size_t pacs_off = mfFirstBlockOfSector(sector) * MFBLOCK_SIZE;
uint8_t pacs_sector[MFBLOCK_SIZE * 3] = {0};
if (pacs_off + sizeof(pacs_sector) > dumplen) {
return PM3_EINVARG;
}
memcpy(pacs_sector, dump + pacs_off, sizeof(pacs_sector));
if (pacs_sector[MFBLOCK_SIZE] != HID_PACS_MARKER) {
return PM3_ESOFT;
}
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "------------------------- " _CYAN_("Wiegand") " ---------------------------");
PrintAndLogEx(INFO, _CYAN_("HID PACS detected"));
PrintAndLogEx(SUCCESS, "Raw...... " _GREEN_("%s"), sprint_hex_inrow(pacs_sector + 24, 8));
//todo: remove preamble/sentinel
uint32_t top = 0, mid = 0, bot = 0;
char hexstr[16 + 1] = {0};
hex_to_buffer((uint8_t *)hexstr, pacs_sector + 24, 8, sizeof(hexstr) - 1, 0, 0, true);
hexstring_to_u96(&top, &mid, &bot, hexstr);
char binstr[64 + 1];
hextobinstring(binstr, hexstr);
char *pbin = binstr;
while (strlen(pbin) && *(++pbin) == '0');
PrintAndLogEx(SUCCESS, "Binary... " _GREEN_("%s"), pbin);
decode_wiegand(top, mid, bot, 0);
return PM3_SUCCESS;
}
+33
View File
@@ -0,0 +1,33 @@
//-----------------------------------------------------------------------------
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------
// HID PACS parser for MIFARE Classic dumps
//-----------------------------------------------------------------------------
#ifndef PARSEHID_H__
#define PARSEHID_H__
#include "common.h"
// MAD application id the HID Global access control scheme claims
#define HID_MAD_AID 0x484d
// True when the MAD of this dump advertises the HID application
bool is_valid_hid_card(const uint8_t *dump, size_t dumplen);
// Decode the HID PACS credential out of a whole card dump and print it
int hid_parser_parse(const uint8_t *dump, size_t dumplen);
#endif
+393
View File
@@ -0,0 +1,393 @@
//-----------------------------------------------------------------------------
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------
// VIGIK PACS parser for MIFARE Classic dumps
//-----------------------------------------------------------------------------
#include "parsevigik.h"
#include <stdlib.h>
#include <string.h>
#include "commonutil.h"
#include "ui.h" // PrintAndLogEx
#include "util.h"
#include "protocols.h" // MFBLOCK_SIZE
#include "mifare/mad.h"
#include "mbedtls/bignum.h"
static const vigik_pk_t vigik_rsa_pk[] = {
{"La Poste Service Universel", 0x07AA, "AB9953CBFCCD9375B6C028ADBAB7584BED15B9CA037FADED9765996F9EA1AB983F3041C90DA3A198804FF90D5D872A96A4988F91F2243B821E01C5021E3ED4E1BA83B7CFECAB0E766D8563164DE0B2412AE4E6EA63804DF5C19C7AA78DC14F608294D732D7C8C67A88C6F84C0F2E3FAFAE34084349E11AB5953AC68729D07715"},
{"La Poste Service Universel", 0x07AA, "1577D02987C63A95B51AE149430834AEAF3F2E0F4CF8C6887AC6C8D732D79482604FC18DA77A9CC1F54D8063EAE6E42A41B2E04D1663856D760EABECCFB783BAE1D43E1E02C5011E823B24F2918F98A4962A875D0DF94F8098A1A30DC941303F98ABA19E6F996597EDAD7F03CAB915ED4B58B7BAAD28C0B67593CDFCCB5399AB"},
{"La Poste Autres Services", 0x07AB, "A6D99B8D902893B04F3F8DE56CB6BF24338FEE897C1BCE6DFD4EBD05B7B1A07FD2EB564BB4F7D35DBFE0A42966C2C137AD156E3DAB62904592BCA20C0BC7B8B1E261EF82D53F52D203843566305A49A22062DECC38C2FE3864CAD08E79219487651E2F79F1C9392B48CAFE1BFFAFF4802AE451E7A283E55A4026AD1E82DF1A15"},
{"La Poste Autres Services", 0x07AB, "151adf821ead26405ae583a2e751e42a80f4afff1bfeca482b39c9f1792f1e65879421798ed0ca6438fec238ccde6220a2495a3066358403d2523fd582ef61e2b1b8c70b0ca2bc92459062ab3d6e15ad37c1c26629a4e0bf5dd3f7b44b56ebd27fa0b1b705bd4efd6dce1b7c89ee8f3324bfb66ce58d3f4fb09328908d9bd9a6"},
{"France Telecom", 0x07AC, "C44DBCD92F9DCF42F4902A87335DBB35D2FF530CDB09814CFA1F4B95A1BD018D099BC6AB69F667B4922AE1ED826E72951AA3E0EAAA7D49A695F04F8CDAAE2D18D10D25BD529CBB05ABF070DC7C041EC35C2BA7F58CC4C349983CC6E11A5CBE828FB8ECBC26F08E1094A6B44C8953C8E1BAFD214DF3E69F430A98CCC75C03669D"},
{"France Telecom", 0x07AC, "9d66035cc7cc980a439fe6f34d21fdbae1c853894cb4a694108ef026bcecb88f82be5c1ae1c63c9849c3c48cf5a72b5cc31e047cdc70f0ab05bb9c52bd250dd1182daeda8c4ff095a6497daaeae0a31a95726e82ede12a92b467f669abc69b098d01bda1954b1ffa4c8109db0c53ffd235bb5d33872a90f442cf9d2fd9bc4dc4"},
{"EDF-GDF", 0x07AD, "B35193DBD2F88A21CDCFFF4BF84F7FC036A991A363DCB3E802407A5E5879DC2127EECFC520779E79E911394882482C87D09A88B0711CBC2973B77FFDAE40EA0001F595072708C558B484AB89D02BCBCB971FF1B80371C0BE30CB13661078078BB68EBCCA524B9DD55EBF7D47D9355AFC95511350CC1103A5DEE847868848B235"},
{"EDF-GDF", 0x07AD, "35b248888647e8dea50311cc50135195fc5a35d9477dbf5ed59d4b52cabc8eb68b0778106613cb30bec07103b8f11f97cbcb2bd089ab84b458c508270795f50100ea40aefd7fb77329bc1c71b0889ad0872c4882483911e9799e7720c5cfee2721dc79585e7a4002e8b3dc63a391a936c07f4ff84bffcfcd218af8d2db9351b3"},
{NULL, 0, NULL}
};
const char *vigik_get_service(uint16_t service_code) {
for (int i = 0; i < ARRAYLEN(vigik_rsa_pk); ++i)
if (service_code == vigik_rsa_pk[i].code)
return vigik_rsa_pk[i].desc;
//No match, return default
return vigik_rsa_pk[ARRAYLEN(vigik_rsa_pk) - 1].desc;
}
int vigik_verify(mfc_vigik_t *d) {
#define PUBLIC_VIGIK_KEYLEN 128
// iso9796
// Exponent V = 2
// n = The public modulus n is the product of the secret prime factors p and q. Its length is 1024 bits.
if (g_debugMode == DEBUG) {
PrintAndLogEx(INFO, "Raw");
print_hex_noascii_break((uint8_t *)d, sizeof(*d) - sizeof(d->rsa_signature), MFBLOCK_SIZE * 2);
PrintAndLogEx(INFO, "Raw signature");
print_hex_noascii_break(d->rsa_signature, sizeof(d->rsa_signature), MFBLOCK_SIZE * 2);
}
/*
int dl = 0;
param_gethex_to_eol("1C07D46DA3849326D24B3468BD76673F4F3C41827DC413E81E4F3C7804FAC727213059B21D047510D6432448643A92EBFC67FBEDDAB468D13D948B172F5EBC79A0E3FEFDFAF4E81FC7108E070F1E3CD0", 0, signature, PUBLIC_VIGIK_KEYLEN, &dl);
param_gethex_to_eol("1AB86FE0C17FFFFE4379D5E15A4B2FAFFEFCFA0F1F3F7FA03E7DDDF1E3C78FFFB1F0E23F7FFF51584771C5C18307FEA36CA74E60AA6B0409ACA66A9EC155F4E9112345708A2B8457E722608EE1157408", 0, signature, PUBLIC_VIGIK_KEYLEN, &dl);
signature_len = dl;
*/
uint8_t rev_sig[128];
reverse_array_copy(d->rsa_signature, sizeof(d->rsa_signature), rev_sig);
PrintAndLogEx(INFO, "Raw signature reverse");
print_hex_noascii_break(rev_sig, sizeof(d->rsa_signature), MFBLOCK_SIZE * 2);
// t = 0xBC = Implicitly known
// t = 0xCC = look at byte before to determine hash function
// uint8_t T[] = {0x33, 0xCC};
// Success decrypt would mean 0x4b BB ... BB BA padding
// padding, message, hash, 8 bits or 16 bits
// signature = h( C || M1 || h(M2) )
// 1024 - 786 - 160 - 16 -1
// salt C
// message M = 96 bytes, 768 bits
// sha1 hash H = 20 bytes, 160 bits
// padding = 20 bytes, 96 bits
uint8_t i;
bool is_valid = false;
for (i = 0; i < ARRAYLEN(vigik_rsa_pk); i++) {
if (vigik_rsa_pk[i].desc == NULL) {
break;
}
mbedtls_mpi RN, E;
mbedtls_mpi_init(&RN);
// exponent 2 = even
mbedtls_mpi_init(&E);
mbedtls_mpi_add_int(&E, &E, 2);
int dl = 0;
uint8_t n[PUBLIC_VIGIK_KEYLEN];
memset(n, 0, sizeof(n));
param_gethex_to_eol(vigik_rsa_pk[i].n, 0, n, PUBLIC_VIGIK_KEYLEN, &dl);
// convert
mbedtls_mpi N, s, sqr, res;
mbedtls_mpi_init(&N);
mbedtls_mpi_init(&s);
mbedtls_mpi_init(&sqr);
mbedtls_mpi_init(&res);
mbedtls_mpi_read_binary(&N, (const unsigned char *)n, PUBLIC_VIGIK_KEYLEN);
//mbedtls_mpi_read_binary(&s, (const unsigned char*)signature, signature_len);
mbedtls_mpi_read_binary(&s, (const unsigned char *)rev_sig, sizeof(d->rsa_signature));
// check is sign < (N/2)
mbedtls_mpi n_2;
mbedtls_mpi_init(&n_2);
mbedtls_mpi_copy(&n_2, &N);
mbedtls_mpi_shift_r(&n_2, 1);
bool is_less = (mbedtls_mpi_cmp_mpi(&s, &n_2) > 0) ? false : true;
PrintAndLogEx(DEBUG, "z < (N/2) ..... %s", (is_less) ? _GREEN_("YES") : _RED_("NO"));
mbedtls_mpi_free(&n_2);
if (is_less) {
mbedtls_mpi_exp_mod(&sqr, &s, &E, &N, &RN);
} else {
continue;
}
/*
if v is even and
⎯ if J* mod 8 = 1, then f* = n–J*.
⎯ if J* mod 8 = 4, then f* = J*,
⎯ if J* mod 8 = 6, then f* = 2J*,
⎯ if J* mod 8 = 7, then f* = 2(n–J*),
*/
uint8_t b2 = mbedtls_mpi_get_bit(&sqr, 2);
uint8_t b1 = mbedtls_mpi_get_bit(&sqr, 1);
uint8_t b0 = mbedtls_mpi_get_bit(&sqr, 0);
uint8_t lsb = (b2 << 2) | (b1 << 1) | b0;
/*
//1
mbedtls_mpi_sub_mpi(&res, &N, &sqr);
mbedtls_mpi_write_file( "[=] 1... ", &res, 16, NULL );
// 4
mbedtls_mpi_copy(&res, &sqr);
mbedtls_mpi_write_file( "[=] 4... ", &res, 16, NULL );
// 6
mbedtls_mpi_mul_int(&res, &sqr, 2);
mbedtls_mpi_write_file( "[=] 6... ", &res, 16, NULL );
// 7
mbedtls_mpi foo;
mbedtls_mpi_init(&foo);
mbedtls_mpi_sub_mpi(&foo, &N, &sqr);
mbedtls_mpi_mul_int(&res, &foo, 2);
mbedtls_mpi_free(&foo);
mbedtls_mpi_write_file( "[=] 7... ", &res, 16, NULL );
*/
switch (lsb) {
case 1: {
mbedtls_mpi_sub_mpi(&res, &N, &sqr);
break;
}
case 4: {
mbedtls_mpi_copy(&res, &sqr);
break;
}
case 6: {
mbedtls_mpi_mul_int(&res, &sqr, 2);
break;
}
case 7: {
mbedtls_mpi foo2;
mbedtls_mpi_init(&foo2);
mbedtls_mpi_sub_mpi(&foo2, &N, &sqr);
mbedtls_mpi_mul_int(&res, &foo2, 2);
mbedtls_mpi_free(&foo2);
break;
}
default: {
continue;
}
}
PrintAndLogEx(DEBUG, "LSB............ " _GREEN_("%u"), lsb);
if (g_debugMode == DEBUG) {
mbedtls_mpi_write_file("[=] N.............. ", &N, 16, NULL);
mbedtls_mpi_write_file("[=] signature...... ", &s, 16, NULL);
mbedtls_mpi_write_file("[=] square mod n... ", &sqr, 16, NULL);
mbedtls_mpi_write_file("[=] n-fs........... ", &res, 16, NULL);
}
uint8_t nfs[128] = {0};
mbedtls_mpi_write_binary(&res, nfs, sizeof(nfs));
// xor 0xDC01
int count_zero = 0;
for (int x = 0; x < sizeof(nfs); x += 2) {
nfs[x] ^= 0xDC;
nfs[x + 1] ^= 0x01;
if (nfs[x] == 0x00)
count_zero++;
if (nfs[x + 1] == 0x00)
count_zero++;
}
if (count_zero > 10) {
PrintAndLogEx(INFO, "");
PrintAndLogEx(INFO, "Message XORED");
print_hex_noascii_break(nfs, sizeof(nfs), 32);
PrintAndLogEx(INFO, "\n");
is_valid = true;
break;
}
/*
if (bar == 0) {
typedef struct vigik_rsa_s {
uint8_t rsa[127];
uint8_t hash;
} vigik_rsa_t;
vigik_rsa_t ts;
memcpy(&ts, nfs, sizeof(ts));
if ( ts.hash == 0xCC ) {
PrintAndLogEx(INFO, "Hash byte... 0x%02X", ts.hash);
switch(ts.rsa[126]) {
case 0x11:
PrintAndLogEx(INFO, "Hash algo ( 0x%02X ) - SHA1");
break;
case 0x22:
PrintAndLogEx(INFO, "Hash algo ( 0x%02X ) - RIPEMD");
break;
case 0x33:
PrintAndLogEx(INFO, "Hash algo ( 0x%02X ) - SHA1");
break;
default:
PrintAndLogEx(INFO, "Hash algo ( 0x%02X ) - " _RED_("err"));
break;
}
} else if ( ts.hash == 0xBC) {
PrintAndLogEx(INFO, "Hash byte... 0x%02X - " _GREEN_("implict"), ts.hash);
} else {
PrintAndLogEx(INFO, "Hash byte... 0x%02x - " _RED_("err"), ts.hash);
}
PrintAndLogEx(INFO, "Message w padding");
print_hex_noascii_break(ts.rsa, sizeof(ts.rsa) - 20, 32);
}
*/
mbedtls_mpi_free(&N);
mbedtls_mpi_free(&s);
mbedtls_mpi_free(&res);
mbedtls_mpi_free(&RN);
mbedtls_mpi_free(&E);
}
PrintAndLogEx(INFO, "");
PrintAndLogEx(INFO, "--- " _CYAN_("Tag Signature"));
PrintAndLogEx(INFO, "RSA: 1024bit");
if (is_valid == false || i == ARRAYLEN(vigik_rsa_pk)) {
PrintAndLogEx(INFO, "Signature:");
print_hex_noascii_break(d->rsa_signature, sizeof(d->rsa_signature), MFBLOCK_SIZE * 2);
PrintAndLogEx(SUCCESS, "Signature verification: " _RED_("failed"));
return PM3_ESOFT;
}
PrintAndLogEx(INFO, "Signature public key name: " _YELLOW_("%s"), vigik_rsa_pk[i].desc);
PrintAndLogEx(INFO, "Signature public key value:");
PrintAndLogEx(INFO, "%.64s", vigik_rsa_pk[i].n);
PrintAndLogEx(INFO, "%.64s", vigik_rsa_pk[i].n + 64);
PrintAndLogEx(INFO, "%.64s", vigik_rsa_pk[i].n + 128);
PrintAndLogEx(INFO, "%.64s", vigik_rsa_pk[i].n + 192);
PrintAndLogEx(INFO, "Signature:");
print_hex_noascii_break(d->rsa_signature, sizeof(d->rsa_signature), MFBLOCK_SIZE * 2);
PrintAndLogEx(SUCCESS, "Signature verification: " _GREEN_("successful"));
return PM3_SUCCESS;
}
int vigik_annotate(mfc_vigik_t *d) {
if (d == NULL)
return PM3_EINVARG;
PrintAndLogEx(INFO, "Manufacture......... %s", sprint_hex(d->b0, sizeof(d->b0)));
PrintAndLogEx(INFO, "MAD................. %s", sprint_hex(d->mad, sizeof(d->mad)));
PrintAndLogEx(INFO, "Counters............ %u", d->counters);
PrintAndLogEx(INFO, "rtf................. %s", sprint_hex(d->rtf, sizeof(d->rtf)));
PrintAndLogEx(INFO, "Service code........ 0x%08x / %u - " _YELLOW_("%s"), d->service_code, d->service_code, vigik_get_service(d->service_code));
PrintAndLogEx(INFO, "Info flag........... %u -", d->info_flag); // , sprint_bin(d->info_flag, 1));
PrintAndLogEx(INFO, "Key version......... %u", d->key_version);
PrintAndLogEx(INFO, "PTR Counter......... %u", d->ptr_counter);
PrintAndLogEx(INFO, "Counter num......... %u", d->counter_num);
PrintAndLogEx(INFO, "Slot access date.... %s", sprint_hex(d->slot_access_date, sizeof(d->slot_access_date)));
PrintAndLogEx(INFO, "Slot dst duration... %u", d->slot_dst_duration);
PrintAndLogEx(INFO, "Other Slots......... %s", sprint_hex(d->other_slots, sizeof(d->other_slots)));
PrintAndLogEx(INFO, "Services counter.... %u", d->services_counter);
PrintAndLogEx(INFO, "Loading date........ %s", sprint_hex(d->loading_date, sizeof(d->loading_date)));
PrintAndLogEx(INFO, "Reserved null....... %u", d->reserved_null);
PrintAndLogEx(INFO, "----------------------------------------------------------------");
PrintAndLogEx(INFO, "");
vigik_verify(d);
PrintAndLogEx(INFO, "----------------------------------------------------------------");
PrintAndLogEx(INFO, "");
return PM3_SUCCESS;
}
bool is_valid_vigik_card(const uint8_t *dump, size_t dumplen) {
if (dump == NULL || dumplen < sizeof(mad1_sector_t)) {
return false;
}
return (DetectHID((const mad1_sector_t *)dump, VIGIK_MAD_AID) > -1);
}
int vigik_parser_parse(const uint8_t *dump, size_t dumplen) {
if (is_valid_vigik_card(dump, dumplen) == false) {
return PM3_EINVARG;
}
const mad1_sector_t *s0 = (const mad1_sector_t *)dump;
PrintAndLogEx(INFO, "");
PrintAndLogEx(INFO, _CYAN_("VIGIK PACS detected"));
mad_entry_list_t mad_list = {0};
int res = MADDecode(s0, NULL, &mad_list, false, true);
if (res != PM3_SUCCESS) {
PrintAndLogEx(ERR, "can't decode MAD");
return res;
}
// The VIGIK structure is the data blocks of sector 0 followed by the data
// blocks of every sector the MAD hands to the VIGIK application.
union {
uint8_t *bytes;
mfc_vigik_t *vigik;
} d;
d.bytes = calloc(dumplen, sizeof(uint8_t));
if (d.bytes == NULL) {
PrintAndLogEx(WARNING, "Failed to allocate memory");
return PM3_EMALLOC;
}
uint16_t dlen = 0;
memcpy(d.bytes + dlen, dump, MFBLOCK_SIZE * 3);
dlen += MFBLOCK_SIZE * 3;
for (size_t i = 0; i < mad_list.len; i++) {
if (VIGIK_MAD_AID == mad_list.entries[i].aid || VIGIK_MAD_AID_ALT == mad_list.entries[i].aid) {
uint32_t offset = mad_list.entries[i].sector * MFBLOCK_SIZE * 4;
if (offset + (MFBLOCK_SIZE * 3) > dumplen) {
continue;
}
memcpy(d.bytes + dlen, dump + offset, MFBLOCK_SIZE * 3);
dlen += MFBLOCK_SIZE * 3;
}
}
vigik_annotate(d.vigik);
free(d.bytes);
return PM3_SUCCESS;
}
+40
View File
@@ -0,0 +1,40 @@
//-----------------------------------------------------------------------------
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------
// VIGIK PACS parser for MIFARE Classic dumps
//-----------------------------------------------------------------------------
#ifndef PARSEVIGIK_H__
#define PARSEVIGIK_H__
#include "common.h"
#include "protocol_vigik.h"
// MAD application ids the VIGIK scheme claims
#define VIGIK_MAD_AID 0x4910
#define VIGIK_MAD_AID_ALT 0x4916
// True when the MAD of this dump advertises the VIGIK application
bool is_valid_vigik_card(const uint8_t *dump, size_t dumplen);
// Collect the VIGIK sectors out of a whole card dump and print them
int vigik_parser_parse(const uint8_t *dump, size_t dumplen);
// Lower level helpers, on an already assembled VIGIK structure
const char *vigik_get_service(uint16_t service_code);
int vigik_verify(mfc_vigik_t *d);
int vigik_annotate(mfc_vigik_t *d);
#endif