/*
*******************************************************************************
\file der.h
\brief Distinguished Encoding Rules
\project bee2 [cryptographic library]
\created 2014.04.21
\version 2025.08.08
\copyright The Bee2 authors
\license Licensed under the Apache License, Version 2.0 (see LICENSE.txt).
*******************************************************************************
*/

/*!
*******************************************************************************
\file der.h
\brief Отличительные правила кодирования
*******************************************************************************
*/

#ifndef __BEE2_DER_H
#define __BEE2_DER_H

#include "bee2/defs.h"

#ifdef __cplusplus
extern "C" {
#endif

/*!
*******************************************************************************
\file der.h

\section der-enc Правила кодирования

Поддержано кодирование по правилам АСН.1. Согласно этим правилам, структуры 
данных представляются в формате TLV: сначала идут октеты тега (T), затем 
октеты длины (L), а после этого L октетов значения (V).

Тег задается, как минимум, одним октетом, в котором младшие 5 битов
представляют номер, 6-й бит является признаком примитивности /
конструктивности (если бит снят / установлен, то V не содержит / содержит
вложенные структуры TLV), а старшие два бита определяют класс тега:
- 00 => UNIVERSAL;
- 01 => APPLICATION;
- 10 => CONTEXT-SPECIFIC;
- 11 => PRIVATE.

Если не все 5 младших битов первого октета тега равняются 1,
то первый октет является единственным (короткий тег). Короткие теги
могут иметь номера от 0 до 30.

Если все 5 младших битов типа первого октета тега равняются 1
(длинный тег), то номер тега представляется в виде
	\sum {i = 0}^{r - 1}t_i 128^i, 0 <= t_i < 128, t_{r - 1} != 0,
и кодируется октетами
	(t_{r - 1} | 128), ..., (t_1 | 128), t_0,
следующими сразу за первым октетом тега.

Длинные теги должен применяться только для номеров >= 31. В частности,
если r == 1, то t_0 >= 31.

Ограничение реализации: тег задается словом u32, которое является готовым кодом.
Кодируются собственно тег-как-число, класс тега и признак примитивности.

Примеры:
- tag = 0x30 = 00110000 =>
	class = UNIVERSAL, is_primitive = FALSE, tag_num = 8;
- tag = 0x7F21 = 01111111 00100001 =>
	class = APPLICATION, is_primitive = FALSE, tag_num = 33;
- tag = 0x5F29 = 01011111 00101001 =>
	class = APPLICATION, is_primitive = TRUE, tag_num = 41.
- tag = 0x42 = 01000010 =>
	class = APPLICATION, is_primitive = TRUE, tag_num = 2.

\remark Конфигурация
	(class = UNIVERSAL, is_primitive = TRUE, tag_num = 0),
которой соответствует нулевое кодовое слово, разрешена.

\subsection der-length Длина (L)

Если L < 128, то длина может кодироваться одним октетом, содержащим L
(короткая явная форма). Если L >= 128, то длина представляется в виде
	L = \sum {i = 0}^{r - 1}l_i 256^i, 0 <= l_i < 256,
и кодируется r + 1 октетами
	(r | 128), l_{r - 1},..., l_0
(длинная явная форма). Заранее неизвестная длина кодируется октетом 128
(неявная форма). Значение 255 первого октета длины зарезервировано и не
должно использоваться.

При кодировании примитивных типов (см. 6-й бит тега) должна использоваться
явная форма длины. Других ограничений в базовых правилах кодирования АСН.1,
которые называются BER (Basic Encoding Rules), нет. Например, допускается
выбор длинной формы для L < 128, допускается использование в длинной
форме нулевых старших октетов (l_{r - 1} может равняться 0).

Реализованные в настоящем модуле правила DER (Distinguished Encoding Rules) 
снимают неоднозначности. По правилам DER длина должна кодироваться всегда 
в явной форме и всегда с помощью минимального числа октетов. Последнее 
означает, что при L < 128 должна применяться короткая форма, 
а при L >= 128 -- длинная форма с l_{r - 1} != 0.

Ограничение реализации: длина укладывается в size_t.

Декодируемый DER-код записывается в виде [<=count]der.
Подразумевается, что код записан в префиксе буфера [count]der.
Точная длина DER-кода (сумма длин полей T, L и V, причем длина V указана в L)
определяется в процессе декодирования.

\remark Допускается передача в функции нулевых входных указателей.

\pre Соответствующий ненулевому указателю буфер корректен.

\pre Входные буферы не пересекаются, если не оговорено противное.
*******************************************************************************
*/

/*!	\brief Кодирование тега и длины

	Определяется число октетов в TL-префиксе DER-кода с тегом tag и значением
	длины val. Если der != 0, то префикс размещается по этому адресу.
	\pre Если der != 0, то по адресу der зарезервировано derEnc(0, tag, len)
	октетов.
	\return Число октетов в TL-префиксе или SIZE_MAX в случае ошибки.
	\remark Ошибкой является неверный формат tag.
*/
size_t derTLEnc(
	octet der[],		/*!< [out] DER-код */
	u32 tag,			/*!< [in] тег */
	size_t len			/*!< [in] длина значения в октетах */
);

/*!	\brief Кодирование

	Определяется число октетов в DER-коде значения [len]val с тегом tag. 
	Если der != 0, то DER-код размещается по этому адресу.
	\pre Если der != 0, то val != 0 и по адресу der зарезервировано
	derEnc(0, tag, val, len) октетов.
	\return Число октетов в DER-коде или SIZE_MAX в случае ошибки.
	\remark Буферы der и val могут пересекаться.
	\remark Ошибкой является неверный формат tag.
	\remark Разрешаются вызовы derEnc(0, tag, val, len),
	derEnc(0, tag, 0, len).
*/
size_t derEnc(
	octet der[],		/*!< [out] DER-код */
	u32 tag,			/*!< [in] тег */
	const void* val,	/*!< [in] значение */
	size_t len			/*!< [in] длина val в октетах */
);

/*!	\brief Корректный код?

	Проверяется корректность DER-кода [count]der. Проверяются следующие 
	условия:
	- тег T и длина L закодированы по правилам ACH.1;
	- тег укладывается в u32 (ограничение реализации);
	- длина укладывается в size_t (органичение реализации);
	- count действительно является длиной кода.
	\return Признак корректности.
	\remark Содержимое V не проверяется.
*/
bool_t derIsValid(
	const octet der[],	/*!< [in] DER-код */
	size_t count		/*!< [in] длина der в октетах */
);

/*!	\brief Корректный код с ожидаемым тегом?

	Проверяется корректность DER-кода [count]der с ожидаемым тегом tag. 
	Проверяются следующие условия:
	- derIsValid(der, count) == TRUE;
	- der имеет тег tag.
	\expect{FALSE} Тег tag корректен.
	\return Признак корректности.
	\remark Содержимое V не проверяется.
*/
bool_t derIsValid2(
	const octet der[],	/*!< [in] DER-код */
	size_t count,		/*!< [in] длина der в октетах */
	u32 tag				/*!< [in] тег */
);

/*!	\brief Корректный код с учетом вложенности?

	Проверяется корректность DER-кода [count]der.
	Проверяются следующие условия:
	- derIsValid(der, count) == TRUE;
	- если der примитивен, т.е. не содержит вложенных der-полей, и имеет
	  распознаваемый реализацией тег (см. далее), то он корректно декодируется;
	- если der конструктивен, т.е. содержит вложенные der-поля, то
	  derIsValid(d, count) == TRUE для каждого такого поля d;
	- глубина вложенности не превышает 32.
	\return Признак корректности.
*/
bool_t derIsValid3(
	const octet der[],	/*!< [in] DER-код */
	size_t count		/*!< [in] длина der в октетах */
);

/*!	\brief Начинается с тега?

	Проверяется, что DER-код [count]der начинается с тега tag.
	\expect{FALSE} Тег tag корректен.
	\return Признак корректности.
*/
bool_t derStartsWith(
	const octet der[],	/*!< [in] DER-код */
	size_t count,		/*!< [in] длина der в октетах */
	u32 tag				/*!< [in] тег */
);

/*!	\brief Декодирование тега и длины

	Определяются тег tag и длина значения len DER-кода с TL-префиксом
	[<=count]der.
	\return Точная длина TL-префикса или SIZE_MAX в случае ошибки формата.
	\remark Любой из указателей tag и len может быть нулевым.
*/
size_t derTLDec(
	u32* tag,				/*!< [out] тег */
	size_t* len,			/*!< [out] длина значения */
	const octet der[],		/*!< [in] DER-код */
	size_t count			/*!< [in] длина der в октетах */
);

/*!	\brief Декодирование

	Определяются тег tag и значение [len?]val DER-кода [<=count]der.
	При этом val указывает на буфер памяти внутри буфера der.
	\return Точная длина DER-кода или SIZE_MAX в случае ошибки формата.
	\remark Любой из указателей tag, val и len может быть нулевым.
*/
size_t derDec(
	u32* tag,				/*!< [out] тег */
	const octet** val,		/*!< [out] указатель на значение */
	size_t* len,			/*!< [out] длина значения */
	const octet der[],		/*!< [in] DER-код */
	size_t count			/*!< [in] длина der в октетах */
);

/*!	\brief Декодирование с проверкой тега

	Проверяется, что тег DER-кода [<=count]der равняется tag и, если это так,
	определяется закодированное значение [len?]val. При этом val указывает
	на буфер памяти внутри буфера der.
	\remark Любой из указателей val, len может быть нулевым, и тогда возврат
	данных по указателю не производится.
	\pre Буферы, на которые ссылаются ненулевые указатели val и len,
	не пересекаются между собой и с буфером der.
	\return Точная длина DER-кода или SIZE_MAX в случае ошибки.
*/
size_t derDec2(
	const octet** val,		/*!< [out] значение */
	size_t* len,			/*!< [out] длина значения */
	const octet der[],		/*!< [in] DER-код */
	size_t count,			/*!< [in] длина der в октетах */
	u32 tag					/*!< [in] тег */
);

/*!	\brief Декодирование с проверкой тега и длины

	Проверяется, что тег и длина значения DER-кода [<=count]der равняются
	соответственно tag и len и, если это так, определяется закодированное
	значение [len]val. При этом val указывает на буфер памяти внутри
	буфера der.
	\remark Указатель val может быть нулевым, и тогда возврат данных по
	указателю не производится. 
	\pre Если val != 0, то соответствующий буфер не пересекается с буфером der.
	\return Точная длина DER-кода или SIZE_MAX в случае ошибки.
*/
size_t derDec3(
	const octet** val,		/*!< [out] значение */
	const octet der[],		/*!< [in] DER-код */
	size_t count,			/*!< [in] длина der в октетах */
	u32 tag,				/*!< [in] тег */
	size_t len				/*!< [in] длина значения */
);

/*!	\brief Проверка кода

	Проверяется, что тег и значение DER-кода [<=count]der равняются
	соответственно tag и [len]val.
	\remark Величина count является оценкой сверху актуальной длины DER-кода.
	\return Точная длина DER-кода или SIZE_MAX в случае ошибки.
*/
size_t derDec4(
	const octet der[],		/*!< [in] DER-код */
	size_t count,			/*!< [in] длина der в октетах */
	u32 tag,				/*!< [in] тег */
	const void* val,		/*!< [in] значение */
	size_t len				/*!< [in] длина значения */
);

/*!
*******************************************************************************
\file der.h DER-кодирование

\section der-base Базовые типы

Реализовано кодирование следующих типов АСН.1:
- INTEGER (0x02, SIZE, UINT);
- BIT STRING (0x03, BIT);
- OCTET STRING (0x04, OCT);
- NULL (0x05, NULL);
- OBJECT IDENTIFIER (0x06, OID);
- PrintableString (0x13, PSTR);
- SEQUENCE (0x30, SEQ).

В скобках указывается стандартный код тега типа и короткое имя. Короткое имя
входит в название функций и макросов работы с типом.

Для некоторых типов реализована поддержка нестандартных тегов.
В соответствующих функциях и макросах к короткому имени добавляется префикс T:
TSIZE, TBIT и т.д.

Тип INTEGER описывает целые числа. При кодировании числа поле V имеет вид
	o1 o2 ... on,
где o1 -- старший октет числа, on -- младший. Установка старшего бита в o1 --
признак отрицательного числа. Должно использоваться минимальное число октетов
для однозначного представления числа.

Примеры:
- der(0) = 02 01 00
- der(127) = 02 01 7F
- der(128) = 02 02 00 80
- der(256) = 02 02 01 00
- der(-128) = 02 01 80
- der(-129) = 02 02 FF 7F

Особенности реализации:
- не поддерживаются отрицательные целые;
- кодируемые числа -- это либо значения типа size_t (SIZE), либо строки
  октетов (UINT);
- в последнем случае (UINT) действуют правила little-endian.

Строка битов (BIT STRING) представляется в памяти строкой октетов.
Биты строки нумеруются от нуля. При этом нулевой бит является старшим битом
первого октета представления, седьмой бит -- младшим битом первого октета,
восьмой бит --- старшим битом второго октета и так далее. Если длина строки
не кратна 8, то она предварительно дополняется нулями. Они будут находиться
в младших битах последнего октета представления. Число дописанных нулевых
битов (от 0 до 7) фиксируется в дополнительном октете, который предшествует
октетам представления.

Идентификатор объекта (OBJECT IDENTIFIER) задается строкой по правилам
модуля oid.h.

При кодировании контейнера (структуры SEQUENCE) длина вложенных данных
становится окончательно известна только в конце кодирования. Для уточнения
длины в начале кодирования сохраняется ссылка на закодированный префикс
структуры. Эта ссылка называется якорем, описывается типом der_anchor_t и
используется при завершении кодирования.

Якорь используется также при декодировании контейнера: сохраняется в начале,
учитывается в конце для проверки кода.

\remark Значение, которое определяется при декодировании SIZE, UINT, BIT,
OCT и OID, возвращается по указанному извне адресу, а не в виде указателя
на участок декодируемого кода (ср. с поведением базовых функций derDecXXX()).
*******************************************************************************
*/

/*
*******************************************************************************
Тип SIZE
*******************************************************************************
*/

/*!	\brief Кодирование TSIZE

	Определяется число октетов в DER-коде с тегом tag неотрицательного
	целого val. Если der != 0, то DER-код размещается по этому адресу.
	\pre Если der != 0, то по адресу der зарезервировано
	derTSIZEEnc(0, tag, val) октетов.
	\return Число октетов в DER-коде или SIZE_MAX в случае ошибки.
*/
size_t derTSIZEEnc(
	octet der[],	/*!< [out] DER-код */
	u32 tag,		/*!< [in] тег */
	size_t val		/*!< [in] значение */
);

/*!	\brief Кодирование SIZE */
#define derSIZEEnc(der, val) derTSIZEEnc(der, 0x02, val)

/*!	\brief Декодирование TSIZE

	Проверяется, что тег DER-кода [<=count]der равняется tag и, если это так,
	определяется закодированное неотрицательное целое val.
	\return Точная длина DER-кода или SIZE_MAX в случае оишбки.
*/
size_t derTSIZEDec(
	size_t* val,		/*!< [out] значение */
	const octet der[],	/*!< [in] DER-код */
	size_t count,		/*!< [in] длина der в октетах */
	u32 tag				/*!< [in] тег */
);

/*!	\brief Кодирование SIZE */
#define derSIZEDec(val, der, count) derTSIZEDec(val, der, count, 0x02)

/*!	\brief Проверка кода TSIZE

	Проверяется, что DER-код [<=count]der имеет тег tag и представляет
	неотрицательное целое val.
	\return Точная длина DER-кода или SIZE_MAX в случае оишбки.
*/
size_t derTSIZEDec2(
	const octet der[],	/*!< [in] DER-код */
	size_t count,		/*!< [in] длина der в октетах */
	u32 tag,			/*!< [in] тег */
	size_t val			/*!< [in] значение */
);

/*!	\brief Проверка кода SIZE */
#define derSIZEDec2(der, count, val) derTSIZEDec2(der, count, 0x02, val)

/*
*******************************************************************************
Тип UINT
*******************************************************************************
*/

/*!	\brief Кодирование TUINT

	Определяется число октетов в DER-коде с тегом tag неотрицательного
	целого [len]val. Если der != 0, то DER-код размещается по этому адресу.
	\pre len > 0.
	\pre Если der != 0, то по адресу der зарезервировано
	derTUINTEnc(0, tag, val, len) октетов.
	\return Число октетов в DER-коде или SIZE_MAX в случае ошибки.
	\remark Поддерживается логика derEnc(), в частности, буферы der и val
	могут пересекаться.
*/
size_t derTUINTEnc(
	octet der[],		/*!< [out] DER-код */
	u32 tag,			/*!< [in] тег */
	const octet* val,	/*!< [in] число */
	size_t len			/*!< [in] длина val в октетах */
);

/*!	\brief Кодирование UINT */
#define derUINTEnc(der, val, len) derTUINTEnc(der, 0x02, val, len)

/*!	\brief Декодирование TUINT

	Проверяется, что тег DER-кода [<=count]der равняется tag и, если это так,
	определяется закодированное неотрицательное целое число [len?]val.
	\remark Любой из указателей val и len может быть нулевым.
	\pre Буферы, на которые ссылаются ненулевые указатели val и len,
	не пересекаются между собой, но могут пересекаться с буфером der.
	\return Точная длина DER-кода или SIZE_MAX в случае ошибки.
*/
size_t derTUINTDec(
	octet* val,			/*!< [out] число */
	size_t* len,		/*!< [out] длина val в октетах */
	const octet der[],	/*!< [in] DER-код */
	size_t count,		/*!< [in] длина der в октетах */
	u32 tag				/*!< [in] тег */
);

/*!	\brief Декодирование UINT */
#define derUINTDec(val, len, der, count) \
	derTUINTDec(val, len, der, count, 0x02)

/*!	\brief Декодирование TUINT с проверкой длины

	Проверяется, что DER-код [<=count]der имеет тег tag и представляет
	неотрицательное число в точности из len октетов и, если это так,
	определяется закодированное значение [len]val.
	\remark Указатель val может быть нулевым.
	\remark Если val != 0, то соответствующий буфер может пересекаться с der.
	\return Точная длина DER-кода или SIZE_MAX в случае ошибки.
	\remark Число val записывается в точности len октетами, если в записи
	исключены незначащие нулевые октеты. В частности,
		val[len - 1] == 0 <=> len == 1.
*/
size_t derTUINTDec2(
	octet* val,			/*!< [out] строка битов */
	const octet der[],	/*!< [in] DER-код */
	size_t count,		/*!< [in] длина der в октетах */
	u32 tag,			/*!< [in] тег */
	size_t len			/*!< [in] длина val в битах */
);

/*!	\brief Декодирование UINT с проверкой длины */
#define derUINTDec2(val, der, count, len) \
	derTUINTDec2(val, der, count, 0x02, len)

/*
*******************************************************************************
Тип BIT
*******************************************************************************
*/

/*!	\brief Кодирование TBIT

	Определяется число октетов в DER-коде с тегом tag строки битов
	[(len + 7)/8]val. Если der != 0, то DER-код размещается по этому адресу.
	\pre Если der != 0, то по адресу der зарезервировано
	derTBITEnc(0, tag, val, len) октетов.
	\return Число октетов в DER-коде или SIZE_MAX в случае ошибки.
	\remark Поддерживается логика derEnc(), в частности, буферы der и val
	могут пересекаться.
*/
size_t derTBITEnc(
	octet der[],		/*!< [out] DER-код */
	u32 tag,			/*!< [in] тег */
	const octet* val,	/*!< [in] строка битов */
	size_t len			/*!< [in] длина val в битах */
);

/*!	\brief Кодирование BIT */
#define derBITEnc(der, val, len) derTBITEnc(der, 0x03, val, len)

/*!	\brief Декодирование TBIT

	Проверяется, что тег DER-кода [<=count]der равняется tag и, если это так,
	определяется закодированная строка битов [(len? + 7)/8]val.
	\remark Любой из указателей val и len может быть нулевым.
	\pre Буферы, на которые ссылаются ненулевые указатели val и len,
	не пересекаются между собой, но могут пересекаться с буфером der.
	\return Точная длина DER-кода или SIZE_MAX в случае ошибки.
*/
size_t derTBITDec(
	octet* val,			/*!< [out] строка битов */
	size_t* len,		/*!< [out] длина val в битах */
	const octet der[],	/*!< [in] DER-код */
	size_t count,		/*!< [in] длина der в октетах */
	u32 tag				/*!< [in] тег */
);

/*!	\brief Декодирование BIT */
#define derBITDec(val, len, der, count) derTBITDec(val, len, der, count, 0x03)

/*!	\brief Декодирование TBIT с проверкой длины

	Проверяется, что DER-код [<=count]der имеет тег tag и представляет строку
	битов длины len и, если это так, определяется закодированное значение
	[(len + 7)/8]val.
	\remark Указатель val может быть нулевым.
	\remark Если val != 0, то соответствующий буфер может пересекаться с der.
	\return Точная длина DER-кода или SIZE_MAX в случае ошибки.
*/
size_t derTBITDec2(
	octet* val,			/*!< [out] строка битов */
	const octet der[],	/*!< [in] DER-код */
	size_t count,		/*!< [in] длина der в октетах */
	u32 tag,			/*!< [in] тег */
	size_t len			/*!< [in] длина val в битах */
);

/*!	\brief Декодирование BIT с проверкой длины */
#define derBITDec2(val, der, count, len) \
	derTBITDec2(val, der, count, 0x03, len)

/*
*******************************************************************************
Тип NULL
*******************************************************************************
*/

/*! \brief Кодирование NULL */
#define derNULLEnc(der) derEnc(der, 0x05, 0, 0)

/*! \brief Проверка кода NULL */
#define derNULLDec(der, count) derDec4(der, count, 0x05, 0, 0)

/*
*******************************************************************************
Тип OID
*******************************************************************************
*/

/*!	\brief Кодирование OID

	Определяется число октетов в DER-коде идентификатора объекта oid.
	Если der != 0, то DER-код размещается по этому адресу.
	\pre Если der != 0, то по адресу der зарезервировано derOIDEnc(0, oid)
	октетов.
	\return Число октетов в DER-коде или SIZE_MAX в случае ошибки.
*/

size_t derOIDEnc(
	octet der[],		/*!< [out] DER-код */
	const char* oid		/*!< [in] идентификатор объекта */
);

/*!	\brief Декодирование OID

	Определяется идентификатор объекта [len? + 1]oid,
	представленный DER-кодом [<=count]der.
	\return Точная длина DER-кода или SIZE_MAX в случае ошибки.
	\remark Длина len? не учитывает завершающий нулевой символ.
*/
size_t derOIDDec(
	char* oid,			/*!< [out] идентификатор */
	size_t* len,		/*!< [out] длина идентификатора */
	const octet der[],	/*!< [in] DER-код */
	size_t count		/*!< [in] длина der в октетах */
);

/*!	\brief Проверка кода OID

	Проверяется, что DER-код [<=count]der построен по идентификатору oid.
	Идентификатор задается строкой, составленной по правилам модуля oid.h.
	\return Точная длина DER-кода или SIZE_MAX в случае оишбки.
*/
size_t derOIDDec2(
	const octet der[],	/*!< [in] DER-код */
	size_t count,		/*!< [in] длина der в октетах */
	const char* oid		/*!< [in] идентификатор */
);

/*
*******************************************************************************
Тип OCT
*******************************************************************************
*/

/*!	\brief Кодирование TOCT */
#define derTOCTEnc(der, tag, val, len) derEnc(der, tag, val, len)

/*!	\brief Кодирование OCT */
#define derOCTEnc(der, val, len) derTOCTEnc(der, 0x04, val, len)

/*!	\brief Декодирование TOCT

	Проверяется, что тег DER-кода [<=count]der равняется tag и, если это так,
	определяется закодированная строка октетов [len?]val.
	\remark Любой из указателей val и len может быть нулевым.
	\pre Буферы, на которые ссылаются ненулевые указатели val и len,
	не пересекаются между собой, но могут пересекаться с буфером der.
	\return Точная длина DER-кода или SIZE_MAX в случае ошибки.
*/
size_t derTOCTDec(
	octet* val,			/*!< [out] строка октетов */
	size_t* len,		/*!< [out] длина val */
	const octet der[],	/*!< [in] DER-код */
	size_t count,		/*!< [in] длина der в октетах */
	u32 tag				/*!< [in] тег */
);

/*!	\brief Декодирование OCT */
#define derOCTDec(val, len, der, count) derTOCTDec(val, len, der, count, 0x04)

/*!	\brief Декодирование TOCT с проверкой длины

	Проверяется, что DER-код [<=count]der имеет тег tag и представляет строку
	октетов	длины len и, если это так, определяется закодированное
	значение [len]val.
	\remark Указатель val может быть нулевым.
	\remark Если val != 0, то соответствующий буфер может пересекаться с der.
	\return Точная длина DER-кода или SIZE_MAX в случае ошибки.
*/
size_t derTOCTDec2(
	octet* val,			/*!< [out] строка октетов */
	const octet der[],	/*!< [in] DER-код */
	size_t count,		/*!< [in] длина der в октетах */
	u32 tag,			/*!< [in] тег */
	size_t len			/*!< [in] длина val */
);

/*!	\brief Декодирование OCT с проверкой длины */
#define derOCTDec2(val, der, count, len) \
	derTOCTDec2(val, der, count, 0x04, len)

/*!	\brief Проверка кода TOCT */
#define derTOCTDec3 derDec4

/*!	\brief Проверка кода OCT */
#define derOCTDec3(der, count, val, len) \
	derTOCTDec3(der, count, 0x04, val, len)

/*
*******************************************************************************
Тип PSTR
*******************************************************************************
*/

/*!	\brief Кодирование TPSTR

	Определяется число октетов в DER-коде с тегом tag печатаемой
	строки str. Если der != 0, то DER-код размещается по этому адресу.
	\pre Если der != 0, то по адресу der зарезервировано
	derTPSTREnc(0, tag, str) октетов.
	\expect{SIZE_MAX} strIsPrintable(val).
	\return Число октетов в DER-коде или SIZE_MAX в случае ошибки.
	\remark Поддерживается логика derEnc(), в частности, буферы der и val
	могут пересекаться.
*/
size_t derTPSTREnc(
	octet der[],		/*!< [out] DER-код */
	u32 tag,			/*!< [in] тег */
	const char* val		/*!< [in] строка */
);

/*!	\brief Кодирование PSTR */
#define derPSTREnc(der, val) derTPSTREnc(der, 0x13, val)

/*!	\brief Декодирование TPSTR

	Проверяется, что тег DER-кода [<=count]der равняется tag и, если это так,
	определяется печатаемая строка [len? + 1]val.
	\remark Любой из указателей val и len может быть нулевым.
	\pre Буферы, на которые ссылаются ненулевые указатели val и len,
	не пересекаются между собой, но могут пересекаться с буфером der.
	\return Точная длина DER-кода или SIZE_MAX в случае ошибки.
	\remark Длина len? не учитывает завершающий нулевой символ.
*/
size_t derTPSTRDec(
	char* val,			/*!< [out] строка */
	size_t* len,		/*!< [out] длина val */
	const octet der[],	/*!< [in] DER-код */
	size_t count,		/*!< [in] длина der в октетах */
	u32 tag				/*!< [in] тег */
);

/*!	\brief Декодирование PSTR */
#define derPSTRDec(val, len, der, count) \
	derTPSTRDec(val, len, der, count, 0x13)

/*
*******************************************************************************
Тип SEQ
*******************************************************************************
*/

/*!	\brief Якорь для кодирования контейнеров */
typedef struct
{
	const octet* der;	/*!< код */
	size_t pos;			/*!< позиция */
	u32 tag;			/*!< тег */
	size_t len;			/*!< длина */
} der_anchor_t;


/*!	\brief Начать кодирование TSEQ

	Определяется число октетов в префиксе DER-кода структуры TSEQ с тегом tag.
	Если der != 0, то префикс размещается по этому адресу. Одновременно
	в якоре anchor сохраняются данные, необходимые для уточнения префикса
	при завершении кодирования структуры. Чтобы корректно уточнить префикс
	при нулевом der, в функцию передается параметр pos. Это текущая позиция
	в коде в абстрактной (со свободной точкой отсчета) системе координат.
	\pre Если der != 0, то по адресу der зарезервировано
	derTSEQEncStart(anchor, 0, pos, tag) октетов.
	\expect{SIZE_MAX} В tag установлен бит конструктивности.
	\return Число октетов в префиксе DER-коде или SIZE_MAX в случае ошибки.
*/
size_t derTSEQEncStart(
	der_anchor_t* anchor,	/*!< [out] якорь */
	octet der[],			/*!< [out] DER-код */
	size_t pos,				/*!< [in] позиция */
	u32 tag					/*!< [in] тег */
);

/*!	\brief Начать кодирование SEQ */
#define derSEQEncStart(anchor, der, pos) \
	derTSEQEncStart(anchor, der, pos, 0x30)

/*!	\brief Завершить кодирование TSEQ

	По якорю anchor и текущей позиции pos в коде определяется дополнительное
	число октетов для завершения кодирования структуры TSEQ. Если der != 0,
	то кодирование завершается.
	\pre Если der != 0, то по адресу der зарезервировано
	derTSEQEncStop(0, pos, anchor) октетов.
	\expect derTSEQEncStop() < derTSEQEncStart().
	\pre Если в derTSEQEncStart() передан ненулевой адрес der, то в
	derTSEQEncStop() также передан ненулевой адрес и разность адресов совпадает
	с разностью позиций.
	\return Число октетов завершающих кодирование или SIZE_MAX в случае	ошибки.
*/
size_t derTSEQEncStop(
	octet der[],				/*!< [out] DER-код */
	size_t pos,					/*!< [in] позиция */
	const der_anchor_t* anchor	/*!< [in] якорь */
);

/*!	\brief Завершить кодирование SEQ */
#define derSEQEncStop derTSEQEncStop

/*!	\brief Начать декодирование TSEQ

	Определяется число октетов в префиксе DER-кода [<=count]der структуры
	TSEQ с тегом tag. Одновременно в якоре anchor сохраняются данные,
	необходимые для проверки префикса при завершении декодирования структуры.
	\expect{SIZE_MAX} В tag установлен бит конструктивности.
	\return Число октетов в префиксе DER-коде или SIZE_MAX в случае ошибки.
*/
size_t derTSEQDecStart(
	der_anchor_t* anchor,		/*!< [out] якорь */
	const octet der[],			/*!< [in] DER-код */
	size_t count,				/*!< [in] длина der в октетах */
	u32 tag						/*!< [in] тег */
);

/*!	\brief Начать декодирование SEQ */
#define derSEQDecStart(anchor, der, count) \
	derTSEQDecStart(anchor, der, count, 0x30)

/*!	\brief Завершить декодирование TSEQ

	Завершается декодирования структуры TSEQ: по якорю anchor,
	сохраненному в начале кодирования, и адресу der, который указывает
	на окончание DER-кода, проверяется корректность кода.
	\pre derTSEQDecStop() < derTSEQDecStart().
	\return 0 в случае успешного завершения или SIZE_MAX в случае ошибки.
*/
size_t derTSEQDecStop(
	const octet der[],			/*!< [in] DER-код */
	const der_anchor_t* anchor	/*!< [in] якорь */
);

/*!	\brief Завершить декодирование SEQ */
#define derSEQDecStop derTSEQDecStop

#ifdef __cplusplus
} /* extern "C" */
#endif

#endif /* __BEE2_DER_H */
