AusweisApp2/test/qt/card/test_SecureMessaging.cpp

301 lines
11 KiB
C++

/*!
* \copyright Copyright (c) 2014-2019 Governikus GmbH & Co. KG, Germany
*/
#include <QtCore>
#include <QtTest>
#include "asn1/KnownOIDs.h"
#include "pace/SecureMessaging.h"
using namespace governikus;
/*
* Test data generated with ocard
*/
class ByteArrayUtil
{
public:
template<typename T> static QByteArray fromValue(T pValue)
{
return QByteArray::fromHex(QByteArray::number(pValue, 16));
}
};
class test_SecureMessaging
: public QObject
{
Q_OBJECT
private:
QScopedPointer<SecureMessaging> mSecureMessaging;
QScopedPointer<CipherMac> mCipherMac;
QScopedPointer<SymmetricCipher> mCipher;
QByteArray concat(std::initializer_list<QByteArray> pList) const
{
QByteArray result;
for (const auto& elem : pList)
{
result += elem;
}
return result;
}
QVector<QByteArray> encryptResponse(const QByteArray& pBuffer, quint32& pSsc) const
{
QVector<QByteArray> result(4);
++pSsc;
QByteArray ssc = QByteArray(mCipher->getBlockSize() - QByteArray::number(pSsc, 16).size(), 0x00).append(ByteArrayUtil::fromValue(pSsc));
if (pBuffer.length() > 2)
{
QByteArray pData = pBuffer.left(pBuffer.length() - 2);
mCipher->setIv(QByteArray().fill(0x00, mCipher->getBlockSize()));
auto iv = mCipher->encrypt(ssc);
mCipher->setIv(iv);
int padLen = mCipher->getBlockSize() - (pData.size() % mCipher->getBlockSize());
pData = concat({pData, QByteArray::fromHex("80"), QByteArray(padLen - 1, 0x00)});
QByteArray encryptedData = mCipher->encrypt(pData).prepend(0x01);
result[0] = QByteArray().append(char(0x87)).append(ByteArrayUtil::fromValue(encryptedData.size())).append(encryptedData);
}
QByteArray pSw = pBuffer.right(2);
result[1] = QByteArray().append(char(0x99)).append(ByteArrayUtil::fromValue(pSw.size())).append(pSw);
{
QByteArray dataToMac = concat({ssc, result[0], result[1]});
int padLen = mCipher->getBlockSize() - (dataToMac.size() % mCipher->getBlockSize());
dataToMac.append(char(0x80)).append(QByteArray(padLen - 1, 0x00));
QByteArray mac = mCipherMac->generate(dataToMac);
result[2] = QByteArray().append(char(0x8E)).append(ByteArrayUtil::fromValue(mac.size())).append(mac);
}
result[3] = pSw;
return result;
}
private Q_SLOTS:
void init()
{
auto paceAlgo = toByteArray(KnownOIDs::id_PACE::ECDH::GM_AES_CBC_CMAC_128);
QByteArray encKey("F1234567890ABCDE");
QByteArray macKey("1234567890ABCDEF");
mSecureMessaging.reset(new SecureMessaging(paceAlgo, encKey, macKey));
mCipherMac.reset(new CipherMac(paceAlgo, macKey));
mCipher.reset(new SymmetricCipher(paceAlgo, encKey));
}
void testNotInitialized()
{
const QByteArray empty;
QByteArray data;
const CommandApdu command(static_cast<char>(0x10), static_cast<char>(0x10), static_cast<char>(0x20), static_cast<char>(0x30), data, CommandApdu::NO_LE);
SecureMessaging n(empty, empty, empty);
CommandApdu commandApdu = n.encrypt(command);
QCOMPARE(commandApdu.getBuffer(), QByteArray());
}
void testCommandChainingCLA()
{
QByteArray data;
CommandApdu command(static_cast<char>(0x10), static_cast<char>(0x10), static_cast<char>(0x20), static_cast<char>(0x30), data, CommandApdu::NO_LE);
QCOMPARE(command.getBuffer(), QByteArray::fromHex("10102030"));
CommandApdu securedCommand = mSecureMessaging->encrypt(command);
QCOMPARE(securedCommand.getBuffer(), QByteArray::fromHex("1c1020300a8e0852d8f5136eddaab800"));
}
void testNoDataNoLe()
{
QByteArray data;
CommandApdu command(static_cast<char>(0x00), static_cast<char>(0x10), static_cast<char>(0x20), static_cast<char>(0x30), data, CommandApdu::NO_LE);
QCOMPARE(command.getBuffer(), QByteArray::fromHex("00102030"));
CommandApdu securedCommand = mSecureMessaging->encrypt(command);
QCOMPARE(securedCommand.getBuffer(), QByteArray::fromHex("0c1020300a8e08fd824409ae18c2f500"));
}
void testNoDataShortLe()
{
QByteArray data;
CommandApdu command(static_cast<char>(0x00), static_cast<char>(0x10), static_cast<char>(0x20), static_cast<char>(0x30), data, 8);
QCOMPARE(command.getBuffer(), QByteArray::fromHex("0010203008"));
CommandApdu securedCommand = mSecureMessaging->encrypt(command);
QCOMPARE(securedCommand.getBuffer(), QByteArray::fromHex("0c1020300d9701088e080639e0cc69d18d3900"));
}
void testNoDataShortMaxLe()
{
QByteArray data;
CommandApdu command(static_cast<char>(0x00), static_cast<char>(0x10), static_cast<char>(0x20), static_cast<char>(0x30), data, CommandApdu::SHORT_MAX_LE);
QCOMPARE(command.getBuffer(), QByteArray::fromHex("0010203000"));
CommandApdu securedCommand = mSecureMessaging->encrypt(command);
QCOMPARE(securedCommand.getBuffer(), QByteArray::fromHex("0c1020300d9701008e08a361948e0c90464b00"));
}
void testNoDataExtendedMaxLe()
{
QByteArray data;
CommandApdu command(static_cast<char>(0x00), static_cast<char>(0x10), static_cast<char>(0x20), static_cast<char>(0x30), data, CommandApdu::EXTENDED_MAX_LE);
QCOMPARE(command.getBuffer(), QByteArray::fromHex("00102030000000"));
CommandApdu securedCommand = mSecureMessaging->encrypt(command);
QCOMPARE(securedCommand.getBuffer(), QByteArray::fromHex("0c10203000000e970200008e088b5e9fdb6cc8d9c70000"));
}
void testDataShortMaxLe()
{
QByteArray data = QByteArray::fromHex("D0D1D2D3");
CommandApdu command(static_cast<char>(0x00), static_cast<char>(0x10), static_cast<char>(0x20), static_cast<char>(0x30), data, CommandApdu::SHORT_MAX_LE);
QCOMPARE(command.getBuffer(), QByteArray::fromHex("0010203004d0d1d2d300"));
CommandApdu securedCommand = mSecureMessaging->encrypt(command);
QCOMPARE(securedCommand.getBuffer(),
QByteArray::fromHex("0c10203020871101cc30c03dbe01213486b74ce1f48f5c499701008e085f5f415d7bcdb09200"));
}
void testDataExtendedMaxLe()
{
QByteArray data = QByteArray::fromHex("D0D1D2D3");
CommandApdu command(static_cast<char>(0x00), static_cast<char>(0x10), static_cast<char>(0x20), static_cast<char>(0x30), data, CommandApdu::EXTENDED_MAX_LE);
QCOMPARE(command.getBuffer(), QByteArray::fromHex("00102030000004d0d1d2d30000"));
CommandApdu securedCommand = mSecureMessaging->encrypt(command);
QCOMPARE(securedCommand.getBuffer(),
QByteArray::fromHex("0c102030000021871101cc30c03dbe01213486b74ce1f48f5c49970200008e08e694d258bc80a23f0000"));
}
void testSendSequenceCounter()
{
QByteArray data = QByteArray::fromHex("D0D1D2D3");
CommandApdu command(static_cast<char>(0x00), static_cast<char>(0x10), static_cast<char>(0x20), static_cast<char>(0x30), data, CommandApdu::EXTENDED_MAX_LE);
QCOMPARE(command.getBuffer(), QByteArray::fromHex("00102030000004d0d1d2d30000"));
CommandApdu securedCommand1 = mSecureMessaging->encrypt(command);
QCOMPARE(securedCommand1.getBuffer(),
QByteArray::fromHex("0c102030000021871101cc30c03dbe01213486b74ce1f48f5c49970200008e08e694d258bc80a23f0000"));
CommandApdu securedCommand2 = mSecureMessaging->encrypt(command);
QCOMPARE(securedCommand2.getBuffer(),
QByteArray::fromHex("0c10203000002187110140eade1a656a226767536a7cec2ac6f9970200008e08e3bcfba8b3fde2d40000"));
CommandApdu securedCommand3 = mSecureMessaging->encrypt(command);
QCOMPARE(securedCommand3.getBuffer(),
QByteArray::fromHex("0c1020300000218711010bdf2b6181a9091e6fe0147d8b11069d970200008e08e794e018bd7414b80000"));
CommandApdu securedCommand4 = mSecureMessaging->encrypt(command);
QCOMPARE(securedCommand4.getBuffer(),
QByteArray::fromHex("0c102030000021871101252538e3ebad320fcd01905459f6b6e0970200008e08e5feec033d432ed00000"));
}
void testLastIndexOf()
{
// Check if lastIndexOf will return the correct result
// https://bugreports.qt.io/browse/QTBUG-44223
const char ISO_LEADING_PAD_BYTE = char(0x80);
QByteArray data = QByteArray::fromHex("109205a006092b864886faad010702a08255913082058d020103310f300d0609608648016503040201050030820148060804007f0007030201a082013a0482013631820132300d060804007f00070202020201023012060a04007f000702020302020201020201413012060a04007f0007020204020202010202010d3017060a04007f0007020205020330090201010201430101ff3017060a04007f0007020205020330090201010201440101003019060904007f000702020502300c060704007f0007010202010d301c060904007f000702020302300c060704007f0007010202010d020141302a060804007f0007020206161e687474703a2f2f62736980");
QCOMPARE(data.lastIndexOf(ISO_LEADING_PAD_BYTE), 255);
}
void testDecrypt_onlyStatusWord()
{
QByteArray plainBuffer = QByteArray::fromHex("9000");
quint32 ssc = 0;
auto result = encryptResponse(plainBuffer, ssc);
ResponseApdu encryptedResponse(concat({result[0], result[1], result[2], result[3]}));
ResponseApdu decryptedResponse = mSecureMessaging->decrypt(encryptedResponse);
QCOMPARE(decryptedResponse.getBuffer(), plainBuffer);
}
void testDecrypt()
{
QByteArray plainBuffer = QByteArray::fromHex("0102030405060708090A9000");
quint32 ssc = 0;
auto result = encryptResponse(plainBuffer, ssc);
ResponseApdu encryptedResponse(concat({result[0], result[1], result[2], result[3]}));
ResponseApdu decryptedResponse = mSecureMessaging->decrypt(encryptedResponse);
QCOMPARE(decryptedResponse.getBuffer(), plainBuffer);
}
void testDecrypt_updateSendSequenceCounter()
{
QByteArray plainBuffer = QByteArray::fromHex("0102030405060708090A9000");
QByteArray plainBuffer2 = QByteArray::fromHex("0102030405060708090A9000");
quint32 ssc = 0;
auto result = encryptResponse(plainBuffer, ssc);
ResponseApdu encryptedResponse(concat({result[0], result[1], result[2], result[3]}));
QCOMPARE(ssc, 1u);
ResponseApdu decryptedResponse = mSecureMessaging->decrypt(encryptedResponse);
QCOMPARE(decryptedResponse.getBuffer(), plainBuffer);
auto result2 = encryptResponse(plainBuffer2, ssc);
ResponseApdu encryptedResponse2(concat({result2[0], result2[1], result2[2], result2[3]}));
QCOMPARE(ssc, 2u);
ResponseApdu decryptedResponse2 = mSecureMessaging->decrypt(encryptedResponse2);
QCOMPARE(decryptedResponse2.getBuffer(), plainBuffer2);
}
void testDecrypt_WrongSw()
{
QByteArray plainBuffer = QByteArray::fromHex("0102030405060708090A9000");
quint32 ssc = 0;
auto result = encryptResponse(plainBuffer, ssc);
ResponseApdu encryptedResponse(concat({result[0], result[1], result[2], QByteArray::fromHex("6B00")}));
QVERIFY(mSecureMessaging->decrypt(encryptedResponse).isEmpty());
}
void testDecrypt_WrongSecuredSw()
{
QByteArray plainBuffer = QByteArray::fromHex("0102030405060708090A9000");
quint32 ssc = 0;
auto result = encryptResponse(plainBuffer, ssc);
ResponseApdu encryptedResponse(concat({result[0], QByteArray::fromHex("99026B00"), result[2], result[3]}));
QVERIFY(mSecureMessaging->decrypt(encryptedResponse).isEmpty());
}
void testDecrypt_WrongMac()
{
QByteArray plainBuffer = QByteArray::fromHex("0102030405060708090A9000");
quint32 ssc = 0;
auto result = encryptResponse(plainBuffer, ssc);
ResponseApdu encryptedResponse(concat({result[0], result[1], QByteArray::fromHex("8E080102030404030201"), result[3]}));
QVERIFY(mSecureMessaging->decrypt(encryptedResponse).isEmpty());
}
};
QTEST_GUILESS_MAIN(test_SecureMessaging)
#include "test_SecureMessaging.moc"