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

Start VDV ticket data parsing

So far this only manages to correctly decode the begin/end time, the rest
still needs a bit more quality time with the hex editor.
parent cc1cea5d
......@@ -56,6 +56,7 @@ set(kitinerary_lib_srcs
vdv/iso9796_2decoder.cpp
vdv/vdvcertificate.cpp
vdv/vdvticket.cpp
vdv/vdvticketparser.cpp
vdv/certs/vdv-certs.qrc
......@@ -206,6 +207,7 @@ ecm_generate_headers(KItinerary_Uic9183_FORWARDING_HEADERS
ecm_generate_headers(KItinerary_Vdv_FORWARDING_HEADERS
HEADER_NAMES
VdvTicket
VdvTicketParser
PREFIX KItinerary
REQUIRED_HEADERS KItinerary_Vdv_HEADERS
......
......@@ -192,6 +192,48 @@ struct VdvCertificateSignature : public VdvTaggedSizeDataBlock<uint16_t, TagCert
/** Certificate signature remainder. */
struct VdvCertificateSignatureRemainder : public VdvSimpleDataBlock<uint16_t, TagCertificateSignatureRemainder> {};
/** Date/time representation encoded in 4 byte. */
struct VdvDateTimeCompact
{
uint32_t data;
inline int year() const
{
return ((qFromBigEndian(data) & 0b1111'1110'0000'0000'0000'0000'0000'0000) >> 25) + 1990;
}
inline int month() const
{
return (qFromBigEndian(data) & 0b0000'0001'1110'0000'0000'0000'0000'0000) >> 21;
}
inline int day() const
{
return (qFromBigEndian(data) & 0b0000'0000'0001'1111'0000'0000'0000'0000) >> 16;
}
inline int hour() const
{
return (qFromBigEndian(data) & 0b0000'0000'0000'0000'1111'1000'0000'0000) >> 11;
}
inline int minute() const
{
return (qFromBigEndian(data) & 0b0000'0000'0000'0000'0000'0111'1110'0000) >> 5;
}
inline int second() const
{
return (qFromBigEndian(data) & 0b0000'0000'0000'0000'0000'0000'0001'1111) * 2;
}
};
/** Ticket data header. */
struct VdvTicketHeader
{
uint32_t ticketId;
uint16_t kvpOrgId;
uint16_t productId;
uint16_t pvOrgId;
VdvDateTimeCompact beginDt;
VdvDateTimeCompact endDt;
};
#pragma pack(pop)
}
......
/*
Copyright (C) 2019 Volker Krause <vkrause@kde.org>
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "vdvticket.h"
#include "vdvdata_p.h"
#include <QDebug>
using namespace KItinerary;
namespace KItinerary {
class VdvTicketPrivate : public QSharedData
{
public:
QByteArray m_data;
};
}
VdvTicket::VdvTicket()
: d(new VdvTicketPrivate)
{
}
VdvTicket::VdvTicket(const QByteArray &data)
: d(new VdvTicketPrivate)
{
qDebug() << data.toHex() << data.size();
if ((unsigned)data.size() < sizeof(VdvTicketHeader)) {
qWarning() << "Ticket data too small";
return;
}
d->m_data = data;
const auto hdr = reinterpret_cast<const VdvTicketHeader*>(d->m_data.constData());
qDebug() << qFromBigEndian(hdr->ticketId) << qFromBigEndian(hdr->kvpOrgId) << qFromBigEndian(hdr->productId) << qFromBigEndian(hdr->pvOrgId);
qDebug() << "begin:" << beginDateTime();
qDebug() << "end:" << endDateTime();
// iterate over TLV blocks
int offset = sizeof(VdvTicketHeader);
while (offset < d->m_data.size() - 1) {
qDebug() << "tag:" << (uint8_t)d->m_data[offset] << "size:" << (uint8_t)d->m_data[offset + 1] << "remaining:" << (d->m_data.size() - offset - (uint8_t)d->m_data[offset + 1]);
offset += (uint8_t)d->m_data[offset + 1] + 2;
}
}
VdvTicket::VdvTicket(const VdvTicket&) = default;
VdvTicket::~VdvTicket() = default;
VdvTicket& VdvTicket::operator=(const VdvTicket&) = default;
static QDateTime dtCompactToQdt(const VdvDateTimeCompact &dtc)
{
return QDateTime({dtc.year(), dtc.month(), dtc.day()}, {dtc.hour(), dtc.minute(), dtc.second()});
}
QDateTime VdvTicket::beginDateTime() const
{
if (d->m_data.isEmpty()) {
return {};
}
const auto hdr = reinterpret_cast<const VdvTicketHeader*>(d->m_data.constData());
return dtCompactToQdt(hdr->beginDt);
}
QDateTime KItinerary::VdvTicket::endDateTime() const
{
if (d->m_data.isEmpty()) {
return {};
}
const auto hdr = reinterpret_cast<const VdvTicketHeader*>(d->m_data.constData());
return dtCompactToQdt(hdr->endDt);
}
/*
Copyright (C) 2019 Volker Krause <vkrause@kde.org>
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef KITINERARY_VDVTICKET_H
#define KITINERARY_VDVTICKET_H
#include "kitinerary_export.h"
#include <QDateTime>
#include <QExplicitlySharedDataPointer>
#include <QMetaType>
namespace KItinerary {
class VdvTicketPrivate;
/** Ticket information from a VDV barcode.
* For use by tooling or custom extractor scripts.
*/
class KITINERARY_EXPORT VdvTicket
{
Q_GADGET
/** Begin of the validitiy of this ticket. */
Q_PROPERTY(QDateTime beginDateTime READ beginDateTime)
/** End of the validity of this ticket. */
Q_PROPERTY(QDateTime endDateTime READ endDateTime)
public:
VdvTicket();
VdvTicket(const QByteArray &data);
VdvTicket(const VdvTicket&);
~VdvTicket();
VdvTicket& operator=(const VdvTicket&);
QDateTime beginDateTime() const;
QDateTime endDateTime() const;
private:
QExplicitlySharedDataPointer<VdvTicketPrivate> d;
};
}
Q_DECLARE_METATYPE(KItinerary::VdvTicket)
#endif // KITINERARY_VDVTICKET_H
......@@ -73,16 +73,13 @@ bool VdvTicketParser::parse(const QByteArray &data)
}
// (3) decode the ticket data using the decoded CV certificate
Iso9796_2Decoder decoder;
decoder.setRsaParameters(cvCert.modulus(), cvCert.modulusSize(), cvCert.exponent(), cvCert.exponentSize());
decoder.addWithRecoveredMessage(sig->contentData(), sig->contentSize());
decoder.add(sigRemainder->contentData(), sigRemainder->contentSize());
// (4) profit!
qDebug() << decoder.recoveredMessage();
qDebug() << decoder.recoveredMessage().toHex();
// TODO
m_ticket = VdvTicket(decoder.recoveredMessage());
return true;
}
......@@ -105,3 +102,8 @@ bool VdvTicketParser::maybeVdvTicket(const QByteArray& data)
// verify the "VDV" marker is there
return strncmp((const char*)(rem->contentData() + rem->contentSize() - 5), "VDV", 3) == 0;
}
VdvTicket VdvTicketParser::ticket() const
{
return m_ticket;
}
......@@ -19,6 +19,7 @@
#define KITINERARY_VDVTICKETPARSER_H
#include "kitinerary_export.h"
#include "vdvticket.h"
class QByteArray;
......@@ -46,10 +47,16 @@ public:
/** Tries to parse the ticket in @p data. */
bool parse(const QByteArray &data);
/** Returns the parsed ticket data. */
VdvTicket ticket() const;
/** Fast check if @p data might contain a VDV ticket.
* Does not perform full decoding, mainly useful for content auto-detection.
*/
static bool maybeVdvTicket(const QByteArray &data);
private:
VdvTicket m_ticket;
};
}
......
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