Сразу оговорюсь, что речь идет о процессоре с аппаратной поддержкой ISO7816 (например, STM32F4xx), написание программного эмулятора — это то еще маньячество, которое имеет место либо если очень сильно «прижало», либо если слишком много свободного времени.
ВЫВОДЫ И СХЕМА ВКЛЮЧЕНИЯ
Итак, что мы имеем на входе? Камень с 3-вольтовым питанием и карточку формата ISO7816-2, вот такую:
- VCC — питание
- RST — вход сброса
- I/O — двунаправленная линия данных
- CLK — тактирование
- GND — земля
- VPP — вывод программирования
Для входа VCC существует 3 варианта: 1.8 В, 3 В, 5 В (классы карт A, B, C, соответственно), RST служит для сброса машины состояний карты (активный уровень — низкий), I/O — это линия передачи данных, которая представляет собой обычный UART, CLK используется для тактирования процессора карты (если карта в неактивном состоянии, частоту подавать, соответственно, не нужно), вывод VPP используется для программирования карты.
Так подключают карточки настоящие хацкеры:
ИНТЕРФЕЙС
Интерфейс является синхронным режимом USART-драйвера, это значит, что передачу каждого бита информации мы синхронизируем частотой на выводе CLK, но здесь есть одно важное отличие от прочих синхронных интерфейсов (вроде того же SPI): для тактирования одного бита информации нужен не один импульс на CLK, а 372 импульса (это магическое число прописано в 3 части ISO7816, и носит название ETU (Elementary Time Unit)), т.е., один бит данных тактируется каждым 372-м (в идеальном случае) фронтом. Сама частота должна лежать в пределах от 1 до 5 МГц.
Теперь разберемся с линией данных (I/O). Как я уже сказал, это обычный UART со следующими параметрами:
- Бит данных: 8
- Стоп-бит: 1,5
- Бит паритета: Even (чет)
- Скорость (на старте): 9600 Бод
В принципе, ничего более об аппаратных свойствах этого интерфейса, нам знать не нужно. Так что, переходим к настройке драйвера.
НАСТРОЙКА ДРАЙВЕРА
Здесь сразу кину кусок кода инициализации, написанный на Standard Peripheral Library:
RCC_ClocksTypeDef RCC_Clocks;
USART_InitTypeDef USART_InitStructure;
USART_ClockInitTypeDef USART_ClockInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
/// Запросим частоту на шине
RCC_GetClocksFreq(&RCC_Clocks);
/// Включим тактирование драйвера
SC_USART_APB_PERIPH_CLOCK(RCC_APB2Periph_USART1, ENABLE);
/// Зададим предделитель
USART_SetPrescaler(USART1, (RCC_Clocks.PCLK2_Frequency / CLK_FREQ) / 2);
/// Зададим Guard Time
USART_SetGuardTime(USART1, 16);
/// Сконфигурируем синхронную часть (вывод CLK)
USART_ClockInitStructure.USART_Clock = USART_Clock_Enable;
USART_ClockInitStructure.USART_CPOL = USART_CPOL_Low;
USART_ClockInitStructure.USART_CPHA = USART_CPHA_1Edge;
USART_ClockInitStructure.USART_LastBit = USART_LastBit_Enable;
USART_ClockInit(USART1, &USART_ClockInitStructure);
/// Сконфигурируем асинхронную часть (вывод I/O)
USART_InitStructure.USART_BaudRate = CLK_FREQ / ETU;
USART_InitStructure.USART_WordLength = USART_WordLength_9b;
USART_InitStructure.USART_StopBits = USART_StopBits_1_5;
USART_InitStructure.USART_Parity = USART_Parity_Even;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_Init(USART1, &USART_InitStructure);
/// Разрешим передачу NACK
USART_SmartCardNACKCmd(USART1, ENABLE);
/// Включим режим работы со смарт-картами
USART_SmartCardCmd(USART1, ENABLE);
/// Подадим питание на драйвер
USART_Cmd(USART1, ENABLE);
/// Разрешим 2 прерывания (по приему и по ошибке паритета)
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
USART_ITConfig(USART1, USART_IT_PE, ENABLE);
/// Разрешим прерывания соответствующего канала
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
Настройку выводов я опустил, чтобы не загромождать кодом, но здесь есть один важный момент, вывод I/O должен быть настроен как Open-Drain, поскольку стандартом предусмотрена возможность нахождения линии в Z-состоянии, когда карточка сама решает, куда ее подтягивать.
Здесь я хотел бы заострить внимание на двух моментах (предделитель и скорость обмена). Дело здесь в чем? С одной стороны нужно задать скорость 9600, а с другой — частоту, кратную системной. Пожалуй, в большинстве случаев, если не требуется сверхнизкое потребление, системная частота выбирается максимальной (в моем случае это 168 МГц), модуль USART, который я использую, тактируется от шины APB2, максимальная частота которой составляет 84 МГц, значит выбранная нами частота должна попадать в интервал от 1 до 5 МГц и быть кратной 84 МГц, но для скорости 9600 частота будет 9600 * 372 = 3,5712 МГц. Как же тут быть? Разработчики стандарта этот момент предусмотрели и заложили возможное отклонение от номинальных значений вплоть до 20%, т.о., мы можем спокойно округлить частоту, допустим, до 3,5 МГц и выбрать скорость 3500000 / 372 = 9409, расхождение здесь составит менее 2%, что вполне допустимо. Значение предделителя мы должны поделить на 2, поскольку оно задается с шагом 2 (т.е. значение 1 соответствует делению на 2, 2 — на 4, 3 — на 6, и т.д.). У нас получается (84 / 3,5) / 2 = 12:
- Частота (CLK): 3,5 МГц
- Скорость (I/O): 9409 Бод
- Presclaer: 12
Далее, на чем я хотел бы остановиться, — это обработка ошибок паритета. Для этого есть специально предусмотренный интервал времени, который зовется Guard Time (у нас он составляет 16 бит). Что такое Guard Time? Guard Time — это интервал времени, в течение которого приемник должен выставить низкий уровень на линии I/O в случае ошибки паритета (NACK), на что передатчик должен отправить тот же самый кадр еще раз. О полезности этой фичи я особо рассуждать не буду, хотя, сугубо мое мнение, если такие ошибки, в принципе, имеют место быть, то канал обмена можно считать ненадежным, и подобные меры, вряд ли, тут помогут.
С настройкой драйвера, я думаю, все понятно, посему, перейдем к процессу инициализации обмена с карточкой.
СТАРТ
Чтобы запустить карточку нужно выполнить «холодый» сброс. Он представляет собой следующую последовательность:
- Выставить на RST низкий уровень
- Подать питание на VCC
- Подать частоту на CLK
- Выждать интервал времени, равный 40000 циклам CLK
- Выставить на RST высокий уровень
- Ждать отклика в течение 40000 циклов
Все просто, выполняем сброс, ждем отклика. Если первый бит отклика не пришел в течение 40000 циклов (t3), необходимо выставить на RST низкий уровень и деактивировать I/O и CLK.
ATR
Что представляет собой этот отклик? ATR (Answer-to-Reset) представляет собой следующую структуру (размер каждого поля составляет 1 байт):
- TS: Initial character
- TO: Format character
- TAi: Interface character [ codes FI,DI ]
- TBi: Interface character [ codes II,PI1 ]
- TCi: Interface character [ codes N ]
- TDi: Interface character [ codes Yi+1, T ]
- T1,…, TK: Historical characters (max,15)
- TCK: Check character
1. TS — инициирующий байт. Он может принимать одно из двух значений: 3Fh и 3Bh:
- 3Fh — Inverse Convention — инверсная полярность, т.е. 0 передается высоким уровнем, а 1 — низким (важный момент, для контроля паритета здесь будет использоваться odd, т.е., нечет):
- 3Bh — Direct Convention — прямая полярность — то же самое, но с точностью до наоборот (паритет — even, т.е., чет)
2. T0 — байт формата. Состоит из 2-х октетов:
- Y1 (старший октет) — битовая маска, которая показывает, какие поля следуют далее:
- b5 — TA1 передается
- b6 — TB1 передается
- b7 — TC1 передается
- b8 — TD1 передается
- K (младший октет) — число «исторических» байт
3. TA1. Содержит параметры для подстройки частоты:
- FI (старший октет) — делимое
- DI (младший октет) — делитель
4. TB1. Содержит характеристики вывода VPP:
- II (биты b7 — b6) — максимальный ток программирования
- PI (биты b5 — b1) — напряжение программирования
5. TC1. Содержит параметр N — дополнительное приращение Guard Time (задается в единицах ETU), может принимать значение от 0 до 254, значение 255 говорит о том, что интервал между первыми фронтами двух соседних кадров сокращен до 11 ETU.
6. TD1. Здесь небольшая путаница, поскольку ISO7816 не раскрывает структуры этого байта, но в источнике [1] все довольно толково расписано. Состоит он из 2-х октетов:
- Y2 (старший октет) — битовая маска, которая показывает, какие поля следуют далее:
- b5 — TA2 передается
- b6 — TB2 передается
- b7 — TC2 передается
- b8 — TD2 передается
- T (младший октет) — используемый протокол (0 — T0, 1 — T1, остальные значения зарезервированы)
7. TA2. Содержит только один значащий бит (старший), он указывает на возможность переключения на другую версию протокола (0 — переключение возможно, 1 — переключение невозможно), если байт не передается, он считается равным 0
8. T1,…, TK — исторические байты. Содержат информацию о карте, кем, когда она выпущена, и т.д., формат этого поля стандартом не регламентируется
9. TCK — байт контрольной суммы. Вычисляется сложением по модулю 2 (xor) всех предшествующих байт (присутствует только в протоколе T1)
Теперь попробуем разобраться, что здесь для чего нужно. В наибольшей степени нас интересуют поля TA1 и TA2, они указывают нам на то, какие действия мы должны предпринять, а именно, выбрать один из двух режимов:
- Режим «переговоров» (negotiation mode)
- Специфицированный режим (specific mode)
Если старший бит TA2 = 0, то мы используем режим «переговоров», иначе — специфицированный режим.
PTS
Обмен в режиме «переговоров» представляет собой процесс, называемый PTS (Protocol Type Selection). Процесс этот состоит в отправке устройством сопряжения последовательности, которая говорит карте о том, что оно готово применить новые настройки. В свою очередь, карта должна ответить той же последовательностью, после чего и карта и устройство сопряжения могут начинать работать с новыми настройками. О том, какие настройки применить, нам говорит байт TA1 кадра ATR. Параметры Fi и Di это не сами значения, а номера в таблице. По таблице мы можем найти соответствующие этим номерам значения F (Clock rate conversion factor) и D (Bit rate adjustment factor):
Таблица Fi-F.
FI | 0000 | 0001 | 0010 | 0011 | 0100 | 0101 | 0110 | 0111 |
F | internal clk | 372 | 558 | 744 | 1116 | 1488 | 1860 | RFU |
FI | 1000 | 1001 | 1010 | 1011 | 1100 | 1101 | 1110 | 1111 |
F | RFU | 512 | 768 | 1024 | 1536 | 2048 | RFU | RFU |
Таблица Di-D.
DI | 0000 | 0001 | 0010 | 0011 | 0100 | 0101 | 0110 | 0111 |
D | RFU | 1 | 2 | 4 | 8 | 16 | RFU | RFU |
DI | 1000 | 1001 | 1010 | 1011 | 1100 | 1101 | 1110 | 1111 |
D | RFU | RFU | 1/2 | 1/4 | 1/8 | 1/16 | 1/32 | 1/64 |
*RFU — зарезервировано для будущего использования
Частное от деления F и D — это новое значение ETU, т.е. мы сможем выбрать любую частоту и скорость, но принимая во внимание, что соотношение между ними должно быть равно частному F / D.
Теперь подробнее о самом кадре PTS:
- PTSS: Initial character (Mandatory)
- PTS0: Format character (Mandatory)
- PTS1 (Optional)
- PTS2 (Optional)
- PTS3 (Optional)
- PCK: Check character (Mandatory)
1. PTSS — инициирующий байт (всегда FFh)
2. PTS0 — байт формата. Определяет, какие поля присутствуют в кадре, старший октет — битовая маска:
- b5 — PTS1 передается
- b6 — PTS2 передается
- b7 — PTS3 передается
- b8 — всегда 0, зарезервирован
- T (младший октет) — используемый протокол (0 — T0, 1 — T1, остальные значения зарезервированы)
3. PTS1. Содержит запрашиваемые значения Fi и Di, полученные в байте TA1 ATR, если байт не передается, то Fi и Di считаются равными 1.
4. PTS2. Показывает, будет ли применяться параметр N, указанный в TC1 ATR
5. PTS3. Зарезервирован.
6. PCK — байт контрольной суммы. Вычисляется сложением по модулю 2 (xor) всех предшествующих байт.
Все просто, формируем последовательность, отправляем, ждем ответа, сравниваем, если совпало, перестраиваем скорость на Fclk / (F / D).
Если карточка не поддерживает режим «переговоров», просто продолжаем работу.
ПРИМЕР
Для закрепления материала, попробуем разобрать простенький пример. Это обычная Билайновская симка. Вот ATR, который она выкидывает:
3B 3B 94 00 9B 44 20 10 4D AD 40 00 33 90 00 3Bh (TS) - direct convention 3Bh (T0) (0011 1011) - ожидаем TA1 и TB1, число "исторических" байт = 11 94h (TA1) - Fi = 9, Di = 4, находим F и D по таблицам 1 и 2 (F = 512, D = 8), новый ETU = 512 / 8 = 64 00h (TB1) - VPP не поддерживается
Кадр PTS, в этом случае, будет выглядеть следующим образом:
FF 10 94 7B FFh (PTSS) - инициирующий байт 10h (PTS0) (0001 0000) - передаем PTS0, протокол T0 94h (PTS1) = TA1 7Bh (PCK) = xor(FF 10 94)
ЗАКЛЮЧЕНИЕ
В своей статье я опустил некоторые подробности, связанные, например, программированием смарт-карт, а также не стал рассматривать протоколы канального и прикладного уровней, но на это есть несколько причин. Во-первых, каждый из этих пунктов тянет на отдельную статью, если не больше, а во-вторых, информации по протоколу APDU в интернете, на мой взгляд, предостаточно.
Ну что, я очень надеюсь, что мой труд не останется без внимания, или, по крайней мере, удовлетворит любопытство страждущих. Так или иначе, спасибо всем, кто осилил, буду рад ответить на вопросы, да и получить пару другую пинков за косяки. Напоследок, очень советую всем прочитать замечательный цикл статей о криптографических Java-картах. Всем добра!
ССЫЛКИ
This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.
Комментариев нет:
Отправить комментарий