refactor(ccid,t1): introduce typed protocol state and named constants

This commit is contained in:
CinderSocket committed 2026-03-06 23:03:57 -08:00
1 parent 9a00e1e515
commit 27a61555b1
7 files changed
+272 -197

No files matched your search

+90 -73
View File
@@ -1,40 +1,52 @@
#include "seader_i.h"
#define TAG "SeaderCCID"
bool hasSAM = false;
const uint8_t SAM_ATR[] =
{0x3b, 0x95, 0x96, 0x80, 0xb1, 0xfe, 0x55, 0x1f, 0xc7, 0x47, 0x72, 0x61, 0x63, 0x65, 0x13};
const uint8_t SAM_ATR2[] = {0x3b, 0x90, 0x96, 0x91, 0x81, 0xb1, 0xfe, 0x55, 0x1f, 0xc7, 0xd4};
//3b95968011fc47726163653c
const uint8_t SAM_ATR3[] = {0x3b, 0x95, 0x96, 0x80, 0x11, 0xfc, 0x47, 0x72, 0x61, 0x63, 0x65, 0x3c};
bool powered[2] = {false, false};
uint8_t sam_slot = 0;
uint8_t sequence[2] = {0, 0};
uint8_t retries = 3;
static SeaderCcidState* seader_ccid_state(SeaderUartBridge* seader_uart) {
return &seader_uart->ccid;
}
uint8_t getSequence(uint8_t slot) {
if(sequence[slot] > 254) {
sequence[slot] = 0;
static SeaderCcidSlotState* seader_ccid_slot_state(SeaderUartBridge* seader_uart, uint8_t slot) {
furi_check(slot < SEADER_CCID_SLOT_COUNT);
return &seader_ccid_state(seader_uart)->slots[slot];
}
static uint8_t seader_ccid_current_slot(SeaderUartBridge* seader_uart) {
return seader_ccid_state(seader_uart)->sam_slot;
}
static void seader_ccid_reset_slot_sequence(SeaderUartBridge* seader_uart, uint8_t slot) {
seader_ccid_slot_state(seader_uart, slot)->sequence = 0;
}
static uint8_t seader_ccid_next_sequence(SeaderUartBridge* seader_uart, uint8_t slot) {
SeaderCcidSlotState* slot_state = seader_ccid_slot_state(seader_uart, slot);
if(slot_state->sequence > 254) {
slot_state->sequence = 0;
}
return sequence[slot]++;
return slot_state->sequence++;
}
void seader_ccid_IccPowerOn(SeaderUartBridge* seader_uart, uint8_t slot) {
if(powered[slot]) {
SeaderCcidSlotState* slot_state = seader_ccid_slot_state(seader_uart, slot);
if(slot_state->powered) {
return;
}
powered[slot] = true;
slot_state->powered = true;
FURI_LOG_D(TAG, "Sending Power On (%d)", slot);
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_IccPowerOn;
seader_uart->tx_buf[2 + 0] = CCID_MESSAGE_TYPE_PC_TO_RDR_ICC_POWER_ON;
seader_uart->tx_buf[2 + 5] = slot;
seader_uart->tx_buf[2 + 6] = getSequence(slot);
seader_uart->tx_buf[2 + 6] = seader_ccid_next_sequence(seader_uart, slot);
seader_uart->tx_buf[2 + 7] = 1; //power
seader_uart->tx_len = seader_add_lrc(seader_uart->tx_buf, 2 + 10);
@@ -42,26 +54,29 @@ void seader_ccid_IccPowerOn(SeaderUartBridge* seader_uart, uint8_t slot) {
}
void seader_ccid_IccPowerOff(SeaderUartBridge* seader_uart, uint8_t slot) {
powered[slot] = false;
seader_ccid_slot_state(seader_uart, slot)->powered = false;
FURI_LOG_D(TAG, "Sending Power Off (%d)", slot);
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_IccPowerOff;
seader_uart->tx_buf[2 + 0] = CCID_MESSAGE_TYPE_PC_TO_RDR_ICC_POWER_OFF;
seader_uart->tx_buf[2 + 5] = slot;
seader_uart->tx_buf[2 + 6] = getSequence(slot);
seader_uart->tx_buf[2 + 6] = seader_ccid_next_sequence(seader_uart, slot);
seader_uart->tx_len = seader_add_lrc(seader_uart->tx_buf, 2 + 10);
furi_thread_flags_set(furi_thread_get_id(seader_uart->tx_thread), WorkerEvtSamRx);
}
void seader_ccid_check_for_sam(SeaderUartBridge* seader_uart) {
hasSAM = false; // If someone is calling this, reset sam state
powered[0] = false;
powered[1] = false;
retries = 3;
SeaderCcidState* ccid_state = seader_ccid_state(seader_uart);
ccid_state->has_sam = false; // If someone is calling this, reset sam state
ccid_state->sam_slot = 0;
ccid_state->retries = 3;
for(size_t slot = 0; slot < SEADER_CCID_SLOT_COUNT; slot++) {
ccid_state->slots[slot].powered = false;
}
seader_ccid_GetSlotStatus(seader_uart, 0);
}
@@ -70,9 +85,9 @@ void seader_ccid_GetSlotStatus(SeaderUartBridge* seader_uart, uint8_t slot) {
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_GetSlotStatus;
seader_uart->tx_buf[2 + 0] = CCID_MESSAGE_TYPE_PC_TO_RDR_GET_SLOT_STATUS;
seader_uart->tx_buf[2 + 5] = slot;
seader_uart->tx_buf[2 + 6] = getSequence(slot);
seader_uart->tx_buf[2 + 6] = seader_ccid_next_sequence(seader_uart, slot);
seader_uart->tx_len = seader_add_lrc(seader_uart->tx_buf, 2 + 10);
furi_thread_flags_set(furi_thread_get_id(seader_uart->tx_thread), WorkerEvtSamRx);
@@ -92,16 +107,16 @@ void seader_ccid_SetParameters(Seader* seader, uint8_t slot) {
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 + 0] = CCID_MESSAGE_TYPE_PC_TO_RDR_SET_PARAMETERS;
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 + 6] = seader_ccid_next_sequence(seader_uart, slot);
seader_uart->tx_buf[2 + 7] = seader_uart->T;
seader_uart->tx_buf[2 + 8] = 0;
seader_uart->tx_buf[2 + 9] = 0;
uint8_t* atr = seader->ATR;
seader_uart->IFSC = atr[5];
seader_uart->t1.ifsc = atr[5];
if(seader_uart->T == 0) {
// I'm leaving this here for completeness, but it was actually causing ICC_MUTE on the first apdu.
@@ -116,7 +131,7 @@ void seader_ccid_SetParameters(Seader* seader, uint8_t slot) {
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] = seader_uart->IFSC; //bIFSC
seader_uart->tx_buf[2 + 15] = seader_uart->t1.ifsc; //bIFSC
seader_uart->tx_buf[2 + 16] = 0x00; //bNadValue
} else if(seader_uart->T == 1) {
seader_uart->tx_buf[2 + 10] = 0x11; //atr[2]; //bmFindexDindex
@@ -124,7 +139,7 @@ void seader_ccid_SetParameters(Seader* seader, uint8_t slot) {
seader_uart->tx_buf[2 + 12] = 0x00; //bGuardTimeT1
seader_uart->tx_buf[2 + 13] = 0x4d; //atr[6]; //bWaitingIntegerT1
seader_uart->tx_buf[2 + 14] = 0x00; //atr[8]; //bClockStop
seader_uart->tx_buf[2 + 15] = seader_uart->IFSC; //bIFSC
seader_uart->tx_buf[2 + 15] = seader_uart->t1.ifsc; //bIFSC
seader_uart->tx_buf[2 + 16] = 0x00; //bNadValue
}
@@ -136,10 +151,11 @@ void seader_ccid_GetParameters(SeaderUartBridge* seader_uart) {
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_GetParameters;
seader_uart->tx_buf[2 + 0] = CCID_MESSAGE_TYPE_PC_TO_RDR_GET_PARAMETERS;
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 + 5] = seader_ccid_current_slot(seader_uart);
seader_uart->tx_buf[2 + 6] =
seader_ccid_next_sequence(seader_uart, seader_ccid_current_slot(seader_uart));
seader_uart->tx_buf[2 + 7] = 0;
seader_uart->tx_buf[2 + 8] = 0;
seader_uart->tx_buf[2 + 9] = 0;
@@ -150,7 +166,7 @@ void seader_ccid_GetParameters(SeaderUartBridge* seader_uart) {
}
void seader_ccid_XfrBlock(SeaderUartBridge* seader_uart, uint8_t* data, size_t len) {
seader_ccid_XfrBlockToSlot(seader_uart, sam_slot, data, len);
seader_ccid_XfrBlockToSlot(seader_uart, seader_ccid_current_slot(seader_uart), data, len);
}
void seader_ccid_XfrBlockToSlot(
@@ -191,13 +207,13 @@ void seader_ccid_XfrBlockToSlot(
frame[0] = SYNC;
frame[1] = CTRL;
frame[2 + 0] = CCID_MESSAGE_TYPE_PC_to_RDR_XfrBlock;
frame[2 + 0] = CCID_MESSAGE_TYPE_PC_TO_RDR_XFR_BLOCK;
frame[2 + 1] = (len >> 0) & 0xff;
frame[2 + 2] = (len >> 8) & 0xff;
frame[2 + 3] = (len >> 16) & 0xff;
frame[2 + 4] = (len >> 24) & 0xff;
frame[2 + 5] = slot;
frame[2 + 6] = getSequence(slot);
frame[2 + 6] = seader_ccid_next_sequence(seader_uart, slot);
frame[2 + 7] = 5;
frame[2 + 8] = 0;
frame[2 + 9] = 0;
@@ -221,6 +237,7 @@ size_t seader_ccid_process(Seader* seader, uint8_t* cmd, size_t cmd_len) {
SeaderUartBridge* seader_uart = seader_worker->uart;
CCID_Message message;
message.consumed = 0;
SeaderCcidState* ccid_state = seader_ccid_state(seader_uart);
char* display = malloc(cmd_len * 2 + 1);
for(uint8_t i = 0; i < cmd_len; i++) {
@@ -230,26 +247,26 @@ size_t seader_ccid_process(Seader* seader, uint8_t* cmd, size_t cmd_len) {
free(display);
if(cmd_len == 2) {
if(cmd[0] == CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange) {
switch(cmd[1] & SLOT_0_MASK) {
if(cmd[0] == CCID_MESSAGE_TYPE_RDR_TO_PC_NOTIFY_SLOT_CHANGE) {
switch(cmd[1] & CCID_SLOT_0_MASK) {
case 0:
case 1:
// No change, no-op
break;
case CARD_IN_1:
case CCID_SLOT_0_CARD_IN:
FURI_LOG_D(TAG, "Card Inserted (0)");
if(hasSAM && sam_slot == 0) {
if(ccid_state->has_sam && ccid_state->sam_slot == 0) {
break;
}
sequence[0] = 0;
seader_ccid_reset_slot_sequence(seader_uart, 0);
seader_ccid_IccPowerOn(seader_uart, 0);
break;
case CARD_OUT_1:
case CCID_SLOT_0_CARD_OUT:
FURI_LOG_D(TAG, "Card Removed (0)");
if(hasSAM && sam_slot == 0) {
powered[0] = false;
hasSAM = false;
retries = 3;
if(ccid_state->has_sam && ccid_state->sam_slot == 0) {
ccid_state->slots[0].powered = false;
ccid_state->has_sam = false;
ccid_state->retries = 3;
if(seader_worker->callback) {
seader_worker->callback(
SeaderWorkerEventSamMissing, seader_worker->context);
@@ -258,25 +275,25 @@ size_t seader_ccid_process(Seader* seader, uint8_t* cmd, size_t cmd_len) {
break;
};
switch(cmd[1] & SLOT_1_MASK) {
switch(cmd[1] & CCID_SLOT_1_MASK) {
case 0:
case 1:
// No change, no-op
break;
case CARD_IN_2:
case CCID_SLOT_1_CARD_IN:
FURI_LOG_D(TAG, "Card Inserted (1)");
if(hasSAM && sam_slot == 1) {
if(ccid_state->has_sam && ccid_state->sam_slot == 1) {
break;
}
sequence[1] = 0;
seader_ccid_reset_slot_sequence(seader_uart, 1);
seader_ccid_IccPowerOn(seader_uart, 1);
break;
case CARD_OUT_2:
case CCID_SLOT_1_CARD_OUT:
FURI_LOG_D(TAG, "Card Removed (1)");
if(hasSAM && sam_slot == 1) {
powered[1] = false;
hasSAM = false;
retries = 3;
if(ccid_state->has_sam && ccid_state->sam_slot == 1) {
ccid_state->slots[1].powered = false;
ccid_state->has_sam = false;
ccid_state->retries = 3;
if(seader_worker->callback) {
seader_worker->callback(
SeaderWorkerEventSamMissing, seader_worker->context);
@@ -332,16 +349,16 @@ size_t seader_ccid_process(Seader* seader, uint8_t* cmd, size_t cmd_len) {
//0306 81 00000000 0000 0200 01 87
//0306 81 00000000 0000 0100 01 84
if(message.bMessageType == CCID_MESSAGE_TYPE_RDR_to_PC_SlotStatus) {
if(message.bMessageType == CCID_MESSAGE_TYPE_RDR_TO_PC_SLOT_STATUS) {
uint8_t status = (message.bStatus & BMICCSTATUS_MASK);
if(status == 0 || status == 1) {
seader_ccid_IccPowerOn(seader_uart, message.bSlot);
return message.consumed;
} else if(status == 2) {
FURI_LOG_W(TAG, "No ICC is present [retries %d]", retries);
if(retries-- > 1 && hasSAM == false) {
} else if(status == CCID_ICC_STATUS_NOT_PRESENT) {
FURI_LOG_W(TAG, "No ICC is present [retries %d]", ccid_state->retries);
if(ccid_state->retries-- > 1 && ccid_state->has_sam == false) {
furi_delay_ms(100);
seader_ccid_GetSlotStatus(seader_uart, retries % 2);
seader_ccid_GetSlotStatus(seader_uart, ccid_state->retries % 2);
} else {
if(seader_worker->callback) {
seader_worker->callback(
@@ -355,7 +372,7 @@ size_t seader_ccid_process(Seader* seader, uint8_t* cmd, size_t cmd_len) {
//0306 80 00000000 0001 42fe 00 38
if(message.bStatus == 0x41 && message.bError == 0xfe) {
FURI_LOG_W(TAG, "card probably upside down");
hasSAM = false;
ccid_state->has_sam = false;
if(seader_worker->callback) {
seader_worker->callback(SeaderWorkerEventSamMissing, seader_worker->context);
}
@@ -370,10 +387,10 @@ size_t seader_ccid_process(Seader* seader, uint8_t* cmd, size_t cmd_len) {
}
if(message.bError != 0) {
switch(message.bError) {
case 0xfe: // CCID_ERROR_ICC_MUTE:
case CCID_ERROR_ICC_MUTE:
FURI_LOG_W(TAG, "CCID error ICC_MUTE");
break;
case 0xfb: // HW_ERROR
case CCID_ERROR_HW_ERROR:
FURI_LOG_W(TAG, "CCID error HW_ERROR");
break;
default:
@@ -387,7 +404,7 @@ 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_Parameters) {
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);
@@ -397,9 +414,9 @@ size_t seader_ccid_process(Seader* seader, uint8_t* cmd, size_t cmd_len) {
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) {
} else if(message.bMessageType == CCID_MESSAGE_TYPE_RDR_TO_PC_DATA_BLOCK) {
if(ccid_state->has_sam) {
if(message.bSlot == ccid_state->sam_slot) {
if(seader_uart->T == 0) {
seader_worker_process_sam_message(
seader, message.payload, message.dwLength);
@@ -412,33 +429,33 @@ size_t seader_ccid_process(Seader* seader, uint8_t* cmd, size_t cmd_len) {
} else {
if(memcmp(SAM_ATR, message.payload, sizeof(SAM_ATR)) == 0) {
FURI_LOG_I(TAG, "SAM ATR!");
hasSAM = true;
sam_slot = message.bSlot;
ccid_state->has_sam = true;
ccid_state->sam_slot = message.bSlot;
seader->ATR_len = sizeof(SAM_ATR);
memcpy(seader->ATR, message.payload, seader->ATR_len);
if(seader_uart->T == 0) {
seader_ccid_GetParameters(seader_uart);
} else if(seader_uart->T == 1) {
seader_ccid_SetParameters(seader, sam_slot);
seader_ccid_SetParameters(seader, ccid_state->sam_slot);
}
} else if(memcmp(SAM_ATR2, message.payload, sizeof(SAM_ATR2)) == 0) {
FURI_LOG_I(TAG, "SAM ATR2!");
hasSAM = true;
sam_slot = message.bSlot;
ccid_state->has_sam = true;
ccid_state->sam_slot = message.bSlot;
seader->ATR_len = sizeof(SAM_ATR);
memcpy(seader->ATR, message.payload, seader->ATR_len);
// I don't have an ATR2 to test with
seader_ccid_GetParameters(seader_uart);
} else if(memcmp(SAM_ATR3, message.payload, sizeof(SAM_ATR3)) == 0) {
FURI_LOG_I(TAG, "SAM ATR3!");
hasSAM = true;
sam_slot = message.bSlot;
ccid_state->has_sam = true;
ccid_state->sam_slot = message.bSlot;
seader->ATR_len = sizeof(SAM_ATR);
memcpy(seader->ATR, message.payload, seader->ATR_len);
if(seader_uart->T == 0) {
seader_ccid_GetParameters(seader_uart);
} else if(seader_uart->T == 1) {
seader_ccid_SetParameters(seader, sam_slot);
seader_ccid_SetParameters(seader, ccid_state->sam_slot);
}
} else {
FURI_LOG_W(TAG, "Unknown ATR");
+52 -45
View File
@@ -22,67 +22,74 @@
*/
// TODO: rename/renumber
#define SLOT_0_MASK 0x03
#define CARD_OUT_1 0x02
#define CARD_IN_1 0x03
#define SLOT_1_MASK 0x0C
#define CARD_IN_2 0x04
#define CARD_OUT_2 0x0C
#define CCID_SLOT_0_MASK 0x03
#define CCID_SLOT_0_CARD_OUT 0x02
#define CCID_SLOT_0_CARD_IN 0x03
#define CCID_SLOT_1_MASK 0x0C
#define CCID_SLOT_1_CARD_IN 0x04
#define CCID_SLOT_1_CARD_OUT 0x0C
/*
* * BULK_OUT messages from PC to Reader
* * Defined in CCID Rev 1.1 6.1 (page 26)
* */
#define CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOn 0x62
#define CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOff 0x63
#define CCID_MESSAGE_TYPE_PC_to_RDR_GetSlotStatus 0x65
#define CCID_MESSAGE_TYPE_PC_to_RDR_XfrBlock 0x6f
#define CCID_MESSAGE_TYPE_PC_to_RDR_GetParameters 0x6c
#define CCID_MESSAGE_TYPE_PC_to_RDR_ResetParameters 0x6d
#define CCID_MESSAGE_TYPE_PC_to_RDR_SetParameters 0x61
#define CCID_MESSAGE_TYPE_PC_to_RDR_Escape 0x6b
#define CCID_MESSAGE_TYPE_PC_to_RDR_IccClock 0x6e
#define CCID_MESSAGE_TYPE_PC_to_RDR_T0APDU 0x6a
#define CCID_MESSAGE_TYPE_PC_to_RDR_Secure 0x69
#define CCID_MESSAGE_TYPE_PC_to_RDR_Mechanical 0x71
#define CCID_MESSAGE_TYPE_PC_to_RDR_Abort 0x72
#define CCID_MESSAGE_TYPE_PC_to_RDR_SetDataRateAndClockFrequency 0x73
typedef enum {
CCID_MESSAGE_TYPE_PC_TO_RDR_ICC_POWER_ON = 0x62,
CCID_MESSAGE_TYPE_PC_TO_RDR_ICC_POWER_OFF = 0x63,
CCID_MESSAGE_TYPE_PC_TO_RDR_GET_SLOT_STATUS = 0x65,
CCID_MESSAGE_TYPE_PC_TO_RDR_XFR_BLOCK = 0x6f,
CCID_MESSAGE_TYPE_PC_TO_RDR_GET_PARAMETERS = 0x6c,
CCID_MESSAGE_TYPE_PC_TO_RDR_RESET_PARAMETERS = 0x6d,
CCID_MESSAGE_TYPE_PC_TO_RDR_SET_PARAMETERS = 0x61,
CCID_MESSAGE_TYPE_PC_TO_RDR_ESCAPE = 0x6b,
CCID_MESSAGE_TYPE_PC_TO_RDR_ICC_CLOCK = 0x6e,
CCID_MESSAGE_TYPE_PC_TO_RDR_T0_APDU = 0x6a,
CCID_MESSAGE_TYPE_PC_TO_RDR_SECURE = 0x69,
CCID_MESSAGE_TYPE_PC_TO_RDR_MECHANICAL = 0x71,
CCID_MESSAGE_TYPE_PC_TO_RDR_ABORT = 0x72,
CCID_MESSAGE_TYPE_PC_TO_RDR_SET_DATA_RATE_AND_CLOCK_FREQUENCY = 0x73,
} SeaderCcidPcToRdrMessageType;
/*
* * BULK_IN messages from Reader to PC
* * Defined in CCID Rev 1.1 6.2 (page 48)
* */
#define CCID_MESSAGE_TYPE_RDR_to_PC_DataBlock 0x80
#define CCID_MESSAGE_TYPE_RDR_to_PC_SlotStatus 0x81
#define CCID_MESSAGE_TYPE_RDR_to_PC_Parameters 0x82
#define CCID_MESSAGE_TYPE_RDR_to_PC_Escape 0x83
#define CCID_MESSAGE_TYPE_RDR_to_PC_DataRateAndClockFrequency 0x84
typedef enum {
CCID_MESSAGE_TYPE_RDR_TO_PC_DATA_BLOCK = 0x80,
CCID_MESSAGE_TYPE_RDR_TO_PC_SLOT_STATUS = 0x81,
CCID_MESSAGE_TYPE_RDR_TO_PC_PARAMETERS = 0x82,
CCID_MESSAGE_TYPE_RDR_TO_PC_ESCAPE = 0x83,
CCID_MESSAGE_TYPE_RDR_TO_PC_DATA_RATE_AND_CLOCK_FREQUENCY = 0x84,
} SeaderCcidRdrToPcMessageType;
/*
* * INTERRUPT_IN messages from Reader to PC
* * Defined in CCID Rev 1.1 6.3 (page 56)
* */
#define CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange 0x50
#define CCID_MESSAGE_TYPE_RDR_to_PC_HardwareError 0x51
typedef enum {
CCID_MESSAGE_TYPE_RDR_TO_PC_NOTIFY_SLOT_CHANGE = 0x50,
CCID_MESSAGE_TYPE_RDR_TO_PC_HARDWARE_ERROR = 0x51,
} SeaderCcidInterruptMessageType;
/* Status codes that go in bStatus (see 6.2.6) */
enum {
ICC_STATUS_PRESENT_ACTIVE = 0,
ICC_STATUS_PRESENT_INACTIVE,
ICC_STATUS_NOT_PRESENT
};
enum {
COMMAND_STATUS_NO_ERROR = 0,
COMMAND_STATUS_FAILED,
COMMAND_STATUS_TIME_EXTENSION_REQUIRED
};
typedef enum {
CCID_ICC_STATUS_PRESENT_ACTIVE = 0,
CCID_ICC_STATUS_PRESENT_INACTIVE = 1,
CCID_ICC_STATUS_NOT_PRESENT = 2,
} SeaderCcidIccStatus;
typedef enum {
CCID_COMMAND_STATUS_NO_ERROR = 0,
CCID_COMMAND_STATUS_FAILED = 1,
CCID_COMMAND_STATUS_TIME_EXTENSION_REQUIRED = 2,
} SeaderCcidCommandStatus;
/* Error codes that go in bError (see 6.2.6) */
enum {
ERROR_CMD_NOT_SUPPORTED = 0,
ERROR_CMD_ABORTED = -1,
ERROR_ICC_MUTE = -2,
ERROR_XFR_PARITY_ERROR = -3,
ERROR_XFR_OVERRUN = -4,
ERROR_HW_ERROR = -5,
};
typedef enum {
CCID_ERROR_CMD_NOT_SUPPORTED = 0x00,
CCID_ERROR_CMD_ABORTED = 0xff,
CCID_ERROR_ICC_MUTE = 0xfe,
CCID_ERROR_XFR_PARITY_ERROR = 0xfd,
CCID_ERROR_XFR_OVERRUN = 0xfc,
CCID_ERROR_HW_ERROR = 0xfb,
} SeaderCcidError;
struct CCID_Message {
uint8_t bMessageType;
+1 -1
View File
@@ -57,7 +57,7 @@ void seader_scene_read_card_success_on_enter(void* context) {
SeaderWorker* seader_worker = seader->worker;
SeaderUartBridge* seader_uart = seader_worker->uart;
seader_t_1_reset();
seader_t_1_reset(seader->uart);
seader_ccid_check_for_sam(seader_uart);
}
+32 -1
View File
@@ -11,6 +11,9 @@
// https://ww1.microchip.com/downloads/en/DeviceDoc/00001561C.pdf
#define SEADER_UART_RX_BUF_SIZE (300)
#define SEADER_CCID_SLOT_COUNT (2U)
typedef struct BitBuffer BitBuffer;
typedef struct {
uint8_t uart_ch;
@@ -23,6 +26,33 @@ typedef struct {
uint8_t protocol;
} SeaderUartState;
typedef struct {
bool powered;
/* CCID sequence counter for this slot. */
uint8_t sequence;
} SeaderCcidSlotState;
typedef struct {
bool has_sam;
uint8_t sam_slot;
uint8_t retries;
SeaderCcidSlotState slots[SEADER_CCID_SLOT_COUNT];
} SeaderCcidState;
typedef struct {
/* ICC information field size for T=1. */
uint8_t ifsc;
/* Host NAD used for T=1 block exchange. */
uint8_t nad;
/* Last transmit I-block sequence bit. */
uint8_t send_pcb;
/* Last receive I-block sequence bit. */
uint8_t recv_pcb;
BitBuffer* tx_buffer;
size_t tx_buffer_offset;
BitBuffer* rx_buffer;
} SeaderT1State;
struct SeaderUartBridge {
SeaderUartConfig cfg;
SeaderUartConfig cfg_new;
@@ -43,7 +73,8 @@ struct SeaderUartBridge {
// T=0 or T=1
uint8_t T;
uint8_t IFSC;
SeaderCcidState ccid;
SeaderT1State t1;
};
typedef struct SeaderUartBridge SeaderUartBridge;
+84 -75
View File
@@ -6,47 +6,53 @@
/* I know my T=1 is terrible, but I'm also only targetting one specific 'card' */
#define MORE_BIT 0x20
#define R_BLOCK 0x80
#define S_BLOCK 0xC0
#define R_SEQUENCE_NUMBER_MASK 0x10
#define T1_S_IFS 0x01
static SeaderT1State* seader_t1_state(SeaderUartBridge* seader_uart) {
return &seader_uart->t1;
}
// 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;
static uint8_t seader_next_dpcb(SeaderUartBridge* seader_uart) {
SeaderT1State* t1 = seader_t1_state(seader_uart);
uint8_t next_pcb = t1->send_pcb ^ SEADER_T1_PCB_SEQUENCE_BIT;
//FURI_LOG_D(TAG, "dPCB was: %02X, current dPCB: %02X", dPCB, next_pcb);
dPCB = next_pcb;
return dPCB;
t1->send_pcb = next_pcb;
return t1->send_pcb;
}
uint8_t seader_next_cpcb() {
uint8_t next_pcb = cPCB ^ 0x40;
static uint8_t seader_next_cpcb(SeaderUartBridge* seader_uart) {
SeaderT1State* t1 = seader_t1_state(seader_uart);
uint8_t next_pcb = t1->recv_pcb ^ SEADER_T1_PCB_SEQUENCE_BIT;
//FURI_LOG_D(TAG, "cPCB was: %02X, current cPCB: %02X", cPCB, next_pcb);
cPCB = next_pcb;
return cPCB;
t1->recv_pcb = next_pcb;
return t1->recv_pcb;
}
void seader_t_1_reset() {
NAD = 0x00;
dPCB = 0x40;
cPCB = 0x00;
void seader_t_1_reset(SeaderUartBridge* seader_uart) {
SeaderT1State* t1 = seader_t1_state(seader_uart);
t1->nad = 0x00;
t1->send_pcb = SEADER_T1_PCB_SEQUENCE_BIT;
t1->recv_pcb = 0x00;
if(t1->tx_buffer != NULL) {
bit_buffer_free(t1->tx_buffer);
}
t1->tx_buffer = NULL;
t1->tx_buffer_offset = 0;
if(t1->rx_buffer != NULL) {
bit_buffer_free(t1->rx_buffer);
}
t1->rx_buffer = NULL;
}
void seader_t_1_set_IFSD(Seader* seader) {
SeaderWorker* seader_worker = seader->worker;
SeaderUartBridge* seader_uart = seader_worker->uart;
SeaderT1State* t1 = seader_t1_state(seader_uart);
uint8_t frame[5];
uint8_t frame_len = 0;
frame[0] = NAD;
frame[1] = S_BLOCK | T1_S_IFS; // S(IFS request)
frame[0] = t1->nad;
frame[1] = SEADER_T1_PCB_S_BLOCK | SEADER_T1_S_BLOCK_IFS; // S(IFS request)
frame[2] = 0x01;
frame[3] = seader_uart->IFSC;
frame[3] = t1->ifsc;
frame_len = 4;
frame_len = seader_add_lrc(frame, frame_len);
@@ -57,13 +63,14 @@ void seader_t_1_set_IFSD(Seader* seader) {
void seader_t_1_IFSD_response(Seader* seader) {
SeaderWorker* seader_worker = seader->worker;
SeaderUartBridge* seader_uart = seader_worker->uart;
SeaderT1State* t1 = seader_t1_state(seader_uart);
uint8_t frame[5];
uint8_t frame_len = 0;
frame[0] = NAD;
frame[0] = t1->nad;
frame[1] = 0xE0 | 0x01; // S(IFS response)
frame[2] = 0x01;
frame[3] = seader_uart->IFSC;
frame[3] = t1->ifsc;
frame_len = 4;
frame_len = seader_add_lrc(frame, frame_len);
@@ -74,11 +81,12 @@ void seader_t_1_IFSD_response(Seader* seader) {
void seader_t_1_send_ack(Seader* seader) {
SeaderWorker* seader_worker = seader->worker;
SeaderUartBridge* seader_uart = seader_worker->uart;
SeaderT1State* t1 = seader_t1_state(seader_uart);
uint8_t frame[4];
uint8_t frame_len = 0;
frame[0] = NAD;
frame[1] = R_BLOCK | (seader_next_cpcb() >> 2);
frame[0] = t1->nad;
frame[1] = SEADER_T1_PCB_R_BLOCK | (seader_next_cpcb(seader_uart) >> 2);
frame[2] = 0x00;
frame_len = 3;
@@ -89,14 +97,12 @@ void seader_t_1_send_ack(Seader* seader) {
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_chunk(SeaderUartBridge* seader_uart, uint8_t PCB, uint8_t* chunk, size_t len) {
SeaderT1State* t1 = seader_t1_state(seader_uart);
uint8_t* frame = malloc(3 + len + 1);
uint8_t frame_len = 0;
frame[0] = NAD;
frame[0] = t1->nad;
frame[1] = PCB;
frame[2] = len;
frame_len = 3;
@@ -117,8 +123,9 @@ void seader_send_t1_scratchpad(
uint8_t PCB,
uint8_t* apdu,
size_t len) {
SeaderT1State* t1 = seader_t1_state(seader_uart);
uint8_t* frame = apdu - 3;
frame[0] = NAD;
frame[0] = t1->nad;
frame[1] = PCB;
frame[2] = (uint8_t)len;
@@ -127,50 +134,51 @@ void seader_send_t1_scratchpad(
}
void seader_send_t1(SeaderUartBridge* seader_uart, uint8_t* apdu, size_t len) {
uint8_t ifsc = seader_uart->IFSC;
SeaderT1State* t1 = seader_t1_state(seader_uart);
uint8_t ifsc = t1->ifsc;
bool in_scratchpad =
(apdu >= seader_uart->tx_buf + 3 && apdu < seader_uart->tx_buf + SEADER_UART_RX_BUF_SIZE);
if(len > ifsc) {
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);
if(t1->tx_buffer == NULL) {
t1->tx_buffer = bit_buffer_alloc(768);
bit_buffer_copy_bytes(t1->tx_buffer, apdu, len);
}
size_t remaining =
(bit_buffer_get_size_bytes(seader_t_1_tx_buffer) - seader_t_1_tx_buffer_offset);
(bit_buffer_get_size_bytes(t1->tx_buffer) - t1->tx_buffer_offset);
size_t copy_length = remaining > ifsc ? ifsc : remaining;
uint8_t* chunk =
(uint8_t*)bit_buffer_get_data(seader_t_1_tx_buffer) + seader_t_1_tx_buffer_offset;
uint8_t* chunk = (uint8_t*)bit_buffer_get_data(t1->tx_buffer) + t1->tx_buffer_offset;
if(remaining > ifsc) {
uint8_t PCB = seader_next_dpcb() | MORE_BIT;
uint8_t PCB = seader_next_dpcb(seader_uart) | SEADER_T1_PCB_I_BLOCK_MORE;
seader_send_t1_chunk(seader_uart, PCB, chunk, copy_length);
} else {
uint8_t PCB = seader_next_dpcb();
uint8_t PCB = seader_next_dpcb(seader_uart);
seader_send_t1_chunk(seader_uart, PCB, chunk, copy_length);
}
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;
t1->tx_buffer_offset += copy_length;
if(t1->tx_buffer_offset >= bit_buffer_get_size_bytes(t1->tx_buffer)) {
bit_buffer_free(t1->tx_buffer);
t1->tx_buffer = NULL;
t1->tx_buffer_offset = 0;
}
return;
}
if(in_scratchpad) {
seader_send_t1_scratchpad(seader_uart, seader_next_dpcb(), apdu, len);
seader_send_t1_scratchpad(seader_uart, seader_next_dpcb(seader_uart), apdu, len);
} else {
seader_send_t1_chunk(seader_uart, seader_next_dpcb(), apdu, len);
seader_send_t1_chunk(seader_uart, seader_next_dpcb(seader_uart), apdu, len);
}
}
BitBuffer* seader_t_1_rx_buffer;
bool seader_recv_t1(Seader* seader, CCID_Message* message) {
SeaderWorker* seader_worker = seader->worker;
SeaderUartBridge* seader_uart = seader_worker->uart;
SeaderT1State* t1 = seader_t1_state(seader_uart);
// remove/validate NAD, PCB, LEN, LRC
if(message->dwLength < 4) {
FURI_LOG_W(TAG, "Invalid T=1 frame: too short");
@@ -193,20 +201,20 @@ bool seader_recv_t1(Seader* seader, CCID_Message* message) {
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);
if(rPCB == t1->recv_pcb) {
seader_next_cpcb(seader_uart);
if(t1->rx_buffer != NULL) {
bit_buffer_append_bytes(t1->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));
(uint8_t*)bit_buffer_get_data(t1->rx_buffer),
bit_buffer_get_size_bytes(t1->rx_buffer));
bit_buffer_free(seader_t_1_rx_buffer);
seader_t_1_rx_buffer = NULL;
bit_buffer_free(t1->rx_buffer);
t1->rx_buffer = NULL;
return true;
}
@@ -223,24 +231,24 @@ bool seader_recv_t1(Seader* seader, CCID_Message* message) {
return true;
}
return seader_worker_process_sam_message(seader, message->payload, message->dwLength);
} else if(rPCB == (cPCB | MORE_BIT)) {
} else if(rPCB == (t1->recv_pcb | SEADER_T1_PCB_I_BLOCK_MORE)) {
//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);
if(t1->rx_buffer == NULL) {
t1->rx_buffer = bit_buffer_alloc(512);
}
bit_buffer_append_bytes(seader_t_1_rx_buffer, message->payload + 3, LEN);
bit_buffer_append_bytes(t1->rx_buffer, message->payload + 3, LEN);
seader_t_1_send_ack(seader);
return false;
} else if((rPCB & S_BLOCK) == S_BLOCK) {
} else if((rPCB & SEADER_T1_PCB_S_BLOCK) == SEADER_T1_PCB_S_BLOCK) {
//FURI_LOG_D(TAG, "Received S-Block");
if((rPCB & 0x0F) == T1_S_IFS) {
if((rPCB & 0x0F) == SEADER_T1_S_BLOCK_IFS) {
seader_t_1_IFSD_response(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;
} else if((rPCB & SEADER_T1_PCB_R_BLOCK) == SEADER_T1_PCB_R_BLOCK) {
uint8_t R_SEQ = (rPCB & SEADER_T1_R_BLOCK_SEQUENCE_MASK) >> 4;
uint8_t I_SEQ = (t1->send_pcb ^ SEADER_T1_PCB_SEQUENCE_BIT) >> 6;
if(R_SEQ != I_SEQ) {
/*
FURI_LOG_D(
@@ -254,19 +262,20 @@ bool seader_recv_t1(Seader* seader, CCID_Message* message) {
return false;
}
if(seader_t_1_tx_buffer != NULL) {
if(t1->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));
(uint8_t*)bit_buffer_get_data(t1->tx_buffer),
bit_buffer_get_size_bytes(t1->tx_buffer));
return false;
}
} else {
FURI_LOG_W(
TAG, "Invalid T=1 frame: PCB mismatch. Expected: %02X, Received: %02X", cPCB, rPCB);
TAG,
"Invalid T=1 frame: PCB mismatch. Expected: %02X, Received: %02X",
t1->recv_pcb,
rPCB);
}
return false;
+10 -1
View File
@@ -4,7 +4,16 @@
typedef struct CCID_Message CCID_Message;
typedef enum {
SEADER_T1_PCB_I_BLOCK_MORE = 0x20,
SEADER_T1_PCB_SEQUENCE_BIT = 0x40,
SEADER_T1_PCB_R_BLOCK = 0x80,
SEADER_T1_PCB_S_BLOCK = 0xC0,
SEADER_T1_R_BLOCK_SEQUENCE_MASK = 0x10,
SEADER_T1_S_BLOCK_IFS = 0x01,
} SeaderT1Constant;
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);
void seader_t_1_reset();
void seader_t_1_reset(SeaderUartBridge* seader_uart);
+3 -1
View File
@@ -148,9 +148,11 @@ int32_t seader_uart_worker(void* context) {
}
SeaderUartBridge* seader_uart_enable(SeaderUartConfig* cfg, Seader* seader) {
SeaderUartBridge* seader_uart = malloc(sizeof(SeaderUartBridge));
SeaderUartBridge* seader_uart = calloc(1, sizeof(SeaderUartBridge));
seader_uart->T = 1;
seader_t_1_reset(seader_uart);
seader_uart->ccid.retries = 3;
memcpy(&(seader_uart->cfg_new), cfg, sizeof(SeaderUartConfig));