File indexing completed on 2024-12-29 04:51:09

0001 /*
0002     SPDX-FileCopyrightText: 2019 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "vdvticketparser.h"
0008 #include "vdvdata_p.h"
0009 #include "vdvcertificate_p.h"
0010 #include "iso9796_2decoder_p.h"
0011 #include "logging.h"
0012 
0013 #include "../asn1/berelement.h"
0014 
0015 #include <QByteArray>
0016 #include <QDebug>
0017 
0018 using namespace KItinerary;
0019 
0020 VdvTicketParser::VdvTicketParser() = default;
0021 VdvTicketParser::~VdvTicketParser() = default;
0022 
0023 bool VdvTicketParser::parse(const QByteArray &data)
0024 {
0025     // (1) find the certificate authority reference (CAR) to identify the key to decode the CV certificate
0026     const auto sig = BER::TypedElement<TagSignature>(data);
0027     if (!sig.isValid()) {
0028         qCDebug(Log) << "Invalid VDV ticket signature.";
0029         return false;
0030     }
0031     const auto sigRemainder = BER::TypedElement<TagSignatureRemainder>(data, sig.size());
0032     if (!sigRemainder.isValid()) {
0033         qCDebug(Log) << "Invalid VDV signature remainder.";
0034         return false;
0035     }
0036 
0037     const auto cvCertOffset = sig.size() + sigRemainder.size();
0038     auto cvCert = VdvCertificate(data, cvCertOffset);
0039     if ((!cvCert.isValid() && !cvCert.needsCaKey())) {
0040         qCDebug(Log) << "Invalid CV signature:" << cvCert.isValid() << cvCertOffset << cvCert.size();
0041         return false;
0042     }
0043 
0044     const auto carOffset = cvCertOffset + cvCert.size();
0045     const auto carBlock = BER::TypedElement<TagCaReference>(data, carOffset);
0046     if (!carBlock.isValid()) {
0047         qCDebug(Log) << "Invalid CA Reference.";
0048         return false;
0049     }
0050     const auto car = carBlock.contentAt<VdvCaReference>(0);
0051     if (!car) {
0052         qCDebug(Log) << "Cannot obtain CA Reference.";
0053         return false;
0054     }
0055     qCDebug(Log) << "CV CAR:" << QByteArray(car->region, 5) << car->serviceIndicator << car->discretionaryData << car->algorithmReference << car->year;
0056 
0057     const auto caCert = VdvPkiRepository::caCertificate(car);
0058     if (!caCert.isValid()) {
0059         qCWarning(Log) << "Could not find CA certificate" << QByteArray(reinterpret_cast<const char*>(car), sizeof(VdvCaReference)).toHex();
0060         return false;
0061     }
0062 
0063     // (2) decode the CV certificate
0064     cvCert.setCaCertificate(caCert);
0065     if (!cvCert.isValid()) {
0066         qCWarning(Log) << "Failed to decode CV certificate.";
0067         return false;
0068     }
0069 
0070     // (3) decode the ticket data using the decoded CV certificate
0071     Iso9796_2Decoder decoder;
0072     decoder.setRsaParameters(cvCert.modulus(), cvCert.modulusSize(), cvCert.exponent(), cvCert.exponentSize());
0073     decoder.addWithRecoveredMessage(sig.contentData(), sig.contentSize());
0074     decoder.add(sigRemainder.contentData(), sigRemainder.contentSize());
0075 
0076     // (4) profit!
0077     m_ticket = VdvTicket(decoder.recoveredMessage(), data);
0078     return true;
0079 }
0080 
0081 bool VdvTicketParser::maybeVdvTicket(const QByteArray& data)
0082 {
0083     if (data.size() < 352) {
0084         return false;
0085     }
0086 
0087     // signature header
0088     const auto sig = BER::TypedElement<TagSignature>(data);
0089     if (!sig.isValid()) {
0090         return false;
0091     }
0092     const auto rem = BER::TypedElement<TagSignatureRemainder>(data, sig.size());
0093     if (!rem.isValid()) {
0094         return false;
0095     }
0096 
0097     // verify the "VDV" marker is there
0098     return strncmp((const char*)(rem.contentData() + rem.contentSize() - 5), "VDV", 3) == 0;
0099 }
0100 
0101 VdvTicket VdvTicketParser::ticket() const
0102 {
0103     return m_ticket;
0104 }