/*
 * Copyright(C) 2003 Проект ИОК
 *
 * Этот файл содержит информацию, являющуюся
 * собственностью компании Крипто Про.
 *
 * Любая часть этого файла не может быть скопирована,
 * исправлена, переведена на другие языки,
 * локализована или модифицирована любым способом,
 * откомпилирована, передана по сети с или на
 * любую компьютерную систему без предварительного
 * заключения соглашения с компанией Крипто Про.
 */

/*!
 * \file $RCSfile$
 * \version $Revision: 240219 $
 * \date $Date:: 2022-04-05 17:54:08 +0300#$
 * \author $Author: ivan $
 *
 * \brief Интерфейсный класс для построения и проверки цепочек.
 */

#ifndef __CHAIN_UTIL_H
#define __CHAIN_UTIL_H

#include <set>
#include <string>

class CertChainBuildFlags
{
public:
    // Проверка в CRL только одного оконечного сертификата в цепочке
    static const unsigned REVOCATION_CHECK_END_CERT;
    // Проверка в CRL всех сертификатов цепочки, включая сертификаты,
    // участвующие при проверке CRL
    static const unsigned REVOCATION_CHECK_CHAIN;
    // Проверка в CRL всех не Root сертификатов цепочки, включая сертификаты
    // участвующие при проверке CRL. Root сертификаты участвующие при проверке
    // CRL на отозванность не проверяются.
    static const unsigned REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT;
    static const unsigned REVOCATION_CHECK_MASK;
    // Запрет отфильтровывания сертификатов на первом проходе по 
    // вилидности времени и по корректности подписи
    static const unsigned DISABLE_PASS1_QUALITY_FILTERING;
    // Не качать CRL из сети, использовать только локальный кэш
    static const unsigned REVOCATION_CHECK_CACHE_ONLY;
    // Накапливать timeout при скачивании CRL
    static const unsigned REVOCATION_ACCUMULATIVE_TIMEOUT;
    // Использовать при скачивании сертификатов для построения
    // цепочки только локальный кэш
    static const unsigned CACHE_ONLY_URL_RETRIEVAL;
    unsigned flags;
};

class TrustStatus
{
public:
    // Конструкторы, деструкторы, операторы присваивания
    explicit TrustStatus() : value( 0 ) {}
    TrustStatus( const TrustStatus& src ) : value( src.value ) {}
    TrustStatus( unsigned src ) : value( src ) {}

    // Получение явного значения
    unsigned get_unsigned() const { return value; }

    // Получение CryptoAPI-варианта значения
    DWORD toCERT_TRUST_STATUS() const { return value; }
    DWORD toRevocationResult() const;

    // Сравнение
    int compare( const TrustStatus &right ) const;

    // Пробразования к строкам
    std::string toString() const;
    std::wstring toWString() const;
    static TrustStatus fromString( const char *str );

    // Биты
    static const unsigned CT_NO_ERROR;
    static const unsigned IS_NOT_TIME_VALID;
    static const unsigned IS_REVOKED;
    static const unsigned IS_NOT_SIGNATURE_VALID;
    static const unsigned IS_NOT_VALID_FOR_USAGE;
    static const unsigned IS_UNTRUSTED_ROOT;
    static const unsigned INVALID_BASIC_CONSTRAINTS;
    static const unsigned IS_PARTIAL_CHAIN;
    static const unsigned REVOCATION_STATUS_UNKNOWN;

    // массивы преобразования к строкам
    static const unsigned VALUES[];
    static const char *STRINGS[];
#ifdef UNIX
    static const char *WSTRINGS[];
#else /* UNIX */
    static const wchar_t *WSTRINGS[];
#endif /* UNIX */
    static const size_t VALUE_LENGTH;

private:
    int compare( const TrustStatus &src, unsigned flag ) const;
    unsigned value;
};

// Операторы сравнения статусов
inline bool operator== ( const TrustStatus &left, const TrustStatus &right )
{ return left.compare( right ) == 0; }

inline bool operator!= ( const TrustStatus &left, const TrustStatus &right )
{ return left.compare( right ) != 0; }

// проверка на то, что левый статус лучше правого
inline bool operator> ( const TrustStatus &left, const TrustStatus &right )
{ return left.compare( right ) > 0; }

// Закрытая реализация алгоритма
class CertChainBuilder;

// Класс построения и проверки цепочки
// Начальное состояние:
// - Дата проверки: текущая
// - CRL store: CA, Root
// - Store: CA
// - Root store: Root
// - Списки доп. crl сертификатов, Trust: пустые
// - Policy: POLICY_ANY
// - Флаги проверки: REVOCATION_CHECK_CHAIN
class CertCC // CertChainContext
{
public:
    // Конструкторы, деструкторы, присваивания
    CertCC( bool add_root = true, bool extra_crl_stores = true,
	bool extra_cert_stores = true );
    ~CertCC();

    // начальные установки
    void set_usage( PCERT_USAGE_MATCH RequestedUsage );
    void set_date( const FILETIME& date );

    void set_flags( unsigned f );
    void set_blacklist_store( HCERTSTORE hStore );
    void set_trusted_store( HCERTSTORE hStore );
    void set_timeout( unsigned long time_value);

    void add_stores( const wchar_t *store, bool issystem = false );
    void add_stores( const store_handle& handle );
    void add_crl_stores( const wchar_t *store, bool issystem = false );
    void add_crl_stores( const store_handle& handle );

    // Функции очистки начальных установок
    void clear_stores();
    void clear_crl_stores();

    // получение начальных установок

    unsigned get_flags( void ) const;
    bool is_blacklisted( PCCERT_CONTEXT ctx ) const;

    // функции проверки
    bool verify_certificate(PCCERT_CONTEXT p);

    // функции получения результата
    TrustStatus get_status() const;

    BOOL toCERT_CHAIN_CONTEXT(PCERT_CHAIN_CONTEXT*);

protected:
    CertChainBuilder *builder;

private:
    // Присваивание и конструктор копирования запрещены
    CertCC &operator=(const CertCC &);
    CertCC(const CertCC&);
};

// 2022-apr-01 dim: выделена из CertCC::toCERT_CHAIN_CONTEXT() для  CertGetCertificateChain()
// -- выделяет инициализированную память
// -- заполняет ref_counter, cbSize
static inline PCERT_CHAIN_CONTEXT newChainCtx()
{
    PCERT_CHAIN_CONTEXT p = (PCERT_CHAIN_CONTEXT) ::calloc(sizeof (CERT_CHAIN_CONTEXT) + sizeof(DWORD), 1); // hack for ref_counter
    if (!p) {
	::SetLastError(ERROR_NOT_ENOUGH_MEMORY);
	return NULL;
    }
    // не знаем размер CERT_CHAIN_CONTEXT и выравнивание ref_counter
    const DWORD ref_counter = 1;
    ::memcpy(p+1, &ref_counter, sizeof(DWORD));
    p->cbSize = sizeof (CERT_CHAIN_CONTEXT);
    // остальные поля не заполняем
    return p;
}
#endif // __CHAIN_UTIL_H
