AusweisApp2/src/core/TcToken.cpp

274 lines
6.0 KiB
C++

/*!
* \copyright Copyright (c) 2014-2018 Governikus GmbH & Co. KG, Germany
*/
#include "AppSettings.h"
#include "TcToken.h"
#include <QLoggingCategory>
#include <QRegularExpression>
#include <QXmlStreamReader>
using namespace governikus;
Q_DECLARE_LOGGING_CATEGORY(developermode)
TcToken::TcToken(const QByteArray& pData)
: mSchemaConform(false)
, mBinding()
, mPathSecurityProtocol()
, mPsk()
, mSessionIdentifier()
, mServerAddress()
, mCommunicationErrorAddress()
, mRefreshAddress()
{
parse(pData);
}
TcToken::~TcToken()
{
}
void TcToken::parse(const QByteArray& pData)
{
qDebug() << "Parsing TcToken:";
qDebug() << pData;
QXmlStreamReader reader(pData);
QString binding, pathSecurityProtocol, serverAddress, refreshAddress, communicationErrorAddress;
QByteArray psk, sessionIdentifier;
for (reader.readNext(); !reader.isEndDocument() && !reader.hasError(); reader.readNext())
{
if (reader.isStartElement())
{
QString currentName = reader.name().toString();
if (currentName == QLatin1String("Binding"))
{
binding = readElementValue(reader);
}
else if (currentName == QLatin1String("PathSecurity-Protocol"))
{
pathSecurityProtocol = readElementValue(reader);
}
else if (currentName == QLatin1String("PSK"))
{
psk = readElementValue(reader).toUtf8();
}
else if (currentName == QLatin1String("ServerAddress"))
{
serverAddress = readElementValue(reader);
}
else if (currentName == QLatin1String("SessionIdentifier"))
{
sessionIdentifier = readElementValue(reader).toUtf8();
}
else if (currentName == QLatin1String("CommunicationErrorAddress"))
{
communicationErrorAddress = readElementValue(reader);
}
else if (currentName == QLatin1String("RefreshAddress"))
{
refreshAddress = readElementValue(reader);
}
}
}
if (reader.hasError())
{
qDebug() << "XML error:" << reader.errorString();
}
else if (valuesAreSchemaConform(binding, pathSecurityProtocol, psk, sessionIdentifier, serverAddress, communicationErrorAddress, refreshAddress))
{
mSchemaConform = true;
mBinding = binding;
mPathSecurityProtocol = pathSecurityProtocol;
mPsk = psk;
mServerAddress.setUrl(serverAddress);
mSessionIdentifier = sessionIdentifier;
mCommunicationErrorAddress.setUrl(communicationErrorAddress);
mRefreshAddress.setUrl(refreshAddress);
}
reader.clear();
}
QString TcToken::readElementValue(QXmlStreamReader& pReader)
{
// helper method to distinguish between the cases,
// 1) where the element was present but empty (so the value is not null but empty) and
// 2) where the element was absent (so the value is null).
QString value = pReader.readElementText();
return value.isNull() ? QLatin1String("") : value.trimmed();
}
bool TcToken::valuesAreSchemaConform(const QString& pBinding,
const QString& pPathSecurityProtocol,
const QByteArray& pPsk,
const QByteArray& pSessionIdentifier,
const QString& pServerAddress,
const QString& pCommunicationErrorAddress,
const QString& pRefreshAddress) const
{
if (pBinding.isNull() || !isAnyUri(pBinding))
{
qCritical() << "Binding is no valid anyUri:" << pBinding;
return false;
}
if (!pPathSecurityProtocol.isNull() && !QUrl(pPathSecurityProtocol).isValid())
{
qCritical() << "PathSecurity-Protocol is no valid URI " << pPathSecurityProtocol;
}
if (pPsk.isNull())
{
// PSK is not needed for attached eID-Server model
qWarning() << "PSK is null";
}
if (pSessionIdentifier.isNull())
{
qWarning() << "SessionIdentifier is null";
}
if (pServerAddress.isNull() || !isAnyUri(pServerAddress))
{
qCritical() << "ServerAddress no valid anyUri:" << pServerAddress;
return false;
}
if (pRefreshAddress.isNull() || !isAnyUri(pRefreshAddress))
{
qCritical() << "RefreshAddress no valid anyUri:" << pRefreshAddress;
return false;
}
if (!isAnyUri(pCommunicationErrorAddress))
{
qCritical() << "CommunicationErrorAddress no valid anyUri:" << pCommunicationErrorAddress;
return false;
}
return true;
}
bool TcToken::isAnyUri(const QString& pCandidate) const
{
return pCandidate.isEmpty() || QUrl(pCandidate).isValid();
}
bool TcToken::isValid() const
{
if (!mSchemaConform)
{
return false;
}
if (mBinding != QLatin1String("urn:liberty:paos:2006-08"))
{
qCritical() << "Wrong binding: " << mBinding;
return false;
}
if (!mPathSecurityProtocol.isNull() && mPathSecurityProtocol != QLatin1String("urn:ietf:rfc:4279"))
{
qCritical() << "Wrong PathSecurity-Protocol: " << mPathSecurityProtocol;
return false;
}
if (mServerAddress.scheme() != QLatin1String("https"))
{
qCritical() << "ServerAddress no valid https url:" << mServerAddress;
return false;
}
if (mRefreshAddress.scheme() != QLatin1String("https"))
{
const QString errorInvalidUrl = QStringLiteral("RefreshAddress no valid https url: %1").arg(mRefreshAddress.toString());
if (AppSettings::getInstance().getGeneralSettings().isDeveloperMode())
{
qCCritical(developermode) << errorInvalidUrl;
}
else
{
qCritical() << errorInvalidUrl;
return false;
}
}
if (!mPsk.isNull() && !isHexBinary(QString::fromLatin1(mPsk)))
{
qCritical() << "PSK no valid hexBinary:" << mPsk;
return false;
}
return true;
}
bool TcToken::isHexBinary(const QString& pCandidate) const
{
static const QRegularExpression hexMatcher(QStringLiteral("^[0-9A-F]*$"), QRegularExpression::CaseInsensitiveOption);
return pCandidate.isEmpty() || (hexMatcher.match(pCandidate).hasMatch() && pCandidate.size() % 2 == 0);
}
const QByteArray& TcToken::getSessionIdentifier() const
{
return mSessionIdentifier;
}
const QUrl& TcToken::getServerAddress() const
{
return mServerAddress;
}
const QUrl& TcToken::getRefreshAddress() const
{
return mRefreshAddress;
}
const QUrl& TcToken::getCommunicationErrorAddress() const
{
return mCommunicationErrorAddress;
}
bool TcToken::usePsk() const
{
return !mPsk.isNull();
}
const QByteArray& TcToken::getPsk() const
{
return mPsk;
}
const QString& TcToken::getBinding() const
{
return mBinding;
}
void TcToken::clearPsk()
{
mPsk.clear();
}