aes-cmac.cpp
This example shows how to implement a client side "provider".There are three important parts to this:
- the class derived from QCA::Provider (in this example called "ClientSideProvider"), that generates the context class
- one or more context classes (in this example only one, implementing AES-CMAC, called "AESCMACContext")
- a call to QCA::insertProvider, to add the QCA::Provider subclass into QCA
#include <QtCrypto>
#include <QCoreApplication>
#include <QDebug>
class AESCMACContext : public QCA::MACContext
{
public:
AESCMACContext(QCA::Provider *p) : QCA::MACContext(p, "cmac(aes)")
{
}
~AESCMACContext()
{
}
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;
}
QCA::SecureArray xorArray(const QCA::SecureArray &array1,
const QCA::SecureArray &array2)
{
if (array1.size() != array2.size())
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)
{
if (key.size() == 0)
return;
m_key = key;
QCA::SecureArray const_Zero(16);
QCA::SecureArray const_Rb(16);
const_Rb[15] = (char)0x87;
m_X = const_Zero;
m_residual = QCA::SecureArray();
QCA::Cipher aesObj(QString("aes128"),
QCA::Cipher::ECB, QCA::Cipher::DefaultPadding,
QCA::Encode, key);
QCA::SecureArray L = aesObj.process(const_Zero);
if (0 == (L[0] & 0x80))
m_k1 = leftShift(L);
else
m_k1 = xorArray(leftShift(L), const_Rb);
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
{
return new AESCMACContext(*this);
}
void clear()
{
setup(m_key);
}
QCA::KeyLength keyLength() const
{
return QCA::KeyLength(16, 16, 1);
}
void update(const QCA::MemoryRegion &a)
{
QCA::SecureArray bytesToProcess = m_residual + a;
int blockNum;
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(QString("aes128"),
QCA::Cipher::ECB, QCA::Cipher::DefaultPadding,
QCA::Encode, m_key);
m_X = aesObj.process(m_Y);
}
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)
{
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(QString("aes128"),
QCA::Cipher::ECB, QCA::Cipher::DefaultPadding,
QCA::Encode, m_key);
*out = aesObj.process(m_Y);
}
protected:
QCA::SecureArray m_k1;
QCA::SecureArray m_k2;
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
{
return QCA_VERSION;
}
QString name() const
{
return "exampleClientSideProvider";
}
QStringList features() const
{
QStringList list;
list += "cmac(aes)";
return list;
}
Provider::Context *createContext(const QString &type)
{
if(type == "cmac(aes)")
return new AESCMACContext(this);
else
return 0;
}
};
class AES_CMAC: public QCA::MessageAuthenticationCode
{
public:
AES_CMAC(const QCA::SymmetricKey &key = QCA::SymmetricKey(),
const QString &provider = QString()):
QCA::MessageAuthenticationCode( "cmac(aes)", key, provider)
{}
};
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
qDebug() << "This example shows AES CMAC";
QCA::Initializer init;
qDebug() << "Completed initialisation";
if( ! QCA::isSupported("aes128-ecb") ) {
qDebug() << "AES not supported!";
}
if ( QCA::insertProvider(new ClientSideProvider, 0) )
qDebug() << "Inserted our provider";
else
qDebug() << "our provider could not be added";
if( ! QCA::isSupported("cmac(aes)") ) {
qDebug() << "AES CMAC not supported!";
} else {
AES_CMAC cmacObject;
QCA::SymmetricKey key(QCA::hexToArray("2b7e151628aed2a6abf7158809cf4f3c"));
cmacObject.setup(key);
QCA::SecureArray message = QCA::hexToArray("6bc1bee22e409f96e93d7e117393172a"
"ae2d8a571e03ac9c9eb76fac45af8e51"
"30c81c46a35ce411e5fbc1191a0a52ef"
"f69f2445df4f9b17ad2b417be66c3710");
QCA::SecureArray message1(message);
message1.resize(0);
qDebug();
qDebug() << "Message1: " << QCA::arrayToHex(message1.toByteArray());
qDebug() << "Expecting: bb1d6929e95937287fa37d129b756746";
qDebug() << "AES-CMAC: " << QCA::arrayToHex(cmacObject.process(message1).toByteArray());
cmacObject.clear();
QCA::SecureArray message2(message);
message2.resize(16);
qDebug();
qDebug() << "Message2: " << QCA::arrayToHex(message2.toByteArray());
qDebug() << "Expecting: 070a16b46b4d4144f79bdd9dd04a287c";
qDebug() << "AES-CMAC: " << QCA::arrayToHex(cmacObject.process(message2).toByteArray());
cmacObject.clear();
QCA::SecureArray message3(message);
message3.resize(40);
qDebug();
qDebug() << "Message3: " << QCA::arrayToHex(message3.toByteArray());
qDebug() << "Expecting: dfa66747de9ae63030ca32611497c827";
qDebug() << "AES-CMAC " << QCA::arrayToHex(cmacObject.process(message3).toByteArray());
cmacObject.clear();
QCA::SecureArray message4(message);
message4.resize(64);
qDebug();
qDebug() << "Message4: " << QCA::arrayToHex(message4.toByteArray());
qDebug() << "Expecting: 51f0bebf7e3b9d92fc49741779363cfe";
qDebug() << "AES-CMAC: " << QCA::arrayToHex(cmacObject.process(message4).toByteArray());
}
return 0;
}