Документация
ОС Аврора 5.1.1

Архитектура

Примечание.

Не нужно разбираться в материале этой статьи, чтобы использовать QCA — это задокументировано для тех, кому интересно, и для всех, кто планирует расширить или изменить QCA.

Архитектура QCA основана на шаблоне проектирования «Мост». Цель шаблона «Мост» — «отделить абстракцию от её реализации, чтобы они могли изменяться независимо». [Gamma et.al, pg 151].

Чтобы понять, как это разделение работает в случае QCA, проще всего взглянуть на пример — криптографическое хеширование. API довольно прост (хотя из него исключены некоторые части, которые не требуются для этого примера):

class QCA_EXPORT Hash : public Algorithm, public BufferedComputation
{
public:
    Hash(const QString &type, const QString &provider);
    virtual void clear();
    virtual void update(const QCA::SecureArray &a);
    virtual QCA::SecureArray final();
}

Реализация класса Hash почти так же проста:

Hash::Hash(const QString &type, const QString &provider)
:Algorithm(type, provider)
{
}
 
void Hash::clear()
{
        static_cast<HashContext *>(context())->clear();
}
 
void Hash::update(const QCA::SecureArray &a)
{
        static_cast<HashContext *>(context())->update(a);
}
 
QCA::SecureArray Hash::final()
{
        return static_cast<HashContext *>(context())->final();
}

Причина, по которой код выглядит так просто, заключается в том, что различные методы в Hash просто вызывают эквивалентные подпрограммы в объекте context(). Контекст исходит из вызова getContext(), который выполняется как часть конструктора Algorithm. Этот вызов getContext() заставляет QCA работать со списком криптопровайдеров (обычно плагинов), о которых он знает, в поисках криптопровайдера, который может создать правильный тип контекста (в данном случае HashContext).

Код для HashContext не нужно связывать с QCA — он может варьироваться в реализации, в том числе изменяться во время выполнения. Приложению не нужно знать, как реализован HashContext, потому что ему просто нужно иметь дело с интерфейсом класса Hash. Фактически, HashContext не может быть реализован, поэтому приложение должно выполнить проверку (с помощью QCA::isSupported()), прежде чем пытаться использовать функциональность, реализованную с помощью плагинов.

Код для одной реализации (в данном случае вызова OpenSSL) приведён ниже.

class opensslHashContext : public HashContext
{
public:
    opensslHashContext(const EVP_MD *algorithm, Provider *p, const QString &type) : HashContext(p, type)
    {
        m_algorithm = algorithm;
        EVP_DigestInit( &m_context, m_algorithm );
    };
 
    ~opensslHashContext()
    {
        EVP_MD_CTX_cleanup(&m_context);
    }
 
    void clear()
    {
        EVP_MD_CTX_cleanup(&m_context);
        EVP_DigestInit( &m_context, m_algorithm );
    }
 
    void update(const QCA::SecureArray &a)
    {
        EVP_DigestUpdate( &m_context, (unsigned char*)a.data(), a.size() );
    }
 
    QCA::SecureArray final()
    {
        QCA::SecureArray a( EVP_MD_size( m_algorithm ) );
        EVP_DigestFinal( &m_context, (unsigned char*)a.data(), 0 );
        return a;
    }
 
    Provider::Context *clone() const
    {
        return new opensslHashContext(*this);
    }
 
protected:
    const EVP_MD *m_algorithm;
    EVP_MD_CTX m_context;
};

Этот подход (с использованием шаблона «Адаптер») очень распространён в бэкэндах QCA, потому что плагины часто основаны на существующих библиотеках.

В дополнение к различным объектам Context каждый криптопровайдер также имеет параметризованный класс Factory с методом createContext(), как показано ниже:

Context *createContext(const QString &type)
{
        //OpenSSL_add_all_digests();
        if ( type == "sha1" )
                return new opensslHashContext( EVP_sha1(), this, type);
        else if ( type == "sha0" )
                return new opensslHashContext( EVP_sha(), this, type);
        else if ( type == "md5" )
                return new opensslHashContext( EVP_md5(), this, type);
        else if ( type == "aes128-cfb" )
                return new opensslCipherContext( EVP_aes_128_cfb(), 0, this, type);
        else if ( type == "aes128-cbc" )
                return new opensslCipherContext( EVP_aes_128_cbc(), 0, this, type);
        else
                return 0;
}

В результате QCA может попросить криптопровайдера предоставить соответствующий объект Context, не беспокоясь о том, как он реализован.

Для функциональности, которая реализована с помощью различных алгоритмов (например, HashContext может поддерживать широкий спектр алгоритмов — MD5, SHA0 и SHA1 в приведённом выше примере; и CipherContext и MACContext также могут это делать), необходимо разрешить приложениям определить, какие алгоритмы поддерживаются. то обрабатывается через класс InfoContext. Типичный пример показан ниже:

class opensslInfoContext : public InfoContext
{
        Q_OBJECT
public:
        opensslInfoContext(Provider *p) : InfoContext(p)
        {
        }
 
        Context *clone() const
        {
                return new opensslInfoContext(*this);
        }
 
        QStringList supportedHashTypes() const
        {
                QStringList list;
                list += "sha1";
                list += "sha0";
                list += "md5";
                return list;
        }
 
        // Здесь можно указать типы MAC и Cipher
};

Следует обратить внимание, что InfoContext сам по себе предосталяет функциональность, поэтому необходимо добавить его в метод createContext() для криптопровайдера, как показано ниже:

Context *createContext(const QString &type)
{
        if ( type == "sha1" )
                return new opensslHashContext( EVP_sha1(), this, type);
        else if ( type == "sha0" )
                return new opensslHashContext( EVP_sha(), this, type);
        else if ( type == "md5" )
                return new opensslHashContext( EVP_md5(), this, type);
        else if ( type == "info" )
                return new opensslInfoContext( this );
        else
                return 0;
}

Мы используем cookies для персонализации сайта и его более удобного использования. Вы можете запретить cookies в настройках браузера.

Пожалуйста ознакомьтесь с политикой использования cookies.