File indexing completed on 2025-07-13 04:48:32
0001 /* 0002 SPDX-FileCopyrightText: 2019 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "vdvcertificate_p.h" 0008 #include "vdvdata_p.h" 0009 #include "iso9796_2decoder_p.h" 0010 0011 #include "../asn1/berelement.h" 0012 0013 #include <QDate> 0014 #include <QDebug> 0015 #include <QFile> 0016 0017 using namespace KItinerary; 0018 0019 VdvCertificate::VdvCertificate() = default; 0020 0021 VdvCertificate::VdvCertificate(const QByteArray &data, int offset) 0022 : m_offset(offset) 0023 { 0024 const auto hdr = BER::TypedElement<TagCertificate>(data, offset); 0025 if (!hdr.isValid()) { 0026 qDebug() << "Invalid certificate header:" << hdr.isValid() << data.size() << offset; 0027 return; 0028 } 0029 0030 m_data = data; 0031 const auto certKeyBlock = hdr.find(TagCertificateContent); 0032 if (certKeyBlock.isValid()) { 0033 m_type = Raw; 0034 qDebug() << "found decrypted key"; 0035 qDebug() << "CHR:" << QByteArray(certKey()->chr.name, 5) << certKey()->chr.algorithmReference << certKey()->chr.year; 0036 qDebug() << "CAR:" << QByteArray(certKey()->car.region, 2) << QByteArray(certKey()->car.name, 3); 0037 return; 0038 } 0039 0040 const auto sig = hdr.find(TagCertificateSignature); 0041 if (!sig.isValid()) { 0042 qWarning() << "Invalid certificate content: neither a key nor a signature!"; 0043 m_data.clear(); 0044 return; 0045 } 0046 0047 m_type = Signed; 0048 qDebug() << "found encrypted key"; 0049 } 0050 0051 VdvCertificate::~VdvCertificate() = default; 0052 0053 bool VdvCertificate::isValid() const 0054 { 0055 if (m_type == Invalid) { 0056 return false; 0057 } 0058 return m_type == Signed ? !m_recoveredData.isEmpty() : !m_data.isEmpty(); 0059 } 0060 0061 bool VdvCertificate::needsCaKey() const 0062 { 0063 return m_type == Signed && m_recoveredData.isEmpty(); 0064 } 0065 0066 int VdvCertificate::size() const 0067 { 0068 return m_type == Invalid ? 0 : header().size(); 0069 } 0070 0071 uint16_t VdvCertificate::modulusSize() const 0072 { 0073 switch (certKey()->certificateProfileIdentifier) { 0074 case 3: 0075 return 1536 / 8; 0076 case 4: 0077 return 1024 / 8; 0078 case 7: 0079 return 1984 / 8; 0080 } 0081 qWarning() << "Unknown certificate profile identifier: " << certKey()->certificateProfileIdentifier; 0082 return 0; 0083 } 0084 0085 const uint8_t* VdvCertificate::modulus() const 0086 { 0087 const auto k = certKey(); 0088 return (&k->oidBegin) + k->oidSize(); 0089 } 0090 0091 uint16_t VdvCertificate::exponentSize() const 0092 { 0093 return 4; 0094 } 0095 0096 const uint8_t* VdvCertificate::exponent() const 0097 { 0098 return modulus() + modulusSize(); 0099 } 0100 0101 void VdvCertificate::setCaCertificate(const VdvCertificate &caCert) 0102 { 0103 if (!caCert.isValid()) { 0104 qWarning() << "Invalid CA certificate."; 0105 return; 0106 } 0107 0108 Iso9796_2Decoder decoder; 0109 decoder.setRsaParameters(caCert.modulus(), caCert.modulusSize(), caCert.exponent(), caCert.exponentSize()); 0110 0111 const auto sig = header().find(TagCertificateSignature); 0112 decoder.addWithRecoveredMessage(sig.contentData(), sig.contentSize()); 0113 0114 if (header().contentSize() > sig.size()) { 0115 const auto rem = header().find(TagCertificateSignatureRemainder); 0116 if (rem.isValid()) { 0117 decoder.add(rem.contentData(), rem.contentSize()); 0118 } else { 0119 qWarning() << "Invalid signature remainder!" << rem.isValid() << rem.size() << sig.size() << header().contentSize(); 0120 } 0121 } 0122 0123 m_recoveredData = decoder.recoveredMessage(); 0124 if (!m_recoveredData.isEmpty() && m_recoveredData.size() >= (certKey()->headerSize() + modulusSize() + exponentSize())) { 0125 qDebug() << "successfully decrypted key"; 0126 qDebug() << "CAR:" << QByteArray(certKey()->car.region, 2) << QByteArray(certKey()->car.name, 3); 0127 qDebug() << "CHR:" << QByteArray(certKey()->chr.name, 5) << certKey()->chr.algorithmReference << certKey()->chr.year; 0128 } else { 0129 qWarning() << "decrypting certificate key failed!"; 0130 qDebug() << "size is:" << m_recoveredData.size() << "expected:" << (certKey()->headerSize() + modulusSize() + exponentSize()); 0131 qDebug() << QByteArray((const char*)sig.contentData(), sig.contentSize()).toHex(); 0132 m_type = Invalid; 0133 m_recoveredData.clear(); 0134 } 0135 } 0136 0137 void VdvCertificate::writeKey(QIODevice *out) const 0138 { 0139 out->write("\x7F\x21"); 0140 if (m_type == Signed) { 0141 BER::Element::writeSize(out, m_recoveredData.size() + 3); 0142 out->write("\x5F\x4E"); 0143 BER::Element::writeSize(out, m_recoveredData.size()); 0144 out->write(m_recoveredData); 0145 } else if (m_type == Raw) { 0146 const auto keyBlock = header().find(TagCertificateContent); 0147 BER::Element::writeSize(out, keyBlock.size()); 0148 out->write(keyBlock.rawData(), keyBlock.size()); 0149 } 0150 } 0151 0152 bool VdvCertificate::isSelfSigned() const 0153 { 0154 return memcmp(&certKey()->car, certKey()->chr.name, sizeof(VdvCaReference)) == 0; 0155 } 0156 0157 QDate VdvCertificate::endOfValidity() const 0158 { 0159 const auto key = certKey(); 0160 return key->date; 0161 } 0162 0163 BER::Element VdvCertificate::header() const 0164 { 0165 return BER::Element(m_data, m_offset); 0166 } 0167 0168 const VdvCertificateKey* VdvCertificate::certKey() const 0169 { 0170 if (m_type == Signed) { 0171 return reinterpret_cast<const VdvCertificateKey*>(m_recoveredData.constData()); 0172 } else if (m_type == Raw) { 0173 return header().find(TagCertificateContent).contentAt<VdvCertificateKey>(); 0174 } 0175 return nullptr; 0176 } 0177 0178 0179 VdvCertificate VdvPkiRepository::caCertificate(const VdvCaReference *car) 0180 { 0181 QFile f(QLatin1StringView(":/org.kde.pim/kitinerary/vdv/certs/") + 0182 QString::fromLatin1(QByteArray(reinterpret_cast<const char *>(car), 0183 sizeof(VdvCaReference)) 0184 .toHex()) + 0185 QLatin1StringView(".vdv-cert")); 0186 if (!f.open(QFile::ReadOnly)) { 0187 qWarning() << "Failed to open CA cert file" << f.fileName() 0188 << f.errorString(); 0189 return VdvCertificate(); 0190 } 0191 0192 VdvCertificate cert(f.readAll()); 0193 if (cert.needsCaKey()) { 0194 VdvCaReference rootCAR; 0195 rootCAR.region[0] = 'E'; rootCAR.region[1] = 'U'; 0196 rootCAR.name[0] = 'V'; rootCAR.name[1] = 'D'; rootCAR.name[2] = 'V'; 0197 rootCAR.serviceIndicator = 0; 0198 rootCAR.discretionaryData = 1; 0199 rootCAR.algorithmReference = 1; 0200 rootCAR.year = 6; 0201 cert.setCaCertificate(caCertificate(&rootCAR)); 0202 } 0203 return cert; 0204 }