der.h - Man Page

Отличительные правила кодирования

Synopsis

#include 'bee2/defs.h'

Классы

struct der_anchor_t
Якорь для кодирования контейнеров

Макросы

#define derSIZEEnc(der,  val)   derTSIZEEnc(der, 0x02, val)
Кодирование SIZE.
#define derSIZEDec(val,  der,  count)   derTSIZEDec(val, der, count, 0x02)
Кодирование SIZE.
#define derSIZEDec2(der,  count,  val)   derTSIZEDec2(der, count, 0x02, val)
Проверка кода SIZE.
#define derBITEnc(der,  val,  len)   derTBITEnc(der, 0x03, val, len)
Кодирование BIT.
#define derBITDec(val,  len,  der,  count)   derTBITDec(val, len, der, count, 0x03)
Декодирование BIT.
#define derBITDec2(val,  der,  count,  len)   derTBITDec2(val, der, count, 0x03, len)
Декодирование BIT с проверкой длины
#define derTOCTEnc(der,  tag,  val,  len)   derEnc(der, tag, val, len)
Кодирование TOCT.
#define derOCTEnc(der,  val,  len)   derTOCTEnc(der, 0x04, val, len)
Кодирование OCT.
#define derOCTDec(val,  len,  der,  count)   derTOCTDec(val, len, der, count, 0x04)
Декодирование OCT.
#define derOCTDec2(val,  der,  count,  len)   derTOCTDec2(val, der, count, 0x04, len)
Декодирование OCT с проверкой длины
#define derTOCTDec3   derDec4
Проверка кода TOCT.
#define derOCTDec3(der,  count,  val,  len)   derTOCTDec3(der, count, 0x04, val, len)
Проверка кода OCT.
#define derNULLEnc(der)   derEnc(der, 0x05, 0, 0)
Кодирование NULL.
#define derNULLDec(der,  count)   derDec4(der, count, 0x05, 0, 0)
Проверка кода NULL.
#define derPSTREnc(der,  val)   derTPSTREnc(der, 0x13, val)
Кодирование PSTR.
#define derPSTRDec(val,  len,  der,  count)   derTPSTRDec(val, len, der, count, 0x13)
Декодирование PSTR.
#define derSEQEncStart(anchor,  der,  pos)   derTSEQEncStart(anchor, der, pos, 0x30)
Начать кодирование SEQ.
#define derSEQEncStop   derTSEQEncStop
Завершить кодирование SEQ.
#define derSEQDecStart(anchor,  der,  count)   derTSEQDecStart(anchor, der, count, 0x30)
Начать декодирование SEQ.
#define derSEQDecStop   derTSEQDecStop
Завершить декодирование SEQ.

Функции

size_t derTLEnc (octet der[], u32 tag, size_t len)
Кодирование тега и длины
size_t derEnc (octet der[], u32 tag, const void *val, size_t len)
Кодирование
bool_t derIsValid (const octet der[], size_t count)
Корректный код?
bool_t derIsValid2 (const octet der[], size_t count, u32 tag)
Корректный код с ожидаемым тегом?
bool_t derStartsWith (const octet der[], size_t count, u32 tag)
Начинается с тега?
size_t derTLDec (u32 *tag, size_t *len, const octet der[], size_t count)
Декодирование тега и длины
size_t derDec (u32 *tag, const octet **val, size_t *len, const octet der[], size_t count)
Декодирование
size_t derDec2 (const octet **val, size_t *len, const octet der[], size_t count, u32 tag)
Декодирование с проверкой тега
size_t derDec3 (const octet **val, const octet der[], size_t count, u32 tag, size_t len)
Декодирование с проверкой тега и длины
size_t derDec4 (const octet der[], size_t count, u32 tag, const void *val, size_t len)
Проверка кода
size_t derTSIZEEnc (octet der[], u32 tag, size_t val)
Кодирование TSIZE.
size_t derTSIZEDec (size_t *val, const octet der[], size_t count, u32 tag)
Декодирование TSIZE.
size_t derTSIZEDec2 (const octet der[], size_t count, u32 tag, size_t val)
Проверка кода TSIZE.
size_t derTBITEnc (octet der[], u32 tag, const octet *val, size_t len)
Кодирование TBIT.
size_t derTBITDec (octet *val, size_t *len, const octet der[], size_t count, u32 tag)
Декодирование TBIT.
size_t derTBITDec2 (octet *val, const octet der[], size_t count, u32 tag, size_t len)
Декодирование TBIT с проверкой длины
size_t derTOCTDec (octet *val, size_t *len, const octet der[], size_t count, u32 tag)
Декодирование TOCT.
size_t derTOCTDec2 (octet *val, const octet der[], size_t count, u32 tag, size_t len)
Декодирование TOCT с проверкой длины
size_t derOIDEnc (octet der[], const char *oid)
Кодирование OID.
size_t derOIDDec (char *oid, size_t *len, const octet der[], size_t count)
Декодирование OID.
size_t derOIDDec2 (const octet der[], size_t count, const char *oid)
Проверка кода OID.
size_t derTPSTREnc (octet der[], u32 tag, const char *val)
Кодирование TPSTR.
size_t derTPSTRDec (char *val, size_t *len, const octet der[], size_t count, u32 tag)
Декодирование TPSTR.
size_t derTSEQEncStart (der_anchor_t *anchor, octet der[], size_t pos, u32 tag)
Начать кодирование TSEQ.
size_t derTSEQEncStop (octet der[], size_t pos, const der_anchor_t *anchor)
Завершить кодирование TSEQ.
size_t derTSEQDecStart (der_anchor_t *anchor, const octet der[], size_t count, u32 tag)
Начать декодирование TSEQ.
size_t derTSEQDecStop (const octet der[], const der_anchor_t *anchor)
Завершить декодирование TSEQ.

Подробное описание

Правила кодирования

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

Тег задается, как минимум, одним октетом, в котором младшие 5 битов представляют номер, 6-й бит является признаком примитивности / конструктивности (если бит снят / установлен, то V не содержит / содержит вложенные структуры TLV), а старшие два бита определяют класс тега:

Если не все 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, которое является готовым кодом. Кодируются собственно тег-как-число, класс тега и признак примитивности.

Примеры:

Прим.

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

Длина (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) определяется в процессе декодирования.

Прим.

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

Предусловие

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

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

DER-кодирование

Базовые типы

Реализовано кодирование следующих типов АСН.1:

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

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

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

Примеры:

Особенность реализации: тип INTEGER сужается до SIZE -- кодируются только неотрицательные целые, которые укладываются в size_t.

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

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

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

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

Прим.

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

Функции

size_t derDec (u32 * tag, const octet ** val, size_t * len, const octet der[], size_t count)

Определяются тег tag и значение [len?]val DER-кода [<=count]der. При этом val указывает на буфер памяти внутри буфера der.

Возвращает

Точная длина DER-кода или SIZE_MAX в случае ошибки формата.

Прим.

Любой из указателей tag, val и len может быть нулевым.

Аргументы

tag тег  
val указатель на значение  
len длина значения  
der DER-код  
count длина der в октетах

size_t derDec2 (const octet ** val, size_t * len, const octet der[], size_t count, u32 tag)

Проверяется, что тег DER-кода [<=count]der равняется tag и, если это так, определяется закодированное значение [len?]val. При этом val указывает на буфер памяти внутри буфера der.

Прим.

Любой из указателей val, len может быть нулевым, и тогда возврат данных по указателю не производится.

Предусловие

Буферы, на которые ссылаются ненулевые указатели val и len, не пересекаются между собой и с буфером der.

Возвращает

Точная длина DER-кода или SIZE_MAX в случае ошибки.

Аргументы

val значение  
len длина значения  
der DER-код  
count длина der в октетах  
tag тег

size_t derDec3 (const octet ** val, const octet der[], size_t count, u32 tag, size_t len)

Проверяется, что тег и длина значения DER-кода [<=count]der равняются соответственно tag и len и, если это так, определяется закодированное значение [len]val. При этом val указывает на буфер памяти внутри буфера der.

Прим.

Указатель val может быть нулевым, и тогда возврат данных по указателю не производится.

Предусловие

Если val != 0, то соответствующий буфер не пересекается с буфером der.

Возвращает

Точная длина DER-кода или SIZE_MAX в случае ошибки.

Аргументы

val значение  
der DER-код  
count длина der в октетах  
tag тег  
len длина значения

size_t derDec4 (const octet der[], size_t count, u32 tag, const void * val, size_t len)

Проверяется, что тег и значение DER-кода [<=count]der равняются соответственно tag и [len]val.

Прим.

Величина count является оценкой сверху актуальной длины DER-кода.

Возвращает

Точная длина DER-кода или SIZE_MAX в случае ошибки.

Аргументы

der DER-код  
count длина der в октетах  
tag тег  
val значение  
len длина значения

size_t derEnc (octet der[], u32 tag, const void * val, size_t len)

Определяется число октетов в DER-коде значения [len]val с тегом tag. Если der != 0, то DER-код размещается по этому адресу.

Предусловие

Если der != 0, то val != 0 и по адресу der зарезервировано derEnc(0, tag, val, len) октетов.

Возвращает

Число октетов в DER-коде или SIZE_MAX в случае ошибки.

Прим.

Буферы der и val могут пересекаться.

Ошибкой является неверный формат tag.

Разрешаются вызовы derEnc(0, tag, val, len), derEnc(0, tag, 0, len).

Аргументы

der DER-код  
tag тег  
val значение  
len длина val в октетах

bool_t derIsValid (const octet der[], size_t count)

Проверяется корректность DER-кода [count]der. Проверяются следующие условия:

  • тег T и длина L закодированы по правилам ACH.1;
  • тег укладывается в u32 (ограничение реализации);
  • длина укладывается в size_t (органичение реализации);
  • count действительно является длиной кода.

Возвращает

Признак корректности.

Прим.

Содержимое V не проверяется.

Аргументы

der DER-код  
count длина der в октетах

bool_t derIsValid2 (const octet der[], size_t count, u32 tag)

Проверяется корректность DER-кода [count]der с ожидаемым тегом tag. Проверяются следующие условия:

  • derIsValid(der, count) == TRUE;
  • der имеет тег tag.

Ожидается [FALSE]

Тег tag корректен.

Возвращает

Признак корректности.

Прим.

Содержимое V не проверяется.

Аргументы

der DER-код  
count длина der в октетах  
tag тег

size_t derOIDDec (char * oid, size_t * len, const octet der[], size_t count)

Определяется идентификатор объекта [len? + 1]oid, представленный DER-кодом [<=count]der.

Возвращает

Точная длина DER-кода или SIZE_MAX в случае ошибки.

Прим.

Длина len? не учитывает завершающий нулевой символ.

Аргументы

oid идентификатор  
len длина идентификатора  
der DER-код  
count длина der в октетах

size_t derOIDDec2 (const octet der[], size_t count, const char * oid)

Проверяется, что DER-код [<=count]der построен по идентификатору oid. Идентификатор задается строкой, составленной по правилам модуля oid.h.

Возвращает

Точная длина DER-кода или SIZE_MAX в случае оишбки.

Аргументы

der DER-код  
count длина der в октетах  
oid идентификатор

size_t derOIDEnc (octet der[], const char * oid)

Определяется число октетов в DER-коде идентификатора объекта oid. Если der != 0, то DER-код размещается по этому адресу.

Предусловие

Если der != 0, то по адресу der зарезервировано derOIDEnc(0, oid) октетов.

Возвращает

Число октетов в DER-коде или SIZE_MAX в случае ошибки.

Аргументы

der DER-код  
oid идентификатор объекта

bool_t derStartsWith (const octet der[], size_t count, u32 tag)

Проверяется, что DER-код [count]der начинается с тега tag.

Ожидается [FALSE]

Тег tag корректен.

Возвращает

Признак корректности.

Аргументы

der DER-код  
count длина der в октетах  
tag тег

size_t derTBITDec (octet * val, size_t * len, const octet der[], size_t count, u32 tag)

Проверяется, что тег DER-кода [<=count]der равняется tag и, если это так, определяется закодированная строка битов [(len? + 7)/8]val.

Прим.

Любой из указателей val и len может быть нулевым.

Предусловие

Буферы, на которые ссылаются ненулевые указатели val и len, не пересекаются между собой, но могут пересекаться с буфером der.

Возвращает

Точная длина DER-кода или SIZE_MAX в случае ошибки.

Аргументы

val строка битов  
len длина val в битах  
der DER-код  
count длина der в октетах  
tag тег

size_t derTBITDec2 (octet * val, const octet der[], size_t count, u32 tag, size_t len)

Проверяется, что DER-код [<=count]der имеет тег tag и представляет строку битов длины len и, если это так, определяется закодированное значение [(len? + 7)/8]val.

Прим.

Указатель val может быть нулевым.

Предусловие

Если val != 0, то соответствующий буфер может пересекается с der.

Возвращает

Точная длина DER-кода или SIZE_MAX в случае ошибки.

Аргументы

val строка битов  
der DER-код  
count длина der в октетах  
tag тег  
len длина val в битах

size_t derTBITEnc (octet der[], u32 tag, const octet * val, size_t len)

Определяется число октетов в DER-коде с тегом tag строки битов [(len + 7)/8]val. Если der != 0, то DER-код размещается по этому адресу.

Предусловие

Если der != 0, то по адресу der зарезервировано derTBITEnc(0, tag, val, len) октетов.

Возвращает

Число октетов в DER-коде или SIZE_MAX в случае ошибки.

Прим.

Поддерживается логика derEnc(), в частности, буферы der и val могут пересекаться.

Аргументы

der DER-код  
tag тег  
val строка битов  
len длина val в битах

size_t derTLDec (u32 * tag, size_t * len, const octet der[], size_t count)

Определяются тег tag и длина значения len DER-кода с TL-префиксом [<=count]der.

Возвращает

Точная длина TL-префикса или SIZE_MAX в случае ошибки формата.

Прим.

Любой из указателей tag и len может быть нулевым.

Аргументы

tag тег  
len длина значения  
der DER-код  
count длина der в октетах

size_t derTLEnc (octet der[], u32 tag, size_t len)

Определяется число октетов в TL-префиксе DER-кода с тегом tag и значением длины val. Если der != 0, то префикс размещается по этому адресу.

Предусловие

Если der != 0, то по адресу der зарезервировано derEnc(0, tag, len) октетов.

Возвращает

Число октетов в TL-префиксе или SIZE_MAX в случае ошибки.

Прим.

Ошибкой является неверный формат tag.

Аргументы

der DER-код  
tag тег  
len длина значения в октетах

size_t derTOCTDec (octet * val, size_t * len, const octet der[], size_t count, u32 tag)

Проверяется, что тег DER-кода [<=count]der равняется tag и, если это так, определяется закодированная строка октетов [len?]val.

Прим.

Любой из указателей val и len может быть нулевым.

Предусловие

Буферы, на которые ссылаются ненулевые указатели val и len, не пересекаются между собой, но могут пересекаться с буфером der.

Возвращает

Точная длина DER-кода или SIZE_MAX в случае ошибки.

Аргументы

val строка октетов  
len длина val  
der DER-код  
count длина der в октетах  
tag тег

size_t derTOCTDec2 (octet * val, const octet der[], size_t count, u32 tag, size_t len)

Проверяется, что DER-код [<=count]der имеет тег tag и представляет строку октетов длины len и, если это так, определяется закодированное значение [len]val.

Прим.

Указатель val может быть нулевым.

Предусловие

Если val != 0, то соответствующий буфер может пересекается с der.

Возвращает

Точная длина DER-кода или SIZE_MAX в случае ошибки.

Аргументы

val строка октетов  
der DER-код  
count длина der в октетах  
tag тег  
len длина val

size_t derTPSTRDec (char * val, size_t * len, const octet der[], size_t count, u32 tag)

Проверяется, что тег DER-кода [<=count]der равняется tag и, если это так, определяется печатаемая строка [len? + 1]val.

Прим.

Любой из указателей val и len может быть нулевым.

Предусловие

Буферы, на которые ссылаются ненулевые указатели val и len, не пересекаются между собой, но могут пересекаться с буфером der.

Возвращает

Точная длина DER-кода или SIZE_MAX в случае ошибки.

Прим.

Длина len? не учитывает завершающий нулевой символ.

Аргументы

val строка  
len длина val  
der DER-код  
count длина der в октетах  
tag тег

size_t derTPSTREnc (octet der[], u32 tag, const char * val)

Определяется число октетов в DER-коде с тегом tag печатаемой строки str. Если der != 0, то DER-код размещается по этому адресу.

Предусловие

Если der != 0, то по адресу der зарезервировано derTPSTREnc(0, tag, str) октетов.

Ожидается [SIZE_MAX]

strIsPrintable(val).

Возвращает

Число октетов в DER-коде или SIZE_MAX в случае ошибки.

Прим.

Поддерживается логика derEnc(), в частности, буферы der и val могут пересекаться.

Аргументы

der DER-код  
tag тег  
val строка

size_t derTSEQDecStart (der_anchor_t * anchor, const octet der[], size_t count, u32 tag)

Определяется число октетов в префиксе DER-кода [<=count]der структуры TSEQ с тегом tag. Одновременно в якоре anchor сохраняются данные, необходимые для проверки префикса при завершении декодирования структуры.

Ожидается [SIZE_MAX]

В tag установлен бит конструктивности.

Возвращает

Число октетов в префиксе DER-коде или SIZE_MAX в случае ошибки.

Аргументы

anchor якорь  
der DER-код  
count длина der в октетах  
tag тег

size_t derTSEQDecStop (const octet der[], const der_anchor_t * anchor)

Завершается декодирования структуры TSEQ: по якорю anchor, сохраненному в начале кодирования, и адресу der, который указывает на окончание DER-кода, проверяется корректность кода.

Предусловие

derTSEQDecStop() < derTSEQDecStart().

Возвращает

0 в случае успешного завершения или SIZE_MAX в случае ошибки.

Аргументы

der DER-код  
anchor якорь

size_t derTSEQEncStart (der_anchor_t * anchor, octet der[], size_t pos, u32 tag)

Определяется число октетов в префиксе DER-кода структуры TSEQ с тегом tag. Если der != 0, то префикс размещается по этому адресу. Одновременно в якоре anchor сохраняются данные, необходимые для уточнения префикса при завершении кодирования структуры. Чтобы корректно уточнить префикс при нулевом der, в функцию передается параметр pos. Это текущая позиция в коде в абстрактной (со свободной точкой отсчета) системе координат.

Предусловие

Если der != 0, то по адресу der зарезервировано derTSEQEncStart(anchor, 0, pos, tag) октетов.

Ожидается [SIZE_MAX]

В tag установлен бит конструктивности.

Возвращает

Число октетов в префиксе DER-коде или SIZE_MAX в случае ошибки.

Аргументы

anchor якорь  
der DER-код  
pos позиция  
tag тег

size_t derTSEQEncStop (octet der[], size_t pos, const der_anchor_t * anchor)

По якорю anchor и текущей позиции pos в коде определяется дополнительное число октетов для завершения кодирования структуры TSEQ. Если der != 0, то кодирование завершается.

Предусловие

Если der != 0, то по адресу der зарезервировано derTSEQEncStop(0, pos, anchor) октетов.

Ожидается

derTSEQEncStop() < derTSEQEncStart().

Предусловие

Если в derTSEQEncStart() передан ненулевой адрес der, то в derTSEQEncStop() также передан ненулевой адрес и разность адресов совпадает с разностью позиций.

Возвращает

Число октетов завершающих кодирование или SIZE_MAX в случае ошибки.

Аргументы

der DER-код  
pos позиция  
anchor якорь

size_t derTSIZEDec (size_t * val, const octet der[], size_t count, u32 tag)

Проверяется, что тег DER-кода [<=count]der равняется tag и, если это так, определяется закодированное неотрицательное целое val.

Возвращает

Точная длина DER-кода или SIZE_MAX в случае оишбки.

Аргументы

val значение  
der DER-код  
count длина der в октетах  
tag тег

size_t derTSIZEDec2 (const octet der[], size_t count, u32 tag, size_t val)

Проверяется, что DER-код [<=count]der имеет тег tag и представляет неотрицательное целое val.

Возвращает

Точная длина DER-кода или SIZE_MAX в случае оишбки.

Аргументы

der DER-код  
count длина der в октетах  
tag тег  
val значение

size_t derTSIZEEnc (octet der[], u32 tag, size_t val)

Определяется число октетов в DER-коде с тегом tag неотрицательного целого val. Если der != 0, то DER-код размещается по этому адресу.

Предусловие

Если der != 0, то по адресу der зарезервировано derTSIZEEnc(0, tag, val) октетов.

Возвращает

Число октетов в DER-коде или SIZE_MAX в случае ошибки.

Аргументы

der DER-код  
tag тег  
val значение

Автор

Автоматически создано Doxygen для Библиотека Bee2 из исходного текста.

Info

Вт 23 Янв 2024 00:00:00 Библиотека Bee2