diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 02c1615..6d187ae 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,235 +1,236 @@ set(KDE_INSTALL_INCLUDEDIR_PIM ${KDE_INSTALL_INCLUDEDIR}/KPim) add_subdirectory(cli) if (TARGET Qt5::Network) add_subdirectory(knowledgedb-generator) endif() configure_file(config-kitinerary.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kitinerary.h) set(kitinerary_lib_srcs datatypes/action.cpp datatypes/brand.cpp datatypes/bustrip.cpp datatypes/creativework.cpp datatypes/event.cpp datatypes/flight.cpp datatypes/organization.cpp datatypes/person.cpp datatypes/place.cpp datatypes/reservation.cpp datatypes/taxi.cpp datatypes/ticket.cpp datatypes/traintrip.cpp datatypes/rentalcar.cpp datatypes/visit.cpp generic/genericpdfextractor.cpp generic/genericpkpassextractor.cpp generic/genericuic918extractor.cpp generic/structureddataextractor.cpp jsapi/barcode.cpp jsapi/bitarray.cpp jsapi/context.cpp jsapi/jsonld.cpp knowledgedb/alphaid.cpp knowledgedb/airportdb.cpp knowledgedb/countrydb.cpp knowledgedb/iatacode.cpp knowledgedb/knowledgedb.cpp knowledgedb/timezonedb.cpp knowledgedb/trainstationdb.cpp pdf/pdfdocument.cpp pdf/pdfextractoroutputdevice.cpp pdf/pdfimage.cpp pdf/pdfvectorpicture.cpp pdf/popplerglobalparams.cpp pdf/popplerutils.cpp uic9183/rct2ticket.cpp uic9183/uic9183block.cpp uic9183/uic9183parser.cpp uic9183/uic9183ticketlayout.cpp uic9183/vendor0080block.cpp + vdv/iso9796_2decoder.cpp vdv/vdvcertificate.cpp vdv/vdvticketparser.cpp vdv/certs/vdv-certs.qrc barcodedecoder.cpp calendarhandler.cpp documentutil.cpp extractor.cpp extractorengine.cpp extractorfilter.cpp extractorinput.cpp extractorpostprocessor.cpp extractorrepository.cpp extractorutil.cpp extractorvalidator.cpp file.cpp flightpostprocessor.cpp htmldocument.cpp iatabcbpparser.cpp jsonlddocument.cpp jsonldimportfilter.cpp locationutil.cpp mergeutil.cpp qimagepurebinarizer.cpp sortutil.cpp stringutil.cpp ) qt5_add_resources(kitinerary_lib_srcs extractors/extractors.qrc) ecm_qt_declare_logging_category(kitinerary_lib_srcs HEADER logging.h IDENTIFIER KItinerary::Log CATEGORY_NAME org.kde.kitinerary) ecm_qt_declare_logging_category(kitinerary_lib_srcs HEADER compare-logging.h IDENTIFIER KItinerary::CompareLog CATEGORY_NAME org.kde.kitinerary.comparator) ecm_qt_declare_logging_category(kitinerary_lib_srcs HEADER validator-logging.h IDENTIFIER KItinerary::ValidatorLog CATEGORY_NAME org.kde.kitinerary.extractorValidator) add_library(KPimItinerary ${kitinerary_lib_srcs}) add_library(KPim::Itinerary ALIAS KPimItinerary) generate_export_header(KPimItinerary BASE_NAME KItinerary) set_target_properties(KPimItinerary PROPERTIES VERSION ${KITINERARY_VERSION_STRING} SOVERSION ${KITINERARY_SOVERSION} EXPORT_NAME Itinerary ) target_include_directories(KPimItinerary INTERFACE "$") target_include_directories(KPimItinerary PUBLIC "$") target_link_libraries(KPimItinerary PUBLIC Qt5::Core KF5::Mime PRIVATE Qt5::Qml KF5::Archive KF5::I18n KF5::Contacts KPim::PkPass ${ZLIB_LIBRARIES} ) if (HAVE_POPPLER) target_link_libraries(KPimItinerary PRIVATE Poppler::Core) endif() if (HAVE_ZXING) target_link_libraries(KPimItinerary PRIVATE ZXing::Core) endif() if (HAVE_KCAL) target_link_libraries(KPimItinerary PUBLIC KF5::CalendarCore) endif() if (HAVE_LIBXML2) target_compile_definitions(KPimItinerary PRIVATE ${LIBXML2_DEFINITIONS}) target_include_directories(KPimItinerary PRIVATE ${LIBXML2_INCLUDE_DIR}) target_link_libraries(KPimItinerary PRIVATE ${LIBXML2_LIBRARIES}) endif() if (HAVE_PHONENUMBER) target_link_libraries(KPimItinerary PRIVATE PhoneNumber::PhoneNumber) endif() ecm_generate_headers(KItinerary_FORWARDING_HEADERS HEADER_NAMES BarcodeDecoder CalendarHandler DocumentUtil Extractor ExtractorEngine ExtractorFilter ExtractorInput ExtractorPostprocessor ExtractorRepository File HtmlDocument IataBcbpParser JsonLdDocument LocationUtil MergeUtil SortUtil PREFIX KItinerary REQUIRED_HEADERS KItinerary_HEADERS ) ecm_generate_headers(KItinerary_KnowledgeDb_FORWARDING_HEADERS HEADER_NAMES AlphaId CountryDb KnowledgeDb PREFIX KItinerary REQUIRED_HEADERS KItinerary_KnowledgeDb_HEADERS RELATIVE knowledgedb ) ecm_generate_headers(KItinerary_Datatypes_FORWARDING_HEADERS HEADER_NAMES Action Brand BusTrip CreativeWork Datatypes Event Flight Organization Reservation RentalCar Person Place Taxi Ticket TrainTrip Visit PREFIX KItinerary REQUIRED_HEADERS KItinerary_Datatypes_HEADERS RELATIVE datatypes ) ecm_generate_headers(KItinerary_Pdf_FORWARDING_HEADERS HEADER_NAMES PdfDocument PdfImage PREFIX KItinerary REQUIRED_HEADERS KItinerary_Pdf_HEADERS RELATIVE pdf ) ecm_generate_headers(KItinerary_Uic9183_FORWARDING_HEADERS HEADER_NAMES Rct2Ticket Uic9183Block Uic9183Parser Uic9183TicketLayout Vendor0080Block PREFIX KItinerary REQUIRED_HEADERS KItinerary_Uic9183_HEADERS RELATIVE uic9183 ) ecm_generate_headers(KItinerary_Vdv_FORWARDING_HEADERS HEADER_NAMES VdvTicketParser PREFIX KItinerary REQUIRED_HEADERS KItinerary_Vdv_HEADERS RELATIVE vdv ) install(TARGETS KPimItinerary EXPORT KPimItineraryTargets ${INSTALL_TARGETS_DEFAULT_ARGS}) install(FILES ${KItinerary_FORWARDING_HEADERS} ${KItinerary_KnowledgeDb_FORWARDING_HEADERS} ${KItinerary_Datatypes_FORWARDING_HEADERS} ${KItinerary_Pdf_FORWARDING_HEADERS} ${KItinerary_Uic9183_FORWARDING_HEADERS} ${KItinerary_Vdv_FORWARDING_HEADERS} DESTINATION ${KDE_INSTALL_INCLUDEDIR_PIM}/KItinerary ) install(FILES ${KItinerary_HEADERS} ${KItinerary_AirportDb_HEADERS} ${KItinerary_Datatypes_HEADERS} ${KItinerary_KnowledgeDb_HEADERS} ${KItinerary_Pdf_HEADERS} ${KItinerary_Uic9183_HEADERS} ${KItinerary_Vdv_HEADERS} ${CMAKE_CURRENT_BINARY_DIR}/kitinerary_export.h DESTINATION ${KDE_INSTALL_INCLUDEDIR_PIM}/kitinerary ) if (NOT ANDROID) install(FILES application-vnd-kde-itinerary.xml DESTINATION ${XDG_MIME_INSTALL_DIR}) update_xdg_mimetypes(${XDG_MIME_INSTALL_DIR}) endif() diff --git a/src/vdv/iso9796_2decoder.cpp b/src/vdv/iso9796_2decoder.cpp new file mode 100644 index 0000000..243a676 --- /dev/null +++ b/src/vdv/iso9796_2decoder.cpp @@ -0,0 +1,61 @@ +/* + Copyright (C) 2019 Volker Krause + + 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 . +*/ + +#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 +} diff --git a/src/vdv/iso9796_2decoder_p.h b/src/vdv/iso9796_2decoder_p.h new file mode 100644 index 0000000..b954238 --- /dev/null +++ b/src/vdv/iso9796_2decoder_p.h @@ -0,0 +1,50 @@ +/* + Copyright (C) 2019 Volker Krause + + 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 . +*/ + +#ifndef KITINERARY_ISO9796_2DECODER_P_H +#define KITINERARY_ISO9796_2DECODER_P_H + +#include + +#include + +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 diff --git a/src/vdv/vdvcertificate.cpp b/src/vdv/vdvcertificate.cpp index ca4d68f..be0bca0 100644 --- a/src/vdv/vdvcertificate.cpp +++ b/src/vdv/vdvcertificate.cpp @@ -1,108 +1,113 @@ /* Copyright (C) 2019 Volker Krause 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 . */ #include "vdvcertificate_p.h" #include "vdvdata_p.h" #include #include 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; } + const auto certKeyBlock = hdr->contentAt(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); 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(m_data.constData() + m_offset); } const VdvCertificateKey* VdvCertificate::certKey() const { // TODO check if m_data is large enough - return reinterpret_cast(m_data.constData() + m_offset + header()->contentOffset()); + return header()->contentAt(0)->contentAt(0); } 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()); } diff --git a/src/vdv/vdvdata_p.h b/src/vdv/vdvdata_p.h index 7b943a8..a9471aa 100644 --- a/src/vdv/vdvdata_p.h +++ b/src/vdv/vdvdata_p.h @@ -1,167 +1,192 @@ /* Copyright (C) 2019 Volker Krause 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 . */ #ifndef KITINERARY_VDVDATA_P_H #define KITINERARY_VDVDATA_P_H #include #include namespace KItinerary { enum : uint8_t { TagSignature = 0x9E, TagSignatureRemainder = 0x9A, TagCaReference = 0x42, TagOneByteSize = 0x81, TagTwoByteSize = 0x82, }; 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 +struct VdvAbstractDataBlock +{ + TagType tag; + + inline bool isValid() const + { + return qFromBigEndian(tag) == TagValue; + } }; -/** Signature Remainder header. */ -struct VdvSignatureRemainder { - enum { Offset = 131 }; +template +struct VdvSimpleDataBlock : public VdvAbstractDataBlock +{ + 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(this) + contentOffset(); + } + + inline uint16_t size() const + { + return contentSize() + contentOffset(); + } + + template + inline const T* contentAt(int offset) const + { + return reinterpret_cast(contentData() + offset); } }; -/** CV certificate. */ -struct VdvCertificateHeader { - uint16_t tag; +template +struct VdvTaggedSizeDataBlock : public VdvAbstractDataBlock +{ uint8_t sizeTag; uint8_t size0; uint8_t size1; inline bool isValid() const { - return qFromBigEndian(tag) == TagCertificate && (sizeTag == TagOneByteSize || sizeTag == TagTwoByteSize); + return VdvAbstractDataBlock::isValid() && (sizeTag == TagOneByteSize || sizeTag == TagTwoByteSize); } inline uint16_t contentSize() const { return sizeTag == TagOneByteSize ? size0 : ((size0 << 8) + size1); } 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(this) + contentOffset(); } inline uint16_t size() const { return contentSize() + contentOffset(); } + + template + inline const T* contentAt(int offset) const + { + return reinterpret_cast(contentData() + offset); + } +}; + + +/** Signature container for the signed part of the payload data. */ +struct VdvSignature : public VdvTaggedSizeDataBlock {}; + +/** Signature Remainder header. */ +struct VdvSignatureRemainder : public VdvSimpleDataBlock { + enum { Offset = 131 }; +}; + +/** CV certificate. */ +struct VdvCertificateHeader : public VdvTaggedSizeDataBlock { }; /** Certificate Authority Reference (CAR) content. */ -struct VdvCaReferenceContent +struct VdvCaReference { 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 - { - return tag == TagCaReference && contentSize == 8; - } -}; +struct VdvCaReferenceBlock : public VdvSimpleDataBlock {}; /** Certificate Holder Reference (CHR) */ struct VdvCertificateHolderReference { uint8_t filler[4]; // always null char name[5]; uint8_t extension[3]; }; /** Certificate Holder Authorization (CHA) */ struct VdvCertificateHolderAuthorization { char name[6]; uint8_t stuff; }; /** 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 {}; /** Certificate signature. */ -struct VdvCertificateSignature { - uint16_t tag; - uint16_t taggedSize; - - inline bool isValid() const - { - return qFromBigEndian(tag) == TagCertificateSignature; - } -}; +struct VdvCertificateSignature : public VdvTaggedSizeDataBlock {}; +/** Certificate signature remainder. */ +struct VdvCertificateSignatureRemainder : public VdvSimpleDataBlock {}; #pragma pack(pop) } #endif // KITINERARY_VDVDATA_P_H diff --git a/src/vdv/vdvticketparser.cpp b/src/vdv/vdvticketparser.cpp index e6b48b1..e9fe2f5 100644 --- a/src/vdv/vdvticketparser.cpp +++ b/src/vdv/vdvticketparser.cpp @@ -1,96 +1,114 @@ /* Copyright (C) 2019 Volker Krause 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 . */ #include "vdvticketparser.h" #include "vdvdata_p.h" #include "vdvcertificate_p.h" +#include "iso9796_2decoder_p.h" #include #include using namespace KItinerary; VdvTicketParser::VdvTicketParser() = default; VdvTicketParser::~VdvTicketParser() = default; void 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(data.constData() + VdvSignatureRemainder::Offset); if (!sigRemainder->isValid() || VdvSignatureRemainder::Offset + sigRemainder->size() + sizeof(VdvCertificateHeader) > (unsigned)data.size()) { qWarning() << "Invalid VDV signature remainder."; return; } - qDebug() << sigRemainder->contentSize; + qDebug() << sigRemainder->contentSize(); const auto cvCertOffset = VdvSignatureRemainder::Offset + sigRemainder->size(); const auto cvCert = reinterpret_cast(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(data.constData() + carOffset); - if (!car->isValid()) { + const auto carBlock = reinterpret_cast(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(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(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(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 // (4) profit! // TODO } bool VdvTicketParser::maybeVdvTicket(const QByteArray& data) { if (data.size() < 352) { return false; } // signature header if ((uint8_t)data[0] != TagSignature || (uint8_t)data[1] != 0x81 || (uint8_t)data[2] != 0x80 || (uint8_t)data[VdvSignatureRemainder::Offset] != TagSignatureRemainder) { return false; } const uint8_t len = data[132]; // length of the 0x9A unsigned data block if (len + 133 > data.size()) { return false; } // verify the "VDV" marker is there return strncmp(data.constData() + 133 + len - 5, "VDV", 3) == 0; }