diff --git a/src/crapto1.c b/src/crapto1.c index c29f4c9..c455933 100644 --- a/src/crapto1.c +++ b/src/crapto1.c @@ -24,49 +24,49 @@ static uint8_t filterlut[1 << 20]; static void __attribute__((constructor)) fill_lut() { - uint32_t i; - for(i = 0; i < 1 << 20; ++i) - filterlut[i] = filter(i); + uint32_t i; + for (i = 0; i < 1 << 20; ++i) + filterlut[i] = filter(i); } #define filter(x) (filterlut[(x) & 0xfffff]) #endif -static void quicksort(uint32_t* const start, uint32_t* const stop) +static void quicksort(uint32_t *const start, uint32_t *const stop) { - uint32_t *it = start + 1, *rit = stop; + uint32_t *it = start + 1, *rit = stop; - if(it > rit) - return; + if (it > rit) + return; - while(it < rit) - if(*it <= *start) - ++it; - else if(*rit > *start) - --rit; - else - *it ^= (*it ^= *rit, *rit ^= *it); + while (it < rit) + if (*it <= *start) + ++it; + else if (*rit > *start) + --rit; + else + *it ^= (*it ^= *rit, *rit ^= *it); - if(*rit >= *start) - --rit; - if(rit != start) - *rit ^= (*rit ^= *start, *start ^= *rit); + if (*rit >= *start) + --rit; + if (rit != start) + *rit ^= (*rit ^= *start, *start ^= *rit); - quicksort(start, rit - 1); - quicksort(rit + 1, stop); + quicksort(start, rit - 1); + quicksort(rit + 1, stop); } /** binsearch * Binary search for the first occurence of *stop's MSB in sorted [start,stop] */ -static inline uint32_t* binsearch(uint32_t *start, uint32_t *stop) +static inline uint32_t *binsearch(uint32_t *start, uint32_t *stop) { - uint32_t mid, val = *stop & 0xff000000; - while(start != stop) - if(start[mid = (stop - start) >> 1] > val) - stop = &start[mid]; - else - start += mid + 1; + uint32_t mid, val = *stop & 0xff000000; + while (start != stop) + if (start[mid = (stop - start) >> 1] > val) + stop = &start[mid]; + else + start += mid + 1; - return start; + return start; } /** update_contribution @@ -75,11 +75,11 @@ static inline uint32_t* binsearch(uint32_t *start, uint32_t *stop) static inline void update_contribution(uint32_t *item, const uint32_t mask1, const uint32_t mask2) { - uint32_t p = *item >> 25; + uint32_t p = *item >> 25; - p = p << 1 | parity(*item & mask1); - p = p << 1 | parity(*item & mask2); - *item = p << 24 | (*item & 0xffffff); + p = p << 1 | parity(*item & mask1); + p = p << 1 | parity(*item & mask2); + *item = p << 24 | (*item & 0xffffff); } /** extend_table @@ -88,228 +88,229 @@ update_contribution(uint32_t *item, const uint32_t mask1, const uint32_t mask2) static inline void extend_table(uint32_t *tbl, uint32_t **end, int bit, int m1, int m2, uint32_t in) { - in <<= 24; - for(*tbl <<= 1; tbl <= *end; *++tbl <<= 1) - if(filter(*tbl) ^ filter(*tbl | 1)) { - *tbl |= filter(*tbl) ^ bit; - update_contribution(tbl, m1, m2); - *tbl ^= in; - } else if(filter(*tbl) == bit) { - *++*end = tbl[1]; - tbl[1] = tbl[0] | 1; - update_contribution(tbl, m1, m2); - *tbl++ ^= in; - update_contribution(tbl, m1, m2); - *tbl ^= in; - } else - *tbl-- = *(*end)--; + in <<= 24; + for (*tbl <<= 1; tbl <= *end; *++tbl <<= 1) + if (filter(*tbl) ^ filter(*tbl | 1)) { + *tbl |= filter(*tbl) ^ bit; + update_contribution(tbl, m1, m2); + *tbl ^= in; + } else if (filter(*tbl) == bit) { + *++*end = tbl[1]; + tbl[1] = tbl[0] | 1; + update_contribution(tbl, m1, m2); + *tbl++ ^= in; + update_contribution(tbl, m1, m2); + *tbl ^= in; + } else + *tbl-- = *(*end)--; } /** extend_table_simple * using a bit of the keystream extend the table of possible lfsr states */ static inline void extend_table_simple(uint32_t *tbl, uint32_t **end, int bit) { - for(*tbl <<= 1; tbl <= *end; *++tbl <<= 1) - if(filter(*tbl) ^ filter(*tbl | 1)) { - *tbl |= filter(*tbl) ^ bit; - } else if(filter(*tbl) == bit) { - *++*end = *++tbl; - *tbl = tbl[-1] | 1; - } else - *tbl-- = *(*end)--; + for (*tbl <<= 1; tbl <= *end; *++tbl <<= 1) + if (filter(*tbl) ^ filter(*tbl | 1)) { + *tbl |= filter(*tbl) ^ bit; + } else if (filter(*tbl) == bit) { + *++*end = *++tbl; + *tbl = tbl[-1] | 1; + } else + *tbl-- = *(*end)--; } /** recover * recursively narrow down the search space, 4 bits of keystream at a time */ -static struct Crypto1State* +static struct Crypto1State * recover(uint32_t *o_head, uint32_t *o_tail, uint32_t oks, - uint32_t *e_head, uint32_t *e_tail, uint32_t eks, int rem, - struct Crypto1State *sl, uint32_t in) -{ - uint32_t *o, *e, i; + uint32_t *e_head, uint32_t *e_tail, uint32_t eks, int rem, + struct Crypto1State *sl, uint32_t in) { + uint32_t *o, *e, i; - if(rem == -1) { - for(e = e_head; e <= e_tail; ++e) { - *e = *e << 1 ^ parity(*e & LF_POLY_EVEN) ^ !!(in & 4); - for(o = o_head; o <= o_tail; ++o, ++sl) { - sl->even = *o; - sl->odd = *e ^ parity(*o & LF_POLY_ODD); - sl[1].odd = sl[1].even = 0; - } - } - return sl; - } + if (rem == -1) { + for (e = e_head; e <= e_tail; ++e) { + *e = *e << 1 ^ parity(*e & LF_POLY_EVEN) ^ !!(in & 4); + for (o = o_head; o <= o_tail; ++o, ++sl) { + sl->even = *o; + sl->odd = *e ^ parity(*o & LF_POLY_ODD); + sl[1].odd = sl[1].even = 0; + } + } + return sl; + } - for(i = 0; i < 4 && rem--; i++) { - oks >>= 1; - eks >>= 1; - in >>= 2; - extend_table(o_head, &o_tail, oks & 1, LF_POLY_EVEN << 1 | 1, - LF_POLY_ODD << 1, 0); - if(o_head > o_tail) - return sl; + for (i = 0; i < 4 && rem--; i++) { + oks >>= 1; + eks >>= 1; + in >>= 2; + extend_table(o_head, &o_tail, oks & 1, LF_POLY_EVEN << 1 | 1, + LF_POLY_ODD << 1, 0); + if (o_head > o_tail) + return sl; - extend_table(e_head, &e_tail, eks & 1, LF_POLY_ODD, - LF_POLY_EVEN << 1 | 1, in & 3); - if(e_head > e_tail) - return sl; - } + extend_table(e_head, &e_tail, eks & 1, LF_POLY_ODD, + LF_POLY_EVEN << 1 | 1, in & 3); + if (e_head > e_tail) + return sl; + } - quicksort(o_head, o_tail); - quicksort(e_head, e_tail); + quicksort(o_head, o_tail); + quicksort(e_head, e_tail); - while(o_tail >= o_head && e_tail >= e_head) - if(((*o_tail ^ *e_tail) >> 24) == 0) { - o_tail = binsearch(o_head, o = o_tail); - e_tail = binsearch(e_head, e = e_tail); - sl = recover(o_tail--, o, oks, - e_tail--, e, eks, rem, sl, in); - } - else if(*o_tail > *e_tail) - o_tail = binsearch(o_head, o_tail) - 1; - else - e_tail = binsearch(e_head, e_tail) - 1; + while (o_tail >= o_head && e_tail >= e_head) + if (((*o_tail ^ *e_tail) >> 24) == 0) { + o_tail = binsearch(o_head, o = o_tail); + e_tail = binsearch(e_head, e = e_tail); + sl = recover(o_tail--, o, oks, + e_tail--, e, eks, rem, sl, in); + } else if (*o_tail > *e_tail) + o_tail = binsearch(o_head, o_tail) - 1; + else + e_tail = binsearch(e_head, e_tail) - 1; - return sl; + return sl; } /** lfsr_recovery * recover the state of the lfsr given 32 bits of the keystream * additionally you can use the in parameter to specify the value * that was fed into the lfsr at the time the keystream was generated */ -struct Crypto1State* lfsr_recovery32(uint32_t ks2, uint32_t in) -{ - struct Crypto1State *statelist; - uint32_t *odd_head = 0, *odd_tail = 0, oks = 0; - uint32_t *even_head = 0, *even_tail = 0, eks = 0; - int i; +struct Crypto1State *lfsr_recovery32(uint32_t ks2, uint32_t in) { + struct Crypto1State *statelist; + uint32_t *odd_head = 0, *odd_tail = 0, oks = 0; + uint32_t *even_head = 0, *even_tail = 0, eks = 0; + int i; - for(i = 31; i >= 0; i -= 2) - oks = oks << 1 | BEBIT(ks2, i); - for(i = 30; i >= 0; i -= 2) - eks = eks << 1 | BEBIT(ks2, i); + for (i = 31; i >= 0; i -= 2) + oks = oks << 1 | BEBIT(ks2, i); + for (i = 30; i >= 0; i -= 2) + eks = eks << 1 | BEBIT(ks2, i); - odd_head = odd_tail = malloc(sizeof(uint32_t) << 21); - even_head = even_tail = malloc(sizeof(uint32_t) << 21); - statelist = malloc(sizeof(struct Crypto1State) << 18); - if(!odd_tail-- || !even_tail-- || !statelist) { - free(statelist); - statelist = 0; - goto out; - } + odd_head = odd_tail = malloc(sizeof(uint32_t) << 21); + even_head = even_tail = malloc(sizeof(uint32_t) << 21); + statelist = malloc(sizeof(struct Crypto1State) << 18); + if (!odd_tail-- || !even_tail-- || !statelist) { + free(statelist); + statelist = 0; + goto out; + } - statelist->odd = statelist->even = 0; + statelist->odd = statelist->even = 0; - for(i = 1 << 20; i >= 0; --i) { - if(filter(i) == (oks & 1)) - *++odd_tail = i; - if(filter(i) == (eks & 1)) - *++even_tail = i; - } + for (i = 1 << 20; i >= 0; --i) { + if (filter(i) == (oks & 1)) + *++odd_tail = i; + if (filter(i) == (eks & 1)) + *++even_tail = i; + } - for(i = 0; i < 4; i++) { - extend_table_simple(odd_head, &odd_tail, (oks >>= 1) & 1); - extend_table_simple(even_head, &even_tail, (eks >>= 1) & 1); - } + for (i = 0; i < 4; i++) { + extend_table_simple(odd_head, &odd_tail, (oks >>= 1) & 1); + extend_table_simple(even_head, &even_tail, (eks >>= 1) & 1); + } - in = (in >> 16 & 0xff) | (in << 16) | (in & 0xff00); - recover(odd_head, odd_tail, oks, - even_head, even_tail, eks, 11, statelist, in << 1); + in = (in >> 16 & 0xff) | (in << 16) | (in & 0xff00); + recover(odd_head, odd_tail, oks, + even_head, even_tail, eks, 11, statelist, in << 1); out: - free(odd_head); - free(even_head); - return statelist; + free(odd_head); + free(even_head); + return statelist; } static const uint32_t S1[] = { 0x62141, 0x310A0, 0x18850, 0x0C428, 0x06214, - 0x0310A, 0x85E30, 0xC69AD, 0x634D6, 0xB5CDE, 0xDE8DA, 0x6F46D, 0xB3C83, - 0x59E41, 0xA8995, 0xD027F, 0x6813F, 0x3409F, 0x9E6FA}; + 0x0310A, 0x85E30, 0xC69AD, 0x634D6, 0xB5CDE, 0xDE8DA, 0x6F46D, 0xB3C83, + 0x59E41, 0xA8995, 0xD027F, 0x6813F, 0x3409F, 0x9E6FA + }; static const uint32_t S2[] = { 0x3A557B00, 0x5D2ABD80, 0x2E955EC0, 0x174AAF60, - 0x0BA557B0, 0x05D2ABD8, 0x0449DE68, 0x048464B0, 0x42423258, 0x278192A8, - 0x156042D0, 0x0AB02168, 0x43F89B30, 0x61FC4D98, 0x765EAD48, 0x7D8FDD20, - 0x7EC7EE90, 0x7F63F748, 0x79117020}; + 0x0BA557B0, 0x05D2ABD8, 0x0449DE68, 0x048464B0, 0x42423258, 0x278192A8, + 0x156042D0, 0x0AB02168, 0x43F89B30, 0x61FC4D98, 0x765EAD48, 0x7D8FDD20, + 0x7EC7EE90, 0x7F63F748, 0x79117020 + }; static const uint32_t T1[] = { - 0x4F37D, 0x279BE, 0x97A6A, 0x4BD35, 0x25E9A, 0x12F4D, 0x097A6, 0x80D66, - 0xC4006, 0x62003, 0xB56B4, 0x5AB5A, 0xA9318, 0xD0F39, 0x6879C, 0xB057B, - 0x582BD, 0x2C15E, 0x160AF, 0x8F6E2, 0xC3DC4, 0xE5857, 0x72C2B, 0x39615, - 0x98DBF, 0xC806A, 0xE0680, 0x70340, 0x381A0, 0x98665, 0x4C332, 0xA272C}; + 0x4F37D, 0x279BE, 0x97A6A, 0x4BD35, 0x25E9A, 0x12F4D, 0x097A6, 0x80D66, + 0xC4006, 0x62003, 0xB56B4, 0x5AB5A, 0xA9318, 0xD0F39, 0x6879C, 0xB057B, + 0x582BD, 0x2C15E, 0x160AF, 0x8F6E2, 0xC3DC4, 0xE5857, 0x72C2B, 0x39615, + 0x98DBF, 0xC806A, 0xE0680, 0x70340, 0x381A0, 0x98665, 0x4C332, 0xA272C +}; static const uint32_t T2[] = { 0x3C88B810, 0x5E445C08, 0x2982A580, 0x14C152C0, - 0x4A60A960, 0x253054B0, 0x52982A58, 0x2FEC9EA8, 0x1156C4D0, 0x08AB6268, - 0x42F53AB0, 0x217A9D58, 0x161DC528, 0x0DAE6910, 0x46D73488, 0x25CB11C0, - 0x52E588E0, 0x6972C470, 0x34B96238, 0x5CFC3A98, 0x28DE96C8, 0x12CFC0E0, - 0x4967E070, 0x64B3F038, 0x74F97398, 0x7CDC3248, 0x38CE92A0, 0x1C674950, - 0x0E33A4A8, 0x01B959D0, 0x40DCACE8, 0x26CEDDF0}; + 0x4A60A960, 0x253054B0, 0x52982A58, 0x2FEC9EA8, 0x1156C4D0, 0x08AB6268, + 0x42F53AB0, 0x217A9D58, 0x161DC528, 0x0DAE6910, 0x46D73488, 0x25CB11C0, + 0x52E588E0, 0x6972C470, 0x34B96238, 0x5CFC3A98, 0x28DE96C8, 0x12CFC0E0, + 0x4967E070, 0x64B3F038, 0x74F97398, 0x7CDC3248, 0x38CE92A0, 0x1C674950, + 0x0E33A4A8, 0x01B959D0, 0x40DCACE8, 0x26CEDDF0 + }; static const uint32_t C1[] = { 0x846B5, 0x4235A, 0x211AD}; static const uint32_t C2[] = { 0x1A822E0, 0x21A822E0, 0x21A822E0}; /** Reverse 64 bits of keystream into possible cipher states * Variation mentioned in the paper. Somewhat optimized version */ -struct Crypto1State* lfsr_recovery64(uint32_t ks2, uint32_t ks3) -{ - struct Crypto1State *statelist, *sl; - uint8_t oks[32], eks[32], hi[32]; - uint32_t low = 0, win = 0; - uint32_t *tail, table[1 << 16]; - int i, j; +struct Crypto1State *lfsr_recovery64(uint32_t ks2, uint32_t ks3) { + struct Crypto1State *statelist, *sl; + uint8_t oks[32], eks[32], hi[32]; + uint32_t low = 0, win = 0; + uint32_t *tail, table[1 << 16]; + int i, j; - sl = statelist = malloc(sizeof(struct Crypto1State) << 4); - if(!sl) - return 0; - sl->odd = sl->even = 0; + sl = statelist = malloc(sizeof(struct Crypto1State) << 4); + if (!sl) + return 0; + sl->odd = sl->even = 0; - for(i = 30; i >= 0; i -= 2) { - oks[i >> 1] = BEBIT(ks2, i); - oks[16 + (i >> 1)] = BEBIT(ks3, i); - } - for(i = 31; i >= 0; i -= 2) { - eks[i >> 1] = BEBIT(ks2, i); - eks[16 + (i >> 1)] = BEBIT(ks3, i); - } + for (i = 30; i >= 0; i -= 2) { + oks[i >> 1] = BEBIT(ks2, i); + oks[16 + (i >> 1)] = BEBIT(ks3, i); + } + for (i = 31; i >= 0; i -= 2) { + eks[i >> 1] = BEBIT(ks2, i); + eks[16 + (i >> 1)] = BEBIT(ks3, i); + } - for(i = 0xfffff; i >= 0; --i) { - if (filter(i) != oks[0]) - continue; + for (i = 0xfffff; i >= 0; --i) { + if (filter(i) != oks[0]) + continue; - *(tail = table) = i; - for(j = 1; tail >= table && j < 29; ++j) - extend_table_simple(table, &tail, oks[j]); + *(tail = table) = i; + for (j = 1; tail >= table && j < 29; ++j) + extend_table_simple(table, &tail, oks[j]); - if(tail < table) - continue; + if (tail < table) + continue; - for(j = 0; j < 19; ++j) - low = low << 1 | parity(i & S1[j]); - for(j = 0; j < 32; ++j) - hi[j] = parity(i & T1[j]); + for (j = 0; j < 19; ++j) + low = low << 1 | parity(i & S1[j]); + for (j = 0; j < 32; ++j) + hi[j] = parity(i & T1[j]); - for(; tail >= table; --tail) { - for(j = 0; j < 3; ++j) { - *tail = *tail << 1; - *tail |= parity((i & C1[j]) ^ (*tail & C2[j])); - if(filter(*tail) != oks[29 + j]) - goto continue2; - } + for (; tail >= table; --tail) { + for (j = 0; j < 3; ++j) { + *tail = *tail << 1; + *tail |= parity((i & C1[j]) ^(*tail & C2[j])); + if (filter(*tail) != oks[29 + j]) + goto continue2; + } - for(j = 0; j < 19; ++j) - win = win << 1 | parity(*tail & S2[j]); + for (j = 0; j < 19; ++j) + win = win << 1 | parity(*tail & S2[j]); - win ^= low; - for(j = 0; j < 32; ++j) { - win = win << 1 ^ hi[j] ^ parity(*tail & T2[j]); - if(filter(win) != eks[j]) - goto continue2; - } + win ^= low; + for (j = 0; j < 32; ++j) { + win = win << 1 ^ hi[j] ^ parity(*tail & T2[j]); + if (filter(win) != eks[j]) + goto continue2; + } - *tail = *tail << 1 | parity(LF_POLY_EVEN & *tail); - sl->odd = *tail ^ parity(LF_POLY_ODD & win); - sl->even = win; - ++sl; - sl->odd = sl->even = 0; - continue2:; - } - } - return statelist; + *tail = *tail << 1 | parity(LF_POLY_EVEN & *tail); + sl->odd = *tail ^ parity(LF_POLY_ODD & win); + sl->even = win; + ++sl; + sl->odd = sl->even = 0; +continue2: + ; + } + } + return statelist; } /** lfsr_rollback_bit @@ -317,41 +318,41 @@ struct Crypto1State* lfsr_recovery64(uint32_t ks2, uint32_t ks3) */ uint8_t lfsr_rollback_bit(struct Crypto1State *s, uint32_t in, int fb) { - int out; - uint8_t ret; + int out; + uint8_t ret; - s->odd &= 0xffffff; - s->odd ^= (s->odd ^= s->even, s->even ^= s->odd); + s->odd &= 0xffffff; + s->odd ^= (s->odd ^= s->even, s->even ^= s->odd); - out = s->even & 1; - out ^= LF_POLY_EVEN & (s->even >>= 1); - out ^= LF_POLY_ODD & s->odd; - out ^= !!in; - out ^= (ret = filter(s->odd)) & !!fb; + out = s->even & 1; + out ^= LF_POLY_EVEN & (s->even >>= 1); + out ^= LF_POLY_ODD & s->odd; + out ^= !!in; + out ^= (ret = filter(s->odd)) & !!fb; - s->even |= parity(out) << 23; - return ret; + s->even |= parity(out) << 23; + return ret; } /** lfsr_rollback_byte * Rollback the shift register in order to get previous states */ uint8_t lfsr_rollback_byte(struct Crypto1State *s, uint32_t in, int fb) { - int i, ret = 0; - for (i = 7; i >= 0; --i) - ret |= lfsr_rollback_bit(s, BIT(in, i), fb) << i; - return ret; + int i, ret = 0; + for (i = 7; i >= 0; --i) + ret |= lfsr_rollback_bit(s, BIT(in, i), fb) << i; + return ret; } /** lfsr_rollback_word * Rollback the shift register in order to get previous states */ uint32_t lfsr_rollback_word(struct Crypto1State *s, uint32_t in, int fb) { - int i; - uint32_t ret = 0; - for (i = 31; i >= 0; --i) - ret |= lfsr_rollback_bit(s, BEBIT(in, i), fb) << (i ^ 24); - return ret; + int i; + uint32_t ret = 0; + for (i = 31; i >= 0; --i) + ret |= lfsr_rollback_bit(s, BEBIT(in, i), fb) << (i ^ 24); + return ret; } /** nonce_distance @@ -360,25 +361,26 @@ uint32_t lfsr_rollback_word(struct Crypto1State *s, uint32_t in, int fb) static uint16_t *dist = 0; int nonce_distance(uint32_t from, uint32_t to) { - uint16_t x, i; - if(!dist) { - dist = malloc(2 << 16); - if(!dist) - return -1; - for (x = i = 1; i; ++i) { - dist[(x & 0xff) << 8 | x >> 8] = i; - x = x >> 1 | (x ^ x >> 2 ^ x >> 3 ^ x >> 5) << 15; - } - } - return (65535 + dist[to >> 16] - dist[from >> 16]) % 65535; + uint16_t x, i; + if (!dist) { + dist = malloc(2 << 16); + if (!dist) + return -1; + for (x = i = 1; i; ++i) { + dist[(x & 0xff) << 8 | x >> 8] = i; + x = x >> 1 | (x ^ x >> 2 ^ x >> 3 ^ x >> 5) << 15; + } + } + return (65535 + dist[to >> 16] - dist[from >> 16]) % 65535; } static uint32_t fastfwd[2][8] = { - { 0, 0x4BC53, 0xECB1, 0x450E2, 0x25E29, 0x6E27A, 0x2B298, 0x60ECB}, - { 0, 0x1D962, 0x4BC53, 0x56531, 0xECB1, 0x135D3, 0x450E2, 0x58980}}; - - + { 0, 0x4BC53, 0xECB1, 0x450E2, 0x25E29, 0x6E27A, 0x2B298, 0x60ECB}, + { 0, 0x1D962, 0x4BC53, 0x56531, 0xECB1, 0x135D3, 0x450E2, 0x58980} +}; + + /** lfsr_prefix_ks * * Is an exported helper function from the common prefix attack @@ -390,91 +392,89 @@ static uint32_t fastfwd[2][8] = { */ uint32_t *lfsr_prefix_ks(uint8_t ks[8], int isodd) { - uint32_t c, entry, *candidates = malloc(4 << 10); - int i, size = 0, good; + uint32_t c, entry, *candidates = malloc(4 << 10); + int i, size = 0, good; - if(!candidates) - return 0; + if (!candidates) + return 0; - for(i = 0; i < 1 << 21; ++i) { - for(c = 0, good = 1; good && c < 8; ++c) { - entry = i ^ fastfwd[isodd][c]; - good &= (BIT(ks[c], isodd) == filter(entry >> 1)); - good &= (BIT(ks[c], isodd + 2) == filter(entry)); - } - if(good) - candidates[size++] = i; - } + for (i = 0; i < 1 << 21; ++i) { + for (c = 0, good = 1; good && c < 8; ++c) { + entry = i ^ fastfwd[isodd][c]; + good &= (BIT(ks[c], isodd) == filter(entry >> 1)); + good &= (BIT(ks[c], isodd + 2) == filter(entry)); + } + if (good) + candidates[size++] = i; + } - candidates[size] = -1; + candidates[size] = -1; - return candidates; + return candidates; } /** check_pfx_parity * helper function which eliminates possible secret states using parity bits */ -static struct Crypto1State* +static struct Crypto1State * check_pfx_parity(uint32_t prefix, uint32_t rresp, uint8_t parities[8][8], - uint32_t odd, uint32_t even, struct Crypto1State* sl) -{ - uint32_t ks1, nr, ks2, rr, ks3, c, good = 1; + uint32_t odd, uint32_t even, struct Crypto1State *sl) { + uint32_t ks1, nr, ks2, rr, ks3, c, good = 1; - for(c = 0; good && c < 8; ++c) { - sl->odd = odd ^ fastfwd[1][c]; - sl->even = even ^ fastfwd[0][c]; + for (c = 0; good && c < 8; ++c) { + sl->odd = odd ^ fastfwd[1][c]; + sl->even = even ^ fastfwd[0][c]; - lfsr_rollback_bit(sl, 0, 0); - lfsr_rollback_bit(sl, 0, 0); + lfsr_rollback_bit(sl, 0, 0); + lfsr_rollback_bit(sl, 0, 0); - ks3 = lfsr_rollback_bit(sl, 0, 0); - ks2 = lfsr_rollback_word(sl, 0, 0); - ks1 = lfsr_rollback_word(sl, prefix | c << 5, 1); + ks3 = lfsr_rollback_bit(sl, 0, 0); + ks2 = lfsr_rollback_word(sl, 0, 0); + ks1 = lfsr_rollback_word(sl, prefix | c << 5, 1); - nr = ks1 ^ (prefix | c << 5); - rr = ks2 ^ rresp; + nr = ks1 ^(prefix | c << 5); + rr = ks2 ^ rresp; - good &= parity(nr & 0x000000ff) ^ parities[c][3] ^ BIT(ks2, 24); - good &= parity(rr & 0xff000000) ^ parities[c][4] ^ BIT(ks2, 16); - good &= parity(rr & 0x00ff0000) ^ parities[c][5] ^ BIT(ks2, 8); - good &= parity(rr & 0x0000ff00) ^ parities[c][6] ^ BIT(ks2, 0); - good &= parity(rr & 0x000000ff) ^ parities[c][7] ^ ks3; - } + good &= parity(nr & 0x000000ff) ^ parities[c][3] ^ BIT(ks2, 24); + good &= parity(rr & 0xff000000) ^ parities[c][4] ^ BIT(ks2, 16); + good &= parity(rr & 0x00ff0000) ^ parities[c][5] ^ BIT(ks2, 8); + good &= parity(rr & 0x0000ff00) ^ parities[c][6] ^ BIT(ks2, 0); + good &= parity(rr & 0x000000ff) ^ parities[c][7] ^ ks3; + } - return sl + good; -} + return sl + good; +} /** lfsr_common_prefix * Implentation of the common prefix attack. */ -struct Crypto1State* -lfsr_common_prefix(uint32_t pfx, uint32_t rr, uint8_t ks[8], uint8_t par[8][8]) -{ - struct Crypto1State *statelist, *s; - uint32_t *odd, *even, *o, *e, top; +struct Crypto1State * +lfsr_common_prefix(uint32_t pfx, uint32_t rr, uint8_t ks[8], uint8_t par[8][8]) { + struct Crypto1State *statelist, *s; + uint32_t *odd, *even, *o, *e, top; - odd = lfsr_prefix_ks(ks, 1); - even = lfsr_prefix_ks(ks, 0); + odd = lfsr_prefix_ks(ks, 1); + even = lfsr_prefix_ks(ks, 0); - s = statelist = malloc((sizeof *statelist) << 20); - if(!s || !odd || !even) { - free(statelist); - statelist = 0; - goto out; - } + s = statelist = malloc((sizeof *statelist) << 20); + if (!s || !odd || !even) { + free(statelist); + statelist = 0; + goto out; + } - for(o = odd; *o + 1; ++o) - for(e = even; *e + 1; ++e) - for(top = 0; top < 64; ++top) { - *o += 1 << 21; - *e += (!(top & 7) + 1) << 21; - s = check_pfx_parity(pfx, rr, par, *o, *e, s); - } + for (o = odd; *o + 1; ++o) + for (e = even; *e + 1; ++e) + for (top = 0; top < 64; ++top) { + *o += 1 << 21; + *e += (!(top & 7) + 1) << 21; + s = check_pfx_parity(pfx, rr, par, *o, *e, s); + } - s->odd = s->even = 0; + s->odd = s->even = 0; out: - free(odd); - free(even); - return statelist; + free(odd); + free(even); + return statelist; } diff --git a/src/crapto1.h b/src/crapto1.h index 127a17d..fecedb1 100644 --- a/src/crapto1.h +++ b/src/crapto1.h @@ -24,69 +24,69 @@ extern "C" { #endif -struct Crypto1State {uint32_t odd, even;}; -struct Crypto1State* crypto1_create(uint64_t); -void crypto1_destroy(struct Crypto1State*); -void crypto1_get_lfsr(struct Crypto1State*, uint64_t*); -uint8_t crypto1_bit(struct Crypto1State*, uint8_t, int); -uint8_t crypto1_byte(struct Crypto1State*, uint8_t, int); -uint32_t crypto1_word(struct Crypto1State*, uint32_t, int); -uint32_t prng_successor(uint32_t x, uint32_t n); + struct Crypto1State {uint32_t odd, even;}; + struct Crypto1State *crypto1_create(uint64_t); + void crypto1_destroy(struct Crypto1State *); + void crypto1_get_lfsr(struct Crypto1State *, uint64_t *); + uint8_t crypto1_bit(struct Crypto1State *, uint8_t, int); + uint8_t crypto1_byte(struct Crypto1State *, uint8_t, int); + uint32_t crypto1_word(struct Crypto1State *, uint32_t, int); + uint32_t prng_successor(uint32_t x, uint32_t n); -struct Crypto1State* lfsr_recovery32(uint32_t ks2, uint32_t in); -struct Crypto1State* lfsr_recovery64(uint32_t ks2, uint32_t ks3); -uint32_t *lfsr_prefix_ks(uint8_t ks[8], int isodd); -struct Crypto1State* -lfsr_common_prefix(uint32_t pfx, uint32_t rr, uint8_t ks[8], uint8_t par[8][8]); + struct Crypto1State *lfsr_recovery32(uint32_t ks2, uint32_t in); + struct Crypto1State *lfsr_recovery64(uint32_t ks2, uint32_t ks3); + uint32_t *lfsr_prefix_ks(uint8_t ks[8], int isodd); + struct Crypto1State * + lfsr_common_prefix(uint32_t pfx, uint32_t rr, uint8_t ks[8], uint8_t par[8][8]); -uint8_t lfsr_rollback_bit(struct Crypto1State* s, uint32_t in, int fb); -uint8_t lfsr_rollback_byte(struct Crypto1State* s, uint32_t in, int fb); -uint32_t lfsr_rollback_word(struct Crypto1State* s, uint32_t in, int fb); -int nonce_distance(uint32_t from, uint32_t to); + uint8_t lfsr_rollback_bit(struct Crypto1State *s, uint32_t in, int fb); + uint8_t lfsr_rollback_byte(struct Crypto1State *s, uint32_t in, int fb); + uint32_t lfsr_rollback_word(struct Crypto1State *s, uint32_t in, int fb); + int nonce_distance(uint32_t from, uint32_t to); #define FOREACH_VALID_NONCE(N, FILTER, FSIZE)\ - uint32_t __n = 0,__M = 0, N = 0;\ - int __i;\ - for(; __n < 1 << 16; N = prng_successor(__M = ++__n, 16))\ - for(__i = FSIZE - 1; __i >= 0; __i--)\ - if(BIT(FILTER, __i) ^ parity(__M & 0xFF01))\ - break;\ - else if(__i)\ - __M = prng_successor(__M, (__i == 7) ? 48 : 8);\ - else + uint32_t __n = 0,__M = 0, N = 0;\ + int __i;\ + for(; __n < 1 << 16; N = prng_successor(__M = ++__n, 16))\ + for(__i = FSIZE - 1; __i >= 0; __i--)\ + if(BIT(FILTER, __i) ^ parity(__M & 0xFF01))\ + break;\ + else if(__i)\ + __M = prng_successor(__M, (__i == 7) ? 48 : 8);\ + else #define LF_POLY_ODD (0x29CE5C) #define LF_POLY_EVEN (0x870804) #define BIT(x, n) ((x) >> (n) & 1) #define BEBIT(x, n) BIT(x, (n) ^ 24) -static inline int parity(uint32_t x) -{ + static inline int parity(uint32_t x) + { #if !defined __i386__ || !defined __GNUC__ - x ^= x >> 16; - x ^= x >> 8; - x ^= x >> 4; - return BIT(0x6996, x & 0xf); + x ^= x >> 16; + x ^= x >> 8; + x ^= x >> 4; + return BIT(0x6996, x & 0xf); #else - asm( "movl %1, %%eax\n" - "mov %%ax, %%cx\n" - "shrl $0x10, %%eax\n" - "xor %%ax, %%cx\n" - "xor %%ch, %%cl\n" - "setpo %%al\n" - "movzx %%al, %0\n": "=r"(x) : "r"(x): "eax","ecx"); - return x; + asm("movl %1, %%eax\n" + "mov %%ax, %%cx\n" + "shrl $0x10, %%eax\n" + "xor %%ax, %%cx\n" + "xor %%ch, %%cl\n" + "setpo %%al\n" + "movzx %%al, %0\n": "=r"(x) : "r"(x): "eax", "ecx"); + return x; #endif -} -static inline int filter(uint32_t const x) -{ - uint32_t f; + } + static inline int filter(uint32_t const x) + { + uint32_t f; - f = 0xf22c0 >> (x & 0xf) & 16; - f |= 0x6c9c0 >> (x >> 4 & 0xf) & 8; - f |= 0x3c8b0 >> (x >> 8 & 0xf) & 4; - f |= 0x1e458 >> (x >> 12 & 0xf) & 2; - f |= 0x0d938 >> (x >> 16 & 0xf) & 1; - return BIT(0xEC57E80A, f); -} + f = 0xf22c0 >> (x & 0xf) & 16; + f |= 0x6c9c0 >> (x >> 4 & 0xf) & 8; + f |= 0x3c8b0 >> (x >> 8 & 0xf) & 4; + f |= 0x1e458 >> (x >> 12 & 0xf) & 2; + f |= 0x0d938 >> (x >> 16 & 0xf) & 1; + return BIT(0xEC57E80A, f); + } #ifdef __cplusplus } #endif diff --git a/src/crypto1.c b/src/crypto1.c index e2aab71..fc11f00 100644 --- a/src/crypto1.c +++ b/src/crypto1.c @@ -21,63 +21,62 @@ #include #define SWAPENDIAN(x)\ - (x = (x >> 8 & 0xff00ff) | (x & 0xff00ff) << 8, x = x >> 16 | x << 16) + (x = (x >> 8 & 0xff00ff) | (x & 0xff00ff) << 8, x = x >> 16 | x << 16) -struct Crypto1State * crypto1_create(uint64_t key) -{ - struct Crypto1State *s = malloc(sizeof(*s)); - int i; +struct Crypto1State *crypto1_create(uint64_t key) { + struct Crypto1State *s = malloc(sizeof(*s)); + int i; - for(i = 47;s && i > 0; i -= 2) { - s->odd = s->odd << 1 | BIT(key, (i - 1) ^ 7); - s->even = s->even << 1 | BIT(key, i ^ 7); - } - return s; + for (i = 47; s && i > 0; i -= 2) { + s->odd = s->odd << 1 | BIT(key, (i - 1) ^ 7); + s->even = s->even << 1 | BIT(key, i ^ 7); + } + return s; } void crypto1_destroy(struct Crypto1State *state) { - free(state); + free(state); } void crypto1_get_lfsr(struct Crypto1State *state, uint64_t *lfsr) { - int i; - for(*lfsr = 0, i = 23; i >= 0; --i) { - *lfsr = *lfsr << 1 | BIT(state->odd, i ^ 3); - *lfsr = *lfsr << 1 | BIT(state->even, i ^ 3); - } + int i; + for (*lfsr = 0, i = 23; i >= 0; --i) { + *lfsr = *lfsr << 1 | BIT(state->odd, i ^ 3); + *lfsr = *lfsr << 1 | BIT(state->even, i ^ 3); + } } uint8_t crypto1_bit(struct Crypto1State *s, uint8_t in, int is_encrypted) { - uint32_t feedin; - uint8_t ret = filter(s->odd); + uint32_t feedin; + uint8_t ret = filter(s->odd); - feedin = ret & !!is_encrypted; - feedin ^= !!in; - feedin ^= LF_POLY_ODD & s->odd; - feedin ^= LF_POLY_EVEN & s->even; - s->even = s->even << 1 | parity(feedin); + feedin = ret & !!is_encrypted; + feedin ^= !!in; + feedin ^= LF_POLY_ODD & s->odd; + feedin ^= LF_POLY_EVEN & s->even; + s->even = s->even << 1 | parity(feedin); - s->odd ^= (s->odd ^= s->even, s->even ^= s->odd); + s->odd ^= (s->odd ^= s->even, s->even ^= s->odd); - return ret; + return ret; } uint8_t crypto1_byte(struct Crypto1State *s, uint8_t in, int is_encrypted) { - uint8_t i, ret = 0; + uint8_t i, ret = 0; - for (i = 0; i < 8; ++i) - ret |= crypto1_bit(s, BIT(in, i), is_encrypted) << i; + for (i = 0; i < 8; ++i) + ret |= crypto1_bit(s, BIT(in, i), is_encrypted) << i; - return ret; + return ret; } uint32_t crypto1_word(struct Crypto1State *s, uint32_t in, int is_encrypted) { - uint32_t i, ret = 0; + uint32_t i, ret = 0; - for (i = 0; i < 32; ++i) - ret |= crypto1_bit(s, BEBIT(in, i), is_encrypted) << (i ^ 24); + for (i = 0; i < 32; ++i) + ret |= crypto1_bit(s, BEBIT(in, i), is_encrypted) << (i ^ 24); - return ret; + return ret; } /* prng_successor @@ -85,9 +84,9 @@ uint32_t crypto1_word(struct Crypto1State *s, uint32_t in, int is_encrypted) */ uint32_t prng_successor(uint32_t x, uint32_t n) { - SWAPENDIAN(x); - while(n--) - x = x >> 1 | (x >> 16 ^ x >> 18 ^ x >> 19 ^ x >> 21) << 31; + SWAPENDIAN(x); + while (n--) + x = x >> 1 | (x >> 16 ^ x >> 18 ^ x >> 19 ^ x >> 21) << 31; - return SWAPENDIAN(x); + return SWAPENDIAN(x); } diff --git a/src/mfcuk.c b/src/mfcuk.c index c4159bc..ecac1f2 100644 --- a/src/mfcuk.c +++ b/src/mfcuk.c @@ -154,17 +154,20 @@ // Fallback... #if !defined (bswap_16) || !defined (bswap_32) || !defined (bswap_64) # warning "No bswap function found! Using untested alternatives..." - static inline uint16_t bswap_16(uint16_t x) { - return (x>>8) | (x<<8); - } - - static inline uint32_t bswap_32(uint32_t x) { - return (bswap_16(x&0xffff)<<16) | (bswap_16(x>>16)); - } - - static inline uint64_t bswap_64(uint64_t x) { - return (((uint64_t)bswap_32(x&0xffffffffull))<<32) | (bswap_32(x>>32)); - } +static inline uint16_t bswap_16(uint16_t x) +{ + return (x >> 8) | (x << 8); +} + +static inline uint32_t bswap_32(uint32_t x) +{ + return (bswap_16(x & 0xffff) << 16) | (bswap_16(x >> 16)); +} + +static inline uint64_t bswap_64(uint64_t x) +{ + return (((uint64_t)bswap_32(x & 0xffffffffull)) << 32) | (bswap_32(x >> 32)); +} #endif #include @@ -172,13 +175,13 @@ #include #ifdef WIN32 - #define NOMINMAX - #include "windows.h" - #include "xgetopt.h" +#define NOMINMAX +#include "windows.h" +#include "xgetopt.h" #elif __STDC__ - #include - #include - #include +#include +#include +#include #endif // NFC @@ -211,9 +214,9 @@ static uint32_t bswap_32_pu8(uint8_t *pu8) { - uint32_t u32; - memcpy(&u32, pu8, sizeof(uint32_t)); - return u32; + uint32_t u32; + memcpy(&u32, pu8, sizeof(uint32_t)); + return u32; } extern mfcuk_finger_tmpl_entry mfcuk_finger_db[]; @@ -231,294 +234,289 @@ static const nfc_modulation nmMifare = { .nbr = NBR_106, }; -static int compareTagNonces (const void * a, const void * b) +static int compareTagNonces(const void *a, const void *b) { - // TODO: test the improvement (especially corner cases, over/under-flows) "return ( (*(uint32_t*)a) - (*(uint32_t*)b) ); - if ( *(uint32_t*)a > *(uint32_t*)b ) return 1; - if ( *(uint32_t*)a == *(uint32_t*)b ) return 0; - if ( *(uint32_t*)a < *(uint32_t*)b ) return -1; + // TODO: test the improvement (especially corner cases, over/under-flows) "return ( (*(uint32_t*)a) - (*(uint32_t*)b) ); + if (*(uint32_t *)a > *(uint32_t *)b) return 1; + if (*(uint32_t *)a == *(uint32_t *)b) return 0; + if (*(uint32_t *)a < * (uint32_t *)b) return -1; - return 0; // Never reach here, but keep compilers happy + return 0; // Never reach here, but keep compilers happy } // TODO: combine mfcuk_verify_key_block() with mfcuk_recover_key_block(), since a lot of code is duplicate -static uint32_t mfcuk_verify_key_block(nfc_device* pnd, uint32_t uiUID, uint64_t ui64Key, mifare_key_type bKeyType, uint8_t bTagType, uint32_t uiBlock) +static uint32_t mfcuk_verify_key_block(nfc_device *pnd, uint32_t uiUID, uint64_t ui64Key, mifare_key_type bKeyType, uint8_t bTagType, uint32_t uiBlock) { - uint32_t pos; + uint32_t pos; - // Keystream related variables - for verification with Crapto1/Crypto1 rollback - uint32_t nr_encrypted = 0; - uint32_t reader_response = 0; - uint32_t tag_response = 0; - uint32_t ks2 = 0; - uint32_t ks3 = 0; - struct Crypto1State *pcs; - uint64_t lfsr; + // Keystream related variables - for verification with Crapto1/Crypto1 rollback + uint32_t nr_encrypted = 0; + uint32_t reader_response = 0; + uint32_t tag_response = 0; + uint32_t ks2 = 0; + uint32_t ks3 = 0; + struct Crypto1State *pcs; + uint64_t lfsr; - // Communication related variables - uint8_t abtAuth[4] = { 0x00,0x00,0x00,0x00 }; - uint8_t abtArEnc[8] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; - uint8_t abtArEncPar[8] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; - uint8_t abtRx[MAX_FRAME_LEN]; - uint8_t abtRxPar[MAX_FRAME_LEN]; - uint32_t nt, nt_orig; // Supplied tag nonce + // Communication related variables + uint8_t abtAuth[4] = { 0x00, 0x00, 0x00, 0x00 }; + uint8_t abtArEnc[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + uint8_t abtArEncPar[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + uint8_t abtRx[MAX_FRAME_LEN]; + uint8_t abtRxPar[MAX_FRAME_LEN]; + uint32_t nt, nt_orig; // Supplied tag nonce - if ( (bKeyType != keyA) && (bKeyType != keyB) ) - { - return MFCUK_FAIL_KEYTYPE_INVALID; + if ((bKeyType != keyA) && (bKeyType != keyB)) { + return MFCUK_FAIL_KEYTYPE_INVALID; + } + + if (!IS_MIFARE_CLASSIC_1K(bTagType) && !IS_MIFARE_CLASSIC_4K(bTagType)) { + return MFCUK_FAIL_TAGTYPE_INVALID; + } + + if (!is_valid_block(bTagType, uiBlock)) { + return MFCUK_FAIL_BLOCK_INVALID; + } + + // Configure the authentication frame using the supplied block + abtAuth[0] = bKeyType; + abtAuth[1] = uiBlock; + iso14443a_crc_append(abtAuth, 2); + + // Now we take over, first we need full control over the CRC + if (0 > nfc_device_set_property_bool(pnd, NP_HANDLE_CRC, false)) { + return MFCUK_FAIL_COMM; + } + + // We need to disable EASY_FRAMING feature to talk in "raw" mode + nfc_device_set_property_bool(pnd, NP_EASY_FRAMING, false); + + // Request plain tag-nonce + if (0 > nfc_initiator_transceive_bytes(pnd, abtAuth, 4, abtRx, sizeof(abtRx), -1)) { + return MFCUK_FAIL_COMM; + } + nfc_device_set_property_bool(pnd, NP_EASY_FRAMING, true); + + // Save the tag nonce (nt) + nt = bswap_32_pu8(abtRx); + nt_orig = nt; + + // Init cipher with key + pcs = crypto1_create(ui64Key); + + // Load (plain) uid^nt into the cipher + for (pos = 0; pos < 4; pos++) { + // Update the cipher with the tag-initialization + crypto1_byte(pcs, ((uiUID >> (8 * (3 - pos))) & 0xFF) ^ abtRx[pos], 0); + } + + // Generate (encrypted) nr+parity by loading it into the cipher (Nr) + for (pos = 0; pos < 4; pos++) { + // Load in, and encrypt, the reader nonce (plain nr=0x00000000) + abtArEnc[pos] = crypto1_byte(pcs, 0x00, 0) ^ 0x00; + + // Encrypt the parity bits for the 4 plaintext bytes of nr + abtArEncPar[pos] = filter(pcs->odd) ^ oddparity(0x00); + + // Get the keystream encrypted Nr value currently loaded into the cypher, i.e. {Nr} + nr_encrypted = nr_encrypted << 8; + nr_encrypted = nr_encrypted | abtArEnc[pos]; + } + + // Skip 32 bits in pseudo random generator + nt = prng_successor(nt, 32); + + // Generate reader-answer from tag-nonce (Ar) + for (pos = 4; pos < 8; pos++) { + // Get the next random byte for verify the reader to the tag + nt = prng_successor(nt, 8); + + // Encrypt the reader-answer (nt' = suc2(nt)) + abtArEnc[pos] = crypto1_byte(pcs, 0x00, 0) ^(nt & 0xff); + + // Encrypt the parity bits for the 4 plaintext bytes of nt' + abtArEncPar[pos] = filter(pcs->odd) ^ oddparity(nt & 0xff); + + // Get the keystream encrypted reader response currently loaded into the cypher, i.e. {Ar} + reader_response = reader_response << 8; + reader_response = reader_response | abtArEnc[pos]; + } + + // Finally we want to send arbitrary parity bits + if (0 > nfc_device_set_property_bool(pnd, NP_HANDLE_PARITY, false)) { + return MFCUK_FAIL_COMM; + } + + int res; + if (0 > (res = nfc_initiator_transceive_bits(pnd, abtArEnc, 64, abtArEncPar, abtRx, sizeof(abtRx), abtRxPar))) { + return MFCUK_FAIL_AUTH; + } + + crypto1_destroy(pcs); + + if (res == 32) { + for (pos = 0; pos < 4; pos++) { + tag_response = tag_response << 8; + tag_response = tag_response | abtRx[pos]; } - if ( !IS_MIFARE_CLASSIC_1K(bTagType) && !IS_MIFARE_CLASSIC_4K(bTagType) ) - { - return MFCUK_FAIL_TAGTYPE_INVALID; - } + ks2 = reader_response ^ prng_successor(nt_orig, 64); + ks3 = tag_response ^ prng_successor(nt_orig, 96); + pcs = lfsr_recovery64(ks2, ks3); - if ( !is_valid_block(bTagType, uiBlock) ) - { - return MFCUK_FAIL_BLOCK_INVALID; - } - - // Configure the authentication frame using the supplied block - abtAuth[0] = bKeyType; - abtAuth[1] = uiBlock; - iso14443a_crc_append(abtAuth,2); - - // Now we take over, first we need full control over the CRC - if ( 0 > nfc_device_set_property_bool(pnd,NP_HANDLE_CRC,false) ) - { - return MFCUK_FAIL_COMM; - } - - // We need to disable EASY_FRAMING feature to talk in "raw" mode - nfc_device_set_property_bool (pnd, NP_EASY_FRAMING, false); - - // Request plain tag-nonce - if (0 > nfc_initiator_transceive_bytes(pnd,abtAuth,4,abtRx,sizeof(abtRx),-1)) - { - return MFCUK_FAIL_COMM; - } - nfc_device_set_property_bool (pnd, NP_EASY_FRAMING, true); - - // Save the tag nonce (nt) - nt = bswap_32_pu8(abtRx); - nt_orig = nt; - - // Init cipher with key - pcs = crypto1_create(ui64Key); - - // Load (plain) uid^nt into the cipher - for (pos=0; pos<4; pos++) - { - // Update the cipher with the tag-initialization - crypto1_byte(pcs, ((uiUID >> (8*(3-pos))) & 0xFF ) ^ abtRx[pos], 0); - } - - // Generate (encrypted) nr+parity by loading it into the cipher (Nr) - for (pos=0; pos<4; pos++) - { - // Load in, and encrypt, the reader nonce (plain nr=0x00000000) - abtArEnc[pos] = crypto1_byte(pcs,0x00,0) ^ 0x00; - - // Encrypt the parity bits for the 4 plaintext bytes of nr - abtArEncPar[pos] = filter(pcs->odd) ^ oddparity(0x00); - - // Get the keystream encrypted Nr value currently loaded into the cypher, i.e. {Nr} - nr_encrypted = nr_encrypted << 8; - nr_encrypted = nr_encrypted | abtArEnc[pos]; - } - - // Skip 32 bits in pseudo random generator - nt = prng_successor(nt,32); - - // Generate reader-answer from tag-nonce (Ar) - for (pos=4; pos<8; pos++) - { - // Get the next random byte for verify the reader to the tag - nt = prng_successor(nt,8); - - // Encrypt the reader-answer (nt' = suc2(nt)) - abtArEnc[pos] = crypto1_byte(pcs,0x00,0) ^ (nt&0xff); - - // Encrypt the parity bits for the 4 plaintext bytes of nt' - abtArEncPar[pos] = filter(pcs->odd) ^ oddparity(nt&0xff); - - // Get the keystream encrypted reader response currently loaded into the cypher, i.e. {Ar} - reader_response = reader_response << 8; - reader_response = reader_response | abtArEnc[pos]; - } - - // Finally we want to send arbitrary parity bits - if ( 0 > nfc_device_set_property_bool(pnd,NP_HANDLE_PARITY,false) ) - { - return MFCUK_FAIL_COMM; - } - - int res; - if ( 0 > (res = nfc_initiator_transceive_bits(pnd,abtArEnc,64,abtArEncPar,abtRx,sizeof(abtRx),abtRxPar)) ) - { - return MFCUK_FAIL_AUTH; - } + lfsr_rollback_word(pcs, 0, 0); + lfsr_rollback_word(pcs, 0, 0); + lfsr_rollback_word(pcs, nr_encrypted, 1); + lfsr_rollback_word(pcs, uiUID ^ nt_orig, 0); + crypto1_get_lfsr(pcs, &lfsr); crypto1_destroy(pcs); - if (res == 32) - { - for (pos=0; pos<4; pos++) - { - tag_response = tag_response << 8; - tag_response = tag_response | abtRx[pos]; - } - - ks2 = reader_response ^ prng_successor(nt_orig, 64); - ks3 = tag_response ^ prng_successor(nt_orig, 96); - pcs = lfsr_recovery64(ks2, ks3); - - lfsr_rollback_word(pcs, 0, 0); - lfsr_rollback_word(pcs, 0, 0); - lfsr_rollback_word(pcs, nr_encrypted, 1); - lfsr_rollback_word(pcs, uiUID ^ nt_orig, 0); - crypto1_get_lfsr(pcs, &lfsr); - - crypto1_destroy(pcs); - - if (lfsr != ui64Key) - { - return MFCUK_FAIL_CRAPTO; - } + if (lfsr != ui64Key) { + return MFCUK_FAIL_CRAPTO; } - else + } else { + return MFCUK_FAIL_AUTH; + } + + return MFCUK_SUCCESS; +} + +static uint32_t mfcuk_key_recovery_block(nfc_device *pnd, uint32_t uiUID, uint64_t ui64Key, mifare_key_type bKeyType, uint8_t bTagType, uint32_t uiBlock, uint64_t *ui64KeyRecovered) +{ + // Communication variables + uint32_t pos, pos2, nt; + struct Crypto1State *pcs; + uint8_t abtAuth[4] = { 0x60, 0x00, 0x00, 0x00 }; + uint8_t abtArEnc[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + uint8_t abtArEncPar[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + uint8_t abtRx[MAX_FRAME_LEN]; + uint8_t abtRxPar[MAX_FRAME_LEN]; + + // zveriu + static uint32_t nt_orig = 0; + char sendSpoofAr = 0; // We want to spoof the Ar response with all 0s and the use random parity bits for that Nt until we have a successful 4 bits response (0x5) + tag_nonce_entry_t *ptrFoundTagNonceEntry = NULL; + + // Key-recovery variables + struct Crypto1State *states_list; + struct Crypto1State *current_state; + uint32_t i; + uint64_t key_recovered; + uint8_t flag_key_recovered = 0; // FIXME: fix the {Nr} iteration properly. This a quick fix for cases when 0xDEADBEEF {Nr} is not working + + if ((bKeyType != keyA) && (bKeyType != keyB)) { + return MFCUK_FAIL_KEYTYPE_INVALID; + } + + if (!IS_MIFARE_CLASSIC_1K(bTagType) && !IS_MIFARE_CLASSIC_4K(bTagType)) { + return MFCUK_FAIL_TAGTYPE_INVALID; + } + + if (!is_valid_block(bTagType, uiBlock)) { + return MFCUK_FAIL_BLOCK_INVALID; + } + + // Configure the authentication frame using the supplied block + abtAuth[0] = bKeyType; + abtAuth[1] = uiBlock; + iso14443a_crc_append(abtAuth, 2); + + // Now we take over, first we need full control over the CRC + nfc_device_set_property_bool(pnd, NP_HANDLE_CRC, false); + + // We need to disable EASY_FRAMING feature to talk in "raw" mode + nfc_device_set_property_bool(pnd, NP_EASY_FRAMING, false); + + // Request plain tag-nonce + //printf("Nt: "); + if (0 > nfc_initiator_transceive_bytes(pnd, abtAuth, 4, abtRx, sizeof(abtRx), -1)) { + //printf("\n\nFAILURE - Failed to get TAG NONCE!!!\n\n"); + return MFCUK_FAIL_COMM; + } + nfc_device_set_property_bool(pnd, NP_EASY_FRAMING, true); + + //print_hex(abtRx,4); + + // Save the tag nonce (nt) + nt = bswap_32_pu8(abtRx); + + // zveriu + //printf("INFO - Nonce distance %d (from 0x%08x, to 0x%08x)\n", nonce_distance(nt, nt_orig), nt, nt_orig); + nt_orig = nt; + + // Max log(2, MAX_TAG_NONCES) searches, i.e. log(2, 65536) = 16 + ptrFoundTagNonceEntry = (tag_nonce_entry_t *) bsearch((void *)(&nt_orig), arrSpoofEntries, numSpoofEntries, sizeof(arrSpoofEntries[0]), compareTagNonces); + + // A new tag nonce detected, initialize it properly and store in the tag nonce "cache" array for use in it's next appearances + if (!ptrFoundTagNonceEntry) { + if (numSpoofEntries >= MAX_TAG_NONCES) { + //printf("\n\nFAILURE - REACHED MAX_TAG_NONCES!!! (Are we so unlucky or the USB/reader is buggy?!)\n\n"); + return MFCUK_FAIL_MEMORY; + } + + arrSpoofEntries[numSpoofEntries].tagNonce = nt_orig; + arrSpoofEntries[numSpoofEntries].num_of_appearances = 1; + numSpoofEntries++; + + // Max log(2, MAX_TAG_NONCES) searches, i.e. log(2, 65536) = 16 + qsort(arrSpoofEntries, numSpoofEntries, sizeof(arrSpoofEntries[0]), compareTagNonces); + + ptrFoundTagNonceEntry = (tag_nonce_entry_t *) bsearch((void *)(&nt_orig), arrSpoofEntries, numSpoofEntries, sizeof(arrSpoofEntries[0]), compareTagNonces); + + // Put the initializations done in abtRxLen == 32 section here also because maybe we don't know the key actually + ptrFoundTagNonceEntry->spoofFlag = 1; + + // Hardcoding {Nr} and {Ar} and try to guess parity bits + ptrFoundTagNonceEntry->spoofNrEnc = MFCUK_DARKSIDE_START_NR; + ptrFoundTagNonceEntry->spoofArEnc = MFCUK_DARKSIDE_START_AR; + ptrFoundTagNonceEntry->spoofParBitsEnc = 0x0; + + // First we need to satisfy STAGE1 + ptrFoundTagNonceEntry->current_out_of_8 = -1; + } else { + ptrFoundTagNonceEntry->num_of_appearances++; + + + if ( // If we went beyond MFCUK_DARKSIDE_MAX_LEVELS without findind a key, need to check next {Nr} + (ptrFoundTagNonceEntry->current_out_of_8 >= MFCUK_DARKSIDE_MAX_LEVELS) || + // Can have only 32 combinations of the last 5 bits of parity bits which generated the first NACK + ((ptrFoundTagNonceEntry->current_out_of_8 >= 0) && (ptrFoundTagNonceEntry->parBitsCrntCombination[ptrFoundTagNonceEntry->current_out_of_8] >= 0x20)) + ) { + // If no key discovered for current {Nr}, {Ar}, 29bit-prefix, go back to satisfy STAGE1 with other {Nr} value, {Ar} we keep the same + ptrFoundTagNonceEntry->spoofNrEnc++; + ptrFoundTagNonceEntry->spoofArEnc = MFCUK_DARKSIDE_START_AR; + ptrFoundTagNonceEntry->spoofParBitsEnc = 0x0; + ptrFoundTagNonceEntry->current_out_of_8 = -1; + + return MFCUK_FAIL_AUTH; + } + + /* + // TODO: if above block is working fine, delete this commented - above one created to reduce code-duplication + // If we went beyond MFCUK_DARKSIDE_MAX_LEVELS without findind a key, need to check next {Nr} + if (ptrFoundTagNonceEntry->current_out_of_8 >= MFCUK_DARKSIDE_MAX_LEVELS) { + //printf("FAILURE - This Nt, {Pfx}, consecutive {Nr}s and {ParBits} combination cannot produce a key-recoverable state\n"); + //printf("\tINFO: try changing initial {Nr}, {Ar} and timings of sleep()\n"); + + //printf("{Nr} is not a DEADBEEF.... Need to find BEEF ALIVE!... Trying next one...\n"); + ptrFoundTagNonceEntry->spoofNrEnc++; + ptrFoundTagNonceEntry->spoofArEnc = 0xFACECAFE; + ptrFoundTagNonceEntry->spoofParBitsEnc = 0x0; + + // If no key discovered for current {Nr}, {Ar}, 29bit-prefix, go back to satisfy STAGE1 with other {Nr} value, {Ar} we keep the same + ptrFoundTagNonceEntry->current_out_of_8 = -1; + return MFCUK_FAIL_AUTH; } - return MFCUK_SUCCESS; -} - -static uint32_t mfcuk_key_recovery_block(nfc_device* pnd, uint32_t uiUID, uint64_t ui64Key, mifare_key_type bKeyType, uint8_t bTagType, uint32_t uiBlock, uint64_t *ui64KeyRecovered) -{ - // Communication variables - uint32_t pos, pos2, nt; - struct Crypto1State* pcs; - uint8_t abtAuth[4] = { 0x60,0x00,0x00,0x00 }; - uint8_t abtArEnc[8] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; - uint8_t abtArEncPar[8] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; - uint8_t abtRx[MAX_FRAME_LEN]; - uint8_t abtRxPar[MAX_FRAME_LEN]; - - // zveriu - static uint32_t nt_orig = 0; - char sendSpoofAr = 0; // We want to spoof the Ar response with all 0s and the use random parity bits for that Nt until we have a successful 4 bits response (0x5) - tag_nonce_entry_t *ptrFoundTagNonceEntry = NULL; - - // Key-recovery variables - struct Crypto1State *states_list; - struct Crypto1State *current_state; - uint32_t i; - uint64_t key_recovered; - uint8_t flag_key_recovered = 0; // FIXME: fix the {Nr} iteration properly. This a quick fix for cases when 0xDEADBEEF {Nr} is not working - - if ( (bKeyType != keyA) && (bKeyType != keyB) ) + if (ptrFoundTagNonceEntry->current_out_of_8 >= 0) { - return MFCUK_FAIL_KEYTYPE_INVALID; - } - - if ( !IS_MIFARE_CLASSIC_1K(bTagType) && !IS_MIFARE_CLASSIC_4K(bTagType) ) - { - return MFCUK_FAIL_TAGTYPE_INVALID; - } - - if ( !is_valid_block(bTagType, uiBlock) ) - { - return MFCUK_FAIL_BLOCK_INVALID; - } - - // Configure the authentication frame using the supplied block - abtAuth[0] = bKeyType; - abtAuth[1] = uiBlock; - iso14443a_crc_append(abtAuth,2); - - // Now we take over, first we need full control over the CRC - nfc_device_set_property_bool(pnd,NP_HANDLE_CRC,false); - - // We need to disable EASY_FRAMING feature to talk in "raw" mode - nfc_device_set_property_bool(pnd, NP_EASY_FRAMING, false); - - // Request plain tag-nonce - //printf("Nt: "); - if (0 > nfc_initiator_transceive_bytes(pnd,abtAuth,4,abtRx,sizeof(abtRx),-1)) - { - //printf("\n\nFAILURE - Failed to get TAG NONCE!!!\n\n"); - return MFCUK_FAIL_COMM; - } - nfc_device_set_property_bool (pnd, NP_EASY_FRAMING, true); - - //print_hex(abtRx,4); - - // Save the tag nonce (nt) - nt = bswap_32_pu8(abtRx); - - // zveriu - //printf("INFO - Nonce distance %d (from 0x%08x, to 0x%08x)\n", nonce_distance(nt, nt_orig), nt, nt_orig); - nt_orig = nt; - - // Max log(2, MAX_TAG_NONCES) searches, i.e. log(2, 65536) = 16 - ptrFoundTagNonceEntry = (tag_nonce_entry_t *) bsearch((void *)(&nt_orig), arrSpoofEntries, numSpoofEntries, sizeof(arrSpoofEntries[0]), compareTagNonces); - - // A new tag nonce detected, initialize it properly and store in the tag nonce "cache" array for use in it's next appearances - if (!ptrFoundTagNonceEntry) - { - if (numSpoofEntries >= MAX_TAG_NONCES) + // Can have only 32 combinations of the last 5 bits of parity bits which generated the first NACK + if (ptrFoundTagNonceEntry->parBitsCrntCombination[ptrFoundTagNonceEntry->current_out_of_8] >= 0x20) { - //printf("\n\nFAILURE - REACHED MAX_TAG_NONCES!!! (Are we so unlucky or the USB/reader is buggy?!)\n\n"); - return MFCUK_FAIL_MEMORY; - } - - arrSpoofEntries[numSpoofEntries].tagNonce = nt_orig; - arrSpoofEntries[numSpoofEntries].num_of_appearances = 1; - numSpoofEntries++; - - // Max log(2, MAX_TAG_NONCES) searches, i.e. log(2, 65536) = 16 - qsort(arrSpoofEntries, numSpoofEntries, sizeof(arrSpoofEntries[0]), compareTagNonces); - - ptrFoundTagNonceEntry = (tag_nonce_entry_t *) bsearch((void *)(&nt_orig), arrSpoofEntries, numSpoofEntries, sizeof(arrSpoofEntries[0]), compareTagNonces); - - // Put the initializations done in abtRxLen == 32 section here also because maybe we don't know the key actually - ptrFoundTagNonceEntry->spoofFlag = 1; - - // Hardcoding {Nr} and {Ar} and try to guess parity bits - ptrFoundTagNonceEntry->spoofNrEnc = MFCUK_DARKSIDE_START_NR; - ptrFoundTagNonceEntry->spoofArEnc = MFCUK_DARKSIDE_START_AR; - ptrFoundTagNonceEntry->spoofParBitsEnc = 0x0; - - // First we need to satisfy STAGE1 - ptrFoundTagNonceEntry->current_out_of_8 = -1; - } - else - { - ptrFoundTagNonceEntry->num_of_appearances++; - - - if ( // If we went beyond MFCUK_DARKSIDE_MAX_LEVELS without findind a key, need to check next {Nr} - (ptrFoundTagNonceEntry->current_out_of_8 >= MFCUK_DARKSIDE_MAX_LEVELS) || - // Can have only 32 combinations of the last 5 bits of parity bits which generated the first NACK - ( (ptrFoundTagNonceEntry->current_out_of_8 >= 0) && (ptrFoundTagNonceEntry->parBitsCrntCombination[ptrFoundTagNonceEntry->current_out_of_8] >= 0x20) ) - ) - { - // If no key discovered for current {Nr}, {Ar}, 29bit-prefix, go back to satisfy STAGE1 with other {Nr} value, {Ar} we keep the same - ptrFoundTagNonceEntry->spoofNrEnc++; - ptrFoundTagNonceEntry->spoofArEnc = MFCUK_DARKSIDE_START_AR; - ptrFoundTagNonceEntry->spoofParBitsEnc = 0x0; - ptrFoundTagNonceEntry->current_out_of_8 = -1; - - return MFCUK_FAIL_AUTH; - } - - /* - // TODO: if above block is working fine, delete this commented - above one created to reduce code-duplication - // If we went beyond MFCUK_DARKSIDE_MAX_LEVELS without findind a key, need to check next {Nr} - if (ptrFoundTagNonceEntry->current_out_of_8 >= MFCUK_DARKSIDE_MAX_LEVELS) - { - //printf("FAILURE - This Nt, {Pfx}, consecutive {Nr}s and {ParBits} combination cannot produce a key-recoverable state\n"); + //printf("FAILURE - This consecutive {Nr}s and {ParBits} combination cannot produce all 8 required NACKs and KSs of NACKs\n"); //printf("\tINFO: try changing initial {Nr}, {Ar} and timings of sleep()\n"); //printf("{Nr} is not a DEADBEEF.... Need to find BEEF ALIVE!... Trying next one...\n"); @@ -531,218 +529,169 @@ static uint32_t mfcuk_key_recovery_block(nfc_device* pnd, uint32_t uiUID, uint64 return MFCUK_FAIL_AUTH; } + } + */ + } - if (ptrFoundTagNonceEntry->current_out_of_8 >= 0) - { - // Can have only 32 combinations of the last 5 bits of parity bits which generated the first NACK - if (ptrFoundTagNonceEntry->parBitsCrntCombination[ptrFoundTagNonceEntry->current_out_of_8] >= 0x20) - { - //printf("FAILURE - This consecutive {Nr}s and {ParBits} combination cannot produce all 8 required NACKs and KSs of NACKs\n"); - //printf("\tINFO: try changing initial {Nr}, {Ar} and timings of sleep()\n"); + sendSpoofAr = ptrFoundTagNonceEntry->spoofFlag; - //printf("{Nr} is not a DEADBEEF.... Need to find BEEF ALIVE!... Trying next one...\n"); - ptrFoundTagNonceEntry->spoofNrEnc++; - ptrFoundTagNonceEntry->spoofArEnc = 0xFACECAFE; - ptrFoundTagNonceEntry->spoofParBitsEnc = 0x0; + // Init cipher with key + pcs = crypto1_create(ui64Key); - // If no key discovered for current {Nr}, {Ar}, 29bit-prefix, go back to satisfy STAGE1 with other {Nr} value, {Ar} we keep the same - ptrFoundTagNonceEntry->current_out_of_8 = -1; + // Load (plain) uid^nt into the cipher + for (pos = 0; pos < 4; pos++) { + // Update the cipher with the tag-initialization + // TODO: remove later - crypto1_byte(pcs, pbtUid[pos]^abtRx[pos], 0); + crypto1_byte(pcs, ((uiUID >> (8 * (3 - pos))) & 0xFF) ^ abtRx[pos], 0); + } - return MFCUK_FAIL_AUTH; - } - } - */ + // Generate (encrypted) nr+parity by loading it into the cipher (Nr) + for (pos = 0; pos < 4; pos++) { + // Load in, and encrypt, the reader nonce (plain nr=0x00000000) + abtArEnc[pos] = crypto1_byte(pcs, 0x00, 0) ^ 0x00; + + // Encrypt the parity bits for the 4 plaintext bytes of nr + abtArEncPar[pos] = filter(pcs->odd) ^ oddparity(0x00); + + if (sendSpoofAr) { + if (ptrFoundTagNonceEntry->current_out_of_8 < 0) { + abtArEnc[pos] = (ptrFoundTagNonceEntry->spoofNrEnc >> (8 * (3 - pos))) & 0xFF; + abtArEncPar[pos] = (ptrFoundTagNonceEntry->spoofParBitsEnc >> (7 - pos)) & 0x01; + } else { + abtArEnc[pos] = (ptrFoundTagNonceEntry->nrEnc[ptrFoundTagNonceEntry->current_out_of_8] >> (8 * (3 - pos))) & 0xFF; + abtArEncPar[pos] = ((ptrFoundTagNonceEntry->parBits[ptrFoundTagNonceEntry->current_out_of_8] + ptrFoundTagNonceEntry->parBitsCrntCombination[ptrFoundTagNonceEntry->current_out_of_8]) >> (7 - pos)) & 0x01; + } + } + } + + // Skip 32 bits in pseudo random generator + nt = prng_successor(nt, 32); + + // Generate reader-answer from tag-nonce (Ar) + for (pos = 4; pos < 8; pos++) { + // Get the next random byte for verify the reader to the tag + nt = prng_successor(nt, 8); + + // Encrypt the reader-answer (nt' = suc2(nt)) + abtArEnc[pos] = crypto1_byte(pcs, 0x00, 0) ^(nt & 0xff); + // Encrypt the parity bits for the 4 plaintext bytes of nt' + abtArEncPar[pos] = filter(pcs->odd) ^ oddparity(nt & 0xff); + + // zveriu - Make the Ar incorrect, but leave parity bits calculated/guessed_spoofed as above + /* If all eight parity bits are correct, but the answer Ar is + wrong, the tag responds with the 4-bit error code 0x5 + signifying failed authentication, called ‘transmission error’ in [KHG08]. + */ + if (sendSpoofAr) { + if (ptrFoundTagNonceEntry->current_out_of_8 < 0) { + abtArEnc[pos] = (ptrFoundTagNonceEntry->spoofArEnc >> (8 * (7 - pos))) & 0xFF; + abtArEncPar[pos] = (ptrFoundTagNonceEntry->spoofParBitsEnc >> (7 - pos)) & 0x01; + } else { + abtArEnc[pos] = (ptrFoundTagNonceEntry->arEnc[ptrFoundTagNonceEntry->current_out_of_8] >> (8 * (7 - pos))) & 0xFF; + abtArEncPar[pos] = ((ptrFoundTagNonceEntry->parBits[ptrFoundTagNonceEntry->current_out_of_8] + ptrFoundTagNonceEntry->parBitsCrntCombination[ptrFoundTagNonceEntry->current_out_of_8]) >> (7 - pos)) & 0x01; + } + } + } + + if (ptrFoundTagNonceEntry->current_out_of_8 >= 0) { + // Prepare for the next round (if this one is not successful) the next 5 bit combination for current parity bits + ptrFoundTagNonceEntry->parBitsCrntCombination[ptrFoundTagNonceEntry->current_out_of_8]++; + } + + // Finally we want to send arbitrary parity bits + nfc_device_set_property_bool(pnd, NP_HANDLE_PARITY, false); + + // Transmit reader-answer + //printf(" Ar: "); + //print_hex_par(abtArEnc,64,abtArEncPar); + + int res; + if (0 > (res = nfc_initiator_transceive_bits(pnd, abtArEnc, 64, abtArEncPar, abtRx, sizeof(abtRx), abtRxPar))) { + if (sendSpoofAr) { + ptrFoundTagNonceEntry->spoofParBitsEnc++; } - sendSpoofAr = ptrFoundTagNonceEntry->spoofFlag; + return MFCUK_FAIL_AUTH; + } - // Init cipher with key - pcs = crypto1_create(ui64Key); + // zveriu - Successful: either authentication (szRx == 32) either encrypted 0x5 reponse (szRx == 4) + if (res == 4) { + //printf("INFO - 4-bit (szRx=%d) error code 0x5 encrypted (abtRx=0x%02x)\n", szRx, abtRx[0] & 0xf); - // Load (plain) uid^nt into the cipher - for (pos=0; pos<4; pos++) - { - // Update the cipher with the tag-initialization - // TODO: remove later - crypto1_byte(pcs, pbtUid[pos]^abtRx[pos], 0); - crypto1_byte(pcs, ((uiUID >> (8*(3-pos))) & 0xFF ) ^ abtRx[pos], 0); - } + if (ptrFoundTagNonceEntry->current_out_of_8 < 0) { + ptrFoundTagNonceEntry->spoofNackEnc = abtRx[0] & 0xf; + ptrFoundTagNonceEntry->spoofKs = ptrFoundTagNonceEntry->spoofNackEnc ^ 0x5; + ptrFoundTagNonceEntry->spoofNrPfx = ptrFoundTagNonceEntry->spoofNrEnc & 0xFFFFFF1F; - // Generate (encrypted) nr+parity by loading it into the cipher (Nr) - for (pos=0; pos<4; pos++) - { - // Load in, and encrypt, the reader nonce (plain nr=0x00000000) - abtArEnc[pos] = crypto1_byte(pcs,0x00,0) ^ 0x00; + // Initialize the {Nr} with proper 29 bits prefix and {Par} with proper 3 bits prefix + for (pos = 0; pos < 8; pos++) { + ptrFoundTagNonceEntry->nrEnc[pos] = ptrFoundTagNonceEntry->spoofNrPfx | pos << 5; + ptrFoundTagNonceEntry->arEnc[pos] = ptrFoundTagNonceEntry->spoofArEnc; + ptrFoundTagNonceEntry->parBits[pos] = ptrFoundTagNonceEntry->spoofParBitsEnc & 0xE0; + ptrFoundTagNonceEntry->parBitsCrntCombination[pos] = 0; + } - // Encrypt the parity bits for the 4 plaintext bytes of nr - abtArEncPar[pos] = filter(pcs->odd) ^ oddparity(0x00); + // Mark the begining of collecting STAGE2 probes + ptrFoundTagNonceEntry->current_out_of_8 = 0; + } else { + ptrFoundTagNonceEntry->nackEnc[ptrFoundTagNonceEntry->current_out_of_8] = abtRx[0] & 0xf; + ptrFoundTagNonceEntry->ks[ptrFoundTagNonceEntry->current_out_of_8] = ptrFoundTagNonceEntry->nackEnc[ptrFoundTagNonceEntry->current_out_of_8] ^ 0x5; + ptrFoundTagNonceEntry->current_out_of_8++; - if (sendSpoofAr) - { - if (ptrFoundTagNonceEntry->current_out_of_8 < 0) - { - abtArEnc[pos] = (ptrFoundTagNonceEntry->spoofNrEnc >> (8*(3-pos))) & 0xFF; - abtArEncPar[pos] = (ptrFoundTagNonceEntry->spoofParBitsEnc >> (7-pos)) & 0x01; - } - else - { - abtArEnc[pos] = (ptrFoundTagNonceEntry->nrEnc[ptrFoundTagNonceEntry->current_out_of_8] >> (8*(3-pos))) & 0xFF; - abtArEncPar[pos] = ((ptrFoundTagNonceEntry->parBits[ptrFoundTagNonceEntry->current_out_of_8] + ptrFoundTagNonceEntry->parBitsCrntCombination[ptrFoundTagNonceEntry->current_out_of_8]) >> (7-pos)) & 0x01; - } - } - } - - // Skip 32 bits in pseudo random generator - nt = prng_successor(nt,32); - - // Generate reader-answer from tag-nonce (Ar) - for (pos=4; pos<8; pos++) - { - // Get the next random byte for verify the reader to the tag - nt = prng_successor(nt,8); - - // Encrypt the reader-answer (nt' = suc2(nt)) - abtArEnc[pos] = crypto1_byte(pcs,0x00,0) ^ (nt&0xff); - // Encrypt the parity bits for the 4 plaintext bytes of nt' - abtArEncPar[pos] = filter(pcs->odd) ^ oddparity(nt&0xff); - - // zveriu - Make the Ar incorrect, but leave parity bits calculated/guessed_spoofed as above - /* If all eight parity bits are correct, but the answer Ar is - wrong, the tag responds with the 4-bit error code 0x5 - signifying failed authentication, called ‘transmission error’ in [KHG08]. - */ - if (sendSpoofAr) - { - if (ptrFoundTagNonceEntry->current_out_of_8 < 0) - { - abtArEnc[pos] = (ptrFoundTagNonceEntry->spoofArEnc >> (8*(7-pos))) & 0xFF; - abtArEncPar[pos] = (ptrFoundTagNonceEntry->spoofParBitsEnc >> (7-pos)) & 0x01; - } - else - { - abtArEnc[pos] = (ptrFoundTagNonceEntry->arEnc[ptrFoundTagNonceEntry->current_out_of_8] >> (8*(7-pos))) & 0xFF; - abtArEncPar[pos] = ((ptrFoundTagNonceEntry->parBits[ptrFoundTagNonceEntry->current_out_of_8] + ptrFoundTagNonceEntry->parBitsCrntCombination[ptrFoundTagNonceEntry->current_out_of_8]) >> (7-pos)) & 0x01; - } - } - } - - if (ptrFoundTagNonceEntry->current_out_of_8 >= 0) - { - // Prepare for the next round (if this one is not successful) the next 5 bit combination for current parity bits - ptrFoundTagNonceEntry->parBitsCrntCombination[ptrFoundTagNonceEntry->current_out_of_8]++; - } - - // Finally we want to send arbitrary parity bits - nfc_device_set_property_bool(pnd,NP_HANDLE_PARITY,false); - - // Transmit reader-answer - //printf(" Ar: "); - //print_hex_par(abtArEnc,64,abtArEncPar); - - int res; - if (0 > (res=nfc_initiator_transceive_bits(pnd,abtArEnc,64,abtArEncPar,abtRx,sizeof(abtRx),abtRxPar))) - { - if (sendSpoofAr) - { - ptrFoundTagNonceEntry->spoofParBitsEnc++; + if (ptrFoundTagNonceEntry->current_out_of_8 == 8) { + for (pos = 0; pos < 8; pos++) { + for (pos2 = 0; pos2 < 8; pos2++) { + ptrFoundTagNonceEntry->parBitsArr[pos][pos2] = ((ptrFoundTagNonceEntry->parBits[pos] + ptrFoundTagNonceEntry->parBitsCrntCombination[pos] - 1) >> (7 - pos2)) & 0x01; + } } - return MFCUK_FAIL_AUTH; - } + states_list = lfsr_common_prefix(ptrFoundTagNonceEntry->spoofNrPfx, ptrFoundTagNonceEntry->spoofArEnc, ptrFoundTagNonceEntry->ks, ptrFoundTagNonceEntry->parBitsArr); - // zveriu - Successful: either authentication (szRx == 32) either encrypted 0x5 reponse (szRx == 4) - if (res == 4) - { - //printf("INFO - 4-bit (szRx=%d) error code 0x5 encrypted (abtRx=0x%02x)\n", szRx, abtRx[0] & 0xf); + for (i = 0; (states_list) && ((states_list + i)->odd != 0 || (states_list + i)->even != 0) && (i < MAX_COMMON_PREFIX_STATES); i++) { + current_state = states_list + i; + lfsr_rollback_word(current_state, uiUID ^ ptrFoundTagNonceEntry->tagNonce, 0); + crypto1_get_lfsr(current_state, &key_recovered); - if (ptrFoundTagNonceEntry->current_out_of_8 < 0) - { - ptrFoundTagNonceEntry->spoofNackEnc = abtRx[0] & 0xf; - ptrFoundTagNonceEntry->spoofKs = ptrFoundTagNonceEntry->spoofNackEnc ^ 0x5; - ptrFoundTagNonceEntry->spoofNrPfx = ptrFoundTagNonceEntry->spoofNrEnc & 0xFFFFFF1F; + if (bfOpts['v'] && (verboseLevel > 1)) { + printf("\nINFO: block %d recovered KEY: %012"PRIx64"\n", uiBlock, key_recovered); + } - // Initialize the {Nr} with proper 29 bits prefix and {Par} with proper 3 bits prefix - for (pos=0; pos<8; pos++) - { - ptrFoundTagNonceEntry->nrEnc[pos] = ptrFoundTagNonceEntry->spoofNrPfx | pos << 5; - ptrFoundTagNonceEntry->arEnc[pos] = ptrFoundTagNonceEntry->spoofArEnc; - ptrFoundTagNonceEntry->parBits[pos] = ptrFoundTagNonceEntry->spoofParBitsEnc & 0xE0; - ptrFoundTagNonceEntry->parBitsCrntCombination[pos] = 0; - } + flag_key_recovered = 1; - // Mark the begining of collecting STAGE2 probes - ptrFoundTagNonceEntry->current_out_of_8 = 0; + *ui64KeyRecovered = key_recovered; } - else - { - ptrFoundTagNonceEntry->nackEnc[ptrFoundTagNonceEntry->current_out_of_8] = abtRx[0] & 0xf; - ptrFoundTagNonceEntry->ks[ptrFoundTagNonceEntry->current_out_of_8] = ptrFoundTagNonceEntry->nackEnc[ptrFoundTagNonceEntry->current_out_of_8] ^ 0x5; - ptrFoundTagNonceEntry->current_out_of_8++; - if (ptrFoundTagNonceEntry->current_out_of_8 == 8) - { - for (pos=0; pos<8; pos++) - { - for (pos2=0; pos2<8; pos2++) - { - ptrFoundTagNonceEntry->parBitsArr[pos][pos2] = ( (ptrFoundTagNonceEntry->parBits[pos] + ptrFoundTagNonceEntry->parBitsCrntCombination[pos] - 1) >> (7-pos2)) & 0x01; - } - } + crypto1_destroy(states_list); - states_list = lfsr_common_prefix(ptrFoundTagNonceEntry->spoofNrPfx, ptrFoundTagNonceEntry->spoofArEnc, ptrFoundTagNonceEntry->ks, ptrFoundTagNonceEntry->parBitsArr); + if (!flag_key_recovered) { + //printf("{Nr} is not a DEADBEEF.... Need to find BEEF ALIVE!... Trying next one...\n"); + ptrFoundTagNonceEntry->spoofNrEnc++; + ptrFoundTagNonceEntry->spoofArEnc = MFCUK_DARKSIDE_START_AR; + ptrFoundTagNonceEntry->spoofParBitsEnc = 0x0; - for (i=0; (states_list) && ((states_list+i)->odd != 0 || (states_list+i)->even != 0) && (itagNonce, 0); - crypto1_get_lfsr(current_state, &key_recovered); + // If no key discovered for current {Nr}, {Ar}, 29bit-prefix, go back to satisfy STAGE1 with other {Nr} value, {Ar} we keep the same + ptrFoundTagNonceEntry->current_out_of_8 = -1; - if ( bfOpts['v'] && (verboseLevel > 1) ) - { - printf("\nINFO: block %d recovered KEY: %012"PRIx64"\n", uiBlock, key_recovered); - } - - flag_key_recovered = 1; - - *ui64KeyRecovered = key_recovered; - } - - crypto1_destroy(states_list); - - if (!flag_key_recovered) - { - //printf("{Nr} is not a DEADBEEF.... Need to find BEEF ALIVE!... Trying next one...\n"); - ptrFoundTagNonceEntry->spoofNrEnc++; - ptrFoundTagNonceEntry->spoofArEnc = MFCUK_DARKSIDE_START_AR; - ptrFoundTagNonceEntry->spoofParBitsEnc = 0x0; - - // If no key discovered for current {Nr}, {Ar}, 29bit-prefix, go back to satisfy STAGE1 with other {Nr} value, {Ar} we keep the same - ptrFoundTagNonceEntry->current_out_of_8 = -1; - - return MFCUK_FAIL_CRAPTO; - } - } + return MFCUK_FAIL_CRAPTO; } + } } - else if (res == 32) - { - // Are we so MFCUKing lucky (?!), since ui64Key is a "dummy" key - flag_key_recovered = true; - *ui64KeyRecovered = ui64Key; - } + } else if (res == 32) { + // Are we so MFCUKing lucky (?!), since ui64Key is a "dummy" key + flag_key_recovered = true; + *ui64KeyRecovered = ui64Key; + } - //printf(" At: "); - //print_hex_par(abtRx,szRx,abtRxPar); + //printf(" At: "); + //print_hex_par(abtRx,szRx,abtRxPar); - crypto1_destroy(pcs); + crypto1_destroy(pcs); - if (flag_key_recovered) - { - return MFCUK_OK_KEY_RECOVERED; - } - else - { - return MFCUK_SUCCESS; - } + if (flag_key_recovered) { + return MFCUK_OK_KEY_RECOVERED; + } else { + return MFCUK_SUCCESS; + } } /* @@ -751,945 +700,837 @@ TODO: -m max_iterations - stop everything after so many iterations, default is infinite until all keys found -T max_elapsed_time - stop after time elapsed */ -static void print_usage(FILE *fp, const char * prog_name) +static void print_usage(FILE *fp, const char *prog_name) { - fprintf(fp, "Usage:\n"); - fprintf(fp, "-C - require explicit connection to the reader. Without this option, the connection is not made and recovery will not occur\n"); - fprintf(fp, "-i mifare.dmp - load input mifare_classic_tag type dump\n"); - fprintf(fp, "-I mifare_ext.dmp - load input extended dump specific to this tool, has several more fields on top of mifare_classic_tag type dump\n"); - fprintf(fp, "-o mifare.dmp - output the resulting mifare_classic_tag dump to a given file\n"); - fprintf(fp, "-O mifare_ext.dmp - output the resulting extended dump to a given file\n"); - fprintf(fp, "-V sector[:A/B/any_other_alphanum[:fullkey]] - verify key for specified sector, -1 means all sectors\n"); - fprintf(fp, "\tAfter first semicolon key-type can specified: A verifies only keyA, B verifies only keyB, anything else verifies both keys\n"); - fprintf(fp, "\tAfter second semicolon full 12 hex-digits key can specified - this key will override any loaded dump key for the given sector(s) and key-type(s)\n"); - fprintf(fp, "-R sector[:A/B/any_other_alphanum] - recover key for sector, -1 means all sectors.\n"); - fprintf(fp, "\tAfter first semicolon key-type can specified: A recovers only keyA, B recovers only keyB, anything else recovers both keys\n"); - fprintf(fp, "-U UID - force specific UID. If a dump was loaded with -i, -U will overwrite the in the memory where dump was loaded\n"); - fprintf(fp, "-M tagtype - force specific tagtype. 8 is 1K, 24 is 4K, 32 is DESFire\n"); - fprintf(fp, "-D - for sectors and key-types marked for verification, in first place use default keys to verify (maybe you are lucky)\n"); - fprintf(fp, "-d key - specifies additional full 12 hex-digits default key to be checked. Multiple -d options can be used for more additional keys\n"); - fprintf(fp, "-s - milliseconds to sleep for SLEEP_AT_FIELD_OFF (Default: %d ms)\n", SLEEP_AT_FIELD_OFF); - fprintf(fp, "-S - milliseconds to sleep for SLEEP_AFTER_FIELD_ON (Default: %d ms)\n", SLEEP_AFTER_FIELD_ON); - fprintf(fp, "-P hex_literals_separated - try to recover the key from a conversation sniffed with Proxmark3 (mifarecrack.c based). Accepts several options:\n"); - fprintf(fp, "\tConcatenated string in hex literal format of form uid:tag_chal:nr_enc:reader_resp:tag_resp\n"); - fprintf(fp, "\tExample -P 0x5c72325e:0x50829cd6:0xb8671f76:0xe00eefc9:0x4888964f would find key FFFFFFFFFFFF\n"); - fprintf(fp, "-p proxmark3_full.log - tries to parse the log file on it's own (mifarecrack.py based), get the values for option -P and invoke it\n"); - fprintf(fp, "-F - tries to fingerprint the input dump (-i) against known cards' data format\n"); - fprintf(fp, "-v verbose_level - verbose level (default is O)\n"); - fprintf(fp, "\n"); + fprintf(fp, "Usage:\n"); + fprintf(fp, "-C - require explicit connection to the reader. Without this option, the connection is not made and recovery will not occur\n"); + fprintf(fp, "-i mifare.dmp - load input mifare_classic_tag type dump\n"); + fprintf(fp, "-I mifare_ext.dmp - load input extended dump specific to this tool, has several more fields on top of mifare_classic_tag type dump\n"); + fprintf(fp, "-o mifare.dmp - output the resulting mifare_classic_tag dump to a given file\n"); + fprintf(fp, "-O mifare_ext.dmp - output the resulting extended dump to a given file\n"); + fprintf(fp, "-V sector[:A/B/any_other_alphanum[:fullkey]] - verify key for specified sector, -1 means all sectors\n"); + fprintf(fp, "\tAfter first semicolon key-type can specified: A verifies only keyA, B verifies only keyB, anything else verifies both keys\n"); + fprintf(fp, "\tAfter second semicolon full 12 hex-digits key can specified - this key will override any loaded dump key for the given sector(s) and key-type(s)\n"); + fprintf(fp, "-R sector[:A/B/any_other_alphanum] - recover key for sector, -1 means all sectors.\n"); + fprintf(fp, "\tAfter first semicolon key-type can specified: A recovers only keyA, B recovers only keyB, anything else recovers both keys\n"); + fprintf(fp, "-U UID - force specific UID. If a dump was loaded with -i, -U will overwrite the in the memory where dump was loaded\n"); + fprintf(fp, "-M tagtype - force specific tagtype. 8 is 1K, 24 is 4K, 32 is DESFire\n"); + fprintf(fp, "-D - for sectors and key-types marked for verification, in first place use default keys to verify (maybe you are lucky)\n"); + fprintf(fp, "-d key - specifies additional full 12 hex-digits default key to be checked. Multiple -d options can be used for more additional keys\n"); + fprintf(fp, "-s - milliseconds to sleep for SLEEP_AT_FIELD_OFF (Default: %d ms)\n", SLEEP_AT_FIELD_OFF); + fprintf(fp, "-S - milliseconds to sleep for SLEEP_AFTER_FIELD_ON (Default: %d ms)\n", SLEEP_AFTER_FIELD_ON); + fprintf(fp, "-P hex_literals_separated - try to recover the key from a conversation sniffed with Proxmark3 (mifarecrack.c based). Accepts several options:\n"); + fprintf(fp, "\tConcatenated string in hex literal format of form uid:tag_chal:nr_enc:reader_resp:tag_resp\n"); + fprintf(fp, "\tExample -P 0x5c72325e:0x50829cd6:0xb8671f76:0xe00eefc9:0x4888964f would find key FFFFFFFFFFFF\n"); + fprintf(fp, "-p proxmark3_full.log - tries to parse the log file on it's own (mifarecrack.py based), get the values for option -P and invoke it\n"); + fprintf(fp, "-F - tries to fingerprint the input dump (-i) against known cards' data format\n"); + fprintf(fp, "-v verbose_level - verbose level (default is O)\n"); + fprintf(fp, "\n"); - fprintf(fp, "Usage examples:\n"); - fprintf(fp, " Recove all keys from all sectors:\n"); - fprintf(fp, " %s -C -R -1\n", prog_name); + fprintf(fp, "Usage examples:\n"); + fprintf(fp, " Recove all keys from all sectors:\n"); + fprintf(fp, " %s -C -R -1\n", prog_name); - fprintf(fp, " Recove the sector #0 key with 250 ms for all delays (delays could give more results): \n"); - fprintf(fp, " %s -C -R 0 -s 250 -S 250\n", prog_name); - return; + fprintf(fp, " Recove the sector #0 key with 250 ms for all delays (delays could give more results): \n"); + fprintf(fp, " %s -C -R 0 -s 250 -S 250\n", prog_name); + return; } static void print_identification(void) { - fprintf(stdout, "%s - %s\n", PACKAGE_NAME, PACKAGE_VERSION); - fprintf(stdout, "%s - %s\n", BUILD_NAME, BUILD_VERSION); - fprintf(stdout, "by %s\n", BUILD_AUTHOR); - fprintf(stdout, "\n"); + fprintf(stdout, "%s - %s\n", PACKAGE_NAME, PACKAGE_VERSION); + fprintf(stdout, "%s - %s\n", BUILD_NAME, BUILD_VERSION); + fprintf(stdout, "by %s\n", BUILD_AUTHOR); + fprintf(stdout, "\n"); } static void print_mifare_classic_tag_actions(const char *title, mifare_classic_tag *tag) { - uint32_t i, max_blocks, trailer_block; - uint8_t bTagType; - mifare_classic_block_trailer *ptr_trailer = NULL; - - if (!tag) - { - return; - } - - bTagType = tag->amb->mbm.btUnknown; - - if ( !IS_MIFARE_CLASSIC_1K(bTagType) && !IS_MIFARE_CLASSIC_4K(bTagType) ) - { - return; - } - - printf("%s - UID %02x %02x %02x %02x - TYPE 0x%02x (%s)\n", - title, tag->amb->mbm.abtUID[0], tag->amb->mbm.abtUID[1], tag->amb->mbm.abtUID[2], tag->amb->mbm.abtUID[3], bTagType, - (IS_MIFARE_CLASSIC_1K(bTagType)?(MIFARE_CLASSIC_1K_NAME):(IS_MIFARE_CLASSIC_4K(bTagType)?(MIFARE_CLASSIC_4K_NAME):(MIFARE_CLASSIC_UNKN_NAME))) - ); - printf("---------------------------------------------------------------------\n"); - printf("Sector\t| Key A\t|ACTS | RESL\t| Key B\t|ACTS | RESL\n"); - printf("---------------------------------------------------------------------\n"); - - if ( IS_MIFARE_CLASSIC_1K(tag->amb->mbm.btUnknown) ) - { - max_blocks = MIFARE_CLASSIC_1K_MAX_BLOCKS; - } - else - { - max_blocks = MIFARE_CLASSIC_4K_MAX_BLOCKS; - } - - for (i=0; iabtKeyA[0], ptr_trailer->abtKeyA[1], ptr_trailer->abtKeyA[2], - ptr_trailer->abtKeyA[3], ptr_trailer->abtKeyA[4], ptr_trailer->abtKeyA[5], - (ptr_trailer->abtAccessBits[ACTIONS_KEY_A] & ACTIONS_VERIFY)?'V':'.', - (ptr_trailer->abtAccessBits[ACTIONS_KEY_A] & ACTIONS_RECOVER)?'R':'.', - (ptr_trailer->abtAccessBits[RESULTS_KEY_A] & ACTIONS_VERIFY)?'V':'.', - (ptr_trailer->abtAccessBits[RESULTS_KEY_A] & ACTIONS_RECOVER)?'R':'.', - ptr_trailer->abtKeyB[0], ptr_trailer->abtKeyB[1], ptr_trailer->abtKeyB[2], - ptr_trailer->abtKeyB[3], ptr_trailer->abtKeyB[4], ptr_trailer->abtKeyB[5], - (ptr_trailer->abtAccessBits[ACTIONS_KEY_B] & ACTIONS_VERIFY)?'V':'.', - (ptr_trailer->abtAccessBits[ACTIONS_KEY_B] & ACTIONS_RECOVER)?'R':'.', - (ptr_trailer->abtAccessBits[RESULTS_KEY_B] & ACTIONS_VERIFY)?'V':'.', - (ptr_trailer->abtAccessBits[RESULTS_KEY_B] & ACTIONS_RECOVER)?'R':'.' - ); - - // Go beyond current trailer block, i.e. go to next sector - i = trailer_block; - } - - printf("\n"); + uint32_t i, max_blocks, trailer_block; + uint8_t bTagType; + mifare_classic_block_trailer *ptr_trailer = NULL; + if (!tag) { return; + } + + bTagType = tag->amb->mbm.btUnknown; + + if (!IS_MIFARE_CLASSIC_1K(bTagType) && !IS_MIFARE_CLASSIC_4K(bTagType)) { + return; + } + + printf("%s - UID %02x %02x %02x %02x - TYPE 0x%02x (%s)\n", + title, tag->amb->mbm.abtUID[0], tag->amb->mbm.abtUID[1], tag->amb->mbm.abtUID[2], tag->amb->mbm.abtUID[3], bTagType, + (IS_MIFARE_CLASSIC_1K(bTagType) ? (MIFARE_CLASSIC_1K_NAME) : (IS_MIFARE_CLASSIC_4K(bTagType) ? (MIFARE_CLASSIC_4K_NAME) : (MIFARE_CLASSIC_UNKN_NAME))) + ); + printf("---------------------------------------------------------------------\n"); + printf("Sector\t| Key A\t|ACTS | RESL\t| Key B\t|ACTS | RESL\n"); + printf("---------------------------------------------------------------------\n"); + + if (IS_MIFARE_CLASSIC_1K(tag->amb->mbm.btUnknown)) { + max_blocks = MIFARE_CLASSIC_1K_MAX_BLOCKS; + } else { + max_blocks = MIFARE_CLASSIC_4K_MAX_BLOCKS; + } + + for (i = 0; i < max_blocks; i++) { + trailer_block = get_trailer_block(bTagType, i); + + if (!is_valid_block(bTagType, trailer_block)) { + break; + } + + ptr_trailer = (mifare_classic_block_trailer *)((char *)tag + (trailer_block * MIFARE_CLASSIC_BYTES_PER_BLOCK)); + + printf("%d\t| %02x%02x%02x%02x%02x%02x\t| %c %c | %c %c\t| %02x%02x%02x%02x%02x%02x\t| %c %c | %c %c\n", + get_sector_for_block(bTagType, trailer_block), + ptr_trailer->abtKeyA[0], ptr_trailer->abtKeyA[1], ptr_trailer->abtKeyA[2], + ptr_trailer->abtKeyA[3], ptr_trailer->abtKeyA[4], ptr_trailer->abtKeyA[5], + (ptr_trailer->abtAccessBits[ACTIONS_KEY_A] & ACTIONS_VERIFY) ? 'V' : '.', + (ptr_trailer->abtAccessBits[ACTIONS_KEY_A] & ACTIONS_RECOVER) ? 'R' : '.', + (ptr_trailer->abtAccessBits[RESULTS_KEY_A] & ACTIONS_VERIFY) ? 'V' : '.', + (ptr_trailer->abtAccessBits[RESULTS_KEY_A] & ACTIONS_RECOVER) ? 'R' : '.', + ptr_trailer->abtKeyB[0], ptr_trailer->abtKeyB[1], ptr_trailer->abtKeyB[2], + ptr_trailer->abtKeyB[3], ptr_trailer->abtKeyB[4], ptr_trailer->abtKeyB[5], + (ptr_trailer->abtAccessBits[ACTIONS_KEY_B] & ACTIONS_VERIFY) ? 'V' : '.', + (ptr_trailer->abtAccessBits[ACTIONS_KEY_B] & ACTIONS_RECOVER) ? 'R' : '.', + (ptr_trailer->abtAccessBits[RESULTS_KEY_B] & ACTIONS_VERIFY) ? 'V' : '.', + (ptr_trailer->abtAccessBits[RESULTS_KEY_B] & ACTIONS_RECOVER) ? 'R' : '.' + ); + + // Go beyond current trailer block, i.e. go to next sector + i = trailer_block; + } + + printf("\n"); + + return; } static bool mfcuk_darkside_reset_advanced(nfc_device *pnd) { - if ( 0 > nfc_device_set_property_bool(pnd,NP_HANDLE_CRC,true) ) - { - //ERR("configuring NP_HANDLE_CRC"); - //return false; - } + if (0 > nfc_device_set_property_bool(pnd, NP_HANDLE_CRC, true)) { + //ERR("configuring NP_HANDLE_CRC"); + //return false; + } - if ( 0 > nfc_device_set_property_bool(pnd,NP_HANDLE_PARITY,true) ) - { - //ERR("configuring NP_HANDLE_PARITY"); - //return false; - } + if (0 > nfc_device_set_property_bool(pnd, NP_HANDLE_PARITY, true)) { + //ERR("configuring NP_HANDLE_PARITY"); + //return false; + } - return true; + return true; } static bool mfcuk_darkside_select_tag(nfc_device *pnd, int iSleepAtFieldOFF, int iSleepAfterFieldON, nfc_target_info *ti) { - nfc_target ti_tmp; + nfc_target ti_tmp; - if ( !pnd || !ti ) - { - ERR("some parameter are NULL"); - return false; - } + if (!pnd || !ti) { + ERR("some parameter are NULL"); + return false; + } - // Drop the field for a while, so the card can reset - if ( 0 > nfc_device_set_property_bool(pnd,NP_ACTIVATE_FIELD,false) ) - { - ERR("configuring NP_ACTIVATE_FIELD"); - return false; - } + // Drop the field for a while, so the card can reset + if (0 > nfc_device_set_property_bool(pnd, NP_ACTIVATE_FIELD, false)) { + ERR("configuring NP_ACTIVATE_FIELD"); + return false; + } - // {WPMCC09} 2.4. Tag nonces: "drop the field (for approximately 30us) to discharge all capacitors" - sleep(iSleepAtFieldOFF); + // {WPMCC09} 2.4. Tag nonces: "drop the field (for approximately 30us) to discharge all capacitors" + sleep(iSleepAtFieldOFF); - // Let the reader only try once to find a tag - if ( 0 > nfc_device_set_property_bool(pnd,NP_INFINITE_SELECT,false) ) - { - ERR("configuring NP_INFINITE_SELECT"); - return false; - } + // Let the reader only try once to find a tag + if (0 > nfc_device_set_property_bool(pnd, NP_INFINITE_SELECT, false)) { + ERR("configuring NP_INFINITE_SELECT"); + return false; + } - // Configure the CRC and Parity settings - if ( 0 > nfc_device_set_property_bool(pnd,NP_HANDLE_CRC,true) ) - { - ERR("configuring NP_HANDLE_CRC"); - return false; - } + // Configure the CRC and Parity settings + if (0 > nfc_device_set_property_bool(pnd, NP_HANDLE_CRC, true)) { + ERR("configuring NP_HANDLE_CRC"); + return false; + } - if ( 0 > nfc_device_set_property_bool(pnd,NP_HANDLE_PARITY,true) ) - { - ERR("configuring NP_HANDLE_PARITY"); - return false; - } + if (0 > nfc_device_set_property_bool(pnd, NP_HANDLE_PARITY, true)) { + ERR("configuring NP_HANDLE_PARITY"); + return false; + } - // Enable field so more power consuming cards can power themselves up - if ( 0 > nfc_device_set_property_bool(pnd,NP_ACTIVATE_FIELD,true) ) - { - ERR("configuring NP_ACTIVATE_FIELD"); - return false; - } + // Enable field so more power consuming cards can power themselves up + if (0 > nfc_device_set_property_bool(pnd, NP_ACTIVATE_FIELD, true)) { + ERR("configuring NP_ACTIVATE_FIELD"); + return false; + } - // Switch the field back on, and wait for a constant amount of time before authenticating - sleep(iSleepAfterFieldON); + // Switch the field back on, and wait for a constant amount of time before authenticating + sleep(iSleepAfterFieldON); - // Poll for a ISO14443A (MIFARE) tag - if ( 0 >= nfc_initiator_select_passive_target(pnd,nmMifare,NULL,0,&ti_tmp)) - { - ERR("connecting to MIFARE Classic tag"); - return false; - } + // Poll for a ISO14443A (MIFARE) tag + if (0 >= nfc_initiator_select_passive_target(pnd, nmMifare, NULL, 0, &ti_tmp)) { + ERR("connecting to MIFARE Classic tag"); + return false; + } - memcpy( ti, &ti_tmp, sizeof(ti_tmp) ); + memcpy(ti, &ti_tmp, sizeof(ti_tmp)); - return true; + return true; } -int main(int argc, char* argv[]) +int main(int argc, char *argv[]) { - // getopt related - int ch = 0; - char strOutputFilename[256] = {0}; // Initialize with '\0' character - //char extendedDescription[MFCUK_EXTENDED_DESCRIPTION_LENGTH] = {0}; // Initialize with '\0' character - uint8_t keyOpt[MIFARE_CLASSIC_KEY_BYTELENGTH] = {0}; - uint8_t uidOpt[MIFARE_CLASSIC_UID_BYTELENGTH] = {0}; - mifare_classic_block_trailer *ptr_trailer = NULL; - mifare_classic_block_trailer *ptr_trailer_dump = NULL; - int sector = 0; - uint32_t block = 0; - uint8_t action = 0; - uint8_t specific_key_type = 0; - uint8_t max_sectors = MIFARE_CLASSIC_4K_MAX_SECTORS; - // Defaults, can be overriden by -S and -s command line arguments - int iSleepAtFieldOFF = SLEEP_AT_FIELD_OFF; // modified with argument -S - int iSleepAfterFieldON = SLEEP_AFTER_FIELD_ON; // modified with argument -s + // getopt related + int ch = 0; + char strOutputFilename[256] = {0}; // Initialize with '\0' character + //char extendedDescription[MFCUK_EXTENDED_DESCRIPTION_LENGTH] = {0}; // Initialize with '\0' character + uint8_t keyOpt[MIFARE_CLASSIC_KEY_BYTELENGTH] = {0}; + uint8_t uidOpt[MIFARE_CLASSIC_UID_BYTELENGTH] = {0}; + mifare_classic_block_trailer *ptr_trailer = NULL; + mifare_classic_block_trailer *ptr_trailer_dump = NULL; + int sector = 0; + uint32_t block = 0; + uint8_t action = 0; + uint8_t specific_key_type = 0; + uint8_t max_sectors = MIFARE_CLASSIC_4K_MAX_SECTORS; + // Defaults, can be overriden by -S and -s command line arguments + int iSleepAtFieldOFF = SLEEP_AT_FIELD_OFF; // modified with argument -S + int iSleepAfterFieldON = SLEEP_AFTER_FIELD_ON; // modified with argument -s - char *token = NULL; - const char *sep = ":"; - char *str = NULL; - int iter = 0; + char *token = NULL; + const char *sep = ":"; + char *str = NULL; + int iter = 0; - // libnfc related - nfc_context *context; - nfc_device* pnd; - nfc_target ti; + // libnfc related + nfc_context *context; + nfc_device *pnd; + nfc_target ti; - // mifare and crapto related - uint32_t uiErrCode = MFCUK_SUCCESS; - uint64_t ui64KeyRecovered; - mifare_classic_tag_ext dump_loaded_tag; - mifare_classic_tag_ext tag_on_reader; - mifare_classic_tag_ext tag_recover_verify; + // mifare and crapto related + uint32_t uiErrCode = MFCUK_SUCCESS; + uint64_t ui64KeyRecovered; + mifare_classic_tag_ext dump_loaded_tag; + mifare_classic_tag_ext tag_on_reader; + mifare_classic_tag_ext tag_recover_verify; - // fingerprint options related - mifare_classic_tag finger_tag; - float finger_score; - float finger_score_highest; - int finger_index_highest; + // fingerprint options related + mifare_classic_tag finger_tag; + float finger_score; + float finger_score_highest; + int finger_index_highest; - // proxmark3 log related - #define PM3_UID 0 - #define PM3_TAG_CHAL 1 - #define PM3_NR_ENC 2 - #define PM3_READER_RESP 3 - #define PM3_TAG_RESP 4 - #define PM3_MULTISECT_AUTH 5 + // proxmark3 log related +#define PM3_UID 0 +#define PM3_TAG_CHAL 1 +#define PM3_NR_ENC 2 +#define PM3_READER_RESP 3 +#define PM3_TAG_RESP 4 +#define PM3_MULTISECT_AUTH 5 - uint32_t pm3_full_set_log[5]; // order is: uid, tag_challenge, nr_enc, reader_response, tag_response - uint32_t pm3_log_multisect_auth; - uint32_t pm3_ks2; - uint32_t pm3_ks3; - struct Crypto1State *pm3_revstate = NULL; - struct Crypto1State *pm3_revstate_multisect_auth = NULL; - uint64_t pm3_lfsr; - unsigned char* pm3_plfsr = (unsigned char*)&pm3_lfsr; - uint8_t pm3_log_multisect_decrypted[4]; - uint8_t pm3_log_multisect_verified[4]; + uint32_t pm3_full_set_log[5]; // order is: uid, tag_challenge, nr_enc, reader_response, tag_response + uint32_t pm3_log_multisect_auth; + uint32_t pm3_ks2; + uint32_t pm3_ks3; + struct Crypto1State *pm3_revstate = NULL; + struct Crypto1State *pm3_revstate_multisect_auth = NULL; + uint64_t pm3_lfsr; + unsigned char *pm3_plfsr = (unsigned char *)&pm3_lfsr; + uint8_t pm3_log_multisect_decrypted[4]; + uint8_t pm3_log_multisect_verified[4]; - // various related - int i, j, k; - size_t st; - int numDefKeys = mfcuk_default_keys_num; - uint8_t (*current_default_keys)[MIFARE_CLASSIC_KEY_BYTELENGTH]; + // various related + int i, j, k; + size_t st; + int numDefKeys = mfcuk_default_keys_num; + uint8_t (*current_default_keys)[MIFARE_CLASSIC_KEY_BYTELENGTH]; - // At runtime, duplicate the mfcuk_default_keys[], and then add at it's bottom the default keys specified via -d command line options - if ( !(current_default_keys = malloc(numDefKeys * MIFARE_CLASSIC_KEY_BYTELENGTH)) ) - { - ERR("failed to allocate memory for current_default_keys"); - return EXIT_FAILURE; - } + // At runtime, duplicate the mfcuk_default_keys[], and then add at it's bottom the default keys specified via -d command line options + if (!(current_default_keys = malloc(numDefKeys * MIFARE_CLASSIC_KEY_BYTELENGTH))) { + ERR("failed to allocate memory for current_default_keys"); + return EXIT_FAILURE; + } - // Init the structs - memcpy( current_default_keys, mfcuk_default_keys, numDefKeys * MIFARE_CLASSIC_KEY_BYTELENGTH); - memset( &dump_loaded_tag, 0, sizeof(dump_loaded_tag) ); - memset( &tag_on_reader, 0, sizeof(tag_on_reader) ); - memset( &tag_recover_verify, 0, sizeof(tag_recover_verify) ); + // Init the structs + memcpy(current_default_keys, mfcuk_default_keys, numDefKeys * MIFARE_CLASSIC_KEY_BYTELENGTH); + memset(&dump_loaded_tag, 0, sizeof(dump_loaded_tag)); + memset(&tag_on_reader, 0, sizeof(tag_on_reader)); + memset(&tag_recover_verify, 0, sizeof(tag_recover_verify)); - tag_recover_verify.type = MIFARE_CLASSIC_4K; - tag_recover_verify.tag_basic.amb[0].mbm.btUnknown = MIFARE_CLASSIC_4K; + tag_recover_verify.type = MIFARE_CLASSIC_4K; + tag_recover_verify.tag_basic.amb[0].mbm.btUnknown = MIFARE_CLASSIC_4K; - // "Sort-of" initializing the entries - memset((void *)arrSpoofEntries, 0, sizeof(arrSpoofEntries)); + // "Sort-of" initializing the entries + memset((void *)arrSpoofEntries, 0, sizeof(arrSpoofEntries)); - // MAIN ( broken-brain (: ) logic of the tool - // --------------------------------------- - clear_screen(); + // MAIN ( broken-brain (: ) logic of the tool + // --------------------------------------- + clear_screen(); - print_identification(); + print_identification(); - if (argc < 2) - { - print_usage(stdout, argv[0]); - return EXIT_FAILURE; - } + if (argc < 2) { + print_usage(stdout, argv[0]); + return EXIT_FAILURE; + } - // Load fingerprinting "database" - mfcuk_finger_load(); -/* - if (mfcuk_finger_load() == 0) - { - ERR ("Unable to load any fingerprinting database."); - exit (EXIT_FAILURE); - } -*/ - // OPTION PROCESSING BLOCK - // TODO: for WIN32 figure out how to use unistd/posix-compatible Gnu.Getopt.dll (http://getopt.codeplex.com) - // For WIN32 using VERY limited (modified) Xgetopt (http://www.codeproject.com/KB/cpp/xgetopt.aspx) - while ((ch = getopt(argc, argv, "htTDCi:I:o:O:V:R:S:s:v:M:U:d:n:P:p:F:")) != -1) // -1 or EOF - { - switch(ch) - { - // Name for the extended dump - case 'n': - strncpy( tag_recover_verify.description, optarg, sizeof(tag_recover_verify.description) ); - break; - case 'C': - bfOpts[ch] = true; - break; - // Additional default key option - case 'd': - memset(&keyOpt, 0, MIFARE_CLASSIC_KEY_BYTELENGTH); + // Load fingerprinting "database" + mfcuk_finger_load(); + /* + if (mfcuk_finger_load() == 0) + { + ERR ("Unable to load any fingerprinting database."); + exit (EXIT_FAILURE); + } + */ + // OPTION PROCESSING BLOCK + // TODO: for WIN32 figure out how to use unistd/posix-compatible Gnu.Getopt.dll (http://getopt.codeplex.com) + // For WIN32 using VERY limited (modified) Xgetopt (http://www.codeproject.com/KB/cpp/xgetopt.aspx) + while ((ch = getopt(argc, argv, "htTDCi:I:o:O:V:R:S:s:v:M:U:d:n:P:p:F:")) != -1) { // -1 or EOF + switch (ch) { + // Name for the extended dump + case 'n': + strncpy(tag_recover_verify.description, optarg, sizeof(tag_recover_verify.description)); + break; + case 'C': + bfOpts[ch] = true; + break; + // Additional default key option + case 'd': + memset(&keyOpt, 0, MIFARE_CLASSIC_KEY_BYTELENGTH); - if ( strlen(optarg) != (MIFARE_CLASSIC_KEY_BYTELENGTH*2) ) - { - // accept only 12 hex digits (fully qualified) Mifare Classic keys - WARN("invalid length key argument (%s)", optarg); - break; - } + if (strlen(optarg) != (MIFARE_CLASSIC_KEY_BYTELENGTH * 2)) { + // accept only 12 hex digits (fully qualified) Mifare Classic keys + WARN("invalid length key argument (%s)", optarg); + break; + } - for (st=0; st < MIFARE_CLASSIC_KEY_BYTELENGTH; st++) - { - if ( !is_hex(optarg[2 * st]) || !is_hex(optarg[2 * st + 1]) ) - { - // bad input hex string - WARN("invalid hex chars in key argument (%s)", optarg); - break; - } - keyOpt[st] = hex2bin(optarg[2 * st], optarg[2 * st + 1]); - } + for (st = 0; st < MIFARE_CLASSIC_KEY_BYTELENGTH; st++) { + if (!is_hex(optarg[2 * st]) || !is_hex(optarg[2 * st + 1])) { + // bad input hex string + WARN("invalid hex chars in key argument (%s)", optarg); + break; + } + keyOpt[st] = hex2bin(optarg[2 * st], optarg[2 * st + 1]); + } - // Increase number of keys - numDefKeys++; + // Increase number of keys + numDefKeys++; - // Also increase the memory to hold one more key. Hope not many keys will be specified, - // so realloc() will not impact performance and will not fragment memory - if ( !(current_default_keys = realloc(current_default_keys, numDefKeys * MIFARE_CLASSIC_KEY_BYTELENGTH)) ) - { - ERR("failed to reallocate memory for current_default_keys"); - return EXIT_FAILURE; - } + // Also increase the memory to hold one more key. Hope not many keys will be specified, + // so realloc() will not impact performance and will not fragment memory + if (!(current_default_keys = realloc(current_default_keys, numDefKeys * MIFARE_CLASSIC_KEY_BYTELENGTH))) { + ERR("failed to reallocate memory for current_default_keys"); + return EXIT_FAILURE; + } - memcpy( &(current_default_keys[numDefKeys-1]), &keyOpt, MIFARE_CLASSIC_KEY_BYTELENGTH); + memcpy(&(current_default_keys[numDefKeys - 1]), &keyOpt, MIFARE_CLASSIC_KEY_BYTELENGTH); - // Mark current option as specified (though not used in any checks) - bfOpts[ch] = true; + // Mark current option as specified (though not used in any checks) + bfOpts[ch] = true; - // Force the use of default keys - bfOpts['D'] = true; + // Force the use of default keys + bfOpts['D'] = true; - break; - // Verbose option and level - case 'v': - if ( !(i = atoi(optarg)) || (i < 1) ) - { - WARN("non-supported verbose-level value (%s)", optarg); - } - else - { - verboseLevel = i; - bfOpts[ch] = true; - } - break; - case 'M': - // Mifare Classic type option - if ( !(i = atoi(optarg)) || (!IS_MIFARE_CLASSIC_1K(i) && !IS_MIFARE_CLASSIC_4K(i)) ) - { - WARN("non-supported tag type value (%s)", optarg); - } - else - { - tag_recover_verify.type = i; - tag_recover_verify.tag_basic.amb[0].mbm.btUnknown = i; - bfOpts[ch] = true; - } - break; - case 'U': - // UID option - if ( strlen(optarg) != (MIFARE_CLASSIC_UID_BYTELENGTH*2) ) - { - // accept only 8 hex digits (fully qualified) Mifare Classic keys - WARN("invalid length UID argument (%s)", optarg); - break; - } + break; + // Verbose option and level + case 'v': + if (!(i = atoi(optarg)) || (i < 1)) { + WARN("non-supported verbose-level value (%s)", optarg); + } else { + verboseLevel = i; + bfOpts[ch] = true; + } + break; + case 'M': + // Mifare Classic type option + if (!(i = atoi(optarg)) || (!IS_MIFARE_CLASSIC_1K(i) && !IS_MIFARE_CLASSIC_4K(i))) { + WARN("non-supported tag type value (%s)", optarg); + } else { + tag_recover_verify.type = i; + tag_recover_verify.tag_basic.amb[0].mbm.btUnknown = i; + bfOpts[ch] = true; + } + break; + case 'U': + // UID option + if (strlen(optarg) != (MIFARE_CLASSIC_UID_BYTELENGTH * 2)) { + // accept only 8 hex digits (fully qualified) Mifare Classic keys + WARN("invalid length UID argument (%s)", optarg); + break; + } - for (st=0; st < MIFARE_CLASSIC_UID_BYTELENGTH; st++) - { - if ( !is_hex(optarg[2 * st]) || !is_hex(optarg[2 * st + 1]) ) - { - // bad input hex string - WARN("invalid hex chars in key argument (%s)", optarg); - break; - } - uidOpt[st] = hex2bin(optarg[2 * st], optarg[2 * st + 1]); - } + for (st = 0; st < MIFARE_CLASSIC_UID_BYTELENGTH; st++) { + if (!is_hex(optarg[2 * st]) || !is_hex(optarg[2 * st + 1])) { + // bad input hex string + WARN("invalid hex chars in key argument (%s)", optarg); + break; + } + uidOpt[st] = hex2bin(optarg[2 * st], optarg[2 * st + 1]); + } - if (st >= MIFARE_CLASSIC_UID_BYTELENGTH) - { - tag_recover_verify.uid = bswap_32_pu8(uidOpt); - memcpy( tag_recover_verify.tag_basic.amb[0].mbm.abtUID, uidOpt, MIFARE_CLASSIC_UID_BYTELENGTH ); - bfOpts[ch] = true; - } - break; - case 'S': - // Sleep for "AT FIELD OFF" - if ( !(i = atoi(optarg)) || (i < 1) || (i > 10000) ) - { - WARN("non-supported sleep-AT-field OFF value (%s)", optarg); - } - else - { - iSleepAtFieldOFF = i; - bfOpts[ch] = true; - } - break; - case 's': - // Sleep for "AFTER FIELD ON" - if ( !(i = atoi(optarg)) || (i < 1) || (i > 10000) ) - { - WARN("non-supported sleep-AFTER-field ON value (%s)", optarg); - } - else - { - iSleepAfterFieldON = i; - bfOpts[ch] = true; - } - break; - case 'D': - // Use DEFAULT KEYS for verification of sectors and key-types marked as ACTIONS_VERIFY - bfOpts[ch] = true; - break; - case 'R': - case 'V': - // Recover or Verify - action = (ch=='R')?ACTIONS_RECOVER:ACTIONS_VERIFY; + if (st >= MIFARE_CLASSIC_UID_BYTELENGTH) { + tag_recover_verify.uid = bswap_32_pu8(uidOpt); + memcpy(tag_recover_verify.tag_basic.amb[0].mbm.abtUID, uidOpt, MIFARE_CLASSIC_UID_BYTELENGTH); + bfOpts[ch] = true; + } + break; + case 'S': + // Sleep for "AT FIELD OFF" + if (!(i = atoi(optarg)) || (i < 1) || (i > 10000)) { + WARN("non-supported sleep-AT-field OFF value (%s)", optarg); + } else { + iSleepAtFieldOFF = i; + bfOpts[ch] = true; + } + break; + case 's': + // Sleep for "AFTER FIELD ON" + if (!(i = atoi(optarg)) || (i < 1) || (i > 10000)) { + WARN("non-supported sleep-AFTER-field ON value (%s)", optarg); + } else { + iSleepAfterFieldON = i; + bfOpts[ch] = true; + } + break; + case 'D': + // Use DEFAULT KEYS for verification of sectors and key-types marked as ACTIONS_VERIFY + bfOpts[ch] = true; + break; + case 'R': + case 'V': + // Recover or Verify + action = (ch == 'R') ? ACTIONS_RECOVER : ACTIONS_VERIFY; - token = NULL; - str = optarg; - iter = 0; - while ( (token = strtok(str, sep)) && (iter < 3) ) - { - switch(iter) - { - // Here is the sector argument - case 0: - // BUG: if sector is 0, atoi() returns 0 (ok); if sector is non-numeric, atoi() returns also 0 (not-ok) - cannot differentiate - if ( !(sector = atoi(token)) && (token[0] != '0') ) - { - WARN("non-numeric sector argument (%s)", token); - return EXIT_FAILURE; - } - - // We don't know apriori whether loaded dump or the card on the reader is 1K or 4K, so assume validity for 4K - if ( (sector != -1) && !is_valid_sector(MIFARE_CLASSIC_4K, sector) ) - { - WARN("invalid sector argument (%d)", sector); - return EXIT_FAILURE; - } - else - { - for (i = ( (sector==-1)?(0):(sector) ); i < ( (sector==-1)?(MIFARE_CLASSIC_4K_MAX_SECTORS):(sector+1) ); i++) - { - // TODO: proper error handling for block and ptr_trailer - block = get_trailer_block_for_sector(MIFARE_CLASSIC_4K, i); - ptr_trailer = (mifare_classic_block_trailer *) ((char *)(&tag_recover_verify.tag_basic) + (block * MIFARE_CLASSIC_BYTES_PER_BLOCK) ); - - ptr_trailer->abtAccessBits[ACTIONS_KEY_A] |= action; - ptr_trailer->abtAccessBits[ACTIONS_KEY_B] |= action; - } - } - break; - // Here is the key-type argument - // after case 0, we can assume sector is a safe and valid sector - case 1: - switch(token[0]) - { - case 'A': - case 'B': - specific_key_type = keyA + (token[0] - 'A'); - - // Invalidate all the opposite keys - for (i = ( (sector==-1)?(0):(sector) ); i < ( (sector==-1)?(MIFARE_CLASSIC_4K_MAX_SECTORS):(sector+1) ); i++) - { - // TODO: proper error handling for block and ptr_trailer - block = get_trailer_block_for_sector(MIFARE_CLASSIC_4K, i); - ptr_trailer = (mifare_classic_block_trailer *) ((char *)(&tag_recover_verify.tag_basic) + (block * MIFARE_CLASSIC_BYTES_PER_BLOCK) ); - - ptr_trailer->abtAccessBits[ACTIONS_KEY_B * (1 - (token[0]-'A'))] &= (~action); - } - break; - default: - specific_key_type = 0; - - // Validate all the key-types - for (i = ( (sector==-1)?(0):(sector) ); i < ( (sector==-1)?(MIFARE_CLASSIC_4K_MAX_SECTORS):(sector+1) ); i++) - { - // TODO: proper error handling for block and ptr_trailer - block = get_trailer_block_for_sector(MIFARE_CLASSIC_4K, i); - ptr_trailer = (mifare_classic_block_trailer *) ((char *)(&tag_recover_verify.tag_basic) + (block * MIFARE_CLASSIC_BYTES_PER_BLOCK) ); - - ptr_trailer->abtAccessBits[ACTIONS_KEY_A] |= action; - ptr_trailer->abtAccessBits[ACTIONS_KEY_B] |= action; - } - break; - } - break; - // Here is the key argument - // after case 0, we can assume sector is a safe and valid sector - case 2: - // Recovery does not need a key - if (ch == 'R') - { - break; - } - - memset(&keyOpt, 0, MIFARE_CLASSIC_KEY_BYTELENGTH); - - if ( strlen(token) != (MIFARE_CLASSIC_KEY_BYTELENGTH*2) ) - { - // accept only 12 hex digits (fully qualified) Mifare Classic keys - WARN("invalid length key argument (%s)", token); - break; - } - - for (st=0; st < MIFARE_CLASSIC_KEY_BYTELENGTH; st++) - { - if ( !is_hex(token[2 * st]) || !is_hex(token[2 * st + 1]) ) - { - // bad input hex string - WARN("invalid hex chars in key argument (%s)", token); - break; - } - keyOpt[st] = hex2bin(token[2 * st], token[2 * st + 1]); - } - - for (i = ( (sector==-1)?(0):(sector) ); i < ( (sector==-1)?(MIFARE_CLASSIC_4K_MAX_SECTORS):(sector+1) ); i++) - { - // TODO: proper error handling for block and ptr_trailer - block = get_trailer_block_for_sector(MIFARE_CLASSIC_4K, i); - ptr_trailer = (mifare_classic_block_trailer *) ((char *)(&tag_recover_verify.tag_basic) + (block * MIFARE_CLASSIC_BYTES_PER_BLOCK) ); - - if ( !specific_key_type || specific_key_type == keyA ) - { - memcpy( &(ptr_trailer->abtKeyA[0]), keyOpt, sizeof(keyOpt)); - ptr_trailer->abtAccessBits[ACTIONS_KEY_A] |= ACTIONS_KEYSET; - } - - if ( !specific_key_type || specific_key_type == keyB ) - { - memcpy( &(ptr_trailer->abtKeyB[0]), keyOpt, sizeof(keyOpt)); - ptr_trailer->abtAccessBits[ACTIONS_KEY_B] |= ACTIONS_KEYSET; - } - } - break; - // We do not support any other arguments for now for -R/-V option - default: - break; - } - str = NULL; - iter++; - } - break; - case 'i': - // Input simple dump file of type mifare_classic_tag, Options i and I are autoexclusive - if (!bfOpts['i'] && !bfOpts['I']) - { - if ( !mfcuk_load_tag_dump(optarg, &(dump_loaded_tag.tag_basic)) ) - { - WARN("Unable to load tag dump from '%s'", optarg); - } - else - { - bfOpts[ch] = true; - } - } - break; - case 'I': - // Input extended dump file of type mifare_classic_tag_ext, Options i and I are autoexclusive - if (!bfOpts['i'] && !bfOpts['I']) - { - if ( !mfcuk_load_tag_dump_ext(optarg, &(dump_loaded_tag)) ) - { - WARN("Unable to load tag dump from '%s'", optarg); - } - else - { - bfOpts[ch] = true; - } - } - break; - case 'o': - case 'O': - // Output simple/extended dump file, Options o and O are autoexclusive - if (!bfOpts['o'] && !bfOpts['O']) - { - strncpy( strOutputFilename, optarg, sizeof(strOutputFilename) ); - bfOpts[ch] = true; - } - break; - // Run just test-cases for verifying the correctnes of is_ and get_ block/sector functions - case 't': - // Requested test of Mifare Classic 1K Blocks and Sectors functionality - test_mifare_classic_blocks_sectors_functions(MIFARE_CLASSIC_1K); - bfOpts[ch] = true; - break; - case 'T': - // Requested test of Mifare Classic 4K Blocks and Sectors functionality - test_mifare_classic_blocks_sectors_functions(MIFARE_CLASSIC_4K); - bfOpts[ch] = true; - break; - case 'P': - token = NULL; - str = optarg; - iter = 0; - - // parse the arguments of the option. ugly, ugly... i know :-S - while ( (token = strtok(str, sep)) && (iter < (int)(sizeof(pm3_full_set_log)/sizeof(pm3_full_set_log[0]))) ) - { - str = NULL; - errno = 0; - pm3_full_set_log[iter] = strtoul(token, NULL, 16); - - // strtoul failed somewhere. WTF?! strtoul() is not properly setting errno... errrrrggh! - if (errno != 0) - { - WARN("Invalid hex literal %s for option -P at position %d", optarg, iter); - } - - iter++; - } - - // if not all arguments were fine, fire warning - if ( iter != sizeof(pm3_full_set_log)/sizeof(pm3_full_set_log[0]) ) - { - WARN("Invalid number of hex literal for option -P"); - } - // otherwise try to recover - else - { - /* - // TODO: implement better this function - mfcuk_get_key_from_full_state(pm3_full_set, &ui64_lsfr); - */ - pm3_ks2 = pm3_full_set_log[PM3_READER_RESP] ^ prng_successor(pm3_full_set_log[PM3_TAG_CHAL], 64); - pm3_ks3 = pm3_full_set_log[PM3_TAG_RESP] ^ prng_successor(pm3_full_set_log[PM3_TAG_CHAL], 96); - - pm3_revstate = lfsr_recovery64(pm3_ks2, pm3_ks3); - lfsr_rollback_word(pm3_revstate, 0, 0); - lfsr_rollback_word(pm3_revstate, 0, 0); - lfsr_rollback_word(pm3_revstate, pm3_full_set_log[PM3_NR_ENC], 1); - lfsr_rollback_word(pm3_revstate, pm3_full_set_log[PM3_UID] ^ pm3_full_set_log[PM3_TAG_CHAL], 0); - crypto1_get_lfsr(pm3_revstate, &pm3_lfsr); - printf("proxmark3 log key: %02x%02x%02x%02x%02x%02x\n", pm3_plfsr[5], pm3_plfsr[4], pm3_plfsr[3], pm3_plfsr[2], pm3_plfsr[1], pm3_plfsr[0]); - crypto1_destroy(pm3_revstate); - - // If all minimum required details from the log were parsed and still there are some more hex tokens, it might be a multi-sector authentication test request - if (token) - { - errno = 0; - pm3_log_multisect_auth = strtoul(token, NULL, 16); - - // strtoul failed somewhere. WTF?! strtoul() is not properly setting errno... errrrrggh! - if (errno != 0) - { - WARN("Invalid hex literal %s for option -P at position %d", optarg, iter); - } - else - { - // TODO: what if the multi-sect authentication comes not directly after the first successful plain authentication, i.e. several read/write/incr/decr command occur first then multi-sect auth?! how does this affects the crypto stream/state, what should we do? need to simulate with a nfc-multisect-auth program which has tests with interleaved multi-sect authentications - pm3_revstate_multisect_auth = lfsr_recovery64(pm3_ks2, pm3_ks3); - - for (i=0; i<4; i++) - { - uint8_t multisect_auth_byte = (pm3_log_multisect_auth >> (8 * (3-i))) & 0xFF; - pm3_log_multisect_decrypted[i] = crypto1_byte(pm3_revstate_multisect_auth,0x00,0) ^ multisect_auth_byte; - pm3_log_multisect_verified[i] = pm3_log_multisect_decrypted[i]; - } - - // TODO: This "<= MIFARE_CLASSIC_4K_MAX_BLOCKS" should be properly checked against either MIFARE_CLASSIC_1K_MAX_BLOCKS or MIFARE_CLASSIC_4K_MAX_BLOCKS (depending on card type detected) - if ((pm3_log_multisect_decrypted[0] == MC_AUTH_A) || (pm3_log_multisect_decrypted[0] == MC_AUTH_B)) - { - iso14443a_crc_append(pm3_log_multisect_verified, 2); - int multisect_auth_verified = 1; - for (i=0; i<4; i++) - { - if (pm3_log_multisect_verified[i] != pm3_log_multisect_decrypted[i]) - { - multisect_auth_verified = 0; - break; - } - } - - printf("proxmark3 log multi-sect auth detected: %02X %02X %02X %02X (parity crc %s)\n", pm3_log_multisect_decrypted[0], pm3_log_multisect_decrypted[1], pm3_log_multisect_decrypted[2], pm3_log_multisect_decrypted[3], multisect_auth_verified?"ok":"NOK"); - } - - crypto1_destroy(pm3_revstate_multisect_auth); - } - } - } - break; - case 'p': - /* - if (mfcuk_pm3_parse_log(optarg, pm3_full_set)) - { - mfcuk_get_key_from_full_state(pm3_full_set, &ui64_lsfr); - } - else - { - } - */ - printf("NOT IMPLEMENTED YET...\n"); - break; - case 'F': - if ( !mfcuk_load_tag_dump(optarg, &(finger_tag)) ) - { - WARN("Unable to load tag dump from '%s'", optarg); - } - else - { - finger_score_highest = -1.0f; - finger_index_highest = -1; - for (i = 0; i finger_score_highest) - { - finger_score_highest = finger_score; - finger_index_highest = i; - } - } - - if (finger_index_highest > -1) - { - printf("Tag '%s' matches '%s' with highest score %f\n", optarg, mfcuk_finger_db[finger_index_highest].tmpl_name, finger_score_highest); - mfcuk_finger_db[finger_index_highest].tmpl_decoder_func(&(finger_tag)); - } - else - { - printf("No template found to match tag '%s'\n", optarg); - } - } - break; - case 'h': - // Help screen - print_usage(stdout, argv[0]); - return EXIT_SUCCESS; - break; - case '?': - default: - // Help screen, on error output - ERR("Unknown option %c\n", ch); - print_usage(stderr, argv[0]); + token = NULL; + str = optarg; + iter = 0; + while ((token = strtok(str, sep)) && (iter < 3)) { + switch (iter) { + // Here is the sector argument + case 0: + // BUG: if sector is 0, atoi() returns 0 (ok); if sector is non-numeric, atoi() returns also 0 (not-ok) - cannot differentiate + if (!(sector = atoi(token)) && (token[0] != '0')) { + WARN("non-numeric sector argument (%s)", token); return EXIT_FAILURE; + } + + // We don't know apriori whether loaded dump or the card on the reader is 1K or 4K, so assume validity for 4K + if ((sector != -1) && !is_valid_sector(MIFARE_CLASSIC_4K, sector)) { + WARN("invalid sector argument (%d)", sector); + return EXIT_FAILURE; + } else { + for (i = ((sector == -1) ? (0) : (sector)); i < ((sector == -1) ? (MIFARE_CLASSIC_4K_MAX_SECTORS) : (sector + 1)); i++) { + // TODO: proper error handling for block and ptr_trailer + block = get_trailer_block_for_sector(MIFARE_CLASSIC_4K, i); + ptr_trailer = (mifare_classic_block_trailer *)((char *)(&tag_recover_verify.tag_basic) + (block * MIFARE_CLASSIC_BYTES_PER_BLOCK)); + + ptr_trailer->abtAccessBits[ACTIONS_KEY_A] |= action; + ptr_trailer->abtAccessBits[ACTIONS_KEY_B] |= action; + } + } + break; + // Here is the key-type argument + // after case 0, we can assume sector is a safe and valid sector + case 1: + switch (token[0]) { + case 'A': + case 'B': + specific_key_type = keyA + (token[0] - 'A'); + + // Invalidate all the opposite keys + for (i = ((sector == -1) ? (0) : (sector)); i < ((sector == -1) ? (MIFARE_CLASSIC_4K_MAX_SECTORS) : (sector + 1)); i++) { + // TODO: proper error handling for block and ptr_trailer + block = get_trailer_block_for_sector(MIFARE_CLASSIC_4K, i); + ptr_trailer = (mifare_classic_block_trailer *)((char *)(&tag_recover_verify.tag_basic) + (block * MIFARE_CLASSIC_BYTES_PER_BLOCK)); + + ptr_trailer->abtAccessBits[ACTIONS_KEY_B * (1 - (token[0] - 'A'))] &= (~action); + } + break; + default: + specific_key_type = 0; + + // Validate all the key-types + for (i = ((sector == -1) ? (0) : (sector)); i < ((sector == -1) ? (MIFARE_CLASSIC_4K_MAX_SECTORS) : (sector + 1)); i++) { + // TODO: proper error handling for block and ptr_trailer + block = get_trailer_block_for_sector(MIFARE_CLASSIC_4K, i); + ptr_trailer = (mifare_classic_block_trailer *)((char *)(&tag_recover_verify.tag_basic) + (block * MIFARE_CLASSIC_BYTES_PER_BLOCK)); + + ptr_trailer->abtAccessBits[ACTIONS_KEY_A] |= action; + ptr_trailer->abtAccessBits[ACTIONS_KEY_B] |= action; + } + break; + } + break; + // Here is the key argument + // after case 0, we can assume sector is a safe and valid sector + case 2: + // Recovery does not need a key + if (ch == 'R') { break; - } - } + } - // Unload fingerprinting - mfcuk_finger_unload(); + memset(&keyOpt, 0, MIFARE_CLASSIC_KEY_BYTELENGTH); - // If tests were requested, exit after tests completed - if ( bfOpts['t'] || bfOpts['T'] ) - { - return EXIT_SUCCESS; - } + if (strlen(token) != (MIFARE_CLASSIC_KEY_BYTELENGTH * 2)) { + // accept only 12 hex digits (fully qualified) Mifare Classic keys + WARN("invalid length key argument (%s)", token); + break; + } - // In case default keys requested (and maybe more specified on command line), - // print the default keys which will be used - if ( bfOpts['D'] ) - { - if (bfOpts['v'] && (verboseLevel > 0)) - { - printf("DEFAULT KEYS:\n"); + for (st = 0; st < MIFARE_CLASSIC_KEY_BYTELENGTH; st++) { + if (!is_hex(token[2 * st]) || !is_hex(token[2 * st + 1])) { + // bad input hex string + WARN("invalid hex chars in key argument (%s)", token); + break; + } + keyOpt[st] = hex2bin(token[2 * st], token[2 * st + 1]); + } - // Skip the key at index 0, since it is initially 0x0 and is reserved for the loaded dump key - for (i=1; i 0)) - { - print_mifare_classic_tag_keys("LOADED TAG DUMP", &(dump_loaded_tag.tag_basic)); - } - - // Overwrite from the loaded dump only the keys for sectors and keys which were not specified on command line - for (i=0; i < MIFARE_CLASSIC_4K_MAX_SECTORS; i++) - { - // TODO: proper error handling for block and ptr_trailer - block = get_trailer_block_for_sector(MIFARE_CLASSIC_4K, i); - ptr_trailer = (mifare_classic_block_trailer *) ((char *)(&tag_recover_verify.tag_basic) + (block * MIFARE_CLASSIC_BYTES_PER_BLOCK) ); - ptr_trailer_dump = (mifare_classic_block_trailer *) ((char *)(&dump_loaded_tag.tag_basic) + (block * MIFARE_CLASSIC_BYTES_PER_BLOCK) ); - - // If no command line keyA is set, copy from loaded dump - if ( !(ptr_trailer->abtAccessBits[ACTIONS_KEY_A] & ACTIONS_KEYSET) ) - { - memcpy( &(ptr_trailer->abtKeyA[0]), &(ptr_trailer_dump->abtKeyA[0]), MIFARE_CLASSIC_KEY_BYTELENGTH); - // TODO: think if to make this sector ACTIONS_KEYSET or introduce a new value ACTIONS_KEYLOAD - } - - // If no command line keyB is set, copy from loaded dump - if ( !(ptr_trailer->abtAccessBits[ACTIONS_KEY_B] & ACTIONS_KEYSET) ) - { - memcpy( &(ptr_trailer->abtKeyB[0]), &(ptr_trailer_dump->abtKeyB[0]), MIFARE_CLASSIC_KEY_BYTELENGTH); - // TODO: think if to make this sector ACTIONS_KEYSET or introduce a new value ACTIONS_KEYLOAD - } - } - - // If no command line UID supplied and not tag-type specified, copy the manufacturer block from the loaded dump - if ( !bfOpts['U'] && !bfOpts['M'] ) - { - ptr_trailer = (mifare_classic_block_trailer *) ((char *)(&tag_recover_verify.tag_basic) + (0 * MIFARE_CLASSIC_BYTES_PER_BLOCK) ); - ptr_trailer_dump = (mifare_classic_block_trailer *) ((char *)(&dump_loaded_tag.tag_basic) + (0 * MIFARE_CLASSIC_BYTES_PER_BLOCK) ); - - memcpy( ptr_trailer, ptr_trailer_dump, sizeof(*ptr_trailer) ); - tag_recover_verify.type = tag_recover_verify.tag_basic.amb[0].mbm.btUnknown; - tag_recover_verify.uid = bswap_32_pu8 (tag_recover_verify.tag_basic.amb[0].mbm.abtUID); - } - } - - if (!bfOpts['C']) - { - printf("No connection to reader requested (need option -C). Exiting...\n"); - return EXIT_SUCCESS; - } - - // READER INITIALIZATION BLOCK - // Try to open the NFC reader - nfc_init(&context); - pnd = nfc_open (context, NULL); - - if (pnd == NULL) - { - ERR("connecting to NFC reader"); - goto error; - } - - if ( 0 > nfc_initiator_init(pnd) ) - { - ERR("initializing NFC reader: %s", nfc_device_get_name(pnd)); - goto error; - } - - printf("\nINFO: Connected to NFC reader: %s\n\n", nfc_device_get_name(pnd)); - - // Select tag and get tag info - if ( !mfcuk_darkside_select_tag(pnd, iSleepAtFieldOFF, iSleepAfterFieldON, &ti.nti) ) - { - ERR("selecting tag on the reader %s", nfc_device_get_name(pnd)); - goto error; - } - - mfcuk_darkside_reset_advanced(pnd); - - // Tag on the reader type - tag_on_reader.type = ti.nti.nai.btSak; - tag_on_reader.tag_basic.amb[0].mbm.btUnknown = ti.nti.nai.btSak; - - // No command line tag type specified, take it from the tag on the reader - if ( !bfOpts['M'] ) - { - tag_recover_verify.type = ti.nti.nai.btSak; - tag_recover_verify.tag_basic.amb[0].mbm.btUnknown = ti.nti.nai.btSak; - } - - // Tag on the reader UID - tag_on_reader.uid = bswap_32_pu8(ti.nti.nai.abtUid); - memcpy( tag_on_reader.tag_basic.amb[0].mbm.abtUID, ti.nti.nai.abtUid, MIFARE_CLASSIC_UID_BYTELENGTH); - - // No command line tag UID specified, take it from the tag on the reader - if ( !bfOpts['U'] ) - { - tag_recover_verify.uid = bswap_32_pu8(ti.nti.nai.abtUid); - memcpy( tag_recover_verify.tag_basic.amb[0].mbm.abtUID, ti.nti.nai.abtUid, MIFARE_CLASSIC_UID_BYTELENGTH); - } - - if (bfOpts['v'] && (verboseLevel > 0)) - { - print_mifare_classic_tag_actions("\n\nINITIAL ACTIONS MATRIX", &(tag_recover_verify.tag_basic)); - } - - max_sectors = (IS_MIFARE_CLASSIC_1K(tag_recover_verify.type)?MIFARE_CLASSIC_1K_MAX_SECTORS:MIFARE_CLASSIC_4K_MAX_SECTORS); - - // VERIFY KEYS CODE-BLOCK - printf("\nVERIFY: "); - for (k = keyA; k <= keyB; k++) - { - // Print key-type for which we are looping the sectors for verification - printf("\n\tKey %c sectors:", 'B'-(keyB-k)); - - for (i=0; iabtAccessBits[action_byte] & ACTIONS_VERIFY) && !(ptr_trailer->abtAccessBits[result_byte] & ACTIONS_VERIFY); j++) - { - // TODO: think of proper mechanism. this is temporary workaround in cases when reader hangs - mfcuk_save_tag_dump("./snapshot.mfd", &(tag_recover_verify.tag_basic)); - - // The first spot in the current_default_keys, is reserved to the key from the loaded dump or from command line - // If not present (dump or command line), the key of this key-type k for current sector i will be 000000000000 - if (j == 0) - { - memcpy( &(current_default_keys[0][0]), (k==keyA)?(&(ptr_trailer->abtKeyA[0])):((&(ptr_trailer->abtKeyB[0]))), MIFARE_CLASSIC_KEY_BYTELENGTH ); + if (!specific_key_type || specific_key_type == keyA) { + memcpy(&(ptr_trailer->abtKeyA[0]), keyOpt, sizeof(keyOpt)); + ptr_trailer->abtAccessBits[ACTIONS_KEY_A] |= ACTIONS_KEYSET; } - if ( !mfcuk_key_arr_to_uint64( &(current_default_keys[j][0]), &crntVerifKey) ) - { - WARN("mfcuk_key_arr_to_uint64() failed, verification key will be %012"PRIx64"", crntVerifKey); + if (!specific_key_type || specific_key_type == keyB) { + memcpy(&(ptr_trailer->abtKeyB[0]), keyOpt, sizeof(keyOpt)); + ptr_trailer->abtAccessBits[ACTIONS_KEY_B] |= ACTIONS_KEYSET; } + } + break; + // We do not support any other arguments for now for -R/-V option + default: + break; + } + str = NULL; + iter++; + } + break; + case 'i': + // Input simple dump file of type mifare_classic_tag, Options i and I are autoexclusive + if (!bfOpts['i'] && !bfOpts['I']) { + if (!mfcuk_load_tag_dump(optarg, &(dump_loaded_tag.tag_basic))) { + WARN("Unable to load tag dump from '%s'", optarg); + } else { + bfOpts[ch] = true; + } + } + break; + case 'I': + // Input extended dump file of type mifare_classic_tag_ext, Options i and I are autoexclusive + if (!bfOpts['i'] && !bfOpts['I']) { + if (!mfcuk_load_tag_dump_ext(optarg, &(dump_loaded_tag))) { + WARN("Unable to load tag dump from '%s'", optarg); + } else { + bfOpts[ch] = true; + } + } + break; + case 'o': + case 'O': + // Output simple/extended dump file, Options o and O are autoexclusive + if (!bfOpts['o'] && !bfOpts['O']) { + strncpy(strOutputFilename, optarg, sizeof(strOutputFilename)); + bfOpts[ch] = true; + } + break; + // Run just test-cases for verifying the correctnes of is_ and get_ block/sector functions + case 't': + // Requested test of Mifare Classic 1K Blocks and Sectors functionality + test_mifare_classic_blocks_sectors_functions(MIFARE_CLASSIC_1K); + bfOpts[ch] = true; + break; + case 'T': + // Requested test of Mifare Classic 4K Blocks and Sectors functionality + test_mifare_classic_blocks_sectors_functions(MIFARE_CLASSIC_4K); + bfOpts[ch] = true; + break; + case 'P': + token = NULL; + str = optarg; + iter = 0; + + // parse the arguments of the option. ugly, ugly... i know :-S + while ((token = strtok(str, sep)) && (iter < (int)(sizeof(pm3_full_set_log) / sizeof(pm3_full_set_log[0])))) { + str = NULL; + errno = 0; + pm3_full_set_log[iter] = strtoul(token, NULL, 16); + + // strtoul failed somewhere. WTF?! strtoul() is not properly setting errno... errrrrggh! + if (errno != 0) { + WARN("Invalid hex literal %s for option -P at position %d", optarg, iter); + } + + iter++; + } + + // if not all arguments were fine, fire warning + if (iter != sizeof(pm3_full_set_log) / sizeof(pm3_full_set_log[0])) { + WARN("Invalid number of hex literal for option -P"); + } + // otherwise try to recover + else { + /* + // TODO: implement better this function + mfcuk_get_key_from_full_state(pm3_full_set, &ui64_lsfr); + */ + pm3_ks2 = pm3_full_set_log[PM3_READER_RESP] ^ prng_successor(pm3_full_set_log[PM3_TAG_CHAL], 64); + pm3_ks3 = pm3_full_set_log[PM3_TAG_RESP] ^ prng_successor(pm3_full_set_log[PM3_TAG_CHAL], 96); + + pm3_revstate = lfsr_recovery64(pm3_ks2, pm3_ks3); + lfsr_rollback_word(pm3_revstate, 0, 0); + lfsr_rollback_word(pm3_revstate, 0, 0); + lfsr_rollback_word(pm3_revstate, pm3_full_set_log[PM3_NR_ENC], 1); + lfsr_rollback_word(pm3_revstate, pm3_full_set_log[PM3_UID] ^ pm3_full_set_log[PM3_TAG_CHAL], 0); + crypto1_get_lfsr(pm3_revstate, &pm3_lfsr); + printf("proxmark3 log key: %02x%02x%02x%02x%02x%02x\n", pm3_plfsr[5], pm3_plfsr[4], pm3_plfsr[3], pm3_plfsr[2], pm3_plfsr[1], pm3_plfsr[0]); + crypto1_destroy(pm3_revstate); + + // If all minimum required details from the log were parsed and still there are some more hex tokens, it might be a multi-sector authentication test request + if (token) { + errno = 0; + pm3_log_multisect_auth = strtoul(token, NULL, 16); + + // strtoul failed somewhere. WTF?! strtoul() is not properly setting errno... errrrrggh! + if (errno != 0) { + WARN("Invalid hex literal %s for option -P at position %d", optarg, iter); + } else { + // TODO: what if the multi-sect authentication comes not directly after the first successful plain authentication, i.e. several read/write/incr/decr command occur first then multi-sect auth?! how does this affects the crypto stream/state, what should we do? need to simulate with a nfc-multisect-auth program which has tests with interleaved multi-sect authentications + pm3_revstate_multisect_auth = lfsr_recovery64(pm3_ks2, pm3_ks3); + + for (i = 0; i < 4; i++) { + uint8_t multisect_auth_byte = (pm3_log_multisect_auth >> (8 * (3 - i))) & 0xFF; + pm3_log_multisect_decrypted[i] = crypto1_byte(pm3_revstate_multisect_auth, 0x00, 0) ^ multisect_auth_byte; + pm3_log_multisect_verified[i] = pm3_log_multisect_decrypted[i]; + } + + // TODO: This "<= MIFARE_CLASSIC_4K_MAX_BLOCKS" should be properly checked against either MIFARE_CLASSIC_1K_MAX_BLOCKS or MIFARE_CLASSIC_4K_MAX_BLOCKS (depending on card type detected) + if ((pm3_log_multisect_decrypted[0] == MC_AUTH_A) || (pm3_log_multisect_decrypted[0] == MC_AUTH_B)) { + iso14443a_crc_append(pm3_log_multisect_verified, 2); + int multisect_auth_verified = 1; + for (i = 0; i < 4; i++) { + if (pm3_log_multisect_verified[i] != pm3_log_multisect_decrypted[i]) { + multisect_auth_verified = 0; + break; + } + } + + printf("proxmark3 log multi-sect auth detected: %02X %02X %02X %02X (parity crc %s)\n", pm3_log_multisect_decrypted[0], pm3_log_multisect_decrypted[1], pm3_log_multisect_decrypted[2], pm3_log_multisect_decrypted[3], multisect_auth_verified ? "ok" : "NOK"); + } + + crypto1_destroy(pm3_revstate_multisect_auth); + } + } + } + break; + case 'p': + /* + if (mfcuk_pm3_parse_log(optarg, pm3_full_set)) + { + mfcuk_get_key_from_full_state(pm3_full_set, &ui64_lsfr); + } + else + { + } + */ + printf("NOT IMPLEMENTED YET...\n"); + break; + case 'F': + if (!mfcuk_load_tag_dump(optarg, &(finger_tag))) { + WARN("Unable to load tag dump from '%s'", optarg); + } else { + finger_score_highest = -1.0f; + finger_index_highest = -1; + for (i = 0; i < mfcuk_finger_db_entries; i++) { + finger_score = -1.0f; + mfcuk_finger_db[i].tmpl_comparison_func(&(finger_tag), mfcuk_finger_db[i].tmpl_data, &finger_score); + + if (finger_score > finger_score_highest) { + finger_score_highest = finger_score; + finger_index_highest = i; + } + } + + if (finger_index_highest > -1) { + printf("Tag '%s' matches '%s' with highest score %f\n", optarg, mfcuk_finger_db[finger_index_highest].tmpl_name, finger_score_highest); + mfcuk_finger_db[finger_index_highest].tmpl_decoder_func(&(finger_tag)); + } else { + printf("No template found to match tag '%s'\n", optarg); + } + } + break; + case 'h': + // Help screen + print_usage(stdout, argv[0]); + return EXIT_SUCCESS; + break; + case '?': + default: + // Help screen, on error output + ERR("Unknown option %c\n", ch); + print_usage(stderr, argv[0]); + return EXIT_FAILURE; + break; + } + } + + // Unload fingerprinting + mfcuk_finger_unload(); + + // If tests were requested, exit after tests completed + if (bfOpts['t'] || bfOpts['T']) { + return EXIT_SUCCESS; + } + + // In case default keys requested (and maybe more specified on command line), + // print the default keys which will be used + if (bfOpts['D']) { + if (bfOpts['v'] && (verboseLevel > 0)) { + printf("DEFAULT KEYS:\n"); + + // Skip the key at index 0, since it is initially 0x0 and is reserved for the loaded dump key + for (i = 1; i < numDefKeys; i++) { + printf("\t"); + print_hex(current_default_keys[i], MIFARE_CLASSIC_KEY_BYTELENGTH); + } + } + } + + if (bfOpts['i'] || bfOpts['I']) { + if (bfOpts['v'] && (verboseLevel > 0)) { + print_mifare_classic_tag_keys("LOADED TAG DUMP", &(dump_loaded_tag.tag_basic)); + } + + // Overwrite from the loaded dump only the keys for sectors and keys which were not specified on command line + for (i = 0; i < MIFARE_CLASSIC_4K_MAX_SECTORS; i++) { + // TODO: proper error handling for block and ptr_trailer + block = get_trailer_block_for_sector(MIFARE_CLASSIC_4K, i); + ptr_trailer = (mifare_classic_block_trailer *)((char *)(&tag_recover_verify.tag_basic) + (block * MIFARE_CLASSIC_BYTES_PER_BLOCK)); + ptr_trailer_dump = (mifare_classic_block_trailer *)((char *)(&dump_loaded_tag.tag_basic) + (block * MIFARE_CLASSIC_BYTES_PER_BLOCK)); + + // If no command line keyA is set, copy from loaded dump + if (!(ptr_trailer->abtAccessBits[ACTIONS_KEY_A] & ACTIONS_KEYSET)) { + memcpy(&(ptr_trailer->abtKeyA[0]), &(ptr_trailer_dump->abtKeyA[0]), MIFARE_CLASSIC_KEY_BYTELENGTH); + // TODO: think if to make this sector ACTIONS_KEYSET or introduce a new value ACTIONS_KEYLOAD + } + + // If no command line keyB is set, copy from loaded dump + if (!(ptr_trailer->abtAccessBits[ACTIONS_KEY_B] & ACTIONS_KEYSET)) { + memcpy(&(ptr_trailer->abtKeyB[0]), &(ptr_trailer_dump->abtKeyB[0]), MIFARE_CLASSIC_KEY_BYTELENGTH); + // TODO: think if to make this sector ACTIONS_KEYSET or introduce a new value ACTIONS_KEYLOAD + } + } + + // If no command line UID supplied and not tag-type specified, copy the manufacturer block from the loaded dump + if (!bfOpts['U'] && !bfOpts['M']) { + ptr_trailer = (mifare_classic_block_trailer *)((char *)(&tag_recover_verify.tag_basic) + (0 * MIFARE_CLASSIC_BYTES_PER_BLOCK)); + ptr_trailer_dump = (mifare_classic_block_trailer *)((char *)(&dump_loaded_tag.tag_basic) + (0 * MIFARE_CLASSIC_BYTES_PER_BLOCK)); + + memcpy(ptr_trailer, ptr_trailer_dump, sizeof(*ptr_trailer)); + tag_recover_verify.type = tag_recover_verify.tag_basic.amb[0].mbm.btUnknown; + tag_recover_verify.uid = bswap_32_pu8(tag_recover_verify.tag_basic.amb[0].mbm.abtUID); + } + } + + if (!bfOpts['C']) { + printf("No connection to reader requested (need option -C). Exiting...\n"); + return EXIT_SUCCESS; + } + + // READER INITIALIZATION BLOCK + // Try to open the NFC reader + nfc_init(&context); + pnd = nfc_open(context, NULL); + + if (pnd == NULL) { + ERR("connecting to NFC reader"); + goto error; + } + + if (0 > nfc_initiator_init(pnd)) { + ERR("initializing NFC reader: %s", nfc_device_get_name(pnd)); + goto error; + } + + printf("\nINFO: Connected to NFC reader: %s\n\n", nfc_device_get_name(pnd)); + + // Select tag and get tag info + if (!mfcuk_darkside_select_tag(pnd, iSleepAtFieldOFF, iSleepAfterFieldON, &ti.nti)) { + ERR("selecting tag on the reader %s", nfc_device_get_name(pnd)); + goto error; + } + + mfcuk_darkside_reset_advanced(pnd); + + // Tag on the reader type + tag_on_reader.type = ti.nti.nai.btSak; + tag_on_reader.tag_basic.amb[0].mbm.btUnknown = ti.nti.nai.btSak; + + // No command line tag type specified, take it from the tag on the reader + if (!bfOpts['M']) { + tag_recover_verify.type = ti.nti.nai.btSak; + tag_recover_verify.tag_basic.amb[0].mbm.btUnknown = ti.nti.nai.btSak; + } + + // Tag on the reader UID + tag_on_reader.uid = bswap_32_pu8(ti.nti.nai.abtUid); + memcpy(tag_on_reader.tag_basic.amb[0].mbm.abtUID, ti.nti.nai.abtUid, MIFARE_CLASSIC_UID_BYTELENGTH); + + // No command line tag UID specified, take it from the tag on the reader + if (!bfOpts['U']) { + tag_recover_verify.uid = bswap_32_pu8(ti.nti.nai.abtUid); + memcpy(tag_recover_verify.tag_basic.amb[0].mbm.abtUID, ti.nti.nai.abtUid, MIFARE_CLASSIC_UID_BYTELENGTH); + } + + if (bfOpts['v'] && (verboseLevel > 0)) { + print_mifare_classic_tag_actions("\n\nINITIAL ACTIONS MATRIX", &(tag_recover_verify.tag_basic)); + } + + max_sectors = (IS_MIFARE_CLASSIC_1K(tag_recover_verify.type) ? MIFARE_CLASSIC_1K_MAX_SECTORS : MIFARE_CLASSIC_4K_MAX_SECTORS); + + // VERIFY KEYS CODE-BLOCK + printf("\nVERIFY: "); + for (k = keyA; k <= keyB; k++) { + // Print key-type for which we are looping the sectors for verification + printf("\n\tKey %c sectors:", 'B' - (keyB - k)); + + for (i = 0; i < max_sectors; i++) { + uint64_t crntVerifKey = 0; + uint8_t crntVerifTagType = tag_recover_verify.type; + int crntNumVerifKeys = (bfOpts['D']) ? (numDefKeys) : (1); + mifare_param mp; + + // Depending on which of keyA or keyB the j value is, the checks and actions below will address exactly that keyA or keyB of current sector + uint8_t action_byte = ACTIONS_KEY_A + 2 * (1 - (keyB - k)); + uint8_t result_byte = RESULTS_KEY_A + 2 * (1 - (keyB - k)); + + printf(" %x", i); + fflush(stdout); + + // TODO: proper error handling + block = get_trailer_block_for_sector(crntVerifTagType, i); + ptr_trailer = (mifare_classic_block_trailer *)((char *)(&tag_recover_verify.tag_basic) + (block * MIFARE_CLASSIC_BYTES_PER_BLOCK)); + + // If DEFAULT KEYS option was specified, crntNumVerifKeys is already taking care of them + // Also, perform verification ontly if the sector has been marked for verification of key and not valid verification yet occured in the loop + for (j = 0; (j < crntNumVerifKeys) && (ptr_trailer->abtAccessBits[action_byte] & ACTIONS_VERIFY) && !(ptr_trailer->abtAccessBits[result_byte] & ACTIONS_VERIFY); j++) { + // TODO: think of proper mechanism. this is temporary workaround in cases when reader hangs + mfcuk_save_tag_dump("./snapshot.mfd", &(tag_recover_verify.tag_basic)); + + // The first spot in the current_default_keys, is reserved to the key from the loaded dump or from command line + // If not present (dump or command line), the key of this key-type k for current sector i will be 000000000000 + if (j == 0) { + memcpy(&(current_default_keys[0][0]), (k == keyA) ? (&(ptr_trailer->abtKeyA[0])) : ((&(ptr_trailer->abtKeyB[0]))), MIFARE_CLASSIC_KEY_BYTELENGTH); + } + + if (!mfcuk_key_arr_to_uint64(&(current_default_keys[j][0]), &crntVerifKey)) { + WARN("mfcuk_key_arr_to_uint64() failed, verification key will be %012"PRIx64"", crntVerifKey); + } /* // TODO: make this kind of key verification as part of option -a - advanced verification of keys with crapto1 rollback for double verification @@ -1731,189 +1572,163 @@ int main(int argc, char* argv[]) // Reset advanced settings mfcuk_darkside_reset_advanced(pnd); */ - memcpy(mp.mpa.abtAuthUid, tag_recover_verify.tag_basic.amb[0].mbm.abtUID, MIFARE_CLASSIC_UID_BYTELENGTH); - memcpy(mp.mpa.abtKey, &(current_default_keys[j][0]), MIFARE_CLASSIC_KEY_BYTELENGTH); + memcpy(mp.mpa.abtAuthUid, tag_recover_verify.tag_basic.amb[0].mbm.abtUID, MIFARE_CLASSIC_UID_BYTELENGTH); + memcpy(mp.mpa.abtKey, &(current_default_keys[j][0]), MIFARE_CLASSIC_KEY_BYTELENGTH); - if ( 0 >= nfc_initiator_select_passive_target(pnd, nmMifare, NULL, 0, &ti) ) - { - ERR("tag was removed or cannot be selected"); - } - - if ( 0 > nfc_initiator_mifare_cmd(pnd, k, block, &mp) ) - { - ERR("AUTH sector %d, block %d, key %012"PRIx64", key-type 0x%02x, error code 0x%02x", i, block, crntVerifKey, k, uiErrCode); - } - else - { - // Mark current key-type as verified - ptr_trailer->abtAccessBits[result_byte] |= ACTIONS_VERIFY; - - // Copy default key on top of dump only in case default keys option was specified in command line and the default key matched - memcpy( (k==keyA)?(ptr_trailer->abtKeyA):(ptr_trailer->abtKeyB), current_default_keys[j], MIFARE_CLASSIC_KEY_BYTELENGTH); - } - } // for (j = 0; (j < crntNumVerifKeys); j++) - } // for (i=0; i 0) ) - { - print_mifare_classic_tag_actions("\n\nACTION RESULTS MATRIX AFTER VERIFY", &(tag_recover_verify.tag_basic)); - } - - // RECOVER KEYS CODE-BLOCK - printf("\nRECOVER: "); - for (i=0; iabtAccessBits[action_byte] & ACTIONS_RECOVER) && - !(ptr_trailer->abtAccessBits[result_byte] & ACTIONS_VERIFY) && - !(ptr_trailer->abtAccessBits[result_byte] & ACTIONS_RECOVER) - ) - { - // TODO: think of proper mechanism. this is temporary workaround in cases when reader hangs - mfcuk_save_tag_dump("./snapshot.mfd", &(tag_recover_verify.tag_basic)); - - // TEST - // Before starting a new recovery session, disconnect and reconnect to reader and then tag - nfc_close(pnd); - - // Try to open the NFC reader - pnd = nfc_open (context, NULL); - - if (pnd == NULL) - { - ERR("connecting to NFC reader"); - return EXIT_FAILURE; - } - - if ( 0 > nfc_initiator_init(pnd) ) - { - ERR("initializing NFC reader: %s", nfc_device_get_name(pnd)); - goto error; - } - // TEST - - // Every new recovery session needs this "sort-of" initializing the entries - memset((void *)arrSpoofEntries, 0, sizeof(arrSpoofEntries)); - numSpoofEntries = 0; - numAuthAttempts = 0; - - // Recovery loop for current key-type of current sector - do - { - mfcuk_darkside_select_tag(pnd, iSleepAtFieldOFF, iSleepAfterFieldON, &ti.nti); - - // Print usefull/useless info (sort-of "Let me entertain you!") - if ( bfOpts['v'] && (verboseLevel > 2) ) - { - printf("\n-----------------------------------------------------\n"); - printf("Let me entertain you!\n"); - printf(" uid: %08x\n", tag_recover_verify.uid); - printf(" type: %02x\n", tag_recover_verify.type); - printf(" key: %012"PRIx64"\n", crntRecovKey); - printf(" block: %02x\n", block); - printf("diff Nt: %d\n", numSpoofEntries); - printf(" auths: %d\n", numAuthAttempts); - printf("-----------------------------------------------------\n"); - } - - uiErrCode = mfcuk_key_recovery_block(pnd, tag_recover_verify.uid, crntRecovKey, j, tag_recover_verify.type, block, &ui64KeyRecovered); - - if ( uiErrCode != MFCUK_OK_KEY_RECOVERED && uiErrCode != MFCUK_SUCCESS && uiErrCode != MFCUK_FAIL_AUTH) - { - ERR("mfcuk_key_recovery_block() (error code=0x%02x)", uiErrCode); - } - - mfcuk_darkside_reset_advanced(pnd); - - numAuthAttempts++; - } while (uiErrCode != MFCUK_OK_KEY_RECOVERED); - - // Store the recovered key A and mark key A for this sector as recovered in results - ptr_trailer->abtAccessBits[result_byte] |= ACTIONS_RECOVER; - - if ( !mfcuk_key_uint64_to_arr( &ui64KeyRecovered, (j == keyA)?(&(ptr_trailer->abtKeyA[0])):(&(ptr_trailer->abtKeyB[0])) ) ) - { - WARN("mfcuk_key_uint64_to_arr() failed, recovered key should have been %012"PRIx64"", ui64KeyRecovered); - } - } - } // for (j=keyA; j<=keyB; j++) - } - printf("\n"); - - if ( bfOpts['v'] && (verboseLevel > 0) ) - { - print_mifare_classic_tag_actions("\n\nACTION RESULTS MATRIX AFTER RECOVER", &(tag_recover_verify.tag_basic)); - } - - // DUMP DATA CODE-BLOCK - // TODO: write this code-block - /* - for (i=0; i= nfc_initiator_select_passive_target(pnd, nmMifare, NULL, 0, &ti)) { + ERR("tag was removed or cannot be selected"); } - else - { - if ( bfOpts['v'] && (verboseLevel > 1) ) - { - printf("INFO: saved tag dump file to '%s'\n", strOutputFilename); - } - } - } - else if ( bfOpts['O'] ) - { - if ( !mfcuk_save_tag_dump_ext(strOutputFilename, &(tag_recover_verify)) ) - { - ERR("could not save extended tag dump to '%s'", strOutputFilename); - } - else - { - if ( bfOpts['v'] && (verboseLevel > 1) ) - { - printf("INFO: saved extended tag dump file to '%s'\n", strOutputFilename); - } - } - } - return EXIT_SUCCESS; + if (0 > nfc_initiator_mifare_cmd(pnd, k, block, &mp)) { + ERR("AUTH sector %d, block %d, key %012"PRIx64", key-type 0x%02x, error code 0x%02x", i, block, crntVerifKey, k, uiErrCode); + } else { + // Mark current key-type as verified + ptr_trailer->abtAccessBits[result_byte] |= ACTIONS_VERIFY; + + // Copy default key on top of dump only in case default keys option was specified in command line and the default key matched + memcpy((k == keyA) ? (ptr_trailer->abtKeyA) : (ptr_trailer->abtKeyB), current_default_keys[j], MIFARE_CLASSIC_KEY_BYTELENGTH); + } + } // for (j = 0; (j < crntNumVerifKeys); j++) + } // for (i=0; i 0)) { + print_mifare_classic_tag_actions("\n\nACTION RESULTS MATRIX AFTER VERIFY", &(tag_recover_verify.tag_basic)); + } + + // RECOVER KEYS CODE-BLOCK + printf("\nRECOVER: "); + for (i = 0; i < max_sectors; i++) { + uint64_t crntRecovKey = 0; + ui64KeyRecovered = 0; + + block = get_trailer_block_for_sector(MIFARE_CLASSIC_4K, i); + ptr_trailer = (mifare_classic_block_trailer *)((char *)(&tag_recover_verify.tag_basic) + (block * MIFARE_CLASSIC_BYTES_PER_BLOCK)); + + printf(" %x", i); + fflush(stdout); + + for (j = keyA; j <= keyB; j++) { + // Depending on which of keyA or keyB the j value is, the checks and actions below will address exactly that keyA or keyB of current sector + uint8_t action_byte = ACTIONS_KEY_A + 2 * (1 - (keyB - j)); + uint8_t result_byte = RESULTS_KEY_A + 2 * (1 - (keyB - j)); + + // We have a sector and a key-type of that sector marked for recovery and still the key was not either verified nor recovered + if ((ptr_trailer->abtAccessBits[action_byte] & ACTIONS_RECOVER) && + !(ptr_trailer->abtAccessBits[result_byte] & ACTIONS_VERIFY) && + !(ptr_trailer->abtAccessBits[result_byte] & ACTIONS_RECOVER) + ) { + // TODO: think of proper mechanism. this is temporary workaround in cases when reader hangs + mfcuk_save_tag_dump("./snapshot.mfd", &(tag_recover_verify.tag_basic)); + + // TEST + // Before starting a new recovery session, disconnect and reconnect to reader and then tag + nfc_close(pnd); + + // Try to open the NFC reader + pnd = nfc_open(context, NULL); + + if (pnd == NULL) { + ERR("connecting to NFC reader"); + return EXIT_FAILURE; + } + + if (0 > nfc_initiator_init(pnd)) { + ERR("initializing NFC reader: %s", nfc_device_get_name(pnd)); + goto error; + } + // TEST + + // Every new recovery session needs this "sort-of" initializing the entries + memset((void *)arrSpoofEntries, 0, sizeof(arrSpoofEntries)); + numSpoofEntries = 0; + numAuthAttempts = 0; + + // Recovery loop for current key-type of current sector + do { + mfcuk_darkside_select_tag(pnd, iSleepAtFieldOFF, iSleepAfterFieldON, &ti.nti); + + // Print usefull/useless info (sort-of "Let me entertain you!") + if (bfOpts['v'] && (verboseLevel > 2)) { + printf("\n-----------------------------------------------------\n"); + printf("Let me entertain you!\n"); + printf(" uid: %08x\n", tag_recover_verify.uid); + printf(" type: %02x\n", tag_recover_verify.type); + printf(" key: %012"PRIx64"\n", crntRecovKey); + printf(" block: %02x\n", block); + printf("diff Nt: %d\n", numSpoofEntries); + printf(" auths: %d\n", numAuthAttempts); + printf("-----------------------------------------------------\n"); + } + + uiErrCode = mfcuk_key_recovery_block(pnd, tag_recover_verify.uid, crntRecovKey, j, tag_recover_verify.type, block, &ui64KeyRecovered); + + if (uiErrCode != MFCUK_OK_KEY_RECOVERED && uiErrCode != MFCUK_SUCCESS && uiErrCode != MFCUK_FAIL_AUTH) { + ERR("mfcuk_key_recovery_block() (error code=0x%02x)", uiErrCode); + } + + mfcuk_darkside_reset_advanced(pnd); + + numAuthAttempts++; + } while (uiErrCode != MFCUK_OK_KEY_RECOVERED); + + // Store the recovered key A and mark key A for this sector as recovered in results + ptr_trailer->abtAccessBits[result_byte] |= ACTIONS_RECOVER; + + if (!mfcuk_key_uint64_to_arr(&ui64KeyRecovered, (j == keyA) ? (&(ptr_trailer->abtKeyA[0])) : (&(ptr_trailer->abtKeyB[0])))) { + WARN("mfcuk_key_uint64_to_arr() failed, recovered key should have been %012"PRIx64"", ui64KeyRecovered); + } + } + } // for (j=keyA; j<=keyB; j++) + } + printf("\n"); + + if (bfOpts['v'] && (verboseLevel > 0)) { + print_mifare_classic_tag_actions("\n\nACTION RESULTS MATRIX AFTER RECOVER", &(tag_recover_verify.tag_basic)); + } + + // DUMP DATA CODE-BLOCK + // TODO: write this code-block + /* + for (i=0; i 1)) { + printf("INFO: saved tag dump file to '%s'\n", strOutputFilename); + } + } + } else if (bfOpts['O']) { + if (!mfcuk_save_tag_dump_ext(strOutputFilename, &(tag_recover_verify))) { + ERR("could not save extended tag dump to '%s'", strOutputFilename); + } else { + if (bfOpts['v'] && (verboseLevel > 1)) { + printf("INFO: saved extended tag dump file to '%s'\n", strOutputFilename); + } + } + } + + return EXIT_SUCCESS; error: - nfc_close(pnd); - nfc_exit (context); - return EXIT_FAILURE; + nfc_close(pnd); + nfc_exit(context); + return EXIT_FAILURE; } diff --git a/src/mfcuk.h b/src/mfcuk.h index fdb594a..1224ba2 100644 --- a/src/mfcuk.h +++ b/src/mfcuk.h @@ -1,7 +1,7 @@ /* Package: MiFare Classic Universal toolKit (MFCUK) - + Filename: mfcuk_keyrecovery_darkside.h @@ -16,7 +16,7 @@ GPL2 (see below), Copyright (C) 2009, Andrei Costin * @file mfcuk_keyrecovery_darkside.h - * @brief + * @brief */ /* @@ -36,14 +36,14 @@ it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 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. - + You should have received a copy of the GNU General Public License - along with this program. If not, see . + along with this program. If not, see . */ #ifndef _MFCUK_KEYRECOVERY_DARKSIDE_H_ @@ -92,29 +92,28 @@ #define MFCUK_DARKSIDE_START_NR 0xDEADBEEF #define MFCUK_DARKSIDE_START_AR 0xFACECAFE -typedef struct tag_nonce_entry -{ - uint32_t tagNonce; // Tag nonce we target for fixation - uint8_t spoofFlag; // No spoofing until we have a successful auth with this tagNonce. Once we have, we want to spoof to get the encrypted 0x5 value - uint32_t num_of_appearances; // For statistics, how many times this tag nonce appeared for the given SLEEP_ values +typedef struct tag_nonce_entry { + uint32_t tagNonce; // Tag nonce we target for fixation + uint8_t spoofFlag; // No spoofing until we have a successful auth with this tagNonce. Once we have, we want to spoof to get the encrypted 0x5 value + uint32_t num_of_appearances; // For statistics, how many times this tag nonce appeared for the given SLEEP_ values - // STAGE1 data for "dark side" and lsfr_common_prefix() - uint32_t spoofNrPfx; // PARAM: used as pfx, calculated from (spoofNrEnc & 0xFFFFFF1F). BUG: weird way to denote "first 29 prefix bits" in "dark side" paper. Perhaps I see the world different - uint32_t spoofNrEnc; // {Nr} value which we will be using to make the tag respond with 4 bits - uint32_t spoofArEnc; // PARAM: used as rr - uint8_t spoofParBitsEnc; // parity bits we are trying to guess for the first time - uint8_t spoofNackEnc; // store here the encrypted NACK returned first time we match the parity bits - uint8_t spoofKs; // store here the keystream ks used for encryptying spoofNackEnc, specifically spoofKs = spoofNackEnc ^ 0x5 + // STAGE1 data for "dark side" and lsfr_common_prefix() + uint32_t spoofNrPfx; // PARAM: used as pfx, calculated from (spoofNrEnc & 0xFFFFFF1F). BUG: weird way to denote "first 29 prefix bits" in "dark side" paper. Perhaps I see the world different + uint32_t spoofNrEnc; // {Nr} value which we will be using to make the tag respond with 4 bits + uint32_t spoofArEnc; // PARAM: used as rr + uint8_t spoofParBitsEnc; // parity bits we are trying to guess for the first time + uint8_t spoofNackEnc; // store here the encrypted NACK returned first time we match the parity bits + uint8_t spoofKs; // store here the keystream ks used for encryptying spoofNackEnc, specifically spoofKs = spoofNackEnc ^ 0x5 - // STAGE2 data for "dark side" and lsfr_common_prefix() - int current_out_of_8; // starting from -1 until we find parity for chosen spoofNrEnc,spoofArEnc - uint8_t parBitsCrntCombination[MFCUK_DARKSIDE_MAX_LEVELS]; // Loops over 32 combinations of the last 5 parity bits which generated the 4 bit NACK in STAGE1 - uint32_t nrEnc[MFCUK_DARKSIDE_MAX_LEVELS]; // the 29 bits constant prefix, varying only 3 bits, thus 8 possible values - uint32_t arEnc[MFCUK_DARKSIDE_MAX_LEVELS]; // the same reader response as spoofArEnc; redundant but... :) - uint8_t ks[MFCUK_DARKSIDE_MAX_LEVELS]; // PARAM: used as ks, obtained as (ks[i] = nackEnc[i] ^ 0x5) - uint8_t nackEnc[MFCUK_DARKSIDE_MAX_LEVELS]; // store here the encrypted 4 bits values which tag responded - uint8_t parBits[MFCUK_DARKSIDE_MAX_LEVELS]; // store here the values based on spoofParBitsEnc, varying only last 5 bits - uint8_t parBitsArr[MFCUK_DARKSIDE_MAX_LEVELS][8]; // PARAM: used as par, contains value of parBits byte-bit values just splitted out one bit per byte thus second pair of braces [8] + // STAGE2 data for "dark side" and lsfr_common_prefix() + int current_out_of_8; // starting from -1 until we find parity for chosen spoofNrEnc,spoofArEnc + uint8_t parBitsCrntCombination[MFCUK_DARKSIDE_MAX_LEVELS]; // Loops over 32 combinations of the last 5 parity bits which generated the 4 bit NACK in STAGE1 + uint32_t nrEnc[MFCUK_DARKSIDE_MAX_LEVELS]; // the 29 bits constant prefix, varying only 3 bits, thus 8 possible values + uint32_t arEnc[MFCUK_DARKSIDE_MAX_LEVELS]; // the same reader response as spoofArEnc; redundant but... :) + uint8_t ks[MFCUK_DARKSIDE_MAX_LEVELS]; // PARAM: used as ks, obtained as (ks[i] = nackEnc[i] ^ 0x5) + uint8_t nackEnc[MFCUK_DARKSIDE_MAX_LEVELS]; // store here the encrypted 4 bits values which tag responded + uint8_t parBits[MFCUK_DARKSIDE_MAX_LEVELS]; // store here the values based on spoofParBitsEnc, varying only last 5 bits + uint8_t parBitsArr[MFCUK_DARKSIDE_MAX_LEVELS][8]; // PARAM: used as par, contains value of parBits byte-bit values just splitted out one bit per byte thus second pair of braces [8] } tag_nonce_entry_t; #endif // _MFCUK_KEYRECOVERY_DARKSIDE_H_ diff --git a/src/mfcuk_finger.c b/src/mfcuk_finger.c index f5f4c9c..c54a29a 100644 --- a/src/mfcuk_finger.c +++ b/src/mfcuk_finger.c @@ -5,23 +5,23 @@ it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 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. - + You should have received a copy of the GNU General Public License - along with this program. If not, see . + along with this program. If not, see . */ /* Package: MiFare Classic Universal toolKit (MFCUK) - + Package version: 0.1 - + Filename: mfcuk_finger.c @@ -38,174 +38,150 @@ #include "mfcuk_finger.h" -mfcuk_finger_tmpl_entry mfcuk_finger_db[] = -{ - { "./data/tmpls_fingerprints/mfcuk_tmpl_skgt.mfd", "Sofia SKGT", mfcuk_finger_default_comparator, mfcuk_finger_skgt_decoder, NULL }, - { "./data/tmpls_fingerprints/mfcuk_tmpl_ratb.mfd", "Bucharest RATB", mfcuk_finger_default_comparator, mfcuk_finger_default_decoder, NULL }, - { "./data/tmpls_fingerprints/mfcuk_tmpl_oyster.mfd", "London OYSTER", mfcuk_finger_default_comparator, mfcuk_finger_default_decoder, NULL }, +mfcuk_finger_tmpl_entry mfcuk_finger_db[] = { + { "./data/tmpls_fingerprints/mfcuk_tmpl_skgt.mfd", "Sofia SKGT", mfcuk_finger_default_comparator, mfcuk_finger_skgt_decoder, NULL }, + { "./data/tmpls_fingerprints/mfcuk_tmpl_ratb.mfd", "Bucharest RATB", mfcuk_finger_default_comparator, mfcuk_finger_default_decoder, NULL }, + { "./data/tmpls_fingerprints/mfcuk_tmpl_oyster.mfd", "London OYSTER", mfcuk_finger_default_comparator, mfcuk_finger_default_decoder, NULL }, }; -int mfcuk_finger_db_entries = sizeof(mfcuk_finger_db)/sizeof(mfcuk_finger_db[0]); +int mfcuk_finger_db_entries = sizeof(mfcuk_finger_db) / sizeof(mfcuk_finger_db[0]); int mfcuk_finger_default_decoder(mifare_classic_tag *dump) { - if (!dump) - { - fprintf(stderr, "ERROR: cannot decode a NULL pointer :)\n"); - return 0; - } - - printf("UID:\t%02x%02x%02x%02x\n", dump->amb[0].mbm.abtUID[0], dump->amb[0].mbm.abtUID[1], dump->amb[0].mbm.abtUID[2], dump->amb[0].mbm.abtUID[3]); - printf("TYPE:\t%02x\n", dump->amb[0].mbm.btUnknown); - - return 1; + if (!dump) { + fprintf(stderr, "ERROR: cannot decode a NULL pointer :)\n"); + return 0; + } + + printf("UID:\t%02x%02x%02x%02x\n", dump->amb[0].mbm.abtUID[0], dump->amb[0].mbm.abtUID[1], dump->amb[0].mbm.abtUID[2], dump->amb[0].mbm.abtUID[3]); + printf("TYPE:\t%02x\n", dump->amb[0].mbm.btUnknown); + + return 1; } // Yes, I know C++ class inheritance would perfectly fit the decoders/comparators... Though C is more to my heart. Anyone to rewrite in C++? int mfcuk_finger_skgt_decoder(mifare_classic_tag *dump) { - if (!dump) - { - fprintf(stderr, "ERROR: cannot decode a NULL pointer :)\n"); - return 0; - } - - printf("Bulgaria/Sofia/SKGT public transport card information decoder (info credits to Andy)\n"); - mfcuk_finger_default_decoder(dump); - - printf("LAST TRAVEL DATA\n"); - - // TODO: get proper information - - return 1; + if (!dump) { + fprintf(stderr, "ERROR: cannot decode a NULL pointer :)\n"); + return 0; + } + + printf("Bulgaria/Sofia/SKGT public transport card information decoder (info credits to Andy)\n"); + mfcuk_finger_default_decoder(dump); + + printf("LAST TRAVEL DATA\n"); + + // TODO: get proper information + + return 1; } int mfcuk_finger_default_comparator(mifare_classic_tag *dump, mfcuk_finger_template *tmpl, float *score) { - int max_bytes = 0; - int i; - int num_bytes_tomatch = 0; - int num_bytes_matched = 0; - - if ( (!dump) || (!tmpl) || (!score) ) - { - return 0; + int max_bytes = 0; + int i; + int num_bytes_tomatch = 0; + int num_bytes_matched = 0; + + if ((!dump) || (!tmpl) || (!score)) { + return 0; + } + + if (IS_MIFARE_CLASSIC_1K_TAG(dump)) { + max_bytes = MIFARE_CLASSIC_BYTES_PER_BLOCK * MIFARE_CLASSIC_1K_MAX_BLOCKS; + } else if (IS_MIFARE_CLASSIC_4K_TAG(dump)) { + max_bytes = MIFARE_CLASSIC_BYTES_PER_BLOCK * MIFARE_CLASSIC_4K_MAX_BLOCKS; + } else { + return 0; + } + + for (i = 0; i < max_bytes; i++) { + if (((char *)(&tmpl->mask))[i] == 0x0) { + continue; } - - if (IS_MIFARE_CLASSIC_1K_TAG(dump)) - { - max_bytes = MIFARE_CLASSIC_BYTES_PER_BLOCK * MIFARE_CLASSIC_1K_MAX_BLOCKS; + + num_bytes_tomatch++; + + if (((char *)(&tmpl->values))[i] == ((char *)dump)[i]) { + num_bytes_matched++; } - else if (IS_MIFARE_CLASSIC_4K_TAG(dump)) - { - max_bytes = MIFARE_CLASSIC_BYTES_PER_BLOCK * MIFARE_CLASSIC_4K_MAX_BLOCKS; - } - else - { - return 0; - } - - for (i=0; imask))[i] == 0x0 ) - { - continue; - } - - num_bytes_tomatch++; - - if ( ((char *)(&tmpl->values))[i] == ((char *)dump)[i] ) - { - num_bytes_matched++; - } - } - - if (num_bytes_tomatch == 0) - { - return 0; - } - else - { - *score = (float)(num_bytes_matched)/num_bytes_tomatch; - } - - return 1; + } + + if (num_bytes_tomatch == 0) { + return 0; + } else { + *score = (float)(num_bytes_matched) / num_bytes_tomatch; + } + + return 1; } int mfcuk_finger_load(void) { - int i; - mifare_classic_tag mask; - mifare_classic_tag values; - FILE *fp = NULL; - size_t result = 0; - mfcuk_finger_template *tmpl_new = NULL; + int i; + mifare_classic_tag mask; + mifare_classic_tag values; + FILE *fp = NULL; + size_t result = 0; + mfcuk_finger_template *tmpl_new = NULL; - int template_loaded_count = 0; - for (i = 0; imask), &(mask), sizeof(mask)); - memcpy( &(tmpl_new->values), &(values), sizeof(values)); - - mfcuk_finger_db[i].tmpl_data = tmpl_new; - template_loaded_count++; - } - - if (fp) - { - fclose(fp); - fp = NULL; - } + int template_loaded_count = 0; + for (i = 0; i < mfcuk_finger_db_entries; i++) { + fp = fopen(mfcuk_finger_db[i].tmpl_filename, "rb"); + + if (!fp) { + fprintf(stderr, "WARN: cannot open template file '%s'\n", mfcuk_finger_db[i].tmpl_filename); + continue; } - - return template_loaded_count; + + // If not read exactly 1 record, something is wrong + if ((result = fread((void *)(&mask), sizeof(mask), 1, fp)) != 1) { + fprintf(stderr, "WARN: cannot read MASK from template file '%s'\n", mfcuk_finger_db[i].tmpl_filename); + fclose(fp); + continue; + } + + // If not read exactly 1 record, something is wrong + if ((result = fread((void *)(&values), sizeof(values), 1, fp)) != 1) { + fprintf(stderr, "WARN: cannot read VALUES template file '%s'\n", mfcuk_finger_db[i].tmpl_filename); + fclose(fp); + continue; + } + + if (mfcuk_finger_db[i].tmpl_data == NULL) { + if ((tmpl_new = (mfcuk_finger_template *) malloc(sizeof(mfcuk_finger_template))) == NULL) { + fprintf(stderr, "WARN: cannot allocate memory to template record %d\n", i); + fclose(fp); + continue; + } + + memcpy(&(tmpl_new->mask), &(mask), sizeof(mask)); + memcpy(&(tmpl_new->values), &(values), sizeof(values)); + + mfcuk_finger_db[i].tmpl_data = tmpl_new; + template_loaded_count++; + } + + if (fp) { + fclose(fp); + fp = NULL; + } + } + + return template_loaded_count; } int mfcuk_finger_unload(void) { - int i; - - for (i = 0; i. + along with this program. If not, see . */ /* Package: MiFare Classic Universal toolKit (MFCUK) - + Package version: 0.1 - + Filename: mfcuk_finger.h @@ -45,24 +45,22 @@ #include "mfcuk_mifare.h" // Wrapping an ugly template into an externally pleasant name. To implement proper template later. -typedef struct _mfcuk_finger_template_ -{ - mifare_classic_tag mask; - mifare_classic_tag values; +typedef struct _mfcuk_finger_template_ { + mifare_classic_tag mask; + mifare_classic_tag values; } mfcuk_finger_template; // Function type definition, to be used for custom decoders/comparators -typedef int (*mfcuk_finger_comparator) (mifare_classic_tag *dump, mfcuk_finger_template *tmpl, float *score); -typedef int (*mfcuk_finger_decoder) (mifare_classic_tag *dump); +typedef int (*mfcuk_finger_comparator)(mifare_classic_tag *dump, mfcuk_finger_template *tmpl, float *score); +typedef int (*mfcuk_finger_decoder)(mifare_classic_tag *dump); // Naive implementation of a self-contained fingerprint database entry -typedef struct _mfcuk_finger_tmpl_entry_ -{ - const char *tmpl_filename; - const char *tmpl_name; - mfcuk_finger_comparator tmpl_comparison_func; - mfcuk_finger_decoder tmpl_decoder_func; - mfcuk_finger_template *tmpl_data; +typedef struct _mfcuk_finger_tmpl_entry_ { + const char *tmpl_filename; + const char *tmpl_name; + mfcuk_finger_comparator tmpl_comparison_func; + mfcuk_finger_decoder tmpl_decoder_func; + mfcuk_finger_template *tmpl_data; } mfcuk_finger_tmpl_entry; int mfcuk_finger_default_comparator(mifare_classic_tag *dump, mfcuk_finger_template *tmpl, float *score); diff --git a/src/mfcuk_mifare.c b/src/mfcuk_mifare.c index 0ab654e..8c84dcb 100644 --- a/src/mfcuk_mifare.c +++ b/src/mfcuk_mifare.c @@ -1,10 +1,10 @@ /* Package: MiFare Classic Universal toolKit (MFCUK) - + Package version: 0.1 - + Filename: mfcuk_mifare.c @@ -20,7 +20,7 @@ GPL2 (see below), Copyright (C) 2009, Andrei Costin * @file mfcuk_mifare.c - * @brief + * @brief */ /* @@ -40,485 +40,424 @@ it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 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. - + You should have received a copy of the GNU General Public License - along with this program. If not, see . + along with this program. If not, see . */ #include "mfcuk_mifare.h" // Default keys used as a *BIG* mistake in many applications - especially System Integrators should pay attention! -uint8_t mfcuk_default_keys[][MIFARE_CLASSIC_KEY_BYTELENGTH] = -{ - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Place-holder for current key to verify - {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, - {0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5}, - {0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x4d, 0x3a, 0x99, 0xc3, 0x51, 0xdd}, - {0x1a, 0x98, 0x2c, 0x7e, 0x45, 0x9a}, - {0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7}, - {0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}, +uint8_t mfcuk_default_keys[][MIFARE_CLASSIC_KEY_BYTELENGTH] = { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Place-holder for current key to verify + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5}, + {0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x4d, 0x3a, 0x99, 0xc3, 0x51, 0xdd}, + {0x1a, 0x98, 0x2c, 0x7e, 0x45, 0x9a}, + {0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7}, + {0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}, }; -int mfcuk_default_keys_num = sizeof(mfcuk_default_keys)/sizeof(mfcuk_default_keys[0]); +int mfcuk_default_keys_num = sizeof(mfcuk_default_keys) / sizeof(mfcuk_default_keys[0]); bool is_valid_block(uint8_t bTagType, uint32_t uiBlock) { - if ( IS_MIFARE_CLASSIC_1K(bTagType) && (uiBlock < MIFARE_CLASSIC_1K_MAX_BLOCKS) ) - { - return true; - } + if (IS_MIFARE_CLASSIC_1K(bTagType) && (uiBlock < MIFARE_CLASSIC_1K_MAX_BLOCKS)) { + return true; + } - if ( IS_MIFARE_CLASSIC_4K(bTagType) && (uiBlock < MIFARE_CLASSIC_4K_MAX_BLOCKS) ) - { - return true; - } + if (IS_MIFARE_CLASSIC_4K(bTagType) && (uiBlock < MIFARE_CLASSIC_4K_MAX_BLOCKS)) { + return true; + } - return false; + return false; } bool is_valid_sector(uint8_t bTagType, uint32_t uiSector) { - if ( IS_MIFARE_CLASSIC_1K(bTagType) && (uiSector < MIFARE_CLASSIC_1K_MAX_SECTORS) ) - { - return true; - } + if (IS_MIFARE_CLASSIC_1K(bTagType) && (uiSector < MIFARE_CLASSIC_1K_MAX_SECTORS)) { + return true; + } - if ( IS_MIFARE_CLASSIC_4K(bTagType) && (uiSector < MIFARE_CLASSIC_4K_MAX_SECTORS) ) - { - return true; - } + if (IS_MIFARE_CLASSIC_4K(bTagType) && (uiSector < MIFARE_CLASSIC_4K_MAX_SECTORS)) { + return true; + } - return false; + return false; } bool is_first_block(uint8_t bTagType, uint32_t uiBlock) { - if ( !is_valid_block(bTagType, uiBlock) ) - { - return false; - } - - // Test if we are in the small or big sectors - if (uiBlock < MIFARE_CLASSIC_4K_MAX_BLOCKS1) - { - // For Mifare Classic 1K, it will enter always here - return ( (uiBlock) % (MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR1) == 0 ); - } - else - { - // This branch will enter only for Mifare Classic 4K big sectors - return ( (uiBlock) % (MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR2) == 0 ); - } - - // Should not reach here, but... never know + if (!is_valid_block(bTagType, uiBlock)) { return false; + } + + // Test if we are in the small or big sectors + if (uiBlock < MIFARE_CLASSIC_4K_MAX_BLOCKS1) { + // For Mifare Classic 1K, it will enter always here + return ((uiBlock) % (MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR1) == 0); + } else { + // This branch will enter only for Mifare Classic 4K big sectors + return ((uiBlock) % (MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR2) == 0); + } + + // Should not reach here, but... never know + return false; } bool is_trailer_block(uint8_t bTagType, uint32_t uiBlock) { - if ( !is_valid_block(bTagType, uiBlock) ) - { - return false; - } - - // Test if we are in the small or big sectors - if (uiBlock < MIFARE_CLASSIC_4K_MAX_BLOCKS1) - { - // For Mifare Classic 1K, it will enter always here - return ( (uiBlock+1) % (MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR1) == 0 ); - } - else - { - // This branch will enter only for Mifare Classic 4K big sectors - return ( (uiBlock+1) % (MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR2) == 0 ); - } - - // Should not reach here, but... never know + if (!is_valid_block(bTagType, uiBlock)) { return false; + } + + // Test if we are in the small or big sectors + if (uiBlock < MIFARE_CLASSIC_4K_MAX_BLOCKS1) { + // For Mifare Classic 1K, it will enter always here + return ((uiBlock + 1) % (MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR1) == 0); + } else { + // This branch will enter only for Mifare Classic 4K big sectors + return ((uiBlock + 1) % (MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR2) == 0); + } + + // Should not reach here, but... never know + return false; } uint32_t get_first_block(uint8_t bTagType, uint32_t uiBlock) { - if ( !is_valid_block(bTagType, uiBlock) ) - { - return MIFARE_CLASSIC_INVALID_BLOCK; - } - - // Test if we are in the small or big sectors - if (uiBlock < MIFARE_CLASSIC_4K_MAX_BLOCKS1) - { - // Integer divide, then integer multiply - return (uiBlock/MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR1) * MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR1; - } - else - { - uint32_t tmp = uiBlock - MIFARE_CLASSIC_4K_MAX_BLOCKS1; - return MIFARE_CLASSIC_4K_MAX_BLOCKS1 + (tmp/MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR2) * MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR2; - } - - // Should not reach here, but... never know + if (!is_valid_block(bTagType, uiBlock)) { return MIFARE_CLASSIC_INVALID_BLOCK; + } + + // Test if we are in the small or big sectors + if (uiBlock < MIFARE_CLASSIC_4K_MAX_BLOCKS1) { + // Integer divide, then integer multiply + return (uiBlock / MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR1) * MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR1; + } else { + uint32_t tmp = uiBlock - MIFARE_CLASSIC_4K_MAX_BLOCKS1; + return MIFARE_CLASSIC_4K_MAX_BLOCKS1 + (tmp / MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR2) * MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR2; + } + + // Should not reach here, but... never know + return MIFARE_CLASSIC_INVALID_BLOCK; } uint32_t get_trailer_block(uint8_t bTagType, uint32_t uiBlock) { - if ( !is_valid_block(bTagType, uiBlock) ) - { - return MIFARE_CLASSIC_INVALID_BLOCK; - } - - // Test if we are in the small or big sectors - if (uiBlock < MIFARE_CLASSIC_4K_MAX_BLOCKS1) - { - // Integer divide, then integer multiply - return (uiBlock/MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR1) * MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR1 + (MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR1-1); - } - else - { - uint32_t tmp = uiBlock - MIFARE_CLASSIC_4K_MAX_BLOCKS1; - return MIFARE_CLASSIC_4K_MAX_BLOCKS1 + (tmp/MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR2) * MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR2 + (MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR2-1); - } - - // Should not reach here, but... never know + if (!is_valid_block(bTagType, uiBlock)) { return MIFARE_CLASSIC_INVALID_BLOCK; + } + + // Test if we are in the small or big sectors + if (uiBlock < MIFARE_CLASSIC_4K_MAX_BLOCKS1) { + // Integer divide, then integer multiply + return (uiBlock / MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR1) * MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR1 + (MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR1 - 1); + } else { + uint32_t tmp = uiBlock - MIFARE_CLASSIC_4K_MAX_BLOCKS1; + return MIFARE_CLASSIC_4K_MAX_BLOCKS1 + (tmp / MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR2) * MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR2 + (MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR2 - 1); + } + + // Should not reach here, but... never know + return MIFARE_CLASSIC_INVALID_BLOCK; } bool is_big_sector(uint8_t bTagType, uint32_t uiSector) { - if ( !is_valid_sector(bTagType, uiSector) ) - { - return false; - } - - if (uiSector >= MIFARE_CLASSIC_4K_MAX_SECTORS1) - { - return true; - } - + if (!is_valid_sector(bTagType, uiSector)) { return false; + } + + if (uiSector >= MIFARE_CLASSIC_4K_MAX_SECTORS1) { + return true; + } + + return false; } uint32_t get_first_block_for_sector(uint8_t bTagType, uint32_t uiSector) { - if ( !is_valid_sector(bTagType, uiSector) ) - { - return MIFARE_CLASSIC_INVALID_BLOCK; - } - - if (uiSector < MIFARE_CLASSIC_4K_MAX_SECTORS1) - { - // For Mifare Classic 1K, it will enter always here - return (uiSector * MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR1); - } - else - { - // For Mifare Classic 4K big sectors it will enter always here - uint32_t tmp = uiSector - MIFARE_CLASSIC_4K_MAX_SECTORS1; - return MIFARE_CLASSIC_4K_MAX_BLOCKS1 + (tmp * MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR2); - } - - // Should not reach here, but... never know + if (!is_valid_sector(bTagType, uiSector)) { return MIFARE_CLASSIC_INVALID_BLOCK; + } + + if (uiSector < MIFARE_CLASSIC_4K_MAX_SECTORS1) { + // For Mifare Classic 1K, it will enter always here + return (uiSector * MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR1); + } else { + // For Mifare Classic 4K big sectors it will enter always here + uint32_t tmp = uiSector - MIFARE_CLASSIC_4K_MAX_SECTORS1; + return MIFARE_CLASSIC_4K_MAX_BLOCKS1 + (tmp * MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR2); + } + + // Should not reach here, but... never know + return MIFARE_CLASSIC_INVALID_BLOCK; } uint32_t get_trailer_block_for_sector(uint8_t bTagType, uint32_t uiSector) { - if ( !is_valid_sector(bTagType, uiSector) ) - { - return MIFARE_CLASSIC_INVALID_BLOCK; - } - - if (uiSector < MIFARE_CLASSIC_4K_MAX_SECTORS1) - { - // For Mifare Classic 1K, it will enter always here - return (uiSector * MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR1) + (MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR1-1); - } - else - { - // For Mifare Classic 4K big sectors it will enter always here - uint32_t tmp = uiSector - MIFARE_CLASSIC_4K_MAX_SECTORS1; - return MIFARE_CLASSIC_4K_MAX_BLOCKS1 + (tmp * MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR2) + (MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR2-1); - } - - // Should not reach here, but... never know + if (!is_valid_sector(bTagType, uiSector)) { return MIFARE_CLASSIC_INVALID_BLOCK; + } + + if (uiSector < MIFARE_CLASSIC_4K_MAX_SECTORS1) { + // For Mifare Classic 1K, it will enter always here + return (uiSector * MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR1) + (MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR1 - 1); + } else { + // For Mifare Classic 4K big sectors it will enter always here + uint32_t tmp = uiSector - MIFARE_CLASSIC_4K_MAX_SECTORS1; + return MIFARE_CLASSIC_4K_MAX_BLOCKS1 + (tmp * MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR2) + (MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR2 - 1); + } + + // Should not reach here, but... never know + return MIFARE_CLASSIC_INVALID_BLOCK; } uint32_t get_sector_for_block(uint8_t bTagType, uint32_t uiBlock) { - if ( !is_valid_block(bTagType, uiBlock) ) - { - return MIFARE_CLASSIC_INVALID_BLOCK; - } - - // Test if we are in the small or big sectors - if (uiBlock < MIFARE_CLASSIC_4K_MAX_BLOCKS1) - { - // For Mifare Classic 1K, it will enter always here - return (uiBlock/MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR1); - } - else - { - uint32_t tmp = uiBlock - MIFARE_CLASSIC_4K_MAX_BLOCKS1; - return MIFARE_CLASSIC_4K_MAX_SECTORS1 + (tmp/MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR2); - } - - // Should not reach here, but... never know + if (!is_valid_block(bTagType, uiBlock)) { return MIFARE_CLASSIC_INVALID_BLOCK; + } + + // Test if we are in the small or big sectors + if (uiBlock < MIFARE_CLASSIC_4K_MAX_BLOCKS1) { + // For Mifare Classic 1K, it will enter always here + return (uiBlock / MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR1); + } else { + uint32_t tmp = uiBlock - MIFARE_CLASSIC_4K_MAX_BLOCKS1; + return MIFARE_CLASSIC_4K_MAX_SECTORS1 + (tmp / MIFARE_CLASSIC_4K_BLOCKS_PER_SECTOR2); + } + + // Should not reach here, but... never know + return MIFARE_CLASSIC_INVALID_BLOCK; } // Test case function for checking correct functionality of the block/sector is_ ang get_ functions void test_mifare_classic_blocks_sectors_functions(uint8_t bTagType) { - uint32_t i; - uint32_t max_blocks, max_sectors; + uint32_t i; + uint32_t max_blocks, max_sectors; - if ( IS_MIFARE_CLASSIC_1K(bTagType) ) - { - printf("\nMIFARE CLASSIC 1K\n"); - max_blocks = MIFARE_CLASSIC_1K_MAX_BLOCKS; - max_sectors = MIFARE_CLASSIC_1K_MAX_SECTORS; - } - else if ( IS_MIFARE_CLASSIC_4K(bTagType) ) - { - printf("\nMIFARE CLASSIC 4K\n"); - max_blocks = MIFARE_CLASSIC_4K_MAX_BLOCKS; - max_sectors = MIFARE_CLASSIC_4K_MAX_SECTORS; - } - else - { - return; - } + if (IS_MIFARE_CLASSIC_1K(bTagType)) { + printf("\nMIFARE CLASSIC 1K\n"); + max_blocks = MIFARE_CLASSIC_1K_MAX_BLOCKS; + max_sectors = MIFARE_CLASSIC_1K_MAX_SECTORS; + } else if (IS_MIFARE_CLASSIC_4K(bTagType)) { + printf("\nMIFARE CLASSIC 4K\n"); + max_blocks = MIFARE_CLASSIC_4K_MAX_BLOCKS; + max_sectors = MIFARE_CLASSIC_4K_MAX_SECTORS; + } else { + return; + } - // Include one invalid block, that is why we add +1 - for (i = 0; iamb->mbm.btUnknown; - - if ( !IS_MIFARE_CLASSIC_1K(bTagType) && !IS_MIFARE_CLASSIC_4K(bTagType) ) - { - return; - } - - printf("%s - UID %02x %02x %02x %02x - TYPE 0x%02x (%s)\n", - title, tag->amb->mbm.abtUID[0], tag->amb->mbm.abtUID[1], tag->amb->mbm.abtUID[2], tag->amb->mbm.abtUID[3], bTagType, - (IS_MIFARE_CLASSIC_1K(bTagType)?(MIFARE_CLASSIC_1K_NAME):(IS_MIFARE_CLASSIC_4K(bTagType)?(MIFARE_CLASSIC_4K_NAME):(MIFARE_CLASSIC_UNKN_NAME))) - ); - printf("-------------------------------------------------------\n"); - printf("Sector\t| Key A\t| AC bits\t| Key B\n"); - printf("-------------------------------------------------------\n"); - - if ( IS_MIFARE_CLASSIC_1K(tag->amb->mbm.btUnknown) ) - { - max_blocks = MIFARE_CLASSIC_1K_MAX_BLOCKS; - } - else - { - max_blocks = MIFARE_CLASSIC_4K_MAX_BLOCKS; - } - - for (i=0; iabtKeyA[0], ptr_trailer->abtKeyA[1], ptr_trailer->abtKeyA[2], - ptr_trailer->abtKeyA[3], ptr_trailer->abtKeyA[4], ptr_trailer->abtKeyA[5], - ptr_trailer->abtAccessBits[0], ptr_trailer->abtAccessBits[1], ptr_trailer->abtAccessBits[2], ptr_trailer->abtAccessBits[3], - ptr_trailer->abtKeyB[0], ptr_trailer->abtKeyB[1], ptr_trailer->abtKeyB[2], - ptr_trailer->abtKeyB[3], ptr_trailer->abtKeyB[4], ptr_trailer->abtKeyB[5] - ); - - // Go beyond current trailer block, i.e. go to next sector - i = trailer_block; - } - - printf("\n"); + uint32_t i, max_blocks, trailer_block; + uint8_t bTagType; + mifare_classic_block_trailer *ptr_trailer = NULL; + if (!tag) { return; + } + + bTagType = tag->amb->mbm.btUnknown; + + if (!IS_MIFARE_CLASSIC_1K(bTagType) && !IS_MIFARE_CLASSIC_4K(bTagType)) { + return; + } + + printf("%s - UID %02x %02x %02x %02x - TYPE 0x%02x (%s)\n", + title, tag->amb->mbm.abtUID[0], tag->amb->mbm.abtUID[1], tag->amb->mbm.abtUID[2], tag->amb->mbm.abtUID[3], bTagType, + (IS_MIFARE_CLASSIC_1K(bTagType) ? (MIFARE_CLASSIC_1K_NAME) : (IS_MIFARE_CLASSIC_4K(bTagType) ? (MIFARE_CLASSIC_4K_NAME) : (MIFARE_CLASSIC_UNKN_NAME))) + ); + printf("-------------------------------------------------------\n"); + printf("Sector\t| Key A\t| AC bits\t| Key B\n"); + printf("-------------------------------------------------------\n"); + + if (IS_MIFARE_CLASSIC_1K(tag->amb->mbm.btUnknown)) { + max_blocks = MIFARE_CLASSIC_1K_MAX_BLOCKS; + } else { + max_blocks = MIFARE_CLASSIC_4K_MAX_BLOCKS; + } + + for (i = 0; i < max_blocks; i++) { + trailer_block = get_trailer_block(bTagType, i); + + if (!is_valid_block(bTagType, trailer_block)) { + break; + } + + ptr_trailer = (mifare_classic_block_trailer *)((char *)tag + (trailer_block * MIFARE_CLASSIC_BYTES_PER_BLOCK)); + + printf("%d\t| %02x%02x%02x%02x%02x%02x\t| %02x%02x%02x%02x\t| %02x%02x%02x%02x%02x%02x\n", + get_sector_for_block(bTagType, trailer_block), + ptr_trailer->abtKeyA[0], ptr_trailer->abtKeyA[1], ptr_trailer->abtKeyA[2], + ptr_trailer->abtKeyA[3], ptr_trailer->abtKeyA[4], ptr_trailer->abtKeyA[5], + ptr_trailer->abtAccessBits[0], ptr_trailer->abtAccessBits[1], ptr_trailer->abtAccessBits[2], ptr_trailer->abtAccessBits[3], + ptr_trailer->abtKeyB[0], ptr_trailer->abtKeyB[1], ptr_trailer->abtKeyB[2], + ptr_trailer->abtKeyB[3], ptr_trailer->abtKeyB[4], ptr_trailer->abtKeyB[5] + ); + + // Go beyond current trailer block, i.e. go to next sector + i = trailer_block; + } + + printf("\n"); + + return; } bool mfcuk_key_uint64_to_arr(const uint64_t *ui64Key, uint8_t *arr6Key) { - int i; + int i; - if ( !ui64Key || !arr6Key ) - { - return false; - } + if (!ui64Key || !arr6Key) { + return false; + } - for (i = 0; i> 8*(MIFARE_CLASSIC_KEY_BYTELENGTH - i - 1)) & 0xFF); - } + for (i = 0; i < MIFARE_CLASSIC_KEY_BYTELENGTH; i++) { + arr6Key[i] = (uint8_t)(((*ui64Key) >> 8 * (MIFARE_CLASSIC_KEY_BYTELENGTH - i - 1)) & 0xFF); + } - return true; + return true; } bool mfcuk_key_arr_to_uint64(const uint8_t *arr6Key, uint64_t *ui64Key) { - uint64_t key = 0; - int i; + uint64_t key = 0; + int i; - if ( !ui64Key || !arr6Key ) - { - return false; - } + if (!ui64Key || !arr6Key) { + return false; + } - for (i = 0; i>= 8; + for (i = 0; i < MIFARE_CLASSIC_KEY_BYTELENGTH; i++, key <<= 8) { + key |= arr6Key[i]; + } + key >>= 8; - *ui64Key = key; + *ui64Key = key; - return true; + return true; } diff --git a/src/mfcuk_mifare.h b/src/mfcuk_mifare.h index d958191..6a7975c 100644 --- a/src/mfcuk_mifare.h +++ b/src/mfcuk_mifare.h @@ -1,10 +1,10 @@ /* Package: MiFare Classic Universal toolKit (MFCUK) - + Package version: 0.1 - + Filename: mfcuk_mifare.h @@ -20,7 +20,7 @@ GPL2 (see below), Copyright (C) 2009, Andrei Costin * @file mfcuk_mifare.h - * @brief + * @brief */ /* @@ -40,14 +40,14 @@ it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 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. - + You should have received a copy of the GNU General Public License - along with this program. If not, see . + along with this program. If not, see . */ #ifndef _MFCUK_MIFARE_H_ @@ -100,17 +100,17 @@ // Define an extended type of dump, basically a wrapper dump around basic tag dump typedef struct { - uint32_t uid; // looks redundant, but it is easier to use dmp.uid instead of dmp.amb.mbm.abtUID[0]...[3] - uint8_t type; // ATS/SAK from ti.tia.btSak, example 0x08h for Mifare 1K, 0x18h for Mifare 4K - char datetime[14]; // non-zero-terminated date-time of dump in format YYYYMMDDH24MISS, example 20091114231541 - 14 Nov 2009, 11:15:41 PM - char description[MFCUK_EXTENDED_DESCRIPTION_LENGTH]; // a description of the tag dump, example "RATB_DUMP_BEFORE_PAY" - mifare_classic_tag tag_basic; + uint32_t uid; // looks redundant, but it is easier to use dmp.uid instead of dmp.amb.mbm.abtUID[0]...[3] + uint8_t type; // ATS/SAK from ti.tia.btSak, example 0x08h for Mifare 1K, 0x18h for Mifare 4K + char datetime[14]; // non-zero-terminated date-time of dump in format YYYYMMDDH24MISS, example 20091114231541 - 14 Nov 2009, 11:15:41 PM + char description[MFCUK_EXTENDED_DESCRIPTION_LENGTH]; // a description of the tag dump, example "RATB_DUMP_BEFORE_PAY" + mifare_classic_tag tag_basic; } mifare_classic_tag_ext; // Define type of keys (A or B) in NXP notation typedef enum { - keyA = 0x60, - keyB = 0x61, + keyA = 0x60, + keyB = 0x61, } mifare_key_type; // Default keys used as a *BIG* mistake in many applications - especially System Integrators should pay attention! diff --git a/src/mfcuk_utils.c b/src/mfcuk_utils.c index 587e6b6..586d2ab 100644 --- a/src/mfcuk_utils.c +++ b/src/mfcuk_utils.c @@ -1,10 +1,10 @@ /* Package: MiFare Classic Universal toolKit (MFCUK) - + Package version: 0.1 - + Filename: mfcuk_utils.c @@ -15,7 +15,7 @@ GPL2 (see below), Copyright (C) 2009, Andrei Costin * @file mfcuk_utils.c - * @brief + * @brief */ /* @@ -35,20 +35,20 @@ it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 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. - + You should have received a copy of the GNU General Public License - along with this program. If not, see . + along with this program. If not, see . */ #include "mfcuk_utils.h" #ifdef __STDC__ - struct timeval global_timeout; +struct timeval global_timeout; #endif /* @@ -65,17 +65,17 @@ The below code is just an optimization of the algorithm. Maxim Yegorushkin /*inline*/ int is_hex(char c) { - return (c >= '0' && c <= '9') || ((c | 0x20) >= 'a' && (c | 0x20) <= 'f'); + return (c >= '0' && c <= '9') || ((c | 0x20) >= 'a' && (c | 0x20) <= 'f'); } /*inline*/ unsigned char hex2bin(unsigned char h, unsigned char l) { - h |= 0x20; // to lower - h -= 0x30; - h -= -(h > 9) & 0x27; - l |= 0x20; - l -= 0x30; - l -= -(l > 9) & 0x27; - return h << 4 | l; + h |= 0x20; // to lower + h -= 0x30; + h -= -(h > 9) & 0x27; + l |= 0x20; + l -= 0x30; + l -= -(l > 9) & 0x27; + return h << 4 | l; } diff --git a/src/mfcuk_utils.h b/src/mfcuk_utils.h index 7d7fb1b..b404344 100644 --- a/src/mfcuk_utils.h +++ b/src/mfcuk_utils.h @@ -1,10 +1,10 @@ /* Package: MiFare Classic Universal toolKit (MFCUK) - + Package version: 0.1 - + Filename: mfcuk_utils.h @@ -15,7 +15,7 @@ GPL2 (see below), Copyright (C) 2009, Andrei Costin * @file mfcuk_utils.h/ - * @brief + * @brief */ /* @@ -35,14 +35,14 @@ it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 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. - + You should have received a copy of the GNU General Public License - along with this program. If not, see . + along with this program. If not, see . */ #ifndef _MFCUK_UTILS_H_ @@ -53,28 +53,28 @@ #include #ifdef WIN32 - #define NOMINMAX - #include "windows.h" - #include "xgetopt.h" +#define NOMINMAX +#include "windows.h" +#include "xgetopt.h" #elif __STDC__ - #include - #include - #include +#include +#include +#include #endif // "Portable" sleep(miliseconds) #ifdef WIN32 - #define sleep(x) Sleep(x) +#define sleep(x) Sleep(x) #elif __STDC__ - extern struct timeval global_timeout; - #define sleep(x) { global_timeout.tv_usec = 1000 * (x); select(0,NULL,NULL,NULL,&global_timeout); } +extern struct timeval global_timeout; +#define sleep(x) { global_timeout.tv_usec = 1000 * (x); select(0,NULL,NULL,NULL,&global_timeout); } #endif // "Portable" clear_screen() - NOTE: system performance penalty introduced #ifdef WIN32 - #define clear_screen() system("cls") +#define clear_screen() system("cls") #elif __STDC__ - #define clear_screen() system("sh -c clear") +#define clear_screen() system("sh -c clear") #endif /** diff --git a/src/xgetopt.c b/src/xgetopt.c index d804054..e33cf3b 100644 --- a/src/xgetopt.c +++ b/src/xgetopt.c @@ -1,223 +1,214 @@ -// XGetopt.cpp Version 1.2 -// -// Author: Hans Dietrich -// hdietrich2@hotmail.com -// -// Description: -// XGetopt.cpp implements getopt(), a function to parse command lines. -// -// History -// Version 1.2 - 2003 May 17 -// - Added Unicode support -// -// Version 1.1 - 2002 March 10 -// - Added example to XGetopt.cpp module header -// -// This software is released into the public domain. -// You are free to use it in any way you like. -// -// This software is provided "as is" with no expressed -// or implied warranty. I accept no liability for any -// damage or loss of business that this software may cause. -// -/////////////////////////////////////////////////////////////////////////////// - - -/////////////////////////////////////////////////////////////////////////////// -// if you are using precompiled headers then include this line: -//#include "stdafx.h" -/////////////////////////////////////////////////////////////////////////////// - - -/////////////////////////////////////////////////////////////////////////////// -// if you are not using precompiled headers then include these lines: -//#include -//#include -//#include -/////////////////////////////////////////////////////////////////////////////// - +// XGetopt.cpp Version 1.2 +// +// Author: Hans Dietrich +// hdietrich2@hotmail.com +// +// Description: +// XGetopt.cpp implements getopt(), a function to parse command lines. +// +// History +// Version 1.2 - 2003 May 17 +// - Added Unicode support +// +// Version 1.1 - 2002 March 10 +// - Added example to XGetopt.cpp module header +// +// This software is released into the public domain. +// You are free to use it in any way you like. +// +// This software is provided "as is" with no expressed +// or implied warranty. I accept no liability for any +// damage or loss of business that this software may cause. +// +/////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +// if you are using precompiled headers then include this line: +//#include "stdafx.h" +/////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +// if you are not using precompiled headers then include these lines: +//#include +//#include +//#include +/////////////////////////////////////////////////////////////////////////////// + #include #include -#include "xgetopt.h" +#include "xgetopt.h" - -/////////////////////////////////////////////////////////////////////////////// -// -// X G e t o p t . c p p -// -// -// NAME -// getopt -- parse command line options -// -// SYNOPSIS -// int getopt(int argc, char *argv[], char *optstring) -// -// extern char *optarg; -// extern int optind; -// -// DESCRIPTION -// The getopt() function parses the command line arguments. Its -// arguments argc and argv are the argument count and array as -// passed into the application on program invocation. In the case -// of Visual C++ programs, argc and argv are available via the -// variables __argc and __argv (double underscores), respectively. -// getopt returns the next option letter in argv that matches a -// letter in optstring. (Note: Unicode programs should use -// __targv instead of __argv. Also, all character and string -// literals should be enclosed in _T( ) ). -// -// optstring is a string of recognized option letters; if a letter -// is followed by a colon, the option is expected to have an argument -// that may or may not be separated from it by white space. optarg -// is set to point to the start of the option argument on return from -// getopt. -// -// Option letters may be combined, e.g., "-ab" is equivalent to -// "-a -b". Option letters are case sensitive. -// -// getopt places in the external variable optind the argv index -// of the next argument to be processed. optind is initialized -// to 0 before the first call to getopt. -// -// When all options have been processed (i.e., up to the first -// non-option argument), getopt returns EOF, optarg will point -// to the argument, and optind will be set to the argv index of -// the argument. If there are no non-option arguments, optarg -// will be set to NULL. -// -// The special option "--" may be used to delimit the end of the -// options; EOF will be returned, and "--" (and everything after it) -// will be skipped. -// -// RETURN VALUE -// For option letters contained in the string optstring, getopt -// will return the option letter. getopt returns a question mark (?) -// when it encounters an option letter not included in optstring. -// EOF is returned when processing is finished. -// -// BUGS -// 1) Long options are not supported. -// 2) The GNU double-colon extension is not supported. -// 3) The environment variable POSIXLY_CORRECT is not supported. -// 4) The + syntax is not supported. -// 5) The automatic permutation of arguments is not supported. -// 6) This implementation of getopt() returns EOF if an error is -// encountered, instead of -1 as the latest standard requires. -// -// EXAMPLE -// BOOL CMyApp::ProcessCommandLine(int argc, char *argv[]) -// { -// int c; -// -// while ((c = getopt(argc, argv, _T("aBn:"))) != EOF) -// { -// switch (c) -// { -// case _T('a'): -// TRACE(_T("option a\n")); -// // -// // set some flag here -// // -// break; -// -// case _T('B'): -// TRACE( _T("option B\n")); -// // -// // set some other flag here -// // -// break; -// -// case _T('n'): -// TRACE(_T("option n: value=%d\n"), atoi(optarg)); -// // -// // do something with value here -// // -// break; -// -// case _T('?'): -// TRACE(_T("ERROR: illegal option %s\n"), argv[optind-1]); -// return FALSE; -// break; -// -// default: -// TRACE(_T("WARNING: no handler for option %c\n"), c); -// return FALSE; -// break; -// } -// } -// // -// // check for non-option args here -// // -// return TRUE; -// } -// -/////////////////////////////////////////////////////////////////////////////// - -char *optarg; // global argument pointer -int optind = 0; // global argv index - -int getopt(int argc, char *argv[], char *optstring) -{ - char c = 0; - char *cp = NULL; - static char *next = NULL; - if (optind == 0) - next = NULL; - - optarg = NULL; - - if (next == NULL || *next == '\0') - { - if (optind == 0) - optind++; - - if (optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0') - { - optarg = NULL; - if (optind < argc) - optarg = argv[optind]; - return EOF; - } - - if (strcmp(argv[optind], "--") == 0) - { - optind++; - optarg = NULL; - if (optind < argc) - optarg = argv[optind]; - return EOF; - } - - next = argv[optind]; - next++; // skip past - - optind++; - } - - c = *next++; - cp = strchr(optstring, c); - - if (cp == NULL || c == ':') - return '?'; - - cp++; - if (*cp == ':') - { - if (*next != '\0') - { - optarg = next; - next = NULL; - } - else if (optind < argc) - { - optarg = argv[optind]; - optind++; - } - else - { - return '?'; - } - } - - return c; -} +/////////////////////////////////////////////////////////////////////////////// +// +// X G e t o p t . c p p +// +// +// NAME +// getopt -- parse command line options +// +// SYNOPSIS +// int getopt(int argc, char *argv[], char *optstring) +// +// extern char *optarg; +// extern int optind; +// +// DESCRIPTION +// The getopt() function parses the command line arguments. Its +// arguments argc and argv are the argument count and array as +// passed into the application on program invocation. In the case +// of Visual C++ programs, argc and argv are available via the +// variables __argc and __argv (double underscores), respectively. +// getopt returns the next option letter in argv that matches a +// letter in optstring. (Note: Unicode programs should use +// __targv instead of __argv. Also, all character and string +// literals should be enclosed in _T( ) ). +// +// optstring is a string of recognized option letters; if a letter +// is followed by a colon, the option is expected to have an argument +// that may or may not be separated from it by white space. optarg +// is set to point to the start of the option argument on return from +// getopt. +// +// Option letters may be combined, e.g., "-ab" is equivalent to +// "-a -b". Option letters are case sensitive. +// +// getopt places in the external variable optind the argv index +// of the next argument to be processed. optind is initialized +// to 0 before the first call to getopt. +// +// When all options have been processed (i.e., up to the first +// non-option argument), getopt returns EOF, optarg will point +// to the argument, and optind will be set to the argv index of +// the argument. If there are no non-option arguments, optarg +// will be set to NULL. +// +// The special option "--" may be used to delimit the end of the +// options; EOF will be returned, and "--" (and everything after it) +// will be skipped. +// +// RETURN VALUE +// For option letters contained in the string optstring, getopt +// will return the option letter. getopt returns a question mark (?) +// when it encounters an option letter not included in optstring. +// EOF is returned when processing is finished. +// +// BUGS +// 1) Long options are not supported. +// 2) The GNU double-colon extension is not supported. +// 3) The environment variable POSIXLY_CORRECT is not supported. +// 4) The + syntax is not supported. +// 5) The automatic permutation of arguments is not supported. +// 6) This implementation of getopt() returns EOF if an error is +// encountered, instead of -1 as the latest standard requires. +// +// EXAMPLE +// BOOL CMyApp::ProcessCommandLine(int argc, char *argv[]) +// { +// int c; +// +// while ((c = getopt(argc, argv, _T("aBn:"))) != EOF) +// { +// switch (c) +// { +// case _T('a'): +// TRACE(_T("option a\n")); +// // +// // set some flag here +// // +// break; +// +// case _T('B'): +// TRACE( _T("option B\n")); +// // +// // set some other flag here +// // +// break; +// +// case _T('n'): +// TRACE(_T("option n: value=%d\n"), atoi(optarg)); +// // +// // do something with value here +// // +// break; +// +// case _T('?'): +// TRACE(_T("ERROR: illegal option %s\n"), argv[optind-1]); +// return FALSE; +// break; +// +// default: +// TRACE(_T("WARNING: no handler for option %c\n"), c); +// return FALSE; +// break; +// } +// } +// // +// // check for non-option args here +// // +// return TRUE; +// } +// +/////////////////////////////////////////////////////////////////////////////// + +char *optarg; // global argument pointer +int optind = 0; // global argv index + +int getopt(int argc, char *argv[], char *optstring) +{ + char c = 0; + char *cp = NULL; + + static char *next = NULL; + if (optind == 0) + next = NULL; + + optarg = NULL; + + if (next == NULL || *next == '\0') { + if (optind == 0) + optind++; + + if (optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0') { + optarg = NULL; + if (optind < argc) + optarg = argv[optind]; + return EOF; + } + + if (strcmp(argv[optind], "--") == 0) { + optind++; + optarg = NULL; + if (optind < argc) + optarg = argv[optind]; + return EOF; + } + + next = argv[optind]; + next++; // skip past - + optind++; + } + + c = *next++; + cp = strchr(optstring, c); + + if (cp == NULL || c == ':') + return '?'; + + cp++; + if (*cp == ':') { + if (*next != '\0') { + optarg = next; + next = NULL; + } else if (optind < argc) { + optarg = argv[optind]; + optind++; + } else { + return '?'; + } + } + + return c; +} diff --git a/src/xgetopt.h b/src/xgetopt.h index a544cdc..fa6f6ad 100644 --- a/src/xgetopt.h +++ b/src/xgetopt.h @@ -1,23 +1,23 @@ -// XGetopt.h Version 1.2 -// -// Author: Hans Dietrich -// hdietrich2@hotmail.com -// -// This software is released into the public domain. -// You are free to use it in any way you like. -// -// This software is provided "as is" with no expressed -// or implied warranty. I accept no liability for any -// damage or loss of business that this software may cause. -// -/////////////////////////////////////////////////////////////////////////////// - -#ifndef XGETOPT_H -#define XGETOPT_H - -extern int optind, opterr; -extern char *optarg; - -int getopt(int argc, char *argv[], char *optstring); - -#endif //XGETOPT_H +// XGetopt.h Version 1.2 +// +// Author: Hans Dietrich +// hdietrich2@hotmail.com +// +// This software is released into the public domain. +// You are free to use it in any way you like. +// +// This software is provided "as is" with no expressed +// or implied warranty. I accept no liability for any +// damage or loss of business that this software may cause. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef XGETOPT_H +#define XGETOPT_H + +extern int optind, opterr; +extern char *optarg; + +int getopt(int argc, char *argv[], char *optstring); + +#endif //XGETOPT_H