/*! * \copyright Copyright (c) 2014-2019 Governikus GmbH & Co. KG, Germany */ #include #include #include "asn1/KnownOIDs.h" #include "pace/SecureMessaging.h" using namespace governikus; /* * Test data generated with ocard */ class ByteArrayUtil { public: template static QByteArray fromValue(T pValue) { return QByteArray::fromHex(QByteArray::number(pValue, 16)); } }; class test_SecureMessaging : public QObject { Q_OBJECT private: QScopedPointer mSecureMessaging; QScopedPointer mCipherMac; QScopedPointer mCipher; QByteArray concat(std::initializer_list pList) const { QByteArray result; for (const auto& elem : pList) { result += elem; } return result; } QVector encryptResponse(const QByteArray& pBuffer, quint32& pSsc) const { QVector 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(0x10), static_cast(0x10), static_cast(0x20), static_cast(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(0x10), static_cast(0x10), static_cast(0x20), static_cast(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(0x00), static_cast(0x10), static_cast(0x20), static_cast(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(0x00), static_cast(0x10), static_cast(0x20), static_cast(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(0x00), static_cast(0x10), static_cast(0x20), static_cast(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(0x00), static_cast(0x10), static_cast(0x20), static_cast(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(0x00), static_cast(0x10), static_cast(0x20), static_cast(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(0x00), static_cast(0x10), static_cast(0x20), static_cast(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(0x00), static_cast(0x10), static_cast(0x20), static_cast(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"