KItinerary

vdvcertificate.cpp
1/*
2 SPDX-FileCopyrightText: 2019 Volker Krause <vkrause@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "vdvcertificate_p.h"
8#include "vdvdata_p.h"
9#include "iso9796_2decoder_p.h"
10
11#include "../asn1/berelement.h"
12
13#include <QDate>
14#include <QDebug>
15#include <QFile>
16
17using namespace KItinerary;
18
19VdvCertificate::VdvCertificate() = default;
20
21VdvCertificate::VdvCertificate(const QByteArray &data, int offset)
22 : m_offset(offset)
23{
24 const auto hdr = BER::TypedElement<TagCertificate>(data, offset);
25 if (!hdr.isValid()) {
26 qDebug() << "Invalid certificate header:" << hdr.isValid() << data.size() << offset;
27 return;
28 }
29
30 m_data = data;
31 const auto certKeyBlock = hdr.find(TagCertificateContent);
32 if (certKeyBlock.isValid()) {
33 m_type = Raw;
34 qDebug() << "found decrypted key";
35 qDebug() << "CHR:" << QByteArray(certKey()->chr.name, 5) << certKey()->chr.algorithmReference << certKey()->chr.year;
36 qDebug() << "CAR:" << QByteArray(certKey()->car.region, 2) << QByteArray(certKey()->car.name, 3);
37 return;
38 }
39
40 const auto sig = hdr.find(TagCertificateSignature);
41 if (!sig.isValid()) {
42 qWarning() << "Invalid certificate content: neither a key nor a signature!";
43 m_data.clear();
44 return;
45 }
46
47 m_type = Signed;
48 qDebug() << "found encrypted key";
49}
50
51VdvCertificate::~VdvCertificate() = default;
52
53bool VdvCertificate::isValid() const
54{
55 if (m_type == Invalid) {
56 return false;
57 }
58 return m_type == Signed ? !m_recoveredData.isEmpty() : !m_data.isEmpty();
59}
60
61bool VdvCertificate::needsCaKey() const
62{
63 return m_type == Signed && m_recoveredData.isEmpty();
64}
65
66int VdvCertificate::size() const
67{
68 return m_type == Invalid ? 0 : header().size();
69}
70
71uint16_t VdvCertificate::modulusSize() const
72{
73 switch (certKey()->certificateProfileIdentifier) {
74 case 3:
75 return 1536 / 8;
76 case 4:
77 return 1024 / 8;
78 case 7:
79 return 1984 / 8;
80 }
81 qWarning() << "Unknown certificate profile identifier: " << certKey()->certificateProfileIdentifier;
82 return 0;
83}
84
85const uint8_t* VdvCertificate::modulus() const
86{
87 const auto k = certKey();
88 return (&k->oidBegin) + k->oidSize();
89}
90
91uint16_t VdvCertificate::exponentSize() const
92{
93 return 4;
94}
95
96const uint8_t* VdvCertificate::exponent() const
97{
98 return modulus() + modulusSize();
99}
100
101void VdvCertificate::setCaCertificate(const VdvCertificate &caCert)
102{
103 if (!caCert.isValid()) {
104 qWarning() << "Invalid CA certificate.";
105 return;
106 }
107
108 Iso9796_2Decoder decoder;
109 decoder.setRsaParameters(caCert.modulus(), caCert.modulusSize(), caCert.exponent(), caCert.exponentSize());
110
111 const auto sig = header().find(TagCertificateSignature);
112 decoder.addWithRecoveredMessage(sig.contentData(), sig.contentSize());
113
114 if (header().contentSize() > sig.size()) {
115 const auto rem = header().find(TagCertificateSignatureRemainder);
116 if (rem.isValid()) {
117 decoder.add(rem.contentData(), rem.contentSize());
118 } else {
119 qWarning() << "Invalid signature remainder!" << rem.isValid() << rem.size() << sig.size() << header().contentSize();
120 }
121 }
122
123 m_recoveredData = decoder.recoveredMessage();
124 if (!m_recoveredData.isEmpty() && m_recoveredData.size() >= (certKey()->headerSize() + modulusSize() + exponentSize())) {
125 qDebug() << "successfully decrypted key";
126 qDebug() << "CAR:" << QByteArray(certKey()->car.region, 2) << QByteArray(certKey()->car.name, 3);
127 qDebug() << "CHR:" << QByteArray(certKey()->chr.name, 5) << certKey()->chr.algorithmReference << certKey()->chr.year;
128 } else {
129 qWarning() << "decrypting certificate key failed!";
130 qDebug() << "size is:" << m_recoveredData.size() << "expected:" << (certKey()->headerSize() + modulusSize() + exponentSize());
131 qDebug() << QByteArray((const char*)sig.contentData(), sig.contentSize()).toHex();
132 m_type = Invalid;
133 m_recoveredData.clear();
134 }
135}
136
137void VdvCertificate::writeKey(QIODevice *out) const
138{
139 out->write("\x7F\x21");
140 if (m_type == Signed) {
141 BER::Element::writeSize(out, m_recoveredData.size() + 3);
142 out->write("\x5F\x4E");
143 BER::Element::writeSize(out, m_recoveredData.size());
144 out->write(m_recoveredData);
145 } else if (m_type == Raw) {
146 const auto keyBlock = header().find(TagCertificateContent);
147 BER::Element::writeSize(out, keyBlock.size());
148 out->write(keyBlock.rawData(), keyBlock.size());
149 }
150}
151
152bool VdvCertificate::isSelfSigned() const
153{
154 return memcmp(&certKey()->car, certKey()->chr.name, sizeof(VdvCaReference)) == 0;
155}
156
157QDate VdvCertificate::endOfValidity() const
158{
159 const auto key = certKey();
160 return key->date;
161}
162
163BER::Element VdvCertificate::header() const
164{
165 return BER::Element(m_data, m_offset);
166}
167
168const VdvCertificateKey* VdvCertificate::certKey() const
169{
170 if (m_type == Signed) {
171 return reinterpret_cast<const VdvCertificateKey*>(m_recoveredData.constData());
172 } else if (m_type == Raw) {
173 return header().find(TagCertificateContent).contentAt<VdvCertificateKey>();
174 }
175 return nullptr;
176}
177
178
179VdvCertificate VdvPkiRepository::caCertificate(const VdvCaReference *car)
180{
181 QFile f(QLatin1StringView(":/org.kde.pim/kitinerary/vdv/certs/") +
182 QString::fromLatin1(QByteArray(reinterpret_cast<const char *>(car),
183 sizeof(VdvCaReference))
184 .toHex()) +
185 QLatin1StringView(".vdv-cert"));
186 if (!f.open(QFile::ReadOnly)) {
187 qWarning() << "Failed to open CA cert file" << f.fileName()
188 << f.errorString();
189 return VdvCertificate();
190 }
191
192 VdvCertificate cert(f.readAll());
193 if (cert.needsCaKey()) {
194 VdvCaReference rootCAR;
195 rootCAR.region[0] = 'E'; rootCAR.region[1] = 'U';
196 rootCAR.name[0] = 'V'; rootCAR.name[1] = 'D'; rootCAR.name[2] = 'V';
197 rootCAR.serviceIndicator = 0;
198 rootCAR.discretionaryData = 1;
199 rootCAR.algorithmReference = 1;
200 rootCAR.year = 6;
201 cert.setCaCertificate(caCertificate(&rootCAR));
202 }
203 return cert;
204}
An element in BER/DER/X.690 encoding.
Definition berelement.h:28
static void writeSize(QIODevice *out, int size)
Writes the given size in BER encoding to out.
AKONADI_MIME_EXPORT const char Signed[]
Classes for reservation/travel data models, data extraction and data augmentation.
Definition berelement.h:17
void clear()
qsizetype size() const const
QByteArray toHex(char separator) const const
qint64 write(const QByteArray &data)
QString fromLatin1(QByteArrayView str)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Oct 11 2024 12:14:42 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.