Commit cc1cea5d authored by Volker Krause's avatar Volker Krause
Browse files

Clean up VDV certificate decoding code a bit

Now that this seems to produce correct results for all samples we don't
need the extensive debug output anymore.
parent cd0e0d3f
......@@ -42,11 +42,17 @@ public:
Iso9796_2Decoder();
~Iso9796_2Decoder();
/** Set RSA modulus and exponents (@see VdvCertificate). */
void setRsaParameters(const uint8_t *modulus, uint16_t modulusSize, const uint8_t *exponent, uint16_t exponentSize);
/** Process the first block of data, containing the recoverable message and the SHA-1 hash. */
void addWithRecoveredMessage(const uint8_t *data, int size);
/** Process any further data. */
void add(const uint8_t *data, int size);
/** Returns the recovered message.
* This should either be modulusSize - 22 bytes, or empty in case of an error.
*/
QByteArray recoveredMessage() const;
private:
......
......@@ -30,14 +30,14 @@ VdvCertificate::VdvCertificate(const QByteArray &data, int offset)
: m_offset(offset)
{
if ((unsigned)data.size() <= m_offset + sizeof(VdvCertificateHeader)) {
qWarning() << "Certificate data too small:" << data.size() << offset;
qDebug() << "Certificate data too small:" << data.size() << offset;
return;
}
m_data = data;
const auto hdr = header();
if (!hdr->isValid() || data.size() < hdr->size() + offset) {
qWarning() << "Invalid certificate header:" << hdr->isValid() << hdr->size() << data.size() << offset;
qDebug() << "Invalid certificate header:" << hdr->isValid() << hdr->size() << data.size() << offset;
m_data.clear();
return;
}
......@@ -45,11 +45,8 @@ VdvCertificate::VdvCertificate(const QByteArray &data, int offset)
if (certKeyBlock->isValid()) {
m_type = Raw;
qDebug() << "found decrypted key";
qDebug() << "car:" << QByteArray(certKey()->car.region, 2) << QByteArray(certKey()->car.name, 3);
qDebug() << "chr:" << QByteArray(certKey()->chr.name, 5) << certKey()->chr.algorithmReference << certKey()->chr.year;
qDebug() << "cha:" << QByteArray(certKey()->cha.name, 6);
qDebug() << "modulus:" << modulusSize() << *modulus() << *(modulus() + modulusSize() - 1) << (modulus() - (const uint8_t*)certKey());
qDebug() << "exponent:" << exponentSize() << *exponent() << *(exponent() + exponentSize() - 1) << (exponent() - (const uint8_t*)certKey());
qDebug() << "CHR:" << QByteArray(certKey()->chr.name, 5) << certKey()->chr.algorithmReference << certKey()->chr.year;
qDebug() << "CAR:" << QByteArray(certKey()->car.region, 2) << QByteArray(certKey()->car.name, 3);
return;
}
......@@ -134,23 +131,16 @@ void VdvCertificate::setCaCertificate(const VdvCertificate &caCert)
} else {
qWarning() << "Invalid signature remainder!" << rem->isValid() << rem->size() << sig->size() << header()->contentSize();
}
qDebug() << rem->isValid() << rem->contentOffset() << rem->contentSize();
}
m_recoveredData = decoder.recoveredMessage();
qDebug() << m_recoveredData.toHex() << m_recoveredData.size();
if (!m_recoveredData.isEmpty() && m_recoveredData.size() >= (certKey()->headerSize() + modulusSize() + exponentSize())) {
qDebug() << "successfully decrypted key";
qDebug() << "car:" << QByteArray(certKey()->car.region, 2) << QByteArray(certKey()->car.name, 3);
qDebug() << "chr:" << QByteArray(certKey()->chr.name, 5) << certKey()->chr.algorithmReference << certKey()->chr.year;
qDebug() << "cha:" << QByteArray(certKey()->cha.name, 6);
qDebug() << "modulus:" << modulusSize() << *modulus() << *(modulus() + modulusSize() - 1) << (modulus() - (const uint8_t*)certKey());
qDebug() << "exponent:" << exponentSize() << *exponent() << *(exponent() + exponentSize() - 1) << (exponent() - (const uint8_t*)certKey());
qDebug() << "CAR:" << QByteArray(certKey()->car.region, 2) << QByteArray(certKey()->car.name, 3);
qDebug() << "CHR:" << QByteArray(certKey()->chr.name, 5) << certKey()->chr.algorithmReference << certKey()->chr.year;
} else {
qWarning() << "decrypting certificate key failed!";
qDebug() << "size is:" << m_recoveredData.size() << "expected:" << (certKey()->headerSize() + modulusSize() + exponentSize());
qDebug() << QByteArray((const char*)caCert.modulus(), caCert.modulusSize()).toHex();
qDebug() << QByteArray((const char*)caCert.exponent(), caCert.exponentSize()).toHex();
qDebug() << QByteArray((const char*)sig->contentData(), sig->contentSize()).toHex();;
m_type = Invalid;
m_recoveredData.clear();
......
......@@ -133,15 +133,10 @@ struct VdvTaggedSizeDataBlock : public VdvAbstractDataBlock<TagType, TagValue>
/** Signature container for the signed part of the payload data. */
struct VdvSignature : public VdvTaggedSizeDataBlock<uint8_t, TagSignature> {};
/** Signature Remainder header. */
struct VdvSignatureRemainder : public VdvSimpleDataBlock<uint8_t, TagSignatureRemainder> {
enum { Offset = 131 };
};
struct VdvSignatureRemainder : public VdvSimpleDataBlock<uint8_t, TagSignatureRemainder> {};
/** CV certificate. */
struct VdvCertificateHeader : public VdvTaggedSizeDataBlock<uint16_t, TagCertificate> {
};
struct VdvCertificateHeader : public VdvTaggedSizeDataBlock<uint16_t, TagCertificate> {};
/** Certificate Authority Reference (CAR) content. */
struct VdvCaReference
......
......@@ -19,6 +19,7 @@
#include "vdvdata_p.h"
#include "vdvcertificate_p.h"
#include "iso9796_2decoder_p.h"
#include "logging.h"
#include <QByteArray>
#include <QDebug>
......@@ -28,54 +29,50 @@ using namespace KItinerary;
VdvTicketParser::VdvTicketParser() = default;
VdvTicketParser::~VdvTicketParser() = default;
void VdvTicketParser::parse(const QByteArray &data)
bool VdvTicketParser::parse(const QByteArray &data)
{
qDebug() << data.size();
if (!maybeVdvTicket(data)) {
qWarning() << "Input data is not a VDV ticket!";
return;
}
// (1) find the certificate authority reference (CAR) to identify the key to decode the CV certificate
const auto sigRemainder = reinterpret_cast<const VdvSignatureRemainder*>(data.constData() + VdvSignatureRemainder::Offset);
if (!sigRemainder->isValid() || VdvSignatureRemainder::Offset + sigRemainder->size() + sizeof(VdvCertificateHeader) > (unsigned)data.size()) {
qWarning() << "Invalid VDV signature remainder.";
return;
const auto sig = reinterpret_cast<const VdvSignature*>(data.constData());
if (!sig->isValid()) {
qCDebug(Log) << "Invalid VDV ticket signature.";
return false;
}
const auto sigRemainder = reinterpret_cast<const VdvSignatureRemainder*>(data.constData() + sig->size());
if (!sigRemainder->isValid() || sig->size() + sigRemainder->size() + sizeof(VdvCertificateHeader) > (unsigned)data.size()) {
qCDebug(Log) << "Invalid VDV signature remainder.";
return false;
}
qDebug() << sigRemainder->contentSize();
const auto cvCertOffset = VdvSignatureRemainder::Offset + sigRemainder->size();
auto cvCert = VdvCertificate(data ,cvCertOffset);
const auto cvCertOffset = sig->size() + sigRemainder->size();
auto cvCert = VdvCertificate(data, cvCertOffset);
if ((!cvCert.isValid() && !cvCert.needsCaKey()) || cvCertOffset + cvCert.size() + sizeof(VdvCaReferenceBlock) > (unsigned)data.size()) {
qWarning() << "Invalid CV signature:" << cvCert.isValid() << cvCertOffset << cvCert.size();
return;
qCDebug(Log) << "Invalid CV signature:" << cvCert.isValid() << cvCertOffset << cvCert.size();
return false;
}
const auto carOffset = cvCertOffset + cvCert.size();
const auto carBlock = reinterpret_cast<const VdvCaReferenceBlock*>(data.constData() + carOffset);
if (!carBlock->isValid() || carBlock->contentSize() < sizeof(VdvCaReference)) {
qWarning() << "Invalid CA Reference.";
return;
qCDebug(Log) << "Invalid CA Reference.";
return false;
}
const auto car = carBlock->contentAt<VdvCaReference>(0);
qDebug() << QByteArray(car->name, 3) << car->serviceIndicator << car->discretionaryData << car->algorithmReference << car->year;
qCDebug(Log) << "CV CAR:" << QByteArray(car->region, 5) << car->serviceIndicator << car->discretionaryData << car->algorithmReference << car->year;
const auto caCert = VdvPkiRepository::caCertificate(car);
if (!caCert.isValid()) {
qWarning() << "Could not find CA certificate" << QByteArray(reinterpret_cast<const char*>(car), sizeof(VdvCaReference)).toHex();
return;
qCWarning(Log) << "Could not find CA certificate" << QByteArray(reinterpret_cast<const char*>(car), sizeof(VdvCaReference)).toHex();
return false;
}
// (2) decode the CV certificate
cvCert.setCaCertificate(caCert);
if (!cvCert.isValid()) {
qDebug() << "Failed to decode CV certificate.";
return;
qCWarning(Log) << "Failed to decode CV certificate.";
return false;
}
// (3) decode the ticket data using the decoded CV certificate
const auto sig = reinterpret_cast<const VdvSignature*>(data.constData());
qDebug() << sig->isValid() << sig->contentSize();
Iso9796_2Decoder decoder;
decoder.setRsaParameters(cvCert.modulus(), cvCert.modulusSize(), cvCert.exponent(), cvCert.exponentSize());
......@@ -86,6 +83,7 @@ void VdvTicketParser::parse(const QByteArray &data)
qDebug() << decoder.recoveredMessage();
qDebug() << decoder.recoveredMessage().toHex();
// TODO
return true;
}
bool VdvTicketParser::maybeVdvTicket(const QByteArray& data)
......@@ -95,15 +93,15 @@ bool VdvTicketParser::maybeVdvTicket(const QByteArray& data)
}
// signature header
if ((uint8_t)data[0] != TagSignature || (uint8_t)data[1] != 0x81 || (uint8_t)data[2] != 0x80 || (uint8_t)data[VdvSignatureRemainder::Offset] != TagSignatureRemainder) {
const auto sig = reinterpret_cast<const VdvSignature*>(data.constData());
if (!sig->isValid()) {
return false;
}
const uint8_t len = data[132]; // length of the 0x9A unsigned data block
if (len + 133 > data.size()) {
const auto rem = reinterpret_cast<const VdvSignatureRemainder*>(data.constData() + sig->size());
if (!rem->isValid() || sig->size() + rem->size() + sizeof(VdvCertificateHeader) > (unsigned)data.size()) {
return false;
}
// verify the "VDV" marker is there
return strncmp(data.constData() + 133 + len - 5, "VDV", 3) == 0;
return strncmp((const char*)(rem->contentData() + rem->contentSize() - 5), "VDV", 3) == 0;
}
......@@ -43,7 +43,8 @@ public:
VdvTicketParser();
~VdvTicketParser();
void parse(const QByteArray &data);
/** Tries to parse the ticket in @p data. */
bool parse(const QByteArray &data);
/** Fast check if @p data might contain a VDV ticket.
* Does not perform full decoding, mainly useful for content auto-detection.
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment