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

Some progress on VDV (sub)CA certificate parsing

In theory this should give us the needed RSA parameters for decoding the
CV certificate now.
parent 666f7317
......@@ -54,7 +54,9 @@ set(kitinerary_lib_srcs
uic9183/uic9183ticketlayout.cpp
uic9183/vendor0080block.cpp
vdv/vdvcertificate.cpp
vdv/vdvticketparser.cpp
vdv/certs/vdv-certs.qrc
barcodedecoder.cpp
calendarhandler.cpp
......
The two CA certs in here are manually retrieved from:
ldap://ldap-vdv-ion.telesec.de:389/cn=4445564456110506,ou=VDV%20KA,o=VDV%20Kernapplikations%20GmbH,c=de
ldap://ldap-vdv-ion.telesec.de:389/cn=4445564456110706,ou=VDV%20KA,o=VDV%20Kernapplikations%20GmbH,c=de
TODO: do this automatically for all relevant certs
<RCC>
<qresource prefix="/org.kde.pim/kitinerary/vdv/certs">
<file>2.vdv-cert</file>
<file>8.vdv-cert</file>
</qresource>
</RCC>
/*
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 "vdvcertificate_p.h"
#include "vdvdata_p.h"
#include <QDebug>
#include <QFile>
using namespace KItinerary;
VdvCertificate::VdvCertificate() = default;
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;
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;
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);
qDebug() << "modulus:" << modulusSize() << *modulus() << *(modulus() + modulusSize() - 1);
qDebug() << "exponent:" << exponentSize() << *exponent() << *(exponent() + exponentSize() - 1);
}
VdvCertificate::~VdvCertificate() = default;
bool VdvCertificate::isValid() const
{
return !m_data.isEmpty();
}
uint16_t VdvCertificate::modulusSize() const
{
switch (certKey()->certificateProfileIdentifier) {
case 3:
return 1536 / 8;
case 4:
return 1024 / 8;
}
qWarning() << "Unknown certificate profile identifier: " << certKey()->certificateProfileIdentifier;
return 0;
}
const uint8_t* VdvCertificate::modulus() const
{
return &(certKey()->modulusBegin);
}
uint16_t VdvCertificate::exponentSize() const
{
return 4;
}
const uint8_t* VdvCertificate::exponent() const
{
return &(certKey()->modulusBegin) + modulusSize();
}
const VdvCertificateHeader* VdvCertificate::header() const
{
return reinterpret_cast<const VdvCertificateHeader*>(m_data.constData() + m_offset);
}
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());
}
VdvCertificate VdvPkiRepository::caCertificate(uint8_t serNum)
{
QFile f(QLatin1String(":/org.kde.pim/kitinerary/vdv/certs/") + QString::number(serNum) + QLatin1String(".vdv-cert"));
if (!f.open(QFile::ReadOnly)) {
qWarning() << "Failed to open CA cert file" << serNum << f.errorString();
return VdvCertificate();
}
qDebug() << f.size();
return VdvCertificate(f.readAll());
}
/*
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_VDVCERTIFICATE_H
#define KITINERARY_VDVCERTIFICATE_H
#include <QByteArray>
namespace KItinerary {
struct VdvCertificateHeader;
struct VdvCertificateKey;
/** Certificate object, to obtain the RSA parameters. */
class VdvCertificate
{
public:
VdvCertificate();
explicit VdvCertificate(const QByteArray &data, int offset = 0);
~VdvCertificate();
bool isValid() const;
/** Amount of bytes in the RSA modulus. */
uint16_t modulusSize() const;
/** RSA modulus. */
const uint8_t* modulus() const;
/** Amount of bytes in the RSA exponent. */
uint16_t exponentSize() const;
/** RSA exponent. */
const uint8_t* exponent() const;
private:
const VdvCertificateHeader *header() const;
const VdvCertificateKey *certKey() const;
QByteArray m_data;
int m_offset = 0;
};
/** VDV (sub)CA certificate access. */
namespace VdvPkiRepository
{
/** Returns the (sub)CA certificate for the given serial number. */
VdvCertificate caCertificate(uint8_t serNum);
}
}
#endif // KITINERARY_VDVCERTIFICATE_H
......@@ -27,12 +27,14 @@ enum : uint8_t {
TagSignature = 0x9E,
TagSignatureRemainder = 0x9A,
TagCaReference = 0x42,
TagOneByteSize = 0x81,
TagTwoByteSize = 0x82,
};
enum : uint16_t {
TagCvCertificate = 0x7F21,
TagCvCertificateSignature = 0x5F37,
TagCvCertificateContent = 0x5F4E,
TagCertificate = 0x7F21,
TagCertificateSignature = 0x5F37,
TagCertificateContent = 0x5F4E,
};
#pragma pack(push)
......@@ -66,37 +68,48 @@ struct VdvSignatureRemainder {
};
/** CV certificate. */
struct VdvCvCertificate {
struct VdvCertificateHeader {
uint16_t tag;
uint8_t sizeTag;
uint8_t size0;
uint8_t size1;
inline bool isValid() const
{
return qFromBigEndian(tag) == TagCvCertificate;
return qFromBigEndian(tag) == TagCertificate && (sizeTag == TagOneByteSize || sizeTag == TagTwoByteSize);
}
inline uint16_t contentSize() const
{
return ((size0 << 8) | size1) - 0x8100;
return sizeTag == TagOneByteSize ? size0 : ((size0 << 8) + size1);
}
inline uint16_t contentOffset() const
{
return sizeof(VdvCertificateHeader) - ((sizeTag == TagOneByteSize) ? 1 : 0);
}
inline uint16_t size() const
{
return contentSize() + sizeof(tag) + sizeof(size0) + sizeof(size1);
return contentSize() + contentOffset();
}
};
/** Certificate Authority Reference (CAR) */
struct VdvCAReference {
uint8_t tag;
uint8_t contentSize;
/** Certificate Authority Reference (CAR) content. */
struct VdvCaReferenceContent
{
char region[2];
char name[3];
uint8_t serviceIndicator: 4;
uint8_t discretionaryData: 4;
uint8_t algorithmReference;
uint8_t year;
};
struct VdvCaReference {
uint8_t tag;
uint8_t contentSize;
VdvCaReferenceContent car;
inline bool isValid() const
{
......@@ -120,18 +133,19 @@ struct VdvCertificateHolderAuthorization {
/** Certificate key, contained in a certificate object. */
struct VdvCertificateKey {
uint16_t tag;
uint16_t taggedSize;
uint8_t cpi;
VdvCAReference car;
uint8_t sizeTag;
uint8_t size0;
uint8_t certificateProfileIdentifier;
VdvCaReferenceContent car;
VdvCertificateHolderReference chr;
VdvCertificateHolderAuthorization cha;
uint8_t date[3];
uint8_t date[4];
uint8_t oid[9];
uint8_t modulusBegin;
inline bool isValid() const
{
return qFromBigEndian(tag) == TagCvCertificateContent;
return qFromBigEndian(tag) == TagCertificateContent && sizeTag == TagOneByteSize;
}
};
......@@ -142,7 +156,7 @@ struct VdvCertificateSignature {
inline bool isValid() const
{
return qFromBigEndian(tag) == TagCvCertificateSignature;
return qFromBigEndian(tag) == TagCertificateSignature;
}
};
......
......@@ -17,6 +17,7 @@
#include "vdvticketparser.h"
#include "vdvdata_p.h"
#include "vdvcertificate_p.h"
#include <QByteArray>
#include <QDebug>
......@@ -36,27 +37,33 @@ void VdvTicketParser::parse(const QByteArray &data)
// (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(VdvCvCertificate) > (unsigned)data.size()) {
if (!sigRemainder->isValid() || VdvSignatureRemainder::Offset + sigRemainder->size() + sizeof(VdvCertificateHeader) > (unsigned)data.size()) {
qWarning() << "Invalid VDV signature remainder.";
return;
}
qDebug() << sigRemainder->contentSize;
const auto cvCertOffset = VdvSignatureRemainder::Offset + sigRemainder->size();
const auto cvCert = reinterpret_cast<const VdvCvCertificate*>(data.constData() + cvCertOffset);
if (!cvCert->isValid() || cvCertOffset + cvCert->size() + sizeof(VdvCAReference) > (unsigned)data.size()) {
qWarning() << "Invalid CV signature.";
const auto cvCert = reinterpret_cast<const VdvCertificateHeader*>(data.constData() + cvCertOffset);
if (!cvCert->isValid() || cvCertOffset + cvCert->size() + sizeof(VdvCaReference) > (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);
const auto car = reinterpret_cast<const VdvCaReference*>(data.constData() + carOffset);
if (!car->isValid()) {
qWarning() << "Invalid CA Reference.";
return;
}
qDebug() << QByteArray(car->name, 3) << car->serviceIndicator << car->discretionaryData << car->algorithmReference << car->year;
qDebug() << QByteArray(car->car.name, 3) << car->car.serviceIndicator << car->car.discretionaryData << car->car.algorithmReference << car->car.year;
const auto caCert = VdvPkiRepository::caCertificate(car->car.algorithmReference);
if (!caCert.isValid()) {
qWarning() << "Could not find CA certificate" << car->car.algorithmReference;
return;
}
// (2) decode the 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