From 29f36f5d26029c3ffb725deeeb337078ed31f554 Mon Sep 17 00:00:00 2001 From: Eric Betts Date: Sun, 14 Jul 2024 20:38:53 -0700 Subject: [PATCH] T=1 support --- ccid.c | 82 ++++++++++++----- sam_api.c | 35 ++++++-- seader_bridge.h | 3 + seader_i.h | 1 + t_1.c | 231 ++++++++++++++++++++++++++++++++++++++++++++++++ t_1.h | 9 ++ uart.c | 3 + 7 files changed, 333 insertions(+), 31 deletions(-) create mode 100644 t_1.c create mode 100644 t_1.h diff --git a/ccid.c b/ccid.c index 2cb4513..fee2751 100644 --- a/ccid.c +++ b/ccid.c @@ -63,23 +63,44 @@ void seader_ccid_GetSlotStatus(SeaderUartBridge* seader_uart, uint8_t slot) { void seader_ccid_SetParameters(Seader* seader, uint8_t slot, uint8_t* atr, size_t atr_len) { SeaderWorker* seader_worker = seader->worker; SeaderUartBridge* seader_uart = seader_worker->uart; - UNUSED(slot); - UNUSED(atr); UNUSED(atr_len); - uint8_t T1 = 1; + FURI_LOG_D(TAG, "seader_ccid_SetParameters(%d)", slot); + + uint8_t payloadLen = 0; + if(seader_uart->T == 0) { + payloadLen = 5; + } else if(atr[4] == 0xB1 && seader_uart->T == 1) { + payloadLen = 7; + } memset(seader_uart->tx_buf, 0, SEADER_UART_RX_BUF_SIZE); seader_uart->tx_buf[0] = SYNC; seader_uart->tx_buf[1] = CTRL; seader_uart->tx_buf[2 + 0] = CCID_MESSAGE_TYPE_PC_to_RDR_SetParameters; - seader_uart->tx_buf[2 + 1] = 0; - seader_uart->tx_buf[2 + 5] = sam_slot; - seader_uart->tx_buf[2 + 6] = getSequence(sam_slot); - seader_uart->tx_buf[2 + 7] = T1; + seader_uart->tx_buf[2 + 1] = payloadLen; + seader_uart->tx_buf[2 + 5] = slot; + seader_uart->tx_buf[2 + 6] = getSequence(slot); + seader_uart->tx_buf[2 + 7] = seader_uart->T; seader_uart->tx_buf[2 + 8] = 0; seader_uart->tx_buf[2 + 9] = 0; - seader_uart->tx_len = seader_add_lrc(seader_uart->tx_buf, 2 + 10); + if(seader_uart->T == 0) { + // I'm leaving this here for completeness, but it was actually causing ICC_MUTE on the first apdu. + seader_uart->tx_buf[2 + 10] = 0x96; //atr[2]; //bmFindexDindex + seader_uart->tx_buf[2 + 11] = 0x00; //bmTCCKST1 + seader_uart->tx_buf[2 + 12] = 0x00; //bGuardTimeT0 + seader_uart->tx_buf[2 + 13] = 0x0a; //bWaitingIntegerT0 + seader_uart->tx_buf[2 + 14] = 0x00; //bClockStop + } else if(seader_uart->T == 1) { + seader_uart->tx_buf[2 + 10] = atr[2]; //bmFindexDindex + seader_uart->tx_buf[2 + 11] = 0x10; //bmTCCKST1 + seader_uart->tx_buf[2 + 12] = 0xfe; //bGuardTimeT1 + seader_uart->tx_buf[2 + 13] = atr[6]; //bWaitingIntegerT1 + seader_uart->tx_buf[2 + 14] = atr[8]; //bClockStop + seader_uart->tx_buf[2 + 15] = atr[5]; //bIFSC + seader_uart->tx_buf[2 + 16] = 0x00; //bNadValue + } + seader_uart->tx_len = seader_add_lrc(seader_uart->tx_buf, 2 + 10 + payloadLen); furi_thread_flags_set(furi_thread_get_id(seader_uart->tx_thread), WorkerEvtSamRx); } @@ -121,14 +142,16 @@ void seader_ccid_XfrBlockToSlot( seader_uart->tx_buf[2 + 8] = 0; seader_uart->tx_buf[2 + 9] = 0; - memcpy(seader_uart->tx_buf + 2 + 10, data, len); - seader_uart->tx_len = seader_add_lrc(seader_uart->tx_buf, 2 + 10 + len); + uint8_t header_len = 2 + 10; + memcpy(seader_uart->tx_buf + header_len, data, len); + seader_uart->tx_len = header_len + len; + seader_uart->tx_len = seader_add_lrc(seader_uart->tx_buf, seader_uart->tx_len); char display[SEADER_UART_RX_BUF_SIZE * 2 + 1] = {0}; for(uint8_t i = 0; i < seader_uart->tx_len; i++) { snprintf(display + (i * 2), sizeof(display), "%02x", seader_uart->tx_buf[i]); } - FURI_LOG_D(TAG, "seader_ccid_XfrBlock %d bytes: %s", seader_uart->tx_len, display); + FURI_LOG_D(TAG, "seader_ccid_XfrBlockToSlot(%d) %d: %s", slot, seader_uart->tx_len, display); furi_thread_flags_set(furi_thread_get_id(seader_uart->tx_thread), WorkerEvtSamRx); } @@ -319,10 +342,25 @@ size_t seader_ccid_process(Seader* seader, uint8_t* cmd, size_t cmd_len) { return message.consumed; } - if(message.bMessageType == CCID_MESSAGE_TYPE_RDR_to_PC_DataBlock) { + if(message.bMessageType == CCID_MESSAGE_TYPE_RDR_to_PC_Parameters) { + FURI_LOG_D(TAG, "Got Parameters"); + if(seader_uart->T == 1) { + seader_t_1_set_IFSD(seader); + } else { + seader_worker_send_version(seader); + if(seader_worker->callback) { + seader_worker->callback(SeaderWorkerEventSamPresent, seader_worker->context); + } + } + } else if(message.bMessageType == CCID_MESSAGE_TYPE_RDR_to_PC_DataBlock) { if(hasSAM) { if(message.bSlot == sam_slot) { - seader_worker_process_sam_message(seader, message.payload, message.dwLength); + if(seader_uart->T == 0) { + seader_worker_process_sam_message( + seader, message.payload, message.dwLength); + } else if(seader_uart->T == 1) { + seader_recv_t1(seader, &message); + } } else { FURI_LOG_D(TAG, "Discarding message on non-sam slot"); } @@ -331,20 +369,18 @@ size_t seader_ccid_process(Seader* seader, uint8_t* cmd, size_t cmd_len) { FURI_LOG_I(TAG, "SAM ATR!"); hasSAM = true; sam_slot = message.bSlot; - seader_worker_send_version(seader); - if(seader_worker->callback) { - seader_worker->callback( - SeaderWorkerEventSamPresent, seader_worker->context); + if(seader_uart->T == 0) { + seader_ccid_GetParameters(seader_uart); + } else if(seader_uart->T == 1) { + seader_ccid_SetParameters( + seader, sam_slot, message.payload, message.dwLength); } } else if(memcmp(SAM_ATR2, message.payload, sizeof(SAM_ATR2)) == 0) { FURI_LOG_I(TAG, "SAM ATR2!"); hasSAM = true; sam_slot = message.bSlot; - seader_worker_send_version(seader); - if(seader_worker->callback) { - seader_worker->callback( - SeaderWorkerEventSamPresent, seader_worker->context); - } + // I don't have an ATR2 to test with + seader_ccid_GetParameters(seader_uart); } else { FURI_LOG_W(TAG, "Unknown ATR"); if(seader_worker->callback) { @@ -353,7 +389,7 @@ size_t seader_ccid_process(Seader* seader, uint8_t* cmd, size_t cmd_len) { } } } else { - FURI_LOG_W(TAG, "Unhandled CCID message type %d", message.bMessageType); + FURI_LOG_W(TAG, "Unhandled CCID message type %02x", message.bMessageType); } } diff --git a/sam_api.c b/sam_api.c index 195e5ee..238964c 100644 --- a/sam_api.c +++ b/sam_api.c @@ -6,7 +6,6 @@ #define TAG "SAMAPI" -#define APDU_HEADER_LEN 5 #define ASN1_PREFIX 6 #define ASN1_DEBUG true #define SEADER_ICLASS_SR_SIO_BASE_BLOCK 10 @@ -137,6 +136,7 @@ void seader_picopass_state_machine(Seader* seader, uint8_t* buffer, size_t len) bit_buffer_free(rx_buffer); } +uint8_t APDU_HEADER_LEN = 5; bool seader_send_apdu( Seader* seader, uint8_t CLA, @@ -144,12 +144,16 @@ bool seader_send_apdu( uint8_t P1, uint8_t P2, uint8_t* payload, - uint8_t length) { + uint8_t payloadLen) { SeaderWorker* seader_worker = seader->worker; SeaderUartBridge* seader_uart = seader_worker->uart; - if(APDU_HEADER_LEN + length > SEADER_UART_RX_BUF_SIZE) { - FURI_LOG_E(TAG, "Cannot send message, too long: %d", APDU_HEADER_LEN + length); + if(seader_uart->T == 1) { + APDU_HEADER_LEN = 7; + } + + if(APDU_HEADER_LEN + payloadLen > SEADER_UART_RX_BUF_SIZE) { + FURI_LOG_E(TAG, "Cannot send message, too long: %d", APDU_HEADER_LEN + payloadLen); return false; } @@ -158,16 +162,30 @@ bool seader_send_apdu( apdu[1] = INS; apdu[2] = P1; apdu[3] = P2; - apdu[4] = length; - memcpy(apdu + APDU_HEADER_LEN, payload, length); + + if(seader_uart->T == 1) { + apdu[4] = 0x00; + apdu[5] = 0x00; + apdu[6] = payloadLen; + } else { + apdu[4] = payloadLen; + } + + memcpy(apdu + APDU_HEADER_LEN, payload, payloadLen); + uint8_t length = APDU_HEADER_LEN + payloadLen; memset(display, 0, sizeof(display)); - for(uint8_t i = 0; i < APDU_HEADER_LEN + length; i++) { + for(uint8_t i = 0; i < length; i++) { snprintf(display + (i * 2), sizeof(display), "%02x", apdu[i]); } FURI_LOG_D(TAG, "seader_send_apdu %s", display); - seader_ccid_XfrBlock(seader_uart, apdu, APDU_HEADER_LEN + length); + if(seader_uart->T == 1) { + seader_send_t1(seader_uart, apdu, length); + } else { + seader_ccid_XfrBlock(seader_uart, apdu, length); + } + return true; } @@ -629,6 +647,7 @@ void seader_iso15693_transmit( uint8_t* buffer, size_t len) { SeaderWorker* seader_worker = seader->worker; + BitBuffer* tx_buffer = bit_buffer_alloc(len); BitBuffer* rx_buffer = bit_buffer_alloc(SEADER_POLLER_MAX_BUFFER_SIZE); diff --git a/seader_bridge.h b/seader_bridge.h index 66602f4..0c68abe 100644 --- a/seader_bridge.h +++ b/seader_bridge.h @@ -42,6 +42,9 @@ struct SeaderUartBridge { uint8_t rx_buf[SEADER_UART_RX_BUF_SIZE]; uint8_t tx_buf[SEADER_UART_RX_BUF_SIZE]; size_t tx_len; + + // T=0 or T=1 + uint8_t T; }; typedef struct SeaderUartBridge SeaderUartBridge; diff --git a/seader_i.h b/seader_i.h index 44735e0..7664489 100644 --- a/seader_i.h +++ b/seader_i.h @@ -51,6 +51,7 @@ #include "ccid.h" #include "uart.h" #include "lrc.h" +#include "t_1.h" #include "seader_worker.h" #include "seader_credential.h" diff --git a/t_1.c b/t_1.c new file mode 100644 index 0000000..5ac85bb --- /dev/null +++ b/t_1.c @@ -0,0 +1,231 @@ +#include "t_1.h" + +#define TAG "Seader:T=1" + +// http://www.sat-digest.com/SatXpress/SmartCard/ISO7816-4.htm + +/* I know my T=1 is terrible, but I'm also only targetting one specific 'card' */ + +#define MORE_BIT 0x20 +#define IFSD_VALUE 0xfe +#define IFSC_VALUE 0xfe // Fom the SAM ATR +#define R_BLOCK 0x80 +#define R_SEQUENCE_NUMBER_MASK 0x10 + +// TODO: T1 struct +uint8_t NAD = 0x00; +uint8_t dPCB = 0x40; // Init to 0x40 so first call to next_pcb will return 0x00 +uint8_t cPCB = 0x00; // Init to 0x40 so first call to next_pcb will return 0x00 + +uint8_t seader_next_dpcb() { + uint8_t next_pcb = dPCB ^ 0x40; + //FURI_LOG_D(TAG, "dPCB was: %02X, current dPCB: %02X", dPCB, next_pcb); + dPCB = next_pcb; + return dPCB; +} + +uint8_t seader_next_cpcb() { + uint8_t next_pcb = cPCB ^ 0x40; + //FURI_LOG_D(TAG, "cPCB was: %02X, current cPCB: %02X", cPCB, next_pcb); + cPCB = next_pcb; + return cPCB; +} + +void seader_t_1_set_IFSD(Seader* seader) { + SeaderWorker* seader_worker = seader->worker; + SeaderUartBridge* seader_uart = seader_worker->uart; + uint8_t frame[5]; + uint8_t frame_len = 0; + + frame[0] = NAD; + frame[1] = 0xC1; // S(IFS request) + frame[2] = 0x01; + frame[3] = IFSD_VALUE; + frame_len = 4; + + frame_len = seader_add_lrc(frame, frame_len); + + seader_ccid_XfrBlock(seader_uart, frame, frame_len); +} + +void seader_t_1_send_ack(Seader* seader) { + SeaderWorker* seader_worker = seader->worker; + SeaderUartBridge* seader_uart = seader_worker->uart; + uint8_t frame[4]; + uint8_t frame_len = 0; + + frame[0] = NAD; + frame[1] = R_BLOCK | (seader_next_cpcb() >> 2); + frame[2] = 0x00; + frame_len = 3; + + frame_len = seader_add_lrc(frame, frame_len); + + //FURI_LOG_D(TAG, "Sending R-Block ACK: PCB: %02x", frame[1]); + + seader_ccid_XfrBlock(seader_uart, frame, frame_len); +} + +BitBuffer* seader_t_1_tx_buffer; +size_t seader_t_1_tx_buffer_offset = 0; + +void seader_send_t1(SeaderUartBridge* seader_uart, uint8_t* apdu, size_t len) { + uint8_t frame[SEADER_UART_RX_BUF_SIZE]; + uint8_t frame_len = 0; + + if(len > IFSC_VALUE) { + if(seader_t_1_tx_buffer == NULL) { + seader_t_1_tx_buffer = bit_buffer_alloc(768); + bit_buffer_copy_bytes(seader_t_1_tx_buffer, apdu, len); + } + size_t remaining = + (bit_buffer_get_size_bytes(seader_t_1_tx_buffer) - seader_t_1_tx_buffer_offset); + size_t copy_length = remaining > IFSC_VALUE ? IFSC_VALUE : remaining; + + frame[0] = NAD; + if(remaining > IFSC_VALUE) { + frame[1] = seader_next_dpcb() | MORE_BIT; + } else { + frame[1] = seader_next_dpcb(); + } + frame[2] = copy_length; + frame_len = 3; + + memcpy( + frame + frame_len, + bit_buffer_get_data(seader_t_1_tx_buffer) + seader_t_1_tx_buffer_offset, + copy_length); + frame_len += copy_length; + + frame_len = seader_add_lrc(frame, frame_len); + + /* + FURI_LOG_D( + TAG, + "Sending T=1 frame %s more bit set. Remaining: %d, Copy Length: %d", + (remaining > IFSC_VALUE) ? "with" : "without", + remaining, + copy_length); + */ + + seader_ccid_XfrBlock(seader_uart, frame, frame_len); + + seader_t_1_tx_buffer_offset += copy_length; + if(seader_t_1_tx_buffer_offset >= bit_buffer_get_size_bytes(seader_t_1_tx_buffer)) { + bit_buffer_free(seader_t_1_tx_buffer); + seader_t_1_tx_buffer = NULL; + seader_t_1_tx_buffer_offset = 0; + } + return; + } + + frame[0] = NAD; + frame[1] = seader_next_dpcb(); + frame[2] = len; + frame_len = 3; + + if(len > 0) { + memcpy(frame + frame_len, apdu, len); + frame_len += len; + } + + frame_len = seader_add_lrc(frame, frame_len); + + seader_ccid_XfrBlock(seader_uart, frame, frame_len); +} + +BitBuffer* seader_t_1_rx_buffer; + +bool seader_recv_t1(Seader* seader, CCID_Message* message) { + // remove/validate NAD, PCB, LEN, LRC + if(message->dwLength < 4) { + FURI_LOG_W(TAG, "Invalid T=1 frame: too short"); + return false; + } + //uint8_t NAD = message->payload[0]; + uint8_t rPCB = message->payload[1]; + uint8_t LEN = message->payload[2]; + //uint8_t LRC = message->payload[3 + LEN]; + //FURI_LOG_D(TAG, "NAD: %02X, rPCB: %02X, LEN: %02X, LRC: %02X", NAD, rPCB, LEN, LRC); + + if(rPCB == 0xE1) { + // S(IFS response) + seader_worker_send_version(seader); + SeaderWorker* seader_worker = seader->worker; + if(seader_worker->callback) { + seader_worker->callback(SeaderWorkerEventSamPresent, seader_worker->context); + } + return false; + } + + if(rPCB == cPCB) { + seader_next_cpcb(); + if(seader_t_1_rx_buffer != NULL) { + bit_buffer_append_bytes(seader_t_1_rx_buffer, message->payload + 3, LEN); + + // TODO: validate LRC + + seader_worker_process_sam_message( + seader, + (uint8_t*)bit_buffer_get_data(seader_t_1_rx_buffer), + bit_buffer_get_size_bytes(seader_t_1_rx_buffer)); + + bit_buffer_free(seader_t_1_rx_buffer); + seader_t_1_rx_buffer = NULL; + return true; + } + + if(seader_validate_lrc(message->payload, message->dwLength) == false) { + return false; + } + + // Skip NAD, PCB, LEN + message->payload = message->payload + 3; + message->dwLength = LEN; + + if(message->dwLength == 0) { + //FURI_LOG_D(TAG, "Received T=1 frame with no data"); + return true; + } + return seader_worker_process_sam_message(seader, message->payload, message->dwLength); + } else if(rPCB == (cPCB | MORE_BIT)) { + //FURI_LOG_D(TAG, "Received T=1 frame with more bit set"); + if(seader_t_1_rx_buffer == NULL) { + seader_t_1_rx_buffer = bit_buffer_alloc(512); + } + bit_buffer_append_bytes(seader_t_1_rx_buffer, message->payload + 3, LEN); + seader_t_1_send_ack(seader); + return false; + } else if((rPCB & R_BLOCK) == R_BLOCK) { + uint8_t R_SEQ = (rPCB & R_SEQUENCE_NUMBER_MASK) >> 4; + uint8_t I_SEQ = (dPCB ^ 0x40) >> 6; + if(R_SEQ != I_SEQ) { + /* + FURI_LOG_D( + TAG, + "Received R-Block: Incorrect sequence. Expected: %02X, Received: %02X", + I_SEQ, + R_SEQ); + + */ + // When this happens, the flipper freezes if it is doing NFC and my attempts to do events to stop that have failed + return false; + } + + if(seader_t_1_tx_buffer != NULL) { + // Send more data, re-using the buffer to trigger the code path that sends the next block + SeaderWorker* seader_worker = seader->worker; + SeaderUartBridge* seader_uart = seader_worker->uart; + seader_send_t1( + seader_uart, + (uint8_t*)bit_buffer_get_data(seader_t_1_tx_buffer), + bit_buffer_get_size_bytes(seader_t_1_tx_buffer)); + return false; + } + } else { + FURI_LOG_W( + TAG, "Invalid T=1 frame: PCB mismatch. Expected: %02X, Received: %02X", cPCB, rPCB); + } + + return false; +} diff --git a/t_1.h b/t_1.h new file mode 100644 index 0000000..7f8355a --- /dev/null +++ b/t_1.h @@ -0,0 +1,9 @@ +#pragma once + +#include "ccid.h" + +typedef struct CCID_Message CCID_Message; + +void seader_send_t1(SeaderUartBridge* seader_uart, uint8_t* apdu, size_t len); +bool seader_recv_t1(Seader* seader, CCID_Message* message); +void seader_t_1_set_IFSD(Seader* seader); diff --git a/uart.c b/uart.c index 3929572..2e7632b 100644 --- a/uart.c +++ b/uart.c @@ -159,6 +159,9 @@ int32_t seader_uart_worker(void* context) { SeaderUartBridge* seader_uart_enable(SeaderUartConfig* cfg, Seader* seader) { SeaderUartBridge* seader_uart = malloc(sizeof(SeaderUartBridge)); + seader_uart->T = 0; + seader_uart->T = (seader->is_debug_enabled ? 1 : 0); + memcpy(&(seader_uart->cfg_new), cfg, sizeof(SeaderUartConfig)); seader_uart->thread =