File indexing completed on 2024-06-09 05:17:27

0001 /* -*- mode: c++; c-basic-offset:4 -*-
0002     utils/compliance.cpp
0003 
0004     This file is part of libkleopatra
0005     SPDX-FileCopyrightText: 2022 g10 Code GmbH
0006     SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
0007 
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 
0011 #include <config-libkleo.h>
0012 
0013 #include "compliance.h"
0014 
0015 #include "algorithm.h"
0016 #include "cryptoconfig.h"
0017 #include "gnupg.h"
0018 #include "keyhelpers.h"
0019 #include "stringutils.h"
0020 #include "systeminfo.h"
0021 
0022 #include <libkleo/debug.h>
0023 #include <libkleo/keyfiltermanager.h>
0024 
0025 #include <libkleo_debug.h>
0026 
0027 #include <KColorScheme>
0028 #include <KLocalizedString>
0029 
0030 #include <QPushButton>
0031 
0032 #include <gpgme++/key.h>
0033 
0034 bool Kleo::DeVSCompliance::isActive()
0035 {
0036     return getCryptoConfigStringValue("gpg", "compliance") == QLatin1StringView{"de-vs"};
0037 }
0038 
0039 bool Kleo::DeVSCompliance::isCompliant()
0040 {
0041     if (!isActive()) {
0042         return false;
0043     }
0044     // The pseudo option compliance_de_vs was fully added in 2.2.34;
0045     // For versions between 2.2.28 and 2.2.33 there was a broken config
0046     // value with a wrong type. So for them we add an extra check. This
0047     // can be removed in future versions because for GnuPG we could assume
0048     // non-compliance for older versions as versions of Kleopatra for
0049     // which this matters are bundled with new enough versions of GnuPG anyway.
0050     if (engineIsVersion(2, 2, 28) && !engineIsVersion(2, 2, 34)) {
0051         return true;
0052     }
0053     return getCryptoConfigIntValue("gpg", "compliance_de_vs", 0) != 0;
0054 }
0055 
0056 bool Kleo::DeVSCompliance::algorithmIsCompliant(std::string_view algo)
0057 {
0058     return !isActive() || Kleo::contains(compliantAlgorithms(), algo);
0059 }
0060 
0061 bool Kleo::DeVSCompliance::allSubkeysAreCompliant(const GpgME::Key &key)
0062 {
0063     if (!isActive()) {
0064         return true;
0065     }
0066     // there is at least one usable subkey
0067     const auto usableSubkeys = Kleo::count_if(key.subkeys(), [](const auto &sub) {
0068         return !sub.isExpired() && !sub.isRevoked();
0069     });
0070     if (usableSubkeys == 0) {
0071         qCDebug(LIBKLEO_LOG) << __func__ << "No usable subkeys found for key" << key;
0072         return false;
0073     }
0074     // and all usable subkeys are compliant
0075     return Kleo::all_of(key.subkeys(), [](const auto &sub) {
0076         return sub.isDeVs() || sub.isExpired() || sub.isRevoked();
0077     });
0078 }
0079 
0080 bool Kleo::DeVSCompliance::userIDIsCompliant(const GpgME::UserID &id)
0081 {
0082     if (!isActive()) {
0083         return true;
0084     }
0085     return (id.parent().keyListMode() & GpgME::Validate) //
0086         && !id.isRevoked() //
0087         && id.validity() >= GpgME::UserID::Full //
0088         && allSubkeysAreCompliant(id.parent());
0089 }
0090 
0091 bool Kleo::DeVSCompliance::keyIsCompliant(const GpgME::Key &key)
0092 {
0093     if (!isActive()) {
0094         return true;
0095     }
0096     return (key.keyListMode() & GpgME::Validate) //
0097         && allUserIDsHaveFullValidity(key) //
0098         && allSubkeysAreCompliant(key);
0099 }
0100 
0101 const std::vector<std::string> &Kleo::DeVSCompliance::compliantAlgorithms()
0102 {
0103     static const std::vector<std::string> compliantAlgos = {
0104         "brainpoolP256r1",
0105         "brainpoolP384r1",
0106         "brainpoolP512r1",
0107         "rsa3072",
0108         "rsa4096",
0109     };
0110     return isActive() ? compliantAlgos : Kleo::availableAlgorithms();
0111 }
0112 
0113 const std::vector<std::string> &Kleo::DeVSCompliance::preferredCompliantAlgorithms()
0114 {
0115     static std::vector<std::string> result;
0116     if (result.empty()) {
0117         const auto &preferredAlgos = Kleo::preferredAlgorithms();
0118         result.reserve(preferredAlgos.size());
0119         Kleo::copy_if(preferredAlgos, std::back_inserter(result), Kleo::DeVSCompliance::algorithmIsCompliant);
0120     }
0121     return result;
0122 }
0123 
0124 void Kleo::DeVSCompliance::decorate(QPushButton *button)
0125 {
0126     decorate(button, isCompliant());
0127 }
0128 
0129 void Kleo::DeVSCompliance::decorate(QPushButton *button, bool compliant)
0130 {
0131     if (!button) {
0132         return;
0133     }
0134     if (compliant) {
0135         button->setIcon(QIcon::fromTheme(QStringLiteral("security-high")));
0136         if (!SystemInfo::isHighContrastModeActive()) {
0137             const auto bgColor = KColorScheme(QPalette::Active, KColorScheme::View).background(KColorScheme::PositiveBackground).color().name();
0138             button->setStyleSheet(QStringLiteral("QPushButton { background-color: %1; };").arg(bgColor));
0139         }
0140     } else {
0141         button->setIcon(QIcon::fromTheme(QStringLiteral("security-medium")));
0142         if (!SystemInfo::isHighContrastModeActive()) {
0143             const auto bgColor = KColorScheme(QPalette::Active, KColorScheme::View).background(KColorScheme::NegativeBackground).color().name();
0144             button->setStyleSheet(QStringLiteral("QPushButton { background-color: %1; };").arg(bgColor));
0145         }
0146     }
0147 }
0148 
0149 QString Kleo::DeVSCompliance::name()
0150 {
0151     return name(isCompliant());
0152 }
0153 
0154 QString Kleo::DeVSCompliance::name(bool compliant)
0155 {
0156     const auto filterId = compliant ? QStringLiteral("de-vs-filter") : QStringLiteral("not-de-vs-filter");
0157     if (auto filter = KeyFilterManager::instance()->keyFilterByID(filterId)) {
0158         return filter->name();
0159     }
0160     return compliant ? i18n("VS-NfD compliant") : i18n("Not VS-NfD compliant");
0161 }