diff --git a/application.fam b/application.fam index d47dd9e..29a6ba2 100644 --- a/application.fam +++ b/application.fam @@ -25,11 +25,16 @@ App( # command="asn1c -D ${FAP_SRC_DIR}/lib/asn1 -no-gen-example -pdu=all ${FAP_SRC_DIR}/seader.asn1" # ), # ), + fap_libs=["mbedtls"], fap_private_libs=[ Lib( name="asn1", cflags=["-Wno-error"], ), + Lib( + name="loclass", + cflags=["-O3"], + ), ], fap_weburl="https://seader.ericbetts.dev", fap_icon_assets="icons", diff --git a/lib/loclass/optimized_cipher.c b/lib/loclass/optimized_cipher.c new file mode 100644 index 0000000..e42dc08 --- /dev/null +++ b/lib/loclass/optimized_cipher.c @@ -0,0 +1,319 @@ +//----------------------------------------------------------------------------- +// Borrowed initially from https://github.com/holiman/loclass +// Copyright (C) 2014 Martin Holst Swende +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// WARNING +// +// THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. +// +// USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL +// PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, +// AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. +// +// THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. +//----------------------------------------------------------------------------- +// It is a reconstruction of the cipher engine used in iClass, and RFID techology. +// +// The implementation is based on the work performed by +// Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and +// Milosch Meriac in the paper "Dismantling IClass". +//----------------------------------------------------------------------------- +/* + This file contains an optimized version of the MAC-calculation algorithm. Some measurements on + a std laptop showed it runs in about 1/3 of the time: + + Std: 0.428962 + Opt: 0.151609 + + Additionally, it is self-reliant, not requiring e.g. bitstreams from the cipherutils, thus can + be easily dropped into a code base. + + The optimizations have been performed in the following steps: + * Parameters passed by reference instead of by value. + * Iteration instead of recursion, un-nesting recursive loops into for-loops. + * Handling of bytes instead of individual bits, for less shuffling and masking + * Less creation of "objects", structs, and instead reuse of alloc:ed memory + * Inlining some functions via #define:s + + As a consequence, this implementation is less generic. Also, I haven't bothered documenting this. + For a thorough documentation, check out the MAC-calculation within cipher.c instead. + + -- MHS 2015 +**/ + +/** + + The runtime of opt_doTagMAC_2() with the MHS optimized version was 403 microseconds on Proxmark3. + This was still to slow for some newer readers which didn't want to wait that long. + + Further optimizations to speedup the MAC calculations: + * Optimized opt_Tt logic + * Look up table for opt_select + * Removing many unnecessary bit maskings (& 0x1) + * updating state in place instead of alternating use of a second state structure + * remove the necessity to reverse bits of input and output bytes + + opt_doTagMAC_2() now completes in 270 microseconds. + + -- piwi 2019 +**/ + +/** + add the possibility to do iCLASS on device only + -- iceman 2020 +**/ + +#include "optimized_cipher.h" +#include "optimized_elite.h" +#include "optimized_ikeys.h" +#include "optimized_cipherutils.h" + +static const uint8_t loclass_opt_select_LUT[256] = { + 00, 03, 02, 01, 02, 03, 00, 01, 04, 07, 07, 04, 06, 07, 05, 04, 01, 02, 03, 00, 02, 03, 00, 01, + 05, 06, 06, 05, 06, 07, 05, 04, 06, 05, 04, 07, 04, 05, 06, 07, 06, 05, 05, 06, 04, 05, 07, 06, + 07, 04, 05, 06, 04, 05, 06, 07, 07, 04, 04, 07, 04, 05, 07, 06, 06, 05, 04, 07, 04, 05, 06, 07, + 02, 01, 01, 02, 00, 01, 03, 02, 03, 00, 01, 02, 00, 01, 02, 03, 07, 04, 04, 07, 04, 05, 07, 06, + 00, 03, 02, 01, 02, 03, 00, 01, 00, 03, 03, 00, 02, 03, 01, 00, 05, 06, 07, 04, 06, 07, 04, 05, + 05, 06, 06, 05, 06, 07, 05, 04, 02, 01, 00, 03, 00, 01, 02, 03, 06, 05, 05, 06, 04, 05, 07, 06, + 03, 00, 01, 02, 00, 01, 02, 03, 07, 04, 04, 07, 04, 05, 07, 06, 02, 01, 00, 03, 00, 01, 02, 03, + 02, 01, 01, 02, 00, 01, 03, 02, 03, 00, 01, 02, 00, 01, 02, 03, 03, 00, 00, 03, 00, 01, 03, 02, + 04, 07, 06, 05, 06, 07, 04, 05, 00, 03, 03, 00, 02, 03, 01, 00, 01, 02, 03, 00, 02, 03, 00, 01, + 05, 06, 06, 05, 06, 07, 05, 04, 04, 07, 06, 05, 06, 07, 04, 05, 04, 07, 07, 04, 06, 07, 05, 04, + 01, 02, 03, 00, 02, 03, 00, 01, 01, 02, 02, 01, 02, 03, 01, 00}; + +/********************** the table above has been generated with this code: ******** +#include "util.h" +static void init_opt_select_LUT(void) { + for (int r = 0; r < 256; r++) { + uint8_t r_ls2 = r << 2; + uint8_t r_and_ls2 = r & r_ls2; + uint8_t r_or_ls2 = r | r_ls2; + uint8_t z0 = (r_and_ls2 >> 5) ^ ((r & ~r_ls2) >> 4) ^ ( r_or_ls2 >> 3); + uint8_t z1 = (r_or_ls2 >> 6) ^ ( r_or_ls2 >> 1) ^ (r >> 5) ^ r; + uint8_t z2 = ((r & ~r_ls2) >> 4) ^ (r_and_ls2 >> 3) ^ r; + loclass_opt_select_LUT[r] = (z0 & 4) | (z1 & 2) | (z2 & 1); + } + print_result("", loclass_opt_select_LUT, 256); +} +***********************************************************************************/ + +static inline void loclass_opt_successor(const uint8_t* k, LoclassState_t* s, uint8_t y) { + uint16_t Tt = s->t & 0xc533; + Tt = Tt ^ (Tt >> 1); + Tt = Tt ^ (Tt >> 4); + Tt = Tt ^ (Tt >> 10); + Tt = Tt ^ (Tt >> 8); + + s->t = (s->t >> 1); + s->t |= (Tt ^ (s->r >> 7) ^ (s->r >> 3)) << 15; + + uint8_t opt_B = s->b; + opt_B ^= s->b >> 6; + opt_B ^= s->b >> 5; + opt_B ^= s->b >> 4; + + s->b = s->b >> 1; + s->b |= (opt_B ^ s->r) << 7; + + uint8_t Tt1 = Tt & 0x01; + uint8_t opt_select = loclass_opt_select_LUT[s->r] ^ Tt1 ^ ((Tt1 ^ (y & 0x01)) << 1); + + uint8_t r = s->r; + s->r = (k[opt_select] ^ s->b) + s->l; + s->l = s->r + r; +} + +static inline void loclass_opt_suc( + const uint8_t* k, + LoclassState_t* s, + const uint8_t* in, + uint8_t length, + bool add32Zeroes) { + for(int i = 0; i < length; i++) { + uint8_t head = in[i]; +#pragma GCC unroll 8 + for(int j = 0; j < 8; j++) { + loclass_opt_successor(k, s, head); + head >>= 1; + } + } + // For tag MAC, an additional 32 zeroes + if(add32Zeroes) { + for(int i = 0; i < 32; i++) { + loclass_opt_successor(k, s, 0); + } + } +} + +static inline void loclass_opt_output(const uint8_t* k, LoclassState_t* s, uint8_t* buffer) { +#pragma GCC unroll 4 + for(uint8_t times = 0; times < 4; times++) { + uint8_t bout = 0; + bout |= (s->r & 0x4) >> 2; + loclass_opt_successor(k, s, 0); + bout |= (s->r & 0x4) >> 1; + loclass_opt_successor(k, s, 0); + bout |= (s->r & 0x4); + loclass_opt_successor(k, s, 0); + bout |= (s->r & 0x4) << 1; + loclass_opt_successor(k, s, 0); + bout |= (s->r & 0x4) << 2; + loclass_opt_successor(k, s, 0); + bout |= (s->r & 0x4) << 3; + loclass_opt_successor(k, s, 0); + bout |= (s->r & 0x4) << 4; + loclass_opt_successor(k, s, 0); + bout |= (s->r & 0x4) << 5; + loclass_opt_successor(k, s, 0); + buffer[times] = bout; + } +} + +static void loclass_opt_MAC(uint8_t* k, uint8_t* input, uint8_t* out) { + LoclassState_t _init = { + ((k[0] ^ 0x4c) + 0xEC) & 0xFF, // l + ((k[0] ^ 0x4c) + 0x21) & 0xFF, // r + 0x4c, // b + 0xE012 // t + }; + + loclass_opt_suc(k, &_init, input, 12, false); + loclass_opt_output(k, &_init, out); +} + +static void loclass_opt_MAC_N(uint8_t* k, uint8_t* input, uint8_t in_size, uint8_t* out) { + LoclassState_t _init = { + ((k[0] ^ 0x4c) + 0xEC) & 0xFF, // l + ((k[0] ^ 0x4c) + 0x21) & 0xFF, // r + 0x4c, // b + 0xE012 // t + }; + + loclass_opt_suc(k, &_init, input, in_size, false); + loclass_opt_output(k, &_init, out); +} + +void loclass_opt_doReaderMAC(uint8_t* cc_nr_p, uint8_t* div_key_p, uint8_t mac[4]) { + uint8_t dest[] = {0, 0, 0, 0, 0, 0, 0, 0}; + loclass_opt_MAC(div_key_p, cc_nr_p, dest); + memcpy(mac, dest, 4); +} + +void loclass_opt_doReaderMAC_2( + LoclassState_t _init, + uint8_t* nr, + uint8_t mac[4], + const uint8_t* div_key_p) { + loclass_opt_suc(div_key_p, &_init, nr, 4, false); + loclass_opt_output(div_key_p, &_init, mac); +} + +void loclass_doMAC_N(uint8_t* in_p, uint8_t in_size, uint8_t* div_key_p, uint8_t mac[4]) { + uint8_t dest[] = {0, 0, 0, 0, 0, 0, 0, 0}; + loclass_opt_MAC_N(div_key_p, in_p, in_size, dest); + memcpy(mac, dest, 4); +} + +void loclass_opt_doTagMAC(uint8_t* cc_p, const uint8_t* div_key_p, uint8_t mac[4]) { + LoclassState_t _init = { + ((div_key_p[0] ^ 0x4c) + 0xEC) & 0xFF, // l + ((div_key_p[0] ^ 0x4c) + 0x21) & 0xFF, // r + 0x4c, // b + 0xE012 // t + }; + loclass_opt_suc(div_key_p, &_init, cc_p, 12, true); + loclass_opt_output(div_key_p, &_init, mac); +} + +/** + * The tag MAC can be divided (both can, but no point in dividing the reader mac) into + * two functions, since the first 8 bytes are known, we can pre-calculate the state + * reached after feeding CC to the cipher. + * @param cc_p + * @param div_key_p + * @return the cipher state + */ +LoclassState_t loclass_opt_doTagMAC_1(uint8_t* cc_p, const uint8_t* div_key_p) { + LoclassState_t _init = { + ((div_key_p[0] ^ 0x4c) + 0xEC) & 0xFF, // l + ((div_key_p[0] ^ 0x4c) + 0x21) & 0xFF, // r + 0x4c, // b + 0xE012 // t + }; + loclass_opt_suc(div_key_p, &_init, cc_p, 8, false); + return _init; +} + +/** + * The second part of the tag MAC calculation, since the CC is already calculated into the state, + * this function is fed only the NR, and internally feeds the remaining 32 0-bits to generate the tag + * MAC response. + * @param _init - precalculated cipher state + * @param nr - the reader challenge + * @param mac - where to store the MAC + * @param div_key_p - the key to use + */ +void loclass_opt_doTagMAC_2( + LoclassState_t _init, + uint8_t* nr, + uint8_t mac[4], + const uint8_t* div_key_p) { + loclass_opt_suc(div_key_p, &_init, nr, 4, true); + loclass_opt_output(div_key_p, &_init, mac); +} + +/** + * The second part of the tag MAC calculation, since the CC is already calculated into the state, + * this function is fed only the NR, and generates both the reader and tag MACs. + * @param _init - precalculated cipher state + * @param nr - the reader challenge + * @param rmac - where to store the reader MAC + * @param tmac - where to store the tag MAC + * @param div_key_p - the key to use + */ +void loclass_opt_doBothMAC_2( + LoclassState_t _init, + uint8_t* nr, + uint8_t rmac[4], + uint8_t tmac[4], + const uint8_t* div_key_p) { + LoclassState_t* s = &_init; + loclass_opt_suc(div_key_p, s, nr, 4, false); + loclass_opt_output(div_key_p, s, rmac); + loclass_opt_output(div_key_p, s, tmac); +} + +void loclass_iclass_calc_div_key( + const uint8_t* csn, + const uint8_t* key, + uint8_t* div_key, + bool elite) { + if(elite) { + uint8_t keytable[128] = {0}; + uint8_t key_index[8] = {0}; + uint8_t key_sel[8] = {0}; + uint8_t key_sel_p[8] = {0}; + loclass_hash2(key, keytable); + loclass_hash1(csn, key_index); + for(uint8_t i = 0; i < 8; i++) key_sel[i] = keytable[key_index[i]]; + + //Permute from iclass format to standard format + loclass_permutekey_rev(key_sel, key_sel_p); + loclass_diversifyKey(csn, key_sel_p, div_key); + } else { + loclass_diversifyKey(csn, key, div_key); + } +} diff --git a/lib/loclass/optimized_cipher.h b/lib/loclass/optimized_cipher.h new file mode 100644 index 0000000..5830bec --- /dev/null +++ b/lib/loclass/optimized_cipher.h @@ -0,0 +1,117 @@ +//----------------------------------------------------------------------------- +// Borrowed initially from https://github.com/holiman/loclass +// More recently from https://github.com/RfidResearchGroup/proxmark3 +// Copyright (C) 2014 Martin Holst Swende +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// WARNING +// +// THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. +// +// USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL +// PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, +// AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. +// +// THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. +//----------------------------------------------------------------------------- +// It is a reconstruction of the cipher engine used in iClass, and RFID techology. +// +// The implementation is based on the work performed by +// Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and +// Milosch Meriac in the paper "Dismantling IClass". +//----------------------------------------------------------------------------- +#ifndef OPTIMIZED_CIPHER_H +#define OPTIMIZED_CIPHER_H +#include +#include +#include +#include + +/** +* Definition 1 (Cipher state). A cipher state of iClass s is an element of F 40/2 +* consisting of the following four components: +* 1. the left register l = (l 0 . . . l 7 ) ∈ F 8/2 ; +* 2. the right register r = (r 0 . . . r 7 ) ∈ F 8/2 ; +* 3. the top register t = (t 0 . . . t 15 ) ∈ F 16/2 . +* 4. the bottom register b = (b 0 . . . b 7 ) ∈ F 8/2 . +**/ +typedef struct { + uint8_t l; + uint8_t r; + uint8_t b; + uint16_t t; +} LoclassState_t; + +/** The reader MAC is MAC(key, CC * NR ) + **/ +void loclass_opt_doReaderMAC(uint8_t* cc_nr_p, uint8_t* div_key_p, uint8_t mac[4]); + +void loclass_opt_doReaderMAC_2( + LoclassState_t _init, + uint8_t* nr, + uint8_t mac[4], + const uint8_t* div_key_p); + +/** + * The tag MAC is MAC(key, CC * NR * 32x0)) + */ +void loclass_opt_doTagMAC(uint8_t* cc_p, const uint8_t* div_key_p, uint8_t mac[4]); + +/** + * The tag MAC can be divided (both can, but no point in dividing the reader mac) into + * two functions, since the first 8 bytes are known, we can pre-calculate the state + * reached after feeding CC to the cipher. + * @param cc_p + * @param div_key_p + * @return the cipher state + */ +LoclassState_t loclass_opt_doTagMAC_1(uint8_t* cc_p, const uint8_t* div_key_p); +/** + * The second part of the tag MAC calculation, since the CC is already calculated into the state, + * this function is fed only the NR, and internally feeds the remaining 32 0-bits to generate the tag + * MAC response. + * @param _init - precalculated cipher state + * @param nr - the reader challenge + * @param mac - where to store the MAC + * @param div_key_p - the key to use + */ +void loclass_opt_doTagMAC_2( + LoclassState_t _init, + uint8_t* nr, + uint8_t mac[4], + const uint8_t* div_key_p); + +/** + * The same as loclass_opt_doTagMAC_2, but calculates both the reader and tag MACs at the same time + * @param _init - precalculated cipher state + * @param nr - the reader challenge + * @param rmac - where to store the reader MAC + * @param tmac - where to store the tag MAC + * @param div_key_p - the key to use + */ +void loclass_opt_doBothMAC_2( + LoclassState_t _init, + uint8_t* nr, + uint8_t rmac[4], + uint8_t tmac[4], + const uint8_t* div_key_p); + +void loclass_doMAC_N(uint8_t* in_p, uint8_t in_size, uint8_t* div_key_p, uint8_t mac[4]); +void loclass_iclass_calc_div_key( + const uint8_t* csn, + const uint8_t* key, + uint8_t* div_key, + bool elite); +#endif // OPTIMIZED_CIPHER_H diff --git a/lib/loclass/optimized_cipherutils.c b/lib/loclass/optimized_cipherutils.c new file mode 100644 index 0000000..e6a87c4 --- /dev/null +++ b/lib/loclass/optimized_cipherutils.c @@ -0,0 +1,136 @@ +//----------------------------------------------------------------------------- +// Borrowed initially from https://github.com/holiman/loclass +// Copyright (C) 2014 Martin Holst Swende +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// WARNING +// +// THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. +// +// USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL +// PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, +// AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. +// +// THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. +//----------------------------------------------------------------------------- +// It is a reconstruction of the cipher engine used in iClass, and RFID techology. +// +// The implementation is based on the work performed by +// Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and +// Milosch Meriac in the paper "Dismantling IClass". +//----------------------------------------------------------------------------- +#include "optimized_cipherutils.h" +#include + +/** + * + * @brief Return and remove the first bit (x0) in the stream : + * @param stream + * @return + */ +bool loclass_headBit(LoclassBitstreamIn_t* stream) { + int bytepos = stream->position >> 3; // divide by 8 + int bitpos = (stream->position++) & 7; // mask out 00000111 + return (*(stream->buffer + bytepos) >> (7 - bitpos)) & 1; +} +/** + * @brief Return and remove the last bit (xn) in the stream: + * @param stream + * @return + */ +bool loclass_tailBit(LoclassBitstreamIn_t* stream) { + int bitpos = stream->numbits - 1 - (stream->position++); + + int bytepos = bitpos >> 3; + bitpos &= 7; + return (*(stream->buffer + bytepos) >> (7 - bitpos)) & 1; +} +/** + * @brief Pushes bit onto the stream + * @param stream + * @param bit + */ +void loclass_pushBit(LoclassBitstreamOut_t* stream, bool bit) { + int bytepos = stream->position >> 3; // divide by 8 + int bitpos = stream->position & 7; + *(stream->buffer + bytepos) |= (bit) << (7 - bitpos); + stream->position++; + stream->numbits++; +} + +/** + * @brief Pushes the lower six bits onto the stream + * as b0 b1 b2 b3 b4 b5 b6 + * @param stream + * @param bits + */ +void loclass_push6bits(LoclassBitstreamOut_t* stream, uint8_t bits) { + loclass_pushBit(stream, bits & 0x20); + loclass_pushBit(stream, bits & 0x10); + loclass_pushBit(stream, bits & 0x08); + loclass_pushBit(stream, bits & 0x04); + loclass_pushBit(stream, bits & 0x02); + loclass_pushBit(stream, bits & 0x01); +} + +/** + * @brief loclass_bitsLeft + * @param stream + * @return number of bits left in stream + */ +int loclass_bitsLeft(LoclassBitstreamIn_t* stream) { + return stream->numbits - stream->position; +} +/** + * @brief numBits + * @param stream + * @return Number of bits stored in stream + */ +void loclass_x_num_to_bytes(uint64_t n, size_t len, uint8_t* dest) { + while(len--) { + dest[len] = (uint8_t)n; + n >>= 8; + } +} + +uint64_t loclass_x_bytes_to_num(uint8_t* src, size_t len) { + uint64_t num = 0; + while(len--) { + num = (num << 8) | (*src); + src++; + } + return num; +} + +uint8_t loclass_reversebytes(uint8_t b) { + b = (b & 0xF0) >> 4 | (b & 0x0F) << 4; + b = (b & 0xCC) >> 2 | (b & 0x33) << 2; + b = (b & 0xAA) >> 1 | (b & 0x55) << 1; + return b; +} + +void loclass_reverse_arraybytes(uint8_t* arr, size_t len) { + uint8_t i; + for(i = 0; i < len; i++) { + arr[i] = loclass_reversebytes(arr[i]); + } +} + +void loclass_reverse_arraycopy(uint8_t* arr, uint8_t* dest, size_t len) { + uint8_t i; + for(i = 0; i < len; i++) { + dest[i] = loclass_reversebytes(arr[i]); + } +} diff --git a/lib/loclass/optimized_cipherutils.h b/lib/loclass/optimized_cipherutils.h new file mode 100644 index 0000000..05b6820 --- /dev/null +++ b/lib/loclass/optimized_cipherutils.h @@ -0,0 +1,64 @@ +//----------------------------------------------------------------------------- +// Borrowed initially from https://github.com/holiman/loclass +// More recently from https://github.com/RfidResearchGroup/proxmark3 +// Copyright (C) 2014 Martin Holst Swende +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// WARNING +// +// THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. +// +// USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL +// PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, +// AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. +// +// THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. +//----------------------------------------------------------------------------- +// It is a reconstruction of the cipher engine used in iClass, and RFID techology. +// +// The implementation is based on the work performed by +// Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and +// Milosch Meriac in the paper "Dismantling IClass". +//----------------------------------------------------------------------------- +#ifndef CIPHERUTILS_H +#define CIPHERUTILS_H +#include +#include +#include + +typedef struct { + uint8_t* buffer; + uint8_t numbits; + uint8_t position; +} LoclassBitstreamIn_t; + +typedef struct { + uint8_t* buffer; + uint8_t numbits; + uint8_t position; +} LoclassBitstreamOut_t; + +bool loclass_headBit(LoclassBitstreamIn_t* stream); +bool loclass_tailBit(LoclassBitstreamIn_t* stream); +void loclass_pushBit(LoclassBitstreamOut_t* stream, bool bit); +int loclass_bitsLeft(LoclassBitstreamIn_t* stream); + +void loclass_push6bits(LoclassBitstreamOut_t* stream, uint8_t bits); +void loclass_x_num_to_bytes(uint64_t n, size_t len, uint8_t* dest); +uint64_t loclass_x_bytes_to_num(uint8_t* src, size_t len); +uint8_t loclass_reversebytes(uint8_t b); +void loclass_reverse_arraybytes(uint8_t* arr, size_t len); +void loclass_reverse_arraycopy(uint8_t* arr, uint8_t* dest, size_t len); +#endif // CIPHERUTILS_H diff --git a/lib/loclass/optimized_elite.c b/lib/loclass/optimized_elite.c new file mode 100644 index 0000000..e198a41 --- /dev/null +++ b/lib/loclass/optimized_elite.c @@ -0,0 +1,232 @@ +//----------------------------------------------------------------------------- +// Borrowed initially from https://github.com/holiman/loclass +// Copyright (C) 2014 Martin Holst Swende +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// WARNING +// +// THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. +// +// USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL +// PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, +// AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. +// +// THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. +//----------------------------------------------------------------------------- +// It is a reconstruction of the cipher engine used in iClass, and RFID techology. +// +// The implementation is based on the work performed by +// Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and +// Milosch Meriac in the paper "Dismantling IClass". +//----------------------------------------------------------------------------- +#include "optimized_elite.h" + +#include +#include +#include +#include +#include "optimized_ikeys.h" + +/** + * @brief Permutes a key from standard NIST format to Iclass specific format + * from http://www.proxmark.org/forum/viewtopic.php?pid=11220#p11220 + * + * If you loclass_permute [6c 8d 44 f9 2a 2d 01 bf] you get [8a 0d b9 88 bb a7 90 ea] as shown below. + * + * 1 0 1 1 1 1 1 1 bf + * 0 0 0 0 0 0 0 1 01 + * 0 0 1 0 1 1 0 1 2d + * 0 0 1 0 1 0 1 0 2a + * 1 1 1 1 1 0 0 1 f9 + * 0 1 0 0 0 1 0 0 44 + * 1 0 0 0 1 1 0 1 8d + * 0 1 1 0 1 1 0 0 6c + * + * 8 0 b 8 b a 9 e + * a d 9 8 b 7 0 a + * + * @param key + * @param dest + */ +void loclass_permutekey(const uint8_t key[8], uint8_t dest[8]) { + int i; + for(i = 0; i < 8; i++) { + dest[i] = (((key[7] & (0x80 >> i)) >> (7 - i)) << 7) | + (((key[6] & (0x80 >> i)) >> (7 - i)) << 6) | + (((key[5] & (0x80 >> i)) >> (7 - i)) << 5) | + (((key[4] & (0x80 >> i)) >> (7 - i)) << 4) | + (((key[3] & (0x80 >> i)) >> (7 - i)) << 3) | + (((key[2] & (0x80 >> i)) >> (7 - i)) << 2) | + (((key[1] & (0x80 >> i)) >> (7 - i)) << 1) | + (((key[0] & (0x80 >> i)) >> (7 - i)) << 0); + } +} +/** + * Permutes a key from iclass specific format to NIST format + * @brief loclass_permutekey_rev + * @param key + * @param dest + */ +void loclass_permutekey_rev(const uint8_t key[8], uint8_t dest[8]) { + int i; + for(i = 0; i < 8; i++) { + dest[7 - i] = (((key[0] & (0x80 >> i)) >> (7 - i)) << 7) | + (((key[1] & (0x80 >> i)) >> (7 - i)) << 6) | + (((key[2] & (0x80 >> i)) >> (7 - i)) << 5) | + (((key[3] & (0x80 >> i)) >> (7 - i)) << 4) | + (((key[4] & (0x80 >> i)) >> (7 - i)) << 3) | + (((key[5] & (0x80 >> i)) >> (7 - i)) << 2) | + (((key[6] & (0x80 >> i)) >> (7 - i)) << 1) | + (((key[7] & (0x80 >> i)) >> (7 - i)) << 0); + } +} + +/** + * Helper function for loclass_hash1 + * @brief loclass_rr + * @param val + * @return + */ +static uint8_t loclass_rr(uint8_t val) { + return val >> 1 | ((val & 1) << 7); +} + +/** + * Helper function for loclass_hash1 + * @brief rl + * @param val + * @return + */ +static uint8_t loclass_rl(uint8_t val) { + return val << 1 | ((val & 0x80) >> 7); +} + +/** + * Helper function for loclass_hash1 + * @brief loclass_swap + * @param val + * @return + */ +static uint8_t loclass_swap(uint8_t val) { + return ((val >> 4) & 0xFF) | ((val & 0xFF) << 4); +} + +/** + * Hash1 takes CSN as input, and determines what bytes in the keytable will be used + * when constructing the K_sel. + * @param csn the CSN used + * @param k output + */ +void loclass_hash1(const uint8_t csn[], uint8_t k[]) { + k[0] = csn[0] ^ csn[1] ^ csn[2] ^ csn[3] ^ csn[4] ^ csn[5] ^ csn[6] ^ csn[7]; + k[1] = csn[0] + csn[1] + csn[2] + csn[3] + csn[4] + csn[5] + csn[6] + csn[7]; + k[2] = loclass_rr(loclass_swap(csn[2] + k[1])); + k[3] = loclass_rl(loclass_swap(csn[3] + k[0])); + k[4] = ~loclass_rr(csn[4] + k[2]) + 1; + k[5] = ~loclass_rl(csn[5] + k[3]) + 1; + k[6] = loclass_rr(csn[6] + (k[4] ^ 0x3c)); + k[7] = loclass_rl(csn[7] + (k[5] ^ 0xc3)); + + k[7] &= 0x7F; + k[6] &= 0x7F; + k[5] &= 0x7F; + k[4] &= 0x7F; + k[3] &= 0x7F; + k[2] &= 0x7F; + k[1] &= 0x7F; + k[0] &= 0x7F; +} +/** +Definition 14. Define the rotate key function loclass_rk : (F 82 ) 8 × N → (F 82 ) 8 as +loclass_rk(x [0] . . . x [7] , 0) = x [0] . . . x [7] +loclass_rk(x [0] . . . x [7] , n + 1) = loclass_rk(loclass_rl(x [0] ) . . . loclass_rl(x [7] ), n) +**/ +static void loclass_rk(const uint8_t* key, uint8_t n, uint8_t* outp_key) { + memcpy(outp_key, key, 8); + uint8_t j; + while(n-- > 0) { + for(j = 0; j < 8; j++) outp_key[j] = loclass_rl(outp_key[j]); + } + return; +} + +static mbedtls_des_context loclass_ctx_enc; +static mbedtls_des_context loclass_ctx_dec; + +static void loclass_desdecrypt_iclass(uint8_t* iclass_key, uint8_t* input, uint8_t* output) { + uint8_t key_std_format[8] = {0}; + loclass_permutekey_rev(iclass_key, key_std_format); + mbedtls_des_setkey_dec(&loclass_ctx_dec, key_std_format); + mbedtls_des_crypt_ecb(&loclass_ctx_dec, input, output); +} + +static void loclass_desencrypt_iclass(const uint8_t* iclass_key, uint8_t* input, uint8_t* output) { + uint8_t key_std_format[8] = {0}; + loclass_permutekey_rev(iclass_key, key_std_format); + mbedtls_des_setkey_enc(&loclass_ctx_enc, key_std_format); + mbedtls_des_crypt_ecb(&loclass_ctx_enc, input, output); +} + +/** + * @brief Insert uint8_t[8] custom master key to calculate hash2 and return key_select. + * @param key unpermuted custom key + * @param loclass_hash1 loclass_hash1 + * @param key_sel output key_sel=h[loclass_hash1[i]] + */ +void loclass_hash2(const uint8_t* key64, uint8_t* outp_keytable) { + /** + *Expected: + * High Security Key Table + + 00 F1 35 59 A1 0D 5A 26 7F 18 60 0B 96 8A C0 25 C1 + 10 BF A1 3B B0 FF 85 28 75 F2 1F C6 8F 0E 74 8F 21 + 20 14 7A 55 16 C8 A9 7D B3 13 0C 5D C9 31 8D A9 B2 + 30 A3 56 83 0F 55 7E DE 45 71 21 D2 6D C1 57 1C 9C + 40 78 2F 64 51 42 7B 64 30 FA 26 51 76 D3 E0 FB B6 + 50 31 9F BF 2F 7E 4F 94 B4 BD 4F 75 91 E3 1B EB 42 + 60 3F 88 6F B8 6C 2C 93 0D 69 2C D5 20 3C C1 61 95 + 70 43 08 A0 2F FE B3 26 D7 98 0B 34 7B 47 70 A0 AB + + **** The 64-bit HS Custom Key Value = 5B7C62C491C11B39 ******/ + uint8_t key64_negated[8] = {0}; + uint8_t z[8][8] = {{0}, {0}}; + uint8_t temp_output[8] = {0}; + + //calculate complement of key + int i; + for(i = 0; i < 8; i++) key64_negated[i] = ~key64[i]; + + // Once again, key is on iclass-format + loclass_desencrypt_iclass(key64, key64_negated, z[0]); + + uint8_t y[8][8] = {{0}, {0}}; + + // y[0]=DES_dec(z[0],~key) + // Once again, key is on iclass-format + loclass_desdecrypt_iclass(z[0], key64_negated, y[0]); + + for(i = 1; i < 8; i++) { + loclass_rk(key64, i, temp_output); + loclass_desdecrypt_iclass(temp_output, z[i - 1], z[i]); + loclass_desencrypt_iclass(temp_output, y[i - 1], y[i]); + } + + if(outp_keytable != NULL) { + for(i = 0; i < 8; i++) { + memcpy(outp_keytable + i * 16, y[i], 8); + memcpy(outp_keytable + 8 + i * 16, z[i], 8); + } + } +} diff --git a/lib/loclass/optimized_elite.h b/lib/loclass/optimized_elite.h new file mode 100644 index 0000000..fba512a --- /dev/null +++ b/lib/loclass/optimized_elite.h @@ -0,0 +1,58 @@ +//----------------------------------------------------------------------------- +// Borrowed initially from https://github.com/holiman/loclass +// More recently from https://github.com/RfidResearchGroup/proxmark3 +// Copyright (C) 2014 Martin Holst Swende +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// WARNING +// +// THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. +// +// USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL +// PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, +// AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. +// +// THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. +//----------------------------------------------------------------------------- +// It is a reconstruction of the cipher engine used in iClass, and RFID techology. +// +// The implementation is based on the work performed by +// Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and +// Milosch Meriac in the paper "Dismantling IClass". +//----------------------------------------------------------------------------- +#ifndef ELITE_CRACK_H +#define ELITE_CRACK_H + +#include +#include + +void loclass_permutekey(const uint8_t key[8], uint8_t dest[8]); +/** + * Permutes a key from iclass specific format to NIST format + * @brief loclass_permutekey_rev + * @param key + * @param dest + */ +void loclass_permutekey_rev(const uint8_t key[8], uint8_t dest[8]); +/** + * Hash1 takes CSN as input, and determines what bytes in the keytable will be used + * when constructing the K_sel. + * @param csn the CSN used + * @param k output + */ +void loclass_hash1(const uint8_t* csn, uint8_t* k); +void loclass_hash2(const uint8_t* key64, uint8_t* outp_keytable); + +#endif diff --git a/lib/loclass/optimized_ikeys.c b/lib/loclass/optimized_ikeys.c new file mode 100644 index 0000000..d19f560 --- /dev/null +++ b/lib/loclass/optimized_ikeys.c @@ -0,0 +1,320 @@ +//----------------------------------------------------------------------------- +// Borrowed initially from https://github.com/holiman/loclass +// Copyright (C) 2014 Martin Holst Swende +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// WARNING +// +// THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. +// +// USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL +// PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, +// AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. +// +// THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. +//----------------------------------------------------------------------------- +// It is a reconstruction of the cipher engine used in iClass, and RFID techology. +// +// The implementation is based on the work performed by +// Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and +// Milosch Meriac in the paper "Dismantling IClass". +//----------------------------------------------------------------------------- + +/** +From "Dismantling iclass": + This section describes in detail the built-in key diversification algorithm of iClass. + Besides the obvious purpose of deriving a card key from a master key, this + algorithm intends to circumvent weaknesses in the cipher by preventing the + usage of certain ‘weak’ keys. In order to compute a diversified key, the iClass + reader first encrypts the card identity id with the master key K, using single + DES. The resulting ciphertext is then input to a function called loclass_hash0 which + outputs the diversified key k. + + k = loclass_hash0(DES enc (id, K)) + + Here the DES encryption of id with master key K outputs a cryptogram c + of 64 bits. These 64 bits are divided as c = x, y, z [0] , . . . , z [7] ∈ F 82 × F 82 × (F 62 ) 8 + which is used as input to the loclass_hash0 function. This function introduces some + obfuscation by performing a number of permutations, complement and modulo + operations, see Figure 2.5. Besides that, it checks for and removes patterns like + similar key bytes, which could produce a strong bias in the cipher. Finally, the + output of loclass_hash0 is the diversified card key k = k [0] , . . . , k [7] ∈ (F 82 ) 8 . + +**/ +#include "optimized_ikeys.h" + +#include +#include +#include +#include +#include "optimized_cipherutils.h" + +static const uint8_t loclass_pi[35] = {0x0F, 0x17, 0x1B, 0x1D, 0x1E, 0x27, 0x2B, 0x2D, 0x2E, + 0x33, 0x35, 0x39, 0x36, 0x3A, 0x3C, 0x47, 0x4B, 0x4D, + 0x4E, 0x53, 0x55, 0x56, 0x59, 0x5A, 0x5C, 0x63, 0x65, + 0x66, 0x69, 0x6A, 0x6C, 0x71, 0x72, 0x74, 0x78}; + +/** + * @brief The key diversification algorithm uses 6-bit bytes. + * This implementation uses 64 bit uint to pack seven of them into one + * variable. When they are there, they are placed as follows: + * XXXX XXXX N0 .... N7, occupying the last 48 bits. + * + * This function picks out one from such a collection + * @param all + * @param n bitnumber + * @return + */ +static uint8_t loclass_getSixBitByte(uint64_t c, int n) { + return (c >> (42 - 6 * n)) & 0x3F; +} + +/** + * @brief Puts back a six-bit 'byte' into a uint64_t. + * @param c buffer + * @param z the value to place there + * @param n bitnumber. + */ +static void loclass_pushbackSixBitByte(uint64_t* c, uint8_t z, int n) { + //0x XXXX YYYY ZZZZ ZZZZ ZZZZ + // ^z0 ^z7 + //z0: 1111 1100 0000 0000 + + uint64_t masked = z & 0x3F; + uint64_t eraser = 0x3F; + masked <<= 42 - 6 * n; + eraser <<= 42 - 6 * n; + + //masked <<= 6*n; + //eraser <<= 6*n; + + eraser = ~eraser; + (*c) &= eraser; + (*c) |= masked; +} +/** + * @brief Swaps the z-values. + * If the input value has format XYZ0Z1...Z7, the output will have the format + * XYZ7Z6...Z0 instead + * @param c + * @return + */ +static uint64_t loclass_swapZvalues(uint64_t c) { + uint64_t newz = 0; + loclass_pushbackSixBitByte(&newz, loclass_getSixBitByte(c, 0), 7); + loclass_pushbackSixBitByte(&newz, loclass_getSixBitByte(c, 1), 6); + loclass_pushbackSixBitByte(&newz, loclass_getSixBitByte(c, 2), 5); + loclass_pushbackSixBitByte(&newz, loclass_getSixBitByte(c, 3), 4); + loclass_pushbackSixBitByte(&newz, loclass_getSixBitByte(c, 4), 3); + loclass_pushbackSixBitByte(&newz, loclass_getSixBitByte(c, 5), 2); + loclass_pushbackSixBitByte(&newz, loclass_getSixBitByte(c, 6), 1); + loclass_pushbackSixBitByte(&newz, loclass_getSixBitByte(c, 7), 0); + newz |= (c & 0xFFFF000000000000); + return newz; +} + +/** +* @return 4 six-bit bytes chunked into a uint64_t,as 00..00a0a1a2a3 +*/ +static uint64_t loclass_ck(int i, int j, uint64_t z) { + if(i == 1 && j == -1) { + // loclass_ck(1, −1, z [0] . . . z [3] ) = z [0] . . . z [3] + return z; + } else if(j == -1) { + // loclass_ck(i, −1, z [0] . . . z [3] ) = loclass_ck(i − 1, i − 2, z [0] . . . z [3] ) + return loclass_ck(i - 1, i - 2, z); + } + + if(loclass_getSixBitByte(z, i) == loclass_getSixBitByte(z, j)) { + //loclass_ck(i, j − 1, z [0] . . . z [i] ← j . . . z [3] ) + uint64_t newz = 0; + int c; + for(c = 0; c < 4; c++) { + uint8_t val = loclass_getSixBitByte(z, c); + if(c == i) + loclass_pushbackSixBitByte(&newz, j, c); + else + loclass_pushbackSixBitByte(&newz, val, c); + } + return loclass_ck(i, j - 1, newz); + } else { + return loclass_ck(i, j - 1, z); + } +} +/** + + Definition 8. + Let the function check : (F 62 ) 8 → (F 62 ) 8 be defined as + check(z [0] . . . z [7] ) = loclass_ck(3, 2, z [0] . . . z [3] ) · loclass_ck(3, 2, z [4] . . . z [7] ) + + where loclass_ck : N × N × (F 62 ) 4 → (F 62 ) 4 is defined as + + loclass_ck(1, −1, z [0] . . . z [3] ) = z [0] . . . z [3] + loclass_ck(i, −1, z [0] . . . z [3] ) = loclass_ck(i − 1, i − 2, z [0] . . . z [3] ) + loclass_ck(i, j, z [0] . . . z [3] ) = + loclass_ck(i, j − 1, z [0] . . . z [i] ← j . . . z [3] ), if z [i] = z [j] ; + loclass_ck(i, j − 1, z [0] . . . z [3] ), otherwise + + otherwise. +**/ + +static uint64_t loclass_check(uint64_t z) { + //These 64 bits are divided as c = x, y, z [0] , . . . , z [7] + + // loclass_ck(3, 2, z [0] . . . z [3] ) + uint64_t ck1 = loclass_ck(3, 2, z); + + // loclass_ck(3, 2, z [4] . . . z [7] ) + uint64_t ck2 = loclass_ck(3, 2, z << 24); + + //The loclass_ck function will place the values + // in the middle of z. + ck1 &= 0x00000000FFFFFF000000; + ck2 &= 0x00000000FFFFFF000000; + + return ck1 | ck2 >> 24; +} + +static void loclass_permute( + LoclassBitstreamIn_t* p_in, + uint64_t z, + int l, + int r, + LoclassBitstreamOut_t* out) { + if(loclass_bitsLeft(p_in) == 0) return; + + bool pn = loclass_tailBit(p_in); + if(pn) { // pn = 1 + uint8_t zl = loclass_getSixBitByte(z, l); + + loclass_push6bits(out, zl + 1); + loclass_permute(p_in, z, l + 1, r, out); + } else { // otherwise + uint8_t zr = loclass_getSixBitByte(z, r); + + loclass_push6bits(out, zr); + loclass_permute(p_in, z, l, r + 1, out); + } +} + +/** + * @brief + *Definition 11. Let the function loclass_hash0 : F 82 × F 82 × (F 62 ) 8 → (F 82 ) 8 be defined as + * loclass_hash0(x, y, z [0] . . . z [7] ) = k [0] . . . k [7] where + * z'[i] = (z[i] mod (63-i)) + i i = 0...3 + * z'[i+4] = (z[i+4] mod (64-i)) + i i = 0...3 + * ẑ = check(z'); + * @param c + * @param k this is where the diversified key is put (should be 8 bytes) + * @return + */ +void loclass_hash0(uint64_t c, uint8_t k[8]) { + c = loclass_swapZvalues(c); + + //These 64 bits are divided as c = x, y, z [0] , . . . , z [7] + // x = 8 bits + // y = 8 bits + // z0-z7 6 bits each : 48 bits + uint8_t x = (c & 0xFF00000000000000) >> 56; + uint8_t y = (c & 0x00FF000000000000) >> 48; + uint64_t zP = 0; + + for(int n = 0; n < 4; n++) { + uint8_t zn = loclass_getSixBitByte(c, n); + uint8_t zn4 = loclass_getSixBitByte(c, n + 4); + uint8_t _zn = (zn % (63 - n)) + n; + uint8_t _zn4 = (zn4 % (64 - n)) + n; + loclass_pushbackSixBitByte(&zP, _zn, n); + loclass_pushbackSixBitByte(&zP, _zn4, n + 4); + } + + uint64_t zCaret = loclass_check(zP); + uint8_t p = loclass_pi[x % 35]; + + if(x & 1) //Check if x7 is 1 + p = ~p; + + LoclassBitstreamIn_t p_in = {&p, 8, 0}; + uint8_t outbuffer[] = {0, 0, 0, 0, 0, 0, 0, 0}; + LoclassBitstreamOut_t out = {outbuffer, 0, 0}; + loclass_permute(&p_in, zCaret, 0, 4, &out); //returns 48 bits? or 6 8-bytes + + //Out is now a buffer containing six-bit bytes, should be 48 bits + // if all went well + //Shift z-values down onto the lower segment + + uint64_t zTilde = loclass_x_bytes_to_num(outbuffer, sizeof(outbuffer)); + + zTilde >>= 16; + + for(int i = 0; i < 8; i++) { + // the key on index i is first a bit from y + // then six bits from z, + // then a bit from p + + // Init with zeroes + k[i] = 0; + // First, place yi leftmost in k + //k[i] |= (y << i) & 0x80 ; + + // First, place y(7-i) leftmost in k + k[i] |= (y << (7 - i)) & 0x80; + + uint8_t zTilde_i = loclass_getSixBitByte(zTilde, i); + // zTildeI is now on the form 00XXXXXX + // with one leftshift, it'll be + // 0XXXXXX0 + // So after leftshift, we can OR it into k + // However, when doing complement, we need to + // again MASK 0XXXXXX0 (0x7E) + zTilde_i <<= 1; + + //Finally, add bit from p or p-mod + //Shift bit i into rightmost location (mask only after complement) + uint8_t p_i = p >> i & 0x1; + + if(k[i]) { // yi = 1 + k[i] |= ~zTilde_i & 0x7E; + k[i] |= p_i & 1; + k[i] += 1; + + } else { // otherwise + k[i] |= zTilde_i & 0x7E; + k[i] |= (~p_i) & 1; + } + } +} +/** + * @brief Performs Elite-class key diversification + * @param csn + * @param key + * @param div_key + */ +void loclass_diversifyKey(const uint8_t* csn, const uint8_t* key, uint8_t* div_key) { + mbedtls_des_context loclass_ctx_enc; + + // Prepare the DES key + mbedtls_des_setkey_enc(&loclass_ctx_enc, key); + + uint8_t crypted_csn[8] = {0}; + + // Calculate DES(CSN, KEY) + mbedtls_des_crypt_ecb(&loclass_ctx_enc, csn, crypted_csn); + + //Calculate HASH0(DES)) + uint64_t c_csn = loclass_x_bytes_to_num(crypted_csn, sizeof(crypted_csn)); + + loclass_hash0(c_csn, div_key); +} diff --git a/lib/loclass/optimized_ikeys.h b/lib/loclass/optimized_ikeys.h new file mode 100644 index 0000000..f40e87b --- /dev/null +++ b/lib/loclass/optimized_ikeys.h @@ -0,0 +1,66 @@ +//----------------------------------------------------------------------------- +// Borrowed initially from https://github.com/holiman/loclass +// More recently from https://github.com/RfidResearchGroup/proxmark3 +// Copyright (C) 2014 Martin Holst Swende +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// WARNING +// +// THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. +// +// USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL +// PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, +// AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. +// +// THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. +//----------------------------------------------------------------------------- +// It is a reconstruction of the cipher engine used in iClass, and RFID techology. +// +// The implementation is based on the work performed by +// Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and +// Milosch Meriac in the paper "Dismantling IClass". +//----------------------------------------------------------------------------- +#ifndef IKEYS_H +#define IKEYS_H + +#include + +/** + * @brief + *Definition 11. Let the function loclass_hash0 : F 82 × F 82 × (F 62 ) 8 → (F 82 ) 8 be defined as + * loclass_hash0(x, y, z [0] . . . z [7] ) = k [0] . . . k [7] where + * z'[i] = (z[i] mod (63-i)) + i i = 0...3 + * z'[i+4] = (z[i+4] mod (64-i)) + i i = 0...3 + * ẑ = check(z'); + * @param c + * @param k this is where the diversified key is put (should be 8 bytes) + * @return + */ +void loclass_hash0(uint64_t c, uint8_t k[8]); +/** + * @brief Performs Elite-class key diversification + * @param csn + * @param key + * @param div_key + */ + +void loclass_diversifyKey(const uint8_t* csn, const uint8_t* key, uint8_t* div_key); +/** + * @brief Permutes a key from standard NIST format to Iclass specific format + * @param key + * @param dest + */ + +#endif // IKEYS_H diff --git a/protocol/picopass_protocol.h b/protocol/picopass_protocol.h index 6c4dd44..73c549c 100644 --- a/protocol/picopass_protocol.h +++ b/protocol/picopass_protocol.h @@ -8,6 +8,16 @@ #define PICOPASS_MAC_LEN 4 #define PICOPASS_KEY_LEN 8 +#define CSN_INDEX 0 +#define CFG_INDEX 1 +#define EPURSE_INDEX 2 +#define KD_INDEX 3 +#define KC_INDEX 4 +#define AIA_INDEX 5 +#define PACS_CFG_INDEX 6 +#define PACS_INDEX 7 +#define SR_SIO_INDEX 10 + #define PICOPASS_FDT_LISTEN_FC (1000) #ifdef __cplusplus diff --git a/sam_api.c b/sam_api.c index 498d02b..eed1114 100644 --- a/sam_api.c +++ b/sam_api.c @@ -6,15 +6,109 @@ #define APDU_HEADER_LEN 5 #define ASN1_PREFIX 6 #define ASN1_DEBUG true +#define SEADER_ICLASS_SR_SIO_BASE_BLOCK 10 + +const uint8_t picopass_iclass_key[] = {0xaf, 0xa7, 0x85, 0xa7, 0xda, 0xb3, 0x33, 0x78}; static char display[SEADER_UART_RX_BUF_SIZE * 2 + 1] = {0}; char asn1_log[SEADER_UART_RX_BUF_SIZE] = {0}; bool requestPacs = true; +uint8_t read4Block6[] = {RFAL_PICOPASS_CMD_READ4, 0x06, 0x45, 0x56}; +uint8_t read4Block9[] = {RFAL_PICOPASS_CMD_READ4, 0x09, 0xB2, 0xAE}; +uint8_t read4Block10[] = {RFAL_PICOPASS_CMD_READ4, 0x0A, 0x29, 0x9C}; +uint8_t read4Block13[] = {RFAL_PICOPASS_CMD_READ4, 0x0D, 0x96, 0xE8}; +uint8_t updateBlock2[] = {RFAL_PICOPASS_CMD_UPDATE, 0x02}; + void* calloc(size_t count, size_t size) { return malloc(count * size); } +// Forward declarations +void seader_send_nfc_rx(SeaderUartBridge* seader_uart, uint8_t* buffer, size_t len); + +PicopassError seader_worker_fake_epurse_update(BitBuffer* tx_buffer, BitBuffer* rx_buffer) { + const uint8_t* buffer = bit_buffer_get_data(tx_buffer); + uint8_t fake_response[8]; + memset(fake_response, 0, sizeof(fake_response)); + memcpy(fake_response + 0, buffer + 6, 4); + memcpy(fake_response + 4, buffer + 2, 4); + + bit_buffer_append_bytes(rx_buffer, fake_response, sizeof(fake_response)); + iso13239_crc_append(Iso13239CrcTypePicopass, rx_buffer); + + memset(display, 0, sizeof(display)); + for(uint8_t i = 0; i < bit_buffer_get_size_bytes(rx_buffer); i++) { + snprintf(display + (i * 2), sizeof(display), "%02x", bit_buffer_get_data(rx_buffer)[i]); + } + FURI_LOG_I(TAG, "Fake update E-Purse response: %s", display); + + return PicopassErrorNone; +} + +void seader_picopass_state_machine(Seader* seader, uint8_t* buffer, size_t len) { + SeaderWorker* seader_worker = seader->worker; + SeaderUartBridge* seader_uart = seader_worker->uart; + + BitBuffer* tx_buffer = bit_buffer_alloc(len); + bit_buffer_append_bytes(tx_buffer, buffer, len); + BitBuffer* rx_buffer = bit_buffer_alloc(SEADER_POLLER_MAX_BUFFER_SIZE); + + uint8_t sr_aia[PICOPASS_BLOCK_LEN] = {0xFF, 0xff, 0xff, 0xff, 0xFF, 0xFf, 0xff, 0xFF}; + uint8_t epurse[PICOPASS_BLOCK_LEN] = {0xff, 0xff, 0xff, 0xff, 0xe3, 0xff, 0xff, 0xff}; + uint8_t pacs_sr_cfg[PICOPASS_BLOCK_LEN] = {0xA3, 0x03, 0x03, 0x03, 0x00, 0x03, 0xe0, 0x14}; + + uint8_t tmac[4] = {}; + uint8_t cc_p[12] = {}; + uint8_t div_key[PICOPASS_BLOCK_LEN] = {}; + uint8_t offset; // for READ4 + + do { + switch(buffer[0]) { + case RFAL_PICOPASS_CMD_READ_OR_IDENTIFY: + if(buffer[1] == AIA_INDEX) { + bit_buffer_append_bytes(rx_buffer, sr_aia, sizeof(sr_aia)); + } else if(buffer[1] == PACS_CFG_INDEX) { + bit_buffer_append_bytes(rx_buffer, pacs_sr_cfg, sizeof(pacs_sr_cfg)); + } + iso13239_crc_append(Iso13239CrcTypePicopass, rx_buffer); + break; + case RFAL_PICOPASS_CMD_UPDATE: + seader_worker_fake_epurse_update(tx_buffer, rx_buffer); + break; + case RFAL_PICOPASS_CMD_READCHECK_KD: + if(buffer[1] == EPURSE_INDEX) { + bit_buffer_append_bytes(rx_buffer, epurse, sizeof(epurse)); + } + break; + case RFAL_PICOPASS_CMD_CHECK: + loclass_iclass_calc_div_key( + seader->credential->diversifier, picopass_iclass_key, div_key, false); + memcpy(cc_p, epurse, PICOPASS_BLOCK_LEN); + memcpy(cc_p + 8, buffer + 1, PICOPASS_MAC_LEN); + loclass_opt_doTagMAC(cc_p, div_key, tmac); + bit_buffer_append_bytes(rx_buffer, tmac, sizeof(tmac)); + break; + case RFAL_PICOPASS_CMD_READ4: + offset = buffer[1] - SEADER_ICLASS_SR_SIO_BASE_BLOCK; + bit_buffer_append_bytes( + rx_buffer, + seader->credential->sio + (PICOPASS_BLOCK_LEN * offset), + PICOPASS_BLOCK_LEN * 4); + iso13239_crc_append(Iso13239CrcTypePicopass, rx_buffer); + break; + } + + seader_send_nfc_rx( + seader_uart, + (uint8_t*)bit_buffer_get_data(rx_buffer), + bit_buffer_get_size_bytes(rx_buffer)); + + } while(false); + bit_buffer_free(tx_buffer); + bit_buffer_free(rx_buffer); +} + bool seader_send_apdu( SeaderUartBridge* seader_uart, uint8_t CLA, @@ -36,6 +130,12 @@ bool seader_send_apdu( apdu[4] = length; memcpy(apdu + APDU_HEADER_LEN, payload, length); + memset(display, 0, sizeof(display)); + for(uint8_t i = 0; i < APDU_HEADER_LEN + length; i++) { + snprintf(display + (i * 2), sizeof(display), "%02x", apdu[i]); + } + FURI_LOG_D(TAG, "seader_send_apdu %s", display); + seader_ccid_XfrBlock(seader_uart, apdu, APDU_HEADER_LEN + length); return true; } @@ -339,38 +439,6 @@ void seader_send_nfc_rx(SeaderUartBridge* seader_uart, uint8_t* buffer, size_t l ASN_STRUCT_FREE(asn_DEF_Response, response); } -static uint16_t seader_worker_picopass_update_ccitt(uint16_t crcSeed, uint8_t dataByte) { - uint16_t crc = crcSeed; - uint8_t dat = dataByte; - - dat ^= (uint8_t)(crc & 0xFFU); - dat ^= (dat << 4); - - crc = (crc >> 8) ^ (((uint16_t)dat) << 8) ^ (((uint16_t)dat) << 3) ^ (((uint16_t)dat) >> 4); - - return crc; -} - -static uint16_t seader_worker_picopass_calculate_ccitt( - uint16_t preloadValue, - const uint8_t* buf, - uint16_t length) { - uint16_t crc = preloadValue; - uint16_t index; - - for(index = 0; index < length; index++) { - crc = seader_worker_picopass_update_ccitt(crc, buf[index]); - } - - return crc; -} - -uint8_t read4Block6[] = {0x06, 0x06, 0x45, 0x56}; -uint8_t read4Block9[] = {0x06, 0x09, 0xB2, 0xAE}; -uint8_t read4Block10[] = {0x06, 0x0A, 0x29, 0x9C}; -uint8_t read4Block13[] = {0x06, 0x0D, 0x96, 0xE8}; -uint8_t updateBlock2[] = {0x87, 0x02}; // TODO - void seader_capture_sio(BitBuffer* tx_buffer, BitBuffer* rx_buffer, SeaderCredential* credential) { const uint8_t* buffer = bit_buffer_get_data(tx_buffer); size_t len = bit_buffer_get_size_bytes(tx_buffer); @@ -387,27 +455,6 @@ void seader_capture_sio(BitBuffer* tx_buffer, BitBuffer* rx_buffer, SeaderCreden } } -PicopassError seader_worker_fake_epurse_update(BitBuffer* tx_buffer, BitBuffer* rx_buffer) { - const uint8_t* buffer = bit_buffer_get_data(tx_buffer); - uint8_t fake_response[10]; - memset(fake_response, 0, sizeof(fake_response)); - memcpy(fake_response + 0, buffer + 6, 4); - memcpy(fake_response + 4, buffer + 2, 4); - - uint16_t crc = seader_worker_picopass_calculate_ccitt(0xE012, fake_response, 8); - memcpy(fake_response + 8, &crc, sizeof(uint16_t)); - - bit_buffer_append_bytes(rx_buffer, fake_response, sizeof(fake_response)); - - memset(display, 0, sizeof(display)); - for(uint8_t i = 0; i < sizeof(fake_response); i++) { - snprintf(display + (i * 2), sizeof(display), "%02x", fake_response[i]); - } - FURI_LOG_I(TAG, "Fake update E-Purse response: %s", display); - - return PicopassErrorNone; -} - void seader_iso15693_transmit( Seader* seader, PicopassPoller* picopass_poller, @@ -519,7 +566,9 @@ void seader_parse_nfc_command_transmit( frameProtocol); #endif - if(frameProtocol == FrameProtocol_iclass) { + if(seader->credential->type == SeaderCredentialTypeVirtual) { + seader_picopass_state_machine(seader, nfcSend->data.buf, nfcSend->data.size); + } else if(frameProtocol == FrameProtocol_iclass) { seader_iso15693_transmit( seader, spc->picopass_poller, nfcSend->data.buf, nfcSend->data.size); } else if(frameProtocol == FrameProtocol_nfc) { diff --git a/sam_api.h b/sam_api.h index ad63350..271d4b3 100644 --- a/sam_api.h +++ b/sam_api.h @@ -1,9 +1,14 @@ #pragma once +#include +#include +#include + #include "seader_i.h" #include "seader_credential.h" #include "seader_bridge.h" #include "seader_worker.h" +#include "protocol/rfal_picopass.h" #include diff --git a/scenes/seader_scene_config.h b/scenes/seader_scene_config.h index 2d8dcb5..58914c1 100644 --- a/scenes/seader_scene_config.h +++ b/scenes/seader_scene_config.h @@ -14,3 +14,4 @@ ADD_SCENE(seader, delete, Delete) ADD_SCENE(seader, delete_success, DeleteSuccess) ADD_SCENE(seader, credential_info, CredentialInfo) ADD_SCENE(seader, sam_info, SamInfo) +ADD_SCENE(seader, virtual_credential, VirtualCredential) diff --git a/scenes/seader_scene_credential_info.c b/scenes/seader_scene_credential_info.c index d8cb084..07e9071 100644 --- a/scenes/seader_scene_credential_info.c +++ b/scenes/seader_scene_credential_info.c @@ -36,7 +36,7 @@ void seader_scene_credential_info_on_enter(void* context) { furi_string_cat_printf(credential_str, "0x%llX", credential->credential); if(credential->type == SeaderCredentialTypeNone) { - furi_string_set(type_str, "None"); + furi_string_set(type_str, "Unknown"); } else if(credential->type == SeaderCredentialType14A) { furi_string_set(type_str, "14443A"); } else if(credential->type == SeaderCredentialTypePicopass) { diff --git a/scenes/seader_scene_read_card_success.c b/scenes/seader_scene_read_card_success.c index cc498ba..8942a0f 100644 --- a/scenes/seader_scene_read_card_success.c +++ b/scenes/seader_scene_read_card_success.c @@ -36,7 +36,9 @@ void seader_scene_read_card_success_on_enter(void* context) { furi_string_cat_printf(credential_str, "0x%llX", credential->credential); if(credential->type == SeaderCredentialTypeNone) { - furi_string_set(type_str, "None"); + furi_string_set(type_str, "Unknown"); + } else if(credential->type == SeaderCredentialTypeVirtual) { + furi_string_set(type_str, "Virtual"); } else if(credential->type == SeaderCredentialType14A) { furi_string_set(type_str, "14443A"); } else if(credential->type == SeaderCredentialTypePicopass) { diff --git a/scenes/seader_scene_saved_menu.c b/scenes/seader_scene_saved_menu.c index d749ae9..acea600 100644 --- a/scenes/seader_scene_saved_menu.c +++ b/scenes/seader_scene_saved_menu.c @@ -3,6 +3,7 @@ enum SubmenuIndex { SubmenuIndexDelete, SubmenuIndexInfo, + SubmenuIndexVirtual, }; void seader_scene_saved_menu_submenu_callback(void* context, uint32_t index) { @@ -13,14 +14,23 @@ void seader_scene_saved_menu_submenu_callback(void* context, uint32_t index) { void seader_scene_saved_menu_on_enter(void* context) { Seader* seader = context; + SeaderCredential* credential = seader->credential; Submenu* submenu = seader->submenu; - UNUSED(submenu); submenu_add_item( submenu, "Delete", SubmenuIndexDelete, seader_scene_saved_menu_submenu_callback, seader); submenu_add_item( submenu, "Info", SubmenuIndexInfo, seader_scene_saved_menu_submenu_callback, seader); + if(credential->sio[0] == 0x30) { + submenu_add_item( + submenu, + "Virtual", + SubmenuIndexVirtual, + seader_scene_saved_menu_submenu_callback, + seader); + } + submenu_set_selected_item( seader->submenu, scene_manager_get_scene_state(seader->scene_manager, SeaderSceneSavedMenu)); @@ -41,6 +51,9 @@ bool seader_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { } else if(event.event == SubmenuIndexInfo) { scene_manager_next_scene(seader->scene_manager, SeaderSceneCredentialInfo); consumed = true; + } else if(event.event == SubmenuIndexVirtual) { + scene_manager_next_scene(seader->scene_manager, SeaderSceneVirtualCredential); + consumed = true; } } diff --git a/scenes/seader_scene_virtual_credential.c b/scenes/seader_scene_virtual_credential.c new file mode 100644 index 0000000..d675939 --- /dev/null +++ b/scenes/seader_scene_virtual_credential.c @@ -0,0 +1,55 @@ +#include "../seader_i.h" +#include + +void seader_virtual_credential_worker_callback(SeaderWorkerEvent event, void* context) { + Seader* seader = context; + view_dispatcher_send_custom_event(seader->view_dispatcher, event); +} + +void seader_scene_virtual_credential_on_enter(void* context) { + Seader* seader = context; + + // Setup view + Popup* popup = seader->popup; + popup_set_header(popup, "Processing\nvirtual\npicopass", 68, 30, AlignLeft, AlignTop); + + // Start worker + seader->credential->type = SeaderCredentialTypeVirtual; + seader_worker_start( + seader->worker, + SeaderWorkerStateVirtualCredential, + seader->uart, + seader_virtual_credential_worker_callback, + seader); + + view_dispatcher_switch_to_view(seader->view_dispatcher, SeaderViewPopup); +} + +bool seader_scene_virtual_credential_on_event(void* context, SceneManagerEvent event) { + Seader* seader = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SeaderCustomEventWorkerExit) { + seader->credential->type = SeaderCredentialTypeVirtual; + scene_manager_next_scene(seader->scene_manager, SeaderSceneReadCardSuccess); + consumed = true; + } else if(event.event == SeaderCustomEventPollerSuccess) { + seader->credential->type = SeaderCredentialTypeVirtual; + scene_manager_next_scene(seader->scene_manager, SeaderSceneReadCardSuccess); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + scene_manager_search_and_switch_to_previous_scene( + seader->scene_manager, SeaderSceneSavedMenu); + consumed = true; + } + return consumed; +} + +void seader_scene_virtual_credential_on_exit(void* context) { + Seader* seader = context; + + // Clear view + popup_reset(seader->popup); +} diff --git a/seader_credential.c b/seader_credential.c index e6da596..c303391 100644 --- a/seader_credential.c +++ b/seader_credential.c @@ -9,17 +9,6 @@ #include #define TAG "SeaderCredential" -#define PICOPASS_BLOCK_LEN 8 - -#define CSN_INDEX 0 -#define CFG_INDEX 1 -#define EPURSE_INDEX 2 -#define KD_INDEX 3 -#define KC_INDEX 4 -#define AIA_INDEX 5 -#define PACS_CFG_INDEX 6 -#define PACS_INDEX 7 -#define SR_SIO_INDEX 10 static const char* seader_file_header = "Flipper Seader Credential"; static const uint32_t seader_file_version = 1; @@ -78,9 +67,11 @@ static bool seader_credential_load(SeaderCredential* cred, FuriString* path, boo // The order is reversed for storage and for the user opening the file uint64_t swapped = __builtin_bswap64(cred->credential); cred->credential = swapped; + // Optional SIO/Diversifier flipper_format_read_hex(file, "SIO", cred->sio, sizeof(cred->sio)); flipper_format_read_hex(file, "Diversifier", cred->diversifier, sizeof(cred->diversifier)); + parsed = true; } while(false); @@ -95,7 +86,9 @@ static bool seader_credential_load(SeaderCredential* cred, FuriString* path, boo dialog_message_show_storage_error(cred->dialogs, "Can not parse\nfile"); } } - FURI_LOG_I(TAG, "PACS: (%d) %016llx", cred->bit_length, cred->credential); + if(parsed) { + FURI_LOG_I(TAG, "PACS: (%d) %016llx", cred->bit_length, cred->credential); + } furi_string_free(temp_str); flipper_format_free(file); @@ -164,7 +157,7 @@ bool seader_credential_save_mfc(SeaderCredential* cred, const char* name) { 0x45, 0x41, 0x54}; - uint8_t sectorn_trailer[16] = { + uint8_t section_trailer[16] = { 0xff, 0xff, 0xff, @@ -312,8 +305,8 @@ bool seader_credential_save_mfc(SeaderCredential* cred, const char* name) { if(!flipper_format_write_hex( file, furi_string_get_cstr(temp_str), - sectorn_trailer, - sizeof(sectorn_trailer))) { + section_trailer, + sizeof(section_trailer))) { block_saved = false; } break; @@ -414,7 +407,7 @@ bool seader_credential_save(SeaderCredential* cred, const char* name) { furi_string_cat_printf(temp_str, "/%s%s", name, ".picopass"); } else { furi_string_printf( - temp_str, "%s/%s%s", STORAGE_APP_DATA_PATH_PREFIX, name, ".picopass"); + temp_str, "%s/%s%s", EXT_PATH("apps_data/picopass"), name, ".picopass"); } FURI_LOG_D(TAG, "Save as Picopass [%s]", furi_string_get_cstr(temp_str)); diff --git a/seader_credential.h b/seader_credential.h index ab9ffbf..038cc33 100644 --- a/seader_credential.h +++ b/seader_credential.h @@ -4,6 +4,7 @@ #include #include #include +#include "protocol/picopass_protocol.h" #define SEADER_CRED_NAME_MAX_LEN 22 #define SEADER_APP_EXTENSION ".credential" @@ -18,6 +19,7 @@ typedef enum { SeaderCredentialType14A, // Might need to make 14a into "javacard" and add Desfire SeaderCredentialTypeMifareClassic, + SeaderCredentialTypeVirtual, } SeaderCredentialType; typedef enum { diff --git a/seader_worker.c b/seader_worker.c index 9449298..da93742 100644 --- a/seader_worker.c +++ b/seader_worker.c @@ -151,13 +151,68 @@ bool seader_worker_process_sam_message(Seader* seader, CCID_Message* message) { return false; } +void seader_worker_virtual_credential(Seader* seader) { + SeaderWorker* seader_worker = seader->worker; + + // Detect card + seader_worker_card_detect( + seader, 0, NULL, seader->credential->diversifier, sizeof(PicopassSerialNum), NULL, 0); + + bool running = true; + // Max times the loop will run with no message to process + uint8_t dead_loops = 20; + + while(running) { + if(furi_mutex_acquire(seader_worker->mq_mutex, 0) == FuriStatusOk) { + uint32_t count = furi_message_queue_get_count(seader_worker->messages); + if(count > 0) { + FURI_LOG_D(TAG, "MessageQueue: %ld messages", count); + + SeaderAPDU seaderApdu = {}; + FuriStatus status = + furi_message_queue_get(seader_worker->messages, &seaderApdu, FuriWaitForever); + if(status != FuriStatusOk) { + FURI_LOG_W(TAG, "furi_message_queue_get fail %d", status); + view_dispatcher_send_custom_event( + seader->view_dispatcher, SeaderCustomEventWorkerExit); + } + if(seader_process_success_response_i( + seader, seaderApdu.buf, seaderApdu.len, true, NULL)) { + // no-op + } else { + FURI_LOG_I(TAG, "Response false"); + running = false; + } + } + furi_mutex_release(seader_worker->mq_mutex); + } else { + dead_loops--; + running = (dead_loops > 0); + FURI_LOG_D( + TAG, "Dead loops: %d -> Running: %s", dead_loops, running ? "true" : "false"); + } + running = (seader_worker->stage != SeaderPollerEventTypeComplete); + } + + if(dead_loops > 0) { + FURI_LOG_D(TAG, "Final dead loops: %d", dead_loops); + view_dispatcher_send_custom_event(seader->view_dispatcher, SeaderCustomEventPollerSuccess); + } else { + view_dispatcher_send_custom_event(seader->view_dispatcher, SeaderCustomEventWorkerExit); + } +} + int32_t seader_worker_task(void* context) { SeaderWorker* seader_worker = context; + Seader* seader = seader_worker->context; SeaderUartBridge* seader_uart = seader_worker->uart; if(seader_worker->state == SeaderWorkerStateCheckSam) { FURI_LOG_D(TAG, "Check for SAM"); seader_ccid_check_for_sam(seader_uart); + } else if(seader_worker->state == SeaderWorkerStateVirtualCredential) { + FURI_LOG_D(TAG, "Virtual Credential"); + seader_worker_virtual_credential(seader); } seader_worker_change_state(seader_worker, SeaderWorkerStateReady); diff --git a/seader_worker.h b/seader_worker.h index 92b9301..9868b20 100644 --- a/seader_worker.h +++ b/seader_worker.h @@ -17,6 +17,7 @@ typedef enum { SeaderWorkerStateReady, // Main worker states SeaderWorkerStateCheckSam, + SeaderWorkerStateVirtualCredential, // Transition SeaderWorkerStateStop, } SeaderWorkerState;