File indexing completed on 2024-06-23 05:14:17

0001 /* -*- mode: c++; c-basic-offset:4 -*-
0002     utils/keyparameters.cpp
0003 
0004     This file is part of Kleopatra, the KDE keymanager
0005     SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
0006 
0007     SPDX-FileCopyrightText: 2020, 2022 g10 Code GmbH
0008     SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
0009 
0010     SPDX-License-Identifier: GPL-2.0-or-later
0011 */
0012 
0013 #include "keyparameters.h"
0014 
0015 #include <Libkleo/KeyUsage>
0016 
0017 #include <QDate>
0018 #include <QUrl>
0019 
0020 #include "kleopatra_debug.h"
0021 
0022 using namespace Kleo;
0023 using namespace GpgME;
0024 
0025 namespace
0026 {
0027 QString encodeDomainName(const QString &domain)
0028 {
0029     const QByteArray encodedDomain = QUrl::toAce(domain);
0030     return encodedDomain.isEmpty() ? domain : QString::fromLatin1(encodedDomain);
0031 }
0032 
0033 QString encodeEmail(const QString &email)
0034 {
0035     const int at = email.lastIndexOf(QLatin1Char('@'));
0036     if (at < 0) {
0037         return email;
0038     }
0039     return email.left(at + 1) + encodeDomainName(email.mid(at + 1));
0040 }
0041 }
0042 
0043 class KeyParameters::Private
0044 {
0045     friend class ::Kleo::KeyParameters;
0046 
0047     Protocol protocol;
0048 
0049     Subkey::PubkeyAlgo keyType = Subkey::AlgoUnknown;
0050     QString cardKeyRef;
0051     unsigned int keyLength = 0;
0052     QString keyCurve;
0053     KeyUsage keyUsage;
0054 
0055     Subkey::PubkeyAlgo subkeyType = Subkey::AlgoUnknown;
0056     unsigned int subkeyLength = 0;
0057     QString subkeyCurve;
0058     KeyUsage subkeyUsage;
0059 
0060     QString name;
0061     QString comment;
0062     QString dn;
0063     std::vector<QString> emailAdresses;
0064     std::vector<QString> domainNames;
0065     std::vector<QString> uris;
0066     std::vector<QString> designatedRevokers;
0067 
0068     QDate expirationDate;
0069 
0070 public:
0071     explicit Private(Protocol proto)
0072         : protocol(proto)
0073     {
0074     }
0075 };
0076 
0077 KeyParameters::KeyParameters()
0078     : KeyParameters{NoProtocol}
0079 {
0080 }
0081 
0082 KeyParameters::KeyParameters(Protocol protocol)
0083     : d{new Private{protocol}}
0084 {
0085 }
0086 
0087 KeyParameters::~KeyParameters() = default;
0088 
0089 KeyParameters::KeyParameters(const KeyParameters &other)
0090     : d{new Private{*other.d}}
0091 {
0092 }
0093 
0094 KeyParameters &KeyParameters::operator=(const KeyParameters &other)
0095 {
0096     *d = *other.d;
0097     return *this;
0098 }
0099 
0100 KeyParameters::KeyParameters(KeyParameters &&other) = default;
0101 
0102 KeyParameters &KeyParameters::operator=(KeyParameters &&other) = default;
0103 
0104 KeyParameters::Protocol KeyParameters::protocol() const
0105 {
0106     return d->protocol;
0107 }
0108 
0109 void KeyParameters::setKeyType(Subkey::PubkeyAlgo type)
0110 {
0111     d->keyType = type;
0112 }
0113 
0114 GpgME::Subkey::PubkeyAlgo KeyParameters::keyType() const
0115 {
0116     return d->keyType;
0117 }
0118 
0119 void KeyParameters::setCardKeyRef(const QString &cardKeyRef)
0120 {
0121     d->cardKeyRef = cardKeyRef;
0122 }
0123 
0124 QString KeyParameters::cardKeyRef() const
0125 {
0126     return d->cardKeyRef;
0127 }
0128 
0129 void KeyParameters::setKeyLength(unsigned int length)
0130 {
0131     d->keyLength = length;
0132 }
0133 
0134 unsigned int KeyParameters::keyLength() const
0135 {
0136     return d->keyLength;
0137 }
0138 
0139 void KeyParameters::setKeyCurve(const QString &curve)
0140 {
0141     d->keyCurve = curve;
0142 }
0143 
0144 QString KeyParameters::keyCurve() const
0145 {
0146     return d->keyCurve;
0147 }
0148 
0149 void KeyParameters::setKeyUsage(const KeyUsage &usage)
0150 {
0151     d->keyUsage = usage;
0152 }
0153 
0154 KeyUsage KeyParameters::keyUsage() const
0155 {
0156     return d->keyUsage;
0157 }
0158 
0159 void KeyParameters::setSubkeyType(Subkey::PubkeyAlgo type)
0160 {
0161     d->subkeyType = type;
0162 }
0163 
0164 Subkey::PubkeyAlgo KeyParameters::subkeyType() const
0165 {
0166     return d->subkeyType;
0167 }
0168 
0169 void KeyParameters::setSubkeyLength(unsigned int length)
0170 {
0171     d->subkeyLength = length;
0172 }
0173 
0174 unsigned int KeyParameters::subkeyLength() const
0175 {
0176     return d->subkeyLength;
0177 }
0178 
0179 void KeyParameters::setSubkeyCurve(const QString &curve)
0180 {
0181     d->subkeyCurve = curve;
0182 }
0183 
0184 QString KeyParameters::subkeyCurve() const
0185 {
0186     return d->subkeyCurve;
0187 }
0188 
0189 void KeyParameters::setSubkeyUsage(const KeyUsage &usage)
0190 {
0191     d->subkeyUsage = usage;
0192 }
0193 
0194 KeyUsage KeyParameters::subkeyUsage() const
0195 {
0196     return d->subkeyUsage;
0197 }
0198 
0199 void KeyParameters::setExpirationDate(const QDate &date)
0200 {
0201     d->expirationDate = date;
0202 }
0203 
0204 QDate KeyParameters::expirationDate() const
0205 {
0206     return d->expirationDate;
0207 }
0208 
0209 void KeyParameters::setName(const QString &name)
0210 {
0211     d->name = name;
0212 }
0213 
0214 QString KeyParameters::name() const
0215 {
0216     return d->name;
0217 }
0218 
0219 void KeyParameters::setComment(const QString &comment)
0220 {
0221     d->comment = comment;
0222 }
0223 
0224 QString KeyParameters::comment() const
0225 {
0226     return d->comment;
0227 }
0228 
0229 void KeyParameters::setDN(const QString &dn)
0230 {
0231     d->dn = dn;
0232 }
0233 
0234 QString KeyParameters::dn() const
0235 {
0236     return d->dn;
0237 }
0238 
0239 void KeyParameters::setEmail(const QString &email)
0240 {
0241     d->emailAdresses = {email};
0242 }
0243 
0244 void KeyParameters::addEmail(const QString &email)
0245 {
0246     d->emailAdresses.push_back(email);
0247 }
0248 
0249 std::vector<QString> KeyParameters::emails() const
0250 {
0251     return d->emailAdresses;
0252 }
0253 
0254 void KeyParameters::addDomainName(const QString &domain)
0255 {
0256     d->domainNames.push_back(domain);
0257 }
0258 
0259 std::vector<QString> KeyParameters::domainNames() const
0260 {
0261     return d->domainNames;
0262 }
0263 
0264 void KeyParameters::addURI(const QString &uri)
0265 {
0266     d->uris.push_back(uri);
0267 }
0268 
0269 std::vector<QString> KeyParameters::uris() const
0270 {
0271     return d->uris;
0272 }
0273 
0274 void KeyParameters::addDesignatedRevoker(const QString &fpr)
0275 {
0276     d->designatedRevokers.push_back(fpr);
0277 }
0278 
0279 std::vector<QString> KeyParameters::designatedRevokers() const
0280 {
0281     return d->designatedRevokers;
0282 }
0283 
0284 namespace
0285 {
0286 QString serialize(Subkey::PubkeyAlgo algo)
0287 {
0288     return QString::fromLatin1(Subkey::publicKeyAlgorithmAsString(algo));
0289 }
0290 
0291 QString serialize(unsigned int number)
0292 {
0293     return QString::number(number);
0294 }
0295 
0296 QString serialize(KeyUsage keyUsage)
0297 {
0298     QStringList usages;
0299     if (keyUsage.canSign()) {
0300         usages << QStringLiteral("sign");
0301     }
0302     if (keyUsage.canEncrypt()) {
0303         usages << QStringLiteral("encrypt");
0304     }
0305     if (keyUsage.canAuthenticate()) {
0306         usages << QStringLiteral("auth");
0307     }
0308     if (keyUsage.canCertify()) {
0309         usages << QStringLiteral("cert");
0310     }
0311     return usages.join(QLatin1Char{' '});
0312 }
0313 
0314 QString serialize(const QDate &date)
0315 {
0316     return date.toString(Qt::ISODate);
0317 }
0318 
0319 QString serialize(const char *key, const QString &value)
0320 {
0321     return QString::fromLatin1(key) + QLatin1Char(':') + value;
0322 }
0323 }
0324 
0325 QString KeyParameters::toString() const
0326 {
0327     QStringList keyParameters;
0328 
0329     keyParameters.push_back(QLatin1StringView("<GnupgKeyParms format=\"internal\">"));
0330 
0331     if (d->protocol == OpenPGP) {
0332         // for backward compatibility with GnuPG 2.0 and earlier
0333         keyParameters.push_back(QStringLiteral("%ask-passphrase"));
0334     }
0335 
0336     // add Key-Type as first parameter
0337     if (!d->cardKeyRef.isEmpty()) {
0338         keyParameters.push_back(serialize("Key-Type", QLatin1StringView{"card:"} + d->cardKeyRef));
0339     } else if (d->keyType != Subkey::AlgoUnknown) {
0340         keyParameters.push_back(serialize("Key-Type", serialize(d->keyType)));
0341     } else {
0342         qCWarning(KLEOPATRA_LOG) << "KeyParameters::toString(): Key type is unset/empty";
0343     }
0344     if (d->keyLength) {
0345         keyParameters.push_back(serialize("Key-Length", serialize(d->keyLength)));
0346     }
0347     if (!d->keyCurve.isEmpty()) {
0348         keyParameters.push_back(serialize("Key-Curve", d->keyCurve));
0349     }
0350     keyParameters.push_back(serialize("Key-Usage", serialize(d->keyUsage)));
0351 
0352     if (d->subkeyType != Subkey::AlgoUnknown) {
0353         keyParameters.push_back(serialize("Subkey-Type", serialize(d->subkeyType)));
0354         if (d->subkeyUsage.value()) {
0355             keyParameters.push_back(serialize("Subkey-Usage", serialize(d->subkeyUsage)));
0356         }
0357         if (d->subkeyLength) {
0358             keyParameters.push_back(serialize("Subkey-Length", serialize(d->subkeyLength)));
0359         }
0360         if (!d->subkeyCurve.isEmpty()) {
0361             keyParameters.push_back(serialize("Subkey-Curve", d->subkeyCurve));
0362         }
0363     }
0364 
0365     if (d->expirationDate.isValid()) {
0366         keyParameters.push_back(serialize("Expire-Date", serialize(d->expirationDate)));
0367     }
0368 
0369     if (!d->name.isEmpty()) {
0370         keyParameters.push_back(serialize("Name-Real", d->name));
0371     }
0372     if (!d->comment.isEmpty()) {
0373         keyParameters.push_back(serialize("Name-Comment", d->comment));
0374     }
0375     if (!d->dn.isEmpty()) {
0376         keyParameters.push_back(serialize("Name-DN", d->dn));
0377     }
0378     std::transform(std::cbegin(d->emailAdresses), std::cend(d->emailAdresses), std::back_inserter(keyParameters), [this](const auto &email) {
0379         return serialize("Name-Email", (d->protocol == CMS) ? encodeEmail(email) : email);
0380     });
0381     std::transform(std::cbegin(d->domainNames), std::cend(d->domainNames), std::back_inserter(keyParameters), [](const auto &domain) {
0382         return serialize("Name-DNS", encodeDomainName(domain));
0383     });
0384     std::transform(std::cbegin(d->uris), std::cend(d->uris), std::back_inserter(keyParameters), [](const auto &uri) {
0385         return serialize("Name-URI", uri);
0386     });
0387     std::transform(std::cbegin(d->designatedRevokers), std::cend(d->designatedRevokers), std::back_inserter(keyParameters), [](const auto &designatedRevoker) {
0388         return serialize("Revoker", designatedRevoker);
0389     });
0390 
0391     keyParameters.push_back(QLatin1StringView("</GnupgKeyParms>"));
0392 
0393     return keyParameters.join(QLatin1Char('\n'));
0394 }