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