Commit 56cdf1a1 authored by Volker Krause's avatar Volker Krause
Browse files

Obtain all input parameters we need for CV cert ISO 9796-2 decoding

Next step is to actually implement the ISO 9796-2 decoder, that's going
to decide whether this whole thing actually works out.
parent c5485c4f
......@@ -54,6 +54,7 @@ set(kitinerary_lib_srcs
uic9183/uic9183ticketlayout.cpp
uic9183/vendor0080block.cpp
vdv/iso9796_2decoder.cpp
vdv/vdvcertificate.cpp
vdv/vdvticketparser.cpp
vdv/certs/vdv-certs.qrc
......
/*
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 "config-kitinerary.h"
#include "iso9796_2decoder_p.h"
using namespace KItinerary;
Iso9796_2Decoder::Iso9796_2Decoder() = default;
Iso9796_2Decoder::~Iso9796_2Decoder() = default;
void Iso9796_2Decoder::setRsaParameters(const uint8_t *modulus, uint16_t modulusSize, const uint8_t *exponent, uint16_t exponentSize)
{
#ifdef HAVE_OPENSSL_RSA
// TODO
#else
Q_UNUSED(modulus);
Q_UNUSED(modulusSize);
Q_UNUSED(exponent);
Q_UNUSED(exponentSize);
#endif
}
void Iso9796_2Decoder::addWithRecoveredMessage(const uint8_t *data, int size)
{
#ifdef HAVE_OPENSSL_RSA
// TODO
#else
Q_UNUSED(data);
Q_UNUSED(size);
#endif
}
void Iso9796_2Decoder::add(const uint8_t *data, int size)
{
#ifdef HAVE_OPENSSL_RSA
// TODO
#else
Q_UNUSED(data);
Q_UNUSED(size);
#endif
}
QByteArray Iso9796_2Decoder::recoveredMessage() const
{
return {}; // TODO
}
/*
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_ISO9796_2DECODER_P_H
#define KITINERARY_ISO9796_2DECODER_P_H
#include <QByteArray>
#include <cstdint>
namespace KItinerary {
/** Message recovery for ISO 9796-2 Schema 1 signatures.
* This does not care at all about security or actually validating the signature,
* this is merely about recoverying the part of the signed message that is mangled
* by the signature.
*/
class Iso9796_2Decoder
{
public:
Iso9796_2Decoder();
~Iso9796_2Decoder();
void setRsaParameters(const uint8_t *modulus, uint16_t modulusSize, const uint8_t *exponent, uint16_t exponentSize);
void addWithRecoveredMessage(const uint8_t *data, int size);
void add(const uint8_t *data, int size);
QByteArray recoveredMessage() const;
private:
};
}
#endif // KITINERARY_ISO9796_2DECODER_P_H
......@@ -40,8 +40,13 @@ VdvCertificate::VdvCertificate(const QByteArray &data, int offset)
m_data.clear();
return;
}
const auto certKeyBlock = hdr->contentAt<VdvCertificateKeyBlock>(0);
if (!certKeyBlock->isValid()) {
qWarning() << "Invalid certificate key block.";
m_data.clear();
return;
}
qDebug() << "key:" << certKey()->isValid();
qDebug() << "car:" << QByteArray(certKey()->car.region, 2) << QByteArray(certKey()->car.name, 3);
qDebug() << "chr:" << QByteArray(certKey()->chr.name, 5);
qDebug() << "cha:" << QByteArray(certKey()->cha.name, 6);
......@@ -91,7 +96,7 @@ const VdvCertificateHeader* VdvCertificate::header() const
const VdvCertificateKey* VdvCertificate::certKey() const
{
// TODO check if m_data is large enough
return reinterpret_cast<const VdvCertificateKey*>(m_data.constData() + m_offset + header()->contentOffset());
return header()->contentAt<VdvCertificateKeyBlock>(0)->contentAt<VdvCertificateKey>(0);
}
......
......@@ -34,49 +34,73 @@ enum : uint8_t {
enum : uint16_t {
TagCertificate = 0x7F21,
TagCertificateSignature = 0x5F37,
TagCertificateSignatureRemainder = 0x5F38,
TagCertificateContent = 0x5F4E,
};
#pragma pack(push)
#pragma pack(1)
/** Signature container for the signed part of the payload data. */
struct VdvSignature {
uint8_t tag;
uint8_t stuff; // always 0x81
uint8_t size; // always 0x80
uint8_t data[128];
/** Generic structure for the header of data blocks in VDV binary data.
* This consits of:
* - a one or two byte tag (@tparam TagType) with a fixed value (@tparam TagValue)
* - a one byte field indicating the size of the size field (optional)
* - one or two bytes for the size
* - followed by size bytes of content
*/
template <typename TagType, TagType TagValue>
struct VdvAbstractDataBlock
{
TagType tag;
inline bool isValid() const
{
return qFromBigEndian(tag) == TagValue;
}
};
/** Signature Remainder header. */
struct VdvSignatureRemainder {
enum { Offset = 131 };
template <typename TagType, TagType TagValue>
struct VdvSimpleDataBlock : public VdvAbstractDataBlock<TagType, TagValue>
{
uint8_t size0;
uint8_t tag;
uint8_t contentSize; // >= 5
// followed by size bytes with the remainder of the signed payload data. */
inline uint16_t contentSize() const
{
return size0;
}
inline bool isValid() const
inline uint16_t contentOffset() const
{
return tag == TagSignatureRemainder && contentSize >= 5;
return sizeof(VdvSimpleDataBlock);
}
inline uint8_t size() const
inline const uint8_t* contentData() const
{
return contentSize + sizeof(tag) + sizeof(contentSize);
return reinterpret_cast<const uint8_t*>(this) + contentOffset();
}
inline uint16_t size() const
{
return contentSize() + contentOffset();
}
template <typename T>
inline const T* contentAt(int offset) const
{
return reinterpret_cast<const T*>(contentData() + offset);
}
};
/** CV certificate. */
struct VdvCertificateHeader {
uint16_t tag;
template <typename TagType, TagType TagValue>
struct VdvTaggedSizeDataBlock : public VdvAbstractDataBlock<TagType, TagValue>
{
uint8_t sizeTag;
uint8_t size0;
uint8_t size1;
inline bool isValid() const
{
return qFromBigEndian(tag) == TagCertificate && (sizeTag == TagOneByteSize || sizeTag == TagTwoByteSize);
return VdvAbstractDataBlock<TagType, TagValue>::isValid() && (sizeTag == TagOneByteSize || sizeTag == TagTwoByteSize);
}
inline uint16_t contentSize() const
......@@ -86,17 +110,41 @@ struct VdvCertificateHeader {
inline uint16_t contentOffset() const
{
return sizeof(VdvCertificateHeader) - ((sizeTag == TagOneByteSize) ? 1 : 0);
return sizeof(VdvTaggedSizeDataBlock) - ((sizeTag == TagOneByteSize) ? 1 : 0);
}
inline const uint8_t* contentData() const
{
return reinterpret_cast<const uint8_t*>(this) + contentOffset();
}
inline uint16_t size() const
{
return contentSize() + contentOffset();
}
template <typename T>
inline const T* contentAt(int offset) const
{
return reinterpret_cast<const T*>(contentData() + offset);
}
};
/** 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 };
};
/** CV certificate. */
struct VdvCertificateHeader : public VdvTaggedSizeDataBlock<uint16_t, TagCertificate> {
};
/** Certificate Authority Reference (CAR) content. */
struct VdvCaReferenceContent
struct VdvCaReference
{
char region[2];
char name[3];
......@@ -105,17 +153,7 @@ struct VdvCaReferenceContent
uint8_t algorithmReference;
uint8_t year;
};
struct VdvCaReference {
uint8_t tag;
uint8_t contentSize;
VdvCaReferenceContent car;
inline bool isValid() const
{
return tag == TagCaReference && contentSize == 8;
}
};
struct VdvCaReferenceBlock : public VdvSimpleDataBlock<uint8_t, TagCaReference> {};
/** Certificate Holder Reference (CHR) */
struct VdvCertificateHolderReference {
......@@ -132,33 +170,20 @@ struct VdvCertificateHolderAuthorization {
/** Certificate key, contained in a certificate object. */
struct VdvCertificateKey {
uint16_t tag;
uint8_t sizeTag;
uint8_t size0;
uint8_t certificateProfileIdentifier;
VdvCaReferenceContent car;
VdvCaReference car;
VdvCertificateHolderReference chr;
VdvCertificateHolderAuthorization cha;
uint8_t date[4];
uint8_t oid[9];
uint8_t modulusBegin;
inline bool isValid() const
{
return qFromBigEndian(tag) == TagCertificateContent && sizeTag == TagOneByteSize;
}
};
struct VdvCertificateKeyBlock : public VdvTaggedSizeDataBlock<uint16_t, TagCertificateContent> {};
/** Certificate signature. */
struct VdvCertificateSignature {
uint16_t tag;
uint16_t taggedSize;
inline bool isValid() const
{
return qFromBigEndian(tag) == TagCertificateSignature;
}
};
struct VdvCertificateSignature : public VdvTaggedSizeDataBlock<uint16_t, TagCertificateSignature> {};
/** Certificate signature remainder. */
struct VdvCertificateSignatureRemainder : public VdvSimpleDataBlock<uint16_t, TagCertificateSignatureRemainder> {};
#pragma pack(pop)
......
......@@ -18,6 +18,7 @@
#include "vdvticketparser.h"
#include "vdvdata_p.h"
#include "vdvcertificate_p.h"
#include "iso9796_2decoder_p.h"
#include <QByteArray>
#include <QDebug>
......@@ -41,32 +42,49 @@ void VdvTicketParser::parse(const QByteArray &data)
qWarning() << "Invalid VDV signature remainder.";
return;
}
qDebug() << sigRemainder->contentSize;
qDebug() << sigRemainder->contentSize();
const auto cvCertOffset = VdvSignatureRemainder::Offset + sigRemainder->size();
const auto cvCert = reinterpret_cast<const VdvCertificateHeader*>(data.constData() + cvCertOffset);
if (!cvCert->isValid() || cvCertOffset + cvCert->size() + sizeof(VdvCaReference) > (unsigned)data.size()) {
if (!cvCert->isValid() || cvCertOffset + cvCert->size() + sizeof(VdvCaReferenceBlock) > (unsigned)data.size()) {
qWarning() << "Invalid CV signature:" << cvCert->isValid() << cvCertOffset << cvCert->size();
return;
}
qDebug() << cvCert->contentSize();
const auto carOffset = cvCertOffset + cvCert->size();
const auto car = reinterpret_cast<const VdvCaReference*>(data.constData() + carOffset);
if (!car->isValid()) {
const auto carBlock = reinterpret_cast<const VdvCaReferenceBlock*>(data.constData() + carOffset);
if (!carBlock->isValid() || carBlock->contentSize() < sizeof(VdvCaReference)) {
qWarning() << "Invalid CA Reference.";
return;
}
qDebug() << QByteArray(car->car.name, 3) << car->car.serviceIndicator << car->car.discretionaryData << car->car.algorithmReference << car->car.year;
const auto car = carBlock->contentAt<VdvCaReference>(0);
qDebug() << QByteArray(car->name, 3) << car->serviceIndicator << car->discretionaryData << car->algorithmReference << car->year;
const auto caCert = VdvPkiRepository::caCertificate(car->car.algorithmReference);
const auto caCert = VdvPkiRepository::caCertificate(car->algorithmReference);
if (!caCert.isValid()) {
qWarning() << "Could not find CA certificate" << car->car.algorithmReference;
qWarning() << "Could not find CA certificate" << car->algorithmReference;
return;
}
// (2) decode the CV certificate
// TODO
const auto cvSig = cvCert->contentAt<VdvCertificateSignature>(0);
if (!cvSig->isValid()) {
qWarning() << "Invalid CV certificate signature structure.";
return;
}
qDebug() << cvCert->contentSize() << cvSig->size() << (uint8_t)*(cvCert->contentData() + cvSig->size());
const auto cvRem = cvCert->contentAt<VdvCertificateSignatureRemainder>(cvSig->size());
if (!cvRem->isValid()) {
qWarning() << "Invalid CV certificate signature remainder structure.";
return;
}
qDebug() << cvSig->contentSize() << cvRem->contentSize();
Iso9796_2Decoder cvDecoder;
cvDecoder.setRsaParameters(caCert.modulus(), caCert.modulusSize(), caCert.exponent(), caCert.exponentSize());
cvDecoder.addWithRecoveredMessage(cvSig->contentData(), cvSig->contentSize());
cvDecoder.add(cvRem->contentData(), cvRem->contentSize());
// (3) decode the ticket data using the decoded CV certificate
// TODO
......
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