Pull to refresh

STM32. Подключаем смарт-карты стандарта ISO7816

Reading time 8 min
Views 61K
О смарт-картах сказано уже немало, но процесс взаимодействия с картами на физическом уровне до недавнего времени оставался для меня загадкой. В своей статье я хотел бы осветить вопрос работы со смарт-картами по интерфейсу, описанному в части 3 стандарта ISO7816. Признаюсь честно, что потратил немало времени добывая информацию, а все оказалось предельно просто. Если интересно, давай под кат.


Сразу оговорюсь, что речь идет о процессоре с аппаратной поддержкой ISO7816 (например, STM32F4xx), написание программного эмулятора — это то еще маньячество, которое имеет место либо если очень сильно «прижало», либо если слишком много свободного времени.

ВЫВОДЫ И СХЕМА ВКЛЮЧЕНИЯ


Итак, что мы имеем на входе? Камень с 3-вольтовым питанием и карточку формата ISO7816-2, вот такую:


  1. VCC — питание
  2. RST — вход сброса
  3. I/O — двунаправленная линия данных
  4. CLK — тактирование
  5. GND — земля
  6. 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 со следующими параметрами:

  1. Бит данных: 8
  2. Стоп-бит: 1,5
  3. Бит паритета: Even (чет)
  4. Скорость (на старте): 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), на что передатчик должен отправить тот же самый кадр еще раз. О полезности этой фичи я особо рассуждать не буду, хотя, сугубо мое мнение, если такие ошибки, в принципе, имеют место быть, то канал обмена можно считать ненадежным, и подобные меры, вряд ли, тут помогут.

С настройкой драйвера, я думаю, все понятно, посему перейдем к процессу инициализации обмена с карточкой.

СТАРТ


Чтобы запустить карточку нужно выполнить «холодный» сброс. Он представляет собой следующую последовательность:

  1. Выставить на RST низкий уровень
  2. Подать питание на VCC
  3. Подать частоту на CLK
  4. Выждать интервал времени, равный 40000 циклам CLK
  5. Выставить на RST высокий уровень
  6. Ждать отклика в течение 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-картах. Всем добра!

ССЫЛКИ


Tags:
Hubs:
+32
Comments 6
Comments Comments 6

Articles