diff --git a/messageviewer/src/dkim-verify/dkimchecksignaturejob.cpp b/messageviewer/src/dkim-verify/dkimchecksignaturejob.cpp index dae9f27a..74d3502e 100644 --- a/messageviewer/src/dkim-verify/dkimchecksignaturejob.cpp +++ b/messageviewer/src/dkim-verify/dkimchecksignaturejob.cpp @@ -1,581 +1,581 @@ /* Copyright (C) 2018-2019 Laurent Montel This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "dkimchecksignaturejob.h" #include "dkimdownloadkeyjob.h" #include "dkimmanagerkey.h" #include "dkiminfo.h" #include "dkimutil.h" #include "dkimkeyrecord.h" #include "messageviewer_dkimcheckerdebug.h" #include #include #include //see https://tools.ietf.org/html/rfc6376 using namespace MessageViewer; DKIMCheckSignatureJob::DKIMCheckSignatureJob(QObject *parent) : QObject(parent) { } DKIMCheckSignatureJob::~DKIMCheckSignatureJob() { } MessageViewer::DKIMCheckSignatureJob::CheckSignatureResult DKIMCheckSignatureJob::createCheckResult() { MessageViewer::DKIMCheckSignatureJob::CheckSignatureResult result; result.error = mError; result.warning = mWarning; result.status = mStatus; result.item = mMessageItem; return result; } QString DKIMCheckSignatureJob::bodyCanonizationResult() const { return mBodyCanonizationResult; } QString DKIMCheckSignatureJob::headerCanonizationResult() const { return mHeaderCanonizationResult; } void DKIMCheckSignatureJob::start() { if (!mMessage) { mStatus = MessageViewer::DKIMCheckSignatureJob::DKIMStatus::Invalid; Q_EMIT result(createCheckResult()); deleteLater(); return; } if (mMessageItem.isValid() && !mMessage) { if (mMessageItem.hasPayload()) { mMessage = mMessageItem.payload(); } } else if (mMessage) { //Nothing } else { qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "Item has not a message"; mStatus = MessageViewer::DKIMCheckSignatureJob::DKIMStatus::Invalid; Q_EMIT result(createCheckResult()); deleteLater(); return; } if (auto hrd = mMessage->headerByType("DKIM-Signature")) { mDkimValue = hrd->asUnicodeString(); } if (mDkimValue.isEmpty()) { mStatus = MessageViewer::DKIMCheckSignatureJob::DKIMStatus::EmailNotSigned; Q_EMIT result(createCheckResult()); deleteLater(); return; } if (!mDkimInfo.parseDKIM(mDkimValue)) { qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "Impossible to parse header" << mDkimValue; mStatus = MessageViewer::DKIMCheckSignatureJob::DKIMStatus::Invalid; Q_EMIT result(createCheckResult()); deleteLater(); return; } const MessageViewer::DKIMCheckSignatureJob::DKIMStatus status = checkSignature(mDkimInfo); if (status != MessageViewer::DKIMCheckSignatureJob::DKIMStatus::Valid) { mStatus = status; Q_EMIT result(createCheckResult()); deleteLater(); return; } //ComputeBodyHash now. switch (mDkimInfo.bodyCanonization()) { case MessageViewer::DKIMInfo::CanonicalizationType::Unknown: mError = MessageViewer::DKIMCheckSignatureJob::DKIMError::InvalidBodyCanonicalization; mStatus = MessageViewer::DKIMCheckSignatureJob::DKIMStatus::Invalid; Q_EMIT result(createCheckResult()); deleteLater(); return; case MessageViewer::DKIMInfo::CanonicalizationType::Simple: mBodyCanonizationResult = bodyCanonizationSimple(); break; case MessageViewer::DKIMInfo::CanonicalizationType::Relaxed: mBodyCanonizationResult = bodyCanonizationRelaxed(); break; } //qDebug() << " bodyCanonizationResult "<< mBodyCanonizationResult << " algorithm " << mDkimInfo.hashingAlgorithm() << mDkimInfo.bodyHash(); if (mDkimInfo.bodyLengthCount() != -1) { //Verify it. if (mDkimInfo.bodyLengthCount() < mBodyCanonizationResult.length()) { // length tag exceeds body size mError = MessageViewer::DKIMCheckSignatureJob::DKIMError::SignatureTooLarge; mStatus = MessageViewer::DKIMCheckSignatureJob::DKIMStatus::Invalid; Q_EMIT result(createCheckResult()); deleteLater(); return; } else if (mDkimInfo.bodyLengthCount() > mBodyCanonizationResult.length()) { mWarning = MessageViewer::DKIMCheckSignatureJob::DKIMWarning::SignatureTooSmall; } // truncated body to the length specified in the "l=" tag mBodyCanonizationResult = mBodyCanonizationResult.left(mDkimInfo.bodyLengthCount()); } QByteArray resultHash; if (mDkimInfo.hashingAlgorithm() == QLatin1String("sha1")) { resultHash = MessageViewer::DKIMUtil::generateHash(mBodyCanonizationResult.toLatin1(), QCryptographicHash::Sha1); } else if (mDkimInfo.hashingAlgorithm() == QLatin1String("sha256")) { resultHash = MessageViewer::DKIMUtil::generateHash(mBodyCanonizationResult.toLatin1(), QCryptographicHash::Sha256); } else { mError = MessageViewer::DKIMCheckSignatureJob::DKIMError::InsupportedHashAlgorithm; mStatus = MessageViewer::DKIMCheckSignatureJob::DKIMStatus::Invalid; Q_EMIT result(createCheckResult()); deleteLater(); return; } // compare body hash qDebug() << "resultHash " << resultHash << "mDkimInfo.bodyHash()" << mDkimInfo.bodyHash(); if (resultHash != mDkimInfo.bodyHash().toLatin1()) { qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << " Corrupted body hash"; mError = MessageViewer::DKIMCheckSignatureJob::DKIMError::CorruptedBodyHash; mStatus = MessageViewer::DKIMCheckSignatureJob::DKIMStatus::Invalid; Q_EMIT result(createCheckResult()); deleteLater(); return; } //Compute Hash Header switch (mDkimInfo.headerCanonization()) { case MessageViewer::DKIMInfo::CanonicalizationType::Unknown: mError = MessageViewer::DKIMCheckSignatureJob::DKIMError::InvalidHeaderCanonicalization; mStatus = MessageViewer::DKIMCheckSignatureJob::DKIMStatus::Invalid; Q_EMIT result(createCheckResult()); deleteLater(); return; case MessageViewer::DKIMInfo::CanonicalizationType::Simple: mHeaderCanonizationResult = headerCanonizationSimple(); break; case MessageViewer::DKIMInfo::CanonicalizationType::Relaxed: mHeaderCanonizationResult = headerCanonizationRelaxed(); break; } // In hash step 2, the Signer/Verifier MUST pass the following to the // hash algorithm in the indicated order. // 1. The header fields specified by the "h=" tag, in the order // specified in that tag, and canonicalized using the header // canonicalization algorithm specified in the "c=" tag. Each // header field MUST be terminated with a single CRLF. // 2. The DKIM-Signature header field that exists (verifying) or will // be inserted (signing) in the message, with the value of the "b=" // tag (including all surrounding whitespace) deleted (i.e., treated // as the empty string), canonicalized using the header // canonicalization algorithm specified in the "c=" tag, and without // a trailing CRLF. // add DKIM-Signature header to the hash input // with the value of the "b=" tag (including all surrounding whitespace) deleted qDebug() << " headerCanonizationResult" << mHeaderCanonizationResult; //Add dkim-signature as lowercase QString dkimValue = mDkimValue; dkimValue = dkimValue.left(dkimValue.indexOf(QLatin1String("b=")) + 2); switch (mDkimInfo.headerCanonization()) { case MessageViewer::DKIMInfo::CanonicalizationType::Unknown: return; case MessageViewer::DKIMInfo::CanonicalizationType::Simple: mHeaderCanonizationResult += QLatin1String("\r\n") + MessageViewer::DKIMUtil::headerCanonizationSimple(QLatin1String("dkim-signature"), dkimValue); break; case MessageViewer::DKIMInfo::CanonicalizationType::Relaxed: mHeaderCanonizationResult += QLatin1String("\r\n") + MessageViewer::DKIMUtil::headerCanonizationRelaxed(QLatin1String("dkim-signature"), dkimValue); break; } qDebug() << " headerCanonizationResult after " << mHeaderCanonizationResult; if (mSaveKey) { const QString keyValue = MessageViewer::DKIMManagerKey::self()->keyValue(mDkimInfo.selector(), mDkimInfo.domain()); if (keyValue.isEmpty()) { downloadKey(mDkimInfo); } else { parseDKIMKeyRecord(keyValue, mDkimInfo.domain(), mDkimInfo.selector(), false); } } else { downloadKey(mDkimInfo); } } QString DKIMCheckSignatureJob::bodyCanonizationSimple() const { /* * canonicalize the body using the simple algorithm * specified in Section 3.4.3 of RFC 6376 */ // The "simple" body canonicalization algorithm ignores all empty lines // at the end of the message body. An empty line is a line of zero // length after removal of the line terminator. If there is no body or // no trailing CRLF on the message body, a CRLF is added. It makes no // other changes to the message body. In more formal terms, the // "simple" body canonicalization algorithm converts "*CRLF" at the end // of the body to a single "CRLF". // Note that a completely empty or missing body is canonicalized as a // single "CRLF"; that is, the canonicalized length will be 2 octets. return MessageViewer::DKIMUtil::bodyCanonizationSimple(QString::fromLatin1(mMessage->body())); } QString DKIMCheckSignatureJob::bodyCanonizationRelaxed() const { /* * canonicalize the body using the relaxed algorithm * specified in Section 3.4.4 of RFC 6376 */ /* a. Reduce whitespace: * Ignore all whitespace at the end of lines. Implementations MUST NOT remove the CRLF at the end of the line. * Reduce all sequences of WSP within a line to a single SP character. b. Ignore all empty lines at the end of the message body. "Empty line" is defined in Section 3.4.3. If the body is non-empty but does not end with a CRLF, a CRLF is added. (For email, this is only possible when using extensions to SMTP or non-SMTP transport mechanisms.) */ return MessageViewer::DKIMUtil::bodyCanonizationRelaxed(QString::fromUtf8(mMessage->body())); } QString DKIMCheckSignatureJob::headerCanonizationSimple() const { QString headers; for (const QString &header : mDkimInfo.listSignedHeader()) { if (auto hrd = mMessage->headerByType(header.toLatin1().constData())) { headers += MessageViewer::DKIMUtil::headerCanonizationSimple(header, hrd->asUnicodeString()); } } return headers; } QString DKIMCheckSignatureJob::headerCanonizationRelaxed() const { // The "relaxed" header canonicalization algorithm MUST apply the // following steps in order: // o Convert all header field names (not the header field values) to // lowercase. For example, convert "SUBJect: AbC" to "subject: AbC". // o Unfold all header field continuation lines as described in // [RFC5322]; in particular, lines with terminators embedded in // continued header field values (that is, CRLF sequences followed by // WSP) MUST be interpreted without the CRLF. Implementations MUST // NOT remove the CRLF at the end of the header field value. // o Convert all sequences of one or more WSP characters to a single SP // character. WSP characters here include those before and after a // line folding boundary. // o Delete all WSP characters at the end of each unfolded header field // value. // o Delete any WSP characters remaining before and after the colon // separating the header field name from the header field value. The // colon separator MUST be retained. QString headers; QStringList headerAlreadyAdded; //Add support for multi headers for (const QString &header : mDkimInfo.listSignedHeader()) { if (headerAlreadyAdded.contains(header)) { continue; } if (auto hrd = mMessage->headerByType(header.toLatin1().constData())) { headerAlreadyAdded << header; if (!headers.isEmpty()) { headers += QLatin1String("\r\n"); } headers += MessageViewer::DKIMUtil::headerCanonizationRelaxed(header, hrd->asUnicodeString()); } } return headers; } void DKIMCheckSignatureJob::downloadKey(const DKIMInfo &info) { DKIMDownloadKeyJob *job = new DKIMDownloadKeyJob(this); job->setDomainName(info.domain()); job->setSelectorName(info.selector()); connect(job, &DKIMDownloadKeyJob::error, this, [this](const QString &errorString) { qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "Impossible to start downloadkey: error returned: " << errorString; deleteLater(); }); connect(job, &DKIMDownloadKeyJob::success, this, &DKIMCheckSignatureJob::slotDownloadKeyDone); if (!job->start()) { qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "Impossible to start downloadkey"; deleteLater(); } } void DKIMCheckSignatureJob::slotDownloadKeyDone(const QList &lst, const QString &domain, const QString &selector) { QByteArray ba; if (lst.count() != 1) { for (const QByteArray &b : lst) { ba += b; } qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "Key result has more that 1 element" << lst; } else { ba = lst.at(0); } parseDKIMKeyRecord(QString::fromLocal8Bit(ba), domain, selector, true); } void DKIMCheckSignatureJob::parseDKIMKeyRecord(const QString &str, const QString &domain, const QString &selector, bool storeKeyValue) { qDebug() << "void DKIMCheckSignatureJob::parseDKIMKeyRecord(const QString &str, const QString &domain, const QString &selector, bool storeKeyValue) " << str; if (!mDkimKeyRecord.parseKey(str)) { qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "Impossible to parse key record " << str; mStatus = MessageViewer::DKIMCheckSignatureJob::DKIMStatus::Invalid; Q_EMIT result(createCheckResult()); deleteLater(); return; } if (mDkimKeyRecord.keyType() != QLatin1String("rsa")) { qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "mDkimKeyRecord key type is unknown " << mDkimKeyRecord.keyType() << " str " << str; mStatus = MessageViewer::DKIMCheckSignatureJob::DKIMStatus::Invalid; Q_EMIT result(createCheckResult()); deleteLater(); return; } // if s flag is set in DKIM key record // AUID must be from the same domain as SDID (and not a subdomain) if (mDkimKeyRecord.flags().contains(QLatin1String("s"))) { // s Any DKIM-Signature header fields using the "i=" tag MUST have // the same domain value on the right-hand side of the "@" in the // "i=" tag and the value of the "d=" tag. That is, the "i=" // domain MUST NOT be a subdomain of "d=". Use of this flag is // RECOMMENDED unless subdomaining is required. if (mDkimInfo.iDomain() != mDkimInfo.domain()) { mStatus = MessageViewer::DKIMCheckSignatureJob::DKIMStatus::Invalid; mError = MessageViewer::DKIMCheckSignatureJob::DKIMError::DomainI; Q_EMIT result(createCheckResult()); deleteLater(); return; } } // check that the testing flag is not set if (mDkimKeyRecord.flags().contains(QLatin1String("y"))) { - // s Any DKIM-Signature header fields using the "i=" tag MUST have - // the same domain value on the right-hand side of the "@" in the - // "i=" tag and the value of the "d=" tag. That is, the "i=" - // domain MUST NOT be a subdomain of "d=". Use of this flag is - // RECOMMENDED unless subdomaining is required. - //TODO + qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "Testing mode!"; + mError = MessageViewer::DKIMCheckSignatureJob::DKIMError::TestKeyMode; + mStatus = MessageViewer::DKIMCheckSignatureJob::DKIMStatus::Invalid; + Q_EMIT result(createCheckResult()); + deleteLater(); + return; } if (mDkimKeyRecord.publicKey().isEmpty()) { // empty value means that this public key has been revoked qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "mDkimKeyRecord public key is empty. It was revoked "; mError = MessageViewer::DKIMCheckSignatureJob::DKIMError::PublicKeyWasRevoked; mStatus = MessageViewer::DKIMCheckSignatureJob::DKIMStatus::Invalid; Q_EMIT result(createCheckResult()); deleteLater(); return; } if (storeKeyValue) { Q_EMIT storeKey(str, domain, selector); } verifyRSASignature(); } void DKIMCheckSignatureJob::verifyRSASignature() { QCA::ConvertResult conversionResult; qDebug() << "mDkimKeyRecord.publicKey() " < currentDate) { mWarning = DKIMCheckSignatureJob::DKIMWarning::SignatureCreatedInFuture; } if (info.signature().isEmpty()) { qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "Signature doesn't exist"; mError = MessageViewer::DKIMCheckSignatureJob::DKIMError::MissingSignature; return MessageViewer::DKIMCheckSignatureJob::DKIMStatus::Invalid; } if (!info.listSignedHeader().contains(QLatin1String("from"), Qt::CaseInsensitive)) { qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "From is not include in headers list"; mError = MessageViewer::DKIMCheckSignatureJob::DKIMError::MissingFrom; return MessageViewer::DKIMCheckSignatureJob::DKIMStatus::Invalid; } if (info.domain().isEmpty()) { qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "Domain is not defined."; mError = MessageViewer::DKIMCheckSignatureJob::DKIMError::DomainNotExist; return MessageViewer::DKIMCheckSignatureJob::DKIMStatus::Invalid; } if (info.query() != QLatin1String("dns/txt")) { qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "Query is incorrect: " << info.query(); mError = MessageViewer::DKIMCheckSignatureJob::DKIMError::InvalidQueryMethod; return MessageViewer::DKIMCheckSignatureJob::DKIMStatus::Invalid; } if (info.hashingAlgorithm().isEmpty()) { qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "body header algorithm is empty"; mError = MessageViewer::DKIMCheckSignatureJob::DKIMError::InvalidBodyHashAlgorithm; return MessageViewer::DKIMCheckSignatureJob::DKIMStatus::Invalid; } if (info.signingAlgorithm().isEmpty()) { qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "signature algorithm is empty"; mError = MessageViewer::DKIMCheckSignatureJob::DKIMError::InvalidSignAlgorithm; return MessageViewer::DKIMCheckSignatureJob::DKIMStatus::Invalid; } if (info.hashingAlgorithm() == QLatin1String("sha1")) { qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "hash algorithm is not secure sha1"; mWarning = MessageViewer::DKIMCheckSignatureJob::DKIMWarning::HashAlgorithmUnsafe; } //Add more test //TODO check if info is valid return MessageViewer::DKIMCheckSignatureJob::DKIMStatus::Valid; } DKIMCheckSignatureJob::DKIMError DKIMCheckSignatureJob::error() const { return mError; } DKIMCheckSignatureJob::DKIMStatus DKIMCheckSignatureJob::status() const { return mStatus; } void DKIMCheckSignatureJob::setStatus(const DKIMCheckSignatureJob::DKIMStatus &status) { mStatus = status; } QString DKIMCheckSignatureJob::dkimValue() const { return mDkimValue; } diff --git a/messageviewer/src/dkim-verify/dkimchecksignaturejob.h b/messageviewer/src/dkim-verify/dkimchecksignaturejob.h index bacf0feb..a6c2162d 100644 --- a/messageviewer/src/dkim-verify/dkimchecksignaturejob.h +++ b/messageviewer/src/dkim-verify/dkimchecksignaturejob.h @@ -1,156 +1,155 @@ /* Copyright (C) 2018-2019 Laurent Montel This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef DKIMCHECKSIGNATUREJOB_H #define DKIMCHECKSIGNATUREJOB_H #include #include "messageviewer_export.h" #include #include #include #include namespace MessageViewer { class MESSAGEVIEWER_EXPORT DKIMCheckSignatureJob : public QObject { Q_OBJECT public: enum class DKIMStatus : int { Unknown = 0, Valid = 1, Invalid = 2, EmailNotSigned = 3 }; Q_ENUM(DKIMStatus) enum class DKIMError : int { Any = 0, CorruptedBodyHash = 1, DomainNotExist = 2, MissingFrom = 3, MissingSignature = 4, InvalidQueryMethod = 5, InvalidHeaderCanonicalization = 6, InvalidBodyCanonicalization = 7, InvalidBodyHashAlgorithm = 8, InvalidSignAlgorithm = 9, PublicKeyWasRevoked = 10, SignatureTooLarge = 11, InsupportedHashAlgorithm = 12, PublicKeyTooSmall = 13, ImpossibleToVerifySignature = 14, DomainI = 15, - - //TODO add more + TestKeyMode = 16, }; Q_ENUM(DKIMError) enum class DKIMWarning : int { Any = 0, SignatureExpired = 1, SignatureCreatedInFuture = 2, SignatureTooSmall = 3, HashAlgorithmUnsafe = 4, }; Q_ENUM(DKIMWarning) struct CheckSignatureResult { bool isValid() const { return status != DKIMCheckSignatureJob::DKIMStatus::Unknown; } Q_REQUIRED_RESULT bool operator==(const CheckSignatureResult &other) const { return error == other.error && warning == other.warning && status == other.status && item == other.item; } Q_REQUIRED_RESULT bool operator!=(const CheckSignatureResult &other) const { return !CheckSignatureResult::operator==(other); } DKIMCheckSignatureJob::DKIMError error = DKIMCheckSignatureJob::DKIMError::Any; DKIMCheckSignatureJob::DKIMWarning warning = DKIMCheckSignatureJob::DKIMWarning::Any; DKIMCheckSignatureJob::DKIMStatus status = DKIMCheckSignatureJob::DKIMStatus::Unknown; Akonadi::Item item; }; explicit DKIMCheckSignatureJob(QObject *parent = nullptr); ~DKIMCheckSignatureJob(); void start(); Q_REQUIRED_RESULT QString dkimValue() const; Q_REQUIRED_RESULT DKIMCheckSignatureJob::DKIMStatus status() const; void setStatus(const DKIMCheckSignatureJob::DKIMStatus &status); Q_REQUIRED_RESULT MessageViewer::DKIMCheckSignatureJob::DKIMStatus checkSignature(const MessageViewer::DKIMInfo &info); Q_REQUIRED_RESULT DKIMCheckSignatureJob::DKIMError error() const; Q_REQUIRED_RESULT KMime::Message::Ptr message() const; void setMessage(const KMime::Message::Ptr &message); Q_REQUIRED_RESULT DKIMCheckSignatureJob::DKIMWarning warning() const; void setWarning(const DKIMWarning &warning); Q_REQUIRED_RESULT QString headerCanonizationResult() const; Q_REQUIRED_RESULT QString bodyCanonizationResult() const; Q_REQUIRED_RESULT Akonadi::Item item() const; void setItem(const Akonadi::Item &item); Q_REQUIRED_RESULT bool saveKey() const; void setSaveKey(bool saveKey); Q_SIGNALS: void result(const MessageViewer::DKIMCheckSignatureJob::CheckSignatureResult &checkResult); void storeKey(const QString &key, const QString &domain, const QString &selector); private: void downloadKey(const DKIMInfo &info); void slotDownloadKeyDone(const QList &lst, const QString &domain, const QString &selector); void parseDKIMKeyRecord(const QString &str, const QString &domain, const QString &selector, bool storeKeyValue = true); Q_REQUIRED_RESULT QString headerCanonizationSimple() const; Q_REQUIRED_RESULT QString headerCanonizationRelaxed() const; Q_REQUIRED_RESULT QString bodyCanonizationRelaxed() const; Q_REQUIRED_RESULT QString bodyCanonizationSimple() const; Q_REQUIRED_RESULT MessageViewer::DKIMCheckSignatureJob::CheckSignatureResult createCheckResult(); void verifyRSASignature(); KMime::Message::Ptr mMessage; Akonadi::Item mMessageItem; DKIMInfo mDkimInfo; DKIMKeyRecord mDkimKeyRecord; QString mDkimValue; QString mHeaderCanonizationResult; QString mBodyCanonizationResult; DKIMCheckSignatureJob::DKIMError mError = DKIMCheckSignatureJob::DKIMError::Any; DKIMCheckSignatureJob::DKIMWarning mWarning = DKIMCheckSignatureJob::DKIMWarning::Any; DKIMCheckSignatureJob::DKIMStatus mStatus = DKIMCheckSignatureJob::DKIMStatus::Unknown; bool mSaveKey = false; }; } #endif // DKIMCHECKSIGNATUREJOB_H diff --git a/messageviewer/src/dkim-verify/dkimwidgetinfo.cpp b/messageviewer/src/dkim-verify/dkimwidgetinfo.cpp index 2e185587..c952ec2f 100644 --- a/messageviewer/src/dkim-verify/dkimwidgetinfo.cpp +++ b/messageviewer/src/dkim-verify/dkimwidgetinfo.cpp @@ -1,147 +1,150 @@ /* Copyright (C) 2019 Laurent Montel This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "dkimwidgetinfo.h" #include "dkimmanager.h" #include #include #include using namespace MessageViewer; DKIMWidgetInfo::DKIMWidgetInfo(QWidget *parent) : QWidget(parent) { QHBoxLayout *mainLayout = new QHBoxLayout(this); mainLayout->setObjectName(QStringLiteral("mainLayout")); mainLayout->setContentsMargins(0, 0, 0, 0); mLabel = new QLabel(this); mLabel->setObjectName(QStringLiteral("label")); mainLayout->addWidget(mLabel); connect(DKIMManager::self(), &DKIMManager::result, this, &DKIMWidgetInfo::setResult); connect(DKIMManager::self(), &DKIMManager::clearInfo, this, &DKIMWidgetInfo::clear); } DKIMWidgetInfo::~DKIMWidgetInfo() { } void DKIMWidgetInfo::setResult(const DKIMCheckSignatureJob::CheckSignatureResult &checkResult) { if (mResult != checkResult) { mResult = checkResult; updateInfo(); } } void DKIMWidgetInfo::clear() { mLabel->clear(); } void DKIMWidgetInfo::updateInfo() { switch (mResult.status) { case DKIMCheckSignatureJob::DKIMStatus::Unknown: mLabel->setText(i18n("Unknown")); break; case DKIMCheckSignatureJob::DKIMStatus::Valid: mLabel->setText(i18n("KDIM: valid")); break; case DKIMCheckSignatureJob::DKIMStatus::Invalid: mLabel->setText(i18n("KDIM: invalid")); break; case DKIMCheckSignatureJob::DKIMStatus::EmailNotSigned: mLabel->setText(i18n("KDIM: Unsigned")); break; } updateToolTip(); } void DKIMWidgetInfo::updateToolTip() { QString tooltip; if (mResult.status == DKIMCheckSignatureJob::DKIMStatus::Invalid) { switch (mResult.error) { case DKIMCheckSignatureJob::DKIMError::Any: break; case DKIMCheckSignatureJob::DKIMError::CorruptedBodyHash: tooltip = i18n("Body Hash was corrupted."); break; case DKIMCheckSignatureJob::DKIMError::DomainNotExist: tooltip = i18n("The domain doesn't exist."); break; case DKIMCheckSignatureJob::DKIMError::MissingFrom: tooltip = i18n("Missing header From."); break; case DKIMCheckSignatureJob::DKIMError::MissingSignature: tooltip = i18n("Missing signature."); break; case DKIMCheckSignatureJob::DKIMError::InvalidQueryMethod: tooltip = i18n("Invalid query method."); break; case DKIMCheckSignatureJob::DKIMError::InvalidHeaderCanonicalization: tooltip = i18n("Invalid header^ canonolization."); break; case DKIMCheckSignatureJob::DKIMError::InvalidBodyCanonicalization: tooltip = i18n("Invalid body canonolization."); break; case DKIMCheckSignatureJob::DKIMError::InvalidBodyHashAlgorithm: tooltip = i18n("Unknown Body Hash Algorithm."); break; case DKIMCheckSignatureJob::DKIMError::InvalidSignAlgorithm: tooltip = i18n("Signature algorithm is invalid."); break; case DKIMCheckSignatureJob::DKIMError::PublicKeyWasRevoked: tooltip = i18n("The public key was revoked."); break; case DKIMCheckSignatureJob::DKIMError::SignatureTooLarge: tooltip = i18n("Signature is too large."); break; case DKIMCheckSignatureJob::DKIMError::InsupportedHashAlgorithm: tooltip = i18n("Hash Algorithm is unsupported."); break; case DKIMCheckSignatureJob::DKIMError::PublicKeyTooSmall: break; case DKIMCheckSignatureJob::DKIMError::ImpossibleToVerifySignature: tooltip = i18n("Impossible to verify signature."); break; case DKIMCheckSignatureJob::DKIMError::DomainI: - + tooltip = i18n("AUID must be in the same domain as SDID (s-flag set in key record)."); + break; + case DKIMCheckSignatureJob::DKIMError::TestKeyMode: + tooltip = i18n("The signing domain is only testing DKIM."); break; } } switch (mResult.warning) { case DKIMCheckSignatureJob::DKIMWarning::Any: break; case DKIMCheckSignatureJob::DKIMWarning::SignatureExpired: tooltip = i18n("Signature expired"); break; case DKIMCheckSignatureJob::DKIMWarning::SignatureCreatedInFuture: tooltip = i18n("Signature created in the future"); break; case DKIMCheckSignatureJob::DKIMWarning::SignatureTooSmall: tooltip = i18n("Signature too small"); break; case DKIMCheckSignatureJob::DKIMWarning::HashAlgorithmUnsafe: tooltip = i18n("Hash Algorithm unsafe (sha1)"); break; } mLabel->setToolTip(tooltip); }