/*
*******************************************************************************
\file cmd.h
\brief Command-line interface to Bee2
\project bee2/cmd
\created 2022.06.09
\version 2025.10.09
\copyright The Bee2 authors
\license Licensed under the Apache License, Version 2.0 (see LICENSE.txt).
*******************************************************************************
*/

#ifndef __BEE2_CMD_H
#define __BEE2_CMD_H

#ifdef __cplusplus
extern "C" {
#endif

#include <bee2/defs.h>
#include <bee2/core/blob.h>
#include <bee2/core/err.h>
#include <bee2/core/tm.h>
#include <bee2/crypto/btok.h>

/*
*******************************************************************************
Блобы
*******************************************************************************
*/

/*! \brief Создание блоба */
#define cmdBlobCreate(blob, size) \
	(((blob) = blobCreate(size)) ? ERR_OK : ERR_OUTOFMEMORY)

/*! \brief Создание блоба с разметкой памяти */
#define cmdBlobCreate2(blob, ...) \
	(((blob) = blobCreate2(__VA_ARGS__)) ? ERR_OK : ERR_OUTOFMEMORY)

	/*! \brief Изменение размера блоба */
#define cmdBlobResize(b, blob, size)\
	(((b) = blobResize(blob, size)) ? ERR_OK : ERR_OUTOFMEMORY)

/*! \brief Закрытие блоба */
#define cmdBlobClose blobClose

/*
*******************************************************************************
Регистрация команд
*******************************************************************************
*/

/*!	\brief Главная функция команды */
typedef int (*cmd_main_i)(
	int argc,				/*!< [in] число параметров командной строки */
	char* argv[]			/*!< [in] параметры командной строки */
);

/*!	\brief Регистрация команды

	Регистрируется команда с именем name, описанием descr и главной
	функцией fn.
	\expect Строки name и descr -- статические, указатели на них остаются
	корректными на протяжении всего времени выполнения. 
	\expect{ERR_BAD_FORMAT} 1 <= strLen(name) <= 8 && strLen(descr) <= 60.
	\return ERR_OK, если команда зарегистрирована, и код ошибки в противном
	случае.
*/
err_t cmdReg(
	const char* name, 		/*!< [in] имя команды */
	const char* descr,		/*!< [in] описание команды */
	cmd_main_i fn			/*!< [in] главная функция команды */
);

/*!
*******************************************************************************
\def PGBC_ST_BASH
\brief Тестирование алгоритмов СТБ 34.101.77 (Bash)

\def PGBC_ST_BELS
\brief Тестирование алгоритмов СТБ 34.101.60 (Bels)

\def PGBC_ST_BELT
\brief Тестирование алгоритмов СТБ 34.101.31 (Belt)

\def PGBC_ST_BIGN
\brief Тестирование алгоритмов СТБ 34.101.45 (Bign)

\def PGBC_ST_BRNG
\brief Тестирование алгоритмов СТБ 34.101.47 (Brng)

\def PGBC_ST_RNG
\brief Тестирование ГСЧ

\def PGBC_ST_STAMP
\brief Проверка контрольного штампа

\def PGBC_ST_ALGS
\brief Тестирование всех криптографических алгоритмов

\def PGBC_ST_ALL
\brief Полное самотестирование
*******************************************************************************
*/

#define CMD_ST_BASH		0x00000001
#define CMD_ST_BELS		0x00000002
#define CMD_ST_BELT		0x00000004
#define CMD_ST_BIGN		0x00000008
#define CMD_ST_BRNG		0x00000010
#define CMD_ST_RNG		0x01000000
#define CMD_ST_STAMP	0x02000000
#define CMD_ST_ALGS\
	(CMD_ST_BASH | CMD_ST_BELS | CMD_ST_BELT | CMD_ST_BIGN | CMD_ST_BRNG)
#define CMD_ST_ALL\
	(CMD_ST_ALGS | CMD_ST_RNG | CMD_ST_STAMP)

/*!	\brief Самотестирование

	Выполняются тесты в соответствии с перечнем tests.
	\return ERR_OK в случае успеха и код ошибки в противном случае.
	\remark Тесты криптографических алгоритмов выполняются раньше остальных.
*/
err_t cmdStDo(
	u32 tests			/*!< [in] перечень тестов */
);

/*!	\brief Контрольная сумма

	Вычисляется контрольная сумма crc исполняемого модуля, в котором вызывается 
	данная функция. Если prefix != 0, то при хэшировании модуль предваряется 
	строкой prefix.
	\return ERR_OK в случае успеха и код ошибки в противном случае.
	\remark Контрольная сумма вычисляется с помощью алгоритма belt-hash.
	\remark Контрольная сумма в ответ на волатильную строку prefix выступает 
	в роли доказаттельства хранения (Proof-of-Storage) исполняемого модуля.
*/
err_t cmdStCrc(
	octet crc[32], 				/*!< [out] контрольная сумма */
	const char* prefix			/*!< [in] prefix */
);

/*
*******************************************************************************
Терминал
*******************************************************************************
*/

/*!	\brief Нажата клавиша?

	Опрашивается теримнал и проверяется, что очередь нажатых клавиш не пуста.
	\return Признак нажатия.
	\remark Очередь очищается в том числе через вызовы cmdTermGetch().
*/
bool_t cmdTermKbhit();

/*!	\brief Чтение символа

	Прочитывается символ, введенный в терминале.
	\return Прочитанный символ.
	\remark Прочитанный символ не экранируется.
*/
int cmdTermGetch();

/*
*******************************************************************************
Пути к исполняемым файлам / модулям

Исполняемый файл -- это главная программа текушего процесса. Исполняемый 
модуль --- это либо исполняемый файл, либо динамическая библиотека, 
используемая в текущем процессе.
*******************************************************************************
*/

/*!	\brief Путь к исполняемому файлу

	Определяется путь [?count]path к текущему исполняемому файлу.
	\return ERR_OK в случае успеха и код ошибки в противном случае.
	\remark Длина пути учитывает завершающий нулевой символ.
*/
err_t cmdSysExePath(
	char* path,			/*!< [out] путь */
	size_t* count 		/*!< [in,out] длина пути */
);

/*!	\brief Путь к исполняемому модулю

	Определяется путь [?count]path к текущему исполняемому модулю.
	\return ERR_OK в случае успеха и код ошибки в противном случае.
	\remark Длина пути учитывает завершающий нулевой символ.
*/
err_t cmdSysModulePath(
	char* path,			/*!< [out] путь */
	size_t* count 		/*!< [in,out] длина пути */
);

/*
*******************************************************************************
ГСЧ

В функции cmdKbRead() реализован клавиатурный источник энтропии. Реализация
соответствует СТБ 34.101.27-2011 (Б.7):
- при нажатии клавиш фиксируются значения высокоточного таймера (регистр TSC);
- разность между значениями регистра сохраняется, если друг за другом нажаты
  две различные клавиши и интервал между нажатиями более 50 мс;
- всего сохраняется 128 разностей;
- собранные разности объединяются и хэшируются;
- хэш-значение (32 октета) возвращается в качестве энтропийных данных.

Дополнительно в cmdKbRead() проверяется, что интервал между нажатиями клавиш
не превышает 5 секунд. При отсутствии активности со стороны пользователя
сбор данных от источника будет прекращен.

В функции cmdRngStart() проверяются требования СТБ 34.101.27-2020 уровня 1:
наличие работоспособного физического источника энтропии или двух различных
работоспособных источников. Если недостает одного источника, то задействуется
клавиатурный.
*******************************************************************************
*/

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

	Прочитываются даные data с клавиатурного источника энтропии.
	\return ERR_OK в случае успеха и код ошибки в противном случае.
*/
err_t cmdRngKbRead(
	tm_ticks_t data[128]	/*!< [out] данные */
);

/*!	\brief Запуск ГСЧ

	Запускается штатный ГСЧ. При установке флага verbose запуск сопровождается
	экранным выводом.
	\return ERR_OK в случае успеха и код ошибки в противном случае.
*/
err_t cmdRngStart(
	bool_t verbose			/*!< [in] печатать подробности */
);

/*
*******************************************************************************
Дата
*******************************************************************************
*/

/*!	\brief Разбор даты

	По строке str формата YYMMDD формируется дата date. При str == "000000" в
	date устанавливается текущая дата.
	\return ERR_OK, если строка имеет нужный формат и дата сформирована, и код
	ошибки в противном случае.
*/
err_t cmdDateParse(
	octet date[6],					/*!< [out] дата */
	const char* str					/*!< [in] строка */
);

/*
*******************************************************************************
Печать
*******************************************************************************
*/

/*!	\brief Печать памяти

	Печатается шестнадцатеричное представление буфера памяти [count]buf.
	\return ERR_OK в случае успеха и код ошибки в противном случае.
*/
err_t cmdPrintMem(
	const void* buf,				/*!< [in] буфер */
	size_t count					/*!< [in] длина буфера в октетах */
);

/*!	\brief Сокращенная печать памяти

	Печатается сокращенное шестнадцатеричное представление буфера памяти
	[count]buf:
	- если count > 14, то печатаются 12 первых октетов, многоточие,
	  2 последних октета и общее число октетов в скобках;
	- если count <= 14, то печатаются все октеты.
	\return ERR_OK в случае успеха и код ошибки в противном случае.
*/
err_t cmdPrintMem2(
	const void* buf,				/*!< [in] буфер */
	size_t count					/*!< [in] длина буфера в октетах */
);

/*!	\brief Печать даты

	Печатается дата date.
	\return ERR_OK в случае успеха и код ошибки в противном случае.
*/
err_t cmdPrintDate(
	const octet date[6]				/*!< [in] дата */
);

/*
*******************************************************************************
Файлы

Файл может содержать несколько префиксов и несколько суффиксов. Префиксы и
суффиксы (аффиксы) кодируются по правилам АСН.1. Префикс представляется 
обычным DER-кодом, суффикс --- реверсивным (обратный порядок октетов).
Поскольку в заголовке DER-кода указывается его длина, при обработке файла
с начала (с конца) можно определить все его последовательные префиксы
(суффиксы).

\remark Пара параметров (count, names) функций cmdFileValXXX() соответствует
паре (argc, argv) функции main(). Если заменить тип names на const char**,
то возможны предупредеждения компилятора при переходе от argv к names
(см. http://c-faq.com/ansi/constmismatch.html).
*******************************************************************************
*/

/*! \brief Открытие файла */
#define cmdFileOpen(file, name, mode) \
	(((file) = fileOpen(name, mode)) ? ERR_OK : ERR_FILE_OPEN)

/*! \brief Закрытие файла */
#define cmdFileClose(file) \
	(fileClose(file))

/*! \brief Закрытие файла с контролем */
#define cmdFileClose2(file) \
	(fileClose2(file) ? ERR_OK : ERR_BAD_FILE)

/*!	\brief Размер файла

	Определяется размер файла с именем name.
	\return Размер файла или SIZE_MAX в случае ошибки.
*/
size_t cmdFileSize(
	const char* name		/*!< [in] имя файла */
);

/*!	\brief Запись в файл

	Создается файл name и в него записывается буфер [count]buf.
	\return ERR_OK в случае успеха и код ошибки в противном случае.
*/
err_t cmdFileWrite(
	const char* name,	/*!< [in] имя файла */
	const void* buf,	/*!< [in] буфер */
	size_t count		/*!< [in] длина буфера */
);

/*!	\brief Дозапись в начало файла

	В начало файла name дописывается буфер [count]buf.
	\return ERR_OK в случае успеха и код ошибки в противном случае.
	\remark Если файл name не существует, то он создается.
*/
err_t cmdFilePrepend(
	const char* name,	/*!< [in] имя файла */
	const void* buf,	/*!< [in] буфер */
	size_t count		/*!< [in] длина буфера */
);

/*!	\brief Дозапись в конец файла

	В конец файла name дописывается буфер [count]buf.
	\return ERR_OK в случае успеха и код ошибки в противном случае.
	\remark Если файл name не существует, то он создается.
*/
err_t cmdFileAppend(
	const char* name,	/*!< [in] имя файла */
	const void* buf,	/*!< [in] буфер */
	size_t count		/*!< [in] длина буфера */
);

/*!	\brief Чтение всего файла

	Буфер [?count]buf прочитывается из файла name. При ненулевом buf
	дополнительно проверяется, что переданная длина в точности совпадает
	с размером файла.
	\return ERR_OK в случае успеха и код ошибки в противном случае.
*/
err_t cmdFileReadAll(
	void* buf,			/*!< [out] буфер */
	size_t* count,		/*!< [in,out] длина буфера */
	const char* name	/*!< [in] имя файла */
);

/*!	\brief Обрезка файла с начала

	В файле name удаляется count первых октетов.
	\expect{ERR_BAD_LENGTH} Длина файла не меньше count.
	\return ERR_OK в случае успеха и код ошибки в противном случае.
*/
err_t cmdFileBehead(
	const char* name,	/*!< [in] имя файла */
	size_t count		/*!< [in] число октетов */
);

/*!	\brief Обрезка файла с конца

	В файле name удаляется count последних октетов.
	\expect{ERR_BAD_LENGTH} Длина файла не меньше count.
	\return ERR_OK в случае успеха и код ошибки в противном случае.
*/
err_t cmdFileDrop(
	const char* name,	/*!< [in] имя файл */
	size_t count		/*!< [in] число октетов */
);

/*!	\brief Дублирование содержимого файла

	В файле iname пропускаются первые skip октетов, а следующие count октетов
	переписываются в файл oname. При count == SIZE_MAX переписываются все
	октеты iname вплоть до конца файла.
	\return ERR_OK в случае успеха и код ошибки в противном случае.
*/
err_t cmdFileDup(
	const char* oname,	/*!< [in] имя выходного файла */
	const char* iname,	/*!< [in] имя входного файла */
	size_t skip,		/*!< [in] число пропускаемых октетов */
	size_t count		/*!< [in] число дублируемых октетов */
);

/*!	\brief Проверка отсутствия файлов

	Проверяется, что файлы списка [count]names отсутствуют и, таким образом,
	их можно создавать и записывать в них данные. Если некоторый файл все-таки
	присутствует, то предлагается его перезаписать. Разрешение на перезапись
	приравнивается к отсутствию файла.
	\return ERR_OK в случае успеха и код ошибки в противном случае. 
*/
err_t cmdFileValNotExist(
	int count,				/*!< [in] число файлов */
	char* names[]			/*!< [in] список имен файлов */
);

/*!	\brief Проверка наличия файлов

	Проверяется существование файлов списка [count]names.
	\return ERR_OK в случае успеха и код ошибки в противном случае.
*/
err_t cmdFileValExist(
	int count,				/*!< [in] число файлов */
	char* names[]			/*!< [in] список имен файлов */
);

/*!	\brief Проверка совпадения файлов

	Проверяется, что имена name1 и name2 соответствуют одному и тому же файлу.
	\return Признак успеха.
	\remark Корректность имен не проверяется.
*/
bool_t cmdFileAreSame(
	const char* name1,		/*!< [in] первое имя файла */
	const char* name2		/*!< [in] второе имя файла */
);

/*!	\brief Чтение префикса файла

	Префикс [?count]prefix прочитывается из файла name со смещением offset
	от его начала.
	\expect{ERR_BAD_FORMAT} Файл содержит префикс по указанному смещению.
	\return ERR_OK в случае успеха и код ошибки в противном случае.
*/
err_t cmdFilePrefixRead(
	octet* prefix,		/*!< [out] префикс */
	size_t* count,		/*!< [in,out] длина префикса */
	const char* name,	/*!< [in] имя файла */
	size_t offset		/*!< [in] смещение */ 
);

/*!	\brief Чтение суффикса файла

	Суффикс [?count]suffix прочитывается из файла name со смещением offset
	от его конца.
	\expect{ERR_BAD_FORMAT} Файл содержит суффикс по указанному смещению.
	\return ERR_OK в случае успеха и код ошибки в противном случае.
	\remark Суффикс возвращается в том виде, в котором он записан в файле --
	без реверса октетов.
*/
err_t cmdFileSuffixRead(
	octet* suffix,		/*!< [out] суффикс */
	size_t* count,		/*!< [in,out] длина суффикса */
	const char* name,	/*!< [in] имя файла */
	size_t offset		/*!< [in] смещение */
);

/*!	\brief Удаление файла

	Удаляется файл с именем name.
	\return ERR_OK в случае успеха и код ошибки в противном случае.
*/
err_t cmdFileDel(
	const char* name	/*!< [in] имя файл */
);

/*
*******************************************************************************
Командная строка
*******************************************************************************
*/

/*!	\brief Создание списка аргументов

	По командной строке args строится список аргументов [argc]argv.
	\pre Указатели argc, argv и строка args корректны.
	\return ERR_OK в случае успеха и код ошибки в противном случае. 
	\remark Правила разбора аргументов (в соответствии с
	https://docs.microsoft.com/cpp/c-language/parsing-c-command-line-arguments):
	1. Аргументами считаются фрагменты args, разделенные пробелами или
	   знаками табуляции.
	2. Фрагмент args, окаймленный двойными кавычками ("), не разбивается
	   на части, даже если содержит пробелы или знаки табуляции. Фрагмент
	   в кавычках переносится в аргумент без кавычек. В аргумент может быть
	   встроено несколько фрагментов в кавычках.
	3. Ошибкой не считается, если достигнут конец строки, но кавычка не закрыта.
	   Символ конца строки в этом случае считается завершающей кавычкой. 
	4. Внутри фрагмента в кавычках две кавычки подряд ("") интерпретируются как
	   одна.
	5. В правилах 2 и 3 двойная кавычка выступает в роли синтаксического
	   разделителя. Для указания на то, что кавычка используется как литерал,
	   перед ней должен быть указан обратный слэш (\).
	6. Последовательность из n обратных слэшей, за которой следует двойная
	   кавычка интерпретируется двумя способами:
	   - при n = 2m как строка из m обратных косых и кавычки-разделителя;
	   - при n = 2m + 1 как строка из m обратных косых и кавычки-литерала.
	   Правило не распространяется на последовательность обратных слэшей, за
	   которой не следует двойная кавычка.
*/
err_t cmdArgCreate(
	int* argc,			/*!< [out] число аргументов */
	char*** argv,		/*!< [out] список аргументов */
	const char* args	/*!< [in] командная строка */
);

/*!	\brief Закрытие списка аргументов

	Закрывается список аргументов argv, созданный в функции cmdArgCreate().
	\pre cmdArgCreate() < cmdArgClose().
*/
void cmdArgClose(
	char** argv			/*!< [in] список аргументов */
);

/*
*******************************************************************************
Управление паролями

Пароль представляется собой стандартную C-строку (с завершающим нулем).

Пароль размещается в блобе, который создается внутри функций cmdPwdGen(),
cmdPwdRead(). Схема возврата [pwd_len?]pwd не используется, потому что
в некоторых случаях (например, при вводе пароля с консоли) важно, чтобы пароль
можно было возвратить за один вызов функции. За закрытие блоба отвечает
вызывающая программа.

Пароль задается в командной строке в стиле OpenSSL:
- обычно после аргумента "-pass" / "-passin" / "-passout" / ...;
- имеется несколько схем задания пароля;
- схема pass предназначена для задания пароля прямо в командной строке:
  "-pass pass:password";
- схема env предназначена для задания пароля через переменную окружения:
  "-pass env:ENV_VAR";
- схема share предназначена для задания пароля с помощью методов
  разделения секрета в соответствии с правилами СТБ 34.101.78:
  "-pass share:\"share_descr\"".

В схеме share параметры share_args, которые описывают настройку разделения
секрета, имеют следующий синтаксис:
\code
  [-t<nn>] [-l<mmm>] -pass <schema> <share1> <share2> ....
\endcode
Здесь:
- <nn> -- порог числа частичных секретов (2 <= n <= 16, 2 по умолчанию);
- <mmm> -- уровень стойкости (128, 192 или 256, 256 по умолчанию);
- <schema> --- описание пароля защиты файлов с частичными секретами;
- <share1>, <share2>,... -- файлы с частичными секретами (их число должно быть
  не меньше порога и не больше 16).

\todo Поддержать опции "file:pathname", "fd:number" и "stdin", реализованные 
в OpenSSL
(https://www.openssl.org/docs/manmaster/man1/openssl-passphrase-options.html).

\todo Поддержать правила кодирования паролей, реализованные в OpenSSL
(https://www.openssl.org/docs/manmaster/man7/passphrase-encoding.html).
*******************************************************************************
*/

/*!	\brief Пароль */
typedef char* cmd_pwd_t;

/*!	\brief Создание пароля

	Создается блоб для хранения пароля максимальной длины size.
	\return Дескриптор блоба. Нулевой дескриптор возвращается
	при нехватке памяти.
	\remark При создании блоба все его октеты обнуляются.
*/
cmd_pwd_t cmdPwdCreate(
	size_t size			/*!< [in] максимальная длина */
);

/*!	\brief Корректный пароль?

	Проверяется корректность пароля pwd.
	\return Признак корректности.
	\remark Пароль корректен, если он хранится в корректном непустом блобе,
	который завершается нулевым октетом.
*/
bool_t cmdPwdIsValid(
	const cmd_pwd_t pwd		/*!< [in] корректный блоб */
);

/*!	\brief Закрытие пароля

	Выполняется очистка и освобождение блоба пароля pwd.
*/
void cmdPwdClose(
	cmd_pwd_t pwd		/*!< [in] пароль */
);

/*!	\brief Длина пароля */
#define cmdPwdLen strLen

/*!	\brief Генерация пароля

	Пароль pwd генерируется по схеме schema.
	\expect{ERR_NOT_IMPLEMENTED} Используется парольная схема share.
	\return ERR_OK, если пароль успешно сгенерирован, и код ошибки в противном
	случае. 
	\remark За закрытие пароля отвечает вызывающая программа.
*/
err_t cmdPwdGen(
	cmd_pwd_t* pwd,			/*!< [out] пароль */
	const char* schema		/*!< [in] схема */
);

/*!	\brief Определение пароля

	Определяется пароль pwd, ранее построенный по схеме schema.
	\return ERR_OK, если пароль успешно определен, и код ошибки в противном
	случае.
	\remark За закрытие пароля отвечает вызывающая программа.
*/
err_t cmdPwdRead(
	cmd_pwd_t* pwd,			/*!< [out] пароль */
	const char* schema		/*!< [in] схема */
);

/*
*******************************************************************************
Управление личными ключами СТБ 34.101.45 (bign)

Личные ключи хранятся в контейнерах, защищенных на паролях по схеме
СТБ 34.101.78 (bpki).

\expect Личный ключ связан со стандартными параметрами СТБ 34.101.45 того или
иного уровня стойкости: bign-curve256v1, bign-curve384v1 или bign-curve512v1.
*******************************************************************************
*/

/*!	\brief Запись личного ключа в контейнер

	Личный ключ [privkey_len]privkey записывается в защищенный файл-контейнер
	name. Защита устанавливается на пароле pwd.
	\return ERR_OK, если ключ успешно записан, и код ошибки	в противном случае.
*/
err_t cmdPrivkeyWrite(
	const octet privkey[],			/*!< [in] личный ключ */
	size_t privkey_len,				/*!< [in] длина личного ключа */
	const char* name,				/*!< [in] имя контейнера */
	const cmd_pwd_t pwd				/*!< [in] пароль защиты */
);

/*!	\brief Чтение личного ключа из контейнера

	Личный ключ [privkey_len?]privkey читается из защищенного файла-контейнера
	name. Защита снимается на пароле pwd.
	\pre Если адрес privkey_len ненулевой, то по этому адресу передается
	одно из следующих значений: 0, 32, 48, 64. Нулевое значение соответствует
	стандартной логике [privkey_len?]. Ненулевые значения соответствуют логике
	[?privkey_len], то есть задают требуемую длину ключа.
	\expect{ERR_BAD_FORMAT} Если privkey_len != 0 и *privkey_len != 0,
	то контейнер name содержит ключ из *privkey_len октетов.
	\return ERR_OK, если ключ успешно прочитан, и код ошибки в противном случае.
*/
err_t cmdPrivkeyRead(
	octet privkey[],				/*!< [out] личный ключ */
	size_t* privkey_len,			/*!< [out] длина личного ключа */
	const char* name,				/*!< [in] имя контейнера */
	const cmd_pwd_t pwd				/*!< [in] пароль защиты */
);

/*
*******************************************************************************
CV-сертификаты
*******************************************************************************
*/

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

	Печатается область CV-сертификата cvc, заданная строкой scope:
	- если scope != 0, то печатается поле с именем scope ("authority",
	  "holder", "from", "until", "eid", "esign" или "pubkey");
	- если scope == 0, то печатаются все поля сертификата.
	\return ERR_OK, если печать успешно выполнена, и код ошибки
	в противном случае. 
*/
err_t cmdCVCPrint(
	const btok_cvc_t* cvc,			/*!< [in] сертификат */
	const char* scope				/*!< [in] область печати */
);

/*
*******************************************************************************
Коллекции и цепочки сертификатов

Коллекция сертификатов --- это конкатенация нескольких сертификатов.
Цепочка сертификатов -- это коллекция, в которой каждый следующий сертификат
выпущен владельцем предыдущего.
*******************************************************************************
*/

/*!	\brief Создание коллекции сертификатов

	По строке descr, в которой через пробел перечисляются имена файлов
	сертификатов, создается коллекция [?cert_len]certs.
	\return ERR_OK, если коллекция создана, и код ошибки в противном случае.
*/
err_t cmdCVCsCreate(
	octet* certs,					/*!< [out] коллекция сертификатов */
	size_t* count,					/*!< [out] длина коллекции (в октетах) */
	const char* descr				/*!< [in] описание коллекции */
);

/*!	\brief Число сертификатов в коллекции

	Определяется число count сертификатов в коллекции [certs_len]certs.
	\return ERR_OK, если число сертификатов определено, и код ошибки
	в противном случае.
*/
err_t cmdCVCsCount(
	size_t* count,					/*!< [out] число сертификатов */
	const octet* certs,				/*!< [in] коллекция сертификатов */
	size_t certs_len				/*!< [in] длина коллекции */
);

/*!	\brief Получение сертификата коллекции

	В коллекции [certs_len]certs определяется сертификат с номером num.
	Его смещение в коллекции возвращается по адресу offset, а длина --
	по адресу cert_len.
	\return ERR_OK, если сертификат получен, и код ошибки в противном случае.
	\remark Любой из адресов offset, cert_len может быть нулевым, и тогда
	данные по этому адресу не возвращаются.
*/
err_t cmdCVCsGet(
	size_t* offset,					/*!< [out] смещение сертификата */
	size_t* cert_len,				/*!< [out] длина сертификата */
	const octet* certs,				/*!< [in] коллекция сертификатов */
	size_t certs_len,				/*!< [in] длина коллекции */
	size_t num						/*!< [in] номер сертификата */
);

/*!	\brief Получение последнего сертификата коллекции

	В коллекции [certs_len]certs определяется последний сертификат.
	Его смещение в коллекции возвращается по адресу offset, а длина --
	по адресу cert_len.
	\return ERR_OK, если сертификат получен, и код ошибки в противном случае.
	\remark Любой из адресов offset, cert_len может быть нулевым, и тогда
	данные по этому адресу не возвращаются.
*/
err_t cmdCVCsGetLast(
	size_t* offset,					/*!< [out] смещение сертификата */
	size_t* cert_len,				/*!< [out] длина сертификата */
	const octet* certs,				/*!< [in] коллекция сертификатов */
	size_t certs_len				/*!< [in] длина коллекции */
);

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

	В коллекции [certs_len]certs ведется поиск сертификата [cert_len]cert.
	Его смещение в коллекции возвращается по адресу offset. 
	\return ERR_OK, если сертификат найден, и код ошибки в противном случае.
	\remark Адрес offset может быть нулевым, и тогда данные по этому адресу
	не возвращаются.
*/
err_t cmdCVCsFind(
	size_t* offset,					/*!< [out] смещение сертификата */
	const octet* certs,				/*!< [in] коллекция сертификатов */
	size_t certs_len,				/*!< [in] длина коллекции */
	const octet* cert,				/*!< [in] cертификат */
	size_t cert_len					/*!< [in] длина сертификата */
);

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

	Проверяется корректность сертификатов коллекции [certs_len]certs.
	\return ERR_OK, если проверка прошла успешно, и код ошибки
	в противном случае.
*/
err_t cmdCVCsCheck(
	const octet* certs,				/*!< [in] коллекция сертификатов */
	size_t certs_len				/*!< [in] длина коллекции */
);

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

	Проверяется корректность цепочки сертификатов [certs_len]certs
	на дату date.
	\return ERR_OK, если проверка прошла успешно, и код ошибки
	в противном случае.
	\remakr Дата date должна попадать в срок действия последнего сертификата
	цепочки.
	\remark Указатель date может быть нулевым или массив date может быть
	заполнен нулями, и тогда дата игнорируется.
*/
err_t cmdCVCsVal(
	const octet* certs,				/*!< [in] цепочка сертификатов */
	size_t certs_len,				/*!< [in] длина цепочки (в октетах) */
	const octet date[6]				/*!< [in] дата проверки */
);

/*!	\brief Печать коллекции сертификатов

	Печатается коллекция сертификатов [certs_len]certs.
	\return ERR_OK, если печать успешно выполнена, и код ошибки
	в противном случае.
*/
err_t cmdCVCsPrint(
	const octet* certs,				/*!< [in] коллекция сертификатов */
	size_t certs_len				/*!< [in] длина коллекции */
);

/*
*******************************************************************************
ЭЦП

ЭЦП может сопровождаться (обратной) цепочкой CV-сертификатов. Первым в цепочке
идет сертификат подписанта. Каждый следующий сертификат -- это сертификат
удостоверяющего центра, выпустившего предыдущий сертификат.

ЭЦП может сопровождаться датой выработки подписи. Дата задается 6 октетами
по схеме YYMMDD, которая используется в CV-сертификатах (см. btok.h).
Если все октеты нулевые, то значит дата не указана.

Цепочка сертификатов и дата подписываются вместе с содержимым целевого файла.
При этом смена сертификата подписанта в цепочке сделает подпись
недействительной, даже если открытый ключ будет повторен в двух сертификатах
(см. RFC5035: описание угроз, описание атрибута SigningCertificateV2).

Формат ЭЦП:
\code
Signature ::= SEQUENCE
{
   certs SEQUENCE OF CVCertificate,
   sig OCTET STRING(SIZE(48|72|96)),
   date CVDate OPTIONAL
}
CVDate ::= OCTET STRING(SIZE(6))
\endcode

Для управления форматом предусмотрена структура cmd_sig_t. В поле certs этой
структуры cmd_sig_t размещается цепочка сертификатов. Сертификаты цепочки
записываются последовательно друг за другом без разделителей. Сертификаты
закодированы по правилам АСН.1, их длины однозначно определяются в ходе
декодирования. Дата (date) и подпись (sig) также кодируются по правилам
АСН.1.

DER-код структуры Signature (строка октетов) переворачивается. За счет
переворота DER-код однозначно декодируется даже будучи записанным в конец
файла произвольного размера. В частности, подпись может быть присоединена
к подписывемому файлу. В этом случае при проверке подписи она исключается
из контролируемого содержимого файла.
*******************************************************************************
*/

/*!	\brief Подпись и сопровождающие сертификаты */
typedef struct {
	octet sig[96];			/*!< подпись */
	size_t sig_len;			/*!< длина подписи в октетах */
	octet certs[1460];		/*!< цепочка сертификатов */
	size_t certs_len;		/*!< cуммарная длина сертификатов */
	octet date[6];			/*!< дата выработки подписи */
} cmd_sig_t;

/*!	\brief Подпись файла

	Содержимое файла name подписывается на личном ключе [privkey_len]privkey
	с указанием date в качестве даты подписания. При передаче нулевого буфера
	date дата подписания не указывается.Подпись сохраняется в файле sig_name
	вместе с сертификатами цепочки certs. Цепочка certs может быть пустой.
	В качестве файла подписи можно указать подписываемый файл, и тогда подпись
	записывается в его конец. 
	\expect{ERR_BAD_KEYPAIR} Если цепочка certs непуста, то ее первый
	сертификат соответствует privkey.
	\expect{ERR_BAD_CERT} Если цепочка certs непуста, то каждый ее сертификат,
	начиная со второго, -- это сертификат удостоверяющего центра, выпустившего
	предыдущий сертификат.
	\expect{ERR_BAD_DATE} Если дата подписания date указана, то она корректна.
	\expect{ERR_OUTOFRANGE} Если дата подписания date указана, то все
	сертификаты цепочки certs действительны на эту дату.
	\return ERR_OK, если файл успешно подписан, и код ошибки в противном
	случае.
	\remark В списке certs указываются имена файлов сертификатов. Имена
	разделяются пробелами. Имена могут окаймляться кавычками.
	\remark Файлы name и sig_name могут совпадать, и тогда подпись
	присоединяется к подписываемому файлу.
*/
err_t cmdSigSign(
	const char* sig_name,		/*!< [in] имя файла подписи */
	const char* name,			/*!< [in] имя подписываемого файла */
	const char* certs,			/*!< [in] цепочка сертификатов */
	const octet date[6],		/*!< [in] дата подписания */
	const octet privkey[],		/*!< [in] личный ключ */
	size_t privkey_len			/*!< [in] длина личного ключа */
);

/*!	\brief Чтение подписи

	Из файла sig_name прочитывается подпись sig. При ненулевом sig_len по этому
	адресу возвращается длина DER-кода подписи.
	\return ERR_OK, если подпись успешно прочитана, и код ошибки в противном
	случае.
*/
err_t cmdSigRead(
	cmd_sig_t* sig,				/*!< [out] подпись */
	size_t* sig_len,			/*!< [out] длина DER-кода подписи */
	const char* sig_name		/*!< [in] имя файла подписи */
);

/*!	\brief Проверка подписи файла на открытом ключе

	Подпись содержимого файла name, размещенная в sig_name, проверяется
	на открытом ключе [pubkey_len]pubkey. В качестве файла подписи может
	указывать подписываемый файл, и тогда подпись прочитывается из его конца
	и исключается из содержимого файла при проверке.
	\expect{ERR_BAD_KEYPAIR} Если подпись сопровождается цепочкой сертификатов,
	то ее первый сертификат соответствует pubkey.
	\expect{ERR_BAD_CERT} Если подпись сопровождается цепочкой сертификатов,
	то каждый ее сертификат, начиная со второго, -- это сертификат
	удостоверяющего центра, выпустившего предыдущий сертификат.
	\expect{ERR_BAD_DATE} Если подпись сопровождается датой подписания,
	то эта дата корректна.
	\expect{ERR_OUTOFRANGE} Если подпись сопровождается датой подписания, то все
	сертификаты вложеннной цепочки действительны на эту дату.
	\expect{ERR_BAD_FORMAT} Если подпись размещается в отдельном файле, то этот
	файл содержит исключительно подпись.
	\return ERR_OK, если подпись корректна, и код ошибки в противном случае.
*/
err_t cmdSigVerify(
	const char* name,			/*!< [in] имя подписанного файла */
	const char* sig_name,		/*!< [in] имя файла подписи */
	const octet pubkey[],		/*!< [in] открытый ключ */
	size_t pubkey_len			/*!< [in] длина открытого ключа */
);

/*!	\brief Проверка подписи файла на доверенном сертификате

	Подпись содержимого файла name, размещенная в sig_name, проверяется
	на доверенном сертификате [anchor_len]anchor. Проверка будет завершена
	успешно, если подпись сопровождается цепочкой сертификатов и признается
	корректной на первом сертификате цепочки. В качестве файла подписи может
	указывать подписываемый файл, и тогда подпись прочитывается из его конца
	и исключается из содержимого файла при проверке.
	\expect{ERR_NO_TRUST} Один из сертификатов цепочки совпадает с anchor.
	\expect{ERR_BAD_CERT} Каждый сертификат цепочки, начиная со второго, --
	это сертификат удостоверяющего центра, выпустившего предыдущий сертификат.
	\expect{ERR_BAD_DATE} Если подпись сопровождается датой подписания,
	то эта дата корректна.
	\expect{ERR_OUTOFRANGE} Если подпись сопровождается датой подписания, то все
	сертификаты вложеннной цепочки действительны на эту дату.
	\expect{ERR_BAD_FORMAT} Если подпись размещается в отдельном файле, то этот
	файл содержит исключительно подпись.
	\return ERR_OK, если подпись корректна, и код ошибки в противном случае.
*/
err_t cmdSigVerify2(
	const char* name,			/*!< [in] имя подписанного файла */
	const char* sig_name,		/*!< [in] имя файла подписи */
	const octet anchor[],		/*!< [in] доверенный сертификат */
	size_t anchor_len			/*!< [in] длина anchor */
);

/*!	\brief Самопроверка подписи на открытом ключе

	Подпись исполняемого модуля, в котором вызывается данная функция,
	прочитывается из файла sig_name или, если sig_name == 0, из конца этого 
	же модуля и проверяется на открытом ключе [pubkey_len]pubkey через 
	обращение к cmdSigVerify(pubkey, pubkey_len).
	\return ERR_OK, если подпись корректна, и код ошибки в противном случае.
*/
err_t cmdSigSelfVerify(
	const octet pubkey[],		/*!< [in] открытый ключ */
	size_t pubkey_len,			/*!< [in] длина открытого ключа */
	const char* sig_name		/*!< [in] имя файла подписи */
);

/*!	\brief Самопроверка подписи на доверенном сертификате

	Подпись исполняемого модуля, в котором вызывается данная функция,
	прочитывается из файла sig_name или, если sig_name == 0, из конца этого 
	же модуля и проверяется на доверенном сертификате [anchor_len]anchor 
	через обращение к cmdSigVerify2(anchor, anchor_len).
	\return ERR_OK, если подпись корректна, и код ошибки в противном случае.
*/
err_t cmdSigSelfVerify2(
	const octet anchor[],		/*!< [in] доверенный сертификат */
	size_t anchor_len,			/*!< [in] длина anchor */
	const char* sig_name		/*!< [in] имя файла подписи */
);

/*!	\brief Извлечение объекта из подписи

	Из подписи, размещенной в файле sig_name, извлекается и сохраняется в файле
	obj_name объект, описанный строкой scope:
	- n-й сертификат, если scope == "cert<n>" (нумерация от нуля, сертификат
	  подписанта идет последним);
	- подписанное содержимое, если scope == "body";
	- собственно подпись, если scope == "sig".
	\return ERR_OK, если объект успешно извлечен, и код ошибки в противном
	случае.
*/
err_t cmdSigExtr(
	const char* obj_name,		/*!< [in] имя файла c объектом */
	const char* sig_name,		/*!< [in] имя файла подписи */
	const char* scope			/*!< [in] область */
);

/*!	\brief Печать подписи

	Печатается область подписи, размещенной в конце файла sig_name. Область
	задается строкой scope:
	- если scope != 0, то печатается поле с именем scope ("certnum", "date");
	- если scope == 0, то печатается вся подпись.
	\return ERR_OK, если печать успешно выполнена, и код ошибки в противном 
	случае. 
*/
err_t cmdSigPrint(
	const char* sig_name,		/*!< [in] имя файл подписи */
	const char* scope			/*!< [in] область печати */
);


/*
*******************************************************************************
Штамп

Штамп -- это контрольная сумма файла, которая вычисляется с помощью алгоритма
bash64. 

Формат штампа:
\code
Stamp ::= OCTET STRING(SIZE(8))
\endcode

DER-код штампа состоит из 10 октетов. Октеты DER-кода записываются в обратном
порядке либо в суффиксе целевого файла, либо в отдельном файле.
*******************************************************************************
*/

/*!	\brief Генерация штампа

	Вычисляется и сохраняется в файле stamp_name штамп файла name. Файлы 
	name и stamp_name могут сопадать и тогда штамп записывается в суффикс name.
	\return ERR_OK в случае успеха и код ошибки в противном случае.
*/
err_t cmdStampGen(
	const char* stamp_name,		/*!< [in] имя файла штампа */
	const char* name			/*!< [in] имя целевого файла */
);

/*!	\brief Проверка штампа

	Проверяется, что штамп файла name совпадает со штампом, записанным в 
	stamp_name. Файлы name и stamp_name могут сопадать и тогда эталонный штамп 
	читается из суффикса name и этот суффикс игнорируется при вычислении штампа 
	файла. 
	\return ERR_OK в случае успеха и код ошибки в противном случае.
*/
err_t cmdStampVal(
	const char* name,			/*!< [in] имя целевого файла */
	const char* stamp_name		/*!< [in] имя файла штампа */
);

/*!	\brief Самопроверка штампа

	Проверяется штамп исполняемого модуля, в котором вызывается данная функция.
	Штамп прочитывается из файла stamp_name или, если stamp_name == 0, из
	суффикса модуля или, если stamp_name == 0 и суффикс отсутствует или не 
	содержит штамп, из файла name.stamp, где name -- имя исполняемого модуля.
	\return ERR_OK в случае успеха и код ошибки в противном случае.
*/
err_t cmdStampSelfVal(
	const char* stamp_name		/*!< [in] имя файла штампа */
);

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

#endif /* __BEE2_CMD_H */
