diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ebebd9c11..7207389e18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] +- Added `hf seos sam` - Added support for HID SAM SEOS communications (@jkramarz) - Changed (extended) area accessible by spiffs into last page of FLASH (@piotrva) - Changed flash-stored key dictionaries (Mifare, iClass, T55XX) and T55XX configurations to SPIFFS files (@piotrva) - Changed `lf em 410x sim` to use default gap value of 0 and extended help (@piotrva) diff --git a/armsrc/Makefile b/armsrc/Makefile index dedccd3e0a..13a817dc93 100644 --- a/armsrc/Makefile +++ b/armsrc/Makefile @@ -37,7 +37,7 @@ APP_CFLAGS = $(PLATFORM_DEFS) \ SRC_LF = lfops.c lfsampling.c pcf7931.c lfdemod.c lfadc.c SRC_HF = hfops.c SRC_ISO15693 = iso15693.c iso15693tools.c -SRC_ISO14443a = iso14443a.c mifareutil.c mifarecmd.c epa.c mifaresim.c sam_mfc.c sam_seos.c +SRC_ISO14443a = iso14443a.c mifareutil.c mifarecmd.c epa.c mifaresim.c sam_common.c sam_mfc.c sam_seos.c #UNUSED: mifaresniff.c SRC_ISO14443b = iso14443b.c SRC_FELICA = felica.c diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 6d42a0e0c4..359985cc38 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -2246,7 +2246,7 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_HF_SAM_SEOS: { -// sam_seos_get_pacs(); + sam_seos_get_pacs(packet); break; } diff --git a/armsrc/sam_common.c b/armsrc/sam_common.c new file mode 100644 index 0000000000..76ca269e16 --- /dev/null +++ b/armsrc/sam_common.c @@ -0,0 +1,373 @@ +//----------------------------------------------------------------------------- +// 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. +//----------------------------------------------------------------------------- +// Routines to support MFC <-> SAM communication +//----------------------------------------------------------------------------- + + +#include +#include "sam_common.h" +#include "iclass.h" +#include "proxmark3_arm.h" +#include "BigBuf.h" +#include "commonutil.h" +#include "ticks.h" +#include "dbprint.h" +#include "i2c.h" +#include "iso15693.h" +#include "protocols.h" + + +/** + * @brief Transmits data to and receives data from a HID®'s iCLASS® SE™ Processor. + * + * This function sends a specified number of bytes to the SAM and receives a response. + * + * @param data Pointer to the data to be transmitted. + * @param n Number of bytes to be transmitted. + * @param resp Pointer to the buffer where the response will be stored. + * @param resplen Pointer to the variable where the length of the response will be stored. + * @return Status code indicating success or failure of the operation. + */ +int sam_rxtx(const uint8_t *data, uint16_t n, uint8_t *resp, uint16_t *resplen) { + bool res = I2C_BufferWrite(data, n, I2C_DEVICE_CMD_SEND_T0, I2C_DEVICE_ADDRESS_MAIN); + if (res == false) { + DbpString("failed to send to SIM CARD"); + goto out; + } + + *resplen = ISO7816_MAX_FRAME; + + res = sc_rx_bytes(resp, resplen, SIM_WAIT_DELAY); + if (res == false) { + DbpString("failed to receive from SIM CARD"); + goto out; + } + + if (*resplen < 2) { + DbpString("received too few bytes from SIM CARD"); + res = false; + goto out; + } + + uint16_t more_len = 0; + + if (resp[*resplen - 2] == 0x61 || resp[*resplen - 2] == 0x9F) { + more_len = resp[*resplen - 1]; + } else { + // we done, return + goto out; + } + + // Don't discard data we already received except the SW code. + // If we only received 1 byte, this is the echo of INS, we discard it. + *resplen -= 2; + if (*resplen == 1) { + *resplen = 0; + } + + uint8_t cmd_getresp[] = {0x00, ISO7816_GET_RESPONSE, 0x00, 0x00, more_len}; + + res = I2C_BufferWrite(cmd_getresp, sizeof(cmd_getresp), I2C_DEVICE_CMD_SEND_T0, I2C_DEVICE_ADDRESS_MAIN); + if (res == false) { + DbpString("failed to send to SIM CARD 2"); + goto out; + } + + more_len = 255 - *resplen; + + res = sc_rx_bytes(resp + *resplen, &more_len, SIM_WAIT_DELAY); + if (res == false) { + DbpString("failed to receive from SIM CARD 2"); + goto out; + } + + *resplen += more_len; + +out: + return res; +} + + +static inline void swap_clock_counters(volatile unsigned int *a, unsigned int *b) { + unsigned int c = *a; + *a = *b; + *b = c; +} + +/** + * @brief Swaps the timer counter values. + * + * AT91SAM7S512 has a single Timer-Counter, that is reused in clocks Ticks + * and CountSspClk. This function stops the current clock and restores previous + * values. It is used to switch between different clock sources. + * It probably makes communication timing off, but at least makes it work. + */ +static void swap_clocks(void) { + static unsigned int tc0, tc1, tc2 = 0; + StopTicks(); + swap_clock_counters(&(AT91C_BASE_TC0->TC_CV), &tc0); + swap_clock_counters(&(AT91C_BASE_TC1->TC_CV), &tc1); + swap_clock_counters(&(AT91C_BASE_TC2->TC_CV), &tc2); +} + +void switch_clock_to_ticks(void) { + swap_clocks(); + StartTicks(); +} + +void switch_clock_to_countsspclk(void) { + swap_clocks(); + StartCountSspClk(); +} + + +/** + * @brief Sends a payload to the SAM + * + * This function prepends the payload with the necessary APDU and application + * headers and sends it to the SAM. + * + * @param addr_src 0x14 for command from NFC, 0x44 for command from application + * @param addr_dest 0x0A for command to SAM + * @param addr_reply same as add_src or 0x00 if no reply is expected + * @param payload Pointer to the data to be sent. + * @param payload_len Length of the data to be sent. + * @param response Pointer to the buffer where the response will be stored. + * @param response_len Pointer to the variable where the length of the response will be stored. + * @param length Length of the data to be sent. + * @return Status code indicating success or failure of the operation. + */ +int sam_send_payload( + const uint8_t addr_src, + const uint8_t addr_dest, + const uint8_t addr_reply, + + const uint8_t *const payload, + const uint16_t *payload_len, + + uint8_t *response, + uint16_t *response_len +) { + int res = PM3_SUCCESS; + + uint8_t *buf = response; + + buf[0] = 0xA0; // CLA + buf[1] = 0xDA; // INS (PUT DATA) + buf[2] = 0x02; // P1 (TLV format?) + buf[3] = 0x63; // P2 + buf[4] = SAM_TX_ASN1_PREFIX_LENGTH + (uint8_t) * payload_len; // LEN + + buf[5] = addr_src; + buf[6] = addr_dest; + buf[7] = addr_reply; + + buf[8] = 0x00; + buf[9] = 0x00; + buf[10] = 0x00; + + memcpy( + &buf[11], + payload, + *payload_len + ); + + uint16_t length = SAM_TX_ASN1_PREFIX_LENGTH + SAM_TX_APDU_PREFIX_LENGTH + (uint8_t) * payload_len; + + LogTrace(buf, length, 0, 0, NULL, true); + if (g_dbglevel >= DBG_INFO) { + DbpString("SAM REQUEST APDU: "); + Dbhexdump(length, buf, false); + } + + if (sam_rxtx(buf, length, response, response_len) == false) { + if (g_dbglevel >= DBG_ERROR) + DbpString("SAM ERROR"); + res = PM3_ECARDEXCHANGE; + goto out; + } + + LogTrace(response, *response_len, 0, 0, NULL, false); + if (g_dbglevel >= DBG_INFO) { + DbpString("SAM RESPONSE APDU: "); + Dbhexdump(*response_len, response, false); + } + +out: + return res; +} + + +/** + * @brief Retreives SAM firmware version. + * + * Used just as ping or sanity check here. + * + * @return Status code indicating success or failure of the operation. + */ +int sam_get_version(void) { + int res = PM3_SUCCESS; + + if (g_dbglevel >= DBG_DEBUG) + DbpString("start sam_get_version"); + + uint8_t *response = BigBuf_malloc(ISO7816_MAX_FRAME); + uint16_t response_len = ISO7816_MAX_FRAME; + + uint8_t payload[] = { + 0xa0, 0x02, // <- SAM command + 0x82, 0x00 // <- get version + }; + uint16_t payload_len = sizeof(payload); + + sam_send_payload( + 0x44, 0x0a, 0x44, + payload, + &payload_len, + response, + &response_len + ); + + // resp: + // c1 64 00 00 00 + // bd 11 <- SAM response + // 8a 0f <- get version response + // 80 02 + // 01 29 <- version + // 81 06 + // 68 3d 05 20 26 b6 <- build ID + // 82 01 + // 01 + // 90 00 + if (g_dbglevel >= DBG_DEBUG) + DbpString("end sam_get_version"); + + if (response[5] != 0xbd) { + Dbprintf("Invalid SAM response"); + goto error; + } else { + uint8_t *sam_response_an = sam_find_asn1_node(response + 5, 0x8a); + if (sam_response_an == NULL) { + if (g_dbglevel >= DBG_ERROR) + DbpString("SAM get response failed"); + goto error; + } + uint8_t *sam_version_an = sam_find_asn1_node(sam_response_an, 0x80); + if (sam_version_an == NULL) { + if (g_dbglevel >= DBG_ERROR) + DbpString("SAM get version failed"); + goto error; + } + uint8_t *sam_build_an = sam_find_asn1_node(sam_response_an, 0x81); + if (sam_build_an == NULL) { + if (g_dbglevel >= DBG_ERROR) + DbpString("SAM get firmware ID failed"); + goto error; + } + if (g_dbglevel >= DBG_INFO) { + DbpString("SAM get version successful"); + Dbprintf("Firmware version: %X.%X", sam_version_an[2], sam_version_an[3]); + Dbprintf("Firmware ID: "); + Dbhexdump(sam_build_an[1], sam_build_an + 2, false); + } + goto out; + } + +error: + res = PM3_ESOFT; + +out: + BigBuf_free(); + + if (g_dbglevel >= DBG_DEBUG) + DbpString("end sam_get_version"); + + return res; +} + + + +/** + * @brief Finds an ASN.1 node of a specified type within a given root node. + * + * This function searches through a single level of the ASN.1 structure starting + * from the root node to find a node of the specified type. + * + * @param root Pointer to the root node of the ASN.1 structure. + * @param type The type of the ASN.1 node to find. + * @return Pointer to the ASN.1 node of the specified type if found, otherwise NULL. + */ +uint8_t *sam_find_asn1_node(const uint8_t *root, const uint8_t type) { + const uint8_t *end = (uint8_t *) root + *(root + 1); + uint8_t *current = (uint8_t *) root + 2; + while (current < end) { + if (*current == type) { + return current; + } else { + current += 2 + *(current + 1); + } + } + return NULL; +} + +/** + * @brief Appends an ASN.1 node to the end of a given node. + * + * This function appends an ASN.1 node of a specified type and length to the end of + * the ASN.1 structure at specified node level. + * + * It is the most naive solution that does not handle the case where the node to append is + * not the last node at the same level. It also does not also care about proper + * order of the nodes. + * + * @param root Pointer to the root node of the ASN.1 structure. + * @param root Pointer to the node to be appended of the ASN.1 structure. + * @param type The type of the ASN.1 node to append. + * @param data Pointer to the data to be appended. + * @param len The length of the data to be appended. + */ +void sam_append_asn1_node(const uint8_t *root, const uint8_t *node, uint8_t type, const uint8_t *const data, uint8_t len) { + uint8_t *end = (uint8_t *) root + *(root + 1) + 2; + + *(end) = type; + *(end + 1) = len; + memcpy(end + 2, data, len); + + for (uint8_t *current = (uint8_t *) root; current <= node; current += 2) { + *(current + 1) += 2 + len; + }; + return; +} + +void sam_send_ack(void) { + uint8_t *response = BigBuf_malloc(ISO7816_MAX_FRAME); + uint16_t response_len = ISO7816_MAX_FRAME; + + uint8_t payload[] = { + 0xa0, 0 + }; + uint16_t payload_len = sizeof(payload); + + sam_send_payload( + 0x44, 0x0a, 0x00, + payload, + &payload_len, + response, + &response_len + ); + + BigBuf_free(); +} diff --git a/armsrc/sam_common.h b/armsrc/sam_common.h new file mode 100644 index 0000000000..645957a3c8 --- /dev/null +++ b/armsrc/sam_common.h @@ -0,0 +1,49 @@ +//----------------------------------------------------------------------------- +// 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. +//----------------------------------------------------------------------------- +#ifndef __SAM_COMMON_H +#define __SAM_COMMON_H + +#include "common.h" + +static const uint8_t SAM_TX_APDU_PREFIX_LENGTH = 5; +static const uint8_t SAM_TX_ASN1_PREFIX_LENGTH = 6; +static const uint8_t SAM_RX_ASN1_PREFIX_LENGTH = 5; + +int sam_rxtx(const uint8_t *data, uint16_t n, uint8_t *resp, uint16_t *resplen); + +void switch_clock_to_ticks(void); +void switch_clock_to_countsspclk(void); + +int sam_send_payload( + const uint8_t addr_src, + const uint8_t addr_dest, + const uint8_t addr_reply, + + const uint8_t *const payload, + const uint16_t *payload_len, + + uint8_t *response, + uint16_t *response_len +); + +int sam_get_version(void); + +uint8_t *sam_find_asn1_node(const uint8_t *root, const uint8_t type); +void sam_append_asn1_node(const uint8_t *root, const uint8_t *node, uint8_t type, const uint8_t *const data, uint8_t len); + +void sam_send_ack(void); + +#endif diff --git a/armsrc/sam_mfc.c b/armsrc/sam_mfc.c index 090f4a781f..5e2309437b 100644 --- a/armsrc/sam_mfc.c +++ b/armsrc/sam_mfc.c @@ -16,7 +16,7 @@ // Routines to support MFC <-> SAM communication //----------------------------------------------------------------------------- #include "sam_mfc.h" -#include "sam_seos.h" +#include "sam_common.h" #include "iclass.h" #include "proxmark3_arm.h" diff --git a/armsrc/sam_mfc.h b/armsrc/sam_mfc.h index 5cf55d7117..527bc77ffe 100644 --- a/armsrc/sam_mfc.h +++ b/armsrc/sam_mfc.h @@ -17,5 +17,6 @@ #define __SAM_MFC_H #include "common.h" +#include "sam_common.h" #endif diff --git a/armsrc/sam_picopass.c b/armsrc/sam_picopass.c index fd465c992c..d396a31c5c 100644 --- a/armsrc/sam_picopass.c +++ b/armsrc/sam_picopass.c @@ -16,6 +16,7 @@ // Routines to support Picopass <-> SAM communication //----------------------------------------------------------------------------- #include "sam_picopass.h" +#include "sam_common.h" #include "iclass.h" #include "crc16.h" #include "proxmark3_arm.h" @@ -30,66 +31,79 @@ #include "optimized_cipher.h" #include "fpgaloader.h" -static int sam_rxtx(const uint8_t *data, uint16_t n, uint8_t *resp, uint16_t *resplen) { - StartTicks(); - - bool res = I2C_BufferWrite(data, n, I2C_DEVICE_CMD_SEND_T0, I2C_DEVICE_ADDRESS_MAIN); - if (res == false) { - DbpString("failed to send to SIM CARD"); - goto out; - } - - *resplen = ISO7816_MAX_FRAME; - - res = sc_rx_bytes(resp, resplen, SIM_WAIT_DELAY); - if (res == false) { - DbpString("failed to receive from SIM CARD"); - goto out; - } - - if (*resplen < 2) { - DbpString("received too few bytes from SIM CARD"); - res = false; - goto out; - } - - uint16_t more_len = 0; - - if (resp[*resplen - 2] == 0x61 || resp[*resplen - 2] == 0x9F) { - more_len = resp[*resplen - 1]; +/** + * @brief Sets the card detected status for the SAM (Secure Access Module). + * + * This function informs that a card has been detected by the reader and + * initializes SAM communication with the card. + * + * @param card_select Pointer to the descriptor of the detected card. + * @return Status code indicating success or failure of the operation. + */ +static int sam_set_card_detected(picopass_hdr_t *card_select) { + int res = PM3_SUCCESS; + if (g_dbglevel >= DBG_DEBUG) + DbpString("start sam_set_card_detected"); + + uint8_t *response = BigBuf_malloc(ISO7816_MAX_FRAME); + uint16_t response_len = ISO7816_MAX_FRAME; + + // a0 12 + // ad 10 + // a0 0e + // 80 02 + // 00 04 <- Picopass + // 81 08 + // 9b fc a4 00 fb ff 12 e0 <- CSN + + uint8_t payload[] = { + 0xa0, 18, // <- SAM command + 0xad, 16, // <- set detected card + 0xa0, 4 + 10, + 0x80, 2, // <- protocol + 0x00, 0x04, // <- Picopass + 0x81, 8, // <- CSN + card_select->csn[0], card_select->csn[1], card_select->csn[2], card_select->csn[3], + card_select->csn[4], card_select->csn[5], card_select->csn[6], card_select->csn[7] + }; + uint16_t payload_len = sizeof(payload); + + sam_send_payload( + 0x44, 0x0a, 0x44, + payload, + &payload_len, + response, + &response_len + ); + + // resp: + // c1 64 00 00 00 + // bd 02 <- response + // 8a 00 <- empty response (accepted) + // 90 00 + + if (response[5] != 0xbd) { + if (g_dbglevel >= DBG_ERROR) + Dbprintf("Invalid SAM response"); + goto error; } else { - // we done, return - goto out; - } - - // Don't discard data we already received except the SW code. - // If we only received 1 byte, this is the echo of INS, we discard it. - *resplen -= 2; - if (*resplen == 1) { - *resplen = 0; - } - - uint8_t cmd_getresp[] = {0x00, ISO7816_GET_RESPONSE, 0x00, 0x00, more_len}; - - res = I2C_BufferWrite(cmd_getresp, sizeof(cmd_getresp), I2C_DEVICE_CMD_SEND_T0, I2C_DEVICE_ADDRESS_MAIN); - if (res == false) { - DbpString("failed to send to SIM CARD 2"); - goto out; - } - - more_len = 255 - *resplen; - - res = sc_rx_bytes(resp + *resplen, &more_len, SIM_WAIT_DELAY); - if (res == false) { - DbpString("failed to receive from SIM CARD 2"); + // uint8_t * sam_response_an = sam_find_asn1_node(response + 5, 0x8a); + // if(sam_response_an == NULL){ + // if (g_dbglevel >= DBG_ERROR) + // Dbprintf("Invalid SAM response"); + // goto error; + // } goto out; } - - *resplen += more_len; +error: + res = PM3_ESOFT; out: - StopTicks(); + BigBuf_free(); + + if (g_dbglevel >= DBG_DEBUG) + DbpString("end sam_set_card_detected"); return res; } @@ -218,23 +232,19 @@ int sam_picopass_get_pacs(void) { uint8_t *sam_apdu = BigBuf_calloc(ISO7816_MAX_FRAME); // ----------------------------------------------------------------------------- - // first - // a0 da 02 63 1a 44 0a 44 00 00 00 a0 12 ad 10 a0 0e 80 02 00 04 81 08 9b fc a4 00 fb ff 12 e0 - hexstr_to_byte_array("a0da02631a440a44000000a012ad10a00e800200048108", sam_apdu, &sam_len); - memcpy(sam_apdu + sam_len, hdr.csn, sizeof(hdr.csn)); - sam_len += sizeof(hdr.csn); - - if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) { - res = PM3_ECARDEXCHANGE; - goto out; - } - print_dbg("-- 1", resp, resp_len); + // first - set detected card (0xAD) + switch_clock_to_ticks(); + sam_set_card_detected(&hdr); // ----------------------------------------------------------------------------- - // second - // a0 da 02 63 0d 44 0a 44 00 00 00 a0 05 a1 03 80 01 04 - hexstr_to_byte_array("a0da02630d440a44000000a005a103800104", sam_apdu, &sam_len); - if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) { + // second - get PACS (0xA1) + + // a0 05 + // a1 03 + // 80 01 + // 04 + hexstr_to_byte_array("a005a103800104", sam_apdu, &sam_len); + if (sam_send_payload(0x44, 0x0a, 0x44, sam_apdu, (uint16_t *) &sam_len, resp, &resp_len) != PM3_SUCCESS) { res = PM3_ECARDEXCHANGE; goto out; } @@ -245,7 +255,7 @@ int sam_picopass_get_pacs(void) { // Tag|c00a140a000000a110a10e8004 0c05de64 8102 0004 820201f4 // ----------------------------------------------------------------------------- - // third AIA block 5 + // third AIA block 5 (emulated tag <-> SAM exchange starts here) // a0da02631c140a00000000bd14a012a010800a ffffff0006fffffff88e 81020000 // picopass legacy is fixed. wants AIA and crc. ff ff ff ff ff ff ff ff ea f5 // picpoasss SE ff ff ff 00 06 ff ff ff f8 8e @@ -300,7 +310,7 @@ int sam_picopass_get_pacs(void) { } // start ssp clock again... - StartCountSspClk(); + switch_clock_to_countsspclk(); // NOW we auth against tag uint8_t cmd_check[9] = { ICLASS_CMD_CHECK }; @@ -325,6 +335,7 @@ int sam_picopass_get_pacs(void) { hexstr_to_byte_array("A0DA026316140A00000000BD0EA00CA00A8004311E32E981020000", sam_apdu, &sam_len); memcpy(sam_apdu + 19, mac, sizeof(mac)); + switch_clock_to_ticks(); if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) { res = PM3_ECARDEXCHANGE; goto out; @@ -355,7 +366,7 @@ int sam_picopass_get_pacs(void) { // c1 61 c1 00 00 a1 10 a1 0e 80 04 0c 06 45 56 81 02 00 04 82 02 01 f4 90 00 // read block 6 - StartCountSspClk(); + switch_clock_to_countsspclk(); start_time = GetCountSspClk(); iclass_send_as_reader(resp + 11, 4, &start_time, &eof_time, shallow_mod); @@ -373,6 +384,7 @@ int sam_picopass_get_pacs(void) { hexstr_to_byte_array("A0DA02631C140A00000000BD14A012A010800A030303030003E017432381020000", sam_apdu, &sam_len); memcpy(sam_apdu + 19, resp, resp_len); + switch_clock_to_ticks(); if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) { res = PM3_ECARDEXCHANGE; goto out; @@ -382,7 +394,7 @@ int sam_picopass_get_pacs(void) { // c161c10000a110a10e8004 0606455681020004820201f49000 // read the credential blocks - StartCountSspClk(); + switch_clock_to_countsspclk(); start_time = GetCountSspClk(); iclass_send_as_reader(resp + 11, 4, &start_time, &eof_time, shallow_mod); @@ -400,6 +412,7 @@ int sam_picopass_get_pacs(void) { hexstr_to_byte_array("A0DA026334140A00000000BD2CA02AA0288022030303030003E017769CB4A198E0DEC82AD4C8211F9968712BE7393CF8E71D7E804C81020000", sam_apdu, &sam_len); memcpy(sam_apdu + 19, resp, resp_len); + switch_clock_to_ticks(); if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) { res = PM3_ECARDEXCHANGE; goto out; @@ -409,7 +422,13 @@ int sam_picopass_get_pacs(void) { // ----------------------------------------------------------------------------- // TEN ask for PACS data - // A0DA02630C440A00000000BD04A0028200 + // A0 DA 02 63 0C + // 44 0A 00 00 00 00 + // BD 04 + // A0 02 + // 82 00 + + // (emulated tag <-> SAM exchange ends here) hexstr_to_byte_array("A0DA02630C440A00000000BD04A0028200", sam_apdu, &sam_len); memcpy(sam_apdu + 19, resp, resp_len); @@ -424,7 +443,12 @@ int sam_picopass_get_pacs(void) { goto out; } - // c164000000bd098a07 030506951f9a00 9000 + // resp: + // c1 64 00 00 00 + // bd 09 + // 8a 07 + // 03 05 06 95 1f 9a 00 <- decoded PACS data + // 90 00 uint8_t *pacs = BigBuf_calloc(resp[8]); memcpy(pacs, resp + 9, resp[8]); @@ -439,6 +463,7 @@ int sam_picopass_get_pacs(void) { off: switch_off(); + StopTicks(); BigBuf_free(); return res; } diff --git a/armsrc/sam_picopass.h b/armsrc/sam_picopass.h index 7feef0bdeb..26d734d397 100644 --- a/armsrc/sam_picopass.h +++ b/armsrc/sam_picopass.h @@ -17,6 +17,7 @@ #define __SAM_PICOPASS_H #include "common.h" +#include "sam_common.h" int sam_picopass_get_pacs(void); diff --git a/armsrc/sam_seos.c b/armsrc/sam_seos.c index 00e4da45b1..2e5c093491 100644 --- a/armsrc/sam_seos.c +++ b/armsrc/sam_seos.c @@ -14,9 +14,392 @@ // See LICENSE.txt for the text of the license. //----------------------------------------------------------------------------- // Routines to support SEOS <-> SAM communication +// communication and ASN.1 messages based on https://github.com/bettse/seader/blob/main/seader.asn1 //----------------------------------------------------------------------------- #include "sam_seos.h" +#include "sam_common.h" #include "iclass.h" #include "proxmark3_arm.h" +#include "iso14443a.h" + +#include "iclass.h" +#include "crc16.h" +#include "proxmark3_arm.h" +#include "BigBuf.h" +#include "cmd.h" +#include "commonutil.h" +#include "ticks.h" +#include "dbprint.h" +#include "i2c.h" +#include "protocols.h" +#include "optimized_cipher.h" +#include "fpgaloader.h" +#include "pm3_cmd.h" + #include "cmd.h" + + +/** + * @brief Sets the card detected status for the SAM (Secure Access Module). + * + * This function informs that a card has been detected by the reader and + * initializes SAM communication with the card. + * + * @param card_select Pointer to the descriptor of the detected card. + * @return Status code indicating success or failure of the operation. + */ +static int sam_set_card_detected(iso14a_card_select_t *card_select) { + int res = PM3_SUCCESS; + if (g_dbglevel >= DBG_DEBUG) + DbpString("start sam_set_card_detected"); + + uint8_t *request = BigBuf_malloc(ISO7816_MAX_FRAME); + uint16_t request_len = ISO7816_MAX_FRAME; + + uint8_t *response = BigBuf_malloc(ISO7816_MAX_FRAME); + uint16_t response_len = ISO7816_MAX_FRAME; + + const uint8_t payload[] = { + 0xa0, 8, // <- SAM command + 0xad, 6, // <- set detected card + 0xa0, 4, // <- detected card details + 0x80, 2, // <- protocol + 0x00, 0x02 // <- ISO14443A + }; + + memcpy(request, payload, sizeof(payload)); + sam_append_asn1_node(request, request + 4, 0x81, card_select->uid, card_select->uidlen); + sam_append_asn1_node(request, request + 4, 0x82, card_select->atqa, 2); + sam_append_asn1_node(request, request + 4, 0x83, &card_select->sak, 1); + request_len = request[1] + 2; + + sam_send_payload( + 0x44, 0x0a, 0x44, + request, + &request_len, + response, + &response_len + ); + + // resp: + // c1 64 00 00 00 + // bd 02 <- response + // 8a 00 <- empty response (accepted) + // 90 00 + + if (response[5] != 0xbd) { + if (g_dbglevel >= DBG_ERROR) + Dbprintf("Invalid SAM response"); + goto error; + } else { + // uint8_t * sam_response_an = sam_find_asn1_node(response + 5, 0x8a); + // if(sam_response_an == NULL){ + // if (g_dbglevel >= DBG_ERROR) + // Dbprintf("Invalid SAM response"); + // goto error; + // } + goto out; + } +error: + res = PM3_ESOFT; + +out: + BigBuf_free(); + + if (g_dbglevel >= DBG_DEBUG) + DbpString("end sam_set_card_detected"); + return res; +} + +/** + * @brief Copies the payload from an NFC buffer to a SAM buffer. + * + * Wraps received data from NFC into an ASN1 tree, so it can be transmitted to the SAM . + * + * @param sam_tx Pointer to the SAM transmit buffer. + * @param nfc_rx Pointer to the NFC receive buffer. + * @param nfc_len Length of the data to be copied from the NFC buffer. + * + * @return Length of SAM APDU to be sent. + */ +inline static uint16_t sam_seos_copy_payload_nfc2sam(uint8_t *sam_tx, uint8_t *nfc_rx, uint8_t nfc_len) { + // NFC resp: + // 6f 0c 84 0a a0 00 00 04 40 00 01 01 00 01 90 00 fb e3 + + // SAM req: + // bd 1c + // a0 1a + // a0 18 + // 80 12 + // 6f 0c 84 0a a0 00 00 04 40 00 01 01 00 01 90 00 fb e3 + // 81 02 + // 00 00 + + const uint8_t payload[] = { + 0xbd, 4, + 0xa0, 2, + 0xa0, 0 + }; + + const uint8_t tag81[] = { + 0x00, 0x00 + }; + + memcpy(sam_tx, payload, sizeof(payload)); + + sam_append_asn1_node(sam_tx, sam_tx + 4, 0x80, nfc_rx, nfc_len); + sam_append_asn1_node(sam_tx, sam_tx + 4, 0x81, tag81, sizeof(tag81)); + + return sam_tx[1] + 2; // length of the ASN1 tree +} + +/** + * @brief Copies the payload from the SAM receive buffer to the NFC transmit buffer. + * + * Unpacks data to be transmitted from ASN1 tree in APDU received from SAM. + * + * @param nfc_tx_buf Pointer to the buffer where the NFC transmit data will be stored. + * @param sam_rx_buf Pointer to the buffer containing the data received from the SAM. + * @return Length of NFC APDU to be sent. + */ +inline static uint16_t sam_seos_copy_payload_sam2nfc(uint8_t *nfc_tx_buf, uint8_t *sam_rx_buf) { + // SAM resp: + // c1 61 c1 00 00 + // a1 21 <- nfc command + // a1 1f <- nfc send + // 80 10 <- data + // 00 a4 04 00 0a a0 00 00 04 40 00 01 01 00 01 00 + // 81 02 <- protocol + // 02 02 + // 82 02 <- timeout + // 01 2e + // 85 03 <- format + // 06 c0 00 + // 90 00 + + // NFC req: + // 00 a4 04 00 0a a0 00 00 04 40 00 01 01 00 01 00 + + // copy data out of c1->a1>->a1->80 node + uint16_t nfc_tx_len = (uint8_t) * (sam_rx_buf + 10); + memcpy(nfc_tx_buf, sam_rx_buf + 11, nfc_tx_len); + return nfc_tx_len; +} + +/** + * @brief Sends a request to the SAM and retrieves the response. + * + * Unpacks request to the SAM and relays ISO14A traffic to the card. + * If no request data provided, sends a request to get PACS data. + * + * @param request Pointer to the buffer containing the request to be sent to the SAM. + * @param request_len Length of the request to be sent to the SAM. + * @param response Pointer to the buffer where the retreived data will be stored. + * @param response_len Pointer to the variable where the length of the retreived data will be stored. + * @return Status code indicating success or failure of the operation. + */ +static int sam_send_request_iso14a(const uint8_t *const request, const uint8_t request_len, uint8_t *response, uint8_t *response_len) { + int res = PM3_SUCCESS; + if (g_dbglevel >= DBG_DEBUG) + DbpString("start sam_send_request_iso14a"); + + uint8_t buf1[ISO7816_MAX_FRAME] = {0}; + uint8_t buf2[ISO7816_MAX_FRAME] = {0}; + + uint8_t *sam_tx_buf = buf1; + uint16_t sam_tx_len; + + uint8_t *sam_rx_buf = buf2; + uint16_t sam_rx_len; + + uint8_t *nfc_tx_buf = buf1; + uint16_t nfc_tx_len; + + uint8_t *nfc_rx_buf = buf2; + uint16_t nfc_rx_len; + + if (request_len > 0) { + sam_tx_len = request_len; + memcpy(sam_tx_buf, request, sam_tx_len); + } else { + // send get pacs + static const uint8_t payload[] = { + 0xa0, 19, // <- SAM command + 0xBE, 17, // <- samCommandGetContentElement2 + 0x80, 1, + 0x04, // <- implicitFormatPhysicalAccessBits + 0x84, 12, + 0x2B, 0x06, 0x01, 0x04, 0x01, 0x81, 0xE4, 0x38, 0x01, 0x01, 0x02, 0x04 // <- SoRootOID + }; + + sam_tx_len = sizeof(payload); + memcpy(sam_tx_buf, payload, sam_tx_len); + } + + sam_send_payload( + 0x44, 0x0a, 0x44, + sam_tx_buf, &sam_tx_len, + sam_rx_buf, &sam_rx_len + ); + + if (sam_rx_buf[1] == 0x61) { // commands to be relayed to card starts with 0x61 + // tag <-> SAM exchange starts here + while (sam_rx_buf[1] == 0x61) { + switch_clock_to_countsspclk(); + nfc_tx_len = sam_seos_copy_payload_sam2nfc(nfc_tx_buf, sam_rx_buf); + + nfc_rx_len = iso14_apdu( + nfc_tx_buf, + nfc_tx_len, + false, + nfc_rx_buf, + ISO7816_MAX_FRAME, + NULL + ); + + switch_clock_to_ticks(); + sam_tx_len = sam_seos_copy_payload_nfc2sam(sam_tx_buf, nfc_rx_buf, nfc_rx_len - 2); + + sam_send_payload( + 0x14, 0x0a, 0x14, + sam_tx_buf, &sam_tx_len, + sam_rx_buf, &sam_rx_len + ); + + // last SAM->TAG + // c1 61 c1 00 00 a1 02 >>82<< 00 90 00 + if (sam_rx_buf[7] == 0x82) { + // tag <-> SAM exchange ends here + break; + } + + } + + static const uint8_t hfack[] = { + 0xbd, 0x04, 0xa0, 0x02, 0x82, 0x00 + }; + + sam_tx_len = sizeof(hfack); + memcpy(sam_tx_buf, hfack, sam_tx_len); + + sam_send_payload( + 0x14, 0x0a, 0x00, + sam_tx_buf, &sam_tx_len, + sam_rx_buf, &sam_rx_len + ); + } + + // resp for SamCommandGetContentElement: + // c1 64 00 00 00 + // bd 09 + // 8a 07 + // 03 05 <- include tag for pm3 client + // 06 85 80 6d c0 <- decoded PACS data + // 90 00 + + // resp for samCommandGetContentElement2: + // c1 64 00 00 00 + // bd 1e + // b3 1c + // a0 1a + // 80 05 + // 06 85 80 6d c0 + // 81 0e + // 2b 06 01 04 01 81 e4 38 01 01 02 04 3c ff + // 82 01 + // 07 + // 90 00 + if (request_len == 0) { + if ( + !(sam_rx_buf[5] == 0xbd && sam_rx_buf[5 + 2] == 0x8a && sam_rx_buf[5 + 4] == 0x03) + && + !(sam_rx_buf[5] == 0xbd && sam_rx_buf[5 + 2] == 0xb3 && sam_rx_buf[5 + 4] == 0xa0) + ) { + if (g_dbglevel >= DBG_ERROR) + Dbprintf("No PACS data in SAM response"); + res = PM3_ESOFT; + } + } + + *response_len = sam_rx_buf[5 + 1] + 2; + memcpy(response, sam_rx_buf + 5, *response_len); + + goto out; + +out: + return res; +} + +/** + * @brief Retrieves PACS data from SEOS card using SAM. + * + * This function is called by appmain.c + * It sends a request to the SAM to get the PACS data from the SEOS card. + * The PACS data is then returned to the PM3 client. + * + * @return Status code indicating success or failure of the operation. + */ +int sam_seos_get_pacs(PacketCommandNG *c) { + bool disconnectAfter = c->oldarg[0] & 0x01; + bool skipDetect = c->oldarg[1] & 0x01; + + uint8_t *cmd = c->data.asBytes; + uint16_t cmd_len = (uint16_t) c->oldarg[2]; + + int res = PM3_EFAILED; + + clear_trace(); + I2C_Reset_EnterMainProgram(); + + set_tracing(true); + StartTicks(); + + // step 1: ping SAM + sam_get_version(); + + if (!skipDetect) { + // step 2: get card information + iso14a_card_select_t card_a_info; + + // implicit StartSspClk() happens here + iso14443a_setup(FPGA_HF_ISO14443A_READER_MOD); + if (!iso14443a_select_card(NULL, &card_a_info, NULL, true, 0, false)) { + goto err; + } + + switch_clock_to_ticks(); + + // step 3: SamCommand CardDetected + sam_set_card_detected(&card_a_info); + } + + // step 3: SamCommand RequestPACS, relay NFC communication + uint8_t sam_response[ISO7816_MAX_FRAME] = { 0x00 }; + uint8_t sam_response_len = 0; + res = sam_send_request_iso14a(cmd, cmd_len, sam_response, &sam_response_len); + if (res != PM3_SUCCESS) { + goto err; + } + if (g_dbglevel >= DBG_INFO) + print_result("Response data", sam_response, sam_response_len); + + goto out; + goto off; + +err: + res = PM3_ENOPACS; + reply_ng(CMD_HF_SAM_SEOS, res, NULL, 0); + goto off; +out: + reply_ng(CMD_HF_SAM_SEOS, PM3_SUCCESS, sam_response, sam_response_len); + goto off; +off: + if (disconnectAfter) { + switch_off(); + } + set_tracing(false); + StopTicks(); + BigBuf_free(); + return res; +} diff --git a/armsrc/sam_seos.h b/armsrc/sam_seos.h index c2100f07e4..8a165f255f 100644 --- a/armsrc/sam_seos.h +++ b/armsrc/sam_seos.h @@ -17,5 +17,8 @@ #define __SAM_SEOS_H #include "common.h" +#include "pm3_cmd.h" + +int sam_seos_get_pacs(PacketCommandNG *c); #endif diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 29512e1927..60c24fb49d 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -5434,79 +5434,14 @@ static int CmdHFiClassSAM(const char *Cmd) { // CSN, config, epurse, NR/MAC, AIA // PACS + // 03 05 + // 06 85 80 6d c0 // first byte skip // second byte length // third padded // fourth .. uint8_t *d = resp.data.asBytes; - uint8_t n = d[1] - 1; // skip length byte - uint8_t pad = d[2]; - char *binstr = (char *)calloc((n * 8) + 1, sizeof(uint8_t)); - if (binstr == NULL) { - return PM3_EMALLOC; - } - - bytes_2_binstr(binstr, d + 3, n); - - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, "PACS......... " _GREEN_("%s"), sprint_hex_inrow(d + 2, resp.length - 2)); - PrintAndLogEx(SUCCESS, "padded bin... " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr)); - - binstr[strlen(binstr) - pad] = '\0'; - PrintAndLogEx(SUCCESS, "bin.......... " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr)); - - size_t hexlen = 0; - uint8_t hex[16] = {0}; - binstr_2_bytes(hex, &hexlen, binstr); - PrintAndLogEx(SUCCESS, "hex.......... " _GREEN_("%s"), sprint_hex_inrow(hex, hexlen)); - - uint32_t top = 0, mid = 0, bot = 0; - if (binstring_to_u96(&top, &mid, &bot, binstr) != strlen(binstr)) { - PrintAndLogEx(ERR, "Binary string contains none <0|1> chars"); - free(binstr); - return PM3_EINVARG; - } - - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "Wiegand decode"); - wiegand_message_t packed = initialize_message_object(top, mid, bot, strlen(binstr)); - HIDTryUnpack(&packed); - - PrintAndLogEx(NORMAL, ""); - - if (strlen(binstr) >= 26 && verbose) { - - // iCLASS Legacy - PrintAndLogEx(INFO, "Clone to " _YELLOW_("iCLASS Legacy")); - PrintAndLogEx(SUCCESS, " hf iclass encode --ki 0 --bin %s", binstr); - PrintAndLogEx(NORMAL, ""); - - // HID Prox II - PrintAndLogEx(INFO, "Downgrade to " _YELLOW_("HID Prox II")); - PrintAndLogEx(SUCCESS, " lf hid clone -w H10301 --bin %s", binstr); - PrintAndLogEx(NORMAL, ""); - - // MIFARE Classic - char mfcbin[28] = {0}; - mfcbin[0] = '1'; - memcpy(mfcbin + 1, binstr, strlen(binstr)); - binstr_2_bytes(hex, &hexlen, mfcbin); - - PrintAndLogEx(INFO, "Downgrade to " _YELLOW_("MIFARE Classic") " (Pm3 simulation)"); - PrintAndLogEx(SUCCESS, " hf mf eclr;"); - PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 0 -d 049DBA42A23E80884400C82000000000;"); - PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 1 -d 1B014D48000000000000000000000000;"); - PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 3 -d A0A1A2A3A4A5787788C189ECA97F8C2A;"); - PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 5 -d 020000000000000000000000%s;", sprint_hex_inrow(hex, hexlen)); - PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 7 -d 484944204953787788AA204752454154;"); - PrintAndLogEx(SUCCESS, " hf mf sim --1k -i;"); - PrintAndLogEx(NORMAL, ""); - - PrintAndLogEx(INFO, "Downgrade to " _YELLOW_("MIFARE Classic 1K")); - PrintAndLogEx(SUCCESS, " hf mf encodehid --bin %s", binstr); - PrintAndLogEx(NORMAL, ""); - } - free(binstr); + HIDDumpPACSBits(d + 2, d[1], verbose); return PM3_SUCCESS; } diff --git a/client/src/cmdhfseos.c b/client/src/cmdhfseos.c index fe57b7c768..54e0ce5502 100644 --- a/client/src/cmdhfseos.c +++ b/client/src/cmdhfseos.c @@ -30,6 +30,9 @@ #include "ui.h" #include "cmdhf14a.h" // manufacture #include "protocols.h" // definitions of ISO14A/7816 protocol +#include "cardhelper.h" +#include "wiegand_formats.h" +#include "wiegand_formatutils.h" #include "iso7816/apduinfo.h" // GetAPDUCodeDescription #include "crypto/asn1utils.h" // ASN1 decode / print #include "crypto/libpcrypto.h" // AES decrypt @@ -97,6 +100,17 @@ static const known_algo_t known_algorithm_map[] = { {9, "AES-128_CBC_MODE"}, }; +static const sioMediaTypeName_t sioMediaTypeMapping[] = { + { 0x00, "Unknown"}, + { 0x01, "DESFire"}, + { 0x02, "MIFARE"}, + { 0x03, "iCLASS (PicoPass)"}, + { 0x04, "ISO14443AL4"}, + { 0x06, "MIFARE Plus"}, + { 0x07, "Seos"}, + { 0xFF, "INVALID VALUE"} +}; + static int create_cmac (uint8_t* key, uint8_t* input, uint8_t* out, int input_len, int encryption_algorithm) { uint8_t iv[16] = {0x00}; @@ -1632,10 +1646,145 @@ static int CmdHfSeosList(const char *Cmd) { return CmdTraceListAlias(Cmd, "hf seos", "seos -c"); } +// get a SIO media type based on the UID +// uid[8] tag uid +// returns description of the best match +static const char *getSioMediaTypeInfo(uint8_t uid) { + + for (int i = 0; i < ARRAYLEN(sioMediaTypeMapping); ++i) { + if (uid == sioMediaTypeMapping[i].uid) { + return sioMediaTypeMapping[i].desc; + } + } + + //No match, return default + return sioMediaTypeMapping[ARRAYLEN(sioMediaTypeMapping) - 1].desc; +} + + +static int CmdHfSeosSAM(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf seos sam", + "Extract PACS via a HID SAM\n", + "hf seos sam\n" + "hd seos sam -d a005a103800104 -> get PACS data\n" + ); + + + + void *argtable[] = { + arg_param_begin, + arg_lit0("v", "verbose", "verbose output"), + arg_lit0("k", "keep", "keep the field active after command executed"), + arg_lit0("n", "nodetect", "skip selecting the card and sending card details to SAM"), + arg_lit0("t", "tlv", "decode TLV"), + arg_strx0("d", "data", "", "DER encoded command to send to SAM"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + bool verbose = false; + if (arg_get_lit(ctx, 1)) { + verbose = true; + } + bool disconnectAfter = true; + if (arg_get_lit(ctx, 2)) { + disconnectAfter = false; + } + bool skipDetect = false; + if (arg_get_lit(ctx, 3)) { + skipDetect = true; + } + bool decodeTLV = false; + if (arg_get_lit(ctx, 4)) { + decodeTLV = true; + } + + uint8_t data[PM3_CMD_DATA_SIZE] = {0}; + int datalen = 0; + CLIGetHexBLessWithReturn(ctx, 5, data, &datalen, 0); + + CLIParserFree(ctx); + + if (IsHIDSamPresent(verbose) == false) { + return PM3_ESOFT; + } + + clearCommandBuffer(); + SendCommandMIX(CMD_HF_SAM_SEOS, disconnectAfter, skipDetect, datalen, data, datalen); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_HF_SAM_SEOS, &resp, 4000) == false) { + PrintAndLogEx(WARNING, "SAM timeout"); + return PM3_ETIMEOUT; + } + + switch (resp.status) { + case PM3_SUCCESS: + break; + case PM3_ENOPACS: + PrintAndLogEx(SUCCESS, "No PACS data found. Card empty?"); + return resp.status; + default: + PrintAndLogEx(WARNING, "SAM select failed"); + return resp.status; + } + + uint8_t *d = resp.data.asBytes; + // check for standard SamCommandGetContentElement response + // bd 09 + // 8a 07 + // 03 05 <- tag + length + // 06 85 80 6d c0 <- decoded PACS data + if (d[0] == 0xbd && d[2] == 0x8a && d[4] == 0x03) { + uint8_t pacs_length = d[5]; + uint8_t *pacs_data = d + 6; + int res = HIDDumpPACSBits(pacs_data, pacs_length, verbose); + if (res != PM3_SUCCESS) { + return res; + } + // check for standard samCommandGetContentElement2: + // bd 1e + // b3 1c + // a0 1a + // 80 05 + // 06 85 80 6d c0 + // 81 0e + // 2b 06 01 04 01 81 e4 38 01 01 02 04 3c ff + // 82 01 + // 07 + } else if (d[0] == 0xbd && d[2] == 0xb3 && d[4] == 0xa0) { + const uint8_t *pacs = d + 6; + const uint8_t pacs_length = pacs[1]; + const uint8_t *pacs_data = pacs + 2; + int res = HIDDumpPACSBits(pacs_data, pacs_length, verbose); + if (res != PM3_SUCCESS) { + return res; + } + + const uint8_t *oid = pacs + 2 + pacs_length; + const uint8_t oid_length = oid[1]; + const uint8_t *oid_data = oid + 2; + PrintAndLogEx(SUCCESS, "SIO OID.......: " _GREEN_("%s"), sprint_hex_inrow(oid_data, oid_length)); + + const uint8_t *mediaType = oid + 2 + oid_length; + const uint8_t mediaType_data = mediaType[2]; + PrintAndLogEx(SUCCESS, "SIO Media Type: " _GREEN_("%s"), getSioMediaTypeInfo(mediaType_data)); + + } else { + print_hex(d, resp.length); + } + if (decodeTLV) { + asn1_print(d, d[1] + 2, " "); + } + + return PM3_SUCCESS; +} + static command_t CommandTable[] = { {"-----------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("General") " -----------------------"}, {"help", CmdHelp, AlwaysAvailable, "This help"}, {"list", CmdHfSeosList, AlwaysAvailable, "List SEOS history"}, + {"sam", CmdHfSeosSAM, IfPm3Smartcard, "SAM tests"}, {"-----------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("Operations") " -----------------------"}, {"info", CmdHfSeosInfo, IfPm3NfcBarcode, "Tag information"}, {"pacs", CmdHfSeosPACS, AlwaysAvailable, "Extract PACS Information from card"}, @@ -1643,7 +1792,6 @@ static command_t CommandTable[] = { {"gdf", CmdHfSeosGDF, AlwaysAvailable, "Read an GDF from card"}, {"-----------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("Utils") " -----------------------"}, {"managekeys", CmdHfSeosManageKeys, AlwaysAvailable, "Manage keys to use with SEOS commands"}, - {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdhfseos.h b/client/src/cmdhfseos.h index 33e6d45f5c..f75e070d57 100644 --- a/client/src/cmdhfseos.h +++ b/client/src/cmdhfseos.h @@ -21,8 +21,14 @@ #include "common.h" +// structure and database for uid -> tagtype lookups +typedef struct { + uint8_t uid; + const char *desc; +} sioMediaTypeName_t; + int infoSeos(bool verbose); int CmdHFSeos(const char *Cmd); -int seos_kdf(bool encryption, uint8_t* masterKey, uint8_t keyslot, - uint8_t* adfOid, size_t adfoid_len, uint8_t* diversifier, uint8_t diversifier_len, uint8_t* out, int encryption_algorithm, int hash_algorithm); +int seos_kdf(bool encryption, uint8_t *masterKey, uint8_t keyslot, + uint8_t *adfOid, size_t adfoid_len, uint8_t *diversifier, uint8_t diversifier_len, uint8_t *out, int encryption_algorithm, int hash_algorithm); #endif diff --git a/client/src/wiegand_formats.c b/client/src/wiegand_formats.c index bf08e09081..e3e1461539 100644 --- a/client/src/wiegand_formats.c +++ b/client/src/wiegand_formats.c @@ -1663,3 +1663,75 @@ void HIDUnpack(int idx, wiegand_message_t *packed) { hid_print_card(&card, FormatTable[idx]); } } + +int HIDDumpPACSBits(const uint8_t *const data, const uint8_t length, bool verbose) { + uint8_t n = length - 1; + uint8_t pad = data[0]; + char *binstr = (char *)calloc((length * 8) + 1, sizeof(uint8_t)); + if (binstr == NULL) { + return PM3_EMALLOC; + } + + bytes_2_binstr(binstr, data + 1, n); + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "PACS......... " _GREEN_("%s"), sprint_hex_inrow(data, length)); + PrintAndLogEx(SUCCESS, "padded bin... " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr)); + + binstr[strlen(binstr) - pad] = '\0'; + PrintAndLogEx(SUCCESS, "bin.......... " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr)); + + size_t hexlen = 0; + uint8_t hex[16] = {0}; + binstr_2_bytes(hex, &hexlen, binstr); + PrintAndLogEx(SUCCESS, "hex.......... " _GREEN_("%s"), sprint_hex_inrow(hex, hexlen)); + + uint32_t top = 0, mid = 0, bot = 0; + if (binstring_to_u96(&top, &mid, &bot, binstr) != strlen(binstr)) { + PrintAndLogEx(ERR, "Binary string contains none <0|1> chars"); + free(binstr); + return PM3_EINVARG; + } + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "Wiegand decode"); + wiegand_message_t packed = initialize_message_object(top, mid, bot, strlen(binstr)); + HIDTryUnpack(&packed); + + PrintAndLogEx(NORMAL, ""); + + if (strlen(binstr) >= 26 && verbose) { + + // iCLASS Legacy + PrintAndLogEx(INFO, "Clone to " _YELLOW_("iCLASS Legacy")); + PrintAndLogEx(SUCCESS, " hf iclass encode --ki 0 --bin %s", binstr); + PrintAndLogEx(NORMAL, ""); + + // HID Prox II + PrintAndLogEx(INFO, "Downgrade to " _YELLOW_("HID Prox II")); + PrintAndLogEx(SUCCESS, " lf hid clone -w H10301 --bin %s", binstr); + PrintAndLogEx(NORMAL, ""); + + // MIFARE Classic + char mfcbin[28] = {0}; + mfcbin[0] = '1'; + memcpy(mfcbin + 1, binstr, strlen(binstr)); + binstr_2_bytes(hex, &hexlen, mfcbin); + + PrintAndLogEx(INFO, "Downgrade to " _YELLOW_("MIFARE Classic") " (Pm3 simulation)"); + PrintAndLogEx(SUCCESS, " hf mf eclr;"); + PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 0 -d 049DBA42A23E80884400C82000000000;"); + PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 1 -d 1B014D48000000000000000000000000;"); + PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 3 -d A0A1A2A3A4A5787788C189ECA97F8C2A;"); + PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 5 -d 020000000000000000000000%s;", sprint_hex_inrow(hex, hexlen)); + PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 7 -d 484944204953787788AA204752454154;"); + PrintAndLogEx(SUCCESS, " hf mf sim --1k -i;"); + PrintAndLogEx(NORMAL, ""); + + PrintAndLogEx(INFO, "Downgrade to " _YELLOW_("MIFARE Classic 1K")); + PrintAndLogEx(SUCCESS, " hf mf encodehid --bin %s", binstr); + PrintAndLogEx(NORMAL, ""); + } + free(binstr); + return PM3_SUCCESS; +} diff --git a/client/src/wiegand_formats.h b/client/src/wiegand_formats.h index 671795c9a7..630d9cbb4c 100644 --- a/client/src/wiegand_formats.h +++ b/client/src/wiegand_formats.h @@ -54,6 +54,7 @@ bool HIDPack(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bo bool HIDTryUnpack(wiegand_message_t *packed); void HIDPackTryAll(wiegand_card_t *card, bool preamble); void HIDUnpack(int idx, wiegand_message_t *packed); +int HIDDumpPACSBits(const uint8_t *const data, const uint8_t length, bool verbose); void print_wiegand_code(wiegand_message_t *packed); void print_desc_wiegand(cardformat_t *fmt, wiegand_message_t *packed); #endif diff --git a/common_arm/ticks.c b/common_arm/ticks.c index 10c3dcb723..4abda26892 100644 --- a/common_arm/ticks.c +++ b/common_arm/ticks.c @@ -336,4 +336,5 @@ void WaitUS(uint32_t us) { void StopTicks(void) { AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKDIS; }