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 }