1 /*!
2 * \copyright Copyright (c) 2014-2021 Governikus GmbH & Co. KG, Germany
3 */
4
5
6 #include "SecureStorage.h"
7
8 #include "FileDestination.h"
9 #include "SingletonHelper.h"
10
11 #include <QDebug>
12 #include <QDir>
13 #include <QFile>
14 #include <QFileInfo>
15 #include <QJsonDocument>
16 #include <QJsonValue>
17 #include <QLoggingCategory>
18 #include <QMetaEnum>
19 #include <QTextStream>
20
21
22 using namespace governikus;
23
24 Q_DECLARE_LOGGING_CATEGORY(securestorage)
25
26 namespace
27 {
28 #define CONFIG_NAME(_name, _key) /* NOLINT */\
29 inline QLatin1String _name(){\
30 return QLatin1String(_key);\
31 }
32
33 CONFIG_NAME(CONFIGURATION_GROUP_NAME_CV_ROOT_CERTIFICATE, "cvRootCertificates")
34 CONFIG_NAME(CONFIGURATION_GROUP_NAME_CV_ROOT_CERTIFICATE_TEST, "cvRootCertificatesTest")
35
36 CONFIG_NAME(CONFIGURATION_GROUP_NAME_UPDATE_CERTIFICATES, "updateCertificates")
37
38 CONFIG_NAME(CONFIGURATION_GROUP_NAME_TLS_SETTINGS, "tlsSettings")
39 CONFIG_NAME(CONFIGURATION_GROUP_NAME_TLS_CONFIGURATION_PSK, "tlsSettingsPsk")
40 CONFIG_NAME(CONFIGURATION_GROUP_NAME_TLS_CONFIGURATION_REMOTE_READER, "tlsSettingsRemoteReader")
41 CONFIG_NAME(CONFIGURATION_GROUP_NAME_TLS_CONFIGURATION_REMOTE_READER_PAIRING, "tlsSettingsRemoteReaderPairing")
42 CONFIG_NAME(CONFIGURATION_GROUP_NAME_MIN_STATIC_KEY_SIZES, "minStaticKeySizes")
43 CONFIG_NAME(CONFIGURATION_GROUP_NAME_MIN_EPHEMERAL_KEY_SIZES, "minEphemeralKeySizes")
44
45 CONFIG_NAME(CONFIGURATION_GROUP_NAME_SELF_AUTHENTICATION, "selfAuthentication")
46 CONFIG_NAME(CONFIGURATION_NAME_SELF_AUTHENTICATION_URL, "url")
47 CONFIG_NAME(CONFIGURATION_NAME_SELF_AUTHENTICATION_TEST_URL, "testUrl")
48
49 CONFIG_NAME(CONFIGURATION_GROUP_NAME_UPDATE_SERVER, "updateServer")
50 CONFIG_NAME(CONFIGURATION_NAME_UPDATE_SERVER_BASE_URL, "baseUrl")
51
52 CONFIG_NAME(CONFIGURATION_GROUP_NAME_WHITELIST_SERVER, "whitelistServer")
53 CONFIG_NAME(CONFIGURATION_NAME_WHITELIST_SERVER_BASE_URL, "baseUrl")
54
55 CONFIG_NAME(CONFIGURATION_GROUP_NAME_UPDATES, "updates")
56 CONFIG_NAME(CONFIGURATION_NAME_APPCAST_UPDATE_URL, "release")
57 CONFIG_NAME(CONFIGURATION_NAME_APPCAST_BETA_UPDATE_URL, "beta")
58 } // namespace
59
defineSingleton(SecureStorage)60 defineSingleton(SecureStorage)
61
62
63 SecureStorage::SecureStorage()
64 : mLoaded(false)
65 , mCvcas()
66 , mCvcasTest()
67 , mUpdateCertificates()
68 , mSelfAuthenticationUrl()
69 , mSelfAuthenticationTestUrl()
70 , mUpdateServerBaseUrl()
71 , mWhitelistServerBaseUrl()
72 , mAppcastUpdateUrl()
73 , mAppcastBetaUpdateUrl()
74 , mTlsConfig()
75 , mTlsConfigPsk()
76 , mTlsConfigRemote()
77 , mTlsConfigRemotePsk()
78 , mMinStaticKeySizes()
79 , mMinEphemeralKeySizes()
80 {
81 load();
82 }
83
84
85 SecureStorage::~SecureStorage() = default;
86
87
isLoaded() const88 bool SecureStorage::isLoaded() const
89 {
90 return mLoaded;
91 }
92
93
loadFile(const QStringList & pFiles) const94 QJsonObject SecureStorage::loadFile(const QStringList& pFiles) const
95 {
96 for (const auto& path : pFiles)
97 {
98 qCDebug(securestorage) << "Load SecureStorage:" << path;
99
100 QFile configFile(path);
101 if (!configFile.exists())
102 {
103 qCCritical(securestorage) << "SecureStorage not found";
104 continue;
105 }
106
107 if (!configFile.open(QIODevice::ReadOnly | QIODevice::Text))
108 {
109 qCCritical(securestorage) << "Wasn't able to open SecureStorage";
110 continue;
111 }
112
113 QJsonParseError parseError {};
114 const auto& document = QJsonDocument::fromJson(configFile.readAll(), &parseError);
115 if (parseError.error != QJsonParseError::NoError)
116 {
117 qCCritical(securestorage) << "Parse error while reading SecureStorage on position" << parseError.offset << ':' << parseError.errorString();
118 continue;
119 }
120
121 return document.object();
122 }
123
124 return QJsonObject();
125 }
126
127
load()128 void SecureStorage::load()
129 {
130 const QStringList files({FileDestination::getPath(QStringLiteral("config.json")), QStringLiteral(":/config.json")});
131 const auto& config = loadFile(files);
132 if (config.isEmpty())
133 {
134 return;
135 }
136
137 mCvcas.clear();
138 mCvcas = readByteArrayList(config, CONFIGURATION_GROUP_NAME_CV_ROOT_CERTIFICATE());
139
140 mCvcasTest.clear();
141 mCvcasTest = readByteArrayList(config, CONFIGURATION_GROUP_NAME_CV_ROOT_CERTIFICATE_TEST());
142 mCvcasTest = loadTestCvcsFromAppDir() + mCvcasTest;
143
144 QByteArrayList certificates = readByteArrayList(config, CONFIGURATION_GROUP_NAME_UPDATE_CERTIFICATES());
145 mUpdateCertificates.clear();
146 for (int i = 0; i < certificates.size(); ++i)
147 {
148 QSslCertificate certificate(QByteArray::fromHex(certificates[i]), QSsl::Der);
149 if (certificate.isNull())
150 {
151 qCCritical(securestorage) << "Failed to read update certificate[" << i << "] from SecureStorage";
152 continue;
153 }
154 mUpdateCertificates += certificate;
155 }
156
157 QJsonValue tlsValue = config.value(CONFIGURATION_GROUP_NAME_TLS_SETTINGS());
158 if (tlsValue.isObject())
159 {
160 mTlsConfig.load(tlsValue.toObject());
161 }
162
163 QJsonValue tlsPskValue = config.value(CONFIGURATION_GROUP_NAME_TLS_CONFIGURATION_PSK());
164 if (tlsPskValue.isObject())
165 {
166 mTlsConfigPsk.load(tlsPskValue.toObject());
167 }
168
169 QJsonValue tlsRemoteReaderValue = config.value(CONFIGURATION_GROUP_NAME_TLS_CONFIGURATION_REMOTE_READER());
170 if (tlsRemoteReaderValue.isObject())
171 {
172 mTlsConfigRemote.load(tlsRemoteReaderValue.toObject());
173 }
174 QJsonValue tlsRemoteReaderPairingValue = config.value(CONFIGURATION_GROUP_NAME_TLS_CONFIGURATION_REMOTE_READER_PAIRING());
175 if (tlsRemoteReaderPairingValue.isObject())
176 {
177 mTlsConfigRemotePsk.load(tlsRemoteReaderPairingValue.toObject());
178 }
179
180 mMinStaticKeySizes = readKeySizes(config, CONFIGURATION_GROUP_NAME_MIN_STATIC_KEY_SIZES());
181 mMinEphemeralKeySizes = readKeySizes(config, CONFIGURATION_GROUP_NAME_MIN_EPHEMERAL_KEY_SIZES());
182
183 mSelfAuthenticationUrl = readGroup(config, CONFIGURATION_GROUP_NAME_SELF_AUTHENTICATION(), CONFIGURATION_NAME_SELF_AUTHENTICATION_URL());
184 mSelfAuthenticationTestUrl = readGroup(config, CONFIGURATION_GROUP_NAME_SELF_AUTHENTICATION(), CONFIGURATION_NAME_SELF_AUTHENTICATION_TEST_URL());
185
186 mUpdateServerBaseUrl = readGroup(config, CONFIGURATION_GROUP_NAME_UPDATE_SERVER(), CONFIGURATION_NAME_UPDATE_SERVER_BASE_URL());
187 mWhitelistServerBaseUrl = readGroup(config, CONFIGURATION_GROUP_NAME_WHITELIST_SERVER(), CONFIGURATION_NAME_WHITELIST_SERVER_BASE_URL());
188
189 mAppcastUpdateUrl = readGroup(config, CONFIGURATION_GROUP_NAME_UPDATES(), CONFIGURATION_NAME_APPCAST_UPDATE_URL());
190 mAppcastBetaUpdateUrl = readGroup(config, CONFIGURATION_GROUP_NAME_UPDATES(), CONFIGURATION_NAME_APPCAST_BETA_UPDATE_URL());
191
192 mLoaded = true;
193 qCInfo(securestorage) << "SecureStorage successfully loaded";
194 }
195
196
loadTestCvcsFromAppDir()197 QByteArrayList SecureStorage::loadTestCvcsFromAppDir()
198 {
199 QByteArrayList testCvcs;
200 const QDir appDir(QCoreApplication::applicationDirPath());
201 const QStringList& dirEntries = appDir.entryList({QStringLiteral("*.cvcert.hex")}, QDir::Files);
202 for (QString cvcFilePath : dirEntries)
203 {
204 cvcFilePath = appDir.absolutePath() + QDir::separator() + cvcFilePath;
205 const QByteArray& hex = loadTestCvc(cvcFilePath);
206 if (hex.isEmpty())
207 {
208 qWarning() << "Can not load CVC from" << cvcFilePath;
209 continue;
210 }
211
212 qDebug() << "Adding CVC from" << cvcFilePath << ':' << hex;
213 testCvcs += hex;
214 }
215 return testCvcs;
216 }
217
218
loadTestCvc(const QString & pPath) const219 QByteArray SecureStorage::loadTestCvc(const QString& pPath) const
220 {
221 QFile cvcFile(pPath);
222 const int TEN_MEGA_BYTE = 10 * 1024 * 1024;
223 if (cvcFile.size() > TEN_MEGA_BYTE)
224 {
225 return QByteArray();
226 }
227 if (!cvcFile.open(QFile::ReadOnly | QFile::Text))
228 {
229 return QByteArray();
230 }
231 return cvcFile.readAll();
232 }
233
234
getCVRootCertificates(bool pProductive) const235 const QByteArrayList& SecureStorage::getCVRootCertificates(bool pProductive) const
236 {
237 return pProductive ? mCvcas : mCvcasTest;
238 }
239
240
getUpdateCertificates() const241 const QVector<QSslCertificate>& SecureStorage::getUpdateCertificates() const
242 {
243 return mUpdateCertificates;
244 }
245
246
getSelfAuthenticationUrl(bool pTest) const247 const QUrl& SecureStorage::getSelfAuthenticationUrl(bool pTest) const
248 {
249 return pTest ? mSelfAuthenticationTestUrl : mSelfAuthenticationUrl;
250 }
251
252
getUpdateServerBaseUrl() const253 const QUrl& SecureStorage::getUpdateServerBaseUrl() const
254 {
255 return mUpdateServerBaseUrl;
256 }
257
258
getWhitelistServerBaseUrl() const259 const QUrl& SecureStorage::getWhitelistServerBaseUrl() const
260 {
261 return mWhitelistServerBaseUrl;
262 }
263
264
getAppcastUpdateUrl() const265 const QUrl& SecureStorage::getAppcastUpdateUrl() const
266 {
267 return mAppcastUpdateUrl;
268 }
269
270
getAppcastBetaUpdateUrl() const271 const QUrl& SecureStorage::getAppcastBetaUpdateUrl() const
272 {
273 return mAppcastBetaUpdateUrl;
274 }
275
276
getTlsConfig(TlsSuite pTlsSuite) const277 const TlsConfiguration& SecureStorage::getTlsConfig(TlsSuite pTlsSuite) const
278 {
279 return pTlsSuite == TlsSuite::PSK ? mTlsConfigPsk : mTlsConfig;
280 }
281
282
getTlsConfigRemote(TlsSuite pTlsSuite) const283 const TlsConfiguration& SecureStorage::getTlsConfigRemote(TlsSuite pTlsSuite) const
284 {
285 return pTlsSuite == TlsSuite::PSK ? mTlsConfigRemotePsk : mTlsConfigRemote;
286 }
287
288
getMinimumStaticKeySize(QSsl::KeyAlgorithm pKeyAlgorithm) const289 int SecureStorage::getMinimumStaticKeySize(QSsl::KeyAlgorithm pKeyAlgorithm) const
290 {
291 if (!mMinStaticKeySizes.contains(pKeyAlgorithm))
292 {
293 qCWarning(securestorage) << "No minimum ephemeral key size specified, returning default";
294 }
295 return mMinStaticKeySizes.value(pKeyAlgorithm, 0);
296 }
297
298
getMinimumEphemeralKeySize(QSsl::KeyAlgorithm pKeyAlgorithm) const299 int SecureStorage::getMinimumEphemeralKeySize(QSsl::KeyAlgorithm pKeyAlgorithm) const
300 {
301 if (!mMinEphemeralKeySizes.contains(pKeyAlgorithm))
302 {
303 qCWarning(securestorage) << "No minimum ephemeral key size specified, returning default";
304 }
305 return mMinEphemeralKeySizes.value(pKeyAlgorithm, 0);
306 }
307
308
readJsonArray(const QJsonObject & pConfig,const QLatin1String pName) const309 QJsonArray SecureStorage::readJsonArray(const QJsonObject& pConfig, const QLatin1String pName) const
310 {
311 QJsonValue value = pConfig.value(pName);
312 if (!value.isArray())
313 {
314 qCCritical(securestorage) << "Expecting array for" << pName << "in SecureStorage";
315 return QJsonArray();
316 }
317 return value.toArray();
318 }
319
320
readGroup(const QJsonObject & pConfig,const QLatin1String pGroup,const QLatin1String pName) const321 QString SecureStorage::readGroup(const QJsonObject& pConfig, const QLatin1String pGroup, const QLatin1String pName) const
322 {
323 QJsonValue value = pConfig.value(pGroup);
324 if (!value.isObject())
325 {
326 qCCritical(securestorage) << "Expecting group for" << pGroup << "in SecureStorage";
327 return QString();
328 }
329
330 QJsonObject groupObject = value.toObject();
331 value = groupObject.value(pName);
332 if (!value.isString())
333 {
334 qCCritical(securestorage) << "Expecting string for" << pGroup << "/" << pName << "in SecureStorage";
335 return QString();
336 }
337
338 return value.toString();
339 }
340
341
readKeySizes(const QJsonObject & pConfig,const QLatin1String pKey) const342 QMap<QSsl::KeyAlgorithm, int> SecureStorage::readKeySizes(const QJsonObject& pConfig, const QLatin1String pKey) const
343 {
344 QMap<QSsl::KeyAlgorithm, int> keySizes;
345 const auto& object = pConfig.value(pKey).toObject();
346 if (!object.isEmpty())
347 {
348 const auto& keys = object.keys();
349 for (const QString& key : keys)
350 {
351 const auto value = object.value(key).toInt(0);
352 if (key == QLatin1String("Rsa"))
353 {
354 keySizes.insert(QSsl::KeyAlgorithm::Rsa, value);
355 }
356 else if (key == QLatin1String("Dsa"))
357 {
358 keySizes.insert(QSsl::KeyAlgorithm::Dsa, value);
359 }
360 #if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0))
361 else if (key == QLatin1String("Dh"))
362 {
363 keySizes.insert(QSsl::KeyAlgorithm::Dh, value);
364 }
365 #endif
366 else if (key == QLatin1String("Ec"))
367 {
368 keySizes.insert(QSsl::KeyAlgorithm::Ec, value);
369 }
370 else
371 {
372 qCCritical(securestorage) << "Ignore unknown key type" << key;
373 }
374 }
375 }
376 return keySizes;
377 }
378
379
readByteArrayList(const QJsonObject & pConfig,const QLatin1String pName) const380 QByteArrayList SecureStorage::readByteArrayList(const QJsonObject& pConfig, const QLatin1String pName) const
381 {
382 QJsonArray jsonArray = readJsonArray(pConfig, pName);
383 QByteArrayList byteArrayList;
384
385 for (int i = 0; i < jsonArray.size(); ++i)
386 {
387 QJsonValue certificate = jsonArray[i];
388 if (!certificate.isString())
389 {
390 qCCritical(securestorage) << "Expected hexstring in array[" << i << "] " << pName << " in SecureStorage";
391 continue;
392 }
393 byteArrayList += certificate.toString().toLatin1();
394 }
395
396 return byteArrayList;
397 }
398