/*
*******************************************************************************
\file btok.h
\brief STB 34.101.79 (btok): cryptographic tokens
\project bee2 [cryptographic library]
\created 2022.07.04
\version 2025.01.08
\copyright The Bee2 authors
\license Licensed under the Apache License, Version 2.0 (see LICENSE.txt).
*******************************************************************************
*/

#ifndef __BEE2_BTOK_H
#define __BEE2_BTOK_H

#include "bee2/defs.h"
#include "bee2/core/apdu.h"
#include "bee2/crypto/bake.h"

/*!
*******************************************************************************
\file btok.h
\brief Криптографическая архитектура СТБ 34.101.79 (btok)
*******************************************************************************
*/


#ifdef __cplusplus
extern "C" {
#endif

/*!
*******************************************************************************
\file btok.h

Реализованы отдельные механизмы СТБ 34.101.79 (btok):
-	управление облегченными (Card Verifiable, CV) сертификатами.

\expect{ERR_BAD_INPUT} Все входные указатели действительны. Исключение
составляют оговоренные случаи. К ним относится запрос на определение объема
памяти, которую требуется зарезервировать при повторном вызове
(конструкция [len?]ptr).
*******************************************************************************
*/

/*
*******************************************************************************
\file btok.h

\section btok-pwd Пароли

\subsection Выдержки из СТБ 34.101.79

1. КТ должен поддерживать три пароля -- PIN, CAN, PUK. Пароли используются
в протоколе BPACE и являются общими для всех прикладных программ КТ.
Пароли записываются на КТ при выпуске токена в обращение. Пароли PIN и PUK
конфиденциально передаются владельцу, CAN передается в открытом виде.

2. Пароль PIN (от Personal Identification Number) представляет собой случайное
число из 6 десятичных цифр, известное только владельцу КТ. Используется для
контроля доступа к данным и прикладным программам КТ. Может быть изменен
владельцем в процессе эксплуатации КТ после ввода верного действующего PIN.

3. PIN снабжается счетчиком попыток, который первоначально равен 3. При
неверном вводе PIN счетчик уменьшается на 1. Если счетчик достигает
значения 1, то PIN приостанавливается и далее требуется ввести CAN.
Ввод CAN не изменяет счетчик. При верном CAN пароль PIN возобновляется --
его снова можно ввести. При неверном CAN доступ к КТ временно блокируется.
Если счетчик попыток достигает значения 0, то блокируется PIN.
При вводе верного PIN счетчик попыток возвращается к значению 3.

4. PIN может быть разблокирован после ввода верного PUK. Однако если при
заблокированном PIN пароль PUK вводится неверно 10 раз, то PIN блокируется
навсегда. При успешной разблокировке PIN счетчик попыток возвращается
к значению 3.

5. PIN может быть деактивирован и повторно активирован с помощью специальных
команд. Для деактивации требуется предъявить верный PIN или PUK,
для активации -- верный PUK. Сразу после выпуска токена PIN активирован.
При деактивации PIN доступ к операциям и данным, требующим аутентификации по
PIN, запрещен. В том числе запрещена аутентификация по PIN, разблокировка PIN.
Деактивация и повторная активация PIN не изменяют ни статус его блокировки,
ни счетчик попыток.

6. Пароль CAN (от Card Access Number) представляет собой число из 6 десятичных
цифр, которое не может быть вычислено на основании общей информации о КТ
(например, серийном номере) или его владельце. Может быть напечатан на корпусе
КТ или указан в сопроводительных документах.

7. Пароль CAN не может быть заблокирован или изменен. Он используется для защиты
от атак типа <<отказ в обслуживании>>. Защита состоит в требовании ввести CAN
перед последней проверкой PIN. Дополнительно CAN может использоваться для
получения доступа к функциям и данным прикладной программы eID
авторизованным терминалом, т. е. терминалом, который был успешно
аутентифицирован с помощью протокола BAUTH и в сертификате которого установлено
соответствующее право.

8. Пароль PUK (от PIN Unlock Key) представляет собой случайное число из 10
десятичных цифр, известное только владельцу КТ. PUK не может быть заблокирован
или изменен. Используется для разблокировки PIN. Дополнительно может
использоваться для деактивации и активации PIN.

9. После ввода неверного CAN или PUK следует временно заблокировать КТ не менее
чем на 1 с. Для контроля времени блокировки следует использовать аппаратный
таймер либо организовать вычисления требуемой продолжительности.

10. При передаче в команды КТ пароли PIN, CAN и PUK представляются строками
октетов, в которых октет 0x30 кодирует цифру 0 пароля, октет 0x31 -- цифру 1,
..., октет 0x39 -- цифру 9.

\subsection Комментарии (реконструкция нераскрытых в СТБ тем)

1. Успешная аутентификация по одному из паролей PIN, CAN, PUK отменяет
статус успешной аутентификации по другому паролю. Нельзя быть одновременно
аутентифицированным по двум паролям.

2. После успешной аутентификация по одному из паролей PIN, CAN, PUK можно
повторно аутентифицироваться по этому же паролю. В случае успеха статус
успешной аутентификации по паролю будет сохранен, в случае ошибки -- потерян.

3. После успешной аутентификация по одному из паролей PIN, CAN, PUK можно
аутентифицироваться по другому паролю. В случае успеха статус успешной
аутентификации по первому паролю будет потерян, в случае ошибки -- сохранен.

4. Даже если PIN заблокирован навсегда, аутентификация по PUK (и тем более по
CAN) остается возможной.

5. Статус успешной аутентификации по CAN должен действовать непосредственно
в момент последней попытки ввода PIN. Иначе эта попытка отменяется.

\subsection Парольный автомат

Состояние автомата состоит из двух частей: pin_state и auth_state.

Первая часть (pin_state) описывает состояние PIN и принимает значения
из множества:
  {puk0, puk1,...., puk9, pin0, pin1, pind, pins, pin2, pin3}.
Семантика состояний:
- в состояниях pinN остается N попыток ввода PIN, при этом pin0 -- состояние
  блокировки PIN;
- в состояниях pukN PIN заблокирован и остается N попыток ввода PUK для его
  разблокировки. При этом puk0 -- состояние полной блокировки PIN;
- состояние pins (suspended) является промежуточным между pin2 и pin1:
  переход pin2 => pins выполняется при ошибке аутентификации по PIN,
  а переход pins => pin1 -- при успешной аутентификации по CAN;
- состояние pind (deactivated) -- это состояние принудительной деактивации PIN.

Подсостояние pin_state сохраняется между сеансами работы с автоматом. Начальное
("заводское") значение pin_state: pin3.

Вторая часть состояния (auth_state) описывает статус аутентификации и принимает
значения из множества:
  {auth_none, auth_pin, auth_can, auth_puk}.
Семантика статусов:
- auth_XXX: успешная аутентификация по паролю XXX;
- auth_none: без аутентификации.

Подсостояние auth_state не сохраняется между сеансами работы с автоматом.
Подсостояние полагается равным auth_none в начале каждого сеанса.

Автомат обрабатывает события из следующего множества:
  { pin_ok, pin_bad, pin_deactivate, pin_activate,
	can_ok, can_bad,
	puk_ok, puk_bad,
	auth_close, }.
Семантика событий:
- XXX_ok: успешная аутентификация по паролю XXX;
- XXX_bad: ошибка аутентификации по паролю XXX;
- pin_deactivate: деактивация PIN;
- pin_activate: активация PIN после деактивации;
- auth_close: отмена аутентификации.

\remark Команд отмены аутентификации нет. Речь может идти о неявной отмене
(например, через переключение на соединение с терминалом). События auth_close
под вопросом.

\remark "Деактивация и повторная активация PIN не изменяют ни статус его
блокировки, ни счетчик попыток." Деактивация выполняется после успешной
аутентификации по PIN или PUK. Но тогда pin_state обязательно равняется pin3.
При этом PIN не заблокирован (pin_state != pin0). Из состояния деактивации
pind мы выходим только по puk_ok. При этом разрешается аутентифицироваться
по PUK произвольное число раз. Из состояния блокировки pin0 мы также выходим
только по puk_ok. Но число попыток ограничено. Только в этом разница между
pind и pin0.
*******************************************************************************
*/

/*!	\brief Состояние PIN парольного автомата */
typedef enum
{
	puk0,													/* terminated */
	puk1, puk2, puk3, puk4, puk5, puk6, puk7, puk8, puk9,	/* puk attempts */
	pin0,													/* locked */
	pin1,													/* last attempt */
	pind,													/* deactivated */
	pins,													/* suspended */
	pin2,													/* two attempts */
	pin3,													/* operational */
} btok_pin_state;

/*!	\brief Статус аутентификации парольного автомата */
typedef enum
{
	auth_none,
	auth_pin,
	auth_can,
	auth_puk,
} btok_auth_state;

/*!	\brief События парольного автомата */
typedef enum
{
	pin_ok, pin_bad, pin_deactivate, pin_activate,
	can_ok, can_bad,
	puk_ok, puk_bad,
	auth_close,
} btok_pwd_event;

/*!	\brief Состояние парольного автомата */
typedef struct
{
	btok_pin_state pin : 5;
	btok_auth_state auth : 3;
} btok_pwd_state;


/*!	\brief Обработка события парольным автоматом

	Парольный автомат с состоянием state обрабатывает событие event. Состояние
	может изменяться при обработке.
	\pre Указатель state корректен.
	\return TRUE, если событие event является допустимым и поэтому обработано,
	и FALSE в противном случае.
*/
bool_t btokPwdTransition(
	btok_pwd_state* state,		/*!< [in,out] состояние */
	btok_pwd_event event		/*!< [in] событие */
);

/*!
*******************************************************************************
\file btok.h

\section btok-cvc CV-сертификаты

Управление CV-сертификатами реализовано в соответствии с профилем, заданным
в СТБ 34.101.79. Профиль назначает:
- использование стандартных долговременных параметров bign-curve256v1, 
  bign-curve384v1 и bign-curve512v1;
- использование алгоритмов bign-sign для выработки и проверки подписи;
- использование алгоритмов belt-hash, bash384, bash512 для хэширования
  подписываемых данных;
- использование (опциональное) 5-октетного слова прав доступа к прикладной
  программе eId;
- использование (опциональное) 2-октетного слова прав доступа к прикладной
  программе eSign;
- использование специфических объектных идентификаторов, в частности,
  bign-pubkey.

В реализации профиль СТБ 34.101.79 расширен: в сертификатах могут 
использоваться ключи и алгоритмы bign96 (см. bign96.h). В частности,
открытые ключи могут лежать как на стандартных кривых bign-curve256v1, 
bign-curve384v1 и bign-curve512v1, так и на кривой bign-curve192v1.

Информационные поля CV-сертификата перечислены в структуре btok_cvc_t.
Сертификат представляет собой DER-код данных полей.

Отсутствие в сертификате слова прав доступа означает, что оно является нулевым,
т.е. что права доступа отстутствуют. По кодировании сертификата нулевое слово
прав доступа опускается. При этом наличие в декодируемом сертификате нулевого
слова не считается ошибкой.

Дата в сертификате задается 6 октетами по схеме YYMMDD (см. tm.h).
*******************************************************************************
*/

/*!	\brief Информационное содержание CV-сертификата */
typedef struct
{
	char authority[13];		/*!< издатель (8 <= strLen(authority) <= 12) */
	char holder[13];		/*!< владелец (8 <= strLen(holder) <= 12) */
	octet pubkey[128];		/*!< открытый ключ */
	size_t pubkey_len;		/*!< длина ключа в октетах (64, 96 или 128) */
	octet from[6];			/*!< дата начала действия (YYMMDD) */
	octet until[6];			/*!< дата окончания действия (YYMMDD) */
	octet hat_eid[5];		/*!< права доступа к eId (optional) */
	octet hat_esign[2];		/*!< права доступа к eSign (optional) */
	octet sig[96];			/*!< подпись */
	size_t sig_len;			/*!< длина подписи в октетах (48, 72 или 96) */
} btok_cvc_t;

/*!	\brief Проверка содержания CV-сертификата

	Проверяется корректность содержания cvc CV-сертификата.
	Проверка завершается успешно, если:
	- cтроки cvc->authority и cvc->holder состоят из печатаемых символов;
	- длины строк cvc->authority и cvc->holder лежат в диапазоне от 8 до 12;
	- даты cvc->from и cvc->until корректны;
	- cvc->from <= cvc->until;
	- открытый ключ [cvc->pubkey_len]cvc->pubkey корректен (лежит на одной
	  из кривых bign-curveXXXv1).
	\return ERR_OK, если проверка прошла успешно, и код ошибки в противном
	случае.
	\remark Подпись cvc->sig не проверяется.
*/
err_t btokCVCCheck(
	const btok_cvc_t* cvc		/*!< [in] содержание сертификата */
);

/*!	\brief Проверка содержания CV-сертификата с учетом данных издателя

	Проверяется корректность содержания cvc CV-сертификата с учетом
	содержания cvca сертификата (потенциального) издателя. 
	Проверка завершается успешно, если:
	- btokCVCCheck(cvc) == ERR_OK;
	- cvc->authority == cvca->holder;
	- даты cvca->from и cvca->until корректны;
	- cvca->from <= cvc->from && cvc->from <= cvca->until.
	\return ERR_OK, если проверка прошла успешно, и код ошибки в противном
	случае.
	\remark Подпись cvc->sig не проверяется.
*/
err_t btokCVCCheck2(
	const btok_cvc_t* cvc,	/*!< [in] содержание сертификата */
	const btok_cvc_t* cvca	/*!< [in] содержание сертификата издателя */
);

/*!	\brief Создание CV-сертификата

	Создается CV-сертификат [cert_len?]cert с содержанием cvc. Сертификат
	подписывается на личном ключе [privkey_len]privkey. Подпись сохраняется
	в [cvc->sig_len]cvc->sig. Если cvc->pubkey_len == 0, то открытый ключ
	[cvc->pubkey_len]cvc->pubkey строится по privkey и выходной
	сертификат выступает в роли доказательства владения личным ключом.
	Непосредственно перед созданием сертификата проверяется содержание cvc.
	Проверка завершается успешно, если:
	- btokCVCCheck(cvc) == ERR_OK.
	\return ERR_OK, если сертификат успешно создан, и код ошибки в противном
	случае.
	\remark Используется детерминированный режим выработки подписи. Если
	инициализирован штатный ГСЧ, то дополнительно используются данные от него.
	\remark Устанавливая cvc->pubkey_len == 0 && cvc->authority == cvc->holder,
	можно создать самоподписанный сертификат.
	\remark Устанавливая cvc->pubkey_len == 0 && cvc->authority != cvc->holder,
	можно создать предварительный сертификат, подпись которого будет изменена
	в дальнейшем стороной cvc->authority. Предварительный сертификат
	доказывает владение стороной cvc->holder личным ключом, которому
	соответствует открытый ключ cvc->pubkey. Предварительный сертификат
	выступает в роли запроса на выпуск сертификата.
*/
err_t btokCVCWrap(
	octet cert[],				/*!< [out] сертификат */
	size_t* cert_len,			/*!< [out] длина cert в октетах */
	btok_cvc_t* cvc,			/*!< [in,out] содержание сертификата */
	const octet privkey[],		/*!< [in] личный ключ */
	size_t privkey_len			/*!< [in] длина privkey в октетах */
);

/*!	\brief Разбор CV-сертификата

	Определяется и проверяется содержание cvc CV-сертификата [cert_len]cert.
	При проверке используется открытый ключ [pubkey_len]pubkey.
	Проверка завершается успешно, если:
	- btokCVCCheck(cvc) == ERR_OK;
	- подпись cert признается корректной на открытом ключе pubkey.
	Может передаваться нулевая длина pubkey_len, и тогда:
	- подпись не проверяется, если pubkey == 0;
	- подпись проверяется на открытом ключе сертификата,
	  если pubkey == cvc->pubkey;
	- индуцируется ошибка, если pubkey != 0 && pubkey != cvc->pubkey.
	\return ERR_OK, если сертификат успешно разобран, и код ошибки в
	противном случае.
	\remark Даже если подпись не проверяется, ее длина все равно
	контролируется. Неверная длина расценивается как ошибка формата.
	\remark Длина cert должна в точности равняться cert_len. Противное
	считается ошибкой формата.
*/
err_t btokCVCUnwrap(
	btok_cvc_t* cvc,			/*!< [out] содержание сертификата */
	const octet cert[],			/*!< [in] сертификат */
	size_t cert_len,			/*!< [in] длина cert в октетах */
	const octet pubkey[],		/*!< [in] открытый ключ */
	size_t pubkey_len			/*!< [in] длина pubkey в октетах */
);

/*!	\brief Выпуск CV-сертификата

	Выпускается CV-сертификат [cert_len?]cert с содержанием cvc. При выпуске
	используются личный ключ [privkeya_len]privkeya и сертификат
	[certa_len]certa издателя. Подпись сертификата сохраняется
	в [cvc->sig_len]cvc->sig. Перед выпуском проверяются следующие условия:
	- certa имеет корректный формат;
	- btokCVCCheck2(cvc, cvca) == ERR_OK, где cvca -- содержание certa;
	- открытый ключ в certa соответствует личному ключу privkeya.
	\return ERR_OK, если сертификат успешно выпущен, и код ошибки в противном
	случае.
	\remark Используется детерминированный режим выработки подписи. Если
	инициализирован штатный ГСЧ, то дополнительно используются данные от него.
*/
err_t btokCVCIss(
	octet cert[],				/*!< [out] сертификат */
	size_t* cert_len,			/*!< [out] длина cert в октетах */
	btok_cvc_t* cvc,			/*!< [in,out] содержание сертификата */
	const octet certa[],		/*!< [in] сертификат издателя */
	size_t certa_len,			/*!< [in] длина certa в октетах */
	const octet privkeya[],		/*!< [in] личный ключ издателя */
	size_t privkeya_len			/*!< [in] длина privkeya в октетах */
);

/*!	\brief Точная длина CV-сертификата

	Определяется точная длина CV-сертификата, размещенного в префиксе
	DER-кода [count]der.
	\return Длина сертификата или SIZE_MAX в случае ошибки.
	\remark Корректность CV-сертификата не проверяется.
*/
size_t btokCVCLen(
	const octet der[],			/*!< [in] DER-код */
	size_t count				/*!< [in] длина der в октетах */
);

/*!	\brief Проверка CV-сертификата

	Проверяется корректность CV-сертификата [cert_len]cert на дату date, 
	для чего используется сертификат издателя [certa_len]certa.
	Проверка завершается успешно, если:
	- certa имеет корректный формат;
	- cert разбирается без ошибок на открытом ключе из certa;
	- btokCVCCheck2(cvc, cvca) == ERR_OK, где cvc и cvca -- содержание cert
	  и certa соответственно;
	- date попадает в срок действия cert.
	Указатель date может быть нулевым, и тогда последняя проверка не
	выполняется.
	\return ERR_OK, если сертификат признан корректным, и код ошибки в
	противном случае.
	\remark Текущую дату можно определить с помощью функции tmDate2().
*/
err_t btokCVCVal(
	const octet cert[],		/*!< [in] сертификат */
	size_t cert_len,		/*!< [in] длина cert в октетах */
	const octet certa[],	/*!< [in] сертификат издателя */
	size_t certa_len,		/*!< [in] длина certa в октетах */
	const octet* date		/*!< [in] дата проверки */
);

/*!	\brief Проверка CV-сертификата в цепочке

	Проверяется корректность CV-сертификата [cert_len]cert на дату date, 
	для чего используется содержание cvca сертификата издателя. В случае 
	успеха определяется содержание cvc проверяемого сертификата. 
	Проверка завершается успешно, если:
	- cert имеет корректный формат;
	- имя издателя в cert совпадает с именем владельца в cvca;
	- подпись cert признается корректной на открытом ключе из cvca;
	- срок действия, заданный в cvca, корректен;
	- начало действия cert не выходит за пределы срока действия cvca;
	- date попадает в срок действия cert.
	Указатель date может быть нулевым, и тогда последняя проверка не
	выполняется. Указатель cvc может быть нулевым, и тогда содержание
	проверяемого сертификата не возвращается.
	\return ERR_OK, если сертификат признан корректным, и код ошибки в
	противном случае.
	\remark Текущую дату можно определить с помощью функции tmDate2().
	\remark Интерфейс ориентирован на проверку цепочки сертификатов.
	Цепочка certa, certb, certc,... проверяется следующим образом:
	- btokCVCUnwrap(cvca, certa, certa_len, 0, 0);
	- btokCVCVal2(cvcb, certb, certb_len, cvca, 0);
	- btokCVCVal2(cvcc, certc, certc_len, cvcb, date);
	- ...
	\remark Если certa является самоподписанным и требуется проверить его
	подпись, то первая строчка в предыдущем коде должна выглядеть следующим
	образом:
	- btokCVCUnwrap(cvca, certa, certa_len, cvca->pubkey, 0);
*/
err_t btokCVCVal2(
	btok_cvc_t* cvc,			/*!< [out] содержание сертификата */
	const octet cert[],			/*!< [in] сертификат */
	size_t cert_len,			/*!< [in] длина cert в октетах */
	const btok_cvc_t* cvca,		/*!< [in] содержание сертификата издателя */
	const octet* date			/*!< [in] дата проверки */
);

/*!	\brief Проверка соответствия CV-сертификата

	Проверяется соответствие между CV-сертификатом [cert_len]cert и личным
	ключом [privkey_len]privkey.
	Проверка завершается успешно, если:
	- cert имеет корректный формат;
	- открытый ключ cert соответствует privkey.
	\return ERR_OK, если соответствие подтверждено, и код ошибки в
	противном случае.
*/
err_t btokCVCMatch(
	const octet cert[],			/*!< [in] сертификат */
	size_t cert_len,			/*!< [in] длина cert в октетах */
	const octet privkey[],		/*!< [in] личный ключ */
	size_t privkey_len			/*!< [in] длина privkey в октетах */
);

/*!
*******************************************************************************
\file btok.h

\section btok-sm Защищенное соединение

Защищенное соединение (Secure Messaging) -- это правила защиты APDU-команд
и APDU-ответов. Общие правила огранизации защищенного соединения определены
в ISO/IEC 7816-4, конкретные правила, которые собственно и реализованы, ---
в СТБ 34.101.79.

Состояние SM содержит три объекта:
- ключ имитозащиты (belt-mac);
- ключ шифрования (belt-cfb);
- счетчик.

Защита организована по схеме encrypt-then-mac: сначала тело команды/ответа
зашифровывается, а затем к шифртексту присоединяется имитовставка, которая
покрывает счетчик, шифртекст и открытые данные команды/ответа.

Счетчик используется в качестве синхропосылки при шифровании. Перед установкой
защиты или ее снятием счетчик инкрементируется. Первоначально счетчик равняется
нулю.

Контролируется следующая логика:
- установка/снятие защиты команды выполняется с нечетными значениями счетчика;
- установка/снятие защиты ответа выполняется с четными значениями счетчика.

\expect Состояние функций SM не изменяется вне этих функций.

\pre Указатели функций SM корректны, если не оговорено противное.

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

\remark Признаком защиты команды является бит 0x04 в поле CLA.
*******************************************************************************
*/

/*!	\brief Длина состояния SM

	Возвращается длина состояния (в октетах) функций SM.
	\return Длина состояния.
*/
size_t btokSM_keep();

/*!	\brief Инициализация функций SM

	По ключу key в state формируются объекты, необходимые для управления
	защищенным соединением.
	\pre По адресу state зарезервировано btokSM_keep() октетов.
	\remark В перечень объектов входит счетчик, первоначально равный нулю. 
*/
void btokSMStart(
	void* state,			/*!< [out] состояние SM */
	const octet key[32]		/*!< [in] ключ */
);

/*!	\brief Увеличение счетчика SM

	Счетчик, размещенный в состоянии state, увеличивается на 1.
	\pre По адресу state зарезервировано btokSM_keep() октетов.
	\expect btokSMStart() < btokSMCtrInc()*.
	\remark Отправитель команд и ответов должен увеличивать счетчик	всякий
	раз перед установкой их защиты. Соответственно получатель должен
	увеличивать счетчик всякий раз перед снятием защиты.
	\remark Установка и снятие защиты команд должны выполняться с нечетными
	значениями счетчика. Установка и снятие защиты ответов -- с четными.
*/
void btokSMCtrInc(
	void* state				/*!< [in,out] состояние SM */
);

/*!	\brief Кодирование и установка защиты команды с помощью SM

	Команда cmd кодируется и защищается с помощью объектов SM, размещенных
	в state. Результат возвращается в буфере [count?]apdu. Указатель state
	может быть нулевым, и тогда выполняется только кодирование, без защиты.
	\pre По адресу state зарезервировано btokSM_keep() октетов.
	\expect btokSMStart() < btokSMCmdWrap()*.
	\expect{ERR_BAD_APDU} В cmd->cla снят бит 0x04 (признак защиты).
	\expect{ERR_BAD_LOGIC} Непосредственно а момент установки защиты
	(apdu != 0 && state != 0) счетчик SM принимает нечетное значение.
	\return ERR_OK в случае успеха и код ошибки в противном случае.
*/
err_t btokSMCmdWrap(
	octet apdu[],				/*!< [out] код команды */
	size_t* count,				/*!< [out] длина кода команды */
	const apdu_cmd_t* cmd,		/*!< [in] команда */
	void* state					/*!< [in,out] состояние SM */
);

/*!	\brief Декодирование и снятие защиты команды с помощью SM

	Код команды [count]apdu декодируется и одновременно с него снимается
	защита с помощью объектов SM, размещенных в state. Результат возвращается
	в буфере [size?]cmd. Указатель state может быть нулевым, и тогда
	выполняется только декодирование, без снятия защиты. Указатель cmd может
	быть нулевым, и тогда выполняется только проверка формата кода, без
	контроля целостности.
	\pre По адресу state зарезервировано btokSM_keep() октетов.
	\expect btokSMStart() < btokSMCmdUnwrap()*.
	\expect{ERR_BAD_APDU} Если state != 0, то в cmd->cla установлен бит 0x04
	(признак защиты). Если state == 0, то бит снят.
	\expect{ERR_BAD_LOGIC} Непосредственно а момент снятия защиты
	(cmd != 0 && state != 0) счетчик SM принимает нечетное значение.
	\return ERR_OK в случае успеха и код ошибки в противном случае.
*/
err_t btokSMCmdUnwrap(
	apdu_cmd_t* cmd,			/*!< [out] команда */
	size_t* size,				/*!< [out] размер буфера для команды */
	const octet apdu[],			/*!< [in] код команды */
	size_t count,				/*!< [in] длина кода команды */
	void* state					/*!< [in,out] состояние SM */
);

/*!	\brief Кодирование и установка защиты ответа с помощью SM

	Ответ resp кодируется и защищается с помощью объектов SM, размещенных
	в state. Результат возвращается в буфере [count?]apdu. Указатель state
	может быть нулевым, и тогда выполняется только кодирование, без защиты.
	\pre По адресу state зарезервировано btokSM_keep() октетов.
	\expect btokSMStart() < btokSMRespWrap()*.
	\expect{ERR_BAD_LOGIC} Непосредственно а момент установки защиты
	(apdu != 0 && state != 0) счетчик SM принимает четное значение.
	\return ERR_OK в случае успеха и код ошибки в противном случае.
*/
err_t btokSMRespWrap(
	octet apdu[],				/*!< [out] код ответа */
	size_t* count,				/*!< [out] длина кода ответа */
	const apdu_resp_t* resp,	/*!< [in] ответ */
	void* state					/*!< [in,out] состояние SM */
);

/*!	\brief Декодирование и снятие защиты ответа с помощью SM

	Код ответа [count]apdu декодируется и одновременно с него снимается
	защита с помощью объектов SM, размещенных в state. Результат возвращается
	в буфере [size?]resp. Указатель state может быть нулевым, и тогда
	выполняется только декодирование, без снятия защиты. Указатель resp может
	быть нулевым, и тогда выполняется только проверка формата кода, без
	контроля целостности. 
	\pre По адресу state зарезервировано btokSM_keep() октетов.
	\expect btokSMStart() < btokSMRespUnwrap()*.
	\expect{ERR_BAD_LOGIC} Непосредственно а момент снятия защиты
	(resp != 0 && state != 0) счетчик SM принимает четное значение.
	\return ERR_OK в случае успеха и код ошибки в противном случае.
*/
err_t btokSMRespUnwrap(
	apdu_resp_t* resp,			/*!< [out] ответ */
	size_t* size,				/*!< [out] размер буфера для ответа */
	const octet apdu[],			/*!< [in] код ответа */
	size_t count,				/*!< [in] длина кода ответа */
	void* state					/*!< [in,out] состояние SM */
);

/*!
*******************************************************************************
\file btok.h

\section btok-bauth Протокол BAUTH

Протокол выполняется между терминалом (T) и криптографическим токеном
(КТ, cryptographic token = CT).

Для настройки протокола используется структура bake_settings (см. bake.h).
В терминах протоколов Bake в роли стороны A выступает терминал, в роли
стороны B -- криптографический токен.

Терминал всегда подтверждает сформированный ключ, т.е. проходит аутентификацию
перед КТ. Поэтому в структуре bake_settings флаг kca должен равняться TRUE.

КТ проходит аутентификацию перед КТ опционально: в структуре bake_settings
флаг kcb может принимать как значение TRUE, так и значение FALSE.

\expect{ERR_BAD_INPUT} Все входные указатели, за исключением оговоренных
случаев, корректны.

\expect При пошаговом выполнении протокола данные, переданные при инициализации
через указатели, остаются корректными и постоянными на протяжении всего
выполнения протокола.
*******************************************************************************
*/

/*!	\brief Длина состояния функций BAUTH на стороне Т

	Определяется длина состояния (в октетах) функций протокола BAUTH на
	стороне Т.
	\return Длина состояния.
*/
size_t btokBAuthT_keep(
	size_t l						/*!< [in] уровень стойкости */
);

/*!	\brief Инициализация протокола BAUTH на стороне Т

	По параметрам params, настройкам settings, личному ключу [l / 4]privkey
	и сертификату cert соответствующего открытого ключа в state формируются
	структуры данных, необходимые для выполнения протокола BAUTH на стороне T.
	\pre По адресу state зарезервировано btokBAuthT_keep() октетов.
	\expect{ERR_BAD_PARAMS} Параметры params корректны.
	\expect{ERR_BAD_INPUT} settings->kca == TRUE.
	\expect{ERR_BAD_INPUT} Указатель settings->helloa нулевой, либо буфер
	[settings->helloa_len]settings->helloa корректен. Аналогичное требование
	касается полей settings->hellob, settings->hellob_len.
	\expect{ERR_BAD_RNG} Генератор settings->rng (с состоянием
	settings->rng_state) корректен.
	\expect Генератор settings->rng является криптографически стойким.
	\expect{ERR_BAD_CERT} Сертификат cert корректен.
	\expect Ключ privkey и сертификат cert согласованы. Если согласование
	нарушено, то протокол будет завершен с ошибкой.
	\return ERR_OK, если инициализация успешно выполнена, и код ошибки
	в противном случае.
*/
err_t btokBAuthTStart(
	void* state,					/*!< [out] состояние */
	const bign_params* params,		/*!< [in] долговременные параметры */
	const bake_settings* settings,	/*!< [in] настройки */
	const octet privkey[],			/*!< [in] личный ключ */
	const bake_cert* cert			/*!< [in] сертификат */
);

/*!	\brief Длина состояния функций BAUTH на стороне КТ

	Определяется длина состояния (в октетах) функций протокола BAUTH на
	стороне КТ.
	\return Длина состояния.
*/
size_t btokBAuthCT_keep(
	size_t l						/*!< [in] уровень стойкости */
);

/*!	\brief Инициализация протокола BAUTH на стороне КТ

	По параметрам params, настройкам settings, личному ключу [l / 4]privkey 
	и сертификату cert соответствующего открытого ключа в state формируются 
	структуры данных, необходимые для выполнения протокола BAUTH на стороне КТ.
	\pre По адресу state зарезервировано btokBAuthCT_keep() октетов.
	\expect{ERR_BAD_PARAMS} Параметры params корректны.
	\expect{ERR_BAD_INPUT} settings->kca == TRUE.
	\expect{ERR_BAD_INPUT} Указатель settings->helloa нулевой, либо буфер 
	[settings->helloa_len]settings->helloa корректен. Аналогичное требование
	касается полей settings->hellob, settings->hellob_len.
	\expect{ERR_BAD_RNG} Генератор settings->rng (с состоянием 
	settings->rng_state) корректен.
	\expect Генератор settings->rng является криптографически стойким.
	\expect{ERR_BAD_CERT} Сертификат cert корректен.
	\expect Ключ privkey и сертификат cert согласованы. Если согласование
	нарушено, то протокол будет завершен с ошибкой.
	\return ERR_OK, если инициализация успешно выполнена, и код ошибки 
	в противном случае.
*/
err_t btokBAuthCTStart(
	void* state,					/*!< [out] состояние */
	const bign_params* params,		/*!< [in] долговременные параметры */
	const bake_settings* settings,	/*!< [in] настройки */
	const octet privkey[],			/*!< [in] личный ключ */
	const bake_cert* cert			/*!< [in] сертификат */
);

/*!	\brief Шаг 2 протокола BAUTH

	КТ выполняет шаг 2 протокола BAUTH, обрабатывая сертификат терминала certt
	и используя состоянием state. При успешном выполнении шага формируется
	сообщение M1 = [5 l / 8 + 16]out.
	\expect btokBAuthCTStart() < btokBAuthCTStep2().
	\return ERR_OK, если шаг успешно выполнен, и код ошибки в противном случае.
	\remark Приветственное сообщение КТ в M1 не передаются. 
*/
err_t btokBAuthCTStep2(
	octet out[],				/*!< [out] выходное сообщение M1 */
	const bake_cert* certt,		/*!< [in] сертификат терминала */
	void* state					/*!< [in,out] состояние */
);

/*!	\brief Шаг 3 протокола BAUTH

	Т выполняет шаг 3 протокола BAUTH, обрабатывая сообщение
	M1 = [5 l / 8 + 16]in и используя состояние state.
	При успешном выполнении шага формируется сообщение M2 = [8]out,
	если не требуется аутентификация перед Т, или M2 = [8 + 16]out, если
	требуется.
	\expect btokBAuthTStart() < btokBAuthTStep3().
	\expect btokBAuthCTStep2() << btokBAuthTStep3().
	\return ERR_OK, если шаг успешно выполнен, и код ошибки в противном случае.
*/
err_t btokBAuthTStep3(
	octet out[],			/*!< [out] выходное сообщение M2 */
	const octet in[],		/*!< [in] входное сообщение M1 */
	void* state				/*!< [in,out] состояние */
);

/*!	\brief Шаг 4 протокола BAUTH

	КТ выполняет шаг 4 протокола BAUTH, обрабатывая сообщение M2 = [8]in,
	если не требуется аутентификация перед Т, или M2 = [8 + 16]in,
	если не требуется, и используя состояние state. Если аутентификация
	перед КТ требуется, то при успешном выполнении шага формируется
	сообщение M3 = [l / 4 + cert->len + 8]out.
	\expect btokBAuthCTStep2() < btokBAuthCTStep4().
	\expect btokBAuthTStep3() << btokBAuthCTStep4().
	\return ERR_OK, если шаг успешно выполнен, и код ошибки в противном случае.
*/
err_t btokBAuthCTStep4(
	octet out[],			/*!< [out] выходное сообщение M3 */
	const octet in[],		/*!< [in] входное сообщение M2 */
	void* state				/*!< [in,out] состояние */
);

/*!	\brief Шаг 5 протокола BAUTH

	Т выполняет шаг 5 протокола BAUTH, обрабатывая сообщение M3 = [in_len]in
	и используя состояние state. Т проверяет присланный в M3 сертификат
	КТ с помощью функции val_ct.
	\expect{ERR_BAD_LOGIC} settings->kcb == TRUE, т.е. требуется аутентификация
	КТ перед Т.
	\expect btokBAuthTStep3() < btokBAuthTStep5().
	\expect btokBAuthCTStep4() << btokBAuthTStep5().
	\return ERR_OK, если шаг успешно выполнен, и код ошибки в противном случае.
*/
err_t btokBAuthTStep5(
	const octet in[],		/*!< [in] входное сообщение M3 */
	size_t in_len,			/*!< [in] длина in */
	bake_certval_i val_ct,	/*!< [in] функция проверки сертификата КТ */
	void* state				/*!< [in,out] состояние */
);

/*!	\brief Извлечение ключа протокола BAUTH на стороне КТ

	КТ определяет общий секретный ключ key, полученный с помощью протокола
	BAUTH с состоянием state.
	\expect btokBAuthCTStep4() < btokBAuthCTStepG().
	\return ERR_OK, если ключ успешно извлечен, и код ошибки в противном
	случае.
*/
err_t btokBAuthCTStepG(
	octet key[32],			/*!< [out] общий ключ */
	void* state				/*!< [in,out] состояние */
);

/*!	\brief Извлечение ключа протокола BAUTH на стороне T

	Т определяет общий секретный ключ key, полученный с помощью протокола BAUTH
	с состоянием state.
	\expect Если КТ аутентифицируется перед терминалом, то 
	btokBAuthTStep5() < btokBAuthTStepG(),
	иначе btokBAuthTStep3() < btokBAuthTStepG().
	\return ERR_OK, если ключ успешно извлечен, и код ошибки в противном случае.
*/
err_t btokBAuthTStepG(
	octet key[32],			/*!< [out] общий ключ */
	void* state				/*!< [in,out] состояние */
);

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

#endif /*__BEE2_BTOK_H */
