File indexing completed on 2025-03-09 04:54:29
0001 /* 0002 SPDX-FileCopyrightText: 2018-2024 Laurent Montel <montel@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "dkiminfo.h" 0008 #include "dkimutil.h" 0009 #include "messageviewer_dkimcheckerdebug.h" 0010 0011 using namespace MessageViewer; 0012 0013 DKIMInfo::DKIMInfo() = default; 0014 0015 bool DKIMInfo::parseDKIM(const QString &header) 0016 { 0017 if (header.isEmpty()) { 0018 qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "Error: trying to parse empty header"; 0019 return false; 0020 } 0021 QString newHeaders = header; 0022 newHeaders.replace(QLatin1StringView("; "), QLatin1StringView(";")); 0023 const QStringList items = newHeaders.split(QLatin1Char(';'), Qt::SkipEmptyParts); 0024 bool foundCanonizations = false; 0025 for (int i = 0; i < items.count(); ++i) { 0026 const QString elem = items.at(i).trimmed(); 0027 if (elem.startsWith(QLatin1StringView("v="))) { 0028 mVersion = QStringView(elem).right(elem.length() - 2).toInt(); 0029 if (mVersion != 1) { 0030 qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "Version is not correct " << mVersion; 0031 } 0032 } else if (elem.startsWith(QLatin1StringView("a="))) { 0033 // Parse it as "algorithm.signature-algorithm.hash 0034 parseAlgorithm(elem.right(elem.length() - 2)); 0035 } else if (elem.startsWith(QLatin1StringView("t="))) { 0036 mSignatureTimeStamp = elem.right(elem.length() - 2).toLong(); 0037 } else if (elem.startsWith(QLatin1StringView("c="))) { 0038 // Parse header/body canonicalization (example c=relaxed/simple) only relaxed and simple. 0039 parseCanonicalization(elem.right(elem.length() - 2)); 0040 foundCanonizations = true; 0041 } else if (elem.startsWith(QLatin1StringView("bh="))) { 0042 mBodyHash = elem.right(elem.length() - 3).remove(QLatin1Char(' ')); 0043 } else if (elem.startsWith(QLatin1StringView("l="))) { 0044 mBodyLengthCount = QStringView(elem).right(elem.length() - 2).toInt(); 0045 } else if (elem.startsWith(QLatin1StringView("i="))) { 0046 mAgentOrUserIdentifier = elem.right(elem.length() - 2); 0047 } else if (elem.startsWith(QLatin1StringView("q="))) { 0048 mQuery = elem.right(elem.length() - 2); 0049 if (mQuery != QLatin1StringView("dns/txt")) { 0050 qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "Query is not correct and not supported " << mQuery; 0051 } 0052 } else if (elem.startsWith(QLatin1StringView("d="))) { 0053 mDomain = elem.right(elem.length() - 2).trimmed(); 0054 } else if (elem.startsWith(QLatin1StringView("s="))) { 0055 mSelector = elem.right(elem.length() - 2).trimmed(); 0056 } else if (elem.startsWith(QLatin1StringView("b="))) { 0057 mSignature = elem.right(elem.length() - 2); 0058 } else if (elem.startsWith(QLatin1StringView("h="))) { 0059 const QString str = MessageViewer::DKIMUtil::cleanString(elem.right(elem.length() - 2)); 0060 mListSignedHeader = str.split(QLatin1Char(':')); 0061 } else if (elem.startsWith(QLatin1StringView("x="))) { 0062 mExpireTime = elem.right(elem.length() - 2).toLong(); 0063 } else if (elem.startsWith(QLatin1StringView("z="))) { 0064 mCopiedHeaderField = elem.right(elem.length() - 2).split(QLatin1Char(':')); 0065 } else { 0066 qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << " Unknown element type" << elem << " : items : " << items; 0067 } 0068 } 0069 if (!foundCanonizations) { // Default 0070 mHeaderCanonization = Simple; 0071 mBodyCanonization = Simple; 0072 } 0073 if (mVersion == -1) { 0074 mVersion = 1; 0075 } 0076 if (mQuery.isEmpty()) { 0077 mQuery = QStringLiteral("dns/txt"); 0078 } 0079 if (mAgentOrUserIdentifier.isEmpty()) { 0080 mAgentOrUserIdentifier = QLatin1Char('@') + mDomain; 0081 mIDomain = mDomain; 0082 } else { 0083 const QStringList lst = mAgentOrUserIdentifier.split(QLatin1Char('@')); 0084 if (lst.count() == 2) { 0085 if (mAgentOrUserIdentifier.isEmpty()) { 0086 mAgentOrUserIdentifier = QLatin1Char('@') + mDomain; 0087 } 0088 mIDomain = lst.at(1); 0089 } 0090 } 0091 return true; 0092 } 0093 0094 void DKIMInfo::parseAlgorithm(const QString &str) 0095 { 0096 // currently only "rsa-sha1" or "rsa-sha256" 0097 const QStringList lst = str.split(QLatin1Char('-')); 0098 if (lst.count() != 2) { 0099 qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "algorithm is invalid " << str; 0100 // Error 0101 } else { 0102 mSigningAlgorithm = lst.at(0); 0103 const QString hashStr = lst.at(1); 0104 if (hashStr == QLatin1StringView("sha1")) { 0105 mHashingAlgorithm = HashingAlgorithmType::Sha1; 0106 } else if (hashStr == QLatin1StringView("sha256")) { 0107 mHashingAlgorithm = HashingAlgorithmType::Sha256; 0108 } else { 0109 mHashingAlgorithm = HashingAlgorithmType::Unknown; 0110 } 0111 } 0112 } 0113 0114 QString DKIMInfo::iDomain() const 0115 { 0116 return mIDomain; 0117 } 0118 0119 void DKIMInfo::setIDomain(const QString &iDomain) 0120 { 0121 mIDomain = iDomain; 0122 } 0123 0124 void DKIMInfo::parseCanonicalization(const QString &str) 0125 { 0126 if (!str.isEmpty()) { 0127 const QStringList canonicalization = str.split(QLatin1Char('/')); 0128 // qDebug() << " canonicalization "<< canonicalization; 0129 if (canonicalization.count() >= 1) { 0130 if (canonicalization.at(0) == QLatin1StringView("relaxed")) { 0131 mHeaderCanonization = DKIMInfo::Relaxed; 0132 } else if (canonicalization.at(0) == QLatin1StringView("simple")) { 0133 mHeaderCanonization = DKIMInfo::Simple; 0134 } else { 0135 qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "canonicalization for header unknown " << canonicalization.at(0); 0136 mHeaderCanonization = DKIMInfo::Unknown; 0137 return; 0138 } 0139 if (canonicalization.count() == 1) { 0140 mBodyCanonization = DKIMInfo::Simple; 0141 } else if (canonicalization.count() == 2) { 0142 if (canonicalization.at(1) == QLatin1StringView("relaxed")) { 0143 mBodyCanonization = DKIMInfo::Relaxed; 0144 } else if (canonicalization.at(1) == QLatin1StringView("simple")) { 0145 mBodyCanonization = DKIMInfo::Simple; 0146 } else { 0147 qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << "canonicalization for body unknown " << canonicalization.at(1); 0148 mBodyCanonization = DKIMInfo::Unknown; 0149 return; 0150 } 0151 } else { 0152 qCWarning(MESSAGEVIEWER_DKIMCHECKER_LOG) << " Problem during parsing canonicalization " << str; 0153 mHeaderCanonization = DKIMInfo::Unknown; 0154 mBodyCanonization = DKIMInfo::Unknown; 0155 } 0156 } 0157 } 0158 } 0159 0160 QStringList DKIMInfo::copiedHeaderField() const 0161 { 0162 return mCopiedHeaderField; 0163 } 0164 0165 void DKIMInfo::setCopiedHeaderField(const QStringList &copiedHeaderField) 0166 { 0167 mCopiedHeaderField = copiedHeaderField; 0168 } 0169 0170 DKIMInfo::CanonicalizationType DKIMInfo::bodyCanonization() const 0171 { 0172 return mBodyCanonization; 0173 } 0174 0175 void DKIMInfo::setBodyCanonization(CanonicalizationType bodyCanonization) 0176 { 0177 mBodyCanonization = bodyCanonization; 0178 } 0179 0180 bool DKIMInfo::operator==(const DKIMInfo &other) const 0181 { 0182 return mVersion == other.version() && mHashingAlgorithm == other.hashingAlgorithm() && mSigningAlgorithm == other.signingAlgorithm() 0183 && mDomain == other.domain() && mSelector == other.selector() && mBodyHash == other.bodyHash() && mSignatureTimeStamp == other.signatureTimeStamp() 0184 && mExpireTime == other.expireTime() && mQuery == other.query() && mSignature == other.signature() 0185 && mAgentOrUserIdentifier == other.agentOrUserIdentifier() && mBodyLengthCount == other.bodyLengthCount() 0186 && mListSignedHeader == other.listSignedHeader() && mHeaderCanonization == other.headerCanonization() && mBodyCanonization == other.bodyCanonization() 0187 && mIDomain == other.iDomain(); 0188 } 0189 0190 DKIMInfo::CanonicalizationType DKIMInfo::headerCanonization() const 0191 { 0192 return mHeaderCanonization; 0193 } 0194 0195 void DKIMInfo::setHeaderCanonization(CanonicalizationType headerCanonization) 0196 { 0197 mHeaderCanonization = headerCanonization; 0198 } 0199 0200 int DKIMInfo::version() const 0201 { 0202 return mVersion; 0203 } 0204 0205 void DKIMInfo::setVersion(int version) 0206 { 0207 mVersion = version; 0208 } 0209 0210 DKIMInfo::HashingAlgorithmType DKIMInfo::hashingAlgorithm() const 0211 { 0212 return mHashingAlgorithm; 0213 } 0214 0215 void DKIMInfo::setHashingAlgorithm(DKIMInfo::HashingAlgorithmType hashingAlgorithm) 0216 { 0217 mHashingAlgorithm = hashingAlgorithm; 0218 } 0219 0220 QString DKIMInfo::domain() const 0221 { 0222 return mDomain; 0223 } 0224 0225 void DKIMInfo::setDomain(const QString &domain) 0226 { 0227 mDomain = domain; 0228 } 0229 0230 QString DKIMInfo::selector() const 0231 { 0232 return mSelector; 0233 } 0234 0235 void DKIMInfo::setSelector(const QString &selector) 0236 { 0237 mSelector = selector; 0238 } 0239 0240 QString DKIMInfo::bodyHash() const 0241 { 0242 return mBodyHash; 0243 } 0244 0245 void DKIMInfo::setBodyHash(const QString &bodyHash) 0246 { 0247 mBodyHash = bodyHash; 0248 } 0249 0250 bool DKIMInfo::isValid() const 0251 { 0252 if (mBodyCanonization == DKIMInfo::Unknown || mHeaderCanonization == DKIMInfo::Unknown) { 0253 return false; 0254 } 0255 0256 return !mSelector.isEmpty() && !mDomain.isEmpty() && !mBodyHash.isEmpty() 0257 && ((mHashingAlgorithm == HashingAlgorithmType::Sha1) || mHashingAlgorithm == HashingAlgorithmType::Sha256); 0258 } 0259 0260 QStringList DKIMInfo::listSignedHeader() const 0261 { 0262 return mListSignedHeader; 0263 } 0264 0265 void DKIMInfo::setListSignedHeader(const QStringList &listSignedHeader) 0266 { 0267 mListSignedHeader = listSignedHeader; 0268 } 0269 0270 QString DKIMInfo::signingAlgorithm() const 0271 { 0272 return mSigningAlgorithm; 0273 } 0274 0275 void DKIMInfo::setSigningAlgorithm(const QString &signingAlgorithm) 0276 { 0277 mSigningAlgorithm = signingAlgorithm; 0278 } 0279 0280 qint64 DKIMInfo::signatureTimeStamp() const 0281 { 0282 return mSignatureTimeStamp; 0283 } 0284 0285 void DKIMInfo::setSignatureTimeStamp(qint64 signatureTimeStamp) 0286 { 0287 mSignatureTimeStamp = signatureTimeStamp; 0288 } 0289 0290 QString DKIMInfo::query() const 0291 { 0292 return mQuery; 0293 } 0294 0295 void DKIMInfo::setQuery(const QString &query) 0296 { 0297 mQuery = query; 0298 } 0299 0300 qint64 DKIMInfo::expireTime() const 0301 { 0302 return mExpireTime; 0303 } 0304 0305 void DKIMInfo::setExpireTime(qint64 expireTime) 0306 { 0307 mExpireTime = expireTime; 0308 } 0309 0310 QString DKIMInfo::signature() const 0311 { 0312 return mSignature; 0313 } 0314 0315 void DKIMInfo::setSignature(const QString &signature) 0316 { 0317 mSignature = signature; 0318 } 0319 0320 QString DKIMInfo::agentOrUserIdentifier() const 0321 { 0322 return mAgentOrUserIdentifier; 0323 } 0324 0325 void DKIMInfo::setAgentOrUserIdentifier(const QString &userAgent) 0326 { 0327 mAgentOrUserIdentifier = userAgent; 0328 } 0329 0330 int DKIMInfo::bodyLengthCount() const 0331 { 0332 return mBodyLengthCount; 0333 } 0334 0335 void DKIMInfo::setBodyLengthCount(int bodyLengthCount) 0336 { 0337 mBodyLengthCount = bodyLengthCount; 0338 } 0339 0340 QDebug operator<<(QDebug d, const DKIMInfo &t) 0341 { 0342 d << "mVersion " << t.version(); 0343 d << "mHashingAlgorithm " << t.hashingAlgorithm(); 0344 d << "mSigningAlgorithm " << t.signingAlgorithm(); 0345 d << "mDomain " << t.domain(); 0346 d << "mSelector " << t.selector(); 0347 d << "mBodyHash " << t.bodyHash(); 0348 d << "mSignatureTimeStamp " << t.signatureTimeStamp(); 0349 d << "mExpireTime " << t.expireTime(); 0350 d << "mQuery " << t.query(); 0351 d << "mSignature " << t.signature(); 0352 d << "mAgentOrUserIdentifier " << t.agentOrUserIdentifier(); 0353 d << "mBodyLengthCount " << t.bodyLengthCount(); 0354 d << "mListSignedHeader " << t.listSignedHeader(); 0355 d << "mHeaderCanonization " << t.headerCanonization(); 0356 d << "mBodyCanonization " << t.bodyCanonization(); 0357 d << "mIdomain " << t.iDomain(); 0358 return d; 0359 } 0360 0361 #include "moc_dkiminfo.cpp"