10#include <config-libkleo.h> 
   12#include "openpgpcertificatecreationdialog.h" 
   14#include "animatedexpander_p.h" 
   15#include "nameandemailwidget.h" 
   16#include "openpgpcertificatecreationconfig.h" 
   17#include "utils/compat.h" 
   18#include "utils/compliance.h" 
   19#include "utils/expiration.h" 
   20#include "utils/gnupg.h" 
   21#include "utils/keyparameters.h" 
   22#include "utils/keyusage.h" 
   24#include <KAdjustingScrollArea> 
   25#include <KConfigGroup> 
   26#include <KDateComboBox> 
   27#include <KLocalizedString> 
   30#include <KSharedConfig> 
   33#include <QDialogButtonBox> 
   38#include <QGpgME/CryptoConfig> 
   39#include <QGpgME/Protocol> 
   41#include "libkleo_debug.h" 
   46static bool unlimitedValidityIsAllowed()
 
   48    return !Kleo::Expiration::maximumExpirationDate().isValid();
 
   51class OpenPGPCertificateCreationDialog::Private
 
   53    friend class ::Kleo::OpenPGPCertificateCreationDialog;
 
   54    OpenPGPCertificateCreationDialog *
const q;
 
   58        KAdjustingScrollArea *scrollArea;
 
   59        NameAndEmailWidget *nameAndEmail;
 
   60        QCheckBox *withPassCheckBox;
 
   61        QDialogButtonBox *buttonBox;
 
   64        KDateComboBox *expiryDE;
 
   67        AnimatedExpander *expander;
 
   71            auto mainLayout = 
new QVBoxLayout{dialog};
 
   73            infoLabel = 
new QLabel{dialog};
 
   74            infoLabel->setWordWrap(
true);
 
   75            mainLayout->addWidget(infoLabel);
 
   79            scrollArea = 
new KAdjustingScrollArea{dialog};
 
   86            scrollArea->setWidget(widget);
 
   87            auto scrollAreaLayout = 
new QVBoxLayout(widget);
 
   88            scrollAreaLayout->setContentsMargins(0, 0, 0, 0);
 
   90            nameAndEmail = 
new NameAndEmailWidget{dialog};
 
   91            nameAndEmail->layout()->setContentsMargins(0, 0, 0, 0);
 
   92            scrollAreaLayout->addWidget(nameAndEmail);
 
   94            withPassCheckBox = 
new QCheckBox{
i18n(
"Protect the generated key with a passphrase."), dialog};
 
   95            withPassCheckBox->setToolTip(
 
   96                i18n(
"Encrypts the secret key with an unrecoverable passphrase. You will be asked for the passphrase during key generation."));
 
   97            scrollAreaLayout->addWidget(withPassCheckBox);
 
   99            expander = 
new AnimatedExpander(
i18n(
"Advanced options"), {}, dialog);
 
  100            scrollAreaLayout->addWidget(expander);
 
  102            auto advancedLayout = 
new QVBoxLayout;
 
  103            expander->setContentLayout(advancedLayout);
 
  105            keyAlgoLabel = 
new QLabel(dialog);
 
  106            keyAlgoLabel->setText(
i18nc(
"The algorithm and strength of encryption key", 
"Key Material"));
 
  107            auto font = keyAlgoLabel->font();
 
  109            keyAlgoLabel->setFont(
font);
 
  110            advancedLayout->addWidget(keyAlgoLabel);
 
  112            keyAlgoCB = 
new QComboBox(dialog);
 
  113            keyAlgoLabel->setBuddy(keyAlgoCB);
 
  114            advancedLayout->addWidget(keyAlgoCB);
 
  117                auto hbox = 
new QHBoxLayout;
 
  119                expiryCB = 
new QCheckBox{dialog};
 
  120                expiryCB->setAccessibleName(Expiration::validUntilLabel());
 
  121                hbox->addWidget(expiryCB);
 
  123                expiryLabel = 
new QLabel{Expiration::validUntilLabel(), dialog};
 
  124                hbox->addWidget(expiryLabel);
 
  126                expiryDE = 
new KDateComboBox(dialog);
 
  127                hbox->addWidget(expiryDE, 1);
 
  129                advancedLayout->addLayout(hbox);
 
  132            scrollAreaLayout->addStretch(1);
 
  134            mainLayout->addWidget(scrollArea);
 
  140            mainLayout->addWidget(buttonBox);
 
  145    explicit Private(OpenPGPCertificateCreationDialog *qq)
 
  148        , technicalParameters{KeyParameters::OpenPGP}
 
  150        q->setWindowTitle(
i18nc(
"title:window", 
"Create OpenPGP Certificate"));
 
  152        OpenPGPCertificateCreationConfig settings;
 
  153        const auto requiredFields = settings.requiredFields();
 
  154        const auto nameIsRequired = requiredFields.contains(QLatin1StringView{
"NAME!"}, 
Qt::CaseInsensitive);
 
  155        const auto emailIsRequired = requiredFields.contains(QLatin1StringView{
"EMAIL!"}, 
Qt::CaseInsensitive);
 
  157        ui.infoLabel->setText(nameIsRequired || emailIsRequired 
 
  158                                  ? 
i18n(
"Enter a name and an email address to use for the certificate.")
 
  159                                  : 
i18n(
"Enter a name and/or an email address to use for the certificate."));
 
  161        ui.nameAndEmail->setNameIsRequired(nameIsRequired);
 
  162        ui.nameAndEmail->setNameLabel(settings.nameLabel());
 
  163        const auto nameHint = settings.nameHint();
 
  164        ui.nameAndEmail->setNameHint(nameHint.isEmpty() ? settings.namePlaceholder() : nameHint);
 
  165        ui.nameAndEmail->setNamePattern(settings.nameRegex());
 
  166        ui.nameAndEmail->setEmailIsRequired(emailIsRequired);
 
  167        ui.nameAndEmail->setEmailLabel(settings.emailLabel());
 
  168        const auto emailHint = settings.emailHint();
 
  169        ui.nameAndEmail->setEmailHint(emailHint.isEmpty() ? settings.emailPlaceholder() : emailHint);
 
  170        ui.nameAndEmail->setEmailPattern(settings.emailRegex());
 
  172        ui.expander->setVisible(!settings.hideAdvanced());
 
  174        const auto conf = QGpgME::cryptoConfig();
 
  175        const auto entry = getCryptoConfigEntry(conf, 
"gpg-agent", 
"enforce-passphrase-constraints");
 
  176        if (entry && entry->boolValue()) {
 
  177            qCDebug(LIBKLEO_LOG) << 
"Disabling passphrase check box because of agent config.";
 
  178            ui.withPassCheckBox->setEnabled(
false);
 
  179            ui.withPassCheckBox->setChecked(
true);
 
  181            ui.withPassCheckBox->setChecked(settings.withPassphrase());
 
  182            ui.withPassCheckBox->setEnabled(!settings.isWithPassphraseImmutable());
 
  190        for (
const auto &algorithm : DeVSCompliance::isActive() ? DeVSCompliance::compliantAlgorithms() : availableAlgorithms()) {
 
  193        auto cryptoConfig = QGpgME::cryptoConfig();
 
  195            auto pubkeyEntry = getCryptoConfigEntry(QGpgME::cryptoConfig(), 
"gpg", 
"default_pubkey_algo");
 
  197                auto algo = pubkeyEntry->stringValue().split(QLatin1Char(
'/'))[0];
 
  198                if (algo == QLatin1StringView(
"ed25519")) {
 
  199                    algo = QStringLiteral(
"curve25519");
 
  200                } 
else if (algo == QLatin1StringView(
"ed448")) {
 
  201                    algo = QStringLiteral(
"curve448");
 
  203                auto index = ui.keyAlgoCB->findData(algo);
 
  205                    ui.keyAlgoCB->setCurrentIndex(index);
 
  207                    ui.keyAlgoCB->setCurrentIndex(0);
 
  210                ui.keyAlgoCB->setCurrentIndex(0);
 
  213            ui.keyAlgoCB->setCurrentIndex(0);
 
  216        Kleo::Expiration::setUpExpirationDateComboBox(ui.expiryDE);
 
  217        ui.expiryCB->setEnabled(
true);
 
  218        setExpiryDate(defaultExpirationDate(Kleo::Expiration::ExpirationOnUnlimitedValidity::InternalDefaultExpiration));
 
  219        if (unlimitedValidityIsAllowed()) {
 
  220            ui.expiryLabel->setEnabled(ui.expiryCB->isChecked());
 
  221            ui.expiryDE->setEnabled(ui.expiryCB->isChecked());
 
  223            ui.expiryCB->setEnabled(
false);
 
  224            ui.expiryCB->setVisible(
false);
 
  227            ui.expiryLabel->setEnabled(checked);
 
  228            ui.expiryDE->setEnabled(checked);
 
  229            if (checked && !ui.expiryDE->isValid()) {
 
  230                setExpiryDate(defaultExpirationDate(Kleo::Expiration::ExpirationOnUnlimitedValidity::InternalDefaultExpiration));
 
  232            updateTechnicalParameters();
 
  235            updateTechnicalParameters();
 
  238            updateTechnicalParameters();
 
  240        updateTechnicalParameters(); 
 
  241        connect(ui.expander, &AnimatedExpander::startExpanding, q, [
this]() {
 
  242            q->resize(std::max(q->sizeHint().width(), ui.expander->contentWidth()) + 20, q->sizeHint().height() + ui.expander->contentHeight() + 20);
 
  247    void updateTechnicalParameters()
 
  249        technicalParameters = KeyParameters{KeyParameters::OpenPGP};
 
  250        auto keyType = GpgME::Subkey::AlgoUnknown;
 
  251        auto subkeyType = GpgME::Subkey::AlgoUnknown;
 
  252        auto algoString = ui.keyAlgoCB->currentData().toString();
 
  253        if (algoString.startsWith(QStringLiteral(
"rsa"))) {
 
  254            keyType = GpgME::Subkey::AlgoRSA;
 
  255            subkeyType = GpgME::Subkey::AlgoRSA;
 
  256            const auto strength = algoString.mid(3).toInt();
 
  257            technicalParameters.setKeyLength(strength);
 
  258            technicalParameters.setSubkeyLength(strength);
 
  260            keyType = GpgME::Subkey::AlgoEDDSA;
 
  261            subkeyType = GpgME::Subkey::AlgoECDH;
 
  262            if (algoString.endsWith(QStringLiteral(
"25519"))) {
 
  263                technicalParameters.setKeyCurve(QStringLiteral(
"ed25519"));
 
  264                technicalParameters.setSubkeyCurve(QStringLiteral(
"cv25519"));
 
  266                technicalParameters.setKeyCurve(QStringLiteral(
"ed448"));
 
  267                technicalParameters.setSubkeyCurve(QStringLiteral(
"cv448"));
 
  269#if GPGMEPP_SUPPORTS_KYBER 
  270        } 
else if (algoString == 
"ky768_bp256"_L1) {
 
  271            keyType = GpgME::Subkey::AlgoECDSA;
 
  272            subkeyType = GpgME::Subkey::AlgoKyber;
 
  273            technicalParameters.setKeyCurve(u
"brainpoolP256r1"_s);
 
  274            technicalParameters.setSubkeyCurve(u
"brainpoolP256r1"_s);
 
  275            technicalParameters.setSubkeyLength(768);
 
  276        } 
else if (algoString == 
"ky1024_bp384"_L1) {
 
  277            keyType = GpgME::Subkey::AlgoECDSA;
 
  278            subkeyType = GpgME::Subkey::AlgoKyber;
 
  279            technicalParameters.setKeyCurve(u
"brainpoolP384r1"_s);
 
  280            technicalParameters.setSubkeyCurve(u
"brainpoolP384r1"_s);
 
  281            technicalParameters.setSubkeyLength(1024);
 
  284            keyType = GpgME::Subkey::AlgoECDSA;
 
  285            subkeyType = GpgME::Subkey::AlgoECDH;
 
  286            technicalParameters.setKeyCurve(algoString);
 
  287            technicalParameters.setSubkeyCurve(algoString);
 
  289        technicalParameters.setKeyType(keyType);
 
  290        technicalParameters.setSubkeyType(subkeyType);
 
  292        technicalParameters.setKeyUsage(KeyUsage(KeyUsage::Certify | KeyUsage::Sign));
 
  293        technicalParameters.setSubkeyUsage(KeyUsage(KeyUsage::Encrypt));
 
  295        technicalParameters.setExpirationDate(expiryDate());
 
  299    QDate expiryDate()
 const 
  301        return ui.expiryCB->isChecked() ? ui.expiryDE->date() : 
QDate{};
 
  304    void setTechnicalParameters(
const KeyParameters ¶meters)
 
  307        if (parameters.keyType() == GpgME::Subkey::AlgoRSA) {
 
  308            index = ui.keyAlgoCB->findData(QStringLiteral(
"rsa%1").arg(parameters.keyLength()));
 
  310            index = ui.keyAlgoCB->findData(QStringLiteral(
"curve25519"));
 
  312            index = ui.keyAlgoCB->findData(QStringLiteral(
"curve448"));
 
  313#if GPGMEPP_SUPPORTS_KYBER 
  314        } 
else if (parameters.subkeyType() == GpgME::Subkey::AlgoKyber) {
 
  315            if (parameters.subkeyLength() == 768 && parameters.keyCurve() == 
"brainpoolP256r1"_L1) {
 
  316                index = ui.keyAlgoCB->findData(
"ky768_bp256"_L1);
 
  317            } 
else if (parameters.subkeyLength() == 1024 && parameters.keyCurve() == 
"brainpoolP384r1"_L1) {
 
  318                index = ui.keyAlgoCB->findData(
"ky1024_bp384"_L1);
 
  320                qCDebug(LIBKLEO_LOG) << __func__ << 
"Unsupported Kyber parameters" << parameters.subkeyLength() << parameters.keyCurve();
 
  324            index = ui.keyAlgoCB->findData(parameters.keyCurve());
 
  327            ui.keyAlgoCB->setCurrentIndex(index);
 
  329        setExpiryDate(parameters.expirationDate());
 
  335        if (ui.nameAndEmail->userID().isEmpty() && !ui.nameAndEmail->nameIsRequired() && !ui.nameAndEmail->emailIsRequired()) {
 
  338        const auto nameError = ui.nameAndEmail->nameError();
 
  339        if (!nameError.isEmpty()) {
 
  342        const auto emailError = ui.nameAndEmail->emailError();
 
  343        if (!emailError.isEmpty()) {
 
  346        if (!Expiration::isValidExpirationDate(expiryDate())) {
 
  347            errors.
push_back(Expiration::validityPeriodHint());
 
  349        if (errors.
size() > 1) {
 
  351        } 
else if (!errors.
empty()) {
 
  358    QDate forceDateIntoAllowedRange(
QDate date)
 const 
  360        const auto minDate = ui.expiryDE->minimumDate();
 
  361        if (minDate.isValid() && date < minDate) {
 
  364        const auto maxDate = ui.expiryDE->maximumDate();
 
  365        if (maxDate.isValid() && date > maxDate) {
 
  371    void setExpiryDate(
QDate date)
 
  374            ui.expiryDE->setDate(forceDateIntoAllowedRange(date));
 
  377            if (unlimitedValidityIsAllowed()) {
 
  378                ui.expiryDE->setDate(date);
 
  381        if (ui.expiryCB->isEnabled()) {
 
  382            ui.expiryCB->setChecked(ui.expiryDE->isValid());
 
  387    KeyParameters technicalParameters;
 
  392    , d(new Private{this})
 
  394    resize(std::max(sizeHint().width(), d->ui.expander->contentWidth()) + 20, sizeHint().height() + 20);
 
  397OpenPGPCertificateCreationDialog::~OpenPGPCertificateCreationDialog() = 
default;
 
  399void OpenPGPCertificateCreationDialog::setName(
const QString &name)
 
  401    d->ui.nameAndEmail->setName(name);
 
  404QString OpenPGPCertificateCreationDialog::name()
 const 
  406    return d->ui.nameAndEmail->name();
 
  409void OpenPGPCertificateCreationDialog::setEmail(
const QString &email)
 
  411    d->ui.nameAndEmail->setEmail(email);
 
  414QString OpenPGPCertificateCreationDialog::email()
 const 
  416    return d->ui.nameAndEmail->email();
 
  419void Kleo::OpenPGPCertificateCreationDialog::setKeyParameters(
const Kleo::KeyParameters ¶meters)
 
  421    setName(parameters.name());
 
  422    const auto emails = parameters.emails();
 
  423    if (!emails.empty()) {
 
  424        setEmail(emails.front());
 
  426    d->setTechnicalParameters(parameters);
 
  429KeyParameters OpenPGPCertificateCreationDialog::keyParameters()
 const 
  432    auto parameters = d->technicalParameters;
 
  433    if (!name().isEmpty()) {
 
  434        parameters.setName(name());
 
  436    if (!email().isEmpty()) {
 
  437        parameters.setEmail(email());
 
  442void Kleo::OpenPGPCertificateCreationDialog::setProtectKeyWithPassword(
bool protectKey)
 
  444    d->ui.withPassCheckBox->setChecked(protectKey);
 
  447bool OpenPGPCertificateCreationDialog::protectKeyWithPassword()
 const 
  449    return d->ui.withPassCheckBox->isChecked();
 
  452#include "moc_openpgpcertificatecreationdialog.cpp" 
void dateChanged(const QDate &date)
 
QString i18nc(const char *context, const char *text, const TYPE &arg...)
 
QString i18n(const char *text, const TYPE &arg...)
 
void errorList(QWidget *parent, const QString &text, const QStringList &strlist, const QString &title=QString(), Options options=Notify)
 
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
 
void currentIndexChanged(int index)
 
bool isValid(int year, int month, int day)
 
void push_back(parameter_type value)
 
qsizetype size() const const
 
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
 
QString fromStdString(const std::string &str)