KItinerary

rsp6decoder.cpp
1/*
2 SPDX-FileCopyrightText: 2023 Volker Krause <vkrause@kde.org>
3 SPDX-License-Identifier: LGPL-2.0-or-later
4*/
5
6#include "rsp6decoder.h"
7
8#include "openssl/bignum_p.h"
9#include "openssl/opensslpp_p.h"
10
11#include <QByteArray>
12#include <QDebug>
13#include <QFile>
14#include <QJsonArray>
15#include <QJsonDocument>
16#include <QJsonObject>
17
18#include <openssl/err.h>
19
20using namespace KItinerary;
21
22static QByteArray decodeBase45Backward(const char *begin, const char *end)
23{
24 openssl::bn_ptr bn(BN_new());
25 BN_zero(bn.get());
26 for (auto it = end - 1; it >= begin; --it) {
27 BN_mul_word(bn.get(), 26);
28 if ((*it) < 'A' || (*it) > 'Z') {
29 return {};
30 }
31 BN_add_word(bn.get(), (*it) - 'A');
32 }
33
34 auto result = Bignum::toByteArray(bn);
35 std::reverse(result.begin(), result.end());
36 return result;
37}
38
39static std::vector<openssl::rsa_ptr> loadKeys(std::string_view keyId)
40{
41 qDebug() << "looking for key:"
42 << QLatin1StringView(keyId.data(), keyId.size());
43
44 const QString keyFileName =
45 QLatin1StringView(":/org.kde.pim/kitinerary/rsp6/keys/") +
46 QLatin1String(keyId.data(), keyId.size()) + QLatin1String(".json");
47 QFile keyFile(keyFileName);
48 if (!keyFile.open(QFile::ReadOnly)) {
49 qWarning() << "failed to open RSP-6 key file:" << keyFileName
50 << keyFile.errorString();
51 return {};
52 }
53
54 const auto keysArray = QJsonDocument::fromJson(keyFile.readAll()).array();
55 std::vector<openssl::rsa_ptr> keys;
56 keys.reserve(keysArray.size());
57
58 for (const auto &keyVal : keysArray) {
59 const auto keyObj = keyVal.toObject();
60 auto n = Bignum::fromByteArray(QByteArray::fromBase64(
61 keyObj.value(QLatin1StringView("n")).toString().toLatin1()));
62 auto e = Bignum::fromByteArray(QByteArray::fromBase64(
63 keyObj.value(QLatin1StringView("e")).toString().toLatin1()));
64
65 openssl::rsa_ptr rsa(RSA_new());
66 RSA_set0_key(rsa.get(), n.release(), e.release(), nullptr);
67 keys.push_back(std::move(rsa));
68 }
69
70 return keys;
71}
72
74{
75 // verify version signature and sufficient size
76 if (data.size() < 20 || !data.startsWith("06")) {
77 return {};
78 }
79
80 // load RSA key
81 const auto keys = loadKeys(std::string_view(data.data() + 13, 2));
82 if (keys.empty()) {
83 qWarning() << "no RSP-6 key found for issuer:" << QByteArray(data.data() + 13 , 2);
84 return {};
85 }
86
87 // remove base26 transport encoding
88 const auto decoded = decodeBase45Backward(data.begin() + 15, data.end());
89
90 // decrypt payload, try all keys for the current issuer until we find one that works
91 QByteArray decrypted;
92 QByteArray depadded;
93 for (const auto &rsa : keys) {
94 decrypted.resize(RSA_size(rsa.get()));
95 const auto decryptedSize = RSA_public_decrypt(decoded.size(), reinterpret_cast<const uint8_t*>(decoded.data()), reinterpret_cast<uint8_t*>(decrypted.data()), rsa.get(), RSA_NO_PADDING);
96 if (decryptedSize < 0) {
97 qDebug() << "RSA error:" << ERR_error_string(ERR_get_error(), nullptr);
98 continue;
99 }
100
101 // we don't know the padding scheme, so we have to try all of them
102 depadded.resize(decryptedSize);
103 // PKCS#1 type 1
104 auto depaddedSize = RSA_padding_check_PKCS1_type_1((uint8_t*)depadded.data(), depadded.size(), (const uint8_t*)decrypted.data(), decrypted.size(), RSA_size(rsa.get()));
105 if (depaddedSize > 0) {
106 depadded.resize(depaddedSize);
107 return depadded;
108 }
109 // PKCS#1 type 2
110 depaddedSize = RSA_padding_check_PKCS1_type_2((uint8_t*)depadded.data(), depadded.size(), (const uint8_t*)decrypted.data(), decrypted.size(), RSA_size(rsa.get()));
111 if (depaddedSize > 0) {
112 depadded.resize(depaddedSize);
113 return depadded;
114 }
115 }
116
117 return {};
118}
static QByteArray decode(const QByteArray &data)
Decodes base26 transport encoding and decrypts the ticket payload.
char * toString(const EngineQuery &query)
Classes for reservation/travel data models, data extraction and data augmentation.
Definition berelement.h:17
const QList< QKeySequence > & begin()
iterator begin()
char * data()
iterator end()
QByteArray fromBase64(const QByteArray &base64, Base64Options options)
void resize(qsizetype newSize, char c)
qsizetype size() const const
bool startsWith(QByteArrayView bv) const const
QJsonArray array() const const
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:14:49 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.