Продолжаю публикацию своей статьи "ASN.1 простыми словами". Предыдущие части статьи, размещённые на Хабре, можно найти здесь: ASN.1 простыми словами (кодирование типа REAL) и ASN.1 простыми словами (часть 2).
Глава 7. Кодирование последовательностей бит (битовых строк)
В разряд типов, кодирующих битовые строки, я отношу два типа — BIT STRING и OCTET STRING.
Тип BIT STRING предназначен для хранения последовательностей наименьших блоков информации — битов. Фактически никакого кодирования не происходит — в блок значения для закодированного значения помещается блок кодируемого значения. За одним только исключением — в первом октете закодированного значения хранится число не используемых бит. Значения количества не используемых бит может изменяться от 0 до 7. Не используемые биты располагаются в битовой строке крайними справа. То есть если закодирована последовательность бит 0000 1111 = 0F, и число не используемых бит равно 4, то тогда при декодировании эту битовую строку следует трактовать как 0000.
При кодировании типа BIT STRING может быть использован как примитивный метод кодирования, так и конструктивный. Конструктивный метод кодирования битовых строк позволяет просто логически разбить одну битовую строку на множество более мелких. При декодировании такой битовой строки, закодированной конструктивным методом, значения из всех вложенных битовых строк объединяются в одну большую битовую строку. В случае использования конструктивного метода задания битовых строк в блоке значения должны быть закодированы только битовые строки, причём у всех битовых подстрок, кроме последней, октет со значением не используемых бит должен быть равен нулю.
Приведем пример конструктивного кодирования битовых строк. Допустим начальная битовая строка содержит значения 0B 0B 0F, причем количество не используемых бит равно 4. Теперь разобьем эту строку на 3 вложенных битовых подстроки и получим следующую последовательность октетов, кодирующую начальную битовую строку в конструктивной форме:
23 0C
03 02 00 0B
03 02 00 0B
03 02 04 0F
Здесь в первых двух октетах приведены идентификационный октет для конструктивной формы кодирования битовой строки и общая длина значения конструктивной формы (12). Далее задано кодирование битовой строки со значением 0B (количество не используемых бит равно нулю!). Потом еще раз закодирована битовая строка со значением 0B (опять обязательно количество не используемых бит должно быть равно нулю). Последней закодирована битовая строка 0F, у которой задано количество не используемых бит равное 4. Таким образом, соединяя последовательно закодированные битовые подстроки, получим изначально закодированную битовую строку 01 01 0 (4 последних бита не используется, а следовательно F = 1111 отсекается от окончательного значения).
Тип OCTET STRING предназначен для хранения простых последовательностей байт (октетов). То есть в этот тип можно закодировать всё что угодно! То есть в этом типе можно кодировать даже содержимое файлов операционной системы. Одно «но» в случае использования «не явного задания длины» (см. главу про основы кодирования в ASN.1) для типа OCTET STRING следует самостоятельно отслеживать появление в кодируемом потоке появления двух нулевых октетов подряд (00 00) и создавать дополнительные вложенные подстроки, разделяющие нулевые октеты во входном потоке октетов.
Глава 8. Кодирование префиксных типов
Зачастую необходимо различать закодированные в одном элементы одного и того же типа, следующие один за другим (например при кодировании типа SET, где порядок вложенных элементов может быть произвольным). Для этого применяют дополнительное кодирование элементов в блоках с новыми, специфичными тэгами. Например типа REAL имеет класс тэга UNIVERSAL (002) и номер тэга 9. Для того чтобы логически отличить два подряд идущих значения REAL применяют дополнительное, «обёрточное» кодирование значения. Для этого используют классы тэгов кроме класса UNIVERSAL (002). То есть вводят новый тип, со своим отличительным номером тэга и классом тэга, отличным от UNIVERSAL (002), а в качестве значения для этого нового типа кодируется весь стандартный блок закодированного типа REAL (новый тип «инкапсулирует» всё закодированное значение другого типа, вместе с блоками идентификационного октета и блоками длины).
Например «обернем» так закодированное значение для типа REAL для значения 0.15625 (кодированное в двоичной форме, основание равно 2, см. главу про кодирование REAL). Полностью это значение кодируется пятью октетами 09 03 80 FB 05. Для внешней «обертки» используем класс тэга PRIVATE (112) и номер тэга выберем равный 2. Так как в качестве значения для внутреннего блока будет использоваться не примитивный тип, а закодированное стандартное значение для типа REAL, то внешняя «обертка» будет иметь конструктивную форму кодирования. Следовательно полностью тип REAL вместе с «оберткой» будет кодироваться с помощью октетов E2 05 09 03 80 FB 05.
В случае, когда между получателем закодированной информации и отправителем существует заранее согласованная и известная схема ASN.1 сообщений, то при использовании «оберточного кодирования» можно не указывать значение информационного октета для внутреннего, «обёрнутого», значения. В этом случае в качестве значения для «обёртки» будет уже использоваться примитивный тип и закодированное значение можно записать как C2 03 80 FB 05 (здесь C2 — указание на применение класса тэга PRIVATE + применение примитивной формы кодирования + номер тэга равен 2; 03 — длина блока значения; оставшиеся октеты — блок значения, непосредственно взятый из блока значения для стандартного кодирования типа REAL). Таким образом можно сказать, что использование заранее согласованных схем ASN.1 позволяет кодировать в выбранных местах общего ASN.1 сообщения выбранные типы с помощью выбранных значений как классов тэгов, так и номеров классов тэгов (полная свобода действий!).
В заключение скажу несколько слов о нотации, которой обозначаются «префиксные типы». Рассмотрим несколько примеров:
Type1 ::= [0] BOOLEAN
Здесь описывается Type1, который имеет класс тэга «Context-specific» (10)2, номер тэга равный 0 и конструктивную форму кодирования. В блоке значения для Type1 передается полностью кодированное значение стандартного типа BOOLEAN.
Type2 ::= [PRIVATE 2] IMPLICIT BOOLEAN
Здесь описывается тип Type2, который имеет класс тэга «Private» (11)2, номер тэга равный 2 и примитивную форму кодирования. То есть в блоке значения для Type2 передается только соответствующий блок значения из стандартно закодированного типа BOOLEAN (идентификационный блок и блок длины теперь берутся из Type2).
То есть по умолчанию в ASN.1 в качестве класса тэга применяется «Context-specific» (10)2, а также применяется конструктивная форма кодирования. То есть первый пример может быть записан в эквивалентном виде (хотя такая запись и не может непосредственно присутствовать в файле, описывающем типы ASN.1):
Type1 ::= [CONTEXT 0] EXPLICIT BOOLEAN
Кстати формально полный эквивалент стандартному типу, например BOOLEAN, может быть записан как:
BOOLEAN_Eq ::= [UNIVERSAL 1] IMPLICIT BOOLEAN
Еще одна интересная ситуация возникает в случае применения нотации IMPLICIT к изначально конструктивным типам, например к типу SEQUENCE. В этом случае в новый тип будет также иметь конструктивную форму, а блок значения для нового типа всё так же будет браться из блока значения кодируемого типа SEQUENCE. Таким образом для нотации IMPLICIT можно обозначить следующие правила:
- Блок значения для нового типа всегда берётся из блока значения кодируемого типа;
- Использование конструктивного или примитивного кодирования зависит от кодируемого типа — тип кодирования нового типа эквивалентен типу кодирования, применяемому в кодируемом типе;
Например в приведенной ниже нотации тип PrimType не смотря на применение IMPLICIT будет иметь конструктивный тип:
ConstType ::= [0] REAL
PrimType ::= [PRIVATE 2] IMPLICIT ConstType
В заключение скажу, что для типа CHOICE по правилам кодирования ASN.1 должна всегда применятся нотация EXPLICIT (конструктивное кодирование).
Глава 9. Кодирование типа SEQUENCE
Тип SEQUENCE служит для логической группировки (объединения) закодированных значений для различных типов. Фактически само название SEQUENCE (в переводе «последовательность») указывает на область применения этого типа. Порядок следования типов в последовательности заранее определен и не может быть изменен.
Кодирование для этого типа является «конструктивным», то есть в блоке с закодированным значением содержаться дополнительные подблоки, кодирующие отдельные значения. За более подробным описанием отсылаю читателя к главе 1 этой статьи с более подробным описанием.
Пример кодирования типа SEQUENCE. Предположим, что в последовательности должны быть закодированы два числа: одно целое (INTEGER), равное -128, и одно число с плавающей точкой (REAL) равное 0.15625 (представленное в разложении по основанию 2). Из соответствующих предыдущих глав можно узнать, что целое число кодируется как последовательность октетов (02 01 80), а число с плавающей запятой кодируется как последовательность октетов (09 03 80 FB 05). Тогда тип SEQUENCE, содержащий эти два закодированных числа будет кодироваться в виде:
30 08 02 01 80 09 03 80 FB 05
Здесь первый октет является идентификационным октетом и информирует о том, что закодировано значение типа SEQUENCE и что используется конструктивный метод кодирования. Второй октет хранит количество октетов, в которых закодировано значение типа SEQUENCE. Следующие три октета представляют закодированное значение целого числа (-128), а последние 5 октетов — значение закодированного числа с плавающей точкой (0.15625).
Глава 10. Кодирование типа SET
Кодирование типа SET фактически полностью соответствует кодированию для типа SEQUENCE за тем исключением, что порядок следования типов, закодированных с помощью SET, может изменяться. То есть если для примера взять числа из главы про тип SEQUENCE, то фактически мы получаем два возможных (абсолютно равноценных) варианта кодирования их для типа SET:
Вариант 1: 31 08 02 01 80 09 03 80 FB 05
Вариант 2: 30 08 09 03 80 FB 05 02 01 80
Второй вариант просто помещает закодированное целое число после закодированного числа с плавающей точкой.
В типе SET также могут быть закодированы два (и более) значений, имеющих одинаковые типы. При декодировании их значения различаются с помощью применения «префиксных типов». О них уже было рассказано в главе 8.
Глава 11. Кодирование типа BOOLEAN
Тип BOOLEAN может кодировать только два значение — или TRUE (истина), или FALSE (ложь). В случае если кодируется значение FALSE, то в блоке значения должен быть только один октет равный 00. В случае если кодируется значение TRUE то в блоке значения должен быть только один октет, значение которого отлично от нуля. То есть следующие два варианта кодирования TRUE для типа BOOLEAN эквивалентны:
Вариант 1: 01 01 01
Вариант 2: 01 01 FF
Глава 12. Кодирование типа NULL
Значение типа NULL всегда постоянно и всегда кодируется всего двумя октетами 05 00, где первый октет является информационным октетом, а второй октет — октет длины, который всегда кодирует нулевую длину.
Заключение для Хабра
Полный текст в формате PDF (вместе с вложенными в PDF дополнительными примерами) можно скачать по прямой ссылке. Лучше для просмотра использовать последние версии Acrobat Reader.
Автор: ystr