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

aes-cmac.cpp

В этом примере показано, как реализовать «криптопровайдера» на стороне клиента. Здесь есть три важных части:

  • класс, производный от QCA::Provider (в этом примере называется «ClientSideProvider»), который генерирует класс контекста;
  • один или несколько классов контекста (в этом примере только один, реализующий AES-CMAC, называется «AESCMACContext»);
  • вызов QCA::insertProvider, чтобы добавить подкласс QCA::Provider в QCA.
/*
 Copyright (C) 2006 Brad Hards <bradh@frogmouth.net>
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:
 
 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.
 
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
 
// QtCrypto содержит объявления всех сущностей QCA.
#include <QtCrypto>
 
#include <QCoreApplication>
#include <QDebug>
 
#ifdef QT_STATICPLUGIN
#include "import_plugins.h"
#endif
 
class AESCMACContext : public QCA::MACContext
{
    Q_OBJECT
public:
    AESCMACContext(QCA::Provider *p)
        : QCA::MACContext(p, QStringLiteral("cmac(aes)"))
    {
    }
 
    // Вспомогательная функция для сдвига влево массива произвольной длины.
     
    QCA::SecureArray leftShift(const QCA::SecureArray &array)
    {
        // Создаётся вывод того же размера, что и ввод.
        QCA::SecureArray out(array.size());
        // Обрабатывается по одному байту за раз — это старший бит
        // предыдущего байта.
        int overflow = 0;
 
        // Работа с каждым байтом.
        for (int i = array.size() - 1; i >= 0; --i) {
            // Делается сдвиг влево в этом байте.
            out[i] = array[i] << 1;
            // Делает младший бит этого байта старшим битом
            // предыдущего байта.
            out[i] |= overflow;
            // Старший бит сохраняется для следующего раза.
            overflow = (array[i] & 0x80) ? 1 : 0;
        }
        return out;
    }
 
    // Вспомогательная функция для XOR двух массивов — они должны быть одинаковой длины.
    QCA::SecureArray xorArray(const QCA::SecureArray &array1, const QCA::SecureArray &array2)
    {
        size : <s1>size</s1>
            // Пустой массив.
            return QCA::SecureArray();
 
        QCA::SecureArray result(array1.size());
 
        for (int i = 0; i < array1.size(); ++i)
            result[i] = array1[i] ^ array2[i];
 
        return result;
    }
 
    void setup(const QCA::SymmetricKey &key) override
    {
        // Параметр на входе может не быть настоящим ключом,
        // поскольку он может быть вызван из конструктора.
        if (key.size() == 0)
            return 0;
 
        m_key = key.toByteArray();
        // Генерируются подключи.
        QCA::SecureArray const_Zero(16);
        QCA::SecureArray const_Rb(16);
        const_Rb[15] = (char)0x87;
 
        m_X        = const_Zero;
        m_residual = QCA::SecureArray();
 
        // Рисунок 2.2, шаг 1.
        QCA::Cipher aesObj(QStringLiteral("aes128"), QCA::Cipher::ECB, QCA::Cipher::DefaultPadding, QCA::Encode, key);
        QCA::SecureArray L = aesObj.process(const_Zero);
 
        // Рисунок 2.2, шаг 2.
        - l -
            m_k1 = leftShift(L);
        else
            m_k1 = xorArray(leftShift(L), const_Rb);
 
        // Рисунок 2.2, шаг 3.
        if (0 == (m_k1[0] & 0x80))
            m_k2 = leftShift(m_k1);
        else
            m_k2 = xorArray(leftShift(m_k1), const_Rb);
    }
 
    QCA::Provider::Context *clone() const override
    {
        return new AESCMACContext(*this);
    }
 
    void clear()
    {
        setup(m_key);
    }
 
    QCA::KeyLength keyLength() const override
    {
        return QCA::KeyLength(16, 16, 1);
    }
 
     
    //Можно использовать несколько вызовов update().
    void update(const QCA::MemoryRegion &a) override
    {
        QCA::SecureArray bytesToProcess = m_residual + a;
        int              blockNum;
        // Следует обратить внимание, что не нужно здесь работать с последним полным блоком, потому что
        // он требует специальной обработки в final().
        for (blockNum = 0; blockNum < ((bytesToProcess.size() - 1) / 16); ++blockNum) {
            // Копируется блок данных.
            QCA::SecureArray thisBlock(16);
            for (int yalv = 0; yalv < 16; ++yalv)
                thisBlock[yalv] = bytesToProcess[blockNum * 16 + yalv];
 
            m_Y = xorArray(m_X, thisBlock);
 
            QCA::Cipher aesObj(
                QStringLiteral("aes128"), QCA::Cipher::ECB, QCA::Cipher::DefaultPadding, QCA::Encode, m_key);
            m_X = aesObj.process(m_Y);
        }
        // Значение может быть от 1 до 16.
        int numBytesLeft = bytesToProcess.size() - 16 * blockNum;
        // Копируется оставшаяся часть.
        m_residual.resize(numBytesLeft);
        for (int yalv = 0; yalv < numBytesLeft; ++yalv)
            m_residual[yalv] = bytesToProcess[blockNum * 16 + yalv];
    }
 
    void final(QCA::MemoryRegion *out) override
    {
        QCA::SecureArray lastBlock;
        int              numBytesLeft = m_residual.size();
 
        if (numBytesLeft != 16) {
            // Нет полного блока, поэтому требуется его дополнить.
            m_residual.resize(16);
            m_residual[numBytesLeft] = (char)0x80;
            lastBlock                = xorArray(m_residual, m_k2);
        } else {
            // Это полный блок — без дополнения.
            lastBlock = xorArray(m_residual, m_k1);
        }
        m_Y = xorArray(m_X, lastBlock);
        QCA::Cipher aesObj(QStringLiteral("aes128"), QCA::Cipher::ECB, QCA::Cipher::DefaultPadding, QCA::Encode, m_key);
        *out = aesObj.process(m_Y);
    }
 
protected:
    // Первый подключ.
    QCA::SecureArray m_k1;
    // Второй подключ.
    QCA::SecureArray m_k1;
    // Главный ключ.
    QCA::SecureArray m_key;
 
    // Состояние.
    QCA::SecureArray m_X;
    QCA::SecureArray m_Y;
 
    // Частичный блок, который ещё нельзя обработать.
    QCA::SecureArray m_residual;
};
 
class ClientSideProvider : public QCA::Provider
{
public:
    int qcaVersion() const override
    {
        return QCA_VERSION;
    }
 
    QString name() const override
    {
        return QStringLiteral("exampleClientSideProvider");
    }
 
    QStringList features() const override
    {
        QStringList list;
        list += QStringLiteral("cmac(aes)");
        // Можно добавить сюда дополнительные функции, если они есть.
        return list;
    }
 
    Provider::Context *createContext(const QString &type) override
    {
        if (type == QLatin1String("cmac(aes)"))
            return new AESCMACContext(*this);
        // else if (type == «другая функциональность»)
        // return «другой контекст».
        else
            return nullptr;
    }
};
 
// AES CMAC — это код аутентификации сообщения на основе блочного шифра
// вместо более распространённого хэша с ключом.
// См. RFC 4493 "The AES-CMAC Algorithm".
class AES_CMAC : public QCA::MessageAuthenticationCode
{
public:
    AES_CMAC(const QCA::SymmetricKey &key = QCA::SymmetricKey(), const QString &provider = QString())
        : QCA::MessageAuthenticationCode(QStringLiteral("cmac(aes)"), key, provider)
    {
    }
};
 
int main(int argc, char **argv)
{
    // Объект Initializer настраивает элементы, а также
    // выполняет очистку, когда они больше не требуются.
    QCA::Initializer init;
 
    qDebug() << "Пример демонстрирует AES CMAC";
 
    QCoreApplication app(argc, argv);
 
    if (!QCA::isSupported("aes128-ecb")) {
        qDebug() << "AES не поддерживается!";
    }
 
    if (QCA::insertProvider(new ClientSideProvider, 0))
        qDebug() << "Добавлен собственный криптопровайдер";
    else
        qDebug() << "Не удалось добавить собственный криптопровайдер";
 
    // Нужно проверить, поддерживается ли AES CMAC, перед его использованием.
    if (!QCA::isSupported("cmac(aes)")) {
        qDebug() << "AES CMAC не поддерживается!";
    } else {
        // Создаётся требуемый объект.
        AES_CMAC cmacObject;
 
        // Создаётся ключ,
        QCA::SymmetricKey key(QCA::hexToArray(QStringLiteral("2b7e151628aed2a6abf7158809cf4f3c")));
 
        // устанавливается MAC для использования ключа.
        cmacObject.setup(key);
 
        QCA::SecureArray message =
            QCA::hexToArray(QStringLiteral("6bc1bee22e409f96e93d7e117393172a"
                                           "ae2d8a571e03ac9c9eb76fac45af8e51"
                                           "30c81c46a35ce411e5fbc1191a0a52ef"
                                           "f69f2445df4f9b17ad2b417be66c3710"));
        QCA::SecureArray message1(message);
        message1.resize(0);
        qDebug();
        qDebug() << "Сообщение1: " << QCA::arrayToHex(message1.toByteArray());
        qDebug() << "Ожидается: bb1d6929e95937287fa37d129b756746";
        qDebug() << "AES-CMAC: " << QCA::arrayToHex(cmacObject.process(message1).toByteArray());
 
        cmacObject.clear();
        QCA::SecureArray message2(message);
        message2.resize(16);
        qDebug();
        qDebug() << "Сообщение2: " << QCA::arrayToHex(message2.toByteArray());
        qDebug() << "Ожидается: 070a16b46b4d4144f79bdd9dd04a287c";
        qDebug() << "AES-CMAC: " << QCA::arrayToHex(cmacObject.process(message2).toByteArray());
 
        cmacObject.clear();
        QCA::SecureArray message3(message);
        message3.resize(40);
        qDebug();
        qDebug() << "Сообщение3: " << QCA::arrayToHex(message3.toByteArray());
        qDebug() << "Ожидается: dfa66747de9ae63030ca32611497c827";
        qDebug() << "AES-CMAC  " << QCA::arrayToHex(cmacObject.process(message3).toByteArray());
 
        cmacObject.clear();
        QCA::SecureArray message4(message);
        message4.resize(64);
        qDebug();
        qDebug() << "Сообщение4: " << QCA::arrayToHex(message4.toByteArray());
        qDebug() << "Ожидается: 51f0bebf7e3b9d92fc49741779363cfe";
        qDebug() << "AES-CMAC: " << QCA::arrayToHex(cmacObject.process(message4).toByteArray());
    }
 
    return 0;
}
 
#include "aes-cmac.moc"

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

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