В прошлой части мы видели как происходит общение между терминалом и картой. Мы посмотрели форматы C-APDU и R-APDU, но мы не обращали внимания на то, какие данные содержат эти APDU. В этой части мы рассмотрим самые распространенные форматы, в которых передается информация между терминалом и картой (и наоборот). Все они относятся к одному семейству — TLV.
TLV означает Tag, Length, Value и используется для того, чтобы структурировать информацию. На очень абстрактном уровне, TLV можно рассматривать как бинарную версию XML. Однако что такое Tag, Length, Value?
- Tag: он говорит, какой вид информации находится в TLV. Видом может быть, к примеру, простая строка или номер, идентификатор или даже сложная структура. В некоторых вариантах Tag также содержит мета-информацию о TLV.
- Length: длина, в байтах, элемента Value.
- Value: данные, содержащиеся в TLV
Каждый вариант TLV имеет свои правила кодирования каждого элемента. Далее мы посмотрим самые распространенные варианты TLV. Хочу сразу отметить, что данная статья будет, в основном, посвящена BER-TLV, поскольку это самый распространенный, гибкий и сложный формат. Остальные варианты TLV будут рассмотрены лишь вкратце.
BER-TLV
Итак, говорим о главном герое этой статьи — BER-TLV. Это часть стандарта ASN.1 и используется далеко не только в области смарт-карт.
Tag
Первый байт тэга имеет следующий формат:
b8 | b7 | b6 | b5 | b4 | b3 | b2 | b1 |
---|---|---|---|---|---|---|---|
Класс | Тип | Тэг |
Существуют нижеуказанные классы:
b8 | b7 | Описание |
---|---|---|
0 | 0 | Универсальный — базовые типы данных, такие как строковые данные или номера. Редко используются в смарт-картах |
0 | 1 | Прикладной — типы, смысл которых меняется в зависимости от приложения |
1 | 0 | Контекстно-зависимый — типы, смысл которых зависит от данного составного типа |
1 | 1 | Частный — типы, смысл которых зависит от конкретной организации |
Поскольку в спецификации описано полное значение тэга, запоминать значение классов совершенно лишнее. Класс не влияет на то, как будет обработан тэг.
Наиболее интересным является бит 6. Когда он равен 1, то этот TLV содержит в себе другие TLV. Если, напротив, его значение — 0, то TLV содержит простые данные или данные, структурированные не в формате TLV.
Остальные биты (5-1) — это, как правило, просто номер тэга. Однако если они все единицы, значит номер тэга продолжается на втором байте. Если бит8 второго байта равен 1, то номер продолжается дальше на третем байте и т.д. На практике, в смарт-картах используются только тэги, записанные на одном или двух байтах.
Length
Длина может быть определенной и неопределенной.
Неопределенная длина записывается на одном байте со значением 0x80. Такой способ допустим только для составных TLV (т.е. TLV, содержащих другие TLV). В таком случае TLV должен обязательно содержать ТLV-NULL как последний элемент, который кодируется как «00 00». Подобный способ зачастую используется в более современных стандартах, но, в целом, он не очень распространен.
Определенная длина пишется на одном или более байтах.
Длина данных | Кодирование |
---|---|
0х00 <= x <= 0x7F | значение пишется без изменений на одном байте |
0х80 <= x <= 0xFF | 0x81 и значение на втором байте |
0х100 <= x <= 0xFFFF | 0x82 и значение на втором и третем байтe |
0х10000 <= x <= 0xFFFFFF | 0x83 и значение на следующих трех байтах |
0х1000000 <= x <= 0xFFFFFFFF | 0x84 и значение на следующих четырех байтах |
В принципе данную таблицу можно было бы продолжить и дальше. Тем не менее в области смарт-карт не встречаются огромные TLV. Длину, записанную больше чем на трех байтах (включая префикс 0x82), лично я не встречал. Значения всегда пишутся в формате Big Endian.
Прошу заметить, что в смарт-картах такой способ записывать длину часто используется даже вне TLV (т.е. только LV), но, как правило, с исключением, что значение 0x80 можно также записать как есть, на одном байте.
Value
Value может быть либо последовательностью TLV, либо просто байтами, значение которых зависит от контекста и приложения. Неприемлемо мешать в одном и том же TLV оба вида данных. TLV, содержащиеся в составном TLV, могут быть любого вида — составные либо простые. Этот элемент, отсутствует когда длина TLV равна 0.
Примеры
AID (Application Identifier)
4F 08 A0 00 00 01 51 00 00 00
Tag 0x4F: простой прикладной TLV, в данном примере содержащий 8 байтов (значение которых — Application Identifier, т.е. идентификатор приложения).
Life Cycle State
9F70 01 0F
Tag 0x9F70: простой контекстно-зависимый TLV, в данном примере содержащий 1 байт. Заметьте, что в первом байте тэга (9F) биты 5-1 — все единицы, поэтому тэг продолжается на следующем байте.
Load File Data Block
C4 82 0x01 0x0c 01… еще 266 байтов… 0a
Tag 0xC4: простой частный TLV, в данном примере содержащий 268 байтов. Длина записана на трех байтах, первый из которых — префикс 0x82.
GlobalPlatform Registry related data
E3 11 4F 08 A0 00 00 01 51 00 00 00 9F70 01 0F C5 01 00
Tag 0xE3: составной частный TLV, содержащий другие TLV. В этом примере только он содержит простые TLV. Также допустимо, чтобы содержались составные TLV или смесь составных и простых.
Теоретически это можно было также записывать как
E3 80 4F 08 A0 00 00 01 51 00 00 00 9F70 01 0F C5 01 00 00 00
используя неопределенную длину. На практике, однако, карта принимает (или посылает) неопределенную длину исключительно, когда это разрешено спецификацией.
COMPREHENSION-TLV
COMPREHENSION-TLV описывается в стандарте ETSI TS 101 220. Длина кодируется так же, как и для BER-TLV. Тэг кодируется по следующим таблицам:
На одном байте (0х01 — 0х7Е)
b8 | b7 | b6 | b5 | b4 | b3 | b2 | b1 |
---|---|---|---|---|---|---|---|
CR | Тэг |
На трех байтах (0х0001 — 0х7FFF)
Байт 1 | Байт 2 | Байт 3 | |||||||
---|---|---|---|---|---|---|---|---|---|
b8-b1 | b8 | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b8-b1 |
0x7F | CR | Тэг |
Аббревиатура CR обозначает Comprehension Required. Когда этот бит равен 1, то получатель TLV должен дать обратно ошибку в случае, если он не поддерживает этот TLV. Если, напротив, он равен 0, то получатель вправе игнорировать TLV, если он не поддерживается.
Главное применение COMPREHENSION-TLV — это передача параметров в SIM-Toolkit (SIM-Toolkit используется, среди прочего, для работы SIM-Menu в телефонах). Для повышения совместимости между картами и телефонами необязательные параметры (т.е. те параметры, без которых можно хоть как-то исполнять команду) посылаются с CR = 0.
SIMPLE-TLV
SIMPLE-TLV описывается в стандарте ISO7816-4. В отличиe от BER-TLV в SIMPLE-TLV тэг — это просто номер между 0x01 и 0xFE, не содержащий информацию о классе и типе. Длина пишется следующим образом:
Длина данных | Кодирование |
---|---|
0х00 <= x <= 0xFE | значение пишется без изменений на одном байте |
0хFF <= x <= 0xFFFF | 0xFF и значение на втором и третьем байтe |
DGI (Data Grouping Identifier)
DGI описывается в спецификациях Global Platform Card Specifications и GP Systems Scripting Language Specification. Тэг всегда кодируется на двух байтах и не содержит никакой мета-информации. Длина записывается так же, как и для SIMPLE-TLV. DGI всегда являются составными и, как правило, содержат BER-TLV.
COMPACT-TLV
COMPACT-TLV описывается в стандарте ISO7816-4 и используется исключительно в ATR (Answer to Reset). В этом формате тэг и длина записываются в одном и том же байте. В старшем ниббл пишется тэг, а в младшем — длина.
Итак мы подошли к концу этой части. Если у кого-то остались вопросы, вы можете написать их в комментариях. Следующая часть статьи будет о JavaCard, а после этого мы уже будем непосредственно говорить о Global Platform.
Остальные части статьи
Часть 1. Принципы работы
Часть 2. APDU
Автор: brainnolo