mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-14 18:48:13 -07:00
Merge pull request #2704 from jkramarz/feature/sam_seos
support for HID SAM communication with SEOS cards
This commit is contained in:
commit
a531870717
17 changed files with 1142 additions and 143 deletions
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
373
armsrc/sam_common.c
Normal file
373
armsrc/sam_common.c
Normal file
|
@ -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 <string.h>
|
||||
#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();
|
||||
}
|
49
armsrc/sam_common.h
Normal file
49
armsrc/sam_common.h
Normal file
|
@ -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
|
|
@ -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"
|
||||
|
|
|
@ -17,5 +17,6 @@
|
|||
#define __SAM_MFC_H
|
||||
|
||||
#include "common.h"
|
||||
#include "sam_common.h"
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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();
|
||||
/**
|
||||
* @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");
|
||||
|
||||
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;
|
||||
}
|
||||
uint8_t *response = BigBuf_malloc(ISO7816_MAX_FRAME);
|
||||
uint16_t response_len = ISO7816_MAX_FRAME;
|
||||
|
||||
*resplen = 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
|
||||
|
||||
res = sc_rx_bytes(resp, resplen, SIM_WAIT_DELAY);
|
||||
if (res == false) {
|
||||
DbpString("failed to receive from SIM CARD");
|
||||
goto out;
|
||||
}
|
||||
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);
|
||||
|
||||
if (*resplen < 2) {
|
||||
DbpString("received too few bytes from SIM CARD");
|
||||
res = false;
|
||||
goto out;
|
||||
}
|
||||
sam_send_payload(
|
||||
0x44, 0x0a, 0x44,
|
||||
payload,
|
||||
&payload_len,
|
||||
response,
|
||||
&response_len
|
||||
);
|
||||
|
||||
uint16_t more_len = 0;
|
||||
// resp:
|
||||
// c1 64 00 00 00
|
||||
// bd 02 <- response
|
||||
// 8a 00 <- empty response (accepted)
|
||||
// 90 00
|
||||
|
||||
if (resp[*resplen - 2] == 0x61 || resp[*resplen - 2] == 0x9F) {
|
||||
more_len = resp[*resplen - 1];
|
||||
if (response[5] != 0xbd) {
|
||||
if (g_dbglevel >= DBG_ERROR)
|
||||
Dbprintf("Invalid SAM response");
|
||||
goto error;
|
||||
} else {
|
||||
// we done, return
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
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 @@ out:
|
|||
|
||||
off:
|
||||
switch_off();
|
||||
StopTicks();
|
||||
BigBuf_free();
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#define __SAM_PICOPASS_H
|
||||
|
||||
#include "common.h"
|
||||
#include "sam_common.h"
|
||||
|
||||
int sam_picopass_get_pacs(void);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -17,5 +17,8 @@
|
|||
#define __SAM_SEOS_H
|
||||
|
||||
#include "common.h"
|
||||
#include "pm3_cmd.h"
|
||||
|
||||
int sam_seos_get_pacs(PacketCommandNG *c);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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", "<hex>", "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}
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue