Merge branch 'master' into master

Signed-off-by: Jarek Barwinski <116510448+jareckib@users.noreply.github.com>
This commit is contained in:
Jarek Barwinski
2025-03-17 23:00:28 +00:00
committed by GitHub
124 changed files with 3736 additions and 2282 deletions
+7
View File
@@ -45,6 +45,13 @@ jobs:
- name: Install dependencies
run: sudo apt-get install -yqq make autoconf build-essential ca-certificates pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev qtbase5-dev libbz2-dev liblz4-dev libbluetooth-dev libpython3-dev python3 python3-dev libpython3-all-dev liblua5.4-dev liblua5.4-0 lua5.4 sed libssl-dev
- name: Install Python dependencies
run: |
python3 -m pip install --upgrade pip
python3 -m pip install setuptools
python3 -m pip install ansicolors sslcrypto
if [ -f requirements.txt ]; then python3 -m pip install -r requirements.txt; fi
- name: Checkout repository
uses: actions/checkout@v4
+17
View File
@@ -3,6 +3,23 @@ All notable changes to this project will be documented in this file.
This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log...
## [unreleased][unreleased]
- Improved `pcf7931` generic readability of the code. Unified datatypes and added documentation/explainations (@tinooo)
- Improved `lf pcf7931` read code - fixed some checks for more stability (@tinooo)
- Changed `trace list -t seos` - improved annotation (@iceman1001)
- Added `make commands` to regenerate commands documentation files and autocompletion data independently of `make style` (@doegox)
- Added texecom identification, thanks @en4rab ! (@iceman1001)
- Added `hf 15 slixprotectpage` command
- Fixed `hf mf gload` - missing parameter (@iceman1001)
- Changed `hf mf gload` - now handles 1k ev1 sized dumps (@iceman1001)
- Changed wiegand format unpack functions to clear struct later (@iceman1001)
- Changed `wiegand decode` - now accepts new padding format (@iceman1001)
- Changed `mem spiffs tree` - ID is now shown in decimal (@iceman1001)
- Added sample wiegand format 56bit (@iceman1001)
- Changed Wiegand formats to include number of bits (@iceman1001)
- Fix compilation warning in hitagS (@iceman1001)
- Added new wiegand format H800002 (@jmichelp)
- Changed `Makefile.platform.sample` file - now have clear instructions for generating images for other proxmark3 hardware (@iceman1001)
- Changed `doc/magic_cards_notes.md` - now contains documentation for iKey LLC's MF4 tag (@team-orangeBlue)
- Changed `hf mf cload` - now accepts MFC Ev1 sized dumps (@iceman1001)
- Changed `hf mfu info` - now properly identify ULEv1 AES 50pF (@iceman1001)
- Changed `hf mf info` - now differentiates between full USCUID and cut down ZUID chips (@nvx)
+4 -2
View File
@@ -204,6 +204,7 @@ help:
@echo "+ fpga_compress - Make tools/fpga_compress"
@echo
@echo "+ style - Apply some automated source code formatting rules"
@echo "+ commands - Regenerate commands documentation files and autocompletion data
@echo "+ check - Run offline tests. Set CHECKARGS to pass arguments to the test script"
@echo "+ .../check - Run offline tests against specific target. See above."
@echo "+ miscchecks - Detect various encoding issues in source code"
@@ -303,7 +304,7 @@ endif
# easy printing of MAKE VARIABLES
print-%: ; @echo $* = $($*)
style:
style: commands
# Make sure astyle is installed
@command -v astyle >/dev/null || ( echo "Please install 'astyle' package first" ; exit 1 )
# Remove spaces & tabs at EOL, add LF at EOF if needed on *.c, *.h, *.cpp. *.lua, *.py, *.pl, Makefile, *.v, pm3
@@ -317,13 +318,14 @@ style:
--keep-one-line-blocks --max-continuation-indent=60 \
--style=google --pad-oper --unpad-paren --pad-header \
--align-pointer=name {} \;
commands:
# Update commands.md
[ -x client/proxmark3 ] && client/proxmark3 -m | tr -d '\r' > doc/commands.md
# Make sure python3 is installed
@command -v python3 >/dev/null || ( echo "Please install 'python3' package first" ; exit 1 )
# Update commands.json, patch port in case it was run under Windows
[ -x client/proxmark3 ] && client/proxmark3 --fulltext | sed 's#com[0-9]#/dev/ttyACM0#'|python3 client/pyscripts/pm3_help2json.py - - | tr -d '\r' > doc/commands.json
# Update the readline autocomplete autogenerated code
[ -x client/proxmark3 ] && client/proxmark3 --fulltext | python3 client/pyscripts/pm3_help2list.py - - | tr -d '\r' > client/src/pm3line_vocabulary.h
+13
View File
@@ -1,8 +1,20 @@
# If you want to use it, copy this file as Makefile.platform and adjust it to your needs
# Run 'make PLATFORM=' to get an exhaustive list of possible parameters for this file.
# By default PM3 RDV4 image is generated.
# Comment the line below and uncomment further down according to which device you have
PLATFORM=PM3RDV4
# For PM3 Easy:
# uncomment the line below
#PLATFORM=PM3GENERIC
# For ICOPY-X and PM3 Max:
# uncomment the two lines below
#PLATFORM=PM3ICOPY3
#PLATFORM_EXTRAS=FLASH
# If you want more than one PLATFORM_EXTRAS option, separate them by spaces:
#PLATFORM_EXTRAS=BTADDON
#PLATFORM_EXTRAS=FLASH
@@ -10,6 +22,7 @@ PLATFORM=PM3RDV4
#PLATFORM_EXTRAS=BTADDON FLASH
#STANDALONE=LF_SAMYRUN
# Uncomment the line below to set the correct LED order on board Proxmark3 Easy
# Only available with PLATFORM=PM3GENERIC
#LED_ORDER=PM3EASY
+4 -2
View File
@@ -321,9 +321,10 @@ bool RAMFUNC LogTraceBits(const uint8_t *btBytes, uint16_t bitLen, uint32_t time
// Emulator memory
int emlSet(const uint8_t *data, uint32_t offset, uint32_t length) {
uint8_t *mem = BigBuf_get_EM_addr();
if (!mem) {
if (mem == NULL) {
return PM3_EMALLOC;
}
if (offset + length <= CARD_MEMORY_SIZE) {
memcpy(mem + offset, data, length);
return PM3_SUCCESS;
@@ -335,9 +336,10 @@ int emlSet(const uint8_t *data, uint32_t offset, uint32_t length) {
int emlGet(uint8_t *out, uint32_t offset, uint32_t length) {
uint8_t *mem = BigBuf_get_EM_addr();
if (!mem) {
if (mem == NULL) {
return PM3_EMALLOC;
}
if (offset + length <= CARD_MEMORY_SIZE) {
memcpy(out, mem + offset, length);
return PM3_SUCCESS;
+4 -4
View File
@@ -311,7 +311,7 @@ void WriteTagToFlash(uint32_t uid, size_t size) {
uint32_t len = size;
uint8_t data[(size * (16 * 64)) / 1024];
emlGetMem(data, 0, (size * 64) / 1024);
emlGetMem_xt(data, 0, (size * 64) / 1024, MIFARE_BLOCK_SIZE);
char dest[SPIFFS_OBJ_NAME_LEN];
uint8_t buid[4];
@@ -646,7 +646,7 @@ failtag:
emlClearMem();
uint8_t mblock[16];
for (uint8_t sectorNo = 0; sectorNo < sectorsCnt; sectorNo++) {
emlGetMem(mblock, FirstBlockOfSector(sectorNo) + NumBlocksPerSector(sectorNo) - 1, 1);
emlGetMem_xt(mblock, FirstBlockOfSector(sectorNo) + NumBlocksPerSector(sectorNo) - 1, 1, MIFARE_BLOCK_SIZE);
for (uint8_t t = 0; t < 2; t++) {
memcpy(mblock + t * 10, foundKey[t][sectorNo], 6);
}
@@ -807,7 +807,7 @@ int e_MifareECardLoad(uint32_t numofsectors, uint8_t keytype) {
emlSetMem_xt(dataoutbuf, FirstBlockOfSector(s) + blockNo, 1, 16);
} else {
// sector trailer, keep the keys, set only the AC
emlGetMem(dataoutbuf2, FirstBlockOfSector(s) + blockNo, 1);
emlGetMem_xt(dataoutbuf2, FirstBlockOfSector(s) + blockNo, 1, MIFARE_BLOCK_SIZE);
memcpy(&dataoutbuf2[6], &dataoutbuf[6], 4);
emlSetMem_xt(dataoutbuf2, FirstBlockOfSector(s) + blockNo, 1, 16);
}
@@ -878,7 +878,7 @@ void saMifareMakeTag(void) {
int flags = 0;
for (int blockNum = 0; blockNum < 16 * 4; blockNum++) {
uint8_t mblock[16];
emlGetMem(mblock, blockNum, 1);
emlGetMem_xt(mblock, blockNum, 1, MIFARE_BLOCK_SIZE);
// switch on field and send magic sequence
if (blockNum == 0)
flags = 0x08 + 0x02;
+1 -1
View File
@@ -500,7 +500,7 @@ void RunMod(void) {
uint8_t mblock[MIFARE_BLOCK_SIZE];
for (uint8_t sectorNo = 0; sectorNo < sectorsCnt; ++sectorNo) {
if (validKey[0][sectorNo] || validKey[1][sectorNo]) {
emlGetMem(mblock, FirstBlockOfSector(sectorNo) + NumBlocksPerSector(sectorNo) - 1, 1);
emlGetMem_xt(mblock, FirstBlockOfSector(sectorNo) + NumBlocksPerSector(sectorNo) - 1, 1, MIFARE_BLOCK_SIZE);
for (uint8_t keyType = 0; keyType < 2; ++keyType) {
if (validKey[keyType][sectorNo]) {
memcpy(mblock + keyType * 10, foundKey[keyType][sectorNo], 6);
+1 -1
View File
@@ -445,7 +445,7 @@ void RunMod(void) {
// received a RATS request
} else if (receivedCmd[0] == ISO14443A_CMD_RATS && len == 4) {
prevCmd = 0;
p_response = &responses[RESP_INDEX_RATS];
p_response = &responses[RESP_INDEX_ATS];
} else {
if (g_dbglevel == DBG_DEBUG) {
+1 -1
View File
@@ -338,7 +338,7 @@ void RunMod() {
} else if (receivedCmd[1] == 0x70 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && len == 9) { // Received a SELECT (cascade 1)
p_response = &responses[RESP_INDEX_SAKC1];
} else if (receivedCmd[0] == ISO14443A_CMD_RATS && len == 4) { // Received a RATS request
p_response = &responses[RESP_INDEX_RATS];
p_response = &responses[RESP_INDEX_ATS];
resp = 1;
} else if (receivedCmd[0] == 0xf2 && len == 4) { // ACKed - Time extension
DbpString(_YELLOW_("!!") " Reader accepted time extension!");
+2 -2
View File
@@ -247,7 +247,7 @@ void RunMod(void) {
} else if (receivedCmd[1] == 0x70 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_2 && len == 9) { // Received a SELECT (cascade 2)
p_response = &responses[RESP_INDEX_SAKC2];
} else if (receivedCmd[0] == ISO14443A_CMD_RATS && len == 4) { // Received a RATS request
p_response = &responses[RESP_INDEX_RATS];
p_response = &responses[RESP_INDEX_ATS];
} else if (receivedCmd[0] == ISO14443A_CMD_PPS) {
p_response = &responses[RESP_INDEX_PPS];
} else {
@@ -425,7 +425,7 @@ void RunMod(void) {
} else if (receivedCmd[1] == 0x70 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_2 && len == 9) { // Received a SELECT (cascade 2)
p_response = &responses[RESP_INDEX_SAKC2];
} else if (receivedCmd[0] == ISO14443A_CMD_RATS && len == 4) { // Received a RATS request
p_response = &responses[RESP_INDEX_RATS];
p_response = &responses[RESP_INDEX_ATS];
} else if (receivedCmd[0] == ISO14443A_CMD_PPS) {
p_response = &responses[RESP_INDEX_PPS];
} else {
+2 -1
View File
@@ -257,7 +257,7 @@ void hid_corporate_1000_calculate_checksum_and_set(uint32_t *high, uint32_t *low
// Calculate new high and low base value from card number and facility code, without parity
new_low = (fc << 21) | (cardnum << 1);
new_high = 0x28 | ((fc >> 11) & 1); // 0x28 is 101000
new_high = (fc >> 11) & 1;
int n_ones;
uint32_t i;
@@ -319,6 +319,7 @@ void hid_corporate_1000_calculate_checksum_and_set(uint32_t *high, uint32_t *low
new_high = new_high | 0x4;
// Setting new calculated values
add_HID_preamble(0, &new_high, &new_low, 35);
*low = new_low;
*high = new_high;
}
+1 -2
View File
@@ -176,8 +176,7 @@ void hid_calculate_checksum_and_set(uint32_t *high, uint32_t *low, uint32_t card
newlow |= oddparity32((newlow >> 1) & 0xFFF);
newlow |= (evenparity32((newlow >> 13) & 0xFFF)) << 25;
newhigh |= 0x20; // Bit 37; standard header
newlow |= 1U << 26; // leading 1: start bit
add_HID_preamble(NULL, &newhigh, &newlow, 26);
*low = newlow;
*high = newhigh;
+4 -4
View File
@@ -16,8 +16,8 @@
//-----------------------------------------------------------------------------
// LF HID ProxII Brutforce v2 by lnv42 - based on Proxbrute by Brad antoniewicz
//
// Following code is a trivial brute forcer for when you know the facility
// code and want to find valid(s) card number(s). It will try all card
// Following code is a trivial brute forcer (H10301 26-bit) when you know the
// facility code and want to find valid(s) card number(s). It will try all card
// fnumbers rom CARDNUM_START to CARDNUM_END one by one (max. ~65k tries).
// This brute force will be a lot faster than Proxbrute that will try all
// possibles values for LF low, even those with bad checksum (~4g tries).
@@ -46,8 +46,7 @@ void RunMod(void) {
StandAloneMode();
Dbprintf(">> LF HID proxII bruteforce v2 a.k.a Prox2Brute Started <<");
FpgaDownloadAndGo(FPGA_BITSTREAM_LF);
const uint32_t high = 0x20; // LF high value is always 0x20 here
uint32_t high = 0;
uint32_t fac = FACILITY_CODE, cardnum = 0;
@@ -82,6 +81,7 @@ void RunMod(void) {
uint32_t low = (cardnum << 1) | (fac << 17);
low |= oddparity32((low >> 1) & 0xFFF);
low |= evenparity32((low >> 13) & 0xFFF) << 25;
add_HID_preamble(NULL, &high, &low, 26);
Dbprintf("[=] trying Facility = %08x, Card = %08x, raw = %08x%08x",
fac, cardnum, high, low);
+67 -22
View File
@@ -1370,7 +1370,11 @@ static void PacketReceived(PacketCommandNG *packet) {
// involved in dealing with emulator memory. But if it is called later, it might
// destroy the Emulator Memory.
//-----------------------------------------------------------------------------
EmlClearIso15693();
// Resetting the bitstream also frees the BigBuf memory, so we do this here to prevent
// an inconvenient reset in the future by Iso15693InitTag
FpgaDownloadAndGo(FPGA_BITSTREAM_HF_15);
BigBuf_Clear_EM();
reply_ng(CMD_HF_ISO15693_EML_CLEAR, PM3_SUCCESS, NULL, 0);
break;
}
case CMD_HF_ISO15693_EML_SETMEM: {
@@ -1402,7 +1406,7 @@ static void PacketReceived(PacketCommandNG *packet) {
return;
}
uint8_t *buf = BigBuf_malloc(payload->length);
uint8_t *buf = BigBuf_calloc(payload->length);
emlGet(buf, payload->offset, payload->length);
LED_B_ON();
reply_ng(CMD_HF_ISO15693_EML_GETMEM, PM3_SUCCESS, buf, payload->length);
@@ -1463,6 +1467,17 @@ static void PacketReceived(PacketCommandNG *packet) {
WritePasswordSlixIso15693(payload->old_pwd, payload->new_pwd, payload->pwd_id);
break;
}
case CMD_HF_ISO15693_SLIX_PROTECT_PAGE: {
struct p {
uint8_t read_pwd[4];
uint8_t write_pwd[4];
uint8_t divide_ptr;
uint8_t prot_status;
} PACKED;
struct p *payload = (struct p *) packet->data.asBytes;
ProtectPageSlixIso15693(payload->read_pwd, payload->write_pwd, payload->divide_ptr, payload->prot_status);
break;
}
case CMD_HF_ISO15693_SLIX_DISABLE_PRIVACY: {
struct p {
uint8_t pwd[4];
@@ -1677,7 +1692,7 @@ static void PacketReceived(PacketCommandNG *packet) {
EMVsim(payload->flags, payload->exitAfter, payload->uid, payload->atqa, payload->sak);
break;
}
#endif
#endif
case CMD_HF_ISO14443A_SIMULATE: {
struct p {
uint8_t tagtype;
@@ -1696,20 +1711,21 @@ static void PacketReceived(PacketCommandNG *packet) {
uint8_t tagtype;
uint16_t flags;
uint8_t uid[10];
uint8_t rats[20];
uint8_t ats[20];
uint8_t aid[30];
uint8_t response[100];
uint8_t apdu[100];
int aid_len;
int respond_len;
int apdu_len;
bool enumerate;
uint8_t selectaid_response[100];
uint8_t getdata_response[100];
uint32_t ats_len;
uint32_t aid_len;
uint32_t selectaid_response_len;
uint32_t getdata_response_len;
} PACKED;
struct p *payload = (struct p *) packet->data.asBytes;
// ## Simulate iso14443a tag - pass tag type, UID, ATS, AID, responses
SimulateIso14443aTagAID(payload->tagtype, payload->flags, payload->uid,
payload->rats, sizeof(payload->rats), payload->aid, payload->response,
payload->apdu, payload->aid_len, payload->respond_len,
payload->apdu_len, payload->enumerate); // ## Simulate iso14443a tag - pass tag type, UID, rats, aid, resp, apdu
payload->ats, payload->ats_len, payload->aid, payload->aid_len,
payload->selectaid_response, payload->selectaid_response_len,
payload->getdata_response, payload->getdata_response_len);
break;
}
case CMD_HF_ISO14443A_ANTIFUZZ: {
@@ -1889,36 +1905,65 @@ static void PacketReceived(PacketCommandNG *packet) {
break;
}
case CMD_HF_MIFARE_EML_MEMCLR: {
MifareEMemClr();
reply_ng(CMD_HF_MIFARE_EML_MEMCLR, PM3_SUCCESS, NULL, 0);
//-----------------------------------------------------------------------------
// Work with emulator memory
//
// Note: we call FpgaDownloadAndGo(FPGA_BITSTREAM_HF) here although FPGA is not
// involved in dealing with emulator memory. But if it is called later, it might
// destroy the Emulator Memory.
//-----------------------------------------------------------------------------
FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
// Not only clears the emulator memory,
// also sets default MIFARE values for sector trailers.
emlClearMem();
reply_ng(CMD_HF_MIFARE_EML_MEMCLR, PM3_SUCCESS, NULL, 0);
break;
}
case CMD_HF_MIFARE_EML_MEMSET: {
FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
struct p {
uint8_t blockno;
uint16_t blockno;
uint8_t blockcnt;
uint8_t blockwidth;
uint8_t data[];
} PACKED;
struct p *payload = (struct p *) packet->data.asBytes;
FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
// backwards compat... default bytewidth
if (payload->blockwidth == 0)
payload->blockwidth = 16;
if (payload->blockwidth == 0) {
payload->blockwidth = MIFARE_BLOCK_SIZE;
}
emlSetMem_xt(payload->data, payload->blockno, payload->blockcnt, payload->blockwidth);
break;
}
case CMD_HF_MIFARE_EML_MEMGET: {
FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
struct p {
uint8_t blockno;
uint16_t blockno;
uint8_t blockcnt;
uint8_t blockwidth;
} PACKED;
struct p *payload = (struct p *) packet->data.asBytes;
MifareEMemGet(payload->blockno, payload->blockcnt);
//
size_t size = payload->blockno * payload->blockwidth;
if (size > PM3_CMD_DATA_SIZE) {
reply_ng(CMD_HF_MIFARE_EML_MEMGET, PM3_EMALLOC, NULL, 0);
return;
}
uint8_t *buf = BigBuf_calloc(size);
emlGetMem_xt(buf, payload->blockno, payload->blockcnt, payload->blockwidth); // data, block num, blocks count (max 4)
LED_B_ON();
reply_ng(CMD_HF_MIFARE_EML_MEMGET, PM3_SUCCESS, buf, size);
LED_B_OFF();
BigBuf_free_keep_EM();
break;
}
case CMD_HF_MIFARE_EML_LOAD: {
+107 -74
View File
@@ -133,7 +133,9 @@ void tdes_nxp_send(const void *in, void *out, size_t length, const void *key, ui
}
void aes128_nxp_receive(const void *in, void *out, size_t length, const void *key, unsigned char iv[16]) {
if (length % 8) return;
if (length % 8) {
return;
}
uint8_t *tin = (uint8_t *) in;
uint8_t *tout = (uint8_t *) out;
@@ -143,7 +145,9 @@ void aes128_nxp_receive(const void *in, void *out, size_t length, const void *ke
}
void aes128_nxp_send(const void *in, void *out, size_t length, const void *key, unsigned char iv[16]) {
if (length % 8) return;
if (length % 8) {
return;
}
uint8_t *tin = (uint8_t *) in;
uint8_t *tout = (uint8_t *) out;
@@ -152,12 +156,15 @@ void aes128_nxp_send(const void *in, void *out, size_t length, const void *key,
mbedtls_aes_crypt_cbc(&actx, MBEDTLS_AES_ENCRYPT, length, iv, tin, tout);
}
void Desfire_des_key_new(const uint8_t value[8], desfirekey_t key) {
uint8_t data[8];
memcpy(data, value, 8);
for (int n = 0; n < 8; n++) {
void Desfire_des_key_new(const uint8_t *value, desfirekey_t key) {
uint8_t data[8] = {0};
memcpy(data, value, sizeof(data));
for (size_t n = 0; n < sizeof(data); n++) {
data[n] &= 0xFE;
}
Desfire_des_key_new_with_version(data, key);
}
@@ -246,22 +253,24 @@ void Desfire_key_set_version(desfirekey_t key, uint8_t version) {
void Desfire_session_key_new(const uint8_t rnda[], const uint8_t rndb[], desfirekey_t authkey, desfirekey_t key) {
uint8_t buffer[24];
uint8_t buffer[24] = {0};
switch (authkey->type) {
case T_DES:
case T_DES: {
memcpy(buffer, rnda, 4);
memcpy(buffer + 4, rndb, 4);
Desfire_des_key_new_with_version(buffer, key);
break;
case T_3DES:
}
case T_3DES: {
memcpy(buffer, rnda, 4);
memcpy(buffer + 4, rndb, 4);
memcpy(buffer + 8, rnda + 4, 4);
memcpy(buffer + 12, rndb + 4, 4);
Desfire_3des_key_new_with_version(buffer, key);
break;
case T_3K3DES:
}
case T_3K3DES: {
memcpy(buffer, rnda, 4);
memcpy(buffer + 4, rndb, 4);
memcpy(buffer + 8, rnda + 6, 4);
@@ -270,22 +279,15 @@ void Desfire_session_key_new(const uint8_t rnda[], const uint8_t rndb[], desfire
memcpy(buffer + 20, rndb + 12, 4);
Desfire_3k3des_key_new(buffer, key);
break;
case T_AES:
}
case T_AES: {
memcpy(buffer, rnda, 4);
memcpy(buffer + 4, rndb, 4);
memcpy(buffer + 8, rnda + 12, 4);
memcpy(buffer + 12, rndb + 12, 4);
Desfire_aes_key_new(buffer, key);
break;
}
}
static size_t key_macing_length(desfirekey_t key);
// iceman, see memxor inside string.c, dest/src swapped..
static void xor(const uint8_t *ivect, uint8_t *data, const size_t len) {
for (size_t i = 0; i < len; i++) {
data[i] ^= ivect[i];
}
}
}
@@ -306,7 +308,7 @@ void cmac_generate_subkeys(desfirekey_t key) {
// Used to compute CMAC on complete blocks
memcpy(key->cmac_sk1, l, kbs);
txor = l[0] & 0x80;
txor = (l[0] & 0x80);
lsl(key->cmac_sk1, kbs);
@@ -317,7 +319,7 @@ void cmac_generate_subkeys(desfirekey_t key) {
// Used to compute CMAC on the last block if non-complete
memcpy(key->cmac_sk2, key->cmac_sk1, kbs);
txor = key->cmac_sk1[0] & 0x80;
txor = (key->cmac_sk1[0] & 0x80);
lsl(key->cmac_sk2, kbs);
@@ -341,15 +343,14 @@ void cmac(const desfirekey_t key, uint8_t *ivect, const uint8_t *data, size_t le
while (len % kbs) {
buffer[len++] = 0x00;
}
xor(key->cmac_sk2, buffer + len - kbs, kbs);
xor(buffer + len - kbs, key->cmac_sk2, kbs);
} else {
xor(key->cmac_sk1, buffer + len - kbs, kbs);
xor(buffer + len - kbs, key->cmac_sk1, kbs);
}
mifare_cypher_blocks_chained(NULL, key, ivect, buffer, len, MCD_SEND, MCO_ENCYPHER);
memcpy(cmac, ivect, kbs);
//free(buffer);
}
size_t key_block_size(const desfirekey_t key) {
@@ -374,7 +375,7 @@ size_t key_block_size(const desfirekey_t key) {
/*
* Size of MACing produced with the key.
*/
static size_t key_macing_length(const desfirekey_t key) {
size_t key_macing_length(const desfirekey_t key) {
size_t mac_length = DESFIRE_MAC_LENGTH;
switch (key->type) {
case T_DES:
@@ -393,10 +394,11 @@ static size_t key_macing_length(const desfirekey_t key) {
* Size required to store nbytes of data in a buffer of size n*block_size.
*/
size_t padded_data_length(const size_t nbytes, const size_t block_size) {
if ((!nbytes) || (nbytes % block_size))
if ((!nbytes) || (nbytes % block_size)) {
return ((nbytes / block_size) + 1) * block_size;
else
} else {
return nbytes;
}
}
/*
@@ -412,12 +414,14 @@ size_t enciphered_data_length(const desfiretag_t tag, const size_t nbytes, int c
size_t crc_length = 0;
if (!(communication_settings & NO_CRC)) {
switch (DESFIRE(tag)->authentication_scheme) {
case AS_LEGACY:
case AS_LEGACY: {
crc_length = 2;
break;
case AS_NEW:
}
case AS_NEW: {
crc_length = 4;
break;
}
}
}
@@ -428,18 +432,20 @@ size_t enciphered_data_length(const desfiretag_t tag, const size_t nbytes, int c
void *mifare_cryto_preprocess_data(desfiretag_t tag, void *data, size_t *nbytes, size_t offset, int communication_settings) {
uint8_t *res = data;
uint8_t mac[4];
uint8_t mac[4] = {0};
size_t edl;
bool append_mac = true;
desfirekey_t key = DESFIRE(tag)->session_key;
if (!key)
desfirekey_t key = DESFIRE(tag)->session_key;
if (!key) {
return data;
}
switch (communication_settings & MDCM_MASK) {
case MDCM_PLAIN:
if (AS_LEGACY == DESFIRE(tag)->authentication_scheme)
case MDCM_PLAIN: {
if (AS_LEGACY == DESFIRE(tag)->authentication_scheme) {
break;
}
/*
* When using new authentication methods, PLAIN data transmission from
@@ -452,13 +458,16 @@ void *mifare_cryto_preprocess_data(desfiretag_t tag, void *data, size_t *nbytes,
*/
append_mac = false;
}
/* pass through */
case MDCM_MACED:
case MDCM_MACED: {
switch (DESFIRE(tag)->authentication_scheme) {
case AS_LEGACY:
if (!(communication_settings & MAC_COMMAND))
case AS_LEGACY: {
if (!(communication_settings & MAC_COMMAND)) {
break;
}
/* pass through */
edl = padded_data_length(*nbytes - offset, key_block_size(DESFIRE(tag)->session_key)) + offset;
@@ -475,8 +484,9 @@ void *mifare_cryto_preprocess_data(desfiretag_t tag, void *data, size_t *nbytes,
// Copy again provided data (was overwritten by mifare_cypher_blocks_chained)
memcpy(res, data, *nbytes);
if (!(communication_settings & MAC_COMMAND))
if (!(communication_settings & MAC_COMMAND)) {
break;
}
// Append MAC
size_t bla = maced_data_length(DESFIRE(tag)->session_key, *nbytes - offset) + offset;
(void)bla++;
@@ -485,9 +495,12 @@ void *mifare_cryto_preprocess_data(desfiretag_t tag, void *data, size_t *nbytes,
*nbytes += 4;
break;
case AS_NEW:
if (!(communication_settings & CMAC_COMMAND))
}
case AS_NEW: {
if (!(communication_settings & CMAC_COMMAND)) {
break;
}
cmac(key, DESFIRE(tag)->ivect, res, *nbytes, DESFIRE(tag)->cmac);
if (append_mac) {
@@ -498,10 +511,11 @@ void *mifare_cryto_preprocess_data(desfiretag_t tag, void *data, size_t *nbytes,
*nbytes += DESFIRE_CMAC_LENGTH;
}
break;
}
}
break;
case MDCM_ENCIPHERED:
}
case MDCM_ENCIPHERED: {
/* |<-------------- data -------------->|
* |<--- offset -->| |
* +---------------+--------------------+-----+---------+
@@ -517,8 +531,10 @@ void *mifare_cryto_preprocess_data(desfiretag_t tag, void *data, size_t *nbytes,
* encypher()/decypher()
*/
if (!(communication_settings & ENC_COMMAND))
if (!(communication_settings & ENC_COMMAND)) {
break;
}
edl = enciphered_data_length(tag, *nbytes - offset, communication_settings) + offset;
// Fill in the crypto buffer with data ...
@@ -526,14 +542,16 @@ void *mifare_cryto_preprocess_data(desfiretag_t tag, void *data, size_t *nbytes,
if (!(communication_settings & NO_CRC)) {
// ... CRC ...
switch (DESFIRE(tag)->authentication_scheme) {
case AS_LEGACY:
case AS_LEGACY: {
AddCrc14A(res + offset, *nbytes - offset);
*nbytes += 2;
break;
case AS_NEW:
}
case AS_NEW: {
crc32_append(res, *nbytes);
*nbytes += 4;
break;
}
}
}
// ... and padding
@@ -543,32 +561,34 @@ void *mifare_cryto_preprocess_data(desfiretag_t tag, void *data, size_t *nbytes,
mifare_cypher_blocks_chained(tag, NULL, NULL, res + offset, *nbytes - offset, MCD_SEND, (AS_NEW == DESFIRE(tag)->authentication_scheme) ? MCO_ENCYPHER : MCO_DECYPHER);
break;
default:
}
default: {
*nbytes = -1;
res = NULL;
break;
}
}
return res;
}
void *mifare_cryto_postprocess_data(desfiretag_t tag, void *data, size_t *nbytes, int communication_settings) {
void *res = data;
uint8_t first_cmac_byte = 0x00;
desfirekey_t key = DESFIRE(tag)->session_key;
if (!key) {
return data;
}
// Return directly if we just have a status code.
if (1 == *nbytes) {
return res;
}
desfirekey_t key = DESFIRE(tag)->session_key;
if (!key) {
return data;
}
uint8_t first_cmac_byte = 0x00;
switch (communication_settings & MDCM_MASK) {
case MDCM_PLAIN: {
@@ -659,11 +679,12 @@ void *mifare_cryto_postprocess_data(desfiretag_t tag, void *data, size_t *nbytes
break;
}
case MDCM_ENCIPHERED: {
(*nbytes)--;
bool verified = false;
int crc_pos = 0x00;
int end_crc_pos = 0x00;
uint8_t x;
/*
* AS_LEGACY:
@@ -742,8 +763,9 @@ void *mifare_cryto_postprocess_data(desfiretag_t tag, void *data, size_t *nbytes
verified = true;
for (int n = end_crc_pos; n < *nbytes - 1; n++) {
uint8_t byte = ((uint8_t *)res)[n];
if (!((0x00 == byte) || ((0x80 == byte) && (n == end_crc_pos))))
if (!((0x00 == byte) || ((0x80 == byte) && (n == end_crc_pos)))) {
verified = false;
}
}
}
@@ -768,7 +790,7 @@ void *mifare_cryto_postprocess_data(desfiretag_t tag, void *data, size_t *nbytes
break;
}
case AS_NEW: {
x = ((uint8_t *)res)[crc_pos - 1];
uint8_t x = ((uint8_t *)res)[crc_pos - 1];
((uint8_t *)res)[crc_pos - 1] = ((uint8_t *)res)[crc_pos];
((uint8_t *)res)[crc_pos] = x;
break;
@@ -802,9 +824,10 @@ void *mifare_cryto_postprocess_data(desfiretag_t tag, void *data, size_t *nbytes
void mifare_cypher_single_block(desfirekey_t key, uint8_t *data, uint8_t *ivect, MifareCryptoDirection direction, MifareCryptoOperation operation, size_t block_size) {
uint8_t ovect[DESFIRE_MAX_CRYPTO_BLOCK_SIZE];
if (direction == MCD_SEND) {
xor(ivect, data, block_size);
xor(data, ivect, block_size);
} else {
memcpy(ovect, data, block_size);
}
@@ -812,70 +835,80 @@ void mifare_cypher_single_block(desfirekey_t key, uint8_t *data, uint8_t *ivect,
uint8_t edata[DESFIRE_MAX_CRYPTO_BLOCK_SIZE] = {0};
switch (key->type) {
case T_DES:
case T_DES: {
switch (operation) {
case MCO_ENCYPHER:
case MCO_ENCYPHER: {
//DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_ENCRYPT);
des_encrypt(edata, data, key->data);
break;
case MCO_DECYPHER:
}
case MCO_DECYPHER: {
//DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_DECRYPT);
des_decrypt(edata, data, key->data);
break;
}
}
break;
case T_3DES:
}
case T_3DES: {
switch (operation) {
case MCO_ENCYPHER:
case MCO_ENCYPHER: {
mbedtls_des3_set2key_enc(&ctx3, key->data);
mbedtls_des3_crypt_ecb(&ctx3, data, edata);
// DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_ENCRYPT);
// DES_ecb_encrypt ((DES_cblock *) edata, (DES_cblock *) data, &(key->ks2), DES_DECRYPT);
// DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_ENCRYPT);
break;
case MCO_DECYPHER:
}
case MCO_DECYPHER: {
mbedtls_des3_set2key_dec(&ctx3, key->data);
mbedtls_des3_crypt_ecb(&ctx3, data, edata);
// DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_DECRYPT);
// DES_ecb_encrypt ((DES_cblock *) edata, (DES_cblock *) data, &(key->ks2), DES_ENCRYPT);
// DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_DECRYPT);
break;
}
}
break;
case T_3K3DES:
}
case T_3K3DES: {
switch (operation) {
case MCO_ENCYPHER:
case MCO_ENCYPHER: {
mbedtls_des3_set3key_enc(&ctx3, key->data);
mbedtls_des3_crypt_ecb(&ctx3, data, edata);
// DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_ENCRYPT);
// DES_ecb_encrypt ((DES_cblock *) edata, (DES_cblock *) data, &(key->ks2), DES_DECRYPT);
// DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks3), DES_ENCRYPT);
break;
case MCO_DECYPHER:
}
case MCO_DECYPHER: {
mbedtls_des3_set3key_dec(&ctx3, key->data);
mbedtls_des3_crypt_ecb(&ctx3, data, edata);
// DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks3), DES_DECRYPT);
// DES_ecb_encrypt ((DES_cblock *) edata, (DES_cblock *) data, &(key->ks2), DES_ENCRYPT);
// DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_DECRYPT);
break;
}
}
break;
case T_AES:
}
case T_AES: {
switch (operation) {
case MCO_ENCYPHER: {
mbedtls_aes_init(&actx);
mbedtls_aes_setkey_enc(&actx, key->data, 128);
mbedtls_aes_crypt_cbc(&actx, MBEDTLS_AES_ENCRYPT, sizeof(edata), ivect, data, edata);
mbedtls_aes_crypt_ecb(&actx, MBEDTLS_AES_ENCRYPT, data, edata);
break;
}
case MCO_DECYPHER: {
mbedtls_aes_init(&actx);
mbedtls_aes_setkey_dec(&actx, key->data, 128);
mbedtls_aes_crypt_cbc(&actx, MBEDTLS_AES_DECRYPT, sizeof(edata), ivect, edata, data);
mbedtls_aes_crypt_ecb(&actx, MBEDTLS_AES_DECRYPT, data, edata);
break;
}
}
break;
}
}
memcpy(data, edata, block_size);
@@ -883,7 +916,7 @@ void mifare_cypher_single_block(desfirekey_t key, uint8_t *data, uint8_t *ivect,
if (direction == MCD_SEND) {
memcpy(ivect, data, block_size);
} else {
xor(ivect, data, block_size);
xor(data, ivect, block_size);
memcpy(ivect, ovect, block_size);
}
}
+3 -1
View File
@@ -183,7 +183,7 @@ void tdes_nxp_send(const void *in, void *out, size_t length, const void *key, ui
void aes128_nxp_receive(const void *in, void *out, size_t length, const void *key, unsigned char iv[16]);
void aes128_nxp_send(const void *in, void *out, size_t length, const void *key, unsigned char iv[16]);
void Desfire_des_key_new(const uint8_t value[8], desfirekey_t key);
void Desfire_des_key_new(const uint8_t *value, desfirekey_t key);
void Desfire_3des_key_new(const uint8_t value[16], desfirekey_t key);
void Desfire_des_key_new_with_version(const uint8_t value[8], desfirekey_t key);
void Desfire_3des_key_new_with_version(const uint8_t value[16], desfirekey_t key);
@@ -207,4 +207,6 @@ size_t enciphered_data_length(const desfiretag_t tag, const size_t nbytes, int c
void cmac_generate_subkeys(desfirekey_t key);
void cmac(const desfirekey_t key, uint8_t *ivect, const uint8_t *data, size_t len, uint8_t *cmac);
size_t key_macing_length(desfirekey_t key);
#endif
+4 -3
View File
@@ -125,7 +125,7 @@ void ExecuteEMVSim(uint8_t *receivedCmd, uint16_t receivedCmd_len, uint8_t *rece
Dbprintf("");
// use annotate to give some hints about the command
annotate(&receivedCmd[1], receivedCmd_len-1);
annotate(&receivedCmd[1], receivedCmd_len - 1);
// This is a common request from the reader which we can just immediately respond to since we know we can't
// handle it.
@@ -141,7 +141,7 @@ void ExecuteEMVSim(uint8_t *receivedCmd, uint16_t receivedCmd_len, uint8_t *rece
currentState = GENERATE_AC;
memcpy(receivedCmd, (unsigned char[]){ 0x03, 0x80, 0xae, 0x80, 0x00, 0x1d }, 6);
memcpy(receivedCmd, (unsigned char[]) { 0x03, 0x80, 0xae, 0x80, 0x00, 0x1d }, 6);
for (int i = 0; i < 29; i++) {
receivedCmd[6 + i] = receivedCmd[12 + i];
@@ -240,7 +240,8 @@ void ExecuteEMVSim(uint8_t *receivedCmd, uint16_t receivedCmd_len, uint8_t *rece
0x20, 0x00, 0x9f, 0x26, 0x08, 0x56, 0xcb, 0x4e, 0xe1, 0xa4,
0xef, 0xac, 0x74, 0x9f, 0x27, 0x01, 0x80, 0x9f, 0x36, 0x02,
0x00, 0x07, 0x9f, 0x6c, 0x02, 0x3e, 0x00, 0x9f, 0x6e, 0x04,
0x20, 0x70, 0x00, 0x00, 0x90, 0x00, 0xff, 0xff};
0x20, 0x70, 0x00, 0x00, 0x90, 0x00, 0xff, 0xff
};
// do the replacement
template[0] = responseToReader[0]; // class bit 0
+9 -4
View File
@@ -821,12 +821,17 @@ static bool hitag2_crypto(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t *
// stage 1, got UID
if (bCrypto == false) {
uint64_t ui64key = key[0] |
((uint64_t)key[1]) << 8 |
((uint64_t)key[2]) << 16 |
((uint64_t)key[3]) << 24 |
((uint64_t)key[4]) << 32 |
((uint64_t)key[5]) << 40;
uint32_t ui32uid = MemLeToUint4byte(rx);
DBG Dbprintf("hitag2_crypto: key array ");
DBG Dbhexdump(6, key, false);
uint64_t ui64key = key[0] | ((uint64_t)key[1]) << 8 | ((uint64_t)key[2]) << 16 | ((uint64_t)key[3]) << 24 | ((uint64_t)key[4]) << 32 | ((uint64_t)key[5]) << 40;
uint32_t ui32uid = rx[0] | ((uint32_t)rx[1]) << 8 | ((uint32_t)rx[2]) << 16 | ((uint32_t)rx[3]) << 24;
DBG Dbprintf("hitag2_crypto: key=0x%x%x uid=0x%x"
, (uint32_t)((REV64(ui64key)) >> 32)
, (uint32_t)((REV64(ui64key)) & 0xffffffff)
+16 -13
View File
@@ -74,7 +74,7 @@ static uint8_t pwdh0, pwdl0, pwdl1; // password bytes
static uint8_t rnd[] = {0x85, 0x44, 0x12, 0x74}; // random number
static uint16_t timestamp_high = 0; // Timer Counter 2 overflow count, ~47min
#define TIMESTAMP (AT91C_BASE_TC2->TC_SR &AT91C_TC_COVFS ? timestamp_high += 1 : 0, ((timestamp_high << 16) + AT91C_BASE_TC2->TC_CV) / T0)
#define TIMESTAMP ( (AT91C_BASE_TC2->TC_SR & AT91C_TC_COVFS) ? timestamp_high += 1 : 0, ((timestamp_high << 16) + AT91C_BASE_TC2->TC_CV) / T0)
//#define SENDBIT_TEST
@@ -538,7 +538,8 @@ static void hts_handle_reader_command(uint8_t *rx, const size_t rxlen,
rotate_uid++;
*txlen = 32;
// init crypt engine
state = ht2_hitag2_init(REV64(tag.data.s.key), REV32(tag.data.s.uid_le), REV32(*(uint32_t *)rx));
uint32_t le_rx = MemLeToUint4byte(rx);
state = ht2_hitag2_init(REV64(tag.data.s.key), REV32(tag.data.s.uid_le), REV32(le_rx));
DBG Dbhexdump(8, tx, false);
for (int i = 0; i < 4; i++) {
@@ -776,7 +777,9 @@ void hts_simulate(bool tag_mem_supplied, const uint8_t *data, bool ledcontrol) {
if (ledcontrol) LED_B_ON();
// Capture reader cmd start timestamp
if (start_time == 0) start_time = TIMESTAMP - HITAG_T_LOW;
if (start_time == 0) {
start_time = TIMESTAMP - HITAG_T_LOW;
}
// Capture reader frame
if (rb >= HITAG_T_STOP) {
@@ -864,9 +867,6 @@ static void hts_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, uint3
uint32_t rb_i = 0, h2 = 0, h3 = 0, h4 = 0;
uint8_t edges[160] = {0};
// Dbprintf("TC0_CV:%i TC1_CV:%i TC1_RB:%i TIMESTAMP:%u", AT91C_BASE_TC0->TC_CV, AT91C_BASE_TC1->TC_CV,
// AT91C_BASE_TC1->TC_RB, TIMESTAMP);
// Receive tag frame, watch for at most T0*HITAG_T_PROG_MAX periods
while (AT91C_BASE_TC0->TC_CV < (T0 * HITAG_T_PROG_MAX)) {
@@ -1034,8 +1034,9 @@ static int hts_send_receive(const uint8_t *tx, size_t txlen, uint8_t *rx, size_t
DbpString("htS: response_bit:");
Dbhexdump(*rxlen, response_bit, false);
Dbprintf("htS: skipping %d bit SOF", sof_bits);
if ((rx[0] >> (8 - sof_bits)) != ((1 << sof_bits) - 1)) {
DbpString("htS: Warning, not all bits of SOF are 1");
DBG DbpString("htS: Warning, not all bits of SOF are 1");
}
}
@@ -1128,12 +1129,10 @@ static int hts_select_tag(const lf_hitag_data_t *packet, uint8_t *tx, size_t siz
// if the tag is in authentication mode try the key or challenge
if (packet->cmd == HTSF_KEY) {
DBG DbpString("Authenticating using key:");
DBG Dbhexdump(6, packet->key, false);
key_le = MemLeToUint6byte(packet->key);
key_le = *(uint64_t *)packet->key;
uint64_t state = ht2_hitag2_init(REV64(key_le), REV32(tag.data.s.uid_le), REV32(*(uint32_t *)rnd));
uint32_t le_val = MemLeToUint4byte(rnd);
uint64_t state = ht2_hitag2_init(REV64(key_le), REV32(tag.data.s.uid_le), REV32(le_val));
uint8_t auth_ks[4];
for (int i = 0; i < 4; i++) {
@@ -1144,6 +1143,8 @@ static int hts_select_tag(const lf_hitag_data_t *packet, uint8_t *tx, size_t siz
txlen = concatbits(tx, txlen, rnd, 0, 32);
txlen = concatbits(tx, txlen, auth_ks, 0, 32);
DBG DbpString("Authenticating using key:");
DBG Dbhexdump(6, packet->key, false);
DBG Dbprintf("%02X %02X %02X %02X %02X %02X %02X %02X", tx[0], tx[1], tx[2], tx[3], tx[4], tx[5], tx[6], tx[7]);
} else if (packet->cmd == HTSF_CHALLENGE) {
@@ -1225,7 +1226,9 @@ static int hts_select_tag(const lf_hitag_data_t *packet, uint8_t *tx, size_t siz
pwdl1 = 0;
if (packet->cmd == HTSF_KEY) {
uint64_t state = ht2_hitag2_init(REV64(key_le), REV32(tag.data.s.uid_le), REV32(*(uint32_t *)rnd));
uint32_t le_val = MemLeToUint4byte(rnd);
uint64_t state = ht2_hitag2_init(REV64(key_le), REV32(tag.data.s.uid_le), REV32(le_val));
for (int i = 0; i < 4; i++) {
ht2_hitag2_byte(&state);
}
+117 -91
View File
@@ -690,8 +690,6 @@ static RAMFUNC int ManchesterDecoding_Thinfilm(uint8_t bit) {
if (Demod.bitCount) { // there are some remaining data bits
Demod.shiftReg <<= (8 - Demod.bitCount); // left align the decoded bits
Demod.output[Demod.len++] = Demod.shiftReg & 0xFF; // and add them to the output
// Dbprintf("A | len... %u - %u == 0x%02x", Demod.len, Demod.bitCount, Demod.output[0]);
return true;
}
@@ -1077,6 +1075,7 @@ bool prepare_tag_modulation(tag_response_info_t *response_info, size_t max_buffe
if (ts->max > max_buffer_size) {
Dbprintf("ToSend buffer, Out-of-bound, when modulating bits for tag answer:");
Dbhexdump(response_info->response_n, response_info->response, false);
Dbprintf("Need %i, got %i", ts->max, max_buffer_size);
return false;
}
@@ -1108,7 +1107,7 @@ bool prepare_allocated_tag_modulation(tag_response_info_t *response_info, uint8_
}
bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data,
uint8_t *iRATs, size_t irats_len, tag_response_info_t **responses,
uint8_t *ats, size_t ats_len, tag_response_info_t **responses,
uint32_t *cuid, uint32_t counters[3], uint8_t tearings[3], uint8_t *pages) {
uint8_t sak = 0;
// The first response contains the ATQA (note: bytes are transmitted in reverse order).
@@ -1130,9 +1129,9 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data,
// TA(1) = 0x80: different divisors not supported, DR = 1, DS = 1
// TB(1) = not present. Defaults: FWI = 4 (FWT = 256 * 16 * 2^4 * 1/fc = 4833us), SFGI = 0 (SFG = 256 * 16 * 2^0 * 1/fc = 302us)
// TC(1) = 0x02: CID supported, NAD not supported
// static uint8_t rRATS[] = { 0x04, 0x58, 0x80, 0x02, 0x00, 0x00 };
static uint8_t rRATS[40] = { 0x05, 0x75, 0x80, 0x60, 0x02, 0x00, 0x00, 0x00 };
uint8_t rRATS_len = 8;
// static uint8_t rATS[] = { 0x04, 0x58, 0x80, 0x02, 0x00, 0x00 };
static uint8_t rATS[40] = { 0x06, 0x75, 0x80, 0x60, 0x02, 0x00, 0x00, 0x00 };
uint8_t rATS_len = 8;
// GET_VERSION response for EV1/NTAG
static uint8_t rVERSION[10] = { 0x00 };
@@ -1184,8 +1183,8 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data,
rATQA[0] = 0x44;
rATQA[1] = 0x03;
sak = 0x20;
memcpy(rRATS, "\x06\x75\x77\x81\x02\x80\x00\x00", 8);
rRATS_len = 8;
memcpy(rATS, "\x06\x75\x77\x81\x02\x80\x00\x00", 8);
rATS_len = 8; // including CRC
break;
}
case 4: { // ISO/IEC 14443-4 - javacard (JCOP)
@@ -1254,8 +1253,8 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data,
}
case 11: { // ISO/IEC 14443-4 - javacard (JCOP) / EMV
memcpy(rRATS, "\x13\x78\x80\x72\x02\x80\x31\x80\x66\xb1\x84\x0c\x01\x6e\x01\x83\x00\x90\x00", 19);
rRATS_len = 19;
memcpy(rATS, "\x13\x78\x80\x72\x02\x80\x31\x80\x66\xb1\x84\x0c\x01\x6e\x01\x83\x00\x90\x00\x00\x00", 21);
rATS_len = 21; // including CRC
rATQA[0] = 0x04;
sak = 0x20;
break;
@@ -1271,19 +1270,21 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data,
}
}
// copy the iRATs if supplied.
// iRATs is a pointer to 20 byte array
// rRATS is a 40 byte array
if ((flags & FLAG_RATS_IN_DATA) == FLAG_RATS_IN_DATA) {
memcpy(rRATS, iRATs, irats_len);
// rats len is dictated by the first char of the string, add 2 crc bytes
rRATS_len = (iRATs[0] + 2);
// Since its Varible length we can send value > 40 and overflow our array.
// Even if RATS protocol defined as max 40 bytes doesn't mean people try stuff
if (rRATS_len > sizeof(rRATS)) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("[-] ERROR: iRATS overflow. Max %zu, got %zu", sizeof(rRATS), rRATS_len);
// copy the ats if supplied.
// ats is a pointer to 20 byte array
// rATS is a 40 byte array
if ((flags & FLAG_ATS_IN_DATA) == FLAG_ATS_IN_DATA) {
// Even if RATS protocol defined as max 40 bytes doesn't mean people try stuff. Check for overflow before copy
if (ats_len + 2 > sizeof(rATS)) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("[-] ERROR: ATS overflow. Max %zu, got %zu", sizeof(rATS) - 2, ats_len);
return false;
}
memcpy(rATS, ats, ats_len);
rATS_len = ats_len + 2;
// ATS length (without CRC) is supposed to match its first byte TL
if (ats_len != ats[0]) {
if (g_dbglevel >= DBG_INFO) Dbprintf("[-] WARNING: actual ATS length (%zu) differs from its TL value (%u).", ats_len, ats[0]);
}
}
// if uid not supplied then get from emulator memory
@@ -1379,7 +1380,7 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data,
return false;
}
AddCrc14A(rRATS, rRATS_len - 2);
AddCrc14A(rATS, rATS_len - 2);
AddCrc14A(rPPS, sizeof(rPPS) - 2);
@@ -1407,7 +1408,7 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data,
{ .response = rSAKc1, .response_n = sizeof(rSAKc1) }, // Acknowledge select - cascade 1
{ .response = rSAKc2, .response_n = sizeof(rSAKc2) }, // Acknowledge select - cascade 2
{ .response = rSAKc3, .response_n = sizeof(rSAKc3) }, // Acknowledge select - cascade 3
{ .response = rRATS, .response_n = sizeof(rRATS) }, // dummy ATS (pseudo-ATR), answer to RATS
{ .response = rATS, .response_n = sizeof(rATS) }, // dummy ATS (pseudo-ATR), answer to RATS
{ .response = rVERSION, .response_n = sizeof(rVERSION) }, // EV1/NTAG GET_VERSION response
{ .response = rSIGN, .response_n = sizeof(rSIGN) }, // EV1/NTAG READ_SIG response
{ .response = rPPS, .response_n = sizeof(rPPS) }, // PPS response
@@ -1415,7 +1416,7 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data,
};
// since rats len is variable now.
responses_init[RESP_INDEX_RATS].response_n = rRATS_len;
responses_init[RESP_INDEX_ATS].response_n = rATS_len;
// "precompiled" responses.
// These exist for speed reasons. There are no time in the anti collision phase to calculate responses.
@@ -1428,7 +1429,7 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data,
// 85 bytes normally (rats = 8 bytes)
// 77 bytes + ratslen,
#define ALLOCATED_TAG_MODULATION_BUFFER_SIZE ( ((77 + rRATS_len) * 8) + 77 + rRATS_len + 12 + 12 + 12)
#define ALLOCATED_TAG_MODULATION_BUFFER_SIZE ( ((77 + rATS_len) * 8) + 77 + rATS_len + 12 + 12 + 12)
uint8_t *free_buffer = BigBuf_calloc(ALLOCATED_TAG_MODULATION_BUFFER_SIZE);
// modulation buffer pointer and current buffer free space size
@@ -1454,8 +1455,8 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data,
// response to send, and send it.
// 'hf 14a sim'
//-----------------------------------------------------------------------------
void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_t exitAfterNReads,
uint8_t *iRATs, size_t irats_len) {
void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uint8_t exitAfterNReads,
uint8_t *ats, size_t ats_len) {
#define ATTACK_KEY_COUNT 16
@@ -1489,7 +1490,17 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_
#define DYNAMIC_MODULATION_BUFFER_SIZE 512
uint8_t *dynamic_response_buffer = BigBuf_calloc(DYNAMIC_RESPONSE_BUFFER_SIZE);
if (dynamic_response_buffer == NULL) {
BigBuf_free_keep_EM();
reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EMALLOC, NULL, 0);
return;
}
uint8_t *dynamic_modulation_buffer = BigBuf_calloc(DYNAMIC_MODULATION_BUFFER_SIZE);
if (dynamic_modulation_buffer == NULL) {
BigBuf_free_keep_EM();
reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EMALLOC, NULL, 0);
return;
}
tag_response_info_t dynamic_response_info = {
.response = dynamic_response_buffer,
.response_n = 0,
@@ -1497,7 +1508,7 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_
.modulation_n = 0
};
if (SimulateIso14443aInit(tagType, flags, data, iRATs, irats_len, &responses, &cuid, counters, tearings, &pages) == false) {
if (SimulateIso14443aInit(tagType, flags, useruid, ats, ats_len, &responses, &cuid, counters, tearings, &pages) == false) {
BigBuf_free_keep_EM();
reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0);
return;
@@ -1671,10 +1682,10 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_
EmSend4bit(CARD_NACK_IV);
} else {
// first blocks of emu are header
uint16_t start = block * 4 + MFU_DUMP_PREFIX_LENGTH;
uint8_t emdata[MAX_MIFARE_FRAME_SIZE];
emlGet(emdata, start, 16);
AddCrc14A(emdata, 16);
uint16_t start = (block * 4) + MFU_DUMP_PREFIX_LENGTH;
uint8_t emdata[MAX_MIFARE_FRAME_SIZE] = {0};
emlGet(emdata, start, MIFARE_BLOCK_SIZE);
AddCrc14A(emdata, MIFARE_BLOCK_SIZE);
EmSendCmd(emdata, sizeof(emdata));
numReads++; // Increment number of times reader requested a block
@@ -1692,8 +1703,8 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_
p_response = &responses[RESP_INDEX_UIDC1];
} else { // all other tags (16 byte block tags)
uint8_t emdata[MAX_MIFARE_FRAME_SIZE] = {0};
emlGet(emdata, block, 16);
AddCrc14A(emdata, 16);
emlGet(emdata, block, MIFARE_BLOCK_SIZE);
AddCrc14A(emdata, MIFARE_BLOCK_SIZE);
EmSendCmd(emdata, sizeof(emdata));
// We already responded, do not send anything with the EmSendCmd14443aRaw() that is called below
p_response = NULL;
@@ -1717,13 +1728,14 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_
} else if (receivedCmd[0] == MIFARE_ULC_WRITE && len == 8 && (tagType == 2 || tagType == 7)) { // Received a WRITE
// cmd + block + 4 bytes data + 2 bytes crc
if (CheckCrc14A(receivedCmd, len)) {
uint8_t block = receivedCmd[1];
if (block > pages) {
// send NACK 0x0 == invalid argument
EmSend4bit(CARD_NACK_IV);
} else {
// first blocks of emu are header
emlSetMem_xt(&receivedCmd[2], block + MFU_DUMP_PREFIX_LENGTH / 4, 1, 4);
emlSetMem_xt(&receivedCmd[2], block + (MFU_DUMP_PREFIX_LENGTH / 4), 1, 4);
// send ACK
EmSend4bit(CARD_ACK);
}
@@ -1820,7 +1832,7 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_
EmSend4bit(CARD_NACK_NA);
p_response = NULL;
} else {
p_response = &responses[RESP_INDEX_RATS];
p_response = &responses[RESP_INDEX_ATS];
}
} else if (receivedCmd[0] == MIFARE_ULC_AUTH_1) { // ULC authentication, or Desfire Authentication
LogTrace(receivedCmd, Uart.len, Uart.startTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true);
@@ -1836,7 +1848,7 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_
}
if (memcmp(pwd, "\x00\x00\x00\x00", 4) == 0) {
Uint4byteToMemLe(pwd, ul_ev1_pwdgenB(data));
Uint4byteToMemLe(pwd, ul_ev1_pwdgenB(useruid));
if (g_dbglevel >= DBG_DEBUG) Dbprintf("Calc pwd... %02X %02X %02X %02X", pwd[0], pwd[1], pwd[2], pwd[3]);
}
@@ -3941,9 +3953,10 @@ It can also continue after the AID has been selected, and respond to other reque
This was forked from the original function to allow for more flexibility in the future, and to increase the processing speed of the original function.
/// */
void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *data,
uint8_t *iRATs, size_t irats_len, uint8_t *aid, uint8_t *resp,
uint8_t *apdu, int aidLen, int respondLen, int apduLen, bool enumerate) {
void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *uid,
uint8_t *ats, size_t ats_len, uint8_t *aid, size_t aid_len,
uint8_t *selectaid_response, size_t selectaid_response_len,
uint8_t *getdata_response, size_t getdata_response_len) {
tag_response_info_t *responses;
uint32_t cuid = 0;
uint32_t counters[3] = { 0x00, 0x00, 0x00 };
@@ -3954,6 +3967,12 @@ void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *data,
uint8_t receivedCmd[MAX_FRAME_SIZE] = { 0x00 };
uint8_t receivedCmdPar[MAX_PARITY_SIZE] = { 0x00 };
// Buffers must be provided by the caller, even if lengths are 0
// Copy the AID, AID Response, and the GetData APDU response into our variables
if ((aid == NULL) || (selectaid_response == NULL) || (getdata_response == NULL)) {
reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINVARG, NULL, 0);
}
// free eventually allocated BigBuf memory but keep Emulator Memory
BigBuf_free_keep_EM();
@@ -3962,7 +3981,17 @@ void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *data,
#define DYNAMIC_MODULATION_BUFFER2_SIZE 1536
uint8_t *dynamic_response_buffer2 = BigBuf_calloc(DYNAMIC_RESPONSE_BUFFER2_SIZE);
if (dynamic_response_buffer2 == NULL) {
BigBuf_free_keep_EM();
reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EMALLOC, NULL, 0);
return;
}
uint8_t *dynamic_modulation_buffer2 = BigBuf_calloc(DYNAMIC_MODULATION_BUFFER2_SIZE);
if (dynamic_modulation_buffer2 == NULL) {
BigBuf_free_keep_EM();
reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EMALLOC, NULL, 0);
return;
}
tag_response_info_t dynamic_response_info = {
.response = dynamic_response_buffer2,
.response_n = 0,
@@ -3970,7 +3999,7 @@ void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *data,
.modulation_n = 0
};
if (SimulateIso14443aInit(tagType, flags, data, iRATs, irats_len, &responses, &cuid, counters, tearings, &pages) == false) {
if (SimulateIso14443aInit(tagType, flags, uid, ats, ats_len, &responses, &cuid, counters, tearings, &pages) == false) {
BigBuf_free_keep_EM();
reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0);
return;
@@ -3990,25 +4019,9 @@ void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *data,
set_tracing(true);
LED_A_ON();
// Filters for when this comes through
static uint8_t aidFilter[30] = { 0x00 }; // Default AID Value
static uint8_t aidResponse[100] = { 0x00 }; // Default AID Response
static uint8_t apduCommand [100] = { 0x00 }; // Default APDU GetData Response
// Copy the AID, AID Response, and the GetData APDU response into our variables
if (aid != 0) {
memcpy(aidFilter, aid, aidLen);
}
if (resp != 0) {
memcpy(aidResponse, resp, respondLen);
}
if (apdu != 0) {
memcpy(apduCommand, apdu, apduLen);
}
// main loop
bool finished = false;
bool got_rats = false;
while (finished == false) {
// BUTTON_PRESS check done in GetIso14443aCommandFromReader
WDT_HIT();
@@ -4046,24 +4059,32 @@ void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *data,
} else if (receivedCmd[0] == ISO14443A_CMD_HALT && len == 4) { // Received a HALT
LogTrace(receivedCmd, Uart.len, Uart.startTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true);
p_response = NULL;
finished = true;
if (got_rats) {
finished = true;
}
} else if (receivedCmd[0] == ISO14443A_CMD_RATS && len == 4) { // Received a RATS request
p_response = &responses[RESP_INDEX_RATS];
p_response = &responses[RESP_INDEX_ATS];
got_rats = true;
} else {
// clear old dynamic responses
dynamic_response_info.response_n = 0;
dynamic_response_info.modulation_n = 0;
// Check for ISO 14443A-4 compliant commands, look at left nibble
// Check for ISO 14443A-4 compliant commands, look at left byte (PCB)
uint8_t offset = 0;
switch (receivedCmd[0]) {
case 0x0B:
case 0x0A: { // IBlock (command CID)
case 0x0B: // IBlock with CID
case 0x0A:
offset = 1;
case 0x02: // IBlock without CID
case 0x03: {
dynamic_response_info.response[0] = receivedCmd[0];
dynamic_response_info.response[1] = 0x00;
switch (receivedCmd[3]) { // APDU Class Byte
// receivedCmd in this case is expecting to structured with a CID, then the APDU command for SelectFile
// | IBlock (CID) | CID | APDU Command | CRC |
switch (receivedCmd[2 + offset]) { // APDU Class Byte
// receivedCmd in this case is expecting to structured with possibly a CID, then the APDU command for SelectFile
// | IBlock (CID) | CID | APDU Command | CRC |
// or | IBlock (noCID) | APDU Command | CRC |
case 0xA4: { // SELECT FILE
// Select File AID uses the following format for GlobalPlatform
@@ -4072,40 +4093,40 @@ void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *data,
// xx in this case is len of the AID value in hex
// aid len is found as a hex value in receivedCmd[6] (Index Starts at 0)
int aid_len = receivedCmd[6];
uint8_t *received_aid = &receivedCmd[7];
int received_aid_len = receivedCmd[5 + offset];
uint8_t *received_aid = &receivedCmd[6 + offset];
// aid enumeration flag
if (enumerate == true) {
Dbprintf("Received AID (%d):", aid_len);
Dbhexdump(aid_len, received_aid, false);
if ((flags & FLAG_ENUMERATE_AID) == FLAG_ENUMERATE_AID) {
Dbprintf("Received AID (%d):", received_aid_len);
Dbhexdump(received_aid_len, received_aid, false);
}
if (memcmp(aidFilter, received_aid, aid_len) == 0) { // Evaluate the AID sent by the Reader to the AID supplied
if ((received_aid_len == aid_len) && (memcmp(aid, received_aid, aid_len) == 0)) { // Evaluate the AID sent by the Reader to the AID supplied
// AID Response will be parsed here
memcpy(dynamic_response_info.response + 2, aidResponse, respondLen + 2);
dynamic_response_info.response_n = respondLen + 2;
memcpy(dynamic_response_info.response + 1 + offset, selectaid_response, selectaid_response_len + 1 + offset);
dynamic_response_info.response_n = selectaid_response_len + 2;
} else { // Any other SELECT FILE command will return with a Not Found
dynamic_response_info.response[2] = 0x6A;
dynamic_response_info.response[3] = 0x82;
dynamic_response_info.response_n = 4;
dynamic_response_info.response[1 + offset] = 0x6A;
dynamic_response_info.response[2 + offset] = 0x82;
dynamic_response_info.response_n = 3 + offset;
}
}
break;
case 0xDA: { // PUT DATA
// Just send them a 90 00 response
dynamic_response_info.response[2] = 0x90;
dynamic_response_info.response[3] = 0x00;
dynamic_response_info.response_n = 4;
dynamic_response_info.response[1 + offset] = 0x90;
dynamic_response_info.response[2 + offset] = 0x00;
dynamic_response_info.response_n = 3 + offset;
}
break;
case 0xCA: { // GET DATA
if (sentCount == 0) {
// APDU Command will just be parsed here
memcpy(dynamic_response_info.response + 2, apduCommand, apduLen + 2);
dynamic_response_info.response_n = respondLen + 2;
memcpy(dynamic_response_info.response + 1 + offset, getdata_response, getdata_response_len + 2);
dynamic_response_info.response_n = selectaid_response_len + 1 + offset;
} else {
finished = true;
break;
@@ -4116,18 +4137,18 @@ void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *data,
default : {
// Any other non-listed command
// Respond Not Found
dynamic_response_info.response[2] = 0x6A;
dynamic_response_info.response[3] = 0x82;
dynamic_response_info.response_n = 4;
dynamic_response_info.response[1 + offset] = 0x6A;
dynamic_response_info.response[2 + offset] = 0x82;
dynamic_response_info.response_n = 3 + offset;
}
}
break;
}
break;
case 0xCA:
case 0xC2: { // Readers sends deselect command
dynamic_response_info.response[0] = 0xCA;
case 0xCA: // S-Block Deselect with CID
case 0xC2: { // S-Block Deselect without CID
dynamic_response_info.response[0] = receivedCmd[0];
dynamic_response_info.response[1] = 0x00;
dynamic_response_info.response_n = 2;
finished = true;
@@ -4135,12 +4156,15 @@ void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *data,
break;
default: {
// Never seen this command before
// Never seen this PCB before
LogTrace(receivedCmd, Uart.len, Uart.startTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true);
if (g_dbglevel >= DBG_DEBUG) {
Dbprintf("Received unknown command (len=%d):", len);
Dbhexdump(len, receivedCmd, false);
}
if ((receivedCmd[0] & 0x10) == 0x10) {
Dbprintf("Warning, reader sent a chained command but we lack support for it. Ignoring command.");
}
// Do not respond
dynamic_response_info.response_n = 0;
}
@@ -4149,13 +4173,15 @@ void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *data,
if (dynamic_response_info.response_n > 0) {
// Copy the CID from the reader query
dynamic_response_info.response[1] = receivedCmd[1];
if (offset > 0) {
dynamic_response_info.response[1] = receivedCmd[1];
}
// Add CRC bytes, always used in ISO 14443A-4 compliant cards
AddCrc14A(dynamic_response_info.response, dynamic_response_info.response_n);
dynamic_response_info.response_n += 2;
if (prepare_tag_modulation(&dynamic_response_info, DYNAMIC_MODULATION_BUFFER_SIZE) == false) {
if (prepare_tag_modulation(&dynamic_response_info, DYNAMIC_MODULATION_BUFFER2_SIZE) == false) {
if (g_dbglevel >= DBG_DEBUG) DbpString("Error preparing tag response");
LogTrace(receivedCmd, Uart.len, Uart.startTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true);
break;

Some files were not shown because too many files have changed in this diff Show More