From 9680baa7dc80fe27546972c11ca35ea4464e43ce Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Wed, 10 Apr 2024 00:31:58 +0300 Subject: [PATCH 01/22] Changed CKey handle to shared pointer --- client/CDoc1.cpp | 82 ++++++++--------- client/CDoc1.h | 2 +- client/CDoc2.cpp | 153 ++++++++++++++++++------------- client/CDoc2.h | 2 +- client/CryptoDoc.cpp | 38 +++++--- client/CryptoDoc.h | 32 +++++-- client/MainWindow.cpp | 4 +- client/MainWindow.h | 2 +- client/dialogs/AddRecipients.cpp | 34 ++++--- client/dialogs/AddRecipients.h | 6 +- client/widgets/AddressItem.cpp | 32 +++---- client/widgets/AddressItem.h | 6 +- client/widgets/ContainerPage.cpp | 4 +- client/widgets/ContainerPage.h | 2 +- schema/header.fbs | 25 +---- schema/recipients.fbs | 68 +++++++------- 16 files changed, 269 insertions(+), 223 deletions(-) diff --git a/client/CDoc1.cpp b/client/CDoc1.cpp index 78596eda..c5e224e8 100644 --- a/client/CDoc1.cpp +++ b/client/CDoc1.cpp @@ -107,9 +107,9 @@ CDoc1::CDoc1(const QString &path) if(xml.name() != QLatin1String("EncryptedKey")) return; - CKey key; - key.id = xml.attributes().value(QLatin1String("Id")).toString(); - key.recipient = xml.attributes().value(QLatin1String("Recipient")).toString(); + std::shared_ptr key = CKey::newEmpty(); + key->id = xml.attributes().value(QLatin1String("Id")).toString(); + key->recipient = xml.attributes().value(QLatin1String("Recipient")).toString(); while(!xml.atEnd()) { xml.readNext(); @@ -119,49 +119,49 @@ CDoc1::CDoc1(const QString &path) continue; // EncryptedData/KeyInfo/KeyName if(xml.name() == QLatin1String("KeyName")) - key.name = xml.readElementText(); + key->name = xml.readElementText(); // EncryptedData/KeyInfo/EncryptedKey/EncryptionMethod else if(xml.name() == QLatin1String("EncryptionMethod")) - key.method = xml.attributes().value(QLatin1String("Algorithm")).toString(); + key->method = xml.attributes().value(QLatin1String("Algorithm")).toString(); // EncryptedData/KeyInfo/EncryptedKey/KeyInfo/AgreementMethod else if(xml.name() == QLatin1String("AgreementMethod")) - key.agreement = xml.attributes().value(QLatin1String("Algorithm")).toString(); + key->agreement = xml.attributes().value(QLatin1String("Algorithm")).toString(); // EncryptedData/KeyInfo/EncryptedKey/KeyInfo/AgreementMethod/KeyDerivationMethod else if(xml.name() == QLatin1String("KeyDerivationMethod")) - key.derive = xml.attributes().value(QLatin1String("Algorithm")).toString(); + key->derive = xml.attributes().value(QLatin1String("Algorithm")).toString(); // EncryptedData/KeyInfo/EncryptedKey/KeyInfo/AgreementMethod/KeyDerivationMethod/ConcatKDFParams else if(xml.name() == QLatin1String("ConcatKDFParams")) { - key.AlgorithmID = QByteArray::fromHex(xml.attributes().value(QLatin1String("AlgorithmID")).toUtf8()); - if(key.AlgorithmID.front() == char(0x00)) key.AlgorithmID.remove(0, 1); - key.PartyUInfo = QByteArray::fromHex(xml.attributes().value(QLatin1String("PartyUInfo")).toUtf8()); - if(key.PartyUInfo.front() == char(0x00)) key.PartyUInfo.remove(0, 1); - key.PartyVInfo = QByteArray::fromHex(xml.attributes().value(QLatin1String("PartyVInfo")).toUtf8()); - if(key.PartyVInfo.front() == char(0x00)) key.PartyVInfo.remove(0, 1); + key->AlgorithmID = QByteArray::fromHex(xml.attributes().value(QLatin1String("AlgorithmID")).toUtf8()); + if(key->AlgorithmID.front() == char(0x00)) key->AlgorithmID.remove(0, 1); + key->PartyUInfo = QByteArray::fromHex(xml.attributes().value(QLatin1String("PartyUInfo")).toUtf8()); + if(key->PartyUInfo.front() == char(0x00)) key->PartyUInfo.remove(0, 1); + key->PartyVInfo = QByteArray::fromHex(xml.attributes().value(QLatin1String("PartyVInfo")).toUtf8()); + if(key->PartyVInfo.front() == char(0x00)) key->PartyVInfo.remove(0, 1); } // EncryptedData/KeyInfo/EncryptedKey/KeyInfo/AgreementMethod/KeyDerivationMethod/ConcatKDFParams/DigestMethod else if(xml.name() == QLatin1String("DigestMethod")) - key.concatDigest = xml.attributes().value(QLatin1String("Algorithm")).toString(); + key->concatDigest = xml.attributes().value(QLatin1String("Algorithm")).toString(); // EncryptedData/KeyInfo/EncryptedKey/KeyInfo/AgreementMethod/OriginatorKeyInfo/KeyValue/ECKeyValue/PublicKey else if(xml.name() == QLatin1String("PublicKey")) { xml.readNext(); - key.publicKey = fromBase64(xml.text()); + key->publicKey = fromBase64(xml.text()); } // EncryptedData/KeyInfo/EncryptedKey/KeyInfo/X509Data/X509Certificate else if(xml.name() == QLatin1String("X509Certificate")) { xml.readNext(); - key.setCert(QSslCertificate(fromBase64(xml.text()), QSsl::Der)); + key->setCert(QSslCertificate(fromBase64(xml.text()), QSsl::Der)); } // EncryptedData/KeyInfo/EncryptedKey/KeyInfo/CipherData/CipherValue else if(xml.name() == QLatin1String("CipherValue")) { xml.readNext(); - key.cipher = fromBase64(xml.text()); + key->cipher = fromBase64(xml.text()); } } - keys.append(std::move(key)); + keys.append(key); }); if(!keys.isEmpty()) setLastError({}); @@ -267,22 +267,22 @@ bool CDoc1::decryptPayload(const QByteArray &key) return !files.empty(); } -CKey CDoc1::canDecrypt(const QSslCertificate &cert) const +std::shared_ptr CDoc1::canDecrypt(const QSslCertificate &cert) const { - for(const CKey &k: qAsConst(keys)) + for(std::shared_ptr k: qAsConst(keys)) { if(!ENC_MTH.contains(method) || - k.cert != cert || - k.cipher.isEmpty()) + k->cert != cert || + k->cipher.isEmpty()) continue; if(cert.publicKey().algorithm() == QSsl::Rsa && - k.method == RSA_MTH) + k->method == RSA_MTH) return k; if(cert.publicKey().algorithm() == QSsl::Ec && - !k.publicKey.isEmpty() && - KWAES_SIZE.contains(k.method) && - k.derive == CONCATKDF_MTH && - k.agreement == AGREEMENT_MTH) + !k->publicKey.isEmpty() && + KWAES_SIZE.contains(k->method) && + k->derive == CONCATKDF_MTH && + k->agreement == AGREEMENT_MTH) return k; } return {}; @@ -429,33 +429,33 @@ bool CDoc1::save(const QString &path) }); w.writeNamespace(DS, QStringLiteral("ds")); writeElement(w, DS, QStringLiteral("KeyInfo"), [&]{ - for(const CKey &k: qAsConst(keys)) + for(std::shared_ptr k: qAsConst(keys)) { writeElement(w, DENC, QStringLiteral("EncryptedKey"), [&]{ - if(!k.id.isEmpty()) - w.writeAttribute(QStringLiteral("Id"), k.id); - if(!k.recipient.isEmpty()) - w.writeAttribute(QStringLiteral("Recipient"), k.recipient); + if(!k->id.isEmpty()) + w.writeAttribute(QStringLiteral("Id"), k->id); + if(!k->recipient.isEmpty()) + w.writeAttribute(QStringLiteral("Recipient"), k->recipient); QByteArray cipher; - if(k.isRSA) + if(k->isRSA) { - cipher = Crypto::encrypt(X509_get0_pubkey((const X509*)k.cert.handle()), RSA_PKCS1_PADDING, transportKey); + cipher = Crypto::encrypt(X509_get0_pubkey((const X509*)k->cert.handle()), RSA_PKCS1_PADDING, transportKey); if(cipher.isEmpty()) return; writeElement(w, DENC, QStringLiteral("EncryptionMethod"), { {QStringLiteral("Algorithm"), RSA_MTH}, }); writeElement(w, DS, QStringLiteral("KeyInfo"), [&]{ - if(!k.name.isEmpty()) - w.writeTextElement(DS, QStringLiteral("KeyName"), k.name); + if(!k->name.isEmpty()) + w.writeTextElement(DS, QStringLiteral("KeyName"), k->name); writeElement(w, DS, QStringLiteral("X509Data"), [&]{ - writeBase64Element(w, DS, QStringLiteral("X509Certificate"), k.cert.toDer()); + writeBase64Element(w, DS, QStringLiteral("X509Certificate"), k->cert.toDer()); }); }); } else { - EVP_PKEY *peerPKey = X509_get0_pubkey((const X509*)k.cert.handle()); + EVP_PKEY *peerPKey = X509_get0_pubkey((const X509*)k->cert.handle()); auto priv = Crypto::genECKey(peerPKey); QByteArray sharedSecret = Crypto::derive(priv.get(), peerPKey); if(sharedSecret.isEmpty()) @@ -472,7 +472,7 @@ bool CDoc1::save(const QString &path) default: concatDigest = SHA512_MTH; break; } QByteArray encryptionKey = Crypto::concatKDF(SHA_MTH[concatDigest], KWAES_SIZE[encryptionMethod], - sharedSecret, props.value(QStringLiteral("DocumentFormat")).toUtf8() + SsDer + k.cert.toDer()); + sharedSecret, props.value(QStringLiteral("DocumentFormat")).toUtf8() + SsDer + k->cert.toDer()); #ifndef NDEBUG qDebug() << "ENC Ss" << SsDer.toHex(); qDebug() << "ENC Ksr" << sharedSecret.toHex(); @@ -497,7 +497,7 @@ bool CDoc1::save(const QString &path) writeElement(w, XENC11, QStringLiteral("ConcatKDFParams"), { {QStringLiteral("AlgorithmID"), QStringLiteral("00") + props.value(QStringLiteral("DocumentFormat")).toUtf8().toHex()}, {QStringLiteral("PartyUInfo"), QStringLiteral("00") + SsDer.toHex()}, - {QStringLiteral("PartyVInfo"), QStringLiteral("00") + k.cert.toDer().toHex()}, + {QStringLiteral("PartyVInfo"), QStringLiteral("00") + k->cert.toDer().toHex()}, }, [&]{ writeElement(w, DS, QStringLiteral("DigestMethod"), { {QStringLiteral("Algorithm"), concatDigest}, @@ -517,7 +517,7 @@ bool CDoc1::save(const QString &path) }); writeElement(w, DENC, QStringLiteral("RecipientKeyInfo"), [&]{ writeElement(w, DS, QStringLiteral("X509Data"), [&]{ - writeBase64Element(w, DS, QStringLiteral("X509Certificate"), k.cert.toDer()); + writeBase64Element(w, DS, QStringLiteral("X509Certificate"), k->cert.toDer()); }); }); }); diff --git a/client/CDoc1.h b/client/CDoc1.h index 82d333c1..4b6052c7 100644 --- a/client/CDoc1.h +++ b/client/CDoc1.h @@ -34,7 +34,7 @@ class CDoc1 final: public CDoc, private QFile public: CDoc1() = default; CDoc1(const QString &path); - CKey canDecrypt(const QSslCertificate &cert) const final; + std::shared_ptr canDecrypt(const QSslCertificate &cert) const final; bool decryptPayload(const QByteArray &key) final; bool save(const QString &path) final; QByteArray transportKey(const CKey &key) final; diff --git a/client/CDoc2.cpp b/client/CDoc2.cpp index 02c09989..e0cecb38 100644 --- a/client/CDoc2.cpp +++ b/client/CDoc2.cpp @@ -397,8 +397,8 @@ namespace cdoc20 { CDoc2::CDoc2(const QString &path) : QFile(path) { - using namespace cdoc20::Recipients; - using namespace cdoc20::Header; + using namespace cdoc20::recipients; + using namespace cdoc20::header; setLastError(QStringLiteral("Invalid CDoc 2.0 header")); uint32_t header_len = 0; if(!open(QFile::ReadOnly) || @@ -438,9 +438,9 @@ CDoc2::CDoc2(const QString &path) continue; } auto fillRecipient = [&] (auto key, bool isRSA) { - CKey k(toByteArray(key->recipient_public_key()), isRSA); - k.recipient = toString(recipient->key_label()); - k.cipher = toByteArray(recipient->encrypted_fmk()); + std::shared_ptr k = CKey::fromKey(toByteArray(key->recipient_public_key()), isRSA); + k->recipient = toString(recipient->key_label()); + k->cipher = toByteArray(recipient->encrypted_fmk()); return k; }; switch(recipient->capsule_type()) @@ -453,39 +453,39 @@ CDoc2::CDoc2(const QString &path) qWarning() << "Unsupported ECC curve: skipping"; continue; } - CKey k = fillRecipient(key, false); - k.publicKey = toByteArray(key->sender_public_key()); - keys.append(std::move(k)); + std::shared_ptr k = fillRecipient(key, false); + k->publicKey = toByteArray(key->sender_public_key()); + keys.append(k); } break; case Capsule::RSAPublicKeyCapsule: if(const auto *key = recipient->capsule_as_RSAPublicKeyCapsule()) { - CKey k = fillRecipient(key, true); - k.encrypted_kek = toByteArray(key->encrypted_kek()); - keys.append(std::move(k)); + std::shared_ptr k = fillRecipient(key, true); + k->encrypted_kek = toByteArray(key->encrypted_kek()); + keys.append(k); } break; case Capsule::KeyServerCapsule: if(const auto *server = recipient->capsule_as_KeyServerCapsule()) { auto fillKeyServer = [&] (auto key, bool isRSA) { - CKey k = fillRecipient(key, isRSA); - k.keyserver_id = toString(server->keyserver_id()); - k.transaction_id = toString(server->transaction_id()); + std::shared_ptr k = fillRecipient(key, isRSA); + k->keyserver_id = toString(server->keyserver_id()); + k->transaction_id = toString(server->transaction_id()); return k; }; switch(server->recipient_key_details_type()) { - case ServerDetailsUnion::ServerEccDetails: - if(const auto *eccDetails = server->recipient_key_details_as_ServerEccDetails()) + case KeyDetailsUnion::EccKeyDetails: + if(const auto *eccDetails = server->recipient_key_details_as_EccKeyDetails()) { if(eccDetails->curve() == EllipticCurve::secp384r1) keys.append(fillKeyServer(eccDetails, false)); } break; - case ServerDetailsUnion::ServerRsaDetails: - if(const auto *rsaDetails = server->recipient_key_details_as_ServerRsaDetails()) + case KeyDetailsUnion::RsaKeyDetails: + if(const auto *rsaDetails = server->recipient_key_details_as_RsaKeyDetails()) keys.append(fillKeyServer(rsaDetails, true)); break; default: @@ -493,15 +493,38 @@ CDoc2::CDoc2(const QString &path) } } break; + case Capsule::SymmetricKeyCapsule: + if(const auto *capsule = recipient->capsule_as_SymmetricKeyCapsule()) + { + auto salt = capsule->salt(); + } + break; + case Capsule::PBKDF2Capsule: + if(const auto *capsule = recipient->capsule_as_PBKDF2Capsule()) + { + KDFAlgorithmIdentifier kdf_id = capsule->kdf_algorithm_identifier(); + if (kdf_id != KDFAlgorithmIdentifier::PBKDF2WithHmacSHA256) { + qWarning() << "Unsupported KDF algorithm: skipping"; + continue; + } + auto salt = capsule->salt(); + auto password_salt = capsule->password_salt(); + int32_t kdf_iter = capsule->kdf_iterations(); + } + break; default: qWarning() << "Unsupported Key Details: skipping"; } } } -CKey CDoc2::canDecrypt(const QSslCertificate &cert) const +std::shared_ptr CDoc2::canDecrypt(const QSslCertificate &cert) const { - return keys.value(keys.indexOf(CKey(cert))); + std::shared_ptr t = CKey::fromCertificate(cert); + for (std::shared_ptr k: keys) { + if (*k == *t) return k; + } + return {}; } bool CDoc2::decryptPayload(const QByteArray &fmk) @@ -546,7 +569,7 @@ bool CDoc2::save(const QString &path) #endif flatbuffers::FlatBufferBuilder builder; - std::vector> recipients; + std::vector> recipients; auto toVector = [&builder](const QByteArray &data) { return builder.CreateVector((const uint8_t*)data.data(), size_t(data.length())); }; @@ -554,13 +577,13 @@ bool CDoc2::save(const QString &path) QByteArray utf8 = data.toUtf8(); return builder.CreateString(utf8.data(), size_t(utf8.length())); }; - auto sendToServer = [this](CKey &key, const QByteArray &recipient_id, const QByteArray &key_material, QLatin1String type) { - key.keyserver_id = Settings::CDOC2_DEFAULT_KEYSERVER; - if(key.keyserver_id.isEmpty()) + auto sendToServer = [this](std::shared_ptr key, const QByteArray &recipient_id, const QByteArray &key_material, QLatin1String type) { + key->keyserver_id = Settings::CDOC2_DEFAULT_KEYSERVER; + if(key->keyserver_id.isEmpty()) return setLastError(QStringLiteral("keyserver_id cannot be empty")); - QNetworkRequest req = cdoc20::req(key.keyserver_id); + QNetworkRequest req = cdoc20::req(key->keyserver_id); if(req.url().isEmpty()) - return setLastError(QStringLiteral("No valid config found for keyserver_id: %1").arg(key.keyserver_id)); + return setLastError(QStringLiteral("No valid config found for keyserver_id: %1").arg(key->keyserver_id)); if(!cdoc20::checkConnection()) return false; QScopedPointer nam(CheckConnection::setupNAM(req, Settings::CDOC2_POST_CERT)); @@ -574,64 +597,64 @@ bool CDoc2::save(const QString &path) e.exec(); if(reply->error() == QNetworkReply::NoError && reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 201) - key.transaction_id = QString::fromLatin1(reply->rawHeader("Location")).remove(QLatin1String("/key-capsules/")); + key->transaction_id = QString::fromLatin1(reply->rawHeader("Location")).remove(QLatin1String("/key-capsules/")); else return setLastError(reply->errorString()); - if(key.transaction_id.isEmpty()) + if(key->transaction_id.isEmpty()) return setLastError(QStringLiteral("Failed to post key capsule")); return true; }; - for(CKey &key: keys) + for(std::shared_ptr key: keys) { - if(key.isRSA) + if(key->isRSA) { QByteArray kek = Crypto::random(fmk.size()); QByteArray xor_key = Crypto::xor_data(fmk, kek); - auto publicKey = Crypto::fromRSAPublicKeyDer(key.key); + auto publicKey = Crypto::fromRSAPublicKeyDer(key->key); if(!publicKey) return false; QByteArray encrytpedKek = Crypto::encrypt(publicKey.get(), RSA_PKCS1_OAEP_PADDING, kek); #ifndef NDEBUG - qDebug() << "publicKeyDer" << key.key.toHex(); + qDebug() << "publicKeyDer" << key->key.toHex(); qDebug() << "kek" << kek.toHex(); qDebug() << "xor" << xor_key.toHex(); qDebug() << "encrytpedKek" << encrytpedKek.toHex(); #endif if(!Settings::CDOC2_USE_KEYSERVER) { - auto rsaPublicKey = cdoc20::Recipients::CreateRSAPublicKeyCapsule(builder, - toVector(key.key), toVector(encrytpedKek)); - recipients.push_back(cdoc20::Header::CreateRecipientRecord(builder, - cdoc20::Recipients::Capsule::RSAPublicKeyCapsule, rsaPublicKey.Union(), - toString(key.recipient), toVector(xor_key), cdoc20::Header::FMKEncryptionMethod::XOR)); + auto rsaPublicKey = cdoc20::recipients::CreateRSAPublicKeyCapsule(builder, + toVector(key->key), toVector(encrytpedKek)); + recipients.push_back(cdoc20::header::CreateRecipientRecord(builder, + cdoc20::recipients::Capsule::RSAPublicKeyCapsule, rsaPublicKey.Union(), + toString(key->recipient), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR)); continue; } - if(!sendToServer(key, key.key, encrytpedKek, QLatin1String("rsa"))) + if(!sendToServer(key, key->key, encrytpedKek, QLatin1String("rsa"))) return false; - auto rsaKeyServer = cdoc20::Recipients::CreateServerRsaDetails(builder, toVector(key.key)); - auto keyServer = cdoc20::Recipients::CreateKeyServerCapsule(builder, - cdoc20::Recipients::ServerDetailsUnion::ServerRsaDetails, - rsaKeyServer.Union(), toString(key.keyserver_id), toString(key.transaction_id)); - recipients.push_back(cdoc20::Header::CreateRecipientRecord(builder, - cdoc20::Recipients::Capsule::KeyServerCapsule, keyServer.Union(), - toString(key.recipient), toVector(xor_key), cdoc20::Header::FMKEncryptionMethod::XOR)); + auto rsaKeyServer = cdoc20::recipients::CreateRsaKeyDetails(builder, toVector(key->key)); + auto keyServer = cdoc20::recipients::CreateKeyServerCapsule(builder, + cdoc20::recipients::KeyDetailsUnion::RsaKeyDetails, + rsaKeyServer.Union(), toString(key->keyserver_id), toString(key->transaction_id)); + recipients.push_back(cdoc20::header::CreateRecipientRecord(builder, + cdoc20::recipients::Capsule::KeyServerCapsule, keyServer.Union(), + toString(key->recipient), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR)); continue; } - auto publicKey = Crypto::fromECPublicKeyDer(key.key, NID_secp384r1); + auto publicKey = Crypto::fromECPublicKeyDer(key->key, NID_secp384r1); if(!publicKey) return false; auto ephKey = Crypto::genECKey(publicKey.get()); QByteArray sharedSecret = Crypto::derive(ephKey.get(), publicKey.get()); QByteArray ephPublicKeyDer = Crypto::toPublicKeyDer(ephKey.get()); QByteArray kekPm = Crypto::extract(sharedSecret, KEKPREMASTER); - QByteArray info = KEK + cdoc20::Header::EnumNameFMKEncryptionMethod(cdoc20::Header::FMKEncryptionMethod::XOR) + key.key + ephPublicKeyDer; + QByteArray info = KEK + cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) + key->key + ephPublicKeyDer; QByteArray kek = Crypto::expand(kekPm, info, fmk.size()); QByteArray xor_key = Crypto::xor_data(fmk, kek); #ifndef NDEBUG - qDebug() << "publicKeyDer" << key.key.toHex(); + qDebug() << "publicKeyDer" << key->key.toHex(); qDebug() << "ephPublicKeyDer" << ephPublicKeyDer.toHex(); qDebug() << "sharedSecret" << sharedSecret.toHex(); qDebug() << "kekPm" << kekPm.toHex(); @@ -640,28 +663,28 @@ bool CDoc2::save(const QString &path) #endif if(!Settings::CDOC2_USE_KEYSERVER) { - auto eccPublicKey = cdoc20::Recipients::CreateECCPublicKeyCapsule(builder, - cdoc20::Recipients::EllipticCurve::secp384r1, toVector(key.key), toVector(ephPublicKeyDer)); - recipients.push_back(cdoc20::Header::CreateRecipientRecord(builder, - cdoc20::Recipients::Capsule::ECCPublicKeyCapsule, eccPublicKey.Union(), - toString(key.recipient), toVector(xor_key), cdoc20::Header::FMKEncryptionMethod::XOR)); + auto eccPublicKey = cdoc20::recipients::CreateECCPublicKeyCapsule(builder, + cdoc20::recipients::EllipticCurve::secp384r1, toVector(key->key), toVector(ephPublicKeyDer)); + recipients.push_back(cdoc20::header::CreateRecipientRecord(builder, + cdoc20::recipients::Capsule::ECCPublicKeyCapsule, eccPublicKey.Union(), + toString(key->recipient), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR)); continue; } - if(!sendToServer(key, key.key, ephPublicKeyDer, QLatin1String("ecc_secp384r1"))) + if(!sendToServer(key, key->key, ephPublicKeyDer, QLatin1String("ecc_secp384r1"))) return false; - auto eccKeyServer = cdoc20::Recipients::CreateServerEccDetails(builder, - cdoc20::Recipients::EllipticCurve::secp384r1, toVector(key.key)); - auto keyServer = cdoc20::Recipients::CreateKeyServerCapsule(builder, - cdoc20::Recipients::ServerDetailsUnion::ServerEccDetails, - eccKeyServer.Union(), toString(key.keyserver_id), toString(key.transaction_id)); - recipients.push_back(cdoc20::Header::CreateRecipientRecord(builder, - cdoc20::Recipients::Capsule::KeyServerCapsule, keyServer.Union(), - toString(key.recipient), toVector(xor_key), cdoc20::Header::FMKEncryptionMethod::XOR)); + auto eccKeyServer = cdoc20::recipients::CreateEccKeyDetails(builder, + cdoc20::recipients::EllipticCurve::secp384r1, toVector(key->key)); + auto keyServer = cdoc20::recipients::CreateKeyServerCapsule(builder, + cdoc20::recipients::KeyDetailsUnion::EccKeyDetails, + eccKeyServer.Union(), toString(key->keyserver_id), toString(key->transaction_id)); + recipients.push_back(cdoc20::header::CreateRecipientRecord(builder, + cdoc20::recipients::Capsule::KeyServerCapsule, keyServer.Union(), + toString(key->recipient), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR)); } - auto offset = cdoc20::Header::CreateHeader(builder, builder.CreateVector(recipients), - cdoc20::Header::PayloadEncryptionMethod::CHACHA20POLY1305); + auto offset = cdoc20::header::CreateHeader(builder, builder.CreateVector(recipients), + cdoc20::header::PayloadEncryptionMethod::CHACHA20POLY1305); builder.Finish(offset); QByteArray header = QByteArray::fromRawData((const char*)builder.GetBufferPointer(), int(builder.GetSize())); @@ -749,7 +772,7 @@ QByteArray CDoc2::transportKey(const CKey &_key) #ifndef NDEBUG qDebug() << "kekPm" << kekPm.toHex(); #endif - QByteArray info = KEK + cdoc20::Header::EnumNameFMKEncryptionMethod(cdoc20::Header::FMKEncryptionMethod::XOR) + key.key + key.publicKey; + QByteArray info = KEK + cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) + key.key + key.publicKey; return Crypto::expand(kekPm, info, KEY_LEN); }); if(kek.isEmpty()) diff --git a/client/CDoc2.h b/client/CDoc2.h index 54bcf58d..20f3ba0d 100644 --- a/client/CDoc2.h +++ b/client/CDoc2.h @@ -28,7 +28,7 @@ class CDoc2 final: public CDoc, private QFile { explicit CDoc2() = default; explicit CDoc2(const QString &path); - CKey canDecrypt(const QSslCertificate &cert) const final; + std::shared_ptr canDecrypt(const QSslCertificate &cert) const final; bool decryptPayload(const QByteArray &fmk) final; QByteArray deriveFMK(const QByteArray &priv, const CKey &key); bool isSupported(); diff --git a/client/CryptoDoc.cpp b/client/CryptoDoc.cpp index 2d04f1f2..8eaec2aa 100644 --- a/client/CryptoDoc.cpp +++ b/client/CryptoDoc.cpp @@ -246,6 +246,20 @@ void CKey::setCert(const QSslCertificate &c) isRSA = k.algorithm() == QSsl::Rsa; } +std::shared_ptr +CKey::newEmpty() { + return std::shared_ptr(new CKey()); +} + +std::shared_ptr +CKey::fromKey(QByteArray _key, bool _isRSA) { + return std::shared_ptr(new CKey(_key, _isRSA)); +} + +std::shared_ptr +CKey::fromCertificate(const QSslCertificate &cert) { + return std::shared_ptr(new CKey(cert)); +} CryptoDoc::CryptoDoc( QObject *parent ) : QObject(parent) @@ -257,14 +271,15 @@ CryptoDoc::CryptoDoc( QObject *parent ) CryptoDoc::~CryptoDoc() { clear(); delete d; } -bool CryptoDoc::addKey( const CKey &key ) +bool CryptoDoc::addKey(std::shared_ptr key ) { if(d->isEncryptedWarning()) return false; - if(d->cdoc->keys.contains(key)) - { - WarningDialog::show(tr("Key already exists")); - return false; + for (std::shared_ptr k: d->cdoc->keys) { + if (*k == *key) { + WarningDialog::show(tr("Key already exists")); + return false; + } } d->cdoc->keys.append(key); return true; @@ -272,7 +287,8 @@ bool CryptoDoc::addKey( const CKey &key ) bool CryptoDoc::canDecrypt(const QSslCertificate &cert) { - return !d->cdoc->canDecrypt(cert).key.isEmpty(); + auto ckey = d->cdoc->canDecrypt(cert); + return !ckey->key.isEmpty(); } void CryptoDoc::clear( const QString &file ) @@ -307,14 +323,14 @@ bool CryptoDoc::decrypt() if(!d->isEncrypted) return true; - CKey key = d->cdoc->canDecrypt(qApp->signer()->tokenauth().cert()); - if(key.key.isEmpty()) + auto ckey = d->cdoc->canDecrypt(qApp->signer()->tokenauth().cert()); + if(ckey->key.isEmpty()) { WarningDialog::show(tr("You do not have the key to decrypt this document")); return false; } - if(d->cdoc->version() == 2 && !key.transaction_id.isEmpty() && !Settings::CDOC2_NOTIFICATION.isSet()) + if(d->cdoc->version() == 2 && !ckey->transaction_id.isEmpty() && !Settings::CDOC2_NOTIFICATION.isSet()) { auto *dlg = new WarningDialog(tr("You must enter your PIN code twice in order to decrypt the CDOC2 container. " "The first PIN entry is required for authentication to the key server referenced in the CDOC2 container. " @@ -332,7 +348,7 @@ bool CryptoDoc::decrypt() } } - d->key = d->cdoc->transportKey(key); + d->key = d->cdoc->transportKey(*ckey); #ifndef NDEBUG qDebug() << "Transport key" << d->key.toHex(); #endif @@ -380,7 +396,7 @@ bool CryptoDoc::encrypt( const QString &filename ) QString CryptoDoc::fileName() const { return d->fileName; } -QList CryptoDoc::keys() const +QList> CryptoDoc::keys() const { return d->cdoc->keys; } diff --git a/client/CryptoDoc.h b/client/CryptoDoc.h index 8929550e..b1aca6c8 100644 --- a/client/CryptoDoc.h +++ b/client/CryptoDoc.h @@ -29,12 +29,14 @@ class QSslKey; -class CKey +struct CKey { public: - CKey() = default; - CKey(QByteArray _key, bool _isRSA): key(std::move(_key)), isRSA(_isRSA) {} - CKey(const QSslCertificate &cert); + enum Type { + CERTIFICATE, + SERVER + }; + bool operator==(const CKey &other) const { return other.key == key; } void setCert(const QSslCertificate &c); @@ -49,9 +51,23 @@ class CKey // CDoc2 QByteArray encrypted_kek; QString keyserver_id, transaction_id; + + static std::shared_ptr newEmpty(); + static std::shared_ptr fromKey(QByteArray _key, bool _isRSA); + static std::shared_ptr fromCertificate(const QSslCertificate &cert); +protected: + CKey() = default; + CKey(QByteArray _key, bool _isRSA): key(std::move(_key)), isRSA(_isRSA) {} + CKey(const QSslCertificate &cert); }; +//struct CKeyCert : public CKey { +// +//} +//struct CKeyServer : public CKey { +// +//} class CDoc { @@ -64,14 +80,14 @@ class CDoc }; virtual ~CDoc() = default; - virtual CKey canDecrypt(const QSslCertificate &cert) const = 0; + virtual std::shared_ptr canDecrypt(const QSslCertificate &cert) const = 0; virtual bool decryptPayload(const QByteArray &key) = 0; virtual bool save(const QString &path) = 0; bool setLastError(const QString &msg) { return (lastError = msg).isEmpty(); } virtual QByteArray transportKey(const CKey &key) = 0; virtual int version() = 0; - QList keys; + QList> keys; std::vector files; QString lastError; }; @@ -83,14 +99,14 @@ class CryptoDoc final: public QObject CryptoDoc(QObject *parent = nullptr); ~CryptoDoc() final; - bool addKey( const CKey &key ); + bool addKey(std::shared_ptr key ); bool canDecrypt(const QSslCertificate &cert); void clear(const QString &file = {}); bool decrypt(); DocumentModel* documentModel() const; bool encrypt(const QString &filename = {}); QString fileName() const; - QList keys() const; + QList> keys() const; bool move(const QString &to); bool open( const QString &file ); void removeKey( int id ); diff --git a/client/MainWindow.cpp b/client/MainWindow.cpp index 2f65bffa..28ee4da9 100644 --- a/client/MainWindow.cpp +++ b/client/MainWindow.cpp @@ -508,7 +508,7 @@ void MainWindow::convertToCDoc() auto cardData = qApp->signer()->tokenauth(); if(!cardData.cert().isNull()) - cryptoContainer->addKey(CKey(cardData.cert())); + cryptoContainer->addKey(CKey::fromCertificate(cardData.cert())); resetCryptoDoc(cryptoContainer.release()); resetDigiDoc(nullptr, false); @@ -1122,7 +1122,7 @@ void MainWindow::updateSelectorData(TokenData data) showCardMenu(false); } -void MainWindow::updateKeys(const QList &keys) +void MainWindow::updateKeys(const QList> &keys) { if(!cryptoDoc) return; diff --git a/client/MainWindow.h b/client/MainWindow.h index c6df059f..ef45b267 100644 --- a/client/MainWindow.h +++ b/client/MainWindow.h @@ -105,7 +105,7 @@ private Q_SLOTS: void showPinBlockedWarning(const QSmartCardData& t); void updateSelector(); void updateSelectorData(TokenData data); - void updateKeys(const QList &keys); + void updateKeys(const QList> &keys); void updateMyEID(const TokenData &t); void updateMyEid(const QSmartCardData &data); bool wrap(const QString& wrappedFile, bool enclose); diff --git a/client/dialogs/AddRecipients.cpp b/client/dialogs/AddRecipients.cpp index 24675aa7..714b089a 100644 --- a/client/dialogs/AddRecipients.cpp +++ b/client/dialogs/AddRecipients.cpp @@ -109,7 +109,7 @@ void AddRecipients::addAllRecipientToRightPane() if(rightList.contains(value->getKey())) continue; addRecipientToRightPane(value); - history.append(value->getKey().cert); + history.append(value->getKey()->cert); } ui->confirm->setDisabled(rightList.isEmpty()); historyCertData.addAndSave(history); @@ -169,10 +169,17 @@ AddressItem * AddRecipients::addRecipientToLeftPane(const QSslCertificate& cert) if(leftItem) return leftItem; - leftItem = new AddressItem(CKey(cert), ui->leftPane); + leftItem = new AddressItem(CKey::fromCertificate(cert), ui->leftPane); leftList.insert(cert, leftItem); ui->leftPane->addWidget(leftItem); - bool contains = rightList.contains(cert); + bool contains = false; + std::shared_ptr t = CKey::fromCertificate(cert); + for (std::shared_ptr k: rightList) { + if (*k == *t) { + contains = true; + break; + } + } leftItem->setDisabled(contains); leftItem->showButton(contains ? AddressItem::Added : AddressItem::Add); @@ -186,14 +193,15 @@ AddressItem * AddRecipients::addRecipientToLeftPane(const QSslCertificate& cert) return leftItem; } -bool AddRecipients::addRecipientToRightPane(const CKey &key, bool update) +bool AddRecipients::addRecipientToRightPane(std::shared_ptr key, bool update) { - if (rightList.contains(key)) - return false; + for (std::shared_ptr k: rightList) { + if (*k == *key) return false; + } if(update) { - if(auto expiryDate = key.cert.expiryDate(); expiryDate <= QDateTime::currentDateTime()) + if(auto expiryDate = key->cert.expiryDate(); expiryDate <= QDateTime::currentDateTime()) { if(Settings::CDOC2_DEFAULT && Settings::CDOC2_USE_KEYSERVER) { @@ -210,9 +218,9 @@ bool AddRecipients::addRecipientToRightPane(const CKey &key, bool update) } QSslConfiguration backup = QSslConfiguration::defaultConfiguration(); QSslConfiguration::setDefaultConfiguration(CheckConnection::sslConfiguration()); - QList errors = QSslCertificate::verify({ key.cert }); + QList errors = QSslCertificate::verify({ key->cert }); QSslConfiguration::setDefaultConfiguration(backup); - errors.removeAll(QSslError(QSslError::CertificateExpired, key.cert)); + errors.removeAll(QSslError(QSslError::CertificateExpired, key->cert)); if(!errors.isEmpty()) { auto *dlg = new WarningDialog(tr("Recipient’s certification chain contains certificates that are not trusted. Continue with encryption?"), this); @@ -230,7 +238,7 @@ bool AddRecipients::addRecipientToRightPane(const CKey &key, bool update) connect(rightItem, &AddressItem::remove, this, &AddRecipients::removeRecipientFromRightPane); ui->rightPane->addWidget(rightItem); ui->confirm->setDisabled(rightList.isEmpty()); - historyCertData.addAndSave({key.cert}); + historyCertData.addAndSave({key->cert}); return true; } @@ -270,9 +278,9 @@ bool AddRecipients::isUpdated() const return updated; } -QList AddRecipients::keys() +QList> AddRecipients::keys() { - QList recipients; + QList> recipients; for(auto *item: ui->rightPane->items) { if(auto *address = qobject_cast(item)) @@ -284,7 +292,7 @@ QList AddRecipients::keys() void AddRecipients::removeRecipientFromRightPane(Item *toRemove) { auto *rightItem = qobject_cast(toRemove); - if(auto it = leftList.find(rightItem->getKey().cert); it != leftList.end()) + if(auto it = leftList.find(rightItem->getKey()->cert); it != leftList.end()) { it.value()->setDisabled(false); it.value()->showButton(AddressItem::Add); diff --git a/client/dialogs/AddRecipients.h b/client/dialogs/AddRecipients.h index 4f9e5d2d..67ece87e 100644 --- a/client/dialogs/AddRecipients.h +++ b/client/dialogs/AddRecipients.h @@ -41,7 +41,7 @@ class AddRecipients final : public QDialog explicit AddRecipients(ItemList* itemList, QWidget *parent = nullptr); ~AddRecipients() final; - QList keys(); + QList> keys(); bool isUpdated() const; private: @@ -50,7 +50,7 @@ class AddRecipients final : public QDialog void addRecipientFromFile(); void addRecipientFromHistory(); AddressItem * addRecipientToLeftPane(const QSslCertificate& cert); - bool addRecipientToRightPane(const CKey &key, bool update = true); + bool addRecipientToRightPane(std::shared_ptr key, bool update = true); void addRecipientToRightPane(AddressItem *leftItem, bool update = true); void addSelectedCerts(const QList& selectedCertData); void enableRecipientFromCard(); @@ -64,7 +64,7 @@ class AddRecipients final : public QDialog Ui::AddRecipients *ui; QHash leftList; - QList rightList; + QList> rightList; LdapSearch *ldap_person, *ldap_corp; bool updated = false; diff --git a/client/widgets/AddressItem.cpp b/client/widgets/AddressItem.cpp index b472cbe1..7015da7f 100644 --- a/client/widgets/AddressItem.cpp +++ b/client/widgets/AddressItem.cpp @@ -32,12 +32,12 @@ class AddressItem::Private: public Ui::AddressItem { public: QString code; - CKey key; + std::shared_ptr key; QString label; bool yourself = false; }; -AddressItem::AddressItem(CKey k, QWidget *parent, bool showIcon) +AddressItem::AddressItem(std::shared_ptr k, QWidget *parent, bool showIcon) : Item(parent) , ui(new Private) { @@ -60,12 +60,12 @@ AddressItem::AddressItem(CKey k, QWidget *parent, bool showIcon) ui->add->setFont(Styles::font(Styles::Condensed, 12)); ui->added->setFont(ui->add->font()); - ui->code = SslCertificate(ui->key.cert).personalCode().toHtmlEscaped(); - ui->label = (!ui->key.cert.subjectInfo("GN").isEmpty() && !ui->key.cert.subjectInfo("SN").isEmpty() ? - ui->key.cert.subjectInfo("GN").join(' ') + " " + ui->key.cert.subjectInfo("SN").join(' ') : - ui->key.cert.subjectInfo("CN").join(' ')).toHtmlEscaped(); + ui->code = SslCertificate(ui->key->cert).personalCode().toHtmlEscaped(); + ui->label = (!ui->key->cert.subjectInfo("GN").isEmpty() && !ui->key->cert.subjectInfo("SN").isEmpty() ? + ui->key->cert.subjectInfo("GN").join(' ') + " " + ui->key->cert.subjectInfo("SN").join(' ') : + ui->key->cert.subjectInfo("CN").join(' ')).toHtmlEscaped(); if(ui->label.isEmpty()) - ui->label = ui->key.recipient; + ui->label = ui->key->encrypted_kek; setIdType(); showButton(AddressItem::Remove); } @@ -90,26 +90,26 @@ bool AddressItem::eventFilter(QObject *o, QEvent *e) { if((o == ui->name || o == ui->idType) && e->type() == QEvent::MouseButtonRelease) { - (new KeyDialog(ui->key, this))->open(); + (new KeyDialog(*ui->key, this))->open(); return true; } return Item::eventFilter(o, e); } -const CKey& AddressItem::getKey() const +const std::shared_ptr AddressItem::getKey() const { return ui->key; } -void AddressItem::idChanged(const CKey &key) +void AddressItem::idChanged(std::shared_ptr key) { - ui->yourself = !key.key.isNull() && ui->key == key; + ui->yourself = !key->key.isNull() && *ui->key == *key; setName(); } void AddressItem::idChanged(const SslCertificate &cert) { - idChanged(CKey(cert)); + idChanged(CKey::fromCertificate(cert)); } void AddressItem::initTabOrder(QWidget *item) @@ -128,7 +128,7 @@ QWidget* AddressItem::lastTabWidget() void AddressItem::mouseReleaseEvent(QMouseEvent * /*event*/) { - (new KeyDialog(ui->key, this))->open(); + (new KeyDialog(*ui->key, this))->open(); } void AddressItem::setName() @@ -151,12 +151,12 @@ void AddressItem::stateChange(ContainerState state) void AddressItem::setIdType() { - ui->idType->setHidden(ui->key.cert.isNull()); - if(ui->key.cert.isNull()) + ui->idType->setHidden(ui->key->cert.isNull()); + if(ui->key->cert.isNull()) return; QString str; - SslCertificate cert(ui->key.cert); + SslCertificate cert(ui->key->cert); SslCertificate::CertType type = cert.type(); if(type & SslCertificate::DigiIDType) str = tr("digi-ID"); diff --git a/client/widgets/AddressItem.h b/client/widgets/AddressItem.h index 54d3788d..80b22ff4 100644 --- a/client/widgets/AddressItem.h +++ b/client/widgets/AddressItem.h @@ -36,11 +36,11 @@ class AddressItem final : public Item Added, }; - explicit AddressItem(CKey k, QWidget *parent = {}, bool showIcon = false); + explicit AddressItem(std::shared_ptr k, QWidget *parent = {}, bool showIcon = false); ~AddressItem() final; - const CKey& getKey() const; - void idChanged(const CKey &cert); + const std::shared_ptr getKey() const; + void idChanged(std::shared_ptr cert); void idChanged(const SslCertificate &cert) final; void initTabOrder(QWidget *item) final; QWidget* lastTabWidget() final; diff --git a/client/widgets/ContainerPage.cpp b/client/widgets/ContainerPage.cpp index 8fa79bc2..ec254840 100644 --- a/client/widgets/ContainerPage.cpp +++ b/client/widgets/ContainerPage.cpp @@ -294,7 +294,7 @@ void ContainerPage::transition(CryptoDoc *container, const QSslCertificate &cert if(!container) return; setHeader(container->fileName()); - for(const CKey &key: container->keys()) + for(std::shared_ptr key: container->keys()) ui->rightPane->addWidget(new AddressItem(key, ui->rightPane, true)); ui->leftPane->setModel(container->documentModel()); updatePanes(container->state()); @@ -369,7 +369,7 @@ void ContainerPage::update(bool canDecrypt, CryptoDoc* container) hasEmptyFile = false; ui->rightPane->clear(); - for(const CKey &key: container->keys()) + for(std::shared_ptr key: container->keys()) ui->rightPane->addWidget(new AddressItem(key, ui->rightPane, true)); if(container->state() & UnencryptedContainer) showMainAction({ EncryptContainer }); diff --git a/client/widgets/ContainerPage.h b/client/widgets/ContainerPage.h index f035d2b6..ad8a8f49 100644 --- a/client/widgets/ContainerPage.h +++ b/client/widgets/ContainerPage.h @@ -58,7 +58,7 @@ class ContainerPage final : public QWidget void action(int code, const QString &info1 = {}, const QString &info2 = {}); void addFiles(const QStringList &files); void fileRemoved(int row); - void keysSelected(const QList &keys); + void keysSelected(const QList> &keys); void moved(const QString &to); void removed(int row); void warning(const WarningText &warningText); diff --git a/schema/header.fbs b/schema/header.fbs index 3c242f20..77747a96 100644 --- a/schema/header.fbs +++ b/schema/header.fbs @@ -1,25 +1,7 @@ -/* - * QDigiDocClient - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - + include "recipients.fbs"; -namespace cdoc20.Header; +namespace cdoc20.header; // FMK encryption method enum. enum FMKEncryptionMethod:byte { @@ -38,7 +20,7 @@ enum PayloadEncryptionMethod:byte { // Thus it is better to have an an array of tables that // contains the union as a field. table RecipientRecord { - capsule: cdoc20.Recipients.Capsule; + capsule: recipients.Capsule; key_label: string (required); encrypted_fmk: [ubyte] (required); fmk_encryption_method: FMKEncryptionMethod = UNKNOWN; @@ -47,6 +29,7 @@ table RecipientRecord { // Header structure. table Header { recipients: [RecipientRecord]; + payload_encryption_method: PayloadEncryptionMethod = UNKNOWN; } diff --git a/schema/recipients.fbs b/schema/recipients.fbs index 99bfbe50..1cba3d33 100644 --- a/schema/recipients.fbs +++ b/schema/recipients.fbs @@ -1,36 +1,18 @@ -/* - * QDigiDocClient - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -namespace cdoc20.Recipients; +namespace cdoc20.recipients; // Union for communicating the recipient type union Capsule { ECCPublicKeyCapsule, RSAPublicKeyCapsule, KeyServerCapsule, - SymmetricKeyCapsule + SymmetricKeyCapsule, + PBKDF2Capsule } -//for future proofing and data type -union ServerDetailsUnion { - ServerEccDetails, - ServerRsaDetails +//server recipient type +union KeyDetailsUnion { + EccKeyDetails, RsaKeyDetails } // Elliptic curve type enum for ECCPublicKey recipient @@ -39,17 +21,24 @@ enum EllipticCurve:byte { secp384r1 } -table ServerRsaDetails { - //RSA pub key in DER - recipient_public_key: [ubyte] (required); +// KDF algorithm identifier enum +enum KDFAlgorithmIdentifier:byte { + UNKNOWN, + PBKDF2WithHmacSHA256 } -table ServerEccDetails { - // Elliptic curve type enum - curve: EllipticCurve = UNKNOWN; - //EC pub key in TLS format - //for secp384r1 curve: 0x04 + X 48 coord bytes + Y coord 48 bytes) - recipient_public_key: [ubyte] (required); +table RsaKeyDetails { + //RSA pub key in DER - RFC8017 RSA Public Key Syntax (A.1.1) https://www.rfc-editor.org/rfc/rfc8017#page-54 + recipient_public_key: [ubyte] (required); +} + +table EccKeyDetails { + // Elliptic curve type enum + curve: EllipticCurve = UNKNOWN; + + //EC pub key in TLS 1.3 format https://datatracker.ietf.org/doc/html/rfc8446#section-4.2.8.2 + //for secp384r1 curve: 0x04 + X 48 coord bytes + Y coord 48 bytes) + recipient_public_key: [ubyte] (required); } // ECC public key recipient @@ -64,8 +53,10 @@ table RSAPublicKeyCapsule { encrypted_kek: [ubyte] (required); } +// recipient where ephemeral key material is download from server (server scenarios) table KeyServerCapsule { - recipient_key_details: ServerDetailsUnion; + // recipient id - key type specific. For public key cryptography this is usually recipient public key + recipient_key_details: KeyDetailsUnion; keyserver_id: string (required); transaction_id: string (required); } @@ -74,3 +65,12 @@ table KeyServerCapsule { table SymmetricKeyCapsule { salt: [ubyte] (required); } + +// password derived key +table PBKDF2Capsule { + // HKDF salt to derive KEK + salt: [ubyte] (required); + password_salt: [ubyte] (required); + kdf_algorithm_identifier: KDFAlgorithmIdentifier = UNKNOWN; + kdf_iterations: int32; +} From 21b7968c4fed026a418ad7800019f3a783c063dd Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Thu, 9 May 2024 16:46:04 +0300 Subject: [PATCH 02/22] Implemented password-based decryption flow Signed-off-by: Lauris Kaplinski --- client/CDoc1.cpp | 64 +++- client/CDoc1.h | 12 +- client/CDoc2.cpp | 509 ++++++++++++++++++++---------- client/CDoc2.h | 17 +- client/CMakeLists.txt | 1 + client/CryptoDoc.cpp | 80 +++-- client/CryptoDoc.h | 102 ++++-- client/MainWindow.cpp | 49 ++- client/MainWindow.h | 4 +- client/dialogs/AddRecipients.cpp | 80 +++-- client/dialogs/KeyDialog.cpp | 45 ++- client/dialogs/PasswordDialog.cpp | 19 ++ client/dialogs/PasswordDialog.h | 23 ++ client/dialogs/PasswordDialog.ui | 84 +++++ client/widgets/AddressItem.cpp | 43 ++- client/widgets/AddressItem.h | 3 + client/widgets/AddressItem.ui | 159 +++++----- client/widgets/ContainerPage.cpp | 25 +- client/widgets/ContainerPage.h | 6 +- 19 files changed, 910 insertions(+), 415 deletions(-) create mode 100644 client/dialogs/PasswordDialog.cpp create mode 100644 client/dialogs/PasswordDialog.h create mode 100644 client/dialogs/PasswordDialog.ui diff --git a/client/CDoc1.cpp b/client/CDoc1.cpp index c5e224e8..68d18040 100644 --- a/client/CDoc1.cpp +++ b/client/CDoc1.cpp @@ -68,6 +68,16 @@ const QHash CDoc1::SHA_MTH{ }; const QHash CDoc1::KWAES_SIZE{{KWAES128_MTH, 16}, {KWAES192_MTH, 24}, {KWAES256_MTH, 32}}; +const QByteArray XML_TAG = ""; + +bool CDoc1::isCDoc1File(const QString& path) +{ + QFile f(path); + if(!f.open(QFile::ReadOnly)) return false; + if (f.read(XML_TAG.length()) != XML_TAG) return false; + return true; +} + CDoc1::CDoc1(const QString &path) : QFile(path) { @@ -107,7 +117,7 @@ CDoc1::CDoc1(const QString &path) if(xml.name() != QLatin1String("EncryptedKey")) return; - std::shared_ptr key = CKey::newEmpty(); + std::shared_ptr key = CKeyCD1::newEmpty(); key->id = xml.attributes().value(QLatin1String("Id")).toString(); key->recipient = xml.attributes().value(QLatin1String("Recipient")).toString(); while(!xml.atEnd()) @@ -178,6 +188,15 @@ CDoc1::CDoc1(const QString &path) } } +std::unique_ptr +CDoc1::load(const QString& path) +{ + CDoc1 *cdoc = new CDoc1(path); + if (!cdoc->keys.isEmpty()) return std::unique_ptr(cdoc); + delete cdoc; + return nullptr; +} + bool CDoc1::decryptPayload(const QByteArray &key) { if(!isOpen()) @@ -267,10 +286,20 @@ bool CDoc1::decryptPayload(const QByteArray &key) return !files.empty(); } -std::shared_ptr CDoc1::canDecrypt(const QSslCertificate &cert) const +CKey::DecryptionStatus +CDoc1::canDecrypt(const QSslCertificate &cert) const +{ + std::shared_ptr key = getDecryptionKey(cert); + if (!key) return CKey::DecryptionStatus::CAN_DECRYPT; + return CKey::DecryptionStatus::CANNOT_DECRYPT; +} + +std::shared_ptr CDoc1::getDecryptionKey(const QSslCertificate &cert) const { - for(std::shared_ptr k: qAsConst(keys)) + for(std::shared_ptr key: qAsConst(keys)) { + if (key->type != CKey::Type::CDOC1) continue; + std::shared_ptr k = std::static_pointer_cast(key); if(!ENC_MTH.contains(method) || k->cert != cert || k->cipher.isEmpty()) @@ -429,15 +458,19 @@ bool CDoc1::save(const QString &path) }); w.writeNamespace(DS, QStringLiteral("ds")); writeElement(w, DS, QStringLiteral("KeyInfo"), [&]{ - for(std::shared_ptr k: qAsConst(keys)) + for(std::shared_ptr key: qAsConst(keys)) { + if (key->type != CKey::Type::CDOC1) { + return; + } + std::shared_ptr k = std::static_pointer_cast(key); writeElement(w, DENC, QStringLiteral("EncryptedKey"), [&]{ if(!k->id.isEmpty()) w.writeAttribute(QStringLiteral("Id"), k->id); if(!k->recipient.isEmpty()) w.writeAttribute(QStringLiteral("Recipient"), k->recipient); QByteArray cipher; - if(k->isRSA) + if(k->pk_type == CKey::PKType::RSA) { cipher = Crypto::encrypt(X509_get0_pubkey((const X509*)k->cert.handle()), RSA_PKCS1_PADDING, transportKey); if(cipher.isEmpty()) @@ -546,27 +579,32 @@ bool CDoc1::save(const QString &path) return true; } -QByteArray CDoc1::transportKey(const CKey &key) +QByteArray CDoc1::getFMK(const CKey &key, const QByteArray& secret) { + if (key.type != CKey::Type::CDOC1) { + setLastError(QStringLiteral("Not a CDoc1 key")); + return {}; + } + const CKeyCD1& ckey = static_cast(key); setLastError({}); - QByteArray decryptedKey = qApp->signer()->decrypt([&key](QCryptoBackend *backend) { - if(key.isRSA) - return backend->decrypt(key.cipher, false); - return backend->deriveConcatKDF(key.publicKey, SHA_MTH[key.concatDigest], - int(KWAES_SIZE[key.method]), key.AlgorithmID, key.PartyUInfo, key.PartyVInfo); + QByteArray decryptedKey = qApp->signer()->decrypt([&ckey](QCryptoBackend *backend) { + if(ckey.pk_type == CKey::PKType::RSA) + return backend->decrypt(ckey.cipher, false); + return backend->deriveConcatKDF(ckey.publicKey, SHA_MTH[ckey.concatDigest], + int(KWAES_SIZE[ckey.method]), ckey.AlgorithmID, ckey.PartyUInfo, ckey.PartyVInfo); }); if(decryptedKey.isEmpty()) { setLastError(QStringLiteral("Failed to decrypt/derive key")); return {}; } - if(key.isRSA) + if(ckey.pk_type == CKey::PKType::RSA) return decryptedKey; #ifndef NDEBUG qDebug() << "DEC Ss" << key.publicKey.toHex(); qDebug() << "DEC ConcatKDF" << decryptedKey.toHex(); #endif - return Crypto::aes_wrap(decryptedKey, key.cipher, false); + return Crypto::aes_wrap(decryptedKey, ckey.cipher, false); } int CDoc1::version() diff --git a/client/CDoc1.h b/client/CDoc1.h index 4b6052c7..6d0262f5 100644 --- a/client/CDoc1.h +++ b/client/CDoc1.h @@ -33,14 +33,20 @@ class CDoc1 final: public CDoc, private QFile { public: CDoc1() = default; - CDoc1(const QString &path); - std::shared_ptr canDecrypt(const QSslCertificate &cert) const final; + + static bool isCDoc1File(const QString& path); + + CKey::DecryptionStatus canDecrypt(const QSslCertificate &cert) const final; + std::shared_ptr getDecryptionKey(const QSslCertificate &cert) const final; bool decryptPayload(const QByteArray &key) final; bool save(const QString &path) final; - QByteArray transportKey(const CKey &key) final; + QByteArray getFMK(const CKey &key, const QByteArray& secret) final; int version() final; + static std::unique_ptr load(const QString& path); private: + CDoc1(const QString &path); + void writeDDoc(QIODevice *ddoc); static QByteArray fromBase64(QStringView data); diff --git a/client/CDoc2.cpp b/client/CDoc2.cpp index e0cecb38..b4c971b6 100644 --- a/client/CDoc2.cpp +++ b/client/CDoc2.cpp @@ -30,6 +30,7 @@ #include "Utils.h" #include "header_generated.h" #include "effects/FadeInNotification.h" +#include "openssl/kdf.h" #include #include @@ -40,18 +41,24 @@ #include #include #include +#include #include #include +using cdoc20::recipients::KeyServerCapsule; +using cdoc20::recipients::KeyDetailsUnion; +using cdoc20::recipients::EccKeyDetails; +using cdoc20::recipients::RsaKeyDetails; + const QByteArray CDoc2::LABEL = "CDOC\x02"; -const QByteArray CDoc2::CEK = "CDOC20cek"; -const QByteArray CDoc2::HMAC = "CDOC20hmac"; -const QByteArray CDoc2::KEK = "CDOC20kek"; -const QByteArray CDoc2::KEKPREMASTER = "CDOC20kekpremaster"; -const QByteArray CDoc2::PAYLOAD = "CDOC20payload"; -const QByteArray CDoc2::SALT = "CDOC20salt"; +const QByteArray CDoc2::CEK = "CDOC2cek"; +const QByteArray CDoc2::HMAC = "CDOC2hmac"; +const QByteArray CDoc2::KEK = "CDOC2kek"; +const QByteArray CDoc2::KEKPREMASTER = "CDOC2kekpremaster"; +const QByteArray CDoc2::PAYLOAD = "CDOC2payload"; +const QByteArray CDoc2::SALT = "CDOC2salt"; namespace cdoc20 { bool checkConnection() { @@ -394,24 +401,36 @@ namespace cdoc20 { const int TAR::Header::Size = int(sizeof(TAR::Header)); } -CDoc2::CDoc2(const QString &path) - : QFile(path) +bool +CDoc2::isCDoc2File(const QString& path) +{ + QFile f(path); + if(!f.open(QFile::ReadOnly)) return false; + if (f.read(LABEL.length()) != LABEL) return false; + return true; +} + +CDoc2::CDoc2(const QString &_path) +: path(_path) +/* : QFile(path) */ { using namespace cdoc20::recipients; using namespace cdoc20::header; setLastError(QStringLiteral("Invalid CDoc 2.0 header")); uint32_t header_len = 0; - if(!open(QFile::ReadOnly) || - read(LABEL.length()) != LABEL || - read((char*)&header_len, int(sizeof(header_len))) != int(sizeof(header_len))) + QFile ifs(path); + if(!ifs.open(QFile::ReadOnly) || + ifs.read(LABEL.length()) != LABEL || + ifs.read((char*)&header_len, int(sizeof(header_len))) != int(sizeof(header_len))) return; - header_data = read(qFromBigEndian(header_len)); + header_data = ifs.read(qFromBigEndian(header_len)); if(header_data.size() != qFromBigEndian(header_len)) return; - headerHMAC = read(KEY_LEN); + headerHMAC = ifs.read(KEY_LEN); if(headerHMAC.size() != KEY_LEN) return; - noncePos = pos(); + noncePos = ifs.pos(); + ifs.close(); flatbuffers::Verifier verifier(reinterpret_cast(header_data.data()), header_data.size()); if(!VerifyHeaderBuffer(verifier)) return; @@ -437,9 +456,9 @@ CDoc2::CDoc2(const QString &path) qWarning() << "Unsupported FMK encryption method: skipping"; continue; } - auto fillRecipient = [&] (auto key, bool isRSA) { - std::shared_ptr k = CKey::fromKey(toByteArray(key->recipient_public_key()), isRSA); - k->recipient = toString(recipient->key_label()); + auto fillRecipientPK = [&] (CKey::PKType pk_type, auto key) { + std::shared_ptr k(new CKeyPK(pk_type, toByteArray(key->recipient_public_key()))); + k->label = toString(recipient->key_label()); k->cipher = toByteArray(recipient->encrypted_fmk()); return k; }; @@ -453,7 +472,7 @@ CDoc2::CDoc2(const QString &path) qWarning() << "Unsupported ECC curve: skipping"; continue; } - std::shared_ptr k = fillRecipient(key, false); + std::shared_ptr k = fillRecipientPK(CKey::PKType::ECC, key); k->publicKey = toByteArray(key->sender_public_key()); keys.append(k); } @@ -461,55 +480,74 @@ CDoc2::CDoc2(const QString &path) case Capsule::RSAPublicKeyCapsule: if(const auto *key = recipient->capsule_as_RSAPublicKeyCapsule()) { - std::shared_ptr k = fillRecipient(key, true); + std::shared_ptr k = fillRecipientPK(CKey::PKType::RSA, key); k->encrypted_kek = toByteArray(key->encrypted_kek()); keys.append(k); } break; case Capsule::KeyServerCapsule: - if(const auto *server = recipient->capsule_as_KeyServerCapsule()) - { - auto fillKeyServer = [&] (auto key, bool isRSA) { - std::shared_ptr k = fillRecipient(key, isRSA); - k->keyserver_id = toString(server->keyserver_id()); - k->transaction_id = toString(server->transaction_id()); - return k; - }; - switch(server->recipient_key_details_type()) - { + if (const KeyServerCapsule *server = recipient->capsule_as_KeyServerCapsule()) { + KeyDetailsUnion details = server->recipient_key_details_type(); + std::shared_ptr ckey = nullptr; + switch (details) { case KeyDetailsUnion::EccKeyDetails: - if(const auto *eccDetails = server->recipient_key_details_as_EccKeyDetails()) - { - if(eccDetails->curve() == EllipticCurve::secp384r1) - keys.append(fillKeyServer(eccDetails, false)); + if(const EccKeyDetails *eccDetails = server->recipient_key_details_as_EccKeyDetails()) { + if(eccDetails->curve() == EllipticCurve::secp384r1) { + ckey = CKeyServer::fromKey(toByteArray(eccDetails->recipient_public_key()), CKey::PKType::ECC); + } else { + qWarning() << "Unsupported elliptic curve key type"; + } + } else { + qWarning() << "Invalid file format"; } break; case KeyDetailsUnion::RsaKeyDetails: - if(const auto *rsaDetails = server->recipient_key_details_as_RsaKeyDetails()) - keys.append(fillKeyServer(rsaDetails, true)); + if(const RsaKeyDetails *rsaDetails = server->recipient_key_details_as_RsaKeyDetails()) { + ckey = CKeyServer::fromKey(toByteArray(rsaDetails->recipient_public_key()), CKey::PKType::RSA); + } else { + qWarning() << "Invalid file format"; + } break; default: qWarning() << "Unsupported Key Server Details: skipping"; } + if (ckey) { + ckey->label = toString(recipient->key_label()); + ckey->cipher = toByteArray(recipient->encrypted_fmk()); + ckey->keyserver_id = toString(server->keyserver_id()); + ckey->transaction_id = toString(server->transaction_id()); + keys.append(ckey); + } + } else { + qWarning() << "Invalid file format"; } break; case Capsule::SymmetricKeyCapsule: if(const auto *capsule = recipient->capsule_as_SymmetricKeyCapsule()) { auto salt = capsule->salt(); + std::shared_ptr key(new CKeySymmetric(toByteArray(salt))); + key->label = toString(recipient->key_label()); + key->cipher = toByteArray(recipient->encrypted_fmk()); + keys.append(key); } break; case Capsule::PBKDF2Capsule: - if(const auto *capsule = recipient->capsule_as_PBKDF2Capsule()) - { + if(const auto *capsule = recipient->capsule_as_PBKDF2Capsule()) { KDFAlgorithmIdentifier kdf_id = capsule->kdf_algorithm_identifier(); if (kdf_id != KDFAlgorithmIdentifier::PBKDF2WithHmacSHA256) { qWarning() << "Unsupported KDF algorithm: skipping"; continue; } auto salt = capsule->salt(); - auto password_salt = capsule->password_salt(); + auto pw_salt = capsule->password_salt(); int32_t kdf_iter = capsule->kdf_iterations(); + std::shared_ptr key(new CKeySymmetric(toByteArray(salt))); + key->label = toString(recipient->key_label()); + key->cipher = toByteArray(recipient->encrypted_fmk()); + key->pw_salt = toByteArray(pw_salt); + key->kdf_iter = kdf_iter; + keys.append(key); } break; default: @@ -518,23 +556,59 @@ CDoc2::CDoc2(const QString &path) } } -std::shared_ptr CDoc2::canDecrypt(const QSslCertificate &cert) const +std::unique_ptr +CDoc2::load(const QString& path) { - std::shared_ptr t = CKey::fromCertificate(cert); - for (std::shared_ptr k: keys) { - if (*k == *t) return k; + CDoc2 *cdoc = new CDoc2(path); + return std::unique_ptr(cdoc); +} + +CKey::DecryptionStatus +CDoc2::canDecrypt(const QSslCertificate &cert) const +{ + std::shared_ptr t = CKeyCD1::fromCertificate(cert); + for (std::shared_ptr key: keys) { + switch (key->type) { + case CKey::Type::CDOC1: + if (*key == *t) return CKey::CAN_DECRYPT; + break; + case CKey::Type::SYMMETRIC_KEY: + return CKey::DecryptionStatus::NEED_KEY; + case CKey::Type::PUBLICKEY: + case CKey::Type::SERVER: + break; + } + } + return CKey::DecryptionStatus::CANNOT_DECRYPT; +} + +std::shared_ptr CDoc2::getDecryptionKey(const QSslCertificate &cert) const +{ + std::shared_ptr t = CKeyCD1::fromCertificate(cert); + for (std::shared_ptr key: keys) { + switch (key->type) { + case CKey::Type::CDOC1: + if (*key == *t) return key; + break; + case CKey::Type::SYMMETRIC_KEY: + return key; + case CKey::Type::PUBLICKEY: + case CKey::Type::SERVER: + break; + } } return {}; } bool CDoc2::decryptPayload(const QByteArray &fmk) { - if(!isOpen() || noncePos == -1) - return false; + QFile ifs(path); + if(!ifs.open(QFile::ReadOnly)) return false; + if (noncePos < 0) return false; setLastError({}); - seek(noncePos); + ifs.seek(noncePos); QByteArray cek = Crypto::expand(fmk, CEK); - QByteArray nonce = read(NONCE_LEN); + QByteArray nonce = ifs.read(NONCE_LEN); #ifndef NDEBUG qDebug() << "cek" << cek.toHex(); qDebug() << "nonce" << nonce.toHex(); @@ -543,10 +617,11 @@ bool CDoc2::decryptPayload(const QByteArray &fmk) if(!dec.updateAAD(PAYLOAD + header_data + headerHMAC)) return false; bool warning = false; - files = cdoc20::TAR(std::unique_ptr(new cdoc20::stream(this, &dec))).files(warning); + files = cdoc20::TAR(std::unique_ptr(new cdoc20::stream(&ifs, &dec))).files(warning); if(warning) setLastError(tr("CDoc contains additional payload data that is not part of content")); - QByteArray tag = read(16); + QByteArray tag = ifs.read(16); + ifs.close(); #ifndef NDEBUG qDebug() << "tag" << tag.toHex(); #endif @@ -556,7 +631,48 @@ bool CDoc2::decryptPayload(const QByteArray &fmk) return !files.empty(); } -bool CDoc2::save(const QString &path) +class TmpFile : public QFile { +public: + /* Existing file */ + std::filesystem::path current_name; + /* Temporary file */ + std::filesystem::path tmp_name; + + TmpFile(const QString& name) : current_name(name.toStdString()) {} + + ~TmpFile() { + /* Close it to avoid overloaded close() to be called */ + if (isOpen()) QFile::close(); + } + + /* "Open" the file - by actually opening temporary in the same directory */ + bool open() { + if (isOpen()) return false; + tmp_name = current_name; + tmp_name.replace_filename("CDOCXXXXXX.tmp"); + /* C++ is a mess here so we go the easy way */ + char *tmp_str = ::strdup(tmp_name.c_str()); + int tmp_handle = ::mkstemps(tmp_str, 4); + tmp_name = tmp_str; + ::free (tmp_str); + if (!QFile::open(tmp_handle, QFile::WriteOnly, QFileDevice::AutoCloseHandle)) { + /* Have to close handle directly */ + ::close(tmp_handle); + ::unlink(tmp_name.c_str()); + return false; + } + return true; + } + + void close() { + if (!isOpen()) return; + QFile::close(); + /* Atomic rename */ + ::rename(tmp_name.c_str(), current_name.c_str()); + } +}; + +bool CDoc2::save(const QString &_path) { setLastError({}); QByteArray fmk = Crypto::extract(Crypto::random(KEY_LEN), SALT); @@ -577,7 +693,7 @@ bool CDoc2::save(const QString &path) QByteArray utf8 = data.toUtf8(); return builder.CreateString(utf8.data(), size_t(utf8.length())); }; - auto sendToServer = [this](std::shared_ptr key, const QByteArray &recipient_id, const QByteArray &key_material, QLatin1String type) { + auto sendToServer = [this](std::shared_ptr key, const QByteArray &recipient_id, const QByteArray &key_material, QLatin1String type) { key->keyserver_id = Settings::CDOC2_DEFAULT_KEYSERVER; if(key->keyserver_id.isEmpty()) return setLastError(QStringLiteral("keyserver_id cannot be empty")); @@ -605,10 +721,14 @@ bool CDoc2::save(const QString &path) return true; }; - for(std::shared_ptr key: keys) - { - if(key->isRSA) - { + for(std::shared_ptr key: keys) { + QString label; + if (CKeyCD1::isCDoc1Key(*key)) { + label = static_cast(*key).recipient; + } else { + label = static_cast(*key).label; + } + if(key->pk_type == CKey::PKType::RSA) { QByteArray kek = Crypto::random(fmk.size()); QByteArray xor_key = Crypto::xor_data(fmk, kek); auto publicKey = Crypto::fromRSAPublicKeyDer(key->key); @@ -621,66 +741,73 @@ bool CDoc2::save(const QString &path) qDebug() << "xor" << xor_key.toHex(); qDebug() << "encrytpedKek" << encrytpedKek.toHex(); #endif - if(!Settings::CDOC2_USE_KEYSERVER) - { + if(!Settings::CDOC2_USE_KEYSERVER) { auto rsaPublicKey = cdoc20::recipients::CreateRSAPublicKeyCapsule(builder, toVector(key->key), toVector(encrytpedKek)); - recipients.push_back(cdoc20::header::CreateRecipientRecord(builder, + auto offs = cdoc20::header::CreateRecipientRecord(builder, cdoc20::recipients::Capsule::RSAPublicKeyCapsule, rsaPublicKey.Union(), - toString(key->recipient), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR)); - continue; + toString(label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); + recipients.push_back(offs); + } else { + if (key->type != CKey::Type::SERVER) { + return setLastError(QStringLiteral("Not a server key")); + } + const std::shared_ptr skey = std::static_pointer_cast(key); + if(!sendToServer(skey, skey->key, encrytpedKek, QLatin1String("rsa"))) + return false; + auto rsaKeyServer = cdoc20::recipients::CreateRsaKeyDetails(builder, toVector(skey->key)); + auto keyServer = cdoc20::recipients::CreateKeyServerCapsule(builder, + cdoc20::recipients::KeyDetailsUnion::RsaKeyDetails, + rsaKeyServer.Union(), toString(skey->keyserver_id), toString(skey->transaction_id)); + auto offs = cdoc20::header::CreateRecipientRecord(builder, + cdoc20::recipients::Capsule::KeyServerCapsule, keyServer.Union(), + toString(label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); + recipients.push_back(offs); } - - if(!sendToServer(key, key->key, encrytpedKek, QLatin1String("rsa"))) + } else { + auto publicKey = Crypto::fromECPublicKeyDer(key->key, NID_secp384r1); + if(!publicKey) return false; - auto rsaKeyServer = cdoc20::recipients::CreateRsaKeyDetails(builder, toVector(key->key)); - auto keyServer = cdoc20::recipients::CreateKeyServerCapsule(builder, - cdoc20::recipients::KeyDetailsUnion::RsaKeyDetails, - rsaKeyServer.Union(), toString(key->keyserver_id), toString(key->transaction_id)); - recipients.push_back(cdoc20::header::CreateRecipientRecord(builder, - cdoc20::recipients::Capsule::KeyServerCapsule, keyServer.Union(), - toString(key->recipient), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR)); - continue; - } - - auto publicKey = Crypto::fromECPublicKeyDer(key->key, NID_secp384r1); - if(!publicKey) - return false; - auto ephKey = Crypto::genECKey(publicKey.get()); - QByteArray sharedSecret = Crypto::derive(ephKey.get(), publicKey.get()); - QByteArray ephPublicKeyDer = Crypto::toPublicKeyDer(ephKey.get()); - QByteArray kekPm = Crypto::extract(sharedSecret, KEKPREMASTER); - QByteArray info = KEK + cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) + key->key + ephPublicKeyDer; - QByteArray kek = Crypto::expand(kekPm, info, fmk.size()); - QByteArray xor_key = Crypto::xor_data(fmk, kek); -#ifndef NDEBUG - qDebug() << "publicKeyDer" << key->key.toHex(); - qDebug() << "ephPublicKeyDer" << ephPublicKeyDer.toHex(); - qDebug() << "sharedSecret" << sharedSecret.toHex(); - qDebug() << "kekPm" << kekPm.toHex(); - qDebug() << "kek" << kek.toHex(); - qDebug() << "xor" << xor_key.toHex(); -#endif - if(!Settings::CDOC2_USE_KEYSERVER) - { - auto eccPublicKey = cdoc20::recipients::CreateECCPublicKeyCapsule(builder, - cdoc20::recipients::EllipticCurve::secp384r1, toVector(key->key), toVector(ephPublicKeyDer)); - recipients.push_back(cdoc20::header::CreateRecipientRecord(builder, - cdoc20::recipients::Capsule::ECCPublicKeyCapsule, eccPublicKey.Union(), - toString(key->recipient), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR)); - continue; + auto ephKey = Crypto::genECKey(publicKey.get()); + QByteArray sharedSecret = Crypto::derive(ephKey.get(), publicKey.get()); + QByteArray ephPublicKeyDer = Crypto::toPublicKeyDer(ephKey.get()); + QByteArray kekPm = Crypto::extract(sharedSecret, KEKPREMASTER); + QByteArray info = KEK + cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) + key->key + ephPublicKeyDer; + QByteArray kek = Crypto::expand(kekPm, info, fmk.size()); + QByteArray xor_key = Crypto::xor_data(fmk, kek); + #ifndef NDEBUG + qDebug() << "publicKeyDer" << key->key.toHex(); + qDebug() << "ephPublicKeyDer" << ephPublicKeyDer.toHex(); + qDebug() << "sharedSecret" << sharedSecret.toHex(); + qDebug() << "kekPm" << kekPm.toHex(); + qDebug() << "kek" << kek.toHex(); + qDebug() << "xor" << xor_key.toHex(); + #endif + if(!Settings::CDOC2_USE_KEYSERVER) { + auto eccPublicKey = cdoc20::recipients::CreateECCPublicKeyCapsule(builder, + cdoc20::recipients::EllipticCurve::secp384r1, toVector(key->key), toVector(ephPublicKeyDer)); + auto offs = cdoc20::header::CreateRecipientRecord(builder, + cdoc20::recipients::Capsule::ECCPublicKeyCapsule, eccPublicKey.Union(), + toString(label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); + recipients.push_back(offs); + } else { + if (key->type != CKey::Type::SERVER) { + return setLastError(QStringLiteral("Not a server key")); + } + const std::shared_ptr skey = std::static_pointer_cast(key); + if(!sendToServer(skey, skey->key, ephPublicKeyDer, QLatin1String("ecc_secp384r1"))) + return false; + auto eccKeyServer = cdoc20::recipients::CreateEccKeyDetails(builder, + cdoc20::recipients::EllipticCurve::secp384r1, toVector(skey->key)); + auto keyServer = cdoc20::recipients::CreateKeyServerCapsule(builder, + cdoc20::recipients::KeyDetailsUnion::EccKeyDetails, + eccKeyServer.Union(), toString(skey->keyserver_id), toString(skey->transaction_id)); + auto offs = cdoc20::header::CreateRecipientRecord(builder, + cdoc20::recipients::Capsule::KeyServerCapsule, keyServer.Union(), + toString(label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); + recipients.push_back(offs); + } } - - if(!sendToServer(key, key->key, ephPublicKeyDer, QLatin1String("ecc_secp384r1"))) - return false; - auto eccKeyServer = cdoc20::recipients::CreateEccKeyDetails(builder, - cdoc20::recipients::EllipticCurve::secp384r1, toVector(key->key)); - auto keyServer = cdoc20::recipients::CreateKeyServerCapsule(builder, - cdoc20::recipients::KeyDetailsUnion::EccKeyDetails, - eccKeyServer.Union(), toString(key->keyserver_id), toString(key->transaction_id)); - recipients.push_back(cdoc20::header::CreateRecipientRecord(builder, - cdoc20::recipients::Capsule::KeyServerCapsule, keyServer.Union(), - toString(key->recipient), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR)); } auto offset = cdoc20::header::CreateHeader(builder, builder.CreateVector(recipients), @@ -697,94 +824,130 @@ bool CDoc2::save(const QString &path) Crypto::Cipher enc(EVP_chacha20_poly1305(), cek, nonce, true); enc.updateAAD(PAYLOAD + header + headerHMAC); auto header_len = qToBigEndian(uint32_t(header.size())); - remove(); - QFile file(path); - if(!file.open(QFile::WriteOnly)) - return setLastError(file.errorString()); - file.write(LABEL); - file.write((const char*)&header_len, int(sizeof(header_len))); - file.write(header); - file.write(headerHMAC); - file.write(nonce); - if(!cdoc20::TAR(std::unique_ptr(new cdoc20::stream(&file, &enc))).save(files)) - { - file.remove(); + + /* Use TmpFile wrapper so the original will not be lost during error */ + TmpFile tmp(_path); + if (!tmp.open()) { + setLastError(tmp.errorString()); return false; } - if(!enc.result()) - { - file.remove(); + /* Write contents to temporary */ + tmp.write(LABEL); + tmp.write((const char*)&header_len, qint64(sizeof(header_len))); + tmp.write(header); + tmp.write(headerHMAC); + tmp.write(nonce); + if(!cdoc20::TAR(std::unique_ptr(new cdoc20::stream(&tmp, &enc))).save(files)) { + return false; + } + if(!enc.result()) { return false; } QByteArray tag = enc.tag(); #ifndef NDEBUG qDebug() << "tag" << tag.toHex(); #endif - file.write(tag); + tmp.write(tag); + tmp.close(); + this->path = _path; return true; } -QByteArray CDoc2::transportKey(const CKey &_key) +QByteArray CDoc2::fetchKeyMaterial(const CKeyServer& key) { - setLastError({}); - CKey key = _key; - if(!key.transaction_id.isEmpty()) - { - QNetworkRequest req = cdoc20::req(key.keyserver_id, key.transaction_id); - if(req.url().isEmpty()) - { - setLastError(QStringLiteral("No valid config found for keyserver_id: %1").arg(key.keyserver_id)); - return {}; - } - if(!cdoc20::checkConnection()) - return {}; - auto authKey = dispatchToMain(&QSigner::key, qApp->signer()); - QScopedPointer nam( - CheckConnection::setupNAM(req, qApp->signer()->tokenauth().cert(), authKey, Settings::CDOC2_GET_CERT)); - QEventLoop e; - QNetworkReply *reply = nam->get(req); - connect(reply, &QNetworkReply::finished, &e, &QEventLoop::quit); - e.exec(); - if(authKey.handle()) - qApp->signer()->logout(); - if(reply->error() != QNetworkReply::NoError && - reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 201) - { - setLastError(reply->errorString()); - return {}; - } - QJsonObject json = QJsonDocument::fromJson(reply->readAll()).object(); - QByteArray key_material = QByteArray::fromBase64( - json.value(QLatin1String("ephemeral_key_material")).toString().toLatin1()); - if(json.value(QLatin1String("capsule_type")) == QLatin1String("rsa")) - key.encrypted_kek = std::move(key_material); - else - key.publicKey = std::move(key_material); + QNetworkRequest req = cdoc20::req(key.keyserver_id, key.transaction_id); + if(req.url().isEmpty()) { + setLastError(QStringLiteral("No valid config found for keyserver_id: %1").arg(key.keyserver_id)); + return {}; } + if(!cdoc20::checkConnection()) { + return {}; + } + auto authKey = dispatchToMain(&QSigner::key, qApp->signer()); + QScopedPointer nam( + CheckConnection::setupNAM(req, qApp->signer()->tokenauth().cert(), authKey, Settings::CDOC2_GET_CERT)); + QEventLoop e; + QNetworkReply *reply = nam->get(req); + connect(reply, &QNetworkReply::finished, &e, &QEventLoop::quit); + e.exec(); + if(authKey.handle()) { + qApp->signer()->logout(); + } + if(reply->error() != QNetworkReply::NoError && reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 201) { + setLastError(reply->errorString()); + return {}; + } + QJsonObject json = QJsonDocument::fromJson(reply->readAll()).object(); + QByteArray key_material = QByteArray::fromBase64(json.value(QLatin1String("ephemeral_key_material")).toString().toLatin1()); + return std::move(key_material); +} + +QByteArray CDoc2::getFMK(const CKey &_key, const QByteArray& secret) +{ + setLastError({}); + QByteArray kek; + if (_key.type == CKey::Type::SYMMETRIC_KEY) { + const CKeySymmetric &sk = static_cast(_key); + qDebug() << "Secret:" << secret.toHex(); + if (sk.kdf_iter > 0) { + // Password key + // PasswordSalt_i = Capsule_i.PasswordSalt + // PasswordKeyMaterial_i = PBKDF2(Password_i, PasswordSalt_i) + QByteArray PasswordKeyMaterial_i = QPasswordDigestor::deriveKeyPbkdf2(QCryptographicHash::Sha256, secret, sk.pw_salt, sk.kdf_iter, 32); + qDebug() << "PasswordKeyMaterial_i:" << PasswordKeyMaterial_i.toHex(); + // KeyMaterialSalt_i = Capsule_i.KeyMaterialSalt + // EncryptedFMK_i = Container_i.EncryptedFMK + // KEK_i = HKDF(KeyMaterialSalt_i, PasswordKeyMaterial_i) + + QByteArray info = KEK + cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) + secret; + + kek = Crypto::hkdf(PasswordKeyMaterial_i, sk.salt, info, 32, EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND); + qDebug() << "kek:" << kek.toHex(); + // FMK = XOR(KEK_i, EncryptedFMK_i) + // CEK = HKDF-Expand(FMK) + + } + } else { + QByteArray key_material; + if(_key.type == CKey::Type::SERVER) { + key_material = fetchKeyMaterial(static_cast(_key)); + } else if (_key.type == CKey::PUBLICKEY) { + const CKeyPK& pk = static_cast(_key); + if (_key.pk_type == CKey::PKType::RSA) { + key_material = pk.encrypted_kek; + } else { + key_material = pk.publicKey; + } + } #ifndef NDEBUG - qDebug() << "publicKeyDer" << key.key.toHex(); - qDebug() << "ephPublicKeyDer" << key.publicKey.toHex(); + qDebug() << "publicKeyDer" << _key.key.toHex(); + qDebug() << "Key material" << key_material.toHex(); #endif - QByteArray kek = qApp->signer()->decrypt([&key](QCryptoBackend *backend) { - if(key.isRSA) - return backend->decrypt(key.encrypted_kek, true); - QByteArray kekPm = backend->deriveHMACExtract(key.publicKey, KEKPREMASTER, KEY_LEN); + if (_key.pk_type == CKey::PKType::RSA) { + kek = qApp->signer()->decrypt([&key_material](QCryptoBackend *backend) { + return backend->decrypt(key_material, true); + }); + } else { + kek = qApp->signer()->decrypt([&_key, &key_material](QCryptoBackend *backend) { + QByteArray kekPm = backend->deriveHMACExtract(key_material, KEKPREMASTER, KEY_LEN); #ifndef NDEBUG - qDebug() << "kekPm" << kekPm.toHex(); + qDebug() << "kekPm" << kekPm.toHex(); #endif - QByteArray info = KEK + cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) + key.key + key.publicKey; - return Crypto::expand(kekPm, info, KEY_LEN); - }); - if(kek.isEmpty()) - { + QByteArray info = KEK + cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) + _key.key + key_material; + return Crypto::expand(kekPm, info, KEY_LEN); + }); + } + } + + if(kek.isEmpty()) { setLastError(QStringLiteral("Failed to derive key")); return {}; } - QByteArray fmk = Crypto::xor_data(key.cipher, kek); + QByteArray fmk = Crypto::xor_data(_key.cipher, kek); QByteArray hhk = Crypto::expand(fmk, HMAC); #ifndef NDEBUG qDebug() << "kek" << kek.toHex(); - qDebug() << "xor" << key.cipher.toHex(); + qDebug() << "xor" << _key.cipher.toHex(); qDebug() << "fmk" << fmk.toHex(); qDebug() << "hhk" << hhk.toHex(); qDebug() << "hmac" << headerHMAC.toHex(); diff --git a/client/CDoc2.h b/client/CDoc2.h index 20f3ba0d..951a490e 100644 --- a/client/CDoc2.h +++ b/client/CDoc2.h @@ -23,23 +23,30 @@ #include -class CDoc2 final: public CDoc, private QFile { +class CDoc2 final: public CDoc, private QObject /*, private QFile */ { public: explicit CDoc2() = default; - explicit CDoc2(const QString &path); - std::shared_ptr canDecrypt(const QSslCertificate &cert) const final; + static bool isCDoc2File(const QString& path); + + CKey::DecryptionStatus canDecrypt(const QSslCertificate &cert) const final; + std::shared_ptr getDecryptionKey(const QSslCertificate &cert) const final; bool decryptPayload(const QByteArray &fmk) final; QByteArray deriveFMK(const QByteArray &priv, const CKey &key); - bool isSupported(); bool save(const QString &path) final; - QByteArray transportKey(const CKey &key) final; + QByteArray getFMK(const CKey &key, const QByteArray& secret) final; int version() final; + static std::unique_ptr load(const QString& _path); private: + CDoc2(const QString &path); + + QString path; QByteArray header_data, headerHMAC; qint64 noncePos = -1; static const QByteArray LABEL, CEK, HMAC, KEK, KEKPREMASTER, PAYLOAD, SALT; static constexpr int KEY_LEN = 32, NONCE_LEN = 12; + + QByteArray fetchKeyMaterial(const CKeyServer& key); }; diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 8986587b..c7174b30 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -85,6 +85,7 @@ add_executable(${PROJECT_NAME} WIN32 MACOSX_BUNDLE TokenData.cpp TokenData.h Utils.h + dialogs/PasswordDialog.h dialogs/PasswordDialog.cpp dialogs/PasswordDialog.ui ) target_link_libraries(${PROJECT_NAME} diff --git a/client/CryptoDoc.cpp b/client/CryptoDoc.cpp index 8eaec2aa..a32c7e04 100644 --- a/client/CryptoDoc.cpp +++ b/client/CryptoDoc.cpp @@ -60,7 +60,7 @@ class CryptoDoc::Private final: public QThread std::unique_ptr cdoc; QString fileName; - QByteArray key; + QByteArray fmk; bool isEncrypted = false; CDocumentModel *documents = new CDocumentModel(this); QStringList tempFiles; @@ -80,7 +80,7 @@ void CryptoDoc::Private::run() if(isEncrypted) { qCDebug(CRYPTO) << "Decrypt" << fileName; - isEncrypted = !cdoc->decryptPayload(key); + isEncrypted = !cdoc->decryptPayload(fmk); } else { @@ -217,7 +217,8 @@ QString CDocumentModel::save(int row, const QString &path) const return fileName; } -CKey::CKey(const QSslCertificate &c) +CKeyCD1::CKeyCD1(const QSslCertificate &c) +: CKey(Type::CDOC1) { setCert(c); recipient = [](const SslCertificate &c) { @@ -238,27 +239,32 @@ CKey::CKey(const QSslCertificate &c) }(c); } -void CKey::setCert(const QSslCertificate &c) -{ - QSslKey k = c.publicKey(); - cert = c; - key = Crypto::toPublicKeyDer(k); - isRSA = k.algorithm() == QSsl::Rsa; +std::shared_ptr +CKeyCD1::newEmpty() { + return std::shared_ptr(new CKeyCD1()); } -std::shared_ptr -CKey::newEmpty() { - return std::shared_ptr(new CKey()); +std::shared_ptr +CKeyCD1::fromKey(QByteArray _key, PKType _pk_type) { + return std::shared_ptr(new CKeyCD1(_key, _pk_type)); } -std::shared_ptr -CKey::fromKey(QByteArray _key, bool _isRSA) { - return std::shared_ptr(new CKey(_key, _isRSA)); +std::shared_ptr +CKeyCD1::fromCertificate(const QSslCertificate &cert) { + return std::shared_ptr(new CKeyCD1(cert)); +} + +void CKeyCD1::setCert(const QSslCertificate &c) +{ + QSslKey k = c.publicKey(); + cert = c; + key = Crypto::toPublicKeyDer(k); + pk_type = (k.algorithm() == QSsl::Rsa) ? PKType::RSA : PKType::ECC; } -std::shared_ptr -CKey::fromCertificate(const QSslCertificate &cert) { - return std::shared_ptr(new CKey(cert)); +std::shared_ptr +CKeyServer::fromKey(QByteArray _key, PKType _pk_type) { + return std::shared_ptr(new CKeyServer(_key, _pk_type)); } CryptoDoc::CryptoDoc( QObject *parent ) @@ -287,8 +293,10 @@ bool CryptoDoc::addKey(std::shared_ptr key ) bool CryptoDoc::canDecrypt(const QSslCertificate &cert) { - auto ckey = d->cdoc->canDecrypt(cert); - return !ckey->key.isEmpty(); + //std::shared_ptr ckey = d->cdoc->getDecryptionKey(cert); + //return (ckey != nullptr) && !ckey->key.isEmpty(); + CKey::DecryptionStatus dec_stat = d->cdoc->canDecrypt(cert); + return (dec_stat == CKey::CAN_DECRYPT) || (dec_stat == CKey::DecryptionStatus::NEED_KEY); } void CryptoDoc::clear( const QString &file ) @@ -313,7 +321,7 @@ ContainerState CryptoDoc::state() const return d->isEncrypted ? EncryptedContainer : UnencryptedContainer; } -bool CryptoDoc::decrypt() +bool CryptoDoc::decrypt(std::shared_ptr key, const QByteArray& secret) { if( d->fileName.isEmpty() ) { @@ -323,14 +331,15 @@ bool CryptoDoc::decrypt() if(!d->isEncrypted) return true; - auto ckey = d->cdoc->canDecrypt(qApp->signer()->tokenauth().cert()); - if(ckey->key.isEmpty()) - { + if (key == nullptr) { + key = d->cdoc->getDecryptionKey(qApp->signer()->tokenauth().cert()); + } + if((key == nullptr) || (key->key.isEmpty() && secret.isEmpty())) { WarningDialog::show(tr("You do not have the key to decrypt this document")); return false; } - if(d->cdoc->version() == 2 && !ckey->transaction_id.isEmpty() && !Settings::CDOC2_NOTIFICATION.isSet()) + if(d->cdoc->version() == 2 && (key->type == CKey::Type::SERVER) && !Settings::CDOC2_NOTIFICATION.isSet()) { auto *dlg = new WarningDialog(tr("You must enter your PIN code twice in order to decrypt the CDOC2 container. " "The first PIN entry is required for authentication to the key server referenced in the CDOC2 container. " @@ -348,12 +357,11 @@ bool CryptoDoc::decrypt() } } - d->key = d->cdoc->transportKey(*ckey); + d->fmk = d->cdoc->getFMK(*key, secret); #ifndef NDEBUG - qDebug() << "Transport key" << d->key.toHex(); + qDebug() << "FMK (Transport key)" << d->fmk.toHex(); #endif - if(d->key.isEmpty()) - { + if(d->fmk.isEmpty()) { WarningDialog::show(tr("Failed to decrypt document. Please check your internet connection and network settings."), d->cdoc->lastError); return false; } @@ -415,11 +423,17 @@ bool CryptoDoc::move(const QString &to) bool CryptoDoc::open( const QString &file ) { clear(file); - d->cdoc = std::make_unique(d->fileName); - if(d->cdoc->keys.isEmpty()) - d->cdoc = std::make_unique(d->fileName); + if (CDoc2::isCDoc2File(d->fileName)) { + d->cdoc = CDoc2::load(d->fileName); + } else if (CDoc1::isCDoc1File(d->fileName)) { + d->cdoc = CDoc1::load(d->fileName); + } else { + WarningDialog::show(tr("Failed to open document"), tr("Unsupported file format")); + return false; + } d->isEncrypted = bool(d->cdoc); - if(!d->isEncrypted || d->cdoc->keys.isEmpty()) + // fixme: This seems wrong + if(!d->isEncrypted) { WarningDialog::show(tr("Failed to open document"), d->cdoc->lastError); return false; diff --git a/client/CryptoDoc.h b/client/CryptoDoc.h index b1aca6c8..8b3755ff 100644 --- a/client/CryptoDoc.h +++ b/client/CryptoDoc.h @@ -33,41 +33,89 @@ struct CKey { public: enum Type { - CERTIFICATE, + CDOC1, + SYMMETRIC_KEY, + PUBLICKEY, SERVER }; - bool operator==(const CKey &other) const { return other.key == key; } + enum PKType { + ECC, + RSA + }; - void setCert(const QSslCertificate &c); + enum DecryptionStatus { + CANNOT_DECRYPT, + CAN_DECRYPT, + NEED_KEY + }; + + Type type; + PKType pk_type; + + bool operator==(const CKey &other) const { return other.key == key; } QByteArray key, cipher, publicKey; - QSslCertificate cert; - bool isRSA = false; - QString recipient; - // CDoc1 + +protected: + CKey(Type _type) : type(_type), pk_type(PKType::ECC) {}; + CKey(Type _type, PKType _pk_type, QByteArray _key): type(_type), pk_type(_pk_type), key(std::move(_key)) {} +}; + +// CDoc1 key + +struct CKeyCD1 : public CKey { QString agreement, concatDigest, derive, method, id, name; QByteArray AlgorithmID, PartyUInfo, PartyVInfo; - // CDoc2 - QByteArray encrypted_kek; - QString keyserver_id, transaction_id; + QSslCertificate cert; + QString recipient; - static std::shared_ptr newEmpty(); - static std::shared_ptr fromKey(QByteArray _key, bool _isRSA); - static std::shared_ptr fromCertificate(const QSslCertificate &cert); + void setCert(const QSslCertificate &c); + + static std::shared_ptr newEmpty(); + static std::shared_ptr fromKey(QByteArray _key, PKType _pk_type); + static std::shared_ptr fromCertificate(const QSslCertificate &cert); + + static bool isCDoc1Key(const CKey& key) { return key.type == Type::CDOC1; } protected: - CKey() = default; - CKey(QByteArray _key, bool _isRSA): key(std::move(_key)), isRSA(_isRSA) {} - CKey(const QSslCertificate &cert); + CKeyCD1() : CKey(Type::CDOC1) {}; + CKeyCD1(QByteArray _key, PKType _pk_type) : CKey(Type::CDOC1, _pk_type, _key) {} + CKeyCD1(const QSslCertificate &cert); }; -//struct CKeyCert : public CKey { -// -//} +struct CKeyCD2 : public CKey { + QString label; + CKeyCD2(Type type) : CKey(type) {}; + CKeyCD2(Type _type, PKType _pk_type, QByteArray _key): CKey(_type, _pk_type, _key) {} + + static bool isCDoc2Key(const CKey& key) { return (key.type == Type::SYMMETRIC_KEY) || (key.type == Type::PUBLICKEY) || (key.type == Type::SERVER); } +}; + +// Symmetric key (plain or PBKDF) + +struct CKeySymmetric : public CKeyCD2 { + QByteArray salt; + // PBKDF + QByteArray pw_salt; + int32_t kdf_iter; // 0 symmetric key, >0 password + + CKeySymmetric(const QByteArray& _salt) : CKeyCD2(Type::SYMMETRIC_KEY), salt(_salt), kdf_iter(0) {} +}; -//struct CKeyServer : public CKey { -// -//} +struct CKeyPK : public CKeyCD2 { + QByteArray encrypted_kek; + + CKeyPK() : CKeyCD2(Type::PUBLICKEY) {}; + CKeyPK(PKType _pk_type, QByteArray _key) : CKeyCD2(Type::PUBLICKEY, _pk_type, _key) {}; +}; + +struct CKeyServer : public CKeyCD2 { + QString keyserver_id, transaction_id; + + static std::shared_ptr fromKey(QByteArray _key, PKType _pk_type); +protected: + CKeyServer(QByteArray _key, PKType _pk_type) : CKeyCD2(Type::SERVER, _pk_type, _key) {}; +}; class CDoc { @@ -80,11 +128,13 @@ class CDoc }; virtual ~CDoc() = default; - virtual std::shared_ptr canDecrypt(const QSslCertificate &cert) const = 0; - virtual bool decryptPayload(const QByteArray &key) = 0; + // Return affirmative if keys match or NEED_KEY if document includes symmetric key */ + virtual CKey::DecryptionStatus canDecrypt(const QSslCertificate &cert) const = 0; + virtual std::shared_ptr getDecryptionKey(const QSslCertificate &cert) const = 0; + virtual bool decryptPayload(const QByteArray &fmk) = 0; virtual bool save(const QString &path) = 0; bool setLastError(const QString &msg) { return (lastError = msg).isEmpty(); } - virtual QByteArray transportKey(const CKey &key) = 0; + virtual QByteArray getFMK(const CKey &key, const QByteArray& secret) = 0; virtual int version() = 0; QList> keys; @@ -102,7 +152,7 @@ class CryptoDoc final: public QObject bool addKey(std::shared_ptr key ); bool canDecrypt(const QSslCertificate &cert); void clear(const QString &file = {}); - bool decrypt(); + bool decrypt(std::shared_ptr key, const QByteArray& secret); DocumentModel* documentModel() const; bool encrypt(const QString &filename = {}); QString fileName() const; diff --git a/client/MainWindow.cpp b/client/MainWindow.cpp index 28ee4da9..b7bb96d1 100644 --- a/client/MainWindow.cpp +++ b/client/MainWindow.cpp @@ -36,6 +36,7 @@ #include "effects/Overlay.h" #include "dialogs/FileDialog.h" #include "dialogs/MobileProgress.h" +#include "dialogs/PasswordDialog.h" #include "dialogs/RoleAddressDialog.h" #include "dialogs/SettingsDialog.h" #include "dialogs/SmartIDProgress.h" @@ -159,6 +160,8 @@ MainWindow::MainWindow( QWidget *parent ) connect(ui->cryptoContainerPage, &ContainerPage::keysSelected, this, &MainWindow::updateKeys); connect(ui->cryptoContainerPage, &ContainerPage::removed, this, &MainWindow::removeAddress); + connect(ui->cryptoContainerPage, &ContainerPage::decryptReq, this, &MainWindow::decryptClicked); + connect(ui->accordion, &Accordion::changePin1Clicked, this, &MainWindow::changePin1Clicked); connect(ui->accordion, &Accordion::changePin2Clicked, this, &MainWindow::changePin2Clicked); connect(ui->accordion, &Accordion::changePukClicked, this, &MainWindow::changePukClicked); @@ -262,14 +265,28 @@ ContainerState MainWindow::currentState() return ContainerState::Uninitialized; } -bool MainWindow::decrypt() +void MainWindow::decrypt(std::shared_ptr key) { - if(!cryptoDoc) - return false; - - WaitDialogHolder waitDialog(this, tr("Decrypting")); - - return cryptoDoc->decrypt(); + if(!cryptoDoc) return; + + QByteArray secret; + if (key->type == CKey::Type::SYMMETRIC_KEY) { + std::shared_ptr skey = std::static_pointer_cast(key); + qDebug() << skey->label; + PasswordDialog p; + if(!p.exec()) return; + QString pwd = p.password(); + qDebug() << pwd; + secret = pwd.toUtf8(); + } + + WaitDialogHolder waitDialog(this, tr("Decrypting")); + + if (cryptoDoc->decrypt(key, secret)) { + ui->cryptoContainerPage->transition(cryptoDoc, qApp->signer()->tokenauth().cert()); + auto *notification = new FadeInNotification(this, WHITE, MANTIS, 110); + notification->start( tr("Decryption succeeded!"), 750, 3000, 1200 ); + } } void MainWindow::dragEnterEvent(QDragEnterEvent *event) @@ -508,7 +525,7 @@ void MainWindow::convertToCDoc() auto cardData = qApp->signer()->tokenauth(); if(!cardData.cert().isNull()) - cryptoContainer->addKey(CKey::fromCertificate(cardData.cert())); + cryptoContainer->addKey(CKeyCD1::fromCertificate(cardData.cert())); resetCryptoDoc(cryptoContainer.release()); resetDigiDoc(nullptr, false); @@ -546,13 +563,8 @@ void MainWindow::onCryptoAction(int action, const QString &/*id*/, const QString convertToBDoc(); break; case DecryptContainer: - case DecryptToken: - if(decrypt()) - { - ui->cryptoContainerPage->transition(cryptoDoc, qApp->signer()->tokenauth().cert()); - auto *notification = new FadeInNotification(this, WHITE, MANTIS, 110); - notification->start( tr("Decryption succeeded!"), 750, 3000, 1200 ); - } + case DecryptToken: + decrypt(nullptr); break; case EncryptContainer: if(encrypt()) @@ -1154,3 +1166,10 @@ void MainWindow::containerSummary() dialog->exec(); dialog->deleteLater(); } + +void +MainWindow::decryptClicked(std::shared_ptr key) +{ + qDebug() << "Decrypt clicked:"; + decrypt(key); +} diff --git a/client/MainWindow.h b/client/MainWindow.h index ef45b267..e6fb63e9 100644 --- a/client/MainWindow.h +++ b/client/MainWindow.h @@ -72,7 +72,7 @@ private Q_SLOTS: void convertToBDoc(); void convertToCDoc(); ria::qdigidoc4::ContainerState currentState(); - bool decrypt(); + void decrypt(std::shared_ptr key); bool encrypt(); void loadPicture(); void moveCryptoContainer(); @@ -120,4 +120,6 @@ private Q_SLOTS: DigiDoc* digiDoc = nullptr; Ui::MainWindow *ui; WarningList *warnings; + + void decryptClicked(std::shared_ptr key); }; diff --git a/client/dialogs/AddRecipients.cpp b/client/dialogs/AddRecipients.cpp index 714b089a..fdd41049 100644 --- a/client/dialogs/AddRecipients.cpp +++ b/client/dialogs/AddRecipients.cpp @@ -109,7 +109,11 @@ void AddRecipients::addAllRecipientToRightPane() if(rightList.contains(value->getKey())) continue; addRecipientToRightPane(value); - history.append(value->getKey()->cert); + std::shared_ptr key = value->getKey(); + if (key->type == CKey::Type::CDOC1) { + std::shared_ptr kd = std::static_pointer_cast(key); + history.append(kd->cert); + } } ui->confirm->setDisabled(rightList.isEmpty()); historyCertData.addAndSave(history); @@ -169,11 +173,11 @@ AddressItem * AddRecipients::addRecipientToLeftPane(const QSslCertificate& cert) if(leftItem) return leftItem; - leftItem = new AddressItem(CKey::fromCertificate(cert), ui->leftPane); + leftItem = new AddressItem(CKeyCD1::fromCertificate(cert), ui->leftPane); leftList.insert(cert, leftItem); ui->leftPane->addWidget(leftItem); bool contains = false; - std::shared_ptr t = CKey::fromCertificate(cert); + std::shared_ptr t = CKeyCD1::fromCertificate(cert); for (std::shared_ptr k: rightList) { if (*k == *t) { contains = true; @@ -201,33 +205,36 @@ bool AddRecipients::addRecipientToRightPane(std::shared_ptr key, bool upda if(update) { - if(auto expiryDate = key->cert.expiryDate(); expiryDate <= QDateTime::currentDateTime()) - { - if(Settings::CDOC2_DEFAULT && Settings::CDOC2_USE_KEYSERVER) + if (key->type == CKey::Type::CDOC1) { + std::shared_ptr kd = std::static_pointer_cast(key); + if(auto expiryDate = kd->cert.expiryDate(); expiryDate <= QDateTime::currentDateTime()) { - WarningDialog::show(this, tr("Failed to add certificate. An expired certificate cannot be used for encryption.")); - return false; + if(Settings::CDOC2_DEFAULT && Settings::CDOC2_USE_KEYSERVER) + { + WarningDialog::show(this, tr("Failed to add certificate. An expired certificate cannot be used for encryption.")); + return false; + } + auto *dlg = new WarningDialog(tr("Are you sure that you want use certificate for encrypting, which expired on %1?
" + "When decrypter has updated certificates then decrypting is impossible.") + .arg(expiryDate.toString(QStringLiteral("dd.MM.yyyy hh:mm:ss"))), this); + dlg->setCancelText(WarningDialog::NO); + dlg->addButton(WarningDialog::YES, QMessageBox::Yes); + if(dlg->exec() != QMessageBox::Yes) + return false; + } + QSslConfiguration backup = QSslConfiguration::defaultConfiguration(); + QSslConfiguration::setDefaultConfiguration(CheckConnection::sslConfiguration()); + QList errors = QSslCertificate::verify({ kd->cert }); + QSslConfiguration::setDefaultConfiguration(backup); + errors.removeAll(QSslError(QSslError::CertificateExpired, kd->cert)); + if(!errors.isEmpty()) + { + auto *dlg = new WarningDialog(tr("Recipient’s certification chain contains certificates that are not trusted. Continue with encryption?"), this); + dlg->setCancelText(WarningDialog::NO); + dlg->addButton(WarningDialog::YES, QMessageBox::Yes); + if(dlg->exec() != QMessageBox::Yes) + return false; } - auto *dlg = new WarningDialog(tr("Are you sure that you want use certificate for encrypting, which expired on %1?
" - "When decrypter has updated certificates then decrypting is impossible.") - .arg(expiryDate.toString(QStringLiteral("dd.MM.yyyy hh:mm:ss"))), this); - dlg->setCancelText(WarningDialog::NO); - dlg->addButton(WarningDialog::YES, QMessageBox::Yes); - if(dlg->exec() != QMessageBox::Yes) - return false; - } - QSslConfiguration backup = QSslConfiguration::defaultConfiguration(); - QSslConfiguration::setDefaultConfiguration(CheckConnection::sslConfiguration()); - QList errors = QSslCertificate::verify({ key->cert }); - QSslConfiguration::setDefaultConfiguration(backup); - errors.removeAll(QSslError(QSslError::CertificateExpired, key->cert)); - if(!errors.isEmpty()) - { - auto *dlg = new WarningDialog(tr("Recipient’s certification chain contains certificates that are not trusted. Continue with encryption?"), this); - dlg->setCancelText(WarningDialog::NO); - dlg->addButton(WarningDialog::YES, QMessageBox::Yes); - if(dlg->exec() != QMessageBox::Yes) - return false; } } updated = update; @@ -238,7 +245,10 @@ bool AddRecipients::addRecipientToRightPane(std::shared_ptr key, bool upda connect(rightItem, &AddressItem::remove, this, &AddRecipients::removeRecipientFromRightPane); ui->rightPane->addWidget(rightItem); ui->confirm->setDisabled(rightList.isEmpty()); - historyCertData.addAndSave({key->cert}); + if (key->type == CKey::Type::CDOC1) { + std::shared_ptr kd = std::static_pointer_cast(key); + historyCertData.addAndSave({kd->cert}); + } return true; } @@ -292,10 +302,14 @@ QList> AddRecipients::keys() void AddRecipients::removeRecipientFromRightPane(Item *toRemove) { auto *rightItem = qobject_cast(toRemove); - if(auto it = leftList.find(rightItem->getKey()->cert); it != leftList.end()) - { - it.value()->setDisabled(false); - it.value()->showButton(AddressItem::Add); + std::shared_ptr key = rightItem->getKey(); + if (key->type == CKey::Type::CDOC1) { + std::shared_ptr kd = std::static_pointer_cast(key); + if(auto it = leftList.find(kd->cert); it != leftList.end()) + { + it.value()->setDisabled(false); + it.value()->showButton(AddressItem::Add); + } } rightList.removeAll(rightItem->getKey()); updated = true; diff --git a/client/dialogs/KeyDialog.cpp b/client/dialogs/KeyDialog.cpp index b9b662ca..72f0dcc5 100644 --- a/client/dialogs/KeyDialog.cpp +++ b/client/dialogs/KeyDialog.cpp @@ -49,10 +49,15 @@ KeyDialog::KeyDialog( const CKey &k, QWidget *parent ) d->view->setHeaderLabels({tr("Attribute"), tr("Value")}); connect(d->close, &QPushButton::clicked, this, &KeyDialog::accept); - connect(d->showCert, &QPushButton::clicked, this, [this, cert=k.cert] { - CertificateDetails::showCertificate(cert, this); - }); - d->showCert->setHidden(k.cert.isNull()); + if (k.type == CKey::CDOC1) { + const CKeyCD1& kd = static_cast(k); + connect(d->showCert, &QPushButton::clicked, this, [this, cert=kd.cert] { + CertificateDetails::showCertificate(cert, this); + }); + d->showCert->setHidden(kd.cert.isNull()); + } else { + d->showCert->setHidden(true); + } auto addItem = [&](const QString ¶meter, const QString &value) { if(value.isEmpty()) @@ -63,16 +68,26 @@ KeyDialog::KeyDialog( const CKey &k, QWidget *parent ) d->view->addTopLevelItem(i); }; - addItem(tr("Recipient"), k.recipient); - addItem(tr("Crypto method"), k.method); - addItem(tr("Agreement method"), k.agreement); - addItem(tr("Key derivation method"), k.derive); - addItem(tr("ConcatKDF digest method"), k.concatDigest); - addItem(tr("Key server ID"), k.keyserver_id); - addItem(tr("Transaction ID"), k.transaction_id); - addItem(tr("Expiry date"), k.cert.expiryDate().toLocalTime().toString(QStringLiteral("dd.MM.yyyy hh:mm:ss"))); - addItem(tr("Issuer"), SslCertificate(k.cert).issuerInfo(QSslCertificate::CommonName)); + bool adjust_size = false; + if (k.type == CKey::Type::CDOC1) { + const CKeyCD1& cd1key = static_cast(k); + addItem(tr("Recipient"), cd1key.recipient); + addItem(tr("Crypto method"), cd1key.method); + addItem(tr("Agreement method"), cd1key.agreement); + addItem(tr("Key derivation method"), cd1key.derive); + addItem(tr("ConcatKDF digest method"), cd1key.concatDigest); + addItem(tr("Expiry date"), cd1key.cert.expiryDate().toLocalTime().toString(QStringLiteral("dd.MM.yyyy hh:mm:ss"))); + addItem(tr("Issuer"), SslCertificate(cd1key.cert).issuerInfo(QSslCertificate::CommonName)); + adjust_size = !cd1key.agreement.isEmpty(); + } else if (CKeyCD2::isCDoc2Key(k)) { + const CKeyCD2& cd2key = static_cast(k); + addItem(tr("Label"), cd2key.label); + if (k.type == CKey::SERVER) { + const CKeyServer& sk = static_cast(k); + addItem(tr("Key server ID"), sk.keyserver_id); + addItem(tr("Transaction ID"), sk.transaction_id); + } + } d->view->resizeColumnToContents( 0 ); - if(!k.agreement.isEmpty()) - adjustSize(); + if(adjust_size) adjustSize(); } diff --git a/client/dialogs/PasswordDialog.cpp b/client/dialogs/PasswordDialog.cpp new file mode 100644 index 00000000..de41e85a --- /dev/null +++ b/client/dialogs/PasswordDialog.cpp @@ -0,0 +1,19 @@ +#include "PasswordDialog.h" +#include "ui_PasswordDialog.h" + +PasswordDialog::PasswordDialog(QWidget *parent) + : QDialog(parent) + , ui(new Ui::PasswordDialog) +{ + ui->setupUi(this); +} + +PasswordDialog::~PasswordDialog() +{ + delete ui; +} + +QString PasswordDialog::password() const +{ + return ui->passwordLine->text(); +} diff --git a/client/dialogs/PasswordDialog.h b/client/dialogs/PasswordDialog.h new file mode 100644 index 00000000..380128cc --- /dev/null +++ b/client/dialogs/PasswordDialog.h @@ -0,0 +1,23 @@ +#ifndef PASSWORDDIALOG_H +#define PASSWORDDIALOG_H + +#include + +namespace Ui { +class PasswordDialog; +} + +class PasswordDialog : public QDialog +{ + Q_OBJECT + +public: + explicit PasswordDialog(QWidget *parent = nullptr); + ~PasswordDialog(); + + QString password() const; +private: + Ui::PasswordDialog *ui; +}; + +#endif // PASSWORDDIALOG_H diff --git a/client/dialogs/PasswordDialog.ui b/client/dialogs/PasswordDialog.ui new file mode 100644 index 00000000..c17c1905 --- /dev/null +++ b/client/dialogs/PasswordDialog.ui @@ -0,0 +1,84 @@ + + + PasswordDialog + + + + 0 + 0 + 400 + 131 + + + + Dialog + + + + + + Enter the password to decrypt document + + + Qt::AlignCenter + + + + + + + QLineEdit::Password + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + false + + + + + + + + + buttonBox + accepted() + PasswordDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + PasswordDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/client/widgets/AddressItem.cpp b/client/widgets/AddressItem.cpp index 7015da7f..cca8d585 100644 --- a/client/widgets/AddressItem.cpp +++ b/client/widgets/AddressItem.cpp @@ -41,7 +41,7 @@ AddressItem::AddressItem(std::shared_ptr k, QWidget *parent, bool showIcon : Item(parent) , ui(new Private) { - ui->key = std::move(k); + ui->key = k; ui->setupUi(this); if(showIcon) ui->icon->load(QStringLiteral(":/images/icon_Krypto_small.svg")); @@ -55,17 +55,27 @@ AddressItem::AddressItem(std::shared_ptr k, QWidget *parent, bool showIcon QStringLiteral("/images/icon_remove_pressed.svg"), 17, 17); ui->remove->init(LabelButton::White); connect(ui->add, &QToolButton::clicked, this, [this]{ emit add(this);}); - connect(ui->remove, &LabelButton::clicked, this, [this]{ emit remove(this);}); + connect(ui->remove, &LabelButton::clicked, this, [this]{ emit remove(this);}); + connect(ui->decrypt, &QToolButton::clicked, this, [this]{ emit decrypt(ui->key);}); ui->add->setFont(Styles::font(Styles::Condensed, 12)); ui->added->setFont(ui->add->font()); - ui->code = SslCertificate(ui->key->cert).personalCode().toHtmlEscaped(); - ui->label = (!ui->key->cert.subjectInfo("GN").isEmpty() && !ui->key->cert.subjectInfo("SN").isEmpty() ? - ui->key->cert.subjectInfo("GN").join(' ') + " " + ui->key->cert.subjectInfo("SN").join(' ') : - ui->key->cert.subjectInfo("CN").join(' ')).toHtmlEscaped(); - if(ui->label.isEmpty()) - ui->label = ui->key->encrypted_kek; + if (CKeyCD1::isCDoc1Key(*ui->key)) { + std::shared_ptr key = std::static_pointer_cast(ui->key); + ui->code = SslCertificate(key->cert).personalCode().toHtmlEscaped(); + ui->label = (!key->cert.subjectInfo("GN").isEmpty() && !key->cert.subjectInfo("SN").isEmpty() ? + key->cert.subjectInfo("GN").join(' ') + " " + key->cert.subjectInfo("SN").join(' ') : + key->cert.subjectInfo("CN").join(' ')).toHtmlEscaped(); + } else { + std::shared_ptr cd2key = std::static_pointer_cast(ui->key); + ui->code = {}; + ui->label = cd2key->label; + } + if(ui->label.isEmpty() && ui->key->type == CKey::PUBLICKEY) { + const CKeyPK& pk = static_cast(*ui->key); + ui->label = pk.encrypted_kek; + } setIdType(); showButton(AddressItem::Remove); } @@ -109,7 +119,7 @@ void AddressItem::idChanged(std::shared_ptr key) void AddressItem::idChanged(const SslCertificate &cert) { - idChanged(CKey::fromCertificate(cert)); + idChanged(CKeyCD1::fromCertificate(cert)); } void AddressItem::initTabOrder(QWidget *item) @@ -133,8 +143,9 @@ void AddressItem::mouseReleaseEvent(QMouseEvent * /*event*/) void AddressItem::setName() { - ui->name->setText(QStringLiteral("%1 %2") - .arg(ui->label, ui->yourself ? ui->code + tr(" (Yourself)") : ui->code)); + QString str = QStringLiteral("%1 %2").arg(ui->label, ui->yourself ? ui->code + tr(" (Yourself)") : ui->code); + qDebug() << "SetName:" << str; + ui->name->setText(str); } void AddressItem::showButton(ShowToolButton show) @@ -151,12 +162,15 @@ void AddressItem::stateChange(ContainerState state) void AddressItem::setIdType() { - ui->idType->setHidden(ui->key->cert.isNull()); - if(ui->key->cert.isNull()) + if (ui->key->type != CKey::CDOC1) { + ui->idType->setText("CDOC2 Key"); return; + } + ui->idType->setHidden(false); + std::shared_ptr ckd = std::static_pointer_cast(ui->key); QString str; - SslCertificate cert(ui->key->cert); + SslCertificate cert(ckd->cert); SslCertificate::CertType type = cert.type(); if(type & SslCertificate::DigiIDType) str = tr("digi-ID"); @@ -181,3 +195,4 @@ void AddressItem::setIdType() cert.isValid() ? tr("Expires on") : tr("Expired on"), date.formatDate(QStringLiteral("dd. MMMM yyyy")))); } + diff --git a/client/widgets/AddressItem.h b/client/widgets/AddressItem.h index 80b22ff4..f85f6b37 100644 --- a/client/widgets/AddressItem.h +++ b/client/widgets/AddressItem.h @@ -47,6 +47,9 @@ class AddressItem final : public Item void showButton(ShowToolButton show); void stateChange(ria::qdigidoc4::ContainerState state) final; +signals: + void decrypt(std::shared_ptr key); + private: void changeEvent(QEvent *event) final; bool eventFilter(QObject *o, QEvent *e) final; diff --git a/client/widgets/AddressItem.ui b/client/widgets/AddressItem.ui index ecbe557e..beb7be7e 100644 --- a/client/widgets/AddressItem.ui +++ b/client/widgets/AddressItem.ui @@ -52,7 +52,11 @@ color: #006EB5; } QToolButton:disabled { color: #727679; -} +} +#decrypt { +background-color: blue; +} + @@ -73,6 +77,31 @@ color: #727679; 0 + + + + false + + + + 0 + 25 + + + + + 16777215 + 25 + + + + Added + + + ADDED + + + @@ -92,52 +121,32 @@ color: #727679; - - - - - Roboto - 14 - false - false - - - - Qt::TabFocus - - - border: none; - - - MARI MAASIKAS MUSTIKAS 48405050123 (Sina ise) - - - true + + + + + 0 + 25 + - - - - - - - Roboto - 11 - false - false - + + + 16777215 + 25 + - - Qt::TabFocus + + PointingHandCursor - - color: #727679;border: none; + + Add - ID-card + ADD - + @@ -173,53 +182,55 @@ color: #727679; - - - - false - - - - 0 - 25 - + + + + + Roboto + 11 + false + false + - - - 16777215 - 25 - + + Qt::TabFocus - - Added + + color: #727679;border: none; - ADDED + ID-card - - - - - 0 - 25 - + + + + + Roboto + 14 + false + false + - - - 16777215 - 25 - + + Qt::TabFocus - - PointingHandCursor + + border: none; - - Add + + MARI MAASIKAS MUSTIKAS 48405050123 (Sina ise) + + + true + + + + - ADD + DECRYPT diff --git a/client/widgets/ContainerPage.cpp b/client/widgets/ContainerPage.cpp index ec254840..a868d3d4 100644 --- a/client/widgets/ContainerPage.cpp +++ b/client/widgets/ContainerPage.cpp @@ -290,13 +290,18 @@ void ContainerPage::showSigningButton() void ContainerPage::transition(CryptoDoc *container, const QSslCertificate &cert) { clear(); - isSupported = container && (container->state() & UnencryptedContainer || container->canDecrypt(cert)); - if(!container) + if (!container) { + isSupported = false; return; + } + isSupported = (container->state() & UnencryptedContainer) || container->canDecrypt(cert); setHeader(container->fileName()); - for(std::shared_ptr key: container->keys()) - ui->rightPane->addWidget(new AddressItem(key, ui->rightPane, true)); - ui->leftPane->setModel(container->documentModel()); + for(std::shared_ptr& key: container->keys()) { + AddressItem *addr = new AddressItem(key, ui->rightPane, true); + connect(addr, &AddressItem::decrypt, this, [this,key]{emit decryptReq(key);}); + ui->rightPane->addWidget(addr); + } + ui->leftPane->setModel(container->documentModel()); updatePanes(container->state()); } @@ -368,9 +373,12 @@ void ContainerPage::update(bool canDecrypt, CryptoDoc* container) return; hasEmptyFile = false; - ui->rightPane->clear(); - for(std::shared_ptr key: container->keys()) - ui->rightPane->addWidget(new AddressItem(key, ui->rightPane, true)); + ui->rightPane->clear(); + for(std::shared_ptr& key: container->keys()) { + AddressItem *addr = new AddressItem(key, ui->rightPane, true); + connect(addr, &AddressItem::decrypt, this, [this,key]{emit decryptReq(key);}); + ui->rightPane->addWidget(addr); + } if(container->state() & UnencryptedContainer) showMainAction({ EncryptContainer }); } @@ -470,3 +478,4 @@ void ContainerPage::translateLabels() ui->convert->setText(tr(convertText)); ui->convert->setAccessibleName(tr(convertText).toLower()); } + diff --git a/client/widgets/ContainerPage.h b/client/widgets/ContainerPage.h index ad8a8f49..ad02417d 100644 --- a/client/widgets/ContainerPage.h +++ b/client/widgets/ContainerPage.h @@ -28,7 +28,7 @@ namespace Ui { class ContainerPage; } -class CKey; +struct CKey; class CryptoDoc; class DigiDoc; class QSslCertificate; @@ -63,6 +63,8 @@ class ContainerPage final : public QWidget void removed(int row); void warning(const WarningText &warningText); + void decryptReq(std::shared_ptr key); + private: void addressSearch(); void changeEvent(QEvent* event) final; @@ -78,7 +80,7 @@ class ContainerPage final : public QWidget void updatePanes(ria::qdigidoc4::ContainerState state); void translateLabels(); - Ui::ContainerPage *ui; + Ui::ContainerPage *ui; std::unique_ptr mainAction; QString cardInReader; QString fileName; From 8d127f1a16c71d4675b464dee2ddf49631e360a0 Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Mon, 13 May 2024 16:34:22 +0300 Subject: [PATCH 03/22] Quick'n'dirty first version that actually encrypts and decrypts with symmetric key Signed-off-by: Lauris Kaplinski --- client/CDoc1.cpp | 57 ++-- client/CDoc2.cpp | 425 +++++++++++++++++++----------- client/CDoc2.h | 2 + client/Crypto.cpp | 12 +- client/Crypto.h | 5 +- client/CryptoDoc.cpp | 70 +++-- client/CryptoDoc.h | 109 +++++--- client/MainWindow.cpp | 35 ++- client/MainWindow.h | 1 + client/dialogs/AddRecipients.cpp | 17 +- client/dialogs/AddRecipients.h | 3 +- client/dialogs/AddRecipients.ui | 9 +- client/dialogs/KeyDialog.cpp | 22 +- client/dialogs/PasswordDialog.cpp | 88 ++++++- client/dialogs/PasswordDialog.h | 23 +- client/dialogs/PasswordDialog.ui | 48 +++- client/widgets/AddressItem.cpp | 84 +++--- client/widgets/ContainerPage.cpp | 13 +- client/widgets/ContainerPage.h | 1 + client/widgets/ContainerPage.ui | 60 ++++- 20 files changed, 769 insertions(+), 315 deletions(-) diff --git a/client/CDoc1.cpp b/client/CDoc1.cpp index 68d18040..65b4b106 100644 --- a/client/CDoc1.cpp +++ b/client/CDoc1.cpp @@ -118,8 +118,9 @@ CDoc1::CDoc1(const QString &path) return; std::shared_ptr key = CKeyCD1::newEmpty(); - key->id = xml.attributes().value(QLatin1String("Id")).toString(); - key->recipient = xml.attributes().value(QLatin1String("Recipient")).toString(); + // Id is never used + //key->id = xml.attributes().value(QLatin1String("Id")).toString(); + key->label = xml.attributes().value(QLatin1String("Recipient")).toString(); while(!xml.atEnd()) { xml.readNext(); @@ -128,19 +129,26 @@ CDoc1::CDoc1(const QString &path) if(!xml.isStartElement()) continue; // EncryptedData/KeyInfo/KeyName - if(xml.name() == QLatin1String("KeyName")) - key->name = xml.readElementText(); + // Name is never used + //if(xml.name() == QLatin1String("KeyName")) + // key->name = xml.readElementText(); // EncryptedData/KeyInfo/EncryptedKey/EncryptionMethod else if(xml.name() == QLatin1String("EncryptionMethod")) key->method = xml.attributes().value(QLatin1String("Algorithm")).toString(); // EncryptedData/KeyInfo/EncryptedKey/KeyInfo/AgreementMethod - else if(xml.name() == QLatin1String("AgreementMethod")) - key->agreement = xml.attributes().value(QLatin1String("Algorithm")).toString(); + else if(xml.name() == QLatin1String("AgreementMethod")) { + // fixme: handle error + if (xml.attributes().value(QLatin1String("Algorithm")).toString() != AGREEMENT_MTH) + return; + //key->agreement = xml.attributes().value(QLatin1String("Algorithm")).toString(); // EncryptedData/KeyInfo/EncryptedKey/KeyInfo/AgreementMethod/KeyDerivationMethod - else if(xml.name() == QLatin1String("KeyDerivationMethod")) - key->derive = xml.attributes().value(QLatin1String("Algorithm")).toString(); + } else if(xml.name() == QLatin1String("KeyDerivationMethod")) { + // fixme: handle error + if (xml.attributes().value(QLatin1String("Algorithm")).toString() != CONCATKDF_MTH) + return; + // key->derive = xml.attributes().value(QLatin1String("Algorithm")).toString(); // EncryptedData/KeyInfo/EncryptedKey/KeyInfo/AgreementMethod/KeyDerivationMethod/ConcatKDFParams - else if(xml.name() == QLatin1String("ConcatKDFParams")) + } else if(xml.name() == QLatin1String("ConcatKDFParams")) { key->AlgorithmID = QByteArray::fromHex(xml.attributes().value(QLatin1String("AlgorithmID")).toUtf8()); if(key->AlgorithmID.front() == char(0x00)) key->AlgorithmID.remove(0, 1); @@ -168,7 +176,7 @@ CDoc1::CDoc1(const QString &path) else if(xml.name() == QLatin1String("CipherValue")) { xml.readNext(); - key->cipher = fromBase64(xml.text()); + key->encrypted_fmk = fromBase64(xml.text()); } } keys.append(key); @@ -302,16 +310,16 @@ std::shared_ptr CDoc1::getDecryptionKey(const QSslCertificate &cert) const std::shared_ptr k = std::static_pointer_cast(key); if(!ENC_MTH.contains(method) || k->cert != cert || - k->cipher.isEmpty()) + k->encrypted_fmk.isEmpty()) continue; if(cert.publicKey().algorithm() == QSsl::Rsa && k->method == RSA_MTH) return k; if(cert.publicKey().algorithm() == QSsl::Ec && !k->publicKey.isEmpty() && - KWAES_SIZE.contains(k->method) && + KWAES_SIZE.contains(k->method) /* && k->derive == CONCATKDF_MTH && - k->agreement == AGREEMENT_MTH) + k->agreement == AGREEMENT_MTH*/ ) return k; } return {}; @@ -465,10 +473,11 @@ bool CDoc1::save(const QString &path) } std::shared_ptr k = std::static_pointer_cast(key); writeElement(w, DENC, QStringLiteral("EncryptedKey"), [&]{ - if(!k->id.isEmpty()) - w.writeAttribute(QStringLiteral("Id"), k->id); - if(!k->recipient.isEmpty()) - w.writeAttribute(QStringLiteral("Recipient"), k->recipient); + // Id is never used + //if(!k->id.isEmpty()) + // w.writeAttribute(QStringLiteral("Id"), k->id); + if(!k->label.isEmpty()) + w.writeAttribute(QStringLiteral("Recipient"), k->label); QByteArray cipher; if(k->pk_type == CKey::PKType::RSA) { @@ -479,8 +488,9 @@ bool CDoc1::save(const QString &path) {QStringLiteral("Algorithm"), RSA_MTH}, }); writeElement(w, DS, QStringLiteral("KeyInfo"), [&]{ - if(!k->name.isEmpty()) - w.writeTextElement(DS, QStringLiteral("KeyName"), k->name); + // Name is never used + //if(!k->name.isEmpty()) + // w.writeTextElement(DS, QStringLiteral("KeyName"), k->name); writeElement(w, DS, QStringLiteral("X509Data"), [&]{ writeBase64Element(w, DS, QStringLiteral("X509Certificate"), k->cert.toDer()); }); @@ -512,7 +522,7 @@ bool CDoc1::save(const QString &path) qDebug() << "ENC ConcatKDF" << encryptionKey.toHex(); #endif - cipher = Crypto::aes_wrap(encryptionKey, transportKey, true); + cipher = Crypto::aes_wrap(encryptionKey, transportKey); if(cipher.isEmpty()) return; @@ -561,6 +571,7 @@ bool CDoc1::save(const QString &path) }); }); }}); + // This is actual content, for some weird reason named cipherData/cipherValue writeElement(w,DENC, QStringLiteral("CipherData"), [&]{ writeBase64Element(w, DENC, QStringLiteral("CipherValue"), Crypto::cipher(ENC_MTH[method], transportKey, data.buffer(), true) @@ -589,7 +600,7 @@ QByteArray CDoc1::getFMK(const CKey &key, const QByteArray& secret) setLastError({}); QByteArray decryptedKey = qApp->signer()->decrypt([&ckey](QCryptoBackend *backend) { if(ckey.pk_type == CKey::PKType::RSA) - return backend->decrypt(ckey.cipher, false); + return backend->decrypt(ckey.encrypted_fmk, false); return backend->deriveConcatKDF(ckey.publicKey, SHA_MTH[ckey.concatDigest], int(KWAES_SIZE[ckey.method]), ckey.AlgorithmID, ckey.PartyUInfo, ckey.PartyVInfo); }); @@ -601,10 +612,10 @@ QByteArray CDoc1::getFMK(const CKey &key, const QByteArray& secret) if(ckey.pk_type == CKey::PKType::RSA) return decryptedKey; #ifndef NDEBUG - qDebug() << "DEC Ss" << key.publicKey.toHex(); + qDebug() << "DEC Ss" << ckey.publicKey.toHex(); qDebug() << "DEC ConcatKDF" << decryptedKey.toHex(); #endif - return Crypto::aes_wrap(decryptedKey, ckey.cipher, false); + return Crypto::aes_unwrap(decryptedKey, ckey.encrypted_fmk); } int CDoc1::version() diff --git a/client/CDoc2.cpp b/client/CDoc2.cpp index b4c971b6..e4629b6b 100644 --- a/client/CDoc2.cpp +++ b/client/CDoc2.cpp @@ -457,9 +457,9 @@ CDoc2::CDoc2(const QString &_path) continue; } auto fillRecipientPK = [&] (CKey::PKType pk_type, auto key) { - std::shared_ptr k(new CKeyPK(pk_type, toByteArray(key->recipient_public_key()))); - k->label = toString(recipient->key_label()); - k->cipher = toByteArray(recipient->encrypted_fmk()); + std::shared_ptr k(new CKeyPK(pk_type, toByteArray(key->recipient_public_key()))); + k->label = toString(recipient->key_label()); + k->encrypted_fmk = toByteArray(recipient->encrypted_fmk()); return k; }; switch(recipient->capsule_type()) @@ -473,7 +473,7 @@ CDoc2::CDoc2(const QString &_path) continue; } std::shared_ptr k = fillRecipientPK(CKey::PKType::ECC, key); - k->publicKey = toByteArray(key->sender_public_key()); + k->key_material = toByteArray(key->sender_public_key()); keys.append(k); } break; @@ -481,7 +481,7 @@ CDoc2::CDoc2(const QString &_path) if(const auto *key = recipient->capsule_as_RSAPublicKeyCapsule()) { std::shared_ptr k = fillRecipientPK(CKey::PKType::RSA, key); - k->encrypted_kek = toByteArray(key->encrypted_kek()); + k->key_material = toByteArray(key->encrypted_kek()); keys.append(k); } break; @@ -513,7 +513,7 @@ CDoc2::CDoc2(const QString &_path) } if (ckey) { ckey->label = toString(recipient->key_label()); - ckey->cipher = toByteArray(recipient->encrypted_fmk()); + ckey->encrypted_fmk = toByteArray(recipient->encrypted_fmk()); ckey->keyserver_id = toString(server->keyserver_id()); ckey->transaction_id = toString(server->transaction_id()); keys.append(ckey); @@ -528,7 +528,7 @@ CDoc2::CDoc2(const QString &_path) auto salt = capsule->salt(); std::shared_ptr key(new CKeySymmetric(toByteArray(salt))); key->label = toString(recipient->key_label()); - key->cipher = toByteArray(recipient->encrypted_fmk()); + key->encrypted_fmk = toByteArray(recipient->encrypted_fmk()); keys.append(key); } break; @@ -544,7 +544,7 @@ CDoc2::CDoc2(const QString &_path) int32_t kdf_iter = capsule->kdf_iterations(); std::shared_ptr key(new CKeySymmetric(toByteArray(salt))); key->label = toString(recipient->key_label()); - key->cipher = toByteArray(recipient->encrypted_fmk()); + key->encrypted_fmk = toByteArray(recipient->encrypted_fmk()); key->pw_salt = toByteArray(pw_salt); key->kdf_iter = kdf_iter; keys.append(key); @@ -566,38 +566,22 @@ CDoc2::load(const QString& path) CKey::DecryptionStatus CDoc2::canDecrypt(const QSslCertificate &cert) const { - std::shared_ptr t = CKeyCD1::fromCertificate(cert); + CKey::DecryptionStatus status = CKey::DecryptionStatus::CANNOT_DECRYPT; for (std::shared_ptr key: keys) { - switch (key->type) { - case CKey::Type::CDOC1: - if (*key == *t) return CKey::CAN_DECRYPT; - break; - case CKey::Type::SYMMETRIC_KEY: - return CKey::DecryptionStatus::NEED_KEY; - case CKey::Type::PUBLICKEY: - case CKey::Type::SERVER: - break; - } + if (key->isTheSameRecipient(cert)) return CKey::CAN_DECRYPT; + if (key->isSymmetric()) status = CKey::DecryptionStatus::NEED_KEY; } - return CKey::DecryptionStatus::CANNOT_DECRYPT; + return status; } std::shared_ptr CDoc2::getDecryptionKey(const QSslCertificate &cert) const { - std::shared_ptr t = CKeyCD1::fromCertificate(cert); - for (std::shared_ptr key: keys) { - switch (key->type) { - case CKey::Type::CDOC1: - if (*key == *t) return key; - break; - case CKey::Type::SYMMETRIC_KEY: - return key; - case CKey::Type::PUBLICKEY: - case CKey::Type::SERVER: - break; - } - } - return {}; + std::shared_ptr best = {}; + for (std::shared_ptr key: keys) { + if (key->isTheSameRecipient(cert)) return key; + if (key->isSymmetric()) best = key; + } + return best; } bool CDoc2::decryptPayload(const QByteArray &fmk) @@ -686,14 +670,17 @@ bool CDoc2::save(const QString &_path) flatbuffers::FlatBufferBuilder builder; std::vector> recipients; - auto toVector = [&builder](const QByteArray &data) { + + auto toVector = [&builder](const QByteArray &data) { return builder.CreateVector((const uint8_t*)data.data(), size_t(data.length())); }; - auto toString = [&builder](const QString &data) { + + auto toString = [&builder](const QString &data) { QByteArray utf8 = data.toUtf8(); return builder.CreateString(utf8.data(), size_t(utf8.length())); }; - auto sendToServer = [this](std::shared_ptr key, const QByteArray &recipient_id, const QByteArray &key_material, QLatin1String type) { + + auto sendToServer = [this](std::shared_ptr key, const QByteArray &recipient_id, const QByteArray &key_material, QLatin1String type) { key->keyserver_id = Settings::CDOC2_DEFAULT_KEYSERVER; if(key->keyserver_id.isEmpty()) return setLastError(QStringLiteral("keyserver_id cannot be empty")); @@ -722,92 +709,102 @@ bool CDoc2::save(const QString &_path) }; for(std::shared_ptr key: keys) { - QString label; - if (CKeyCD1::isCDoc1Key(*key)) { - label = static_cast(*key).recipient; - } else { - label = static_cast(*key).label; - } - if(key->pk_type == CKey::PKType::RSA) { - QByteArray kek = Crypto::random(fmk.size()); - QByteArray xor_key = Crypto::xor_data(fmk, kek); - auto publicKey = Crypto::fromRSAPublicKeyDer(key->key); - if(!publicKey) - return false; - QByteArray encrytpedKek = Crypto::encrypt(publicKey.get(), RSA_PKCS1_OAEP_PADDING, kek); + if (key->isSymmetric()) { + const CKeySymmetric &sk = static_cast(*key); + QByteArray symmetric_key; + if (sk.kdf_iter > 0) { + // PasswordSalt_i = CSRNG() + // PasswordKeyMaterial_i = PBKDF2(Password_i, PasswordSalt_i) + } else { + //symmetric_key = + } + // KeyMaterialSalt_i = CSRNG() + // KEK_i = HKDF(KeyMaterialSalt_i, PasswordKeyMaterial_i) + // Capsule_i = {KeyMaterialSalt_i, PasswordSalt_i} + // EncryptedFMK_i = XOR(FMK, KEK_i) + + } else if (!key->isPKI()) { + const CKeyPKI& pki = static_cast(*key); + if(pki.pk_type == CKey::PKType::RSA) { + QByteArray kek = Crypto::random(fmk.size()); + QByteArray xor_key = Crypto::xor_data(fmk, kek); + auto publicKey = Crypto::fromRSAPublicKeyDer(pki.rcpt_key); + if(!publicKey) + return false; + QByteArray encrytpedKek = Crypto::encrypt(publicKey.get(), RSA_PKCS1_OAEP_PADDING, kek); #ifndef NDEBUG - qDebug() << "publicKeyDer" << key->key.toHex(); - qDebug() << "kek" << kek.toHex(); - qDebug() << "xor" << xor_key.toHex(); - qDebug() << "encrytpedKek" << encrytpedKek.toHex(); + qDebug() << "publicKeyDer" << pki.rcpt_key.toHex(); + qDebug() << "kek" << kek.toHex(); + qDebug() << "xor" << xor_key.toHex(); + qDebug() << "encrytpedKek" << encrytpedKek.toHex(); #endif - if(!Settings::CDOC2_USE_KEYSERVER) { - auto rsaPublicKey = cdoc20::recipients::CreateRSAPublicKeyCapsule(builder, - toVector(key->key), toVector(encrytpedKek)); - auto offs = cdoc20::header::CreateRecipientRecord(builder, - cdoc20::recipients::Capsule::RSAPublicKeyCapsule, rsaPublicKey.Union(), - toString(label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); - recipients.push_back(offs); - } else { - if (key->type != CKey::Type::SERVER) { - return setLastError(QStringLiteral("Not a server key")); - } - const std::shared_ptr skey = std::static_pointer_cast(key); - if(!sendToServer(skey, skey->key, encrytpedKek, QLatin1String("rsa"))) - return false; - auto rsaKeyServer = cdoc20::recipients::CreateRsaKeyDetails(builder, toVector(skey->key)); - auto keyServer = cdoc20::recipients::CreateKeyServerCapsule(builder, - cdoc20::recipients::KeyDetailsUnion::RsaKeyDetails, - rsaKeyServer.Union(), toString(skey->keyserver_id), toString(skey->transaction_id)); - auto offs = cdoc20::header::CreateRecipientRecord(builder, - cdoc20::recipients::Capsule::KeyServerCapsule, keyServer.Union(), - toString(label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); - recipients.push_back(offs); - } - } else { - auto publicKey = Crypto::fromECPublicKeyDer(key->key, NID_secp384r1); - if(!publicKey) - return false; - auto ephKey = Crypto::genECKey(publicKey.get()); - QByteArray sharedSecret = Crypto::derive(ephKey.get(), publicKey.get()); - QByteArray ephPublicKeyDer = Crypto::toPublicKeyDer(ephKey.get()); - QByteArray kekPm = Crypto::extract(sharedSecret, KEKPREMASTER); - QByteArray info = KEK + cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) + key->key + ephPublicKeyDer; - QByteArray kek = Crypto::expand(kekPm, info, fmk.size()); - QByteArray xor_key = Crypto::xor_data(fmk, kek); - #ifndef NDEBUG - qDebug() << "publicKeyDer" << key->key.toHex(); - qDebug() << "ephPublicKeyDer" << ephPublicKeyDer.toHex(); - qDebug() << "sharedSecret" << sharedSecret.toHex(); - qDebug() << "kekPm" << kekPm.toHex(); - qDebug() << "kek" << kek.toHex(); - qDebug() << "xor" << xor_key.toHex(); - #endif - if(!Settings::CDOC2_USE_KEYSERVER) { - auto eccPublicKey = cdoc20::recipients::CreateECCPublicKeyCapsule(builder, - cdoc20::recipients::EllipticCurve::secp384r1, toVector(key->key), toVector(ephPublicKeyDer)); - auto offs = cdoc20::header::CreateRecipientRecord(builder, - cdoc20::recipients::Capsule::ECCPublicKeyCapsule, eccPublicKey.Union(), - toString(label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); - recipients.push_back(offs); - } else { - if (key->type != CKey::Type::SERVER) { - return setLastError(QStringLiteral("Not a server key")); - } - const std::shared_ptr skey = std::static_pointer_cast(key); - if(!sendToServer(skey, skey->key, ephPublicKeyDer, QLatin1String("ecc_secp384r1"))) - return false; - auto eccKeyServer = cdoc20::recipients::CreateEccKeyDetails(builder, - cdoc20::recipients::EllipticCurve::secp384r1, toVector(skey->key)); - auto keyServer = cdoc20::recipients::CreateKeyServerCapsule(builder, - cdoc20::recipients::KeyDetailsUnion::EccKeyDetails, - eccKeyServer.Union(), toString(skey->keyserver_id), toString(skey->transaction_id)); - auto offs = cdoc20::header::CreateRecipientRecord(builder, - cdoc20::recipients::Capsule::KeyServerCapsule, keyServer.Union(), - toString(label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); - recipients.push_back(offs); - } - } + if(!Settings::CDOC2_USE_KEYSERVER) { + auto rsaPublicKey = cdoc20::recipients::CreateRSAPublicKeyCapsule(builder, + toVector(pki.rcpt_key), toVector(encrytpedKek)); + auto offs = cdoc20::header::CreateRecipientRecord(builder, + cdoc20::recipients::Capsule::RSAPublicKeyCapsule, rsaPublicKey.Union(), + toString(pki.label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); + recipients.push_back(offs); + } else { + if (key->type != CKey::Type::SERVER) { + return setLastError(QStringLiteral("Not a server key")); + } + const std::shared_ptr skey = std::static_pointer_cast(key); + if(!sendToServer(skey, skey->rcpt_key, encrytpedKek, QLatin1String("rsa"))) + return false; + auto rsaKeyServer = cdoc20::recipients::CreateRsaKeyDetails(builder, toVector(skey->rcpt_key)); + auto keyServer = cdoc20::recipients::CreateKeyServerCapsule(builder, + cdoc20::recipients::KeyDetailsUnion::RsaKeyDetails, + rsaKeyServer.Union(), toString(skey->keyserver_id), toString(skey->transaction_id)); + auto offs = cdoc20::header::CreateRecipientRecord(builder, + cdoc20::recipients::Capsule::KeyServerCapsule, keyServer.Union(), + toString(pki.label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); + recipients.push_back(offs); + } + } else { + auto publicKey = Crypto::fromECPublicKeyDer(pki.rcpt_key, NID_secp384r1); + if(!publicKey) return false; + auto ephKey = Crypto::genECKey(publicKey.get()); + QByteArray sharedSecret = Crypto::derive(ephKey.get(), publicKey.get()); + QByteArray ephPublicKeyDer = Crypto::toPublicKeyDer(ephKey.get()); + QByteArray kekPm = Crypto::extract(sharedSecret, KEKPREMASTER); + QByteArray info = KEK + cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) + pki.rcpt_key + ephPublicKeyDer; + QByteArray kek = Crypto::expand(kekPm, info, fmk.size()); + QByteArray xor_key = Crypto::xor_data(fmk, kek); +#ifndef NDEBUG + qDebug() << "publicKeyDer" << pki.rcpt_key.toHex(); + qDebug() << "ephPublicKeyDer" << ephPublicKeyDer.toHex(); + qDebug() << "sharedSecret" << sharedSecret.toHex(); + qDebug() << "kekPm" << kekPm.toHex(); + qDebug() << "kek" << kek.toHex(); + qDebug() << "xor" << xor_key.toHex(); +#endif + if(!Settings::CDOC2_USE_KEYSERVER) { + auto eccPublicKey = cdoc20::recipients::CreateECCPublicKeyCapsule(builder, + cdoc20::recipients::EllipticCurve::secp384r1, toVector(pki.rcpt_key), toVector(ephPublicKeyDer)); + auto offs = cdoc20::header::CreateRecipientRecord(builder, + cdoc20::recipients::Capsule::ECCPublicKeyCapsule, eccPublicKey.Union(), + toString(pki.label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); + recipients.push_back(offs); + } else { + if (key->type != CKey::Type::SERVER) { + return setLastError(QStringLiteral("Not a server key")); + } + const std::shared_ptr skey = std::static_pointer_cast(key); + if(!sendToServer(skey, skey->rcpt_key, ephPublicKeyDer, QLatin1String("ecc_secp384r1"))) + return false; + auto eccKeyServer = cdoc20::recipients::CreateEccKeyDetails(builder, + cdoc20::recipients::EllipticCurve::secp384r1, toVector(skey->rcpt_key)); + auto keyServer = cdoc20::recipients::CreateKeyServerCapsule(builder, + cdoc20::recipients::KeyDetailsUnion::EccKeyDetails, + eccKeyServer.Union(), toString(skey->keyserver_id), toString(skey->transaction_id)); + auto offs = cdoc20::header::CreateRecipientRecord(builder, + cdoc20::recipients::Capsule::KeyServerCapsule, keyServer.Union(), + toString(pki.label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); + recipients.push_back(offs); + } + } + } } auto offset = cdoc20::header::CreateHeader(builder, builder.CreateVector(recipients), @@ -853,6 +850,113 @@ bool CDoc2::save(const QString &_path) return true; } +bool +CDoc2::save(QString _path, const std::vector& files, const QString& label, const QByteArray& secret, unsigned int kdf_iter) +{ + //setLastError({}); + QByteArray fmk = Crypto::extract(Crypto::random(KEY_LEN), SALT); + QByteArray cek = Crypto::expand(fmk, CEK); + QByteArray hhk = Crypto::expand(fmk, HMAC); +#ifndef NDEBUG + qDebug() << "fmk" << fmk.toHex(); + qDebug() << "cek" << cek.toHex(); + qDebug() << "hhk" << hhk.toHex(); +#endif + + flatbuffers::FlatBufferBuilder builder; + std::vector> recipients; + + auto toVector = [&builder](const QByteArray &data) { + return builder.CreateVector((const uint8_t*)data.data(), size_t(data.length())); + }; + + auto toString = [&builder](const QString &data) { + QByteArray utf8 = data.toUtf8(); + return builder.CreateString(utf8.data(), size_t(utf8.length())); + }; + + if (kdf_iter > 0) { + // PasswordSalt_i = CSRNG() + QByteArray pw_salt = Crypto::random(); + // PasswordKeyMaterial_i = PBKDF2(Password_i, PasswordSalt_i) + QByteArray key_material = QPasswordDigestor::deriveKeyPbkdf2(QCryptographicHash::Sha256, secret, pw_salt, kdf_iter, 32); +#ifndef NDEBUG + qDebug() << "Key material: " << key_material.toHex(); +#endif \ + // KeyMaterialSalt_i = CSRNG() + QByteArray salt = Crypto::random(); + // KEK_i = HKDF(KeyMaterialSalt_i, PasswordKeyMaterial_i) + QByteArray info = KEK + cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) + secret; + QByteArray kek = Crypto::hkdf(key_material, salt, info, 32, EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND); + + QByteArray xor_key = Crypto::xor_data(fmk, kek); + + auto capsule = cdoc20::recipients::CreatePBKDF2Capsule(builder, toVector(salt), toVector(pw_salt), cdoc20::recipients::KDFAlgorithmIdentifier::PBKDF2WithHmacSHA256, kdf_iter); + auto offs = cdoc20::header::CreateRecipientRecord(builder, + cdoc20::recipients::Capsule::PBKDF2Capsule, capsule.Union(), + toString(label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); + recipients.push_back(offs); + } else { + // KeyMaterialSalt_i = CSRNG() + QByteArray salt = Crypto::random(); + // KeyMaterialSalt_i = CSRNG() + // KEK_i = HKDF(KeyMaterialSalt_i, S_i) + QByteArray info = KEK + cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) + label.toUtf8(); + QByteArray kek = Crypto::hkdf(secret, salt, info, 32, EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND); + + QByteArray xor_key = Crypto::xor_data(fmk, kek); + + auto capsule = cdoc20::recipients::CreateSymmetricKeyCapsule(builder, toVector(salt)); + auto offs = cdoc20::header::CreateRecipientRecord(builder, + cdoc20::recipients::Capsule::SymmetricKeyCapsule, capsule.Union(), + toString(label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); + recipients.push_back(offs); + } + + auto offset = cdoc20::header::CreateHeader(builder, builder.CreateVector(recipients), + cdoc20::header::PayloadEncryptionMethod::CHACHA20POLY1305); + builder.Finish(offset); + + QByteArray header = QByteArray::fromRawData((const char*)builder.GetBufferPointer(), int(builder.GetSize())); + QByteArray headerHMAC = Crypto::sign_hmac(hhk, header); + QByteArray nonce = Crypto::random(NONCE_LEN); +#ifndef NDEBUG + qDebug() << "hmac" << headerHMAC.toHex(); + qDebug() << "nonce" << nonce.toHex(); +#endif + Crypto::Cipher enc(EVP_chacha20_poly1305(), cek, nonce, true); + enc.updateAAD(PAYLOAD + header + headerHMAC); + auto header_len = qToBigEndian(uint32_t(header.size())); + + /* Use TmpFile wrapper so the original will not be lost during error */ + TmpFile tmp(_path); + if (!tmp.open()) { + //setLastError(tmp.errorString()); + return false; + } + /* Write contents to temporary */ + tmp.write(LABEL); + tmp.write((const char*)&header_len, qint64(sizeof(header_len))); + tmp.write(header); + tmp.write(headerHMAC); + tmp.write(nonce); + if(!cdoc20::TAR(std::unique_ptr(new cdoc20::stream(&tmp, &enc))).save(files)) { + return false; + } + if(!enc.result()) { + return false; + } + QByteArray tag = enc.tag(); +#ifndef NDEBUG + qDebug() << "tag" << tag.toHex(); +#endif + tmp.write(tag); + tmp.close(); + //this->path = _path; + return true; +} + + QByteArray CDoc2::fetchKeyMaterial(const CKeyServer& key) { QNetworkRequest req = cdoc20::req(key.keyserver_id, key.transaction_id); @@ -882,80 +986,85 @@ QByteArray CDoc2::fetchKeyMaterial(const CKeyServer& key) return std::move(key_material); } -QByteArray CDoc2::getFMK(const CKey &_key, const QByteArray& secret) +QByteArray CDoc2::getFMK(const CKey &key, const QByteArray& secret) { setLastError({}); QByteArray kek; - if (_key.type == CKey::Type::SYMMETRIC_KEY) { - const CKeySymmetric &sk = static_cast(_key); - qDebug() << "Secret:" << secret.toHex(); + if (key.isSymmetric()) { + // Symmetric key + const CKeySymmetric &sk = static_cast(key); if (sk.kdf_iter > 0) { - // Password key - // PasswordSalt_i = Capsule_i.PasswordSalt - // PasswordKeyMaterial_i = PBKDF2(Password_i, PasswordSalt_i) - QByteArray PasswordKeyMaterial_i = QPasswordDigestor::deriveKeyPbkdf2(QCryptographicHash::Sha256, secret, sk.pw_salt, sk.kdf_iter, 32); - qDebug() << "PasswordKeyMaterial_i:" << PasswordKeyMaterial_i.toHex(); - // KeyMaterialSalt_i = Capsule_i.KeyMaterialSalt - // EncryptedFMK_i = Container_i.EncryptedFMK - // KEK_i = HKDF(KeyMaterialSalt_i, PasswordKeyMaterial_i) - +#ifndef NDEBUG + qDebug() << "Password based symmetric key: " << key.label; +#endif + // KEY_MATERIAL = PBKDF2(PASSWORD, PASSWORD_SALT) + QByteArray key_material = QPasswordDigestor::deriveKeyPbkdf2(QCryptographicHash::Sha256, secret, sk.pw_salt, sk.kdf_iter, 32); +#ifndef NDEBUG + qDebug() << "Key material: " << key_material.toHex(); +#endif + // KEK = HKDF(SALT, KEY_MATERIAL) QByteArray info = KEK + cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) + secret; - - kek = Crypto::hkdf(PasswordKeyMaterial_i, sk.salt, info, 32, EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND); + kek = Crypto::hkdf(key_material, sk.salt, info, 32, EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND); + } else { +#ifndef NDEBUG + qDebug() << "Plain symmetric key: " << key.label; +#endif + QByteArray info = KEK + cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) + sk.label.toUtf8(); + kek = Crypto::hkdf(secret, sk.salt, info, 32, EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND); qDebug() << "kek:" << kek.toHex(); - // FMK = XOR(KEK_i, EncryptedFMK_i) - // CEK = HKDF-Expand(FMK) - } } else { + // Public/private key + const CKeyPKI &pki = static_cast(key); QByteArray key_material; - if(_key.type == CKey::Type::SERVER) { - key_material = fetchKeyMaterial(static_cast(_key)); - } else if (_key.type == CKey::PUBLICKEY) { - const CKeyPK& pk = static_cast(_key); - if (_key.pk_type == CKey::PKType::RSA) { - key_material = pk.encrypted_kek; - } else { - key_material = pk.publicKey; - } + if(key.type == CKey::Type::SERVER) { + const CKeyServer &sk = static_cast(key); + key_material = fetchKeyMaterial(sk); + } else if (key.type == CKey::PUBLICKEY) { + const CKeyPK& pk = static_cast(key); + key_material = pk.key_material; } #ifndef NDEBUG - qDebug() << "publicKeyDer" << _key.key.toHex(); + qDebug() << "Public key" << pki.rcpt_key.toHex(); qDebug() << "Key material" << key_material.toHex(); #endif - if (_key.pk_type == CKey::PKType::RSA) { + if (pki.pk_type == CKey::PKType::RSA) { kek = qApp->signer()->decrypt([&key_material](QCryptoBackend *backend) { return backend->decrypt(key_material, true); }); } else { - kek = qApp->signer()->decrypt([&_key, &key_material](QCryptoBackend *backend) { + kek = qApp->signer()->decrypt([&pki, &key_material](QCryptoBackend *backend) { QByteArray kekPm = backend->deriveHMACExtract(key_material, KEKPREMASTER, KEY_LEN); #ifndef NDEBUG qDebug() << "kekPm" << kekPm.toHex(); #endif - QByteArray info = KEK + cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) + _key.key + key_material; + QByteArray info = KEK + cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) + pki.rcpt_key + key_material; return Crypto::expand(kekPm, info, KEY_LEN); }); } } +#ifndef NDEBUG + qDebug() << "KEK: " << kek.toHex(); +#endif if(kek.isEmpty()) { setLastError(QStringLiteral("Failed to derive key")); return {}; } - QByteArray fmk = Crypto::xor_data(_key.cipher, kek); + QByteArray fmk = Crypto::xor_data(key.encrypted_fmk, kek); QByteArray hhk = Crypto::expand(fmk, HMAC); #ifndef NDEBUG qDebug() << "kek" << kek.toHex(); - qDebug() << "xor" << _key.cipher.toHex(); + qDebug() << "xor" << key.encrypted_fmk.toHex(); qDebug() << "fmk" << fmk.toHex(); qDebug() << "hhk" << hhk.toHex(); qDebug() << "hmac" << headerHMAC.toHex(); #endif - if(Crypto::sign_hmac(hhk, header_data) == headerHMAC) - return fmk; - setLastError(QStringLiteral("CDoc 2.0 hash mismatch")); - return {}; + if(Crypto::sign_hmac(hhk, header_data) != headerHMAC) { + setLastError(QStringLiteral("CDoc 2.0 hash mismatch")); + return {}; + } + return fmk; } int CDoc2::version() diff --git a/client/CDoc2.h b/client/CDoc2.h index 951a490e..3fdf1565 100644 --- a/client/CDoc2.h +++ b/client/CDoc2.h @@ -38,6 +38,8 @@ class CDoc2 final: public CDoc, private QObject /*, private QFile */ { int version() final; static std::unique_ptr load(const QString& _path); + + static bool save(QString path, const std::vector& files, const QString& label, const QByteArray& secret, unsigned int kdf_iter); private: CDoc2(const QString &path); diff --git a/client/Crypto.cpp b/client/Crypto.cpp index 607e961e..41de9668 100644 --- a/client/Crypto.cpp +++ b/client/Crypto.cpp @@ -93,14 +93,22 @@ bool Crypto::Cipher::setTag(const QByteArray &data) const return !isError(EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_AEAD_SET_TAG, int(data.size()), const_cast(data.data()))); } -QByteArray Crypto::aes_wrap(const QByteArray &key, const QByteArray &data, bool encrypt) +QByteArray Crypto::aes_wrap(const QByteArray &key, const QByteArray &data) { - Cipher c(key.size() == 32 ? EVP_aes_256_wrap() : EVP_aes_128_wrap(), key, {}, encrypt); + Cipher c(key.size() == 32 ? EVP_aes_256_wrap() : EVP_aes_128_wrap(), key, {}, true); if(QByteArray result = c.update(data); c.result()) return result; return {}; } +QByteArray Crypto::aes_unwrap(const QByteArray &key, const QByteArray &data) +{ + Cipher c(key.size() == 32 ? EVP_aes_256_wrap() : EVP_aes_128_wrap(), key, {}, false); + if(QByteArray result = c.update(data); c.result()) + return result; + return {}; +} + QByteArray Crypto::cipher(const EVP_CIPHER *cipher, const QByteArray &key, QByteArray &data, bool encrypt) { QByteArray iv(EVP_CIPHER_iv_length(cipher), 0), tag; diff --git a/client/Crypto.h b/client/Crypto.h index 58e8972b..d7260a7c 100644 --- a/client/Crypto.h +++ b/client/Crypto.h @@ -50,8 +50,9 @@ class Crypto bool setTag(const QByteArray &data) const; }; - static QByteArray aes_wrap(const QByteArray &key, const QByteArray &data, bool encrypt); - static QByteArray cipher(const EVP_CIPHER *cipher, const QByteArray &key, QByteArray &data, bool encrypt); + static QByteArray aes_wrap(const QByteArray &key, const QByteArray &data); + static QByteArray aes_unwrap(const QByteArray &key, const QByteArray &data); + static QByteArray cipher(const EVP_CIPHER *cipher, const QByteArray &key, QByteArray &data, bool encrypt); static QByteArray curve_oid(EVP_PKEY *key); static QByteArray concatKDF(QCryptographicHash::Algorithm digestMethod, quint32 keyDataLen, const QByteArray &z, const QByteArray &otherInfo); diff --git a/client/CryptoDoc.cpp b/client/CryptoDoc.cpp index a32c7e04..aa730aa4 100644 --- a/client/CryptoDoc.cpp +++ b/client/CryptoDoc.cpp @@ -217,11 +217,40 @@ QString CDocumentModel::save(int row, const QString &path) const return fileName; } -CKeyCD1::CKeyCD1(const QSslCertificate &c) -: CKey(Type::CDOC1) +bool +CKey::isTheSameRecipient(const CKey& other) const +{ + QByteArray this_key, other_key; + if (this->isCertificate()) { + const CKeyCert& ckc = static_cast(*this); + this_key = ckc.cert.publicKey().toDer(); + } + if (other.isCertificate()) { + const CKeyCert& ckc = static_cast(other); + other_key = ckc.cert.publicKey().toDer(); + } + if (this_key.isEmpty() || other_key.isEmpty()) return false; + return this_key == other_key; +} + +bool +CKey::isTheSameRecipient(const QSslCertificate &cert) const +{ + QByteArray this_key, other_key; + if (this->isCertificate()) { + const CKeyCert& ckc = static_cast(*this); + this_key = ckc.cert.publicKey().toDer(); + } + other_key = cert.publicKey().toDer(); + if (this_key.isEmpty() || other_key.isEmpty()) return false; + return this_key == other_key; +} + +CKeyCert::CKeyCert(Type _type, const QSslCertificate &c) + : CKeyPKI(_type) { setCert(c); - recipient = [](const SslCertificate &c) { + label = [](const SslCertificate &c) { QString cn = c.subjectInfo(QSslCertificate::CommonName); QString gn = c.subjectInfo("GN"); QString sn = c.subjectInfo("SN"); @@ -239,14 +268,17 @@ CKeyCD1::CKeyCD1(const QSslCertificate &c) }(c); } -std::shared_ptr -CKeyCD1::newEmpty() { - return std::shared_ptr(new CKeyCD1()); +void CKeyCert::setCert(const QSslCertificate &c) +{ + cert = c; + QSslKey k = c.publicKey(); + rcpt_key = Crypto::toPublicKeyDer(k); + pk_type = (k.algorithm() == QSsl::Rsa) ? PKType::RSA : PKType::ECC; } std::shared_ptr -CKeyCD1::fromKey(QByteArray _key, PKType _pk_type) { - return std::shared_ptr(new CKeyCD1(_key, _pk_type)); +CKeyCD1::newEmpty() { + return std::shared_ptr(new CKeyCD1()); } std::shared_ptr @@ -254,14 +286,6 @@ CKeyCD1::fromCertificate(const QSslCertificate &cert) { return std::shared_ptr(new CKeyCD1(cert)); } -void CKeyCD1::setCert(const QSslCertificate &c) -{ - QSslKey k = c.publicKey(); - cert = c; - key = Crypto::toPublicKeyDer(k); - pk_type = (k.algorithm() == QSsl::Rsa) ? PKType::RSA : PKType::ECC; -} - std::shared_ptr CKeyServer::fromKey(QByteArray _key, PKType _pk_type) { return std::shared_ptr(new CKeyServer(_key, _pk_type)); @@ -282,7 +306,7 @@ bool CryptoDoc::addKey(std::shared_ptr key ) if(d->isEncryptedWarning()) return false; for (std::shared_ptr k: d->cdoc->keys) { - if (*k == *key) { + if (k->isTheSameRecipient(*key)) { WarningDialog::show(tr("Key already exists")); return false; } @@ -334,7 +358,7 @@ bool CryptoDoc::decrypt(std::shared_ptr key, const QByteArray& secret) if (key == nullptr) { key = d->cdoc->getDecryptionKey(qApp->signer()->tokenauth().cert()); } - if((key == nullptr) || (key->key.isEmpty() && secret.isEmpty())) { + if((key == nullptr) || (key->isSymmetric() && secret.isEmpty())) { WarningDialog::show(tr("You do not have the key to decrypt this document")); return false; } @@ -457,4 +481,14 @@ bool CryptoDoc::saveCopy(const QString &filename) return QFile::copy(d->fileName, filename); } +bool +CryptoDoc::encryptLT(const QString& label, const QByteArray& secret, unsigned int kdf_iter) +{ + if( d->fileName.isEmpty()) { + WarningDialog::show(tr("Container is not open")); + return false; + } + return CDoc2::save(d->fileName, d->cdoc->files, label, secret, kdf_iter); +} + #include "CryptoDoc.moc" diff --git a/client/CryptoDoc.h b/client/CryptoDoc.h index 8b3755ff..75d9b89b 100644 --- a/client/CryptoDoc.h +++ b/client/CryptoDoc.h @@ -33,8 +33,9 @@ struct CKey { public: enum Type { + SYMMETRIC_KEY, + CERTIFICATE, CDOC1, - SYMMETRIC_KEY, PUBLICKEY, SERVER }; @@ -51,70 +52,98 @@ struct CKey }; Type type; - PKType pk_type; + QString label; - bool operator==(const CKey &other) const { return other.key == key; } + // Decryption data + QByteArray encrypted_fmk; - QByteArray key, cipher, publicKey; + // Recipients public key + // QByteArray key; + + bool isSymmetric() const { return type == Type::SYMMETRIC_KEY; } + bool isPKI() const { return (type == Type::CERTIFICATE) || (type == Type::CDOC1) || (type == Type::PUBLICKEY) || (type == Type::SERVER); } + bool isCertificate() const { return (type == Type::CERTIFICATE) || (type == Type::CDOC1); } + + bool isTheSameRecipient(const CKey &key) const; + bool isTheSameRecipient(const QSslCertificate &cert) const; protected: - CKey(Type _type) : type(_type), pk_type(PKType::ECC) {}; - CKey(Type _type, PKType _pk_type, QByteArray _key): type(_type), pk_type(_pk_type), key(std::move(_key)) {} + CKey(Type _type) : type(_type) {}; +private: + bool operator==(const CKey &other) const { return false; } }; -// CDoc1 key +// Symmetric key (plain or PBKDF) -struct CKeyCD1 : public CKey { - QString agreement, concatDigest, derive, method, id, name; - QByteArray AlgorithmID, PartyUInfo, PartyVInfo; - QSslCertificate cert; - QString recipient; +struct CKeySymmetric : public CKey { + QByteArray salt; + // PBKDF + QByteArray pw_salt; + // 0 symmetric key, >0 password + int32_t kdf_iter; - void setCert(const QSslCertificate &c); + CKeySymmetric(const QByteArray& _salt) : CKey(Type::SYMMETRIC_KEY), salt(_salt), kdf_iter(0) {} +}; - static std::shared_ptr newEmpty(); - static std::shared_ptr fromKey(QByteArray _key, PKType _pk_type); - static std::shared_ptr fromCertificate(const QSslCertificate &cert); +// Public/private key + +struct CKeyPKI : public CKey { + // Recipient's public key + PKType pk_type; + QByteArray rcpt_key; - static bool isCDoc1Key(const CKey& key) { return key.type == Type::CDOC1; } protected: - CKeyCD1() : CKey(Type::CDOC1) {}; - CKeyCD1(QByteArray _key, PKType _pk_type) : CKey(Type::CDOC1, _pk_type, _key) {} - CKeyCD1(const QSslCertificate &cert); + CKeyPKI(Type _type) : CKey(_type), pk_type(PKType::ECC) {}; + CKeyPKI(Type _type, PKType _pk_type, QByteArray _rcpt_key) : CKey(_type), pk_type(_pk_type), rcpt_key(_rcpt_key) {}; }; -struct CKeyCD2 : public CKey { - QString label; - CKeyCD2(Type type) : CKey(type) {}; - CKeyCD2(Type _type, PKType _pk_type, QByteArray _key): CKey(_type, _pk_type, _key) {} - static bool isCDoc2Key(const CKey& key) { return (key.type == Type::SYMMETRIC_KEY) || (key.type == Type::PUBLICKEY) || (key.type == Type::SERVER); } +// Contains relevant encryption data + +struct CKeyCert : public CKeyPKI { + QSslCertificate cert; + +protected: + CKeyCert(Type _type) : CKeyPKI(_type) {}; + CKeyCert(Type _type, const QSslCertificate &cert); + + void setCert(const QSslCertificate &c); }; -// Symmetric key (plain or PBKDF) +// CDoc1 key + +struct CKeyCD1 : public CKeyCert { + + QByteArray publicKey; + QString concatDigest, method; + QByteArray AlgorithmID, PartyUInfo, PartyVInfo; + + static std::shared_ptr newEmpty(); + static std::shared_ptr fromCertificate(const QSslCertificate &cert); -struct CKeySymmetric : public CKeyCD2 { - QByteArray salt; - // PBKDF - QByteArray pw_salt; - int32_t kdf_iter; // 0 symmetric key, >0 password + void setCert(const QSslCertificate &c) { CKeyCert::setCert(c); } - CKeySymmetric(const QByteArray& _salt) : CKeyCD2(Type::SYMMETRIC_KEY), salt(_salt), kdf_iter(0) {} + static bool isCDoc1Key(const CKey& key) { return key.type == Type::CDOC1; } +protected: + CKeyCD1() : CKeyCert(Type::CDOC1) {}; + CKeyCD1(const QSslCertificate &cert) : CKeyCert(CKey::Type::CDOC1, cert) {}; }; -struct CKeyPK : public CKeyCD2 { - QByteArray encrypted_kek; +struct CKeyPK : public CKeyPKI { + // Either ECC public key or RSA encrypted kek + QByteArray key_material; - CKeyPK() : CKeyCD2(Type::PUBLICKEY) {}; - CKeyPK(PKType _pk_type, QByteArray _key) : CKeyCD2(Type::PUBLICKEY, _pk_type, _key) {}; + CKeyPK() : CKeyPKI(Type::PUBLICKEY) {}; + CKeyPK(PKType _pk_type, QByteArray _rcpt_key) : CKeyPKI(Type::PUBLICKEY, _pk_type, _rcpt_key) {}; }; -struct CKeyServer : public CKeyCD2 { - QString keyserver_id, transaction_id; +struct CKeyServer : public CKeyPKI { + // Server info + QString keyserver_id, transaction_id; static std::shared_ptr fromKey(QByteArray _key, PKType _pk_type); protected: - CKeyServer(QByteArray _key, PKType _pk_type) : CKeyCD2(Type::SERVER, _pk_type, _key) {}; + CKeyServer(QByteArray _rcpt_key, PKType _pk_type) : CKeyPKI(Type::SERVER, _pk_type, _rcpt_key) {}; }; class CDoc @@ -163,6 +192,8 @@ class CryptoDoc final: public QObject bool saveCopy(const QString &filename); ria::qdigidoc4::ContainerState state() const; + bool encryptLT(const QString& label, const QByteArray& secret, unsigned int kdf_iter); + private: class Private; Private *d; diff --git a/client/MainWindow.cpp b/client/MainWindow.cpp index b7bb96d1..c0f99b61 100644 --- a/client/MainWindow.cpp +++ b/client/MainWindow.cpp @@ -161,6 +161,7 @@ MainWindow::MainWindow( QWidget *parent ) connect(ui->cryptoContainerPage, &ContainerPage::removed, this, &MainWindow::removeAddress); connect(ui->cryptoContainerPage, &ContainerPage::decryptReq, this, &MainWindow::decryptClicked); + connect(ui->cryptoContainerPage, &ContainerPage::encryptLTReq, this, &MainWindow::encryptLTClicked); connect(ui->accordion, &Accordion::changePin1Clicked, this, &MainWindow::changePin1Clicked); connect(ui->accordion, &Accordion::changePin2Clicked, this, &MainWindow::changePin2Clicked); @@ -274,10 +275,17 @@ void MainWindow::decrypt(std::shared_ptr key) std::shared_ptr skey = std::static_pointer_cast(key); qDebug() << skey->label; PasswordDialog p; - if(!p.exec()) return; - QString pwd = p.password(); - qDebug() << pwd; - secret = pwd.toUtf8(); + if (skey->kdf_iter > 0) { + p.setMode(PasswordDialog::Mode::DECRYPT, PasswordDialog::Type::PASSWORD); + if(!p.exec()) return; + secret = p.secret(); + qDebug() << "Secret:" << QString::fromUtf8(secret); + } else { + p.setMode(PasswordDialog::Mode::DECRYPT, PasswordDialog::Type::KEY); + if(!p.exec()) return; + secret = p.secret(); + qDebug() << "Secret:" << QString::fromUtf8(secret.toHex()); + } } WaitDialogHolder waitDialog(this, tr("Decrypting")); @@ -1173,3 +1181,22 @@ MainWindow::decryptClicked(std::shared_ptr key) qDebug() << "Decrypt clicked:"; decrypt(key); } + +void +MainWindow::encryptLTClicked() +{ + qDebug() << "LT encrypt"; + if (!cryptoDoc) return; + PasswordDialog p; + p.setMode(PasswordDialog::Mode::ENCRYPT, PasswordDialog::Type::PASSWORD); + if(!p.exec()) return; + QString label = p.label(); + QByteArray secret = p.secret(); + if (p.type == PasswordDialog::Type::PASSWORD) { + qDebug() << "Secret:" << QString::fromUtf8(secret); + cryptoDoc->encryptLT(label, secret, 65536); + } else { + qDebug() << "Secret:" << QString::fromUtf8(secret.toHex()); + cryptoDoc->encryptLT(label, secret, 0); + } +} diff --git a/client/MainWindow.h b/client/MainWindow.h index e6fb63e9..88b02bc1 100644 --- a/client/MainWindow.h +++ b/client/MainWindow.h @@ -122,4 +122,5 @@ private Q_SLOTS: WarningList *warnings; void decryptClicked(std::shared_ptr key); + void encryptLTClicked(); }; diff --git a/client/dialogs/AddRecipients.cpp b/client/dialogs/AddRecipients.cpp index fdd41049..e44ed911 100644 --- a/client/dialogs/AddRecipients.cpp +++ b/client/dialogs/AddRecipients.cpp @@ -19,6 +19,7 @@ #include "AddRecipients.h" +#include "dialogs/PasswordDialog.h" #include "ui_AddRecipients.h" #include "Application.h" @@ -33,6 +34,7 @@ #include "TokenData.h" #include "dialogs/WarningDialog.h" #include "effects/Overlay.h" +#include "Crypto.h" #include #include @@ -63,6 +65,7 @@ AddRecipients::AddRecipients(ItemList* itemList, QWidget *parent) ui->fromCard->setFont(Styles::font(Styles::Condensed, 12)); ui->fromFile->setFont(Styles::font(Styles::Condensed, 12)); ui->fromHistory->setFont(Styles::font(Styles::Condensed, 12)); + ui->addKey->setFont(Styles::font(Styles::Condensed, 12)); ui->cancel->setFont(Styles::font(Styles::Condensed, 14)); ui->confirm->setFont(Styles::font(Styles::Condensed, 14)); @@ -90,6 +93,7 @@ AddRecipients::AddRecipients(ItemList* itemList, QWidget *parent) connect(ui->fromFile, &QPushButton::clicked, this, &AddRecipients::addRecipientFromFile); connect(ui->fromHistory, &QPushButton::clicked, this, &AddRecipients::addRecipientFromHistory); + connect(ui->addKey, &QPushButton::clicked, this, &AddRecipients::addRecipientKey); for(Item *item: itemList->items) addRecipientToRightPane((qobject_cast(item))->getKey(), false); @@ -167,6 +171,11 @@ void AddRecipients::addRecipientFromHistory() dlg->open(); } +void AddRecipients::addRecipientKey() +{ + qDebug() << "Adding key to recipients list"; +} + AddressItem * AddRecipients::addRecipientToLeftPane(const QSslCertificate& cert) { AddressItem *leftItem = leftList.value(cert); @@ -176,10 +185,10 @@ AddressItem * AddRecipients::addRecipientToLeftPane(const QSslCertificate& cert) leftItem = new AddressItem(CKeyCD1::fromCertificate(cert), ui->leftPane); leftList.insert(cert, leftItem); ui->leftPane->addWidget(leftItem); - bool contains = false; - std::shared_ptr t = CKeyCD1::fromCertificate(cert); + + bool contains = false; for (std::shared_ptr k: rightList) { - if (*k == *t) { + if (k->isTheSameRecipient(cert)) { contains = true; break; } @@ -200,7 +209,7 @@ AddressItem * AddRecipients::addRecipientToLeftPane(const QSslCertificate& cert) bool AddRecipients::addRecipientToRightPane(std::shared_ptr key, bool update) { for (std::shared_ptr k: rightList) { - if (*k == *key) return false; + if (k->isTheSameRecipient(*key)) return false; } if(update) diff --git a/client/dialogs/AddRecipients.h b/client/dialogs/AddRecipients.h index 67ece87e..29516adc 100644 --- a/client/dialogs/AddRecipients.h +++ b/client/dialogs/AddRecipients.h @@ -49,7 +49,8 @@ class AddRecipients final : public QDialog void addRecipientFromCard(); void addRecipientFromFile(); void addRecipientFromHistory(); - AddressItem * addRecipientToLeftPane(const QSslCertificate& cert); + void addRecipientKey(); + AddressItem * addRecipientToLeftPane(const QSslCertificate& cert); bool addRecipientToRightPane(std::shared_ptr key, bool update = true); void addRecipientToRightPane(AddressItem *leftItem, bool update = true); void addSelectedCerts(const QList& selectedCertData); diff --git a/client/dialogs/AddRecipients.ui b/client/dialogs/AddRecipients.ui index 66fa94fe..d833abec 100644 --- a/client/dialogs/AddRecipients.ui +++ b/client/dialogs/AddRecipients.ui @@ -97,7 +97,7 @@ QPushButton:disabled { color: #727679; } - + 15 @@ -179,6 +179,13 @@ QPushButton:disabled { + + + + KEY + + + diff --git a/client/dialogs/KeyDialog.cpp b/client/dialogs/KeyDialog.cpp index 72f0dcc5..c8a113dd 100644 --- a/client/dialogs/KeyDialog.cpp +++ b/client/dialogs/KeyDialog.cpp @@ -71,22 +71,20 @@ KeyDialog::KeyDialog( const CKey &k, QWidget *parent ) bool adjust_size = false; if (k.type == CKey::Type::CDOC1) { const CKeyCD1& cd1key = static_cast(k); - addItem(tr("Recipient"), cd1key.recipient); + addItem(tr("Recipient"), cd1key.label); addItem(tr("Crypto method"), cd1key.method); - addItem(tr("Agreement method"), cd1key.agreement); - addItem(tr("Key derivation method"), cd1key.derive); + //7addItem(tr("Agreement method"), cd1key.agreement); + //addItem(tr("Key derivation method"), cd1key.derive); addItem(tr("ConcatKDF digest method"), cd1key.concatDigest); addItem(tr("Expiry date"), cd1key.cert.expiryDate().toLocalTime().toString(QStringLiteral("dd.MM.yyyy hh:mm:ss"))); addItem(tr("Issuer"), SslCertificate(cd1key.cert).issuerInfo(QSslCertificate::CommonName)); - adjust_size = !cd1key.agreement.isEmpty(); - } else if (CKeyCD2::isCDoc2Key(k)) { - const CKeyCD2& cd2key = static_cast(k); - addItem(tr("Label"), cd2key.label); - if (k.type == CKey::SERVER) { - const CKeyServer& sk = static_cast(k); - addItem(tr("Key server ID"), sk.keyserver_id); - addItem(tr("Transaction ID"), sk.transaction_id); - } + adjust_size = !cd1key.concatDigest.isEmpty(); + } + addItem(tr("Label"), k.label); + if (k.type == CKey::SERVER) { + const CKeyServer& sk = static_cast(k); + addItem(tr("Key server ID"), sk.keyserver_id); + addItem(tr("Transaction ID"), sk.transaction_id); } d->view->resizeColumnToContents( 0 ); if(adjust_size) adjustSize(); diff --git a/client/dialogs/PasswordDialog.cpp b/client/dialogs/PasswordDialog.cpp index de41e85a..b54f11be 100644 --- a/client/dialogs/PasswordDialog.cpp +++ b/client/dialogs/PasswordDialog.cpp @@ -1,11 +1,15 @@ #include "PasswordDialog.h" -#include "ui_PasswordDialog.h" +#include "Crypto.h" +#include "ui_passworddialog.h" PasswordDialog::PasswordDialog(QWidget *parent) - : QDialog(parent) + : QDialog(parent), mode(Mode::DECRYPT), type(Type::PASSWORD) , ui(new Ui::PasswordDialog) { ui->setupUi(this); + connect(ui->radioPassword, &QRadioButton::toggled, this, &PasswordDialog::passwordSelected); + connect(ui->radioKey, &QRadioButton::toggled, this, &PasswordDialog::symmetricKeySelected); + connect(ui->buttonGenKey, &QPushButton::clicked, this, &PasswordDialog::genKeyClicked); } PasswordDialog::~PasswordDialog() @@ -13,7 +17,83 @@ PasswordDialog::~PasswordDialog() delete ui; } -QString PasswordDialog::password() const +void +PasswordDialog::setMode(Mode _mode, Type _type) { - return ui->passwordLine->text(); + mode = _mode; + type = _type; + updateUI(); +} + +QString +PasswordDialog::label() +{ + return ui->labelLine->text(); +} + +QByteArray +PasswordDialog::secret() const +{ + if (type == Type::PASSWORD) { + return ui->passwordLine->text().toUtf8(); + } else { + QString hex = ui->passwordLine->text(); + return QByteArray::fromHex(hex.toUtf8()); + } +} + +void +PasswordDialog::passwordSelected(bool checked) +{ + Type new_type = (checked) ? Type::PASSWORD : Type::KEY; + if (new_type != type) setMode(mode, new_type); +} + +void +PasswordDialog::symmetricKeySelected(bool checked) +{ + Type new_type = (checked) ? Type::KEY : Type::PASSWORD; + if (new_type != type) setMode(mode, new_type); +} + +void +PasswordDialog::genKeyClicked() +{ + QByteArray key = Crypto::random(); + ui->passwordLine->setText(key.toHex()); +} + +void +PasswordDialog::updateUI() +{ + if (mode == Mode::DECRYPT) { + ui->radioBox->hide(); + ui->labelLabel->hide(); + ui->labelLine->hide(); + ui->buttonGenKey->hide(); + if (type == Type::PASSWORD) { + ui->radioPassword->setChecked(true); + ui->passwordLabel->setText("Enter password to decrypt the document"); + ui->passwordLine->setEchoMode(QLineEdit::EchoMode::Password); + } else { + ui->radioKey->setChecked(true); + ui->passwordLabel->setText("Enter key to decrypt the document"); + ui->passwordLine->setEchoMode(QLineEdit::EchoMode::Normal); + } + } else { + ui->radioBox->show(); + ui->labelLabel->show(); + ui->labelLine->show(); + if (type == Type::PASSWORD) { + ui->radioPassword->setChecked(true); + ui->passwordLabel->setText("Enter a password to encrypt the document"); + ui->passwordLine->setEchoMode(QLineEdit::EchoMode::Password); + ui->buttonGenKey->hide(); + } else { + ui->radioKey->setChecked(true); + ui->passwordLabel->setText("Enter a key to encrypt the document"); + ui->passwordLine->setEchoMode(QLineEdit::EchoMode::Normal); + ui->buttonGenKey->show(); + } + } } diff --git a/client/dialogs/PasswordDialog.h b/client/dialogs/PasswordDialog.h index 380128cc..e4b317da 100644 --- a/client/dialogs/PasswordDialog.h +++ b/client/dialogs/PasswordDialog.h @@ -12,12 +12,33 @@ class PasswordDialog : public QDialog Q_OBJECT public: + enum Mode { + ENCRYPT, + DECRYPT + }; + + enum Type { + PASSWORD, + KEY + }; + + Mode mode; + Type type; + explicit PasswordDialog(QWidget *parent = nullptr); ~PasswordDialog(); - QString password() const; + void setMode(Mode mode, Type type); + + QString label(); + QByteArray secret() const; private: Ui::PasswordDialog *ui; + + void passwordSelected(bool checked); + void symmetricKeySelected(bool checked); + void genKeyClicked(); + void updateUI(); }; #endif // PASSWORDDIALOG_H diff --git a/client/dialogs/PasswordDialog.ui b/client/dialogs/PasswordDialog.ui index c17c1905..a3b1d7f1 100644 --- a/client/dialogs/PasswordDialog.ui +++ b/client/dialogs/PasswordDialog.ui @@ -7,13 +7,52 @@ 0 0 400 - 131 + 253 Dialog + + + + + + + + + + Password + + + true + + + + + + + Symmetric key + + + + + + + + + + Key label (recipient name or id) + + + Qt::AlignCenter + + + + + + @@ -31,6 +70,13 @@ + + + + Generate Key + + + diff --git a/client/widgets/AddressItem.cpp b/client/widgets/AddressItem.cpp index cca8d585..e7bab427 100644 --- a/client/widgets/AddressItem.cpp +++ b/client/widgets/AddressItem.cpp @@ -68,13 +68,12 @@ AddressItem::AddressItem(std::shared_ptr k, QWidget *parent, bool showIcon key->cert.subjectInfo("GN").join(' ') + " " + key->cert.subjectInfo("SN").join(' ') : key->cert.subjectInfo("CN").join(' ')).toHtmlEscaped(); } else { - std::shared_ptr cd2key = std::static_pointer_cast(ui->key); ui->code = {}; - ui->label = cd2key->label; + ui->label = k->label; } if(ui->label.isEmpty() && ui->key->type == CKey::PUBLICKEY) { const CKeyPK& pk = static_cast(*ui->key); - ui->label = pk.encrypted_kek; + ui->label = pk.key_material; } setIdType(); showButton(AddressItem::Remove); @@ -113,7 +112,7 @@ const std::shared_ptr AddressItem::getKey() const void AddressItem::idChanged(std::shared_ptr key) { - ui->yourself = !key->key.isNull() && *ui->key == *key; + ui->yourself = key->isTheSameRecipient(*ui->key); setName(); } @@ -162,37 +161,52 @@ void AddressItem::stateChange(ContainerState state) void AddressItem::setIdType() { - if (ui->key->type != CKey::CDOC1) { - ui->idType->setText("CDOC2 Key"); - return; + if (ui->key->isPKI()) { + std::shared_ptr pki = std::static_pointer_cast(ui->key); + if (ui->key->isCertificate()) { + std::shared_ptr ckd = std::static_pointer_cast(ui->key); + ui->idType->setHidden(false); + QString str; + SslCertificate cert(ckd->cert); + SslCertificate::CertType type = cert.type(); + if(type & SslCertificate::DigiIDType) + str = tr("digi-ID"); + else if(type & SslCertificate::EstEidType) + str = tr("ID-card"); + else if(type & SslCertificate::MobileIDType) + str = tr("mobile-ID"); + else if(type & SslCertificate::TempelType) + { + if(cert.keyUsage().contains(SslCertificate::NonRepudiation)) + str = tr("e-Seal"); + else if(cert.enhancedKeyUsage().contains(SslCertificate::ClientAuth)) + str = tr("Authentication certificate"); + else + str = tr("Certificate for Encryption"); + } + + if(!str.isEmpty()) + str += QStringLiteral(" - "); + DateTime date(cert.expiryDate().toLocalTime()); + ui->idType->setText(QStringLiteral("%1%2 %3").arg(str, + cert.isValid() ? tr("Expires on") : tr("Expired on"), + date.formatDate(QStringLiteral("dd. MMMM yyyy")))); + } else { + QString type = (pki->pk_type == CKey::PKType::RSA) ? "RSA" : "ECC"; + ui->idType->setHidden(false); + ui->idType->setText(type + " public key"); + } + } else if (ui->key->isSymmetric()) { + std::shared_ptr ckd = std::static_pointer_cast(ui->key); + ui->idType->setHidden(false); + if (ckd->kdf_iter > 0) { + ui->idType->setText("Password derived key"); + } else { + ui->idType->setText("Symmetric key"); + } + } else { + ui->idType->setHidden(false); + ui->idType->setText("Unknown key type"); } - ui->idType->setHidden(false); - std::shared_ptr ckd = std::static_pointer_cast(ui->key); - - QString str; - SslCertificate cert(ckd->cert); - SslCertificate::CertType type = cert.type(); - if(type & SslCertificate::DigiIDType) - str = tr("digi-ID"); - else if(type & SslCertificate::EstEidType) - str = tr("ID-card"); - else if(type & SslCertificate::MobileIDType) - str = tr("mobile-ID"); - else if(type & SslCertificate::TempelType) - { - if(cert.keyUsage().contains(SslCertificate::NonRepudiation)) - str = tr("e-Seal"); - else if(cert.enhancedKeyUsage().contains(SslCertificate::ClientAuth)) - str = tr("Authentication certificate"); - else - str = tr("Certificate for Encryption"); - } - - if(!str.isEmpty()) - str += QStringLiteral(" - "); - DateTime date(cert.expiryDate().toLocalTime()); - ui->idType->setText(QStringLiteral("%1%2 %3").arg(str, - cert.isValid() ? tr("Expires on") : tr("Expired on"), - date.formatDate(QStringLiteral("dd. MMMM yyyy")))); } diff --git a/client/widgets/ContainerPage.cpp b/client/widgets/ContainerPage.cpp index a868d3d4..213b2724 100644 --- a/client/widgets/ContainerPage.cpp +++ b/client/widgets/ContainerPage.cpp @@ -18,6 +18,7 @@ */ #include "ContainerPage.h" +#include "dialogs/PasswordDialog.h" #include "ui_ContainerPage.h" #include "CryptoDoc.h" @@ -85,6 +86,8 @@ ContainerPage::ContainerPage(QWidget *parent) connect(ui->containerFile, &QLabel::linkActivated, this, [this](const QString &link) { emit action(Actions::ContainerNavigate, link); }); + connect(ui->encryptLT, &LabelButton::clicked, this, [this]{emit encryptLTReq();}); + ui->summary->setVisible(Settings::SHOW_PRINT_SUMMARY); } @@ -408,7 +411,7 @@ void ContainerPage::updatePanes(ContainerState state) ui->leftPane->init(fileName, QT_TRANSLATE_NOOP("ItemList", "Container files")); showSigningButton(); setButtonsVisible({ ui->cancel, ui->convert, ui->save }, true); - setButtonsVisible({ ui->saveAs, ui->email, ui->summary }, false); + setButtonsVisible({ ui->saveAs, ui->email, ui->summary, ui->encryptLT }, false); break; case UnsignedSavedContainer: cancelText = QT_TR_NOOP("STARTING"); @@ -419,7 +422,7 @@ void ContainerPage::updatePanes(ContainerState state) setButtonsVisible({ ui->cancel, ui->convert, ui->saveAs, ui->email, ui->summary }, true); else setButtonsVisible({ ui->cancel, ui->convert, ui->saveAs, ui->email }, true); - setButtonsVisible({ ui->save }, false); + setButtonsVisible({ ui->save, ui->encryptLT }, false); showRightPane( ItemSignature, QT_TRANSLATE_NOOP("ItemList", "Container is not signed")); break; case SignedContainer: @@ -432,7 +435,7 @@ void ContainerPage::updatePanes(ContainerState state) setButtonsVisible({ ui->cancel, ui->convert, ui->saveAs, ui->email, ui->summary }, true); else setButtonsVisible({ ui->cancel, ui->convert, ui->saveAs, ui->email }, true); - setButtonsVisible({ ui->save }, false); + setButtonsVisible({ ui->save, ui->encryptLT }, false); break; case UnencryptedContainer: cancelText = QT_TR_NOOP("STARTING"); @@ -442,7 +445,7 @@ void ContainerPage::updatePanes(ContainerState state) ui->leftPane->init(fileName); showRightPane(ItemAddress, QT_TRANSLATE_NOOP("ItemList", "Recipients")); showMainAction({ EncryptContainer }); - setButtonsVisible({ ui->cancel, ui->convert }, true); + setButtonsVisible({ ui->cancel, ui->convert, ui->encryptLT }, true); setButtonsVisible({ ui->save, ui->saveAs, ui->email, ui->summary }, false); break; case EncryptedContainer: @@ -454,7 +457,7 @@ void ContainerPage::updatePanes(ContainerState state) showRightPane(ItemAddress, QT_TRANSLATE_NOOP("ItemList", "Recipients")); updateDecryptionButton(); setButtonsVisible({ ui->save, ui->summary }, false); - setButtonsVisible({ ui->cancel, ui->convert, ui->saveAs, ui->email }, true); + setButtonsVisible({ ui->cancel, ui->convert, ui->saveAs, ui->email, ui->encryptLT }, true); break; default: // Uninitialized cannot be shown on container page diff --git a/client/widgets/ContainerPage.h b/client/widgets/ContainerPage.h index ad02417d..22299e17 100644 --- a/client/widgets/ContainerPage.h +++ b/client/widgets/ContainerPage.h @@ -64,6 +64,7 @@ class ContainerPage final : public QWidget void warning(const WarningText &warningText); void decryptReq(std::shared_ptr key); + void encryptLTReq(); private: void addressSearch(); diff --git a/client/widgets/ContainerPage.ui b/client/widgets/ContainerPage.ui index a1832cc1..0142e815 100644 --- a/client/widgets/ContainerPage.ui +++ b/client/widgets/ContainerPage.ui @@ -214,6 +214,8 @@ background-color: #F4F5F6; Roboto Condensed 12 + false + false @@ -303,6 +305,8 @@ border: none; Roboto Condensed 12 + false + false @@ -340,6 +344,8 @@ border: none; Roboto Condensed 12 + false + false @@ -376,6 +382,8 @@ border: none; 12 + false + false @@ -414,6 +422,8 @@ border: none; Roboto Condensed 12 + false + false @@ -434,6 +444,46 @@ border: none; + + + + + 182 + 25 + + + + + 182 + 25 + + + + + Roboto Condensed + 12 + false + false + + + + PointingHandCursor + + + Encrypt long-term + + + border-radius: 3px; +color: #006eb5; +text-decoration: none solid rgb(0, 110, 181); +text-align: center; +border: none; + + + ENCRYPT LONG_TERM + + + @@ -456,17 +506,17 @@ border: none; - - LabelButton - QToolButton -
widgets/LabelButton.h
-
ItemList QScrollArea
widgets/ItemList.h
1
+ + LabelButton + QToolButton +
widgets/LabelButton.h
+
FileList QScrollArea From 907bf68b9851594809262def6c22560f530d9d7c Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Wed, 15 May 2024 00:23:04 +0300 Subject: [PATCH 04/22] Fixes and cleanups for LT crypto Signed-off-by: Lauris Kaplinski --- client/CDoc1.cpp | 33 +++--- client/CDoc2.cpp | 175 ++++++++++++++---------------- client/CryptoDoc.cpp | 30 ++--- client/CryptoDoc.h | 57 ++++++---- client/MainWindow.cpp | 70 ++++++++---- client/MainWindow.h | 2 +- client/common_enums.h | 4 +- client/dialogs/AddRecipients.cpp | 17 +-- client/dialogs/AddRecipients.h | 1 - client/dialogs/AddRecipients.ui | 9 +- client/dialogs/KeyDialog.cpp | 4 +- client/dialogs/PasswordDialog.cpp | 47 ++++---- client/dialogs/PasswordDialog.h | 3 +- client/dialogs/PasswordDialog.ui | 125 +++++++++++++-------- client/widgets/AddressItem.cpp | 24 ++-- client/widgets/AddressItem.ui | 18 +-- client/widgets/ContainerPage.cpp | 6 +- client/widgets/ContainerPage.h | 1 - 18 files changed, 329 insertions(+), 297 deletions(-) diff --git a/client/CDoc1.cpp b/client/CDoc1.cpp index 65b4b106..1a98d22a 100644 --- a/client/CDoc1.cpp +++ b/client/CDoc1.cpp @@ -117,7 +117,7 @@ CDoc1::CDoc1(const QString &path) if(xml.name() != QLatin1String("EncryptedKey")) return; - std::shared_ptr key = CKeyCD1::newEmpty(); + std::shared_ptr key = std::make_shared(); // Id is never used //key->id = xml.attributes().value(QLatin1String("Id")).toString(); key->label = xml.attributes().value(QLatin1String("Recipient")).toString(); @@ -298,7 +298,7 @@ CKey::DecryptionStatus CDoc1::canDecrypt(const QSslCertificate &cert) const { std::shared_ptr key = getDecryptionKey(cert); - if (!key) return CKey::DecryptionStatus::CAN_DECRYPT; + if (key) return CKey::DecryptionStatus::CAN_DECRYPT; return CKey::DecryptionStatus::CANNOT_DECRYPT; } @@ -307,7 +307,7 @@ std::shared_ptr CDoc1::getDecryptionKey(const QSslCertificate &cert) const for(std::shared_ptr key: qAsConst(keys)) { if (key->type != CKey::Type::CDOC1) continue; - std::shared_ptr k = std::static_pointer_cast(key); + std::shared_ptr k = std::static_pointer_cast(key); if(!ENC_MTH.contains(method) || k->cert != cert || k->encrypted_fmk.isEmpty()) @@ -468,20 +468,19 @@ bool CDoc1::save(const QString &path) writeElement(w, DS, QStringLiteral("KeyInfo"), [&]{ for(std::shared_ptr key: qAsConst(keys)) { - if (key->type != CKey::Type::CDOC1) { - return; - } - std::shared_ptr k = std::static_pointer_cast(key); + // Only certificate-based keys can be used in CDoc1 + if (key->type != CKey::Type::CERTIFICATE) return; + std::shared_ptr ckey = std::static_pointer_cast(key); writeElement(w, DENC, QStringLiteral("EncryptedKey"), [&]{ // Id is never used //if(!k->id.isEmpty()) // w.writeAttribute(QStringLiteral("Id"), k->id); - if(!k->label.isEmpty()) - w.writeAttribute(QStringLiteral("Recipient"), k->label); + if(!ckey->label.isEmpty()) + w.writeAttribute(QStringLiteral("Recipient"), ckey->label); QByteArray cipher; - if(k->pk_type == CKey::PKType::RSA) + if(ckey->pk_type == CKey::PKType::RSA) { - cipher = Crypto::encrypt(X509_get0_pubkey((const X509*)k->cert.handle()), RSA_PKCS1_PADDING, transportKey); + cipher = Crypto::encrypt(X509_get0_pubkey((const X509*)ckey->cert.handle()), RSA_PKCS1_PADDING, transportKey); if(cipher.isEmpty()) return; writeElement(w, DENC, QStringLiteral("EncryptionMethod"), { @@ -492,13 +491,13 @@ bool CDoc1::save(const QString &path) //if(!k->name.isEmpty()) // w.writeTextElement(DS, QStringLiteral("KeyName"), k->name); writeElement(w, DS, QStringLiteral("X509Data"), [&]{ - writeBase64Element(w, DS, QStringLiteral("X509Certificate"), k->cert.toDer()); + writeBase64Element(w, DS, QStringLiteral("X509Certificate"), ckey->cert.toDer()); }); }); } else { - EVP_PKEY *peerPKey = X509_get0_pubkey((const X509*)k->cert.handle()); + EVP_PKEY *peerPKey = X509_get0_pubkey((const X509*)ckey->cert.handle()); auto priv = Crypto::genECKey(peerPKey); QByteArray sharedSecret = Crypto::derive(priv.get(), peerPKey); if(sharedSecret.isEmpty()) @@ -515,7 +514,7 @@ bool CDoc1::save(const QString &path) default: concatDigest = SHA512_MTH; break; } QByteArray encryptionKey = Crypto::concatKDF(SHA_MTH[concatDigest], KWAES_SIZE[encryptionMethod], - sharedSecret, props.value(QStringLiteral("DocumentFormat")).toUtf8() + SsDer + k->cert.toDer()); + sharedSecret, props.value(QStringLiteral("DocumentFormat")).toUtf8() + SsDer + ckey->cert.toDer()); #ifndef NDEBUG qDebug() << "ENC Ss" << SsDer.toHex(); qDebug() << "ENC Ksr" << sharedSecret.toHex(); @@ -540,7 +539,7 @@ bool CDoc1::save(const QString &path) writeElement(w, XENC11, QStringLiteral("ConcatKDFParams"), { {QStringLiteral("AlgorithmID"), QStringLiteral("00") + props.value(QStringLiteral("DocumentFormat")).toUtf8().toHex()}, {QStringLiteral("PartyUInfo"), QStringLiteral("00") + SsDer.toHex()}, - {QStringLiteral("PartyVInfo"), QStringLiteral("00") + k->cert.toDer().toHex()}, + {QStringLiteral("PartyVInfo"), QStringLiteral("00") + ckey->cert.toDer().toHex()}, }, [&]{ writeElement(w, DS, QStringLiteral("DigestMethod"), { {QStringLiteral("Algorithm"), concatDigest}, @@ -560,7 +559,7 @@ bool CDoc1::save(const QString &path) }); writeElement(w, DENC, QStringLiteral("RecipientKeyInfo"), [&]{ writeElement(w, DS, QStringLiteral("X509Data"), [&]{ - writeBase64Element(w, DS, QStringLiteral("X509Certificate"), k->cert.toDer()); + writeBase64Element(w, DS, QStringLiteral("X509Certificate"), ckey->cert.toDer()); }); }); }); @@ -596,7 +595,7 @@ QByteArray CDoc1::getFMK(const CKey &key, const QByteArray& secret) setLastError(QStringLiteral("Not a CDoc1 key")); return {}; } - const CKeyCD1& ckey = static_cast(key); + const CKeyCDoc1& ckey = static_cast(key); setLastError({}); QByteArray decryptedKey = qApp->signer()->decrypt([&ckey](QCryptoBackend *backend) { if(ckey.pk_type == CKey::PKType::RSA) diff --git a/client/CDoc2.cpp b/client/CDoc2.cpp index e4629b6b..7df3ff73 100644 --- a/client/CDoc2.cpp +++ b/client/CDoc2.cpp @@ -457,7 +457,7 @@ CDoc2::CDoc2(const QString &_path) continue; } auto fillRecipientPK = [&] (CKey::PKType pk_type, auto key) { - std::shared_ptr k(new CKeyPK(pk_type, toByteArray(key->recipient_public_key()))); + std::shared_ptr k(new CKeyPublicKey(pk_type, toByteArray(key->recipient_public_key()))); k->label = toString(recipient->key_label()); k->encrypted_fmk = toByteArray(recipient->encrypted_fmk()); return k; @@ -472,7 +472,7 @@ CDoc2::CDoc2(const QString &_path) qWarning() << "Unsupported ECC curve: skipping"; continue; } - std::shared_ptr k = fillRecipientPK(CKey::PKType::ECC, key); + std::shared_ptr k = fillRecipientPK(CKey::PKType::ECC, key); k->key_material = toByteArray(key->sender_public_key()); keys.append(k); } @@ -480,7 +480,7 @@ CDoc2::CDoc2(const QString &_path) case Capsule::RSAPublicKeyCapsule: if(const auto *key = recipient->capsule_as_RSAPublicKeyCapsule()) { - std::shared_ptr k = fillRecipientPK(CKey::PKType::RSA, key); + std::shared_ptr k = fillRecipientPK(CKey::PKType::RSA, key); k->key_material = toByteArray(key->encrypted_kek()); keys.append(k); } @@ -709,103 +709,90 @@ bool CDoc2::save(const QString &_path) }; for(std::shared_ptr key: keys) { - if (key->isSymmetric()) { - const CKeySymmetric &sk = static_cast(*key); - QByteArray symmetric_key; - if (sk.kdf_iter > 0) { - // PasswordSalt_i = CSRNG() - // PasswordKeyMaterial_i = PBKDF2(Password_i, PasswordSalt_i) - } else { - //symmetric_key = - } - // KeyMaterialSalt_i = CSRNG() - // KEK_i = HKDF(KeyMaterialSalt_i, PasswordKeyMaterial_i) - // Capsule_i = {KeyMaterialSalt_i, PasswordSalt_i} - // EncryptedFMK_i = XOR(FMK, KEK_i) - - } else if (!key->isPKI()) { - const CKeyPKI& pki = static_cast(*key); - if(pki.pk_type == CKey::PKType::RSA) { - QByteArray kek = Crypto::random(fmk.size()); - QByteArray xor_key = Crypto::xor_data(fmk, kek); - auto publicKey = Crypto::fromRSAPublicKeyDer(pki.rcpt_key); - if(!publicKey) - return false; - QByteArray encrytpedKek = Crypto::encrypt(publicKey.get(), RSA_PKCS1_OAEP_PADDING, kek); + if (!key->isPKI()) { + return setLastError(QStringLiteral("Invalid key type")); + } + const CKeyPKI& pki = static_cast(*key); + if(pki.pk_type == CKey::PKType::RSA) { + QByteArray kek = Crypto::random(fmk.size()); + QByteArray xor_key = Crypto::xor_data(fmk, kek); + auto publicKey = Crypto::fromRSAPublicKeyDer(pki.rcpt_key); + if(!publicKey) + return false; + QByteArray encrytpedKek = Crypto::encrypt(publicKey.get(), RSA_PKCS1_OAEP_PADDING, kek); #ifndef NDEBUG - qDebug() << "publicKeyDer" << pki.rcpt_key.toHex(); - qDebug() << "kek" << kek.toHex(); - qDebug() << "xor" << xor_key.toHex(); - qDebug() << "encrytpedKek" << encrytpedKek.toHex(); + qDebug() << "publicKeyDer" << pki.rcpt_key.toHex(); + qDebug() << "kek" << kek.toHex(); + qDebug() << "xor" << xor_key.toHex(); + qDebug() << "encrytpedKek" << encrytpedKek.toHex(); #endif - if(!Settings::CDOC2_USE_KEYSERVER) { - auto rsaPublicKey = cdoc20::recipients::CreateRSAPublicKeyCapsule(builder, - toVector(pki.rcpt_key), toVector(encrytpedKek)); - auto offs = cdoc20::header::CreateRecipientRecord(builder, - cdoc20::recipients::Capsule::RSAPublicKeyCapsule, rsaPublicKey.Union(), - toString(pki.label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); - recipients.push_back(offs); - } else { - if (key->type != CKey::Type::SERVER) { - return setLastError(QStringLiteral("Not a server key")); - } - const std::shared_ptr skey = std::static_pointer_cast(key); - if(!sendToServer(skey, skey->rcpt_key, encrytpedKek, QLatin1String("rsa"))) - return false; - auto rsaKeyServer = cdoc20::recipients::CreateRsaKeyDetails(builder, toVector(skey->rcpt_key)); - auto keyServer = cdoc20::recipients::CreateKeyServerCapsule(builder, - cdoc20::recipients::KeyDetailsUnion::RsaKeyDetails, - rsaKeyServer.Union(), toString(skey->keyserver_id), toString(skey->transaction_id)); - auto offs = cdoc20::header::CreateRecipientRecord(builder, - cdoc20::recipients::Capsule::KeyServerCapsule, keyServer.Union(), - toString(pki.label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); - recipients.push_back(offs); - } + if(!Settings::CDOC2_USE_KEYSERVER) { + auto rsaPublicKey = cdoc20::recipients::CreateRSAPublicKeyCapsule(builder, + toVector(pki.rcpt_key), toVector(encrytpedKek)); + auto offs = cdoc20::header::CreateRecipientRecord(builder, + cdoc20::recipients::Capsule::RSAPublicKeyCapsule, rsaPublicKey.Union(), + toString(pki.label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); + recipients.push_back(offs); } else { - auto publicKey = Crypto::fromECPublicKeyDer(pki.rcpt_key, NID_secp384r1); - if(!publicKey) return false; - auto ephKey = Crypto::genECKey(publicKey.get()); - QByteArray sharedSecret = Crypto::derive(ephKey.get(), publicKey.get()); - QByteArray ephPublicKeyDer = Crypto::toPublicKeyDer(ephKey.get()); - QByteArray kekPm = Crypto::extract(sharedSecret, KEKPREMASTER); - QByteArray info = KEK + cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) + pki.rcpt_key + ephPublicKeyDer; - QByteArray kek = Crypto::expand(kekPm, info, fmk.size()); - QByteArray xor_key = Crypto::xor_data(fmk, kek); + if (key->type != CKey::Type::SERVER) { + return setLastError(QStringLiteral("Not a server key")); + } + const std::shared_ptr skey = std::static_pointer_cast(key); + if(!sendToServer(skey, skey->rcpt_key, encrytpedKek, QLatin1String("rsa"))) + return false; + auto rsaKeyServer = cdoc20::recipients::CreateRsaKeyDetails(builder, toVector(skey->rcpt_key)); + auto keyServer = cdoc20::recipients::CreateKeyServerCapsule(builder, + cdoc20::recipients::KeyDetailsUnion::RsaKeyDetails, + rsaKeyServer.Union(), toString(skey->keyserver_id), toString(skey->transaction_id)); + auto offs = cdoc20::header::CreateRecipientRecord(builder, + cdoc20::recipients::Capsule::KeyServerCapsule, keyServer.Union(), + toString(pki.label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); + recipients.push_back(offs); + } + } else { + auto publicKey = Crypto::fromECPublicKeyDer(pki.rcpt_key, NID_secp384r1); + if(!publicKey) return false; + auto ephKey = Crypto::genECKey(publicKey.get()); + QByteArray sharedSecret = Crypto::derive(ephKey.get(), publicKey.get()); + QByteArray ephPublicKeyDer = Crypto::toPublicKeyDer(ephKey.get()); + QByteArray kekPm = Crypto::extract(sharedSecret, KEKPREMASTER); + QByteArray info = KEK + cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) + pki.rcpt_key + ephPublicKeyDer; + QByteArray kek = Crypto::expand(kekPm, info, fmk.size()); + QByteArray xor_key = Crypto::xor_data(fmk, kek); #ifndef NDEBUG - qDebug() << "publicKeyDer" << pki.rcpt_key.toHex(); - qDebug() << "ephPublicKeyDer" << ephPublicKeyDer.toHex(); - qDebug() << "sharedSecret" << sharedSecret.toHex(); - qDebug() << "kekPm" << kekPm.toHex(); - qDebug() << "kek" << kek.toHex(); - qDebug() << "xor" << xor_key.toHex(); + qDebug() << "publicKeyDer" << pki.rcpt_key.toHex(); + qDebug() << "ephPublicKeyDer" << ephPublicKeyDer.toHex(); + qDebug() << "sharedSecret" << sharedSecret.toHex(); + qDebug() << "kekPm" << kekPm.toHex(); + qDebug() << "kek" << kek.toHex(); + qDebug() << "xor" << xor_key.toHex(); #endif - if(!Settings::CDOC2_USE_KEYSERVER) { - auto eccPublicKey = cdoc20::recipients::CreateECCPublicKeyCapsule(builder, - cdoc20::recipients::EllipticCurve::secp384r1, toVector(pki.rcpt_key), toVector(ephPublicKeyDer)); - auto offs = cdoc20::header::CreateRecipientRecord(builder, - cdoc20::recipients::Capsule::ECCPublicKeyCapsule, eccPublicKey.Union(), - toString(pki.label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); - recipients.push_back(offs); - } else { - if (key->type != CKey::Type::SERVER) { - return setLastError(QStringLiteral("Not a server key")); - } - const std::shared_ptr skey = std::static_pointer_cast(key); - if(!sendToServer(skey, skey->rcpt_key, ephPublicKeyDer, QLatin1String("ecc_secp384r1"))) - return false; - auto eccKeyServer = cdoc20::recipients::CreateEccKeyDetails(builder, - cdoc20::recipients::EllipticCurve::secp384r1, toVector(skey->rcpt_key)); - auto keyServer = cdoc20::recipients::CreateKeyServerCapsule(builder, - cdoc20::recipients::KeyDetailsUnion::EccKeyDetails, - eccKeyServer.Union(), toString(skey->keyserver_id), toString(skey->transaction_id)); - auto offs = cdoc20::header::CreateRecipientRecord(builder, - cdoc20::recipients::Capsule::KeyServerCapsule, keyServer.Union(), - toString(pki.label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); - recipients.push_back(offs); + if(!Settings::CDOC2_USE_KEYSERVER) { + auto eccPublicKey = cdoc20::recipients::CreateECCPublicKeyCapsule(builder, + cdoc20::recipients::EllipticCurve::secp384r1, toVector(pki.rcpt_key), toVector(ephPublicKeyDer)); + auto offs = cdoc20::header::CreateRecipientRecord(builder, + cdoc20::recipients::Capsule::ECCPublicKeyCapsule, eccPublicKey.Union(), + toString(pki.label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); + recipients.push_back(offs); + } else { + if (key->type != CKey::Type::SERVER) { + return setLastError(QStringLiteral("Not a server key")); } + const std::shared_ptr skey = std::static_pointer_cast(key); + if(!sendToServer(skey, skey->rcpt_key, ephPublicKeyDer, QLatin1String("ecc_secp384r1"))) + return false; + auto eccKeyServer = cdoc20::recipients::CreateEccKeyDetails(builder, + cdoc20::recipients::EllipticCurve::secp384r1, toVector(skey->rcpt_key)); + auto keyServer = cdoc20::recipients::CreateKeyServerCapsule(builder, + cdoc20::recipients::KeyDetailsUnion::EccKeyDetails, + eccKeyServer.Union(), toString(skey->keyserver_id), toString(skey->transaction_id)); + auto offs = cdoc20::header::CreateRecipientRecord(builder, + cdoc20::recipients::Capsule::KeyServerCapsule, keyServer.Union(), + toString(pki.label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); + recipients.push_back(offs); } } - } + } auto offset = cdoc20::header::CreateHeader(builder, builder.CreateVector(recipients), cdoc20::header::PayloadEncryptionMethod::CHACHA20POLY1305); @@ -1020,8 +1007,8 @@ QByteArray CDoc2::getFMK(const CKey &key, const QByteArray& secret) if(key.type == CKey::Type::SERVER) { const CKeyServer &sk = static_cast(key); key_material = fetchKeyMaterial(sk); - } else if (key.type == CKey::PUBLICKEY) { - const CKeyPK& pk = static_cast(key); + } else if (key.type == CKey::PUBLIC_KEY) { + const CKeyPublicKey& pk = static_cast(key); key_material = pk.key_material; } #ifndef NDEBUG diff --git a/client/CryptoDoc.cpp b/client/CryptoDoc.cpp index aa730aa4..54602c66 100644 --- a/client/CryptoDoc.cpp +++ b/client/CryptoDoc.cpp @@ -276,16 +276,6 @@ void CKeyCert::setCert(const QSslCertificate &c) pk_type = (k.algorithm() == QSsl::Rsa) ? PKType::RSA : PKType::ECC; } -std::shared_ptr -CKeyCD1::newEmpty() { - return std::shared_ptr(new CKeyCD1()); -} - -std::shared_ptr -CKeyCD1::fromCertificate(const QSslCertificate &cert) { - return std::shared_ptr(new CKeyCD1(cert)); -} - std::shared_ptr CKeyServer::fromKey(QByteArray _key, PKType _pk_type) { return std::shared_ptr(new CKeyServer(_key, _pk_type)); @@ -426,6 +416,16 @@ bool CryptoDoc::encrypt( const QString &filename ) return d->isEncrypted; } +bool +CryptoDoc::encryptLT(const QString& label, const QByteArray& secret, unsigned int kdf_iter) +{ + if( d->fileName.isEmpty()) { + WarningDialog::show(tr("Container is not open")); + return false; + } + return CDoc2::save(d->fileName, d->cdoc->files, label, secret, kdf_iter); +} + QString CryptoDoc::fileName() const { return d->fileName; } QList> CryptoDoc::keys() const @@ -481,14 +481,4 @@ bool CryptoDoc::saveCopy(const QString &filename) return QFile::copy(d->fileName, filename); } -bool -CryptoDoc::encryptLT(const QString& label, const QByteArray& secret, unsigned int kdf_iter) -{ - if( d->fileName.isEmpty()) { - WarningDialog::show(tr("Container is not open")); - return false; - } - return CDoc2::save(d->fileName, d->cdoc->files, label, secret, kdf_iter); -} - #include "CryptoDoc.moc" diff --git a/client/CryptoDoc.h b/client/CryptoDoc.h index 75d9b89b..ade62877 100644 --- a/client/CryptoDoc.h +++ b/client/CryptoDoc.h @@ -34,9 +34,9 @@ struct CKey public: enum Type { SYMMETRIC_KEY, + PUBLIC_KEY, CERTIFICATE, CDOC1, - PUBLICKEY, SERVER }; @@ -61,8 +61,9 @@ struct CKey // QByteArray key; bool isSymmetric() const { return type == Type::SYMMETRIC_KEY; } - bool isPKI() const { return (type == Type::CERTIFICATE) || (type == Type::CDOC1) || (type == Type::PUBLICKEY) || (type == Type::SERVER); } + bool isPKI() const { return (type == Type::CERTIFICATE) || (type == Type::CDOC1) || (type == Type::PUBLIC_KEY) || (type == Type::SERVER); } bool isCertificate() const { return (type == Type::CERTIFICATE) || (type == Type::CDOC1); } + bool isCDoc1() const { return type == Type::CDOC1; } bool isTheSameRecipient(const CKey &key) const; bool isTheSameRecipient(const QSslCertificate &cert) const; @@ -74,6 +75,8 @@ struct CKey }; // Symmetric key (plain or PBKDF) +// Usage: +// CDoc2:encrypt/decrypt struct CKeySymmetric : public CKey { QByteArray salt; @@ -85,7 +88,9 @@ struct CKeySymmetric : public CKey { CKeySymmetric(const QByteArray& _salt) : CKey(Type::SYMMETRIC_KEY), salt(_salt), kdf_iter(0) {} }; -// Public/private key +// Base PKI key +// Usage: +// CDoc2:encrypt struct CKeyPKI : public CKey { // Recipient's public key @@ -98,45 +103,52 @@ struct CKeyPKI : public CKey { }; -// Contains relevant encryption data +// Public key with additonal information +// Usage: +// CDoc2:encrypt - if recipient is specified by certificate +// CDoc1:encrypt struct CKeyCert : public CKeyPKI { QSslCertificate cert; + CKeyCert(const QSslCertificate &cert) : CKeyCert(CKey::Type::CERTIFICATE, cert) {}; + + void setCert(const QSslCertificate &c); + protected: CKeyCert(Type _type) : CKeyPKI(_type) {}; CKeyCert(Type _type, const QSslCertificate &cert); - - void setCert(const QSslCertificate &c); }; -// CDoc1 key +// CDoc1 decryption key (with additional information from file) +// Usage: +// CDoc1:decrypt -struct CKeyCD1 : public CKeyCert { +struct CKeyCDoc1 : public CKeyCert { QByteArray publicKey; QString concatDigest, method; QByteArray AlgorithmID, PartyUInfo, PartyVInfo; - static std::shared_ptr newEmpty(); - static std::shared_ptr fromCertificate(const QSslCertificate &cert); - - void setCert(const QSslCertificate &c) { CKeyCert::setCert(c); } - - static bool isCDoc1Key(const CKey& key) { return key.type == Type::CDOC1; } -protected: - CKeyCD1() : CKeyCert(Type::CDOC1) {}; - CKeyCD1(const QSslCertificate &cert) : CKeyCert(CKey::Type::CDOC1, cert) {}; + CKeyCDoc1() : CKeyCert(Type::CDOC1) {}; }; -struct CKeyPK : public CKeyPKI { +// CDoc2 PKI key with key material +// Usage: +// CDoc2: decrypt + +struct CKeyPublicKey : public CKeyPKI { // Either ECC public key or RSA encrypted kek QByteArray key_material; - CKeyPK() : CKeyPKI(Type::PUBLICKEY) {}; - CKeyPK(PKType _pk_type, QByteArray _rcpt_key) : CKeyPKI(Type::PUBLICKEY, _pk_type, _rcpt_key) {}; + CKeyPublicKey() : CKeyPKI(Type::PUBLIC_KEY) {}; + CKeyPublicKey(PKType _pk_type, QByteArray _rcpt_key) : CKeyPKI(Type::PUBLIC_KEY, _pk_type, _rcpt_key) {}; }; +// CDoc2 PKI key with server info +// Usage: +// CDoc2: decrypt + struct CKeyServer : public CKeyPKI { // Server info QString keyserver_id, transaction_id; @@ -184,7 +196,8 @@ class CryptoDoc final: public QObject bool decrypt(std::shared_ptr key, const QByteArray& secret); DocumentModel* documentModel() const; bool encrypt(const QString &filename = {}); - QString fileName() const; + bool encryptLT(const QString& label, const QByteArray& secret, unsigned int kdf_iter); + QString fileName() const; QList> keys() const; bool move(const QString &to); bool open( const QString &file ); @@ -192,8 +205,6 @@ class CryptoDoc final: public QObject bool saveCopy(const QString &filename); ria::qdigidoc4::ContainerState state() const; - bool encryptLT(const QString& label, const QByteArray& secret, unsigned int kdf_iter); - private: class Private; Private *d; diff --git a/client/MainWindow.cpp b/client/MainWindow.cpp index c0f99b61..e0e73301 100644 --- a/client/MainWindow.cpp +++ b/client/MainWindow.cpp @@ -161,7 +161,6 @@ MainWindow::MainWindow( QWidget *parent ) connect(ui->cryptoContainerPage, &ContainerPage::removed, this, &MainWindow::removeAddress); connect(ui->cryptoContainerPage, &ContainerPage::decryptReq, this, &MainWindow::decryptClicked); - connect(ui->cryptoContainerPage, &ContainerPage::encryptLTReq, this, &MainWindow::encryptLTClicked); connect(ui->accordion, &Accordion::changePin1Clicked, this, &MainWindow::changePin1Clicked); connect(ui->accordion, &Accordion::changePin2Clicked, this, &MainWindow::changePin2Clicked); @@ -271,7 +270,7 @@ void MainWindow::decrypt(std::shared_ptr key) if(!cryptoDoc) return; QByteArray secret; - if (key->type == CKey::Type::SYMMETRIC_KEY) { + if (key && (key->type == CKey::Type::SYMMETRIC_KEY)) { std::shared_ptr skey = std::static_pointer_cast(key); qDebug() << skey->label; PasswordDialog p; @@ -363,6 +362,43 @@ bool MainWindow::encrypt() return cryptoDoc->encrypt(); } +bool +MainWindow::encryptLT() +{ + qDebug() << "LT encrypt"; + if (!cryptoDoc) return false; + if(!FileDialog::fileIsWritable(cryptoDoc->fileName())) { + auto *dlg = new WarningDialog(tr("Cannot alter container %1. Save different location?") + .arg(FileDialog::normalized(cryptoDoc->fileName())), this); + dlg->addButton(WarningDialog::YES, QMessageBox::Yes); + if(dlg->exec() == QMessageBox::Yes) { + moveCryptoContainer(); + return encryptLT(); + } + return false; + } + PasswordDialog p; + p.setMode(PasswordDialog::Mode::ENCRYPT, PasswordDialog::Type::PASSWORD); + if(!p.exec()) return false; + QString label = p.label(); + QByteArray secret = p.secret(); + bool result; + if (p.type == PasswordDialog::Type::PASSWORD) { + qDebug() << "Secret:" << QString::fromUtf8(secret); + WaitDialogHolder waitDialog(this, tr("Encrypting")); + result = cryptoDoc->encryptLT(label, secret, 65536); + } else { + qDebug() << "Secret:" << QString::fromUtf8(secret.toHex()); + WaitDialogHolder waitDialog(this, tr("Encrypting")); + result = cryptoDoc->encryptLT(label, secret, 0); + } + if (result) { + auto *notification = new FadeInNotification(this, WHITE, MANTIS, 110); + notification->start( tr("Encryption succeeded!"), 750, 3000, 1200 ); + } + return result; +} + void MainWindow::mouseReleaseEvent(QMouseEvent *event) { emit clearPopups(); @@ -533,7 +569,7 @@ void MainWindow::convertToCDoc() auto cardData = qApp->signer()->tokenauth(); if(!cardData.cert().isNull()) - cryptoContainer->addKey(CKeyCD1::fromCertificate(cardData.cert())); + cryptoContainer->addKey(std::make_shared(cardData.cert())); resetCryptoDoc(cryptoContainer.release()); resetDigiDoc(nullptr, false); @@ -582,7 +618,15 @@ void MainWindow::onCryptoAction(int action, const QString &/*id*/, const QString notification->start( tr("Encryption succeeded!"), 750, 3000, 1200 ); } break; - case ContainerSaveAs: + case EncryptLT: + if(encryptLT()) + { + ui->cryptoContainerPage->transition(cryptoDoc, qApp->signer()->tokenauth().cert()); + auto *notification = new FadeInNotification(this, WHITE, MANTIS, 110); + notification->start( tr("Encryption succeeded!"), 750, 3000, 1200 ); + } + break; + case ContainerSaveAs: { if(!cryptoDoc) break; @@ -1182,21 +1226,3 @@ MainWindow::decryptClicked(std::shared_ptr key) decrypt(key); } -void -MainWindow::encryptLTClicked() -{ - qDebug() << "LT encrypt"; - if (!cryptoDoc) return; - PasswordDialog p; - p.setMode(PasswordDialog::Mode::ENCRYPT, PasswordDialog::Type::PASSWORD); - if(!p.exec()) return; - QString label = p.label(); - QByteArray secret = p.secret(); - if (p.type == PasswordDialog::Type::PASSWORD) { - qDebug() << "Secret:" << QString::fromUtf8(secret); - cryptoDoc->encryptLT(label, secret, 65536); - } else { - qDebug() << "Secret:" << QString::fromUtf8(secret.toHex()); - cryptoDoc->encryptLT(label, secret, 0); - } -} diff --git a/client/MainWindow.h b/client/MainWindow.h index 88b02bc1..29be196a 100644 --- a/client/MainWindow.h +++ b/client/MainWindow.h @@ -122,5 +122,5 @@ private Q_SLOTS: WarningList *warnings; void decryptClicked(std::shared_ptr key); - void encryptLTClicked(); + bool encryptLT(); }; diff --git a/client/common_enums.h b/client/common_enums.h index fdc828db..f7663060 100644 --- a/client/common_enums.h +++ b/client/common_enums.h @@ -54,7 +54,9 @@ enum Actions { SignatureMobile, SignatureSmartID, SignatureToken, - ClearSignatureWarning + ClearSignatureWarning, + + EncryptLT }; enum ItemType { diff --git a/client/dialogs/AddRecipients.cpp b/client/dialogs/AddRecipients.cpp index e44ed911..3f116c6e 100644 --- a/client/dialogs/AddRecipients.cpp +++ b/client/dialogs/AddRecipients.cpp @@ -65,7 +65,6 @@ AddRecipients::AddRecipients(ItemList* itemList, QWidget *parent) ui->fromCard->setFont(Styles::font(Styles::Condensed, 12)); ui->fromFile->setFont(Styles::font(Styles::Condensed, 12)); ui->fromHistory->setFont(Styles::font(Styles::Condensed, 12)); - ui->addKey->setFont(Styles::font(Styles::Condensed, 12)); ui->cancel->setFont(Styles::font(Styles::Condensed, 14)); ui->confirm->setFont(Styles::font(Styles::Condensed, 14)); @@ -93,7 +92,6 @@ AddRecipients::AddRecipients(ItemList* itemList, QWidget *parent) connect(ui->fromFile, &QPushButton::clicked, this, &AddRecipients::addRecipientFromFile); connect(ui->fromHistory, &QPushButton::clicked, this, &AddRecipients::addRecipientFromHistory); - connect(ui->addKey, &QPushButton::clicked, this, &AddRecipients::addRecipientKey); for(Item *item: itemList->items) addRecipientToRightPane((qobject_cast(item))->getKey(), false); @@ -115,7 +113,7 @@ void AddRecipients::addAllRecipientToRightPane() addRecipientToRightPane(value); std::shared_ptr key = value->getKey(); if (key->type == CKey::Type::CDOC1) { - std::shared_ptr kd = std::static_pointer_cast(key); + std::shared_ptr kd = std::static_pointer_cast(key); history.append(kd->cert); } } @@ -171,18 +169,13 @@ void AddRecipients::addRecipientFromHistory() dlg->open(); } -void AddRecipients::addRecipientKey() -{ - qDebug() << "Adding key to recipients list"; -} - AddressItem * AddRecipients::addRecipientToLeftPane(const QSslCertificate& cert) { AddressItem *leftItem = leftList.value(cert); if(leftItem) return leftItem; - leftItem = new AddressItem(CKeyCD1::fromCertificate(cert), ui->leftPane); + leftItem = new AddressItem(std::make_shared(cert), ui->leftPane); leftList.insert(cert, leftItem); ui->leftPane->addWidget(leftItem); @@ -215,7 +208,7 @@ bool AddRecipients::addRecipientToRightPane(std::shared_ptr key, bool upda if(update) { if (key->type == CKey::Type::CDOC1) { - std::shared_ptr kd = std::static_pointer_cast(key); + std::shared_ptr kd = std::static_pointer_cast(key); if(auto expiryDate = kd->cert.expiryDate(); expiryDate <= QDateTime::currentDateTime()) { if(Settings::CDOC2_DEFAULT && Settings::CDOC2_USE_KEYSERVER) @@ -255,7 +248,7 @@ bool AddRecipients::addRecipientToRightPane(std::shared_ptr key, bool upda ui->rightPane->addWidget(rightItem); ui->confirm->setDisabled(rightList.isEmpty()); if (key->type == CKey::Type::CDOC1) { - std::shared_ptr kd = std::static_pointer_cast(key); + std::shared_ptr kd = std::static_pointer_cast(key); historyCertData.addAndSave({kd->cert}); } return true; @@ -313,7 +306,7 @@ void AddRecipients::removeRecipientFromRightPane(Item *toRemove) auto *rightItem = qobject_cast(toRemove); std::shared_ptr key = rightItem->getKey(); if (key->type == CKey::Type::CDOC1) { - std::shared_ptr kd = std::static_pointer_cast(key); + std::shared_ptr kd = std::static_pointer_cast(key); if(auto it = leftList.find(kd->cert); it != leftList.end()) { it.value()->setDisabled(false); diff --git a/client/dialogs/AddRecipients.h b/client/dialogs/AddRecipients.h index 29516adc..da597820 100644 --- a/client/dialogs/AddRecipients.h +++ b/client/dialogs/AddRecipients.h @@ -49,7 +49,6 @@ class AddRecipients final : public QDialog void addRecipientFromCard(); void addRecipientFromFile(); void addRecipientFromHistory(); - void addRecipientKey(); AddressItem * addRecipientToLeftPane(const QSslCertificate& cert); bool addRecipientToRightPane(std::shared_ptr key, bool update = true); void addRecipientToRightPane(AddressItem *leftItem, bool update = true); diff --git a/client/dialogs/AddRecipients.ui b/client/dialogs/AddRecipients.ui index d833abec..66fa94fe 100644 --- a/client/dialogs/AddRecipients.ui +++ b/client/dialogs/AddRecipients.ui @@ -97,7 +97,7 @@ QPushButton:disabled { color: #727679; }
- + 15 @@ -179,13 +179,6 @@ QPushButton:disabled { - - - - KEY - - - diff --git a/client/dialogs/KeyDialog.cpp b/client/dialogs/KeyDialog.cpp index c8a113dd..fe4351cc 100644 --- a/client/dialogs/KeyDialog.cpp +++ b/client/dialogs/KeyDialog.cpp @@ -50,7 +50,7 @@ KeyDialog::KeyDialog( const CKey &k, QWidget *parent ) connect(d->close, &QPushButton::clicked, this, &KeyDialog::accept); if (k.type == CKey::CDOC1) { - const CKeyCD1& kd = static_cast(k); + const CKeyCDoc1& kd = static_cast(k); connect(d->showCert, &QPushButton::clicked, this, [this, cert=kd.cert] { CertificateDetails::showCertificate(cert, this); }); @@ -70,7 +70,7 @@ KeyDialog::KeyDialog( const CKey &k, QWidget *parent ) bool adjust_size = false; if (k.type == CKey::Type::CDOC1) { - const CKeyCD1& cd1key = static_cast(k); + const CKeyCDoc1& cd1key = static_cast(k); addItem(tr("Recipient"), cd1key.label); addItem(tr("Crypto method"), cd1key.method); //7addItem(tr("Agreement method"), cd1key.agreement); diff --git a/client/dialogs/PasswordDialog.cpp b/client/dialogs/PasswordDialog.cpp index b54f11be..c44516a0 100644 --- a/client/dialogs/PasswordDialog.cpp +++ b/client/dialogs/PasswordDialog.cpp @@ -7,9 +7,8 @@ PasswordDialog::PasswordDialog(QWidget *parent) , ui(new Ui::PasswordDialog) { ui->setupUi(this); - connect(ui->radioPassword, &QRadioButton::toggled, this, &PasswordDialog::passwordSelected); - connect(ui->radioKey, &QRadioButton::toggled, this, &PasswordDialog::symmetricKeySelected); - connect(ui->buttonGenKey, &QPushButton::clicked, this, &PasswordDialog::genKeyClicked); + connect(ui->generateKey, &QPushButton::clicked, this, &PasswordDialog::genKeyClicked); + connect(ui->typeSelector, &QTabWidget::currentChanged, this, &PasswordDialog::typeChanged); } PasswordDialog::~PasswordDialog() @@ -37,22 +36,15 @@ PasswordDialog::secret() const if (type == Type::PASSWORD) { return ui->passwordLine->text().toUtf8(); } else { - QString hex = ui->passwordLine->text(); + QString hex = ui->keyEdit->toPlainText(); return QByteArray::fromHex(hex.toUtf8()); } } void -PasswordDialog::passwordSelected(bool checked) +PasswordDialog::typeChanged(int index) { - Type new_type = (checked) ? Type::PASSWORD : Type::KEY; - if (new_type != type) setMode(mode, new_type); -} - -void -PasswordDialog::symmetricKeySelected(bool checked) -{ - Type new_type = (checked) ? Type::KEY : Type::PASSWORD; + Type new_type = static_cast(index); if (new_type != type) setMode(mode, new_type); } @@ -60,40 +52,41 @@ void PasswordDialog::genKeyClicked() { QByteArray key = Crypto::random(); - ui->passwordLine->setText(key.toHex()); + ui->keyEdit->clear(); + ui->keyEdit->appendPlainText(key.toHex()); } void PasswordDialog::updateUI() { + ui->typeSelector->setCurrentIndex(type); if (mode == Mode::DECRYPT) { - ui->radioBox->hide(); ui->labelLabel->hide(); ui->labelLine->hide(); - ui->buttonGenKey->hide(); if (type == Type::PASSWORD) { - ui->radioPassword->setChecked(true); + ui->typeSelector->setTabVisible(Type::PASSWORD, true); + ui->typeSelector->setTabVisible(Type::KEY, false); ui->passwordLabel->setText("Enter password to decrypt the document"); ui->passwordLine->setEchoMode(QLineEdit::EchoMode::Password); } else { - ui->radioKey->setChecked(true); - ui->passwordLabel->setText("Enter key to decrypt the document"); - ui->passwordLine->setEchoMode(QLineEdit::EchoMode::Normal); + ui->typeSelector->setTabVisible(Type::PASSWORD, false); + ui->typeSelector->setTabVisible(Type::KEY, true); + ui->keyLabel->setText("Enter key to decrypt the document"); + ui->generateKey->hide(); } } else { - ui->radioBox->show(); ui->labelLabel->show(); ui->labelLine->show(); if (type == Type::PASSWORD) { - ui->radioPassword->setChecked(true); + ui->typeSelector->setTabVisible(Type::PASSWORD, true); + ui->typeSelector->setTabVisible(Type::KEY, true); ui->passwordLabel->setText("Enter a password to encrypt the document"); ui->passwordLine->setEchoMode(QLineEdit::EchoMode::Password); - ui->buttonGenKey->hide(); } else { - ui->radioKey->setChecked(true); - ui->passwordLabel->setText("Enter a key to encrypt the document"); - ui->passwordLine->setEchoMode(QLineEdit::EchoMode::Normal); - ui->buttonGenKey->show(); + ui->typeSelector->setTabVisible(Type::PASSWORD, true); + ui->typeSelector->setTabVisible(Type::KEY, true); + ui->keyLabel->setText("Enter a key to encrypt the document"); + ui->generateKey->show(); } } } diff --git a/client/dialogs/PasswordDialog.h b/client/dialogs/PasswordDialog.h index e4b317da..22f1ed9e 100644 --- a/client/dialogs/PasswordDialog.h +++ b/client/dialogs/PasswordDialog.h @@ -35,8 +35,7 @@ class PasswordDialog : public QDialog private: Ui::PasswordDialog *ui; - void passwordSelected(bool checked); - void symmetricKeySelected(bool checked); + void typeChanged(int index); void genKeyClicked(); void updateUI(); }; diff --git a/client/dialogs/PasswordDialog.ui b/client/dialogs/PasswordDialog.ui index a3b1d7f1..916605da 100644 --- a/client/dialogs/PasswordDialog.ui +++ b/client/dialogs/PasswordDialog.ui @@ -7,39 +7,13 @@ 0 0 400 - 253 + 322 Dialog - - - - - - - - - - Password - - - true - - - - - - - Symmetric key - - - - - - @@ -54,27 +28,84 @@ - - - Enter the password to decrypt document - - - Qt::AlignCenter - - - - - - - QLineEdit::Password - - - - - - - Generate Key + + + 1 + + + Password + + + + + + Password + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + QLineEdit::Password + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + Symmetric key + + + + + + Symmetric key + + + + + + + + + + Generate Key + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + diff --git a/client/widgets/AddressItem.cpp b/client/widgets/AddressItem.cpp index e7bab427..5280a358 100644 --- a/client/widgets/AddressItem.cpp +++ b/client/widgets/AddressItem.cpp @@ -37,11 +37,11 @@ class AddressItem::Private: public Ui::AddressItem bool yourself = false; }; -AddressItem::AddressItem(std::shared_ptr k, QWidget *parent, bool showIcon) +AddressItem::AddressItem(std::shared_ptr key, QWidget *parent, bool showIcon) : Item(parent) , ui(new Private) { - ui->key = k; + ui->key = key; ui->setupUi(this); if(showIcon) ui->icon->load(QStringLiteral(":/images/icon_Krypto_small.svg")); @@ -56,23 +56,29 @@ AddressItem::AddressItem(std::shared_ptr k, QWidget *parent, bool showIcon ui->remove->init(LabelButton::White); connect(ui->add, &QToolButton::clicked, this, [this]{ emit add(this);}); connect(ui->remove, &LabelButton::clicked, this, [this]{ emit remove(this);}); - connect(ui->decrypt, &QToolButton::clicked, this, [this]{ emit decrypt(ui->key);}); + + if (key->isSymmetric()) { + ui->decrypt->show(); + connect(ui->decrypt, &QToolButton::clicked, this, [this]{ emit decrypt(ui->key);}); + } else { + ui->decrypt->hide(); + } ui->add->setFont(Styles::font(Styles::Condensed, 12)); ui->added->setFont(ui->add->font()); - if (CKeyCD1::isCDoc1Key(*ui->key)) { - std::shared_ptr key = std::static_pointer_cast(ui->key); + if (ui->key->isCDoc1()) { + std::shared_ptr key = std::static_pointer_cast(ui->key); ui->code = SslCertificate(key->cert).personalCode().toHtmlEscaped(); ui->label = (!key->cert.subjectInfo("GN").isEmpty() && !key->cert.subjectInfo("SN").isEmpty() ? key->cert.subjectInfo("GN").join(' ') + " " + key->cert.subjectInfo("SN").join(' ') : key->cert.subjectInfo("CN").join(' ')).toHtmlEscaped(); } else { ui->code = {}; - ui->label = k->label; + ui->label = key->label; } - if(ui->label.isEmpty() && ui->key->type == CKey::PUBLICKEY) { - const CKeyPK& pk = static_cast(*ui->key); + if(ui->label.isEmpty() && ui->key->type == CKey::PUBLIC_KEY) { + const CKeyPublicKey& pk = static_cast(*ui->key); ui->label = pk.key_material; } setIdType(); @@ -118,7 +124,7 @@ void AddressItem::idChanged(std::shared_ptr key) void AddressItem::idChanged(const SslCertificate &cert) { - idChanged(CKeyCD1::fromCertificate(cert)); + idChanged(std::make_shared(cert)); } void AddressItem::initTabOrder(QWidget *item) diff --git a/client/widgets/AddressItem.ui b/client/widgets/AddressItem.ui index beb7be7e..be7d9adf 100644 --- a/client/widgets/AddressItem.ui +++ b/client/widgets/AddressItem.ui @@ -54,7 +54,11 @@ QToolButton:disabled { color: #727679; } #decrypt { -background-color: blue; +color: white; +background-color: #006EB5; +} +#decrypt:hover { +background-color: #00AEE5; } @@ -237,17 +241,17 @@ background-color: blue; - - LabelButton - QToolButton -
widgets/LabelButton.h
-
QSvgWidget QWidget -
QtSvg/QSvgWidget
+
QSvgWidget
1
+ + LabelButton + QToolButton +
widgets/LabelButton.h
+
diff --git a/client/widgets/ContainerPage.cpp b/client/widgets/ContainerPage.cpp index 213b2724..0bfad21a 100644 --- a/client/widgets/ContainerPage.cpp +++ b/client/widgets/ContainerPage.cpp @@ -86,7 +86,7 @@ ContainerPage::ContainerPage(QWidget *parent) connect(ui->containerFile, &QLabel::linkActivated, this, [this](const QString &link) { emit action(Actions::ContainerNavigate, link); }); - connect(ui->encryptLT, &LabelButton::clicked, this, [this]{emit encryptLTReq();}); + connect(ui->encryptLT, &LabelButton::clicked, this, [this]{emit action(Actions::EncryptLT);}); ui->summary->setVisible(Settings::SHOW_PRINT_SUMMARY); } @@ -456,8 +456,8 @@ void ContainerPage::updatePanes(ContainerState state) ui->leftPane->init(fileName, QT_TRANSLATE_NOOP("ItemList", "Encrypted files")); showRightPane(ItemAddress, QT_TRANSLATE_NOOP("ItemList", "Recipients")); updateDecryptionButton(); - setButtonsVisible({ ui->save, ui->summary }, false); - setButtonsVisible({ ui->cancel, ui->convert, ui->saveAs, ui->email, ui->encryptLT }, true); + setButtonsVisible({ ui->save, ui->summary, ui->encryptLT }, false); + setButtonsVisible({ ui->cancel, ui->convert, ui->saveAs, ui->email }, true); break; default: // Uninitialized cannot be shown on container page diff --git a/client/widgets/ContainerPage.h b/client/widgets/ContainerPage.h index 22299e17..ad02417d 100644 --- a/client/widgets/ContainerPage.h +++ b/client/widgets/ContainerPage.h @@ -64,7 +64,6 @@ class ContainerPage final : public QWidget void warning(const WarningText &warningText); void decryptReq(std::shared_ptr key); - void encryptLTReq(); private: void addressSearch(); From de64734520492c9e4b71c3e0c880d9bc45fc5e7c Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Wed, 15 May 2024 00:23:04 +0300 Subject: [PATCH 05/22] Cleanups Signed-off-by: Lauris Kaplinski (+1 squashed commit) Squashed commits: [907bf68] Fixes and cleanups for LT crypto Signed-off-by: Lauris Kaplinski --- client/CDoc1.cpp | 33 ++-- client/CDoc2.cpp | 283 ++++++++++++------------------ client/CDoc2.h | 7 +- client/CryptoDoc.cpp | 88 +++++----- client/CryptoDoc.h | 59 ++++--- client/MainWindow.cpp | 62 +++---- client/MainWindow.h | 5 +- client/common_enums.h | 4 +- client/dialogs/AddRecipients.cpp | 17 +- client/dialogs/AddRecipients.h | 1 - client/dialogs/AddRecipients.ui | 9 +- client/dialogs/KeyDialog.cpp | 4 +- client/dialogs/PasswordDialog.cpp | 91 +++++++--- client/dialogs/PasswordDialog.h | 7 +- client/dialogs/PasswordDialog.ui | 139 ++++++++++----- client/widgets/AddressItem.cpp | 24 ++- client/widgets/AddressItem.ui | 18 +- client/widgets/ContainerPage.cpp | 51 ++++-- client/widgets/ContainerPage.h | 8 +- client/widgets/ContainerPage.ui | 40 ----- client/widgets/MainAction.cpp | 3 +- 21 files changed, 489 insertions(+), 464 deletions(-) diff --git a/client/CDoc1.cpp b/client/CDoc1.cpp index 65b4b106..1a98d22a 100644 --- a/client/CDoc1.cpp +++ b/client/CDoc1.cpp @@ -117,7 +117,7 @@ CDoc1::CDoc1(const QString &path) if(xml.name() != QLatin1String("EncryptedKey")) return; - std::shared_ptr key = CKeyCD1::newEmpty(); + std::shared_ptr key = std::make_shared(); // Id is never used //key->id = xml.attributes().value(QLatin1String("Id")).toString(); key->label = xml.attributes().value(QLatin1String("Recipient")).toString(); @@ -298,7 +298,7 @@ CKey::DecryptionStatus CDoc1::canDecrypt(const QSslCertificate &cert) const { std::shared_ptr key = getDecryptionKey(cert); - if (!key) return CKey::DecryptionStatus::CAN_DECRYPT; + if (key) return CKey::DecryptionStatus::CAN_DECRYPT; return CKey::DecryptionStatus::CANNOT_DECRYPT; } @@ -307,7 +307,7 @@ std::shared_ptr CDoc1::getDecryptionKey(const QSslCertificate &cert) const for(std::shared_ptr key: qAsConst(keys)) { if (key->type != CKey::Type::CDOC1) continue; - std::shared_ptr k = std::static_pointer_cast(key); + std::shared_ptr k = std::static_pointer_cast(key); if(!ENC_MTH.contains(method) || k->cert != cert || k->encrypted_fmk.isEmpty()) @@ -468,20 +468,19 @@ bool CDoc1::save(const QString &path) writeElement(w, DS, QStringLiteral("KeyInfo"), [&]{ for(std::shared_ptr key: qAsConst(keys)) { - if (key->type != CKey::Type::CDOC1) { - return; - } - std::shared_ptr k = std::static_pointer_cast(key); + // Only certificate-based keys can be used in CDoc1 + if (key->type != CKey::Type::CERTIFICATE) return; + std::shared_ptr ckey = std::static_pointer_cast(key); writeElement(w, DENC, QStringLiteral("EncryptedKey"), [&]{ // Id is never used //if(!k->id.isEmpty()) // w.writeAttribute(QStringLiteral("Id"), k->id); - if(!k->label.isEmpty()) - w.writeAttribute(QStringLiteral("Recipient"), k->label); + if(!ckey->label.isEmpty()) + w.writeAttribute(QStringLiteral("Recipient"), ckey->label); QByteArray cipher; - if(k->pk_type == CKey::PKType::RSA) + if(ckey->pk_type == CKey::PKType::RSA) { - cipher = Crypto::encrypt(X509_get0_pubkey((const X509*)k->cert.handle()), RSA_PKCS1_PADDING, transportKey); + cipher = Crypto::encrypt(X509_get0_pubkey((const X509*)ckey->cert.handle()), RSA_PKCS1_PADDING, transportKey); if(cipher.isEmpty()) return; writeElement(w, DENC, QStringLiteral("EncryptionMethod"), { @@ -492,13 +491,13 @@ bool CDoc1::save(const QString &path) //if(!k->name.isEmpty()) // w.writeTextElement(DS, QStringLiteral("KeyName"), k->name); writeElement(w, DS, QStringLiteral("X509Data"), [&]{ - writeBase64Element(w, DS, QStringLiteral("X509Certificate"), k->cert.toDer()); + writeBase64Element(w, DS, QStringLiteral("X509Certificate"), ckey->cert.toDer()); }); }); } else { - EVP_PKEY *peerPKey = X509_get0_pubkey((const X509*)k->cert.handle()); + EVP_PKEY *peerPKey = X509_get0_pubkey((const X509*)ckey->cert.handle()); auto priv = Crypto::genECKey(peerPKey); QByteArray sharedSecret = Crypto::derive(priv.get(), peerPKey); if(sharedSecret.isEmpty()) @@ -515,7 +514,7 @@ bool CDoc1::save(const QString &path) default: concatDigest = SHA512_MTH; break; } QByteArray encryptionKey = Crypto::concatKDF(SHA_MTH[concatDigest], KWAES_SIZE[encryptionMethod], - sharedSecret, props.value(QStringLiteral("DocumentFormat")).toUtf8() + SsDer + k->cert.toDer()); + sharedSecret, props.value(QStringLiteral("DocumentFormat")).toUtf8() + SsDer + ckey->cert.toDer()); #ifndef NDEBUG qDebug() << "ENC Ss" << SsDer.toHex(); qDebug() << "ENC Ksr" << sharedSecret.toHex(); @@ -540,7 +539,7 @@ bool CDoc1::save(const QString &path) writeElement(w, XENC11, QStringLiteral("ConcatKDFParams"), { {QStringLiteral("AlgorithmID"), QStringLiteral("00") + props.value(QStringLiteral("DocumentFormat")).toUtf8().toHex()}, {QStringLiteral("PartyUInfo"), QStringLiteral("00") + SsDer.toHex()}, - {QStringLiteral("PartyVInfo"), QStringLiteral("00") + k->cert.toDer().toHex()}, + {QStringLiteral("PartyVInfo"), QStringLiteral("00") + ckey->cert.toDer().toHex()}, }, [&]{ writeElement(w, DS, QStringLiteral("DigestMethod"), { {QStringLiteral("Algorithm"), concatDigest}, @@ -560,7 +559,7 @@ bool CDoc1::save(const QString &path) }); writeElement(w, DENC, QStringLiteral("RecipientKeyInfo"), [&]{ writeElement(w, DS, QStringLiteral("X509Data"), [&]{ - writeBase64Element(w, DS, QStringLiteral("X509Certificate"), k->cert.toDer()); + writeBase64Element(w, DS, QStringLiteral("X509Certificate"), ckey->cert.toDer()); }); }); }); @@ -596,7 +595,7 @@ QByteArray CDoc1::getFMK(const CKey &key, const QByteArray& secret) setLastError(QStringLiteral("Not a CDoc1 key")); return {}; } - const CKeyCD1& ckey = static_cast(key); + const CKeyCDoc1& ckey = static_cast(key); setLastError({}); QByteArray decryptedKey = qApp->signer()->decrypt([&ckey](QCryptoBackend *backend) { if(ckey.pk_type == CKey::PKType::RSA) diff --git a/client/CDoc2.cpp b/client/CDoc2.cpp index e4629b6b..9cef588b 100644 --- a/client/CDoc2.cpp +++ b/client/CDoc2.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include @@ -53,12 +54,12 @@ using cdoc20::recipients::EccKeyDetails; using cdoc20::recipients::RsaKeyDetails; const QByteArray CDoc2::LABEL = "CDOC\x02"; -const QByteArray CDoc2::CEK = "CDOC2cek"; -const QByteArray CDoc2::HMAC = "CDOC2hmac"; -const QByteArray CDoc2::KEK = "CDOC2kek"; -const QByteArray CDoc2::KEKPREMASTER = "CDOC2kekpremaster"; -const QByteArray CDoc2::PAYLOAD = "CDOC2payload"; -const QByteArray CDoc2::SALT = "CDOC2salt"; +const QByteArray CDoc2::CEK = "CDOC20cek"; +const QByteArray CDoc2::HMAC = "CDOC20hmac"; +const QByteArray CDoc2::KEK = "CDOC20kek"; +const QByteArray CDoc2::KEKPREMASTER = "CDOC20kekpremaster"; +const QByteArray CDoc2::PAYLOAD = "CDOC20payload"; +const QByteArray CDoc2::SALT = "CDOC20salt"; namespace cdoc20 { bool checkConnection() { @@ -457,7 +458,7 @@ CDoc2::CDoc2(const QString &_path) continue; } auto fillRecipientPK = [&] (CKey::PKType pk_type, auto key) { - std::shared_ptr k(new CKeyPK(pk_type, toByteArray(key->recipient_public_key()))); + std::shared_ptr k(new CKeyPublicKey(pk_type, toByteArray(key->recipient_public_key()))); k->label = toString(recipient->key_label()); k->encrypted_fmk = toByteArray(recipient->encrypted_fmk()); return k; @@ -472,15 +473,16 @@ CDoc2::CDoc2(const QString &_path) qWarning() << "Unsupported ECC curve: skipping"; continue; } - std::shared_ptr k = fillRecipientPK(CKey::PKType::ECC, key); + std::shared_ptr k = fillRecipientPK(CKey::PKType::ECC, key); k->key_material = toByteArray(key->sender_public_key()); + qDebug() << "Load PK:" << k->rcpt_key.toHex(); keys.append(k); } break; case Capsule::RSAPublicKeyCapsule: if(const auto *key = recipient->capsule_as_RSAPublicKeyCapsule()) { - std::shared_ptr k = fillRecipientPK(CKey::PKType::RSA, key); + std::shared_ptr k = fillRecipientPK(CKey::PKType::RSA, key); k->key_material = toByteArray(key->encrypted_kek()); keys.append(k); } @@ -615,47 +617,6 @@ bool CDoc2::decryptPayload(const QByteArray &fmk) return !files.empty(); } -class TmpFile : public QFile { -public: - /* Existing file */ - std::filesystem::path current_name; - /* Temporary file */ - std::filesystem::path tmp_name; - - TmpFile(const QString& name) : current_name(name.toStdString()) {} - - ~TmpFile() { - /* Close it to avoid overloaded close() to be called */ - if (isOpen()) QFile::close(); - } - - /* "Open" the file - by actually opening temporary in the same directory */ - bool open() { - if (isOpen()) return false; - tmp_name = current_name; - tmp_name.replace_filename("CDOCXXXXXX.tmp"); - /* C++ is a mess here so we go the easy way */ - char *tmp_str = ::strdup(tmp_name.c_str()); - int tmp_handle = ::mkstemps(tmp_str, 4); - tmp_name = tmp_str; - ::free (tmp_str); - if (!QFile::open(tmp_handle, QFile::WriteOnly, QFileDevice::AutoCloseHandle)) { - /* Have to close handle directly */ - ::close(tmp_handle); - ::unlink(tmp_name.c_str()); - return false; - } - return true; - } - - void close() { - if (!isOpen()) return; - QFile::close(); - /* Atomic rename */ - ::rename(tmp_name.c_str(), current_name.c_str()); - } -}; - bool CDoc2::save(const QString &_path) { setLastError({}); @@ -680,15 +641,19 @@ bool CDoc2::save(const QString &_path) return builder.CreateString(utf8.data(), size_t(utf8.length())); }; - auto sendToServer = [this](std::shared_ptr key, const QByteArray &recipient_id, const QByteArray &key_material, QLatin1String type) { - key->keyserver_id = Settings::CDOC2_DEFAULT_KEYSERVER; - if(key->keyserver_id.isEmpty()) - return setLastError(QStringLiteral("keyserver_id cannot be empty")); - QNetworkRequest req = cdoc20::req(key->keyserver_id); - if(req.url().isEmpty()) - return setLastError(QStringLiteral("No valid config found for keyserver_id: %1").arg(key->keyserver_id)); - if(!cdoc20::checkConnection()) - return false; + auto sendToServer = [this] (const QString& keyserver_id, const QByteArray &recipient_id, const QByteArray &key_material, QLatin1String type) -> QString { + if(keyserver_id.isEmpty()) { + setLastError(QStringLiteral("keyserver_id cannot be empty")); + return {}; + } + QNetworkRequest req = cdoc20::req(keyserver_id); + if(req.url().isEmpty()) { + setLastError(QStringLiteral("No valid config found for keyserver_id: %1").arg(keyserver_id)); + return {}; + } + if(!cdoc20::checkConnection()) { + return {}; + } QScopedPointer nam(CheckConnection::setupNAM(req, Settings::CDOC2_POST_CERT)); QEventLoop e; QNetworkReply *reply = nam->post(req, QJsonDocument({ @@ -698,114 +663,98 @@ bool CDoc2::save(const QString &_path) }).toJson()); connect(reply, &QNetworkReply::finished, &e, &QEventLoop::quit); e.exec(); + QString transaction_id; if(reply->error() == QNetworkReply::NoError && - reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 201) - key->transaction_id = QString::fromLatin1(reply->rawHeader("Location")).remove(QLatin1String("/key-capsules/")); - else - return setLastError(reply->errorString()); - if(key->transaction_id.isEmpty()) - return setLastError(QStringLiteral("Failed to post key capsule")); - return true; + reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 201) { + transaction_id = QString::fromLatin1(reply->rawHeader("Location")).remove(QLatin1String("/key-capsules/")); + } else { + setLastError(reply->errorString()); + return {}; + } + if(transaction_id.isEmpty()) + setLastError(QStringLiteral("Failed to post key capsule")); + return transaction_id; }; for(std::shared_ptr key: keys) { - if (key->isSymmetric()) { - const CKeySymmetric &sk = static_cast(*key); - QByteArray symmetric_key; - if (sk.kdf_iter > 0) { - // PasswordSalt_i = CSRNG() - // PasswordKeyMaterial_i = PBKDF2(Password_i, PasswordSalt_i) - } else { - //symmetric_key = - } - // KeyMaterialSalt_i = CSRNG() - // KEK_i = HKDF(KeyMaterialSalt_i, PasswordKeyMaterial_i) - // Capsule_i = {KeyMaterialSalt_i, PasswordSalt_i} - // EncryptedFMK_i = XOR(FMK, KEK_i) - - } else if (!key->isPKI()) { - const CKeyPKI& pki = static_cast(*key); - if(pki.pk_type == CKey::PKType::RSA) { - QByteArray kek = Crypto::random(fmk.size()); - QByteArray xor_key = Crypto::xor_data(fmk, kek); - auto publicKey = Crypto::fromRSAPublicKeyDer(pki.rcpt_key); - if(!publicKey) - return false; - QByteArray encrytpedKek = Crypto::encrypt(publicKey.get(), RSA_PKCS1_OAEP_PADDING, kek); + if (!key->isPKI()) { + return setLastError(QStringLiteral("Invalid key type")); + } + const CKeyPKI& pki = static_cast(*key); + if(pki.pk_type == CKey::PKType::RSA) { + QByteArray kek = Crypto::random(fmk.size()); + QByteArray xor_key = Crypto::xor_data(fmk, kek); + auto publicKey = Crypto::fromRSAPublicKeyDer(pki.rcpt_key); + if(!publicKey) + return false; + QByteArray encrytpedKek = Crypto::encrypt(publicKey.get(), RSA_PKCS1_OAEP_PADDING, kek); #ifndef NDEBUG - qDebug() << "publicKeyDer" << pki.rcpt_key.toHex(); - qDebug() << "kek" << kek.toHex(); - qDebug() << "xor" << xor_key.toHex(); - qDebug() << "encrytpedKek" << encrytpedKek.toHex(); + qDebug() << "publicKeyDer" << pki.rcpt_key.toHex(); + qDebug() << "kek" << kek.toHex(); + qDebug() << "xor" << xor_key.toHex(); + qDebug() << "encrytpedKek" << encrytpedKek.toHex(); #endif - if(!Settings::CDOC2_USE_KEYSERVER) { - auto rsaPublicKey = cdoc20::recipients::CreateRSAPublicKeyCapsule(builder, - toVector(pki.rcpt_key), toVector(encrytpedKek)); - auto offs = cdoc20::header::CreateRecipientRecord(builder, - cdoc20::recipients::Capsule::RSAPublicKeyCapsule, rsaPublicKey.Union(), - toString(pki.label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); - recipients.push_back(offs); - } else { - if (key->type != CKey::Type::SERVER) { - return setLastError(QStringLiteral("Not a server key")); - } - const std::shared_ptr skey = std::static_pointer_cast(key); - if(!sendToServer(skey, skey->rcpt_key, encrytpedKek, QLatin1String("rsa"))) - return false; - auto rsaKeyServer = cdoc20::recipients::CreateRsaKeyDetails(builder, toVector(skey->rcpt_key)); - auto keyServer = cdoc20::recipients::CreateKeyServerCapsule(builder, - cdoc20::recipients::KeyDetailsUnion::RsaKeyDetails, - rsaKeyServer.Union(), toString(skey->keyserver_id), toString(skey->transaction_id)); - auto offs = cdoc20::header::CreateRecipientRecord(builder, - cdoc20::recipients::Capsule::KeyServerCapsule, keyServer.Union(), - toString(pki.label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); - recipients.push_back(offs); - } + if(!Settings::CDOC2_USE_KEYSERVER) { + auto rsaPublicKey = cdoc20::recipients::CreateRSAPublicKeyCapsule(builder, + toVector(pki.rcpt_key), toVector(encrytpedKek)); + auto offs = cdoc20::header::CreateRecipientRecord(builder, + cdoc20::recipients::Capsule::RSAPublicKeyCapsule, rsaPublicKey.Union(), + toString(pki.label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); + recipients.push_back(offs); } else { - auto publicKey = Crypto::fromECPublicKeyDer(pki.rcpt_key, NID_secp384r1); - if(!publicKey) return false; - auto ephKey = Crypto::genECKey(publicKey.get()); - QByteArray sharedSecret = Crypto::derive(ephKey.get(), publicKey.get()); - QByteArray ephPublicKeyDer = Crypto::toPublicKeyDer(ephKey.get()); - QByteArray kekPm = Crypto::extract(sharedSecret, KEKPREMASTER); - QByteArray info = KEK + cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) + pki.rcpt_key + ephPublicKeyDer; - QByteArray kek = Crypto::expand(kekPm, info, fmk.size()); - QByteArray xor_key = Crypto::xor_data(fmk, kek); + QString keyserver_id = Settings::CDOC2_DEFAULT_KEYSERVER; + QString transaction_id = sendToServer(keyserver_id, pki.rcpt_key, encrytpedKek, QLatin1String("rsa")); + if (transaction_id.isEmpty()) return false; + auto rsaKeyServer = cdoc20::recipients::CreateRsaKeyDetails(builder, toVector(pki.rcpt_key)); + auto keyServer = cdoc20::recipients::CreateKeyServerCapsule(builder, + cdoc20::recipients::KeyDetailsUnion::RsaKeyDetails, + rsaKeyServer.Union(), toString(keyserver_id), toString(transaction_id)); + auto offs = cdoc20::header::CreateRecipientRecord(builder, + cdoc20::recipients::Capsule::KeyServerCapsule, keyServer.Union(), + toString(pki.label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); + recipients.push_back(offs); + } + } else { + auto publicKey = Crypto::fromECPublicKeyDer(pki.rcpt_key, NID_secp384r1); + if(!publicKey) return false; + auto ephKey = Crypto::genECKey(publicKey.get()); + QByteArray sharedSecret = Crypto::derive(ephKey.get(), publicKey.get()); + QByteArray ephPublicKeyDer = Crypto::toPublicKeyDer(ephKey.get()); + QByteArray kekPm = Crypto::extract(sharedSecret, KEKPREMASTER); + QByteArray info = KEK + cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) + pki.rcpt_key + ephPublicKeyDer; + QByteArray kek = Crypto::expand(kekPm, info, fmk.size()); + QByteArray xor_key = Crypto::xor_data(fmk, kek); #ifndef NDEBUG - qDebug() << "publicKeyDer" << pki.rcpt_key.toHex(); - qDebug() << "ephPublicKeyDer" << ephPublicKeyDer.toHex(); - qDebug() << "sharedSecret" << sharedSecret.toHex(); - qDebug() << "kekPm" << kekPm.toHex(); - qDebug() << "kek" << kek.toHex(); - qDebug() << "xor" << xor_key.toHex(); + qDebug() << "publicKeyDer" << pki.rcpt_key.toHex(); + qDebug() << "ephPublicKeyDer" << ephPublicKeyDer.toHex(); + qDebug() << "sharedSecret" << sharedSecret.toHex(); + qDebug() << "kekPm" << kekPm.toHex(); + qDebug() << "kek" << kek.toHex(); + qDebug() << "xor" << xor_key.toHex(); #endif - if(!Settings::CDOC2_USE_KEYSERVER) { - auto eccPublicKey = cdoc20::recipients::CreateECCPublicKeyCapsule(builder, - cdoc20::recipients::EllipticCurve::secp384r1, toVector(pki.rcpt_key), toVector(ephPublicKeyDer)); - auto offs = cdoc20::header::CreateRecipientRecord(builder, - cdoc20::recipients::Capsule::ECCPublicKeyCapsule, eccPublicKey.Union(), - toString(pki.label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); - recipients.push_back(offs); - } else { - if (key->type != CKey::Type::SERVER) { - return setLastError(QStringLiteral("Not a server key")); - } - const std::shared_ptr skey = std::static_pointer_cast(key); - if(!sendToServer(skey, skey->rcpt_key, ephPublicKeyDer, QLatin1String("ecc_secp384r1"))) - return false; - auto eccKeyServer = cdoc20::recipients::CreateEccKeyDetails(builder, - cdoc20::recipients::EllipticCurve::secp384r1, toVector(skey->rcpt_key)); - auto keyServer = cdoc20::recipients::CreateKeyServerCapsule(builder, - cdoc20::recipients::KeyDetailsUnion::EccKeyDetails, - eccKeyServer.Union(), toString(skey->keyserver_id), toString(skey->transaction_id)); - auto offs = cdoc20::header::CreateRecipientRecord(builder, - cdoc20::recipients::Capsule::KeyServerCapsule, keyServer.Union(), - toString(pki.label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); - recipients.push_back(offs); - } + if(!Settings::CDOC2_USE_KEYSERVER) { + auto eccPublicKey = cdoc20::recipients::CreateECCPublicKeyCapsule(builder, + cdoc20::recipients::EllipticCurve::secp384r1, toVector(pki.rcpt_key), toVector(ephPublicKeyDer)); + auto offs = cdoc20::header::CreateRecipientRecord(builder, + cdoc20::recipients::Capsule::ECCPublicKeyCapsule, eccPublicKey.Union(), + toString(pki.label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); + recipients.push_back(offs); + } else { + QString keyserver_id = Settings::CDOC2_DEFAULT_KEYSERVER; + QString transaction_id = sendToServer(keyserver_id, pki.rcpt_key, ephPublicKeyDer, QLatin1String("ecc_secp384r1")); + if (transaction_id.isEmpty()) return false; + auto eccKeyServer = cdoc20::recipients::CreateEccKeyDetails(builder, + cdoc20::recipients::EllipticCurve::secp384r1, toVector(pki.rcpt_key)); + auto keyServer = cdoc20::recipients::CreateKeyServerCapsule(builder, + cdoc20::recipients::KeyDetailsUnion::EccKeyDetails, + eccKeyServer.Union(), toString(keyserver_id), toString(transaction_id)); + auto offs = cdoc20::header::CreateRecipientRecord(builder, + cdoc20::recipients::Capsule::KeyServerCapsule, keyServer.Union(), + toString(pki.label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); + recipients.push_back(offs); } } - } + } auto offset = cdoc20::header::CreateHeader(builder, builder.CreateVector(recipients), cdoc20::header::PayloadEncryptionMethod::CHACHA20POLY1305); @@ -823,8 +772,9 @@ bool CDoc2::save(const QString &_path) auto header_len = qToBigEndian(uint32_t(header.size())); /* Use TmpFile wrapper so the original will not be lost during error */ - TmpFile tmp(_path); - if (!tmp.open()) { + QSaveFile tmp(_path); + tmp.setDirectWriteFallback(true); + if (!tmp.open(QFile::WriteOnly)) { setLastError(tmp.errorString()); return false; } @@ -838,14 +788,14 @@ bool CDoc2::save(const QString &_path) return false; } if(!enc.result()) { - return false; + return false; } QByteArray tag = enc.tag(); #ifndef NDEBUG qDebug() << "tag" << tag.toHex(); #endif tmp.write(tag); - tmp.close(); + tmp.commit(); this->path = _path; return true; } @@ -929,9 +879,9 @@ CDoc2::save(QString _path, const std::vector& files, const QString& label, auto header_len = qToBigEndian(uint32_t(header.size())); /* Use TmpFile wrapper so the original will not be lost during error */ - TmpFile tmp(_path); - if (!tmp.open()) { - //setLastError(tmp.errorString()); + QSaveFile tmp(_path); + tmp.setDirectWriteFallback(true); + if (!tmp.open(QFile::WriteOnly)) { return false; } /* Write contents to temporary */ @@ -951,8 +901,7 @@ CDoc2::save(QString _path, const std::vector& files, const QString& label, qDebug() << "tag" << tag.toHex(); #endif tmp.write(tag); - tmp.close(); - //this->path = _path; + tmp.commit(); return true; } @@ -1020,8 +969,8 @@ QByteArray CDoc2::getFMK(const CKey &key, const QByteArray& secret) if(key.type == CKey::Type::SERVER) { const CKeyServer &sk = static_cast(key); key_material = fetchKeyMaterial(sk); - } else if (key.type == CKey::PUBLICKEY) { - const CKeyPK& pk = static_cast(key); + } else if (key.type == CKey::PUBLIC_KEY) { + const CKeyPublicKey& pk = static_cast(key); key_material = pk.key_material; } #ifndef NDEBUG diff --git a/client/CDoc2.h b/client/CDoc2.h index 3fdf1565..b6d82dcf 100644 --- a/client/CDoc2.h +++ b/client/CDoc2.h @@ -33,13 +33,16 @@ class CDoc2 final: public CDoc, private QObject /*, private QFile */ { std::shared_ptr getDecryptionKey(const QSslCertificate &cert) const final; bool decryptPayload(const QByteArray &fmk) final; QByteArray deriveFMK(const QByteArray &priv, const CKey &key); - bool save(const QString &path) final; + + bool save(const QString &path) final; + // Write payload encrypted with sinbgle symmetric key + static bool save(QString path, const std::vector& files, const QString& label, const QByteArray& secret, unsigned int kdf_iter); + QByteArray getFMK(const CKey &key, const QByteArray& secret) final; int version() final; static std::unique_ptr load(const QString& _path); - static bool save(QString path, const std::vector& files, const QString& label, const QByteArray& secret, unsigned int kdf_iter); private: CDoc2(const QString &path); diff --git a/client/CryptoDoc.cpp b/client/CryptoDoc.cpp index aa730aa4..a2aff0f6 100644 --- a/client/CryptoDoc.cpp +++ b/client/CryptoDoc.cpp @@ -60,10 +60,15 @@ class CryptoDoc::Private final: public QThread std::unique_ptr cdoc; QString fileName; - QByteArray fmk; bool isEncrypted = false; CDocumentModel *documents = new CDocumentModel(this); QStringList tempFiles; + // Decryption data + QByteArray fmk; + // Encryption data + QString label; + QByteArray secret; + uint32_t kdf_iter; }; bool CryptoDoc::Private::isEncryptedWarning() const @@ -85,7 +90,11 @@ void CryptoDoc::Private::run() else { qCDebug(CRYPTO) << "Encrypt" << fileName; - isEncrypted = cdoc->save(fileName); + if (secret.isEmpty()) { + isEncrypted = cdoc->save(fileName); + } else { + isEncrypted = CDoc2::save(fileName, cdoc->files, label, secret, kdf_iter); + } } } @@ -236,12 +245,13 @@ CKey::isTheSameRecipient(const CKey& other) const bool CKey::isTheSameRecipient(const QSslCertificate &cert) const { - QByteArray this_key, other_key; - if (this->isCertificate()) { - const CKeyCert& ckc = static_cast(*this); - this_key = ckc.cert.publicKey().toDer(); - } - other_key = cert.publicKey().toDer(); + if (!isPKI()) return false; + const CKeyPKI& pki = static_cast(*this); + QByteArray this_key = pki.rcpt_key; + QSslKey k = cert.publicKey(); + QByteArray other_key = Crypto::toPublicKeyDer(k); + qDebug() << "This key:" << this_key.toHex(); + qDebug() << "Other key:" << other_key.toHex(); if (this_key.isEmpty() || other_key.isEmpty()) return false; return this_key == other_key; } @@ -273,19 +283,10 @@ void CKeyCert::setCert(const QSslCertificate &c) cert = c; QSslKey k = c.publicKey(); rcpt_key = Crypto::toPublicKeyDer(k); + qDebug() << "Set cert PK:" << rcpt_key.toHex(); pk_type = (k.algorithm() == QSsl::Rsa) ? PKType::RSA : PKType::ECC; } -std::shared_ptr -CKeyCD1::newEmpty() { - return std::shared_ptr(new CKeyCD1()); -} - -std::shared_ptr -CKeyCD1::fromCertificate(const QSslCertificate &cert) { - return std::shared_ptr(new CKeyCD1(cert)); -} - std::shared_ptr CKeyServer::fromKey(QByteArray _key, PKType _pk_type) { return std::shared_ptr(new CKeyServer(_key, _pk_type)); @@ -301,6 +302,12 @@ CryptoDoc::CryptoDoc( QObject *parent ) CryptoDoc::~CryptoDoc() { clear(); delete d; } +bool +CryptoDoc::supportsSymmetricKeys() const +{ + return d->cdoc->version() >= 2; +} + bool CryptoDoc::addKey(std::shared_ptr key ) { if(d->isEncryptedWarning()) @@ -401,25 +408,32 @@ bool CryptoDoc::decrypt(std::shared_ptr key, const QByteArray& secret) DocumentModel* CryptoDoc::documentModel() const { return d->documents; } -bool CryptoDoc::encrypt( const QString &filename ) +bool CryptoDoc::encrypt( const QString &filename, const QString& label, const QByteArray& secret, uint32_t kdf_iter) { - if( !filename.isEmpty() ) - d->fileName = filename; - if( d->fileName.isEmpty() ) - { + if(!filename.isEmpty()) d->fileName = filename; + if(d->fileName.isEmpty()) { WarningDialog::show(tr("Container is not open")); return false; } - if(d->isEncrypted) - return true; - if(d->cdoc->keys.isEmpty()) - { - WarningDialog::show(tr("No keys specified")); - return false; - } - + // I think the correct semantics is to fail if container is already encrypted + if(d->isEncrypted) return false; + if (secret.isEmpty()) { + // Encrypt for address list + if(d->cdoc->keys.isEmpty()) + { + WarningDialog::show(tr("No keys specified")); + return false; + } + } else { + // Encrypt with symmetric key + d->label = label; + d->secret = secret; + d->kdf_iter = kdf_iter; + } d->waitForFinished(); - if(d->isEncrypted) + d->label.clear(); + d->secret.clear(); + if(d->isEncrypted) open(d->fileName); else WarningDialog::show(tr("Failed to encrypt document. Please check your internet connection and network settings."), d->cdoc->lastError); @@ -481,14 +495,4 @@ bool CryptoDoc::saveCopy(const QString &filename) return QFile::copy(d->fileName, filename); } -bool -CryptoDoc::encryptLT(const QString& label, const QByteArray& secret, unsigned int kdf_iter) -{ - if( d->fileName.isEmpty()) { - WarningDialog::show(tr("Container is not open")); - return false; - } - return CDoc2::save(d->fileName, d->cdoc->files, label, secret, kdf_iter); -} - #include "CryptoDoc.moc" diff --git a/client/CryptoDoc.h b/client/CryptoDoc.h index 75d9b89b..405ce5f6 100644 --- a/client/CryptoDoc.h +++ b/client/CryptoDoc.h @@ -34,9 +34,9 @@ struct CKey public: enum Type { SYMMETRIC_KEY, + PUBLIC_KEY, CERTIFICATE, CDOC1, - PUBLICKEY, SERVER }; @@ -61,8 +61,9 @@ struct CKey // QByteArray key; bool isSymmetric() const { return type == Type::SYMMETRIC_KEY; } - bool isPKI() const { return (type == Type::CERTIFICATE) || (type == Type::CDOC1) || (type == Type::PUBLICKEY) || (type == Type::SERVER); } + bool isPKI() const { return (type == Type::CERTIFICATE) || (type == Type::CDOC1) || (type == Type::PUBLIC_KEY) || (type == Type::SERVER); } bool isCertificate() const { return (type == Type::CERTIFICATE) || (type == Type::CDOC1); } + bool isCDoc1() const { return type == Type::CDOC1; } bool isTheSameRecipient(const CKey &key) const; bool isTheSameRecipient(const QSslCertificate &cert) const; @@ -74,6 +75,8 @@ struct CKey }; // Symmetric key (plain or PBKDF) +// Usage: +// CDoc2:encrypt/decrypt LT struct CKeySymmetric : public CKey { QByteArray salt; @@ -85,7 +88,9 @@ struct CKeySymmetric : public CKey { CKeySymmetric(const QByteArray& _salt) : CKey(Type::SYMMETRIC_KEY), salt(_salt), kdf_iter(0) {} }; -// Public/private key +// Base PKI key +// Usage: +// CDoc2:encrypt struct CKeyPKI : public CKey { // Recipient's public key @@ -98,45 +103,50 @@ struct CKeyPKI : public CKey { }; -// Contains relevant encryption data +// Public key with additonal information +// Usage: +// CDoc1:encrypt struct CKeyCert : public CKeyPKI { QSslCertificate cert; + CKeyCert(const QSslCertificate &cert) : CKeyCert(CKey::Type::CERTIFICATE, cert) {}; + + void setCert(const QSslCertificate &c); + protected: CKeyCert(Type _type) : CKeyPKI(_type) {}; CKeyCert(Type _type, const QSslCertificate &cert); - - void setCert(const QSslCertificate &c); }; -// CDoc1 key +// CDoc1 decryption key (with additional information from file) +// Usage: +// CDoc1:decrypt -struct CKeyCD1 : public CKeyCert { +struct CKeyCDoc1 : public CKeyCert { QByteArray publicKey; QString concatDigest, method; QByteArray AlgorithmID, PartyUInfo, PartyVInfo; - static std::shared_ptr newEmpty(); - static std::shared_ptr fromCertificate(const QSslCertificate &cert); - - void setCert(const QSslCertificate &c) { CKeyCert::setCert(c); } - - static bool isCDoc1Key(const CKey& key) { return key.type == Type::CDOC1; } -protected: - CKeyCD1() : CKeyCert(Type::CDOC1) {}; - CKeyCD1(const QSslCertificate &cert) : CKeyCert(CKey::Type::CDOC1, cert) {}; + CKeyCDoc1() : CKeyCert(Type::CDOC1) {}; }; -struct CKeyPK : public CKeyPKI { +// CDoc2 PKI key with key material +// Usage: +// CDoc2: decrypt + +struct CKeyPublicKey : public CKeyPKI { // Either ECC public key or RSA encrypted kek QByteArray key_material; - CKeyPK() : CKeyPKI(Type::PUBLICKEY) {}; - CKeyPK(PKType _pk_type, QByteArray _rcpt_key) : CKeyPKI(Type::PUBLICKEY, _pk_type, _rcpt_key) {}; + CKeyPublicKey(PKType _pk_type, QByteArray _rcpt_key) : CKeyPKI(Type::PUBLIC_KEY, _pk_type, _rcpt_key) {}; }; +// CDoc2 PKI key with server info +// Usage: +// CDoc2: decrypt + struct CKeyServer : public CKeyPKI { // Server info QString keyserver_id, transaction_id; @@ -178,13 +188,14 @@ class CryptoDoc final: public QObject CryptoDoc(QObject *parent = nullptr); ~CryptoDoc() final; + bool supportsSymmetricKeys() const; bool addKey(std::shared_ptr key ); bool canDecrypt(const QSslCertificate &cert); void clear(const QString &file = {}); bool decrypt(std::shared_ptr key, const QByteArray& secret); - DocumentModel* documentModel() const; - bool encrypt(const QString &filename = {}); - QString fileName() const; + bool encrypt(const QString &filename = {}, const QString& label = {}, const QByteArray& secret = {}, uint32_t kdf_iter = 0); + DocumentModel* documentModel() const; + QString fileName() const; QList> keys() const; bool move(const QString &to); bool open( const QString &file ); @@ -192,8 +203,6 @@ class CryptoDoc final: public QObject bool saveCopy(const QString &filename); ria::qdigidoc4::ContainerState state() const; - bool encryptLT(const QString& label, const QByteArray& secret, unsigned int kdf_iter); - private: class Private; Private *d; diff --git a/client/MainWindow.cpp b/client/MainWindow.cpp index c0f99b61..2e440820 100644 --- a/client/MainWindow.cpp +++ b/client/MainWindow.cpp @@ -161,7 +161,6 @@ MainWindow::MainWindow( QWidget *parent ) connect(ui->cryptoContainerPage, &ContainerPage::removed, this, &MainWindow::removeAddress); connect(ui->cryptoContainerPage, &ContainerPage::decryptReq, this, &MainWindow::decryptClicked); - connect(ui->cryptoContainerPage, &ContainerPage::encryptLTReq, this, &MainWindow::encryptLTClicked); connect(ui->accordion, &Accordion::changePin1Clicked, this, &MainWindow::changePin1Clicked); connect(ui->accordion, &Accordion::changePin2Clicked, this, &MainWindow::changePin2Clicked); @@ -271,20 +270,18 @@ void MainWindow::decrypt(std::shared_ptr key) if(!cryptoDoc) return; QByteArray secret; - if (key->type == CKey::Type::SYMMETRIC_KEY) { + if (key && (key->type == CKey::Type::SYMMETRIC_KEY)) { std::shared_ptr skey = std::static_pointer_cast(key); - qDebug() << skey->label; PasswordDialog p; + p.setLabel(key->label); if (skey->kdf_iter > 0) { p.setMode(PasswordDialog::Mode::DECRYPT, PasswordDialog::Type::PASSWORD); if(!p.exec()) return; secret = p.secret(); - qDebug() << "Secret:" << QString::fromUtf8(secret); } else { p.setMode(PasswordDialog::Mode::DECRYPT, PasswordDialog::Type::KEY); if(!p.exec()) return; secret = p.secret(); - qDebug() << "Secret:" << QString::fromUtf8(secret.toHex()); } } @@ -342,7 +339,7 @@ QStringList MainWindow::dropEventFiles(QDropEvent *event) return files; } -bool MainWindow::encrypt() +bool MainWindow::encrypt(bool askForKey) { if(!cryptoDoc) return false; @@ -353,14 +350,29 @@ bool MainWindow::encrypt() dlg->addButton(WarningDialog::YES, QMessageBox::Yes); if(dlg->exec() == QMessageBox::Yes) { moveCryptoContainer(); - return encrypt(); + return encrypt(askForKey); } return false; } - WaitDialogHolder waitDialog(this, tr("Encrypting")); - - return cryptoDoc->encrypt(); + if (askForKey) { + PasswordDialog p; + p.setMode(PasswordDialog::Mode::ENCRYPT, PasswordDialog::Type::PASSWORD); + if(!p.exec()) return false; + QString label = p.label(); + QByteArray secret = p.secret(); + bool result; + if (p.type == PasswordDialog::Type::PASSWORD) { + WaitDialogHolder waitDialog(this, tr("Encrypting")); + return cryptoDoc->encrypt(cryptoDoc->fileName(), label, secret, 65536); + } else { + WaitDialogHolder waitDialog(this, tr("Encrypting")); + return cryptoDoc->encrypt(cryptoDoc->fileName(), label, secret, 0); + } + } else { + WaitDialogHolder waitDialog(this, tr("Encrypting")); + return cryptoDoc->encrypt(); + } } void MainWindow::mouseReleaseEvent(QMouseEvent *event) @@ -533,7 +545,7 @@ void MainWindow::convertToCDoc() auto cardData = qApp->signer()->tokenauth(); if(!cardData.cert().isNull()) - cryptoContainer->addKey(CKeyCD1::fromCertificate(cardData.cert())); + cryptoContainer->addKey(std::make_shared(cardData.cert())); resetCryptoDoc(cryptoContainer.release()); resetDigiDoc(nullptr, false); @@ -582,7 +594,15 @@ void MainWindow::onCryptoAction(int action, const QString &/*id*/, const QString notification->start( tr("Encryption succeeded!"), 750, 3000, 1200 ); } break; - case ContainerSaveAs: + case EncryptLT: + if(encrypt(true)) + { + ui->cryptoContainerPage->transition(cryptoDoc, qApp->signer()->tokenauth().cert()); + auto *notification = new FadeInNotification(this, WHITE, MANTIS, 110); + notification->start( tr("Encryption succeeded!"), 750, 3000, 1200 ); + } + break; + case ContainerSaveAs: { if(!cryptoDoc) break; @@ -1182,21 +1202,3 @@ MainWindow::decryptClicked(std::shared_ptr key) decrypt(key); } -void -MainWindow::encryptLTClicked() -{ - qDebug() << "LT encrypt"; - if (!cryptoDoc) return; - PasswordDialog p; - p.setMode(PasswordDialog::Mode::ENCRYPT, PasswordDialog::Type::PASSWORD); - if(!p.exec()) return; - QString label = p.label(); - QByteArray secret = p.secret(); - if (p.type == PasswordDialog::Type::PASSWORD) { - qDebug() << "Secret:" << QString::fromUtf8(secret); - cryptoDoc->encryptLT(label, secret, 65536); - } else { - qDebug() << "Secret:" << QString::fromUtf8(secret.toHex()); - cryptoDoc->encryptLT(label, secret, 0); - } -} diff --git a/client/MainWindow.h b/client/MainWindow.h index 88b02bc1..b3832657 100644 --- a/client/MainWindow.h +++ b/client/MainWindow.h @@ -73,8 +73,8 @@ private Q_SLOTS: void convertToCDoc(); ria::qdigidoc4::ContainerState currentState(); void decrypt(std::shared_ptr key); - bool encrypt(); - void loadPicture(); + bool encrypt(bool askForKey = false); + void loadPicture(); void moveCryptoContainer(); void moveSignatureContainer(); void navigateToPage( ria::qdigidoc4::Pages page, const QStringList &files = QStringList(), bool create = true ); @@ -122,5 +122,4 @@ private Q_SLOTS: WarningList *warnings; void decryptClicked(std::shared_ptr key); - void encryptLTClicked(); }; diff --git a/client/common_enums.h b/client/common_enums.h index fdc828db..f7663060 100644 --- a/client/common_enums.h +++ b/client/common_enums.h @@ -54,7 +54,9 @@ enum Actions { SignatureMobile, SignatureSmartID, SignatureToken, - ClearSignatureWarning + ClearSignatureWarning, + + EncryptLT }; enum ItemType { diff --git a/client/dialogs/AddRecipients.cpp b/client/dialogs/AddRecipients.cpp index e44ed911..3f116c6e 100644 --- a/client/dialogs/AddRecipients.cpp +++ b/client/dialogs/AddRecipients.cpp @@ -65,7 +65,6 @@ AddRecipients::AddRecipients(ItemList* itemList, QWidget *parent) ui->fromCard->setFont(Styles::font(Styles::Condensed, 12)); ui->fromFile->setFont(Styles::font(Styles::Condensed, 12)); ui->fromHistory->setFont(Styles::font(Styles::Condensed, 12)); - ui->addKey->setFont(Styles::font(Styles::Condensed, 12)); ui->cancel->setFont(Styles::font(Styles::Condensed, 14)); ui->confirm->setFont(Styles::font(Styles::Condensed, 14)); @@ -93,7 +92,6 @@ AddRecipients::AddRecipients(ItemList* itemList, QWidget *parent) connect(ui->fromFile, &QPushButton::clicked, this, &AddRecipients::addRecipientFromFile); connect(ui->fromHistory, &QPushButton::clicked, this, &AddRecipients::addRecipientFromHistory); - connect(ui->addKey, &QPushButton::clicked, this, &AddRecipients::addRecipientKey); for(Item *item: itemList->items) addRecipientToRightPane((qobject_cast(item))->getKey(), false); @@ -115,7 +113,7 @@ void AddRecipients::addAllRecipientToRightPane() addRecipientToRightPane(value); std::shared_ptr key = value->getKey(); if (key->type == CKey::Type::CDOC1) { - std::shared_ptr kd = std::static_pointer_cast(key); + std::shared_ptr kd = std::static_pointer_cast(key); history.append(kd->cert); } } @@ -171,18 +169,13 @@ void AddRecipients::addRecipientFromHistory() dlg->open(); } -void AddRecipients::addRecipientKey() -{ - qDebug() << "Adding key to recipients list"; -} - AddressItem * AddRecipients::addRecipientToLeftPane(const QSslCertificate& cert) { AddressItem *leftItem = leftList.value(cert); if(leftItem) return leftItem; - leftItem = new AddressItem(CKeyCD1::fromCertificate(cert), ui->leftPane); + leftItem = new AddressItem(std::make_shared(cert), ui->leftPane); leftList.insert(cert, leftItem); ui->leftPane->addWidget(leftItem); @@ -215,7 +208,7 @@ bool AddRecipients::addRecipientToRightPane(std::shared_ptr key, bool upda if(update) { if (key->type == CKey::Type::CDOC1) { - std::shared_ptr kd = std::static_pointer_cast(key); + std::shared_ptr kd = std::static_pointer_cast(key); if(auto expiryDate = kd->cert.expiryDate(); expiryDate <= QDateTime::currentDateTime()) { if(Settings::CDOC2_DEFAULT && Settings::CDOC2_USE_KEYSERVER) @@ -255,7 +248,7 @@ bool AddRecipients::addRecipientToRightPane(std::shared_ptr key, bool upda ui->rightPane->addWidget(rightItem); ui->confirm->setDisabled(rightList.isEmpty()); if (key->type == CKey::Type::CDOC1) { - std::shared_ptr kd = std::static_pointer_cast(key); + std::shared_ptr kd = std::static_pointer_cast(key); historyCertData.addAndSave({kd->cert}); } return true; @@ -313,7 +306,7 @@ void AddRecipients::removeRecipientFromRightPane(Item *toRemove) auto *rightItem = qobject_cast(toRemove); std::shared_ptr key = rightItem->getKey(); if (key->type == CKey::Type::CDOC1) { - std::shared_ptr kd = std::static_pointer_cast(key); + std::shared_ptr kd = std::static_pointer_cast(key); if(auto it = leftList.find(kd->cert); it != leftList.end()) { it.value()->setDisabled(false); diff --git a/client/dialogs/AddRecipients.h b/client/dialogs/AddRecipients.h index 29516adc..da597820 100644 --- a/client/dialogs/AddRecipients.h +++ b/client/dialogs/AddRecipients.h @@ -49,7 +49,6 @@ class AddRecipients final : public QDialog void addRecipientFromCard(); void addRecipientFromFile(); void addRecipientFromHistory(); - void addRecipientKey(); AddressItem * addRecipientToLeftPane(const QSslCertificate& cert); bool addRecipientToRightPane(std::shared_ptr key, bool update = true); void addRecipientToRightPane(AddressItem *leftItem, bool update = true); diff --git a/client/dialogs/AddRecipients.ui b/client/dialogs/AddRecipients.ui index d833abec..66fa94fe 100644 --- a/client/dialogs/AddRecipients.ui +++ b/client/dialogs/AddRecipients.ui @@ -97,7 +97,7 @@ QPushButton:disabled { color: #727679; } - + 15 @@ -179,13 +179,6 @@ QPushButton:disabled {
- - - - KEY - - - diff --git a/client/dialogs/KeyDialog.cpp b/client/dialogs/KeyDialog.cpp index c8a113dd..fe4351cc 100644 --- a/client/dialogs/KeyDialog.cpp +++ b/client/dialogs/KeyDialog.cpp @@ -50,7 +50,7 @@ KeyDialog::KeyDialog( const CKey &k, QWidget *parent ) connect(d->close, &QPushButton::clicked, this, &KeyDialog::accept); if (k.type == CKey::CDOC1) { - const CKeyCD1& kd = static_cast(k); + const CKeyCDoc1& kd = static_cast(k); connect(d->showCert, &QPushButton::clicked, this, [this, cert=kd.cert] { CertificateDetails::showCertificate(cert, this); }); @@ -70,7 +70,7 @@ KeyDialog::KeyDialog( const CKey &k, QWidget *parent ) bool adjust_size = false; if (k.type == CKey::Type::CDOC1) { - const CKeyCD1& cd1key = static_cast(k); + const CKeyCDoc1& cd1key = static_cast(k); addItem(tr("Recipient"), cd1key.label); addItem(tr("Crypto method"), cd1key.method); //7addItem(tr("Agreement method"), cd1key.agreement); diff --git a/client/dialogs/PasswordDialog.cpp b/client/dialogs/PasswordDialog.cpp index b54f11be..a4c5026a 100644 --- a/client/dialogs/PasswordDialog.cpp +++ b/client/dialogs/PasswordDialog.cpp @@ -7,9 +7,11 @@ PasswordDialog::PasswordDialog(QWidget *parent) , ui(new Ui::PasswordDialog) { ui->setupUi(this); - connect(ui->radioPassword, &QRadioButton::toggled, this, &PasswordDialog::passwordSelected); - connect(ui->radioKey, &QRadioButton::toggled, this, &PasswordDialog::symmetricKeySelected); - connect(ui->buttonGenKey, &QPushButton::clicked, this, &PasswordDialog::genKeyClicked); + connect(ui->generateKey, &QPushButton::clicked, this, &PasswordDialog::genKeyClicked); + connect(ui->typeSelector, &QTabWidget::currentChanged, this, &PasswordDialog::typeChanged); + connect(ui->passwordLine, &QLineEdit::textChanged, this, &PasswordDialog::lineChanged); + connect(ui->password2Line, &QLineEdit::textChanged, this, &PasswordDialog::lineChanged); + connect(ui->keyEdit, &QPlainTextEdit::textChanged, this, &PasswordDialog::editChanged); } PasswordDialog::~PasswordDialog() @@ -25,6 +27,12 @@ PasswordDialog::setMode(Mode _mode, Type _type) updateUI(); } +void +PasswordDialog::setLabel(const QString& label) +{ + ui->labelLine->setText(label); +} + QString PasswordDialog::label() { @@ -37,63 +45,92 @@ PasswordDialog::secret() const if (type == Type::PASSWORD) { return ui->passwordLine->text().toUtf8(); } else { - QString hex = ui->passwordLine->text(); + QString hex = ui->keyEdit->toPlainText(); return QByteArray::fromHex(hex.toUtf8()); } } void -PasswordDialog::passwordSelected(bool checked) +PasswordDialog::typeChanged(int index) { - Type new_type = (checked) ? Type::PASSWORD : Type::KEY; + Type new_type = static_cast(index); if (new_type != type) setMode(mode, new_type); } void -PasswordDialog::symmetricKeySelected(bool checked) +PasswordDialog::lineChanged(const QString& text) { - Type new_type = (checked) ? Type::KEY : Type::PASSWORD; - if (new_type != type) setMode(mode, new_type); + updateOK(); +} + +void +PasswordDialog::editChanged() +{ + updateOK(); } void PasswordDialog::genKeyClicked() { QByteArray key = Crypto::random(); - ui->passwordLine->setText(key.toHex()); + ui->keyEdit->clear(); + ui->keyEdit->appendPlainText(key.toHex()); } void PasswordDialog::updateUI() { + ui->typeSelector->setCurrentIndex(type); if (mode == Mode::DECRYPT) { - ui->radioBox->hide(); - ui->labelLabel->hide(); - ui->labelLine->hide(); - ui->buttonGenKey->hide(); + ui->labelLine->setReadOnly(true); if (type == Type::PASSWORD) { - ui->radioPassword->setChecked(true); + ui->typeSelector->setTabVisible(Type::PASSWORD, true); + ui->typeSelector->setTabVisible(Type::KEY, false); ui->passwordLabel->setText("Enter password to decrypt the document"); ui->passwordLine->setEchoMode(QLineEdit::EchoMode::Password); + ui->password2Label->hide(); + ui->password2Line->hide(); } else { - ui->radioKey->setChecked(true); - ui->passwordLabel->setText("Enter key to decrypt the document"); - ui->passwordLine->setEchoMode(QLineEdit::EchoMode::Normal); + ui->typeSelector->setTabVisible(Type::PASSWORD, false); + ui->typeSelector->setTabVisible(Type::KEY, true); + ui->keyLabel->setText("Enter key to decrypt the document"); + ui->generateKey->hide(); } } else { - ui->radioBox->show(); - ui->labelLabel->show(); - ui->labelLine->show(); + ui->labelLine->setReadOnly(false); if (type == Type::PASSWORD) { - ui->radioPassword->setChecked(true); + ui->typeSelector->setTabVisible(Type::PASSWORD, true); + ui->typeSelector->setTabVisible(Type::KEY, true); ui->passwordLabel->setText("Enter a password to encrypt the document"); ui->passwordLine->setEchoMode(QLineEdit::EchoMode::Password); - ui->buttonGenKey->hide(); + ui->password2Label->show(); + ui->password2Line->show(); + } else { + ui->typeSelector->setTabVisible(Type::PASSWORD, true); + ui->typeSelector->setTabVisible(Type::KEY, true); + ui->keyLabel->setText("Enter a key to encrypt the document"); + ui->generateKey->show(); + } + } + updateOK(); +} + +void +PasswordDialog::updateOK() +{ + bool active = false; + if (mode == Mode::DECRYPT) { + if (type == Type::PASSWORD) { + active = !ui->passwordLine->text().isEmpty(); + } else { + active = !ui->keyEdit->toPlainText().isEmpty(); + } + } else { + if (type == Type::PASSWORD) { + active = !ui->passwordLine->text().isEmpty() && ui->passwordLine->text() == ui->password2Line->text(); } else { - ui->radioKey->setChecked(true); - ui->passwordLabel->setText("Enter a key to encrypt the document"); - ui->passwordLine->setEchoMode(QLineEdit::EchoMode::Normal); - ui->buttonGenKey->show(); + active = !ui->keyEdit->toPlainText().isEmpty(); } } + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(active); } diff --git a/client/dialogs/PasswordDialog.h b/client/dialogs/PasswordDialog.h index e4b317da..b85639b3 100644 --- a/client/dialogs/PasswordDialog.h +++ b/client/dialogs/PasswordDialog.h @@ -30,15 +30,18 @@ class PasswordDialog : public QDialog void setMode(Mode mode, Type type); + void setLabel(const QString& label); QString label(); QByteArray secret() const; private: Ui::PasswordDialog *ui; - void passwordSelected(bool checked); - void symmetricKeySelected(bool checked); + void typeChanged(int index); + void lineChanged(const QString& text); + void editChanged(); void genKeyClicked(); void updateUI(); + void updateOK(); }; #endif // PASSWORDDIALOG_H diff --git a/client/dialogs/PasswordDialog.ui b/client/dialogs/PasswordDialog.ui index a3b1d7f1..dd8d2049 100644 --- a/client/dialogs/PasswordDialog.ui +++ b/client/dialogs/PasswordDialog.ui @@ -7,39 +7,13 @@ 0 0 400 - 253 + 322 Dialog - - - - - - - - - - Password - - - true - - - - - - - Symmetric key - - - - - - @@ -54,27 +28,98 @@ - - - Enter the password to decrypt document - - - Qt::AlignCenter - - - - - - - QLineEdit::Password - - - - - - - Generate Key + + + 0 + + + Password + + + + + + Password + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + QLineEdit::Password + + + + + + + Repeat password + + + + + + + QLineEdit::Password + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + Symmetric key + + + + + + Symmetric key + + + + + + + + + + Generate Key + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + diff --git a/client/widgets/AddressItem.cpp b/client/widgets/AddressItem.cpp index e7bab427..5280a358 100644 --- a/client/widgets/AddressItem.cpp +++ b/client/widgets/AddressItem.cpp @@ -37,11 +37,11 @@ class AddressItem::Private: public Ui::AddressItem bool yourself = false; }; -AddressItem::AddressItem(std::shared_ptr k, QWidget *parent, bool showIcon) +AddressItem::AddressItem(std::shared_ptr key, QWidget *parent, bool showIcon) : Item(parent) , ui(new Private) { - ui->key = k; + ui->key = key; ui->setupUi(this); if(showIcon) ui->icon->load(QStringLiteral(":/images/icon_Krypto_small.svg")); @@ -56,23 +56,29 @@ AddressItem::AddressItem(std::shared_ptr k, QWidget *parent, bool showIcon ui->remove->init(LabelButton::White); connect(ui->add, &QToolButton::clicked, this, [this]{ emit add(this);}); connect(ui->remove, &LabelButton::clicked, this, [this]{ emit remove(this);}); - connect(ui->decrypt, &QToolButton::clicked, this, [this]{ emit decrypt(ui->key);}); + + if (key->isSymmetric()) { + ui->decrypt->show(); + connect(ui->decrypt, &QToolButton::clicked, this, [this]{ emit decrypt(ui->key);}); + } else { + ui->decrypt->hide(); + } ui->add->setFont(Styles::font(Styles::Condensed, 12)); ui->added->setFont(ui->add->font()); - if (CKeyCD1::isCDoc1Key(*ui->key)) { - std::shared_ptr key = std::static_pointer_cast(ui->key); + if (ui->key->isCDoc1()) { + std::shared_ptr key = std::static_pointer_cast(ui->key); ui->code = SslCertificate(key->cert).personalCode().toHtmlEscaped(); ui->label = (!key->cert.subjectInfo("GN").isEmpty() && !key->cert.subjectInfo("SN").isEmpty() ? key->cert.subjectInfo("GN").join(' ') + " " + key->cert.subjectInfo("SN").join(' ') : key->cert.subjectInfo("CN").join(' ')).toHtmlEscaped(); } else { ui->code = {}; - ui->label = k->label; + ui->label = key->label; } - if(ui->label.isEmpty() && ui->key->type == CKey::PUBLICKEY) { - const CKeyPK& pk = static_cast(*ui->key); + if(ui->label.isEmpty() && ui->key->type == CKey::PUBLIC_KEY) { + const CKeyPublicKey& pk = static_cast(*ui->key); ui->label = pk.key_material; } setIdType(); @@ -118,7 +124,7 @@ void AddressItem::idChanged(std::shared_ptr key) void AddressItem::idChanged(const SslCertificate &cert) { - idChanged(CKeyCD1::fromCertificate(cert)); + idChanged(std::make_shared(cert)); } void AddressItem::initTabOrder(QWidget *item) diff --git a/client/widgets/AddressItem.ui b/client/widgets/AddressItem.ui index beb7be7e..be7d9adf 100644 --- a/client/widgets/AddressItem.ui +++ b/client/widgets/AddressItem.ui @@ -54,7 +54,11 @@ QToolButton:disabled { color: #727679; } #decrypt { -background-color: blue; +color: white; +background-color: #006EB5; +} +#decrypt:hover { +background-color: #00AEE5; } @@ -237,17 +241,17 @@ background-color: blue; - - LabelButton - QToolButton -
widgets/LabelButton.h
-
QSvgWidget QWidget -
QtSvg/QSvgWidget
+
QSvgWidget
1
+ + LabelButton + QToolButton +
widgets/LabelButton.h
+
diff --git a/client/widgets/ContainerPage.cpp b/client/widgets/ContainerPage.cpp index 213b2724..6c06e130 100644 --- a/client/widgets/ContainerPage.cpp +++ b/client/widgets/ContainerPage.cpp @@ -18,7 +18,6 @@ */ #include "ContainerPage.h" -#include "dialogs/PasswordDialog.h" #include "ui_ContainerPage.h" #include "CryptoDoc.h" @@ -86,8 +85,6 @@ ContainerPage::ContainerPage(QWidget *parent) connect(ui->containerFile, &QLabel::linkActivated, this, [this](const QString &link) { emit action(Actions::ContainerNavigate, link); }); - connect(ui->encryptLT, &LabelButton::clicked, this, [this]{emit encryptLTReq();}); - ui->summary->setVisible(Settings::SHOW_PRINT_SUMMARY); } @@ -273,6 +270,26 @@ void ContainerPage::showMainAction(const QList &actions) ui->navigationArea->layout()->invalidate(); } +void ContainerPage::showMainActionEncrypt(bool showLT) +{ + if(!mainAction) { + mainAction = std::make_unique(this); + connect(mainAction.get(), &MainAction::action, this, &ContainerPage::forward); + } + if (showLT) { + if (ui->rightPane->findChildren().isEmpty()) { + mainAction->showActions({ EncryptLT }); + } else { + mainAction->showActions({ EncryptContainer, EncryptLT }); + } + } else { + mainAction->showActions({ EncryptContainer }); + } + mainAction->setButtonEnabled(isSupported && !hasEmptyFile); + ui->mainActionSpacer->changeSize(198, 20, QSizePolicy::Fixed); + ui->navigationArea->layout()->invalidate(); +} + void ContainerPage::showSigningButton() { if (!isSupported || hasEmptyFile) @@ -305,7 +322,7 @@ void ContainerPage::transition(CryptoDoc *container, const QSslCertificate &cert ui->rightPane->addWidget(addr); } ui->leftPane->setModel(container->documentModel()); - updatePanes(container->state()); + updatePanes(container->state(), container); } void ContainerPage::transition(DigiDoc* container) @@ -363,7 +380,7 @@ void ContainerPage::transition(DigiDoc* container) showSigningButton(); ui->leftPane->setModel(container->documentModel()); - updatePanes(container->state()); + updatePanes(container->state(), nullptr); } void ContainerPage::update(bool canDecrypt, CryptoDoc* container) @@ -383,7 +400,7 @@ void ContainerPage::update(bool canDecrypt, CryptoDoc* container) ui->rightPane->addWidget(addr); } if(container->state() & UnencryptedContainer) - showMainAction({ EncryptContainer }); + showMainActionEncrypt(container->supportsSymmetricKeys()); } void ContainerPage::updateDecryptionButton() @@ -391,16 +408,16 @@ void ContainerPage::updateDecryptionButton() showMainAction({ isSeal ? DecryptToken : DecryptContainer }); } -void ContainerPage::updatePanes(ContainerState state) +void ContainerPage::updatePanes(ria::qdigidoc4::ContainerState state, CryptoDoc *crypto_container) { - ui->leftPane->stateChange(state); - ui->rightPane->stateChange(state); + ui->leftPane->stateChange(state); + ui->rightPane->stateChange(state); bool showPrintSummary = Settings::SHOW_PRINT_SUMMARY; auto setButtonsVisible = [](const QVector &buttons, bool visible) { for(QWidget *button: buttons) button->setVisible(visible); }; - switch( state ) + switch(state) { case UnsignedContainer: cancelText = QT_TR_NOOP("CANCEL"); @@ -411,7 +428,7 @@ void ContainerPage::updatePanes(ContainerState state) ui->leftPane->init(fileName, QT_TRANSLATE_NOOP("ItemList", "Container files")); showSigningButton(); setButtonsVisible({ ui->cancel, ui->convert, ui->save }, true); - setButtonsVisible({ ui->saveAs, ui->email, ui->summary, ui->encryptLT }, false); + setButtonsVisible({ ui->saveAs, ui->email, ui->summary }, false); break; case UnsignedSavedContainer: cancelText = QT_TR_NOOP("STARTING"); @@ -422,7 +439,7 @@ void ContainerPage::updatePanes(ContainerState state) setButtonsVisible({ ui->cancel, ui->convert, ui->saveAs, ui->email, ui->summary }, true); else setButtonsVisible({ ui->cancel, ui->convert, ui->saveAs, ui->email }, true); - setButtonsVisible({ ui->save, ui->encryptLT }, false); + setButtonsVisible({ ui->save }, false); showRightPane( ItemSignature, QT_TRANSLATE_NOOP("ItemList", "Container is not signed")); break; case SignedContainer: @@ -435,7 +452,7 @@ void ContainerPage::updatePanes(ContainerState state) setButtonsVisible({ ui->cancel, ui->convert, ui->saveAs, ui->email, ui->summary }, true); else setButtonsVisible({ ui->cancel, ui->convert, ui->saveAs, ui->email }, true); - setButtonsVisible({ ui->save, ui->encryptLT }, false); + setButtonsVisible({ ui->save }, false); break; case UnencryptedContainer: cancelText = QT_TR_NOOP("STARTING"); @@ -444,8 +461,8 @@ void ContainerPage::updatePanes(ContainerState state) ui->changeLocation->show(); ui->leftPane->init(fileName); showRightPane(ItemAddress, QT_TRANSLATE_NOOP("ItemList", "Recipients")); - showMainAction({ EncryptContainer }); - setButtonsVisible({ ui->cancel, ui->convert, ui->encryptLT }, true); + showMainActionEncrypt(crypto_container && crypto_container->supportsSymmetricKeys()); + setButtonsVisible({ ui->cancel, ui->convert }, true); setButtonsVisible({ ui->save, ui->saveAs, ui->email, ui->summary }, false); break; case EncryptedContainer: @@ -456,8 +473,8 @@ void ContainerPage::updatePanes(ContainerState state) ui->leftPane->init(fileName, QT_TRANSLATE_NOOP("ItemList", "Encrypted files")); showRightPane(ItemAddress, QT_TRANSLATE_NOOP("ItemList", "Recipients")); updateDecryptionButton(); - setButtonsVisible({ ui->save, ui->summary }, false); - setButtonsVisible({ ui->cancel, ui->convert, ui->saveAs, ui->email, ui->encryptLT }, true); + setButtonsVisible({ ui->save, ui->summary }, false); + setButtonsVisible({ ui->cancel, ui->convert, ui->saveAs, ui->email }, true); break; default: // Uninitialized cannot be shown on container page diff --git a/client/widgets/ContainerPage.h b/client/widgets/ContainerPage.h index 22299e17..13827a1d 100644 --- a/client/widgets/ContainerPage.h +++ b/client/widgets/ContainerPage.h @@ -64,7 +64,6 @@ class ContainerPage final : public QWidget void warning(const WarningText &warningText); void decryptReq(std::shared_ptr key); - void encryptLTReq(); private: void addressSearch(); @@ -75,10 +74,11 @@ class ContainerPage final : public QWidget void forward(int code); void hideRightPane(); void showMainAction(const QList &actions); - void showRightPane(ria::qdigidoc4::ItemType itemType, const char *header); + void showMainActionEncrypt(bool showLT); + void showRightPane(ria::qdigidoc4::ItemType itemType, const char *header); void showSigningButton(); - void updateDecryptionButton(); - void updatePanes(ria::qdigidoc4::ContainerState state); + void updateDecryptionButton(); + void updatePanes(ria::qdigidoc4::ContainerState state, CryptoDoc *crypto_container); void translateLabels(); Ui::ContainerPage *ui; diff --git a/client/widgets/ContainerPage.ui b/client/widgets/ContainerPage.ui index 0142e815..05cfd153 100644 --- a/client/widgets/ContainerPage.ui +++ b/client/widgets/ContainerPage.ui @@ -444,46 +444,6 @@ border: none;
- - - - - 182 - 25 - - - - - 182 - 25 - - - - - Roboto Condensed - 12 - false - false - - - - PointingHandCursor - - - Encrypt long-term - - - border-radius: 3px; -color: #006eb5; -text-decoration: none solid rgb(0, 110, 181); -text-align: center; -border: none; - - - ENCRYPT LONG_TERM - - - diff --git a/client/widgets/MainAction.cpp b/client/widgets/MainAction.cpp index 3635e24e..fcb30868 100644 --- a/client/widgets/MainAction.cpp +++ b/client/widgets/MainAction.cpp @@ -135,7 +135,8 @@ QString MainAction::label(Actions action) case SignatureSmartID: return tr("SignatureSmartID"); case SignatureToken: return tr("SignatureToken"); case EncryptContainer: return tr("EncryptContainer"); - case DecryptContainer: return tr("DecryptContainer"); + case EncryptLT: return tr("EncryptLongTerm"); + case DecryptContainer: return tr("DecryptContainer"); case DecryptToken: return tr("DECRYPT"); default: return tr("SignatureAdd"); } From c4f866dd403ff360409c7bf8190f50a3e5a39ff5 Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Thu, 16 May 2024 12:26:14 +0300 Subject: [PATCH 06/22] removed encryptLT --- client/CryptoDoc.cpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/client/CryptoDoc.cpp b/client/CryptoDoc.cpp index 02be21eb..a2aff0f6 100644 --- a/client/CryptoDoc.cpp +++ b/client/CryptoDoc.cpp @@ -440,16 +440,6 @@ bool CryptoDoc::encrypt( const QString &filename, const QString& label, const QB return d->isEncrypted; } -bool -CryptoDoc::encryptLT(const QString& label, const QByteArray& secret, unsigned int kdf_iter) -{ - if( d->fileName.isEmpty()) { - WarningDialog::show(tr("Container is not open")); - return false; - } - return CDoc2::save(d->fileName, d->cdoc->files, label, secret, kdf_iter); -} - QString CryptoDoc::fileName() const { return d->fileName; } QList> CryptoDoc::keys() const From 059a62d060211592da43c53fbd2ab5276ca2709e Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Thu, 16 May 2024 13:05:41 +0300 Subject: [PATCH 07/22] Fixed include filename capitalization --- client/dialogs/PasswordDialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/dialogs/PasswordDialog.cpp b/client/dialogs/PasswordDialog.cpp index a4c5026a..60c643d3 100644 --- a/client/dialogs/PasswordDialog.cpp +++ b/client/dialogs/PasswordDialog.cpp @@ -1,6 +1,6 @@ #include "PasswordDialog.h" #include "Crypto.h" -#include "ui_passworddialog.h" +#include "ui_PasswordDialog.h" PasswordDialog::PasswordDialog(QWidget *parent) : QDialog(parent), mode(Mode::DECRYPT), type(Type::PASSWORD) From f77decc4fc87c7193c5f60a5f07b673e3a94a136 Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Thu, 16 May 2024 13:22:37 +0300 Subject: [PATCH 08/22] Fixed CKey struct/class confusion and made PasswordDialog Qt5 compatible --- client/MainWindow.h | 2 +- client/dialogs/KeyDialog.h | 3 ++- client/dialogs/PasswordDialog.cpp | 16 ++++++++-------- client/widgets/AddressItem.h | 2 +- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/client/MainWindow.h b/client/MainWindow.h index b3832657..42281fa6 100644 --- a/client/MainWindow.h +++ b/client/MainWindow.h @@ -28,7 +28,7 @@ namespace Ui { class MainWindow; } -class CKey; +struct CKey; class CryptoDoc; class DigiDoc; class DocumentModel; diff --git a/client/dialogs/KeyDialog.h b/client/dialogs/KeyDialog.h index f65afe7c..a80600c7 100644 --- a/client/dialogs/KeyDialog.h +++ b/client/dialogs/KeyDialog.h @@ -21,7 +21,8 @@ #include -class CKey; +struct CKey; + class KeyDialog final: public QDialog { Q_OBJECT diff --git a/client/dialogs/PasswordDialog.cpp b/client/dialogs/PasswordDialog.cpp index 60c643d3..9cbc5d3e 100644 --- a/client/dialogs/PasswordDialog.cpp +++ b/client/dialogs/PasswordDialog.cpp @@ -84,30 +84,30 @@ PasswordDialog::updateUI() if (mode == Mode::DECRYPT) { ui->labelLine->setReadOnly(true); if (type == Type::PASSWORD) { - ui->typeSelector->setTabVisible(Type::PASSWORD, true); - ui->typeSelector->setTabVisible(Type::KEY, false); + ui->typeSelector->setTabEnabled(Type::PASSWORD, true); + ui->typeSelector->setTabEnabled(Type::KEY, false); ui->passwordLabel->setText("Enter password to decrypt the document"); ui->passwordLine->setEchoMode(QLineEdit::EchoMode::Password); ui->password2Label->hide(); ui->password2Line->hide(); } else { - ui->typeSelector->setTabVisible(Type::PASSWORD, false); - ui->typeSelector->setTabVisible(Type::KEY, true); + ui->typeSelector->setTabEnabled(Type::PASSWORD, false); + ui->typeSelector->setTabEnabled(Type::KEY, true); ui->keyLabel->setText("Enter key to decrypt the document"); ui->generateKey->hide(); } } else { ui->labelLine->setReadOnly(false); if (type == Type::PASSWORD) { - ui->typeSelector->setTabVisible(Type::PASSWORD, true); - ui->typeSelector->setTabVisible(Type::KEY, true); + ui->typeSelector->setTabEnabled(Type::PASSWORD, true); + ui->typeSelector->setTabEnabled(Type::KEY, true); ui->passwordLabel->setText("Enter a password to encrypt the document"); ui->passwordLine->setEchoMode(QLineEdit::EchoMode::Password); ui->password2Label->show(); ui->password2Line->show(); } else { - ui->typeSelector->setTabVisible(Type::PASSWORD, true); - ui->typeSelector->setTabVisible(Type::KEY, true); + ui->typeSelector->setTabEnabled(Type::PASSWORD, true); + ui->typeSelector->setTabEnabled(Type::KEY, true); ui->keyLabel->setText("Enter a key to encrypt the document"); ui->generateKey->show(); } diff --git a/client/widgets/AddressItem.h b/client/widgets/AddressItem.h index f85f6b37..26322de9 100644 --- a/client/widgets/AddressItem.h +++ b/client/widgets/AddressItem.h @@ -21,7 +21,7 @@ #include "widgets/Item.h" -class CKey; +struct CKey; class AddressItem final : public Item { From e747ed50b0bd7f93cef73378d67201b2fb2a63ae Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Thu, 16 May 2024 13:32:39 +0300 Subject: [PATCH 09/22] Some more fixes for ubuntu20 --- client/widgets/AddressItem.cpp | 2 ++ client/widgets/AddressItem.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/client/widgets/AddressItem.cpp b/client/widgets/AddressItem.cpp index 53877699..99dc7c19 100644 --- a/client/widgets/AddressItem.cpp +++ b/client/widgets/AddressItem.cpp @@ -17,6 +17,8 @@ * */ +#include + #include "AddressItem.h" #include "ui_AddressItem.h" diff --git a/client/widgets/AddressItem.h b/client/widgets/AddressItem.h index 26322de9..1f7b8e32 100644 --- a/client/widgets/AddressItem.h +++ b/client/widgets/AddressItem.h @@ -19,6 +19,8 @@ #pragma once +#include + #include "widgets/Item.h" struct CKey; From f3379d4082317e8ceae8838c9710233fab9d2213 Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Thu, 16 May 2024 13:36:42 +0300 Subject: [PATCH 10/22] Fixed ambiguous overload --- client/widgets/AddressItem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/widgets/AddressItem.cpp b/client/widgets/AddressItem.cpp index 99dc7c19..57a9c30d 100644 --- a/client/widgets/AddressItem.cpp +++ b/client/widgets/AddressItem.cpp @@ -76,7 +76,7 @@ AddressItem::AddressItem(std::shared_ptr key, QWidget *parent, bool showIc key->cert.subjectInfo("GN").join(' ') + " " + key->cert.subjectInfo("SN").join(' ') : key->cert.subjectInfo("CN").join(' ')).toHtmlEscaped(); } else { - ui->code = {}; + ui->code.clear(); ui->label = key->label.toHtmlEscaped(); } if(ui->label.isEmpty() && ui->key->type == CKey::PUBLIC_KEY) { From 49c0fed48f7864466a8c76e7b153e8d62df9e7a5 Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Thu, 16 May 2024 22:16:09 +0300 Subject: [PATCH 11/22] Use PBKDF from openssl instead of Qt --- client/CDoc2.cpp | 5 ++--- client/Crypto.cpp | 11 +++++++++++ client/Crypto.h | 1 + 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/client/CDoc2.cpp b/client/CDoc2.cpp index 9cef588b..8468cb21 100644 --- a/client/CDoc2.cpp +++ b/client/CDoc2.cpp @@ -41,7 +41,6 @@ #include #include #include -#include #include #include @@ -829,7 +828,7 @@ CDoc2::save(QString _path, const std::vector& files, const QString& label, // PasswordSalt_i = CSRNG() QByteArray pw_salt = Crypto::random(); // PasswordKeyMaterial_i = PBKDF2(Password_i, PasswordSalt_i) - QByteArray key_material = QPasswordDigestor::deriveKeyPbkdf2(QCryptographicHash::Sha256, secret, pw_salt, kdf_iter, 32); + QByteArray key_material = Crypto::pbkdf2_sha256(secret, pw_salt, kdf_iter); #ifndef NDEBUG qDebug() << "Key material: " << key_material.toHex(); #endif \ @@ -947,7 +946,7 @@ QByteArray CDoc2::getFMK(const CKey &key, const QByteArray& secret) qDebug() << "Password based symmetric key: " << key.label; #endif // KEY_MATERIAL = PBKDF2(PASSWORD, PASSWORD_SALT) - QByteArray key_material = QPasswordDigestor::deriveKeyPbkdf2(QCryptographicHash::Sha256, secret, sk.pw_salt, sk.kdf_iter, 32); + QByteArray key_material = Crypto::pbkdf2_sha256(secret, sk.pw_salt, sk.kdf_iter); #ifndef NDEBUG qDebug() << "Key material: " << key_material.toHex(); #endif diff --git a/client/Crypto.cpp b/client/Crypto.cpp index 41de9668..64c4a118 100644 --- a/client/Crypto.cpp +++ b/client/Crypto.cpp @@ -380,3 +380,14 @@ QByteArray Crypto::xor_data(const QByteArray &a, const QByteArray &b) result[i] = char(a[i] ^ b[i]); return result; } + +QByteArray +Crypto::pbkdf2_sha256(const QByteArray& pw, const QByteArray& salt, uint32_t iter) +{ + unsigned char key[32]; + PKCS5_PBKDF2_HMAC(pw.data(), pw.length(), + (const unsigned char *) salt.data(), salt.length(), + iter, EVP_sha256(), 32, key); + return QByteArray((const char *) key, 32); +} + diff --git a/client/Crypto.h b/client/Crypto.h index d7260a7c..021bac2a 100644 --- a/client/Crypto.h +++ b/client/Crypto.h @@ -72,6 +72,7 @@ class Crypto static QByteArray random(int len = 32); static QByteArray xor_data(const QByteArray &a, const QByteArray &b); + static QByteArray pbkdf2_sha256(const QByteArray& pw, const QByteArray& salt, uint32_t iter); private: static bool isError(int err); }; From dde8a823695e2b40410d91366733e40b9ad2ec19 Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Thu, 16 May 2024 22:37:35 +0300 Subject: [PATCH 12/22] Removed EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND, replaced with 2-step extract and expand --- client/CDoc2.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/client/CDoc2.cpp b/client/CDoc2.cpp index 8468cb21..c019b999 100644 --- a/client/CDoc2.cpp +++ b/client/CDoc2.cpp @@ -836,7 +836,8 @@ CDoc2::save(QString _path, const std::vector& files, const QString& label, QByteArray salt = Crypto::random(); // KEK_i = HKDF(KeyMaterialSalt_i, PasswordKeyMaterial_i) QByteArray info = KEK + cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) + secret; - QByteArray kek = Crypto::hkdf(key_material, salt, info, 32, EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND); + QByteArray tmp = Crypto::extract(key_material, salt, 32); + QByteArray kek = Crypto::expand(tmp, info, 32); QByteArray xor_key = Crypto::xor_data(fmk, kek); @@ -851,7 +852,8 @@ CDoc2::save(QString _path, const std::vector& files, const QString& label, // KeyMaterialSalt_i = CSRNG() // KEK_i = HKDF(KeyMaterialSalt_i, S_i) QByteArray info = KEK + cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) + label.toUtf8(); - QByteArray kek = Crypto::hkdf(secret, salt, info, 32, EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND); + QByteArray tmp = Crypto::extract(secret, salt, 32); + QByteArray kek = Crypto::expand(tmp, info, 32); QByteArray xor_key = Crypto::xor_data(fmk, kek); @@ -952,14 +954,15 @@ QByteArray CDoc2::getFMK(const CKey &key, const QByteArray& secret) #endif // KEK = HKDF(SALT, KEY_MATERIAL) QByteArray info = KEK + cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) + secret; - kek = Crypto::hkdf(key_material, sk.salt, info, 32, EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND); + QByteArray tmp = Crypto::extract(key_material, sk.salt, 32); + kek = Crypto::expand(tmp, info, 32); } else { #ifndef NDEBUG qDebug() << "Plain symmetric key: " << key.label; #endif QByteArray info = KEK + cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) + sk.label.toUtf8(); - kek = Crypto::hkdf(secret, sk.salt, info, 32, EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND); - qDebug() << "kek:" << kek.toHex(); + QByteArray tmp = Crypto::extract(secret, sk.salt, 32); + kek = Crypto::expand(tmp, info, 32); } } else { // Public/private key From cda361db234ed0cf0dbe6e2f97e312704c3b79e4 Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Fri, 17 May 2024 13:21:48 +0300 Subject: [PATCH 13/22] Fixed indentation and CodeQL warnings --- client/CDoc1.cpp | 222 +++--- client/CDoc1.h | 2 +- client/CDoc2.cpp | 1115 ++++++++++++++--------------- client/CDoc2.h | 8 +- client/Crypto.cpp | 74 +- client/Crypto.h | 10 +- client/CryptoDoc.cpp | 160 ++--- client/CryptoDoc.h | 94 +-- client/MainWindow.cpp | 148 ++-- client/MainWindow.h | 8 +- client/dialogs/AddRecipients.cpp | 54 +- client/dialogs/AddRecipients.h | 2 +- client/dialogs/KeyDialog.cpp | 18 +- client/dialogs/PasswordDialog.cpp | 150 ++-- client/dialogs/PasswordDialog.h | 50 +- client/widgets/AddressItem.cpp | 134 ++-- client/widgets/AddressItem.h | 2 +- client/widgets/ContainerPage.cpp | 106 +-- client/widgets/ContainerPage.h | 12 +- client/widgets/MainAction.cpp | 14 +- 20 files changed, 1189 insertions(+), 1194 deletions(-) diff --git a/client/CDoc1.cpp b/client/CDoc1.cpp index 1a98d22a..22bc9c63 100644 --- a/client/CDoc1.cpp +++ b/client/CDoc1.cpp @@ -99,12 +99,12 @@ CDoc1::CDoc1(const QString &path) { QStringList fileparts = xml.readElementText().split('|'); files.push_back({ - fileparts.value(0), - fileparts.value(3), - fileparts.value(2), - fileparts.value(1).toUInt(), - {} - }); + fileparts.value(0), + fileparts.value(3), + fileparts.value(2), + fileparts.value(1).toUInt(), + {} + }); } else properties[attr.value().toString()] = xml.readElementText(); @@ -117,10 +117,10 @@ CDoc1::CDoc1(const QString &path) if(xml.name() != QLatin1String("EncryptedKey")) return; - std::shared_ptr key = std::make_shared(); - // Id is never used - //key->id = xml.attributes().value(QLatin1String("Id")).toString(); - key->label = xml.attributes().value(QLatin1String("Recipient")).toString(); + std::shared_ptr key = std::make_shared(); + // Id is never used + //key->id = xml.attributes().value(QLatin1String("Id")).toString(); + key->label = xml.attributes().value(QLatin1String("Recipient")).toString(); while(!xml.atEnd()) { xml.readNext(); @@ -129,26 +129,26 @@ CDoc1::CDoc1(const QString &path) if(!xml.isStartElement()) continue; // EncryptedData/KeyInfo/KeyName - // Name is never used - //if(xml.name() == QLatin1String("KeyName")) - // key->name = xml.readElementText(); + // Name is never used + //if(xml.name() == QLatin1String("KeyName")) + // key->name = xml.readElementText(); // EncryptedData/KeyInfo/EncryptedKey/EncryptionMethod else if(xml.name() == QLatin1String("EncryptionMethod")) key->method = xml.attributes().value(QLatin1String("Algorithm")).toString(); // EncryptedData/KeyInfo/EncryptedKey/KeyInfo/AgreementMethod - else if(xml.name() == QLatin1String("AgreementMethod")) { - // fixme: handle error - if (xml.attributes().value(QLatin1String("Algorithm")).toString() != AGREEMENT_MTH) - return; - //key->agreement = xml.attributes().value(QLatin1String("Algorithm")).toString(); - // EncryptedData/KeyInfo/EncryptedKey/KeyInfo/AgreementMethod/KeyDerivationMethod - } else if(xml.name() == QLatin1String("KeyDerivationMethod")) { - // fixme: handle error - if (xml.attributes().value(QLatin1String("Algorithm")).toString() != CONCATKDF_MTH) - return; - // key->derive = xml.attributes().value(QLatin1String("Algorithm")).toString(); - // EncryptedData/KeyInfo/EncryptedKey/KeyInfo/AgreementMethod/KeyDerivationMethod/ConcatKDFParams - } else if(xml.name() == QLatin1String("ConcatKDFParams")) + else if(xml.name() == QLatin1String("AgreementMethod")) { + // fixme: handle error + if (xml.attributes().value(QLatin1String("Algorithm")).toString() != AGREEMENT_MTH) + return; + //key->agreement = xml.attributes().value(QLatin1String("Algorithm")).toString(); + // EncryptedData/KeyInfo/EncryptedKey/KeyInfo/AgreementMethod/KeyDerivationMethod + } else if(xml.name() == QLatin1String("KeyDerivationMethod")) { + // fixme: handle error + if (xml.attributes().value(QLatin1String("Algorithm")).toString() != CONCATKDF_MTH) + return; + // key->derive = xml.attributes().value(QLatin1String("Algorithm")).toString(); + // EncryptedData/KeyInfo/EncryptedKey/KeyInfo/AgreementMethod/KeyDerivationMethod/ConcatKDFParams + } else if(xml.name() == QLatin1String("ConcatKDFParams")) { key->AlgorithmID = QByteArray::fromHex(xml.attributes().value(QLatin1String("AlgorithmID")).toUtf8()); if(key->AlgorithmID.front() == char(0x00)) key->AlgorithmID.remove(0, 1); @@ -176,7 +176,7 @@ CDoc1::CDoc1(const QString &path) else if(xml.name() == QLatin1String("CipherValue")) { xml.readNext(); - key->encrypted_fmk = fromBase64(xml.text()); + key->encrypted_fmk = fromBase64(xml.text()); } } keys.append(key); @@ -187,12 +187,12 @@ CDoc1::CDoc1(const QString &path) if(files.empty() && properties.contains(QStringLiteral("Filename"))) { files.push_back({ - properties.value(QStringLiteral("Filename")), - {}, - mime == MIME_ZLIB ? properties.value(QStringLiteral("OriginalMimeType")) : mime, - properties.value(QStringLiteral("OriginalSize")).toUInt(), - {} - }); + properties.value(QStringLiteral("Filename")), + {}, + mime == MIME_ZLIB ? properties.value(QStringLiteral("OriginalMimeType")) : mime, + properties.value(QStringLiteral("OriginalSize")).toUInt(), + {} + }); } } @@ -281,12 +281,12 @@ bool CDoc1::decryptPayload(const QByteArray &key) else if(properties.contains(QStringLiteral("Filename"))) { files.push_back({ - properties.value(QStringLiteral("Filename")), - {}, - mime, - data.size(), - std::move(buffer), - }); + properties.value(QStringLiteral("Filename")), + {}, + mime, + data.size(), + std::move(buffer), + }); } else return setLastError(CryptoDoc::tr("Error parsing document")); @@ -298,7 +298,7 @@ CKey::DecryptionStatus CDoc1::canDecrypt(const QSslCertificate &cert) const { std::shared_ptr key = getDecryptionKey(cert); - if (key) return CKey::DecryptionStatus::CAN_DECRYPT; + if (key) return CKey::DecryptionStatus::CAN_DECRYPT; return CKey::DecryptionStatus::CANNOT_DECRYPT; } @@ -309,17 +309,17 @@ std::shared_ptr CDoc1::getDecryptionKey(const QSslCertificate &cert) const if (key->type != CKey::Type::CDOC1) continue; std::shared_ptr k = std::static_pointer_cast(key); if(!ENC_MTH.contains(method) || - k->cert != cert || - k->encrypted_fmk.isEmpty()) + k->cert != cert || + k->encrypted_fmk.isEmpty()) continue; if(cert.publicKey().algorithm() == QSsl::Rsa && - k->method == RSA_MTH) + k->method == RSA_MTH) return k; if(cert.publicKey().algorithm() == QSsl::Ec && - !k->publicKey.isEmpty() && - KWAES_SIZE.contains(k->method) /* && - k->derive == CONCATKDF_MTH && - k->agreement == AGREEMENT_MTH*/ ) + !k->publicKey.isEmpty() && + KWAES_SIZE.contains(k->method) /* && + k->derive == CONCATKDF_MTH && + k->agreement == AGREEMENT_MTH*/ ) return k; } return {}; @@ -462,42 +462,42 @@ bool CDoc1::save(const QString &path) if(!mime.isEmpty()) w.writeAttribute(QStringLiteral("MimeType"), mime); writeElement(w, DENC, QStringLiteral("EncryptionMethod"), { - {QStringLiteral("Algorithm"), method}, - }); + {QStringLiteral("Algorithm"), method}, + }); w.writeNamespace(DS, QStringLiteral("ds")); writeElement(w, DS, QStringLiteral("KeyInfo"), [&]{ for(std::shared_ptr key: qAsConst(keys)) { - // Only certificate-based keys can be used in CDoc1 - if (key->type != CKey::Type::CERTIFICATE) return; - std::shared_ptr ckey = std::static_pointer_cast(key); + // Only certificate-based keys can be used in CDoc1 + if (key->type != CKey::Type::CERTIFICATE) return; + std::shared_ptr ckey = std::static_pointer_cast(key); writeElement(w, DENC, QStringLiteral("EncryptedKey"), [&]{ - // Id is never used - //if(!k->id.isEmpty()) - // w.writeAttribute(QStringLiteral("Id"), k->id); - if(!ckey->label.isEmpty()) - w.writeAttribute(QStringLiteral("Recipient"), ckey->label); + // Id is never used + //if(!k->id.isEmpty()) + // w.writeAttribute(QStringLiteral("Id"), k->id); + if(!ckey->label.isEmpty()) + w.writeAttribute(QStringLiteral("Recipient"), ckey->label); QByteArray cipher; - if(ckey->pk_type == CKey::PKType::RSA) + if(ckey->pk_type == CKey::PKType::RSA) { - cipher = Crypto::encrypt(X509_get0_pubkey((const X509*)ckey->cert.handle()), RSA_PKCS1_PADDING, transportKey); + cipher = Crypto::encrypt(X509_get0_pubkey((const X509*)ckey->cert.handle()), RSA_PKCS1_PADDING, transportKey); if(cipher.isEmpty()) return; writeElement(w, DENC, QStringLiteral("EncryptionMethod"), { - {QStringLiteral("Algorithm"), RSA_MTH}, - }); + {QStringLiteral("Algorithm"), RSA_MTH}, + }); writeElement(w, DS, QStringLiteral("KeyInfo"), [&]{ - // Name is never used - //if(!k->name.isEmpty()) - // w.writeTextElement(DS, QStringLiteral("KeyName"), k->name); + // Name is never used + //if(!k->name.isEmpty()) + // w.writeTextElement(DS, QStringLiteral("KeyName"), k->name); writeElement(w, DS, QStringLiteral("X509Data"), [&]{ - writeBase64Element(w, DS, QStringLiteral("X509Certificate"), ckey->cert.toDer()); + writeBase64Element(w, DS, QStringLiteral("X509Certificate"), ckey->cert.toDer()); }); }); } else { - EVP_PKEY *peerPKey = X509_get0_pubkey((const X509*)ckey->cert.handle()); + EVP_PKEY *peerPKey = X509_get0_pubkey((const X509*)ckey->cert.handle()); auto priv = Crypto::genECKey(peerPKey); QByteArray sharedSecret = Crypto::derive(priv.get(), peerPKey); if(sharedSecret.isEmpty()) @@ -514,36 +514,36 @@ bool CDoc1::save(const QString &path) default: concatDigest = SHA512_MTH; break; } QByteArray encryptionKey = Crypto::concatKDF(SHA_MTH[concatDigest], KWAES_SIZE[encryptionMethod], - sharedSecret, props.value(QStringLiteral("DocumentFormat")).toUtf8() + SsDer + ckey->cert.toDer()); + sharedSecret, props.value(QStringLiteral("DocumentFormat")).toUtf8() + SsDer + ckey->cert.toDer()); #ifndef NDEBUG qDebug() << "ENC Ss" << SsDer.toHex(); qDebug() << "ENC Ksr" << sharedSecret.toHex(); qDebug() << "ENC ConcatKDF" << encryptionKey.toHex(); #endif - cipher = Crypto::aes_wrap(encryptionKey, transportKey); + cipher = Crypto::aes_wrap(encryptionKey, transportKey); if(cipher.isEmpty()) return; writeElement(w, DENC, QStringLiteral("EncryptionMethod"), { - {QStringLiteral("Algorithm"), encryptionMethod}, - }); + {QStringLiteral("Algorithm"), encryptionMethod}, + }); writeElement(w, DS, QStringLiteral("KeyInfo"), [&]{ writeElement(w, DENC, QStringLiteral("AgreementMethod"), { - {QStringLiteral("Algorithm"), AGREEMENT_MTH}, - }, [&]{ + {QStringLiteral("Algorithm"), AGREEMENT_MTH}, + }, [&]{ w.writeNamespace(XENC11, QStringLiteral("xenc11")); writeElement(w, XENC11, QStringLiteral("KeyDerivationMethod"), { - {QStringLiteral("Algorithm"), CONCATKDF_MTH}, - }, [&]{ + {QStringLiteral("Algorithm"), CONCATKDF_MTH}, + }, [&]{ writeElement(w, XENC11, QStringLiteral("ConcatKDFParams"), { - {QStringLiteral("AlgorithmID"), QStringLiteral("00") + props.value(QStringLiteral("DocumentFormat")).toUtf8().toHex()}, - {QStringLiteral("PartyUInfo"), QStringLiteral("00") + SsDer.toHex()}, - {QStringLiteral("PartyVInfo"), QStringLiteral("00") + ckey->cert.toDer().toHex()}, - }, [&]{ + {QStringLiteral("AlgorithmID"), QStringLiteral("00") + props.value(QStringLiteral("DocumentFormat")).toUtf8().toHex()}, + {QStringLiteral("PartyUInfo"), QStringLiteral("00") + SsDer.toHex()}, + {QStringLiteral("PartyVInfo"), QStringLiteral("00") + ckey->cert.toDer().toHex()}, + }, [&]{ writeElement(w, DS, QStringLiteral("DigestMethod"), { - {QStringLiteral("Algorithm"), concatDigest}, - }); + {QStringLiteral("Algorithm"), concatDigest}, + }); }); }); writeElement(w, DENC, QStringLiteral("OriginatorKeyInfo"), [&]{ @@ -551,15 +551,15 @@ bool CDoc1::save(const QString &path) w.writeNamespace(DSIG11, QStringLiteral("dsig11")); writeElement(w, DSIG11, QStringLiteral("ECKeyValue"), [&]{ writeElement(w, DSIG11, QStringLiteral("NamedCurve"), { - {QStringLiteral("URI"), QStringLiteral("urn:oid:") + oid}, - }); + {QStringLiteral("URI"), QStringLiteral("urn:oid:") + oid}, + }); writeBase64Element(w, DSIG11, QStringLiteral("PublicKey"), SsDer); }); }); }); writeElement(w, DENC, QStringLiteral("RecipientKeyInfo"), [&]{ writeElement(w, DS, QStringLiteral("X509Data"), [&]{ - writeBase64Element(w, DS, QStringLiteral("X509Certificate"), ckey->cert.toDer()); + writeBase64Element(w, DS, QStringLiteral("X509Certificate"), ckey->cert.toDer()); }); }); }); @@ -570,17 +570,17 @@ bool CDoc1::save(const QString &path) }); }); }}); - // This is actual content, for some weird reason named cipherData/cipherValue + // This is actual content, for some weird reason named cipherData/cipherValue writeElement(w,DENC, QStringLiteral("CipherData"), [&]{ writeBase64Element(w, DENC, QStringLiteral("CipherValue"), - Crypto::cipher(ENC_MTH[method], transportKey, data.buffer(), true) - ); + Crypto::cipher(ENC_MTH[method], transportKey, data.buffer(), true) + ); }); writeElement(w, DENC, QStringLiteral("EncryptionProperties"), [&]{ for(QMultiHash::const_iterator i = props.constBegin(); i != props.constEnd(); ++i) writeElement(w, DENC, QStringLiteral("EncryptionProperty"), { - {QStringLiteral("Name"), i.key()}, - }, [&]{ + {QStringLiteral("Name"), i.key()}, + }, [&]{ w.writeCharacters(i.value()); }); }); @@ -598,23 +598,23 @@ QByteArray CDoc1::getFMK(const CKey &key, const QByteArray& secret) const CKeyCDoc1& ckey = static_cast(key); setLastError({}); QByteArray decryptedKey = qApp->signer()->decrypt([&ckey](QCryptoBackend *backend) { - if(ckey.pk_type == CKey::PKType::RSA) - return backend->decrypt(ckey.encrypted_fmk, false); - return backend->deriveConcatKDF(ckey.publicKey, SHA_MTH[ckey.concatDigest], + if(ckey.pk_type == CKey::PKType::RSA) + return backend->decrypt(ckey.encrypted_fmk, false); + return backend->deriveConcatKDF(ckey.publicKey, SHA_MTH[ckey.concatDigest], int(KWAES_SIZE[ckey.method]), ckey.AlgorithmID, ckey.PartyUInfo, ckey.PartyVInfo); - }); - if(decryptedKey.isEmpty()) - { - setLastError(QStringLiteral("Failed to decrypt/derive key")); - return {}; - } - if(ckey.pk_type == CKey::PKType::RSA) - return decryptedKey; +}); +if(decryptedKey.isEmpty()) +{ + setLastError(QStringLiteral("Failed to decrypt/derive key")); + return {}; +} +if(ckey.pk_type == CKey::PKType::RSA) +return decryptedKey; #ifndef NDEBUG - qDebug() << "DEC Ss" << ckey.publicKey.toHex(); - qDebug() << "DEC ConcatKDF" << decryptedKey.toHex(); +qDebug() << "DEC Ss" << ckey.publicKey.toHex(); +qDebug() << "DEC ConcatKDF" << decryptedKey.toHex(); #endif - return Crypto::aes_unwrap(decryptedKey, ckey.encrypted_fmk); +return Crypto::aes_unwrap(decryptedKey, ckey.encrypted_fmk); } int CDoc1::version() @@ -645,20 +645,20 @@ void CDoc1::writeDDoc(QIODevice *ddoc) x.writeDefaultNamespace(QStringLiteral("http://www.sk.ee/DigiDoc/v1.3.0#")); x.writeStartElement(QStringLiteral("SignedDoc")); writeAttributes(x, { - {QStringLiteral("format"), QStringLiteral("DIGIDOC-XML")}, - {QStringLiteral("version"), QStringLiteral("1.3")}, - }); + {QStringLiteral("format"), QStringLiteral("DIGIDOC-XML")}, + {QStringLiteral("version"), QStringLiteral("1.3")}, + }); for(const File &file: qAsConst(files)) { x.writeStartElement(QStringLiteral("DataFile")); writeAttributes(x, { - {QStringLiteral("ContentType"), QStringLiteral("EMBEDDED_BASE64")}, - {QStringLiteral("Filename"), file.name}, - {QStringLiteral("Id"), file.id}, - {QStringLiteral("MimeType"), file.mime}, - {QStringLiteral("Size"), QString::number(file.size)}, - }); + {QStringLiteral("ContentType"), QStringLiteral("EMBEDDED_BASE64")}, + {QStringLiteral("Filename"), file.name}, + {QStringLiteral("Id"), file.id}, + {QStringLiteral("MimeType"), file.mime}, + {QStringLiteral("Size"), QString::number(file.size)}, + }); std::array buf{}; file.data->seek(0); for(auto size = file.data->read(buf.data(), buf.size()); size > 0; size = file.data->read(buf.data(), buf.size())) diff --git a/client/CDoc1.h b/client/CDoc1.h index 6d0262f5..8b7a7e73 100644 --- a/client/CDoc1.h +++ b/client/CDoc1.h @@ -40,7 +40,7 @@ class CDoc1 final: public CDoc, private QFile std::shared_ptr getDecryptionKey(const QSslCertificate &cert) const final; bool decryptPayload(const QByteArray &key) final; bool save(const QString &path) final; - QByteArray getFMK(const CKey &key, const QByteArray& secret) final; + QByteArray getFMK(const CKey &key, const QByteArray& secret) final; int version() final; static std::unique_ptr load(const QString& path); diff --git a/client/CDoc2.cpp b/client/CDoc2.cpp index c019b999..90bd5d98 100644 --- a/client/CDoc2.cpp +++ b/client/CDoc2.cpp @@ -61,344 +61,344 @@ const QByteArray CDoc2::PAYLOAD = "CDOC20payload"; const QByteArray CDoc2::SALT = "CDOC20salt"; namespace cdoc20 { - bool checkConnection() { - if(CheckConnection().check()) - return true; - return dispatchToMain([] { - auto *notification = new FadeInNotification(Application::mainWindow(), - ria::qdigidoc4::colors::WHITE, ria::qdigidoc4::colors::MANTIS, 110); - notification->start(QCoreApplication::translate("MainWindow", "Check internet connection"), 750, 3000, 1200); - return false; - }); - } +bool checkConnection() { + if(CheckConnection().check()) + return true; + return dispatchToMain([] { + auto *notification = new FadeInNotification(Application::mainWindow(), + ria::qdigidoc4::colors::WHITE, ria::qdigidoc4::colors::MANTIS, 110); + notification->start(QCoreApplication::translate("MainWindow", "Check internet connection"), 750, 3000, 1200); + return false; + }); +} - QNetworkRequest req(const QString &keyserver_id, const QString &transaction_id = {}) { +QNetworkRequest req(const QString &keyserver_id, const QString &transaction_id = {}) { #ifdef CONFIG_URL - QJsonObject list = Application::confValue(QLatin1String("CDOC2-CONF")).toObject(); - QJsonObject data = list.value(keyserver_id).toObject(); - QString url = transaction_id.isEmpty() ? - data.value(QLatin1String("POST")).toString(Settings::CDOC2_POST) : - data.value(QLatin1String("FETCH")).toString(Settings::CDOC2_GET); + QJsonObject list = Application::confValue(QLatin1String("CDOC2-CONF")).toObject(); + QJsonObject data = list.value(keyserver_id).toObject(); + QString url = transaction_id.isEmpty() ? + data.value(QLatin1String("POST")).toString(Settings::CDOC2_POST) : + data.value(QLatin1String("FETCH")).toString(Settings::CDOC2_GET); #else - QString url = transaction_id.isEmpty() ? Settings::CDOC2_POST : Settings::CDOC2_GET; + QString url = transaction_id.isEmpty() ? Settings::CDOC2_POST : Settings::CDOC2_GET; #endif - if(url.isEmpty()) - return QNetworkRequest{}; - QNetworkRequest req(QStringLiteral("%1/key-capsules%2").arg(url, - transaction_id.isEmpty() ? QString(): QStringLiteral("/%1").arg(transaction_id))); - req.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/json")); - return req; - } + if(url.isEmpty()) + return QNetworkRequest{}; + QNetworkRequest req(QStringLiteral("%1/key-capsules%2").arg(url, + transaction_id.isEmpty() ? QString(): QStringLiteral("/%1").arg(transaction_id))); + req.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/json")); + return req; +} - struct stream final: public QIODevice +struct stream final: public QIODevice +{ + static constexpr qint64 CHUNK = 16LL * 1024LL; + QIODevice *io {}; + Crypto::Cipher *cipher {}; + z_stream s {}; + QByteArray buf; + int flush = Z_NO_FLUSH; + + stream(QIODevice *_io, Crypto::Cipher *_cipher) + : io(_io) + , cipher(_cipher) { - static constexpr qint64 CHUNK = 16LL * 1024LL; - QIODevice *io {}; - Crypto::Cipher *cipher {}; - z_stream s {}; - QByteArray buf; - int flush = Z_NO_FLUSH; - - stream(QIODevice *_io, Crypto::Cipher *_cipher) - : io(_io) - , cipher(_cipher) + if(io->isReadable()) { - if(io->isReadable()) - { - if(inflateInit2(&s, MAX_WBITS) == Z_OK) - open(QIODevice::ReadOnly); - } - if(io->isWritable()) - { - if(deflateInit(&s, Z_DEFAULT_COMPRESSION) == Z_OK) - open(QIODevice::WriteOnly); - } + if(inflateInit2(&s, MAX_WBITS) == Z_OK) + open(QIODevice::ReadOnly); } - - ~stream() final + if(io->isWritable()) { - if(io->isReadable()) - inflateEnd(&s); - if(io->isWritable()) - { - flush = Z_FINISH; - writeData(nullptr, 0); - deflateEnd(&s); - } + if(deflateInit(&s, Z_DEFAULT_COMPRESSION) == Z_OK) + open(QIODevice::WriteOnly); } + } - bool isSequential() const final + ~stream() final + { + if(io->isReadable()) + inflateEnd(&s); + if(io->isWritable()) { - return true; + flush = Z_FINISH; + writeData(nullptr, 0); + deflateEnd(&s); } + } - qint64 bytesAvailable() const final - { - return (io->bytesAvailable() - Crypto::Cipher::tagLen()) + buf.size() + QIODevice::bytesAvailable(); - } + bool isSequential() const final + { + return true; + } - qint64 readData(char *data, qint64 maxlen) final + qint64 bytesAvailable() const final + { + return (io->bytesAvailable() - Crypto::Cipher::tagLen()) + buf.size() + QIODevice::bytesAvailable(); + } + + qint64 readData(char *data, qint64 maxlen) final + { + s.next_out = (Bytef*)data; + s.avail_out = uInt(maxlen); + std::array in{}; + for(int res = Z_OK; s.avail_out > 0 && res == Z_OK;) { - s.next_out = (Bytef*)data; - s.avail_out = uInt(maxlen); - std::array in{}; - for(int res = Z_OK; s.avail_out > 0 && res == Z_OK;) - { - if(auto insize = io->bytesAvailable() - Crypto::Cipher::tagLen(), + if(auto insize = io->bytesAvailable() - Crypto::Cipher::tagLen(), size = io->read(in.data(), qMin(insize, in.size())); size > 0) - { - if(!cipher->update(in.data(), int(size))) - return -1; - buf.append(in.data(), size); - } - s.next_in = (z_const Bytef*)buf.data(); - s.avail_in = uInt(buf.size()); - switch(res = inflate(&s, flush)) - { - case Z_OK: - buf = buf.right(s.avail_in); - break; - case Z_STREAM_END: - buf.clear(); - break; - default: return -1; - } + { + if(!cipher->update(in.data(), int(size))) + return -1; + buf.append(in.data(), size); + } + s.next_in = (z_const Bytef*)buf.data(); + s.avail_in = uInt(buf.size()); + switch(res = inflate(&s, flush)) + { + case Z_OK: + buf = buf.right(s.avail_in); + break; + case Z_STREAM_END: + buf.clear(); + break; + default: return -1; } - return qint64(maxlen - s.avail_out); } + return qint64(maxlen - s.avail_out); + } - qint64 writeData(const char *data, qint64 len) final - { - s.next_in = (z_const Bytef *)data; - s.avail_in = uInt(len); - std::array out{}; - while(true) { - s.next_out = (Bytef *)out.data(); - s.avail_out = out.size(); - int res = deflate(&s, flush); - if(res == Z_STREAM_ERROR) - return -1; - if(auto size = out.size() - s.avail_out; size > 0) - { - if(!cipher->update(out.data(), int(size)) || + qint64 writeData(const char *data, qint64 len) final + { + s.next_in = (z_const Bytef *)data; + s.avail_in = uInt(len); + std::array out{}; + while(true) { + s.next_out = (Bytef *)out.data(); + s.avail_out = out.size(); + int res = deflate(&s, flush); + if(res == Z_STREAM_ERROR) + return -1; + if(auto size = out.size() - s.avail_out; size > 0) + { + if(!cipher->update(out.data(), int(size)) || io->write(out.data(), size) != size) - return -1; - } - if(res == Z_STREAM_END) - break; - if(flush == Z_FINISH) - continue; - if(s.avail_in == 0) - break; + return -1; } - return len; + if(res == Z_STREAM_END) + break; + if(flush == Z_FINISH) + continue; + if(s.avail_in == 0) + break; } - }; + return len; + } +}; - struct TAR { - std::unique_ptr io; - explicit TAR(std::unique_ptr &&in) - : io(std::move(in)) - {} +struct TAR { + std::unique_ptr io; + explicit TAR(std::unique_ptr &&in) + : io(std::move(in)) + {} - bool save(const std::vector &files) + bool save(const std::vector &files) + { + auto writeHeader = [this](Header &h, qint64 size) { + h.chksum.fill(' '); + toOctal(h.size, size); + toOctal(h.chksum, h.checksum().first); + return io->write((const char*)&h, Header::Size) == Header::Size; + }; + auto writePadding = [this](qint64 size) { + QByteArray pad(padding(size), 0); + return io->write(pad) == pad.size(); + }; + auto toPaxRecord = [](const QByteArray &keyword, const QByteArray &value) { + QByteArray record = ' ' + keyword + '=' + value + '\n'; + QByteArray result; + for(auto len = record.size(); result.size() != len; ++len) + result = QByteArray::number(len + 1) + record; + return result; + }; + for(const CDoc::File &file: files) { - auto writeHeader = [this](Header &h, qint64 size) { - h.chksum.fill(' '); - toOctal(h.size, size); - toOctal(h.chksum, h.checksum().first); - return io->write((const char*)&h, Header::Size) == Header::Size; - }; - auto writePadding = [this](qint64 size) { - QByteArray pad(padding(size), 0); - return io->write(pad) == pad.size(); - }; - auto toPaxRecord = [](const QByteArray &keyword, const QByteArray &value) { - QByteArray record = ' ' + keyword + '=' + value + '\n'; - QByteArray result; - for(auto len = record.size(); result.size() != len; ++len) - result = QByteArray::number(len + 1) + record; - return result; - }; - for(const CDoc::File &file: files) - { - Header h {}; - QByteArray filename = file.name.toUtf8(); - QByteArray filenameTruncated = filename.left(h.name.size()); - std::copy(filenameTruncated.cbegin(), filenameTruncated.cend(), h.name.begin()); + Header h {}; + QByteArray filename = file.name.toUtf8(); + QByteArray filenameTruncated = filename.left(h.name.size()); + std::copy(filenameTruncated.cbegin(), filenameTruncated.cend(), h.name.begin()); - if(filename.size() > 100 || + if(filename.size() > 100 || file.size > 07777777) - { - h.typeflag = 'x'; - QByteArray paxData; - if(filename.size() > 100) - paxData += toPaxRecord("path", filename); - if(file.size > 07777777) - paxData += toPaxRecord("size", QByteArray::number(file.size)); - if(!writeHeader(h, paxData.size()) || + { + h.typeflag = 'x'; + QByteArray paxData; + if(filename.size() > 100) + paxData += toPaxRecord("path", filename); + if(file.size > 07777777) + paxData += toPaxRecord("size", QByteArray::number(file.size)); + if(!writeHeader(h, paxData.size()) || io->write(paxData) != paxData.size() || !writePadding(paxData.size())) - return false; - } - - h.typeflag = '0'; - if(!writeHeader(h, file.size)) - return false; - file.data->seek(0); - if(auto size = copyIODevice(file.data.get(), io.get()); size < 0 || !writePadding(size)) return false; } - return io->write((const char*)&Header::Empty, Header::Size) == Header::Size && - io->write((const char*)&Header::Empty, Header::Size) == Header::Size; + + h.typeflag = '0'; + if(!writeHeader(h, file.size)) + return false; + file.data->seek(0); + if(auto size = copyIODevice(file.data.get(), io.get()); size < 0 || !writePadding(size)) + return false; } + return io->write((const char*)&Header::Empty, Header::Size) == Header::Size && + io->write((const char*)&Header::Empty, Header::Size) == Header::Size; + } - std::vector files(bool &warning) const + std::vector files(bool &warning) const + { + std::vector result; + Header h {}; + auto readHeader = [&h, this] { return io->read((char*)&h, Header::Size) == Header::Size; }; + while(io->bytesAvailable() > 0) { - std::vector result; - Header h {}; - auto readHeader = [&h, this] { return io->read((char*)&h, Header::Size) == Header::Size; }; - while(io->bytesAvailable() > 0) + if(!readHeader()) + return {}; + if(h.isNull()) { - if(!readHeader()) - return {}; - if(h.isNull()) - { - if(!readHeader() && !h.isNull()) - return {}; - warning = io->bytesAvailable() > 0; - return result; - } - if(!h.verify()) + if(!readHeader() && !h.isNull()) return {}; + warning = io->bytesAvailable() > 0; + return result; + } + if(!h.verify()) + return {}; - CDoc::File f; - f.name = QString::fromUtf8(h.name.data(), std::min(h.name.size(), int(strlen(h.name.data())))); + CDoc::File f; + f.name = QString::fromUtf8(h.name.data(), std::min(h.name.size(), int(strlen(h.name.data())))); + f.size = fromOctal(h.size); + if(h.typeflag == 'x') + { + QByteArray paxData = io->read(f.size); + if(paxData.size() != f.size) + return {}; + io->skip(padding(f.size)); + if(!readHeader() || h.isNull() || !h.verify()) + return {}; f.size = fromOctal(h.size); - if(h.typeflag == 'x') + for(const QByteArray &data: paxData.split('\n')) { - QByteArray paxData = io->read(f.size); - if(paxData.size() != f.size) + if(data.isEmpty()) + break; + const auto &headerValue = data.split('='); + const auto &lenKeyword = headerValue[0].split(' '); + if(data.size() + 1 != lenKeyword[0].toUInt()) return {}; - io->skip(padding(f.size)); - if(!readHeader() || h.isNull() || !h.verify()) - return {}; - f.size = fromOctal(h.size); - for(const QByteArray &data: paxData.split('\n')) - { - if(data.isEmpty()) - break; - const auto &headerValue = data.split('='); - const auto &lenKeyword = headerValue[0].split(' '); - if(data.size() + 1 != lenKeyword[0].toUInt()) - return {}; - if(lenKeyword[1] == "path") - f.name = QString::fromUtf8(headerValue[1]); - if(lenKeyword[1] == "size") - f.size = headerValue[1].toUInt(); - } + if(lenKeyword[1] == "path") + f.name = QString::fromUtf8(headerValue[1]); + if(lenKeyword[1] == "size") + f.size = headerValue[1].toUInt(); } + } - if(h.typeflag == '0' || h.typeflag == 0) - { - if(f.size > 500L * 1024L * 1024L) - f.data = std::make_unique(); - else - f.data = std::make_unique(); - f.data->open(QIODevice::ReadWrite); - if(f.size != copyIODevice(io.get(), f.data.get(), f.size)) - return {}; - io->skip(padding(f.size)); - result.push_back(std::move(f)); - } + if(h.typeflag == '0' || h.typeflag == 0) + { + if(f.size > 500L * 1024L * 1024L) + f.data = std::make_unique(); else - io->skip(f.size + padding(f.size)); + f.data = std::make_unique(); + f.data->open(QIODevice::ReadWrite); + if(f.size != copyIODevice(io.get(), f.data.get(), f.size)) + return {}; + io->skip(padding(f.size)); + result.push_back(std::move(f)); } - return result; + else + io->skip(f.size + padding(f.size)); } + return result; + } - private: - struct Header { - std::array name; - std::array mode; - std::array uid; - std::array gid; - std::array size; - std::array mtime; - std::array chksum; - char typeflag; - std::array linkname; - std::array magic; - std::array version; - std::array uname; - std::array gname; - std::array devmajor; - std::array devminor; - std::array prefix; - std::array padding; - - std::pair checksum() const - { - int64_t unsignedSum = 0; - int64_t signedSum = 0; - for (size_t i = 0, size = sizeof(Header); i < size; i++) { - unsignedSum += ((unsigned char*) this)[i]; - signedSum += ((signed char*) this)[i]; - } - return {unsignedSum, signedSum}; +private: + struct Header { + std::array name; + std::array mode; + std::array uid; + std::array gid; + std::array size; + std::array mtime; + std::array chksum; + char typeflag; + std::array linkname; + std::array magic; + std::array version; + std::array uname; + std::array gname; + std::array devmajor; + std::array devminor; + std::array prefix; + std::array padding; + + std::pair checksum() const + { + int64_t unsignedSum = 0; + int64_t signedSum = 0; + for (size_t i = 0, size = sizeof(Header); i < size; i++) { + unsignedSum += ((unsigned char*) this)[i]; + signedSum += ((signed char*) this)[i]; } + return {unsignedSum, signedSum}; + } - bool isNull() { - return memcmp(this, &Empty, sizeof(Header)) == 0; - } + bool isNull() { + return memcmp(this, &Empty, sizeof(Header)) == 0; + } - bool verify() { - auto copy = chksum; - chksum.fill(' '); - auto checkSum = checksum(); - chksum.swap(copy); - int64_t referenceChecksum = fromOctal(chksum); - return referenceChecksum == checkSum.first || - referenceChecksum == checkSum.second; - } + bool verify() { + auto copy = chksum; + chksum.fill(' '); + auto checkSum = checksum(); + chksum.swap(copy); + int64_t referenceChecksum = fromOctal(chksum); + return referenceChecksum == checkSum.first || + referenceChecksum == checkSum.second; + } - static const Header Empty; - static const int Size; - }; + static const Header Empty; + static const int Size; + }; - template - static int64_t fromOctal(const std::array &data) + template + static int64_t fromOctal(const std::array &data) + { + int64_t i = 0; + for(const char c: data) { - int64_t i = 0; - for(const char c: data) - { - if(c < '0' || c > '7') - continue; - i <<= 3; - i += c - '0'; - } - return i; + if(c < '0' || c > '7') + continue; + i <<= 3; + i += c - '0'; } + return i; + } - template - static void toOctal(std::array &data, int64_t value) + template + static void toOctal(std::array &data, int64_t value) + { + data.fill(' '); + for(auto it = data.rbegin() + 1; it != data.rend(); ++it) { - data.fill(' '); - for(auto it = data.rbegin() + 1; it != data.rend(); ++it) - { - *it = char(value & 7) + '0'; - value >>= 3; - } + *it = char(value & 7) + '0'; + value >>= 3; } + } - static constexpr int padding(int64_t size) - { - return Header::Size - size % Header::Size; - } - }; + static constexpr int padding(int64_t size) + { + return Header::Size - size % Header::Size; + } +}; - const TAR::Header TAR::Header::Empty {}; - const int TAR::Header::Size = int(sizeof(TAR::Header)); +const TAR::Header TAR::Header::Empty {}; +const int TAR::Header::Size = int(sizeof(TAR::Header)); } bool @@ -411,8 +411,8 @@ CDoc2::isCDoc2File(const QString& path) } CDoc2::CDoc2(const QString &_path) -: path(_path) -/* : QFile(path) */ + : path(_path) + /* : QFile(path) */ { using namespace cdoc20::recipients; using namespace cdoc20::header; @@ -420,8 +420,8 @@ CDoc2::CDoc2(const QString &_path) uint32_t header_len = 0; QFile ifs(path); if(!ifs.open(QFile::ReadOnly) || - ifs.read(LABEL.length()) != LABEL || - ifs.read((char*)&header_len, int(sizeof(header_len))) != int(sizeof(header_len))) + ifs.read(LABEL.length()) != LABEL || + ifs.read((char*)&header_len, int(sizeof(header_len))) != int(sizeof(header_len))) return; header_data = ifs.read(qFromBigEndian(header_len)); if(header_data.size() != qFromBigEndian(header_len)) @@ -457,9 +457,9 @@ CDoc2::CDoc2(const QString &_path) continue; } auto fillRecipientPK = [&] (CKey::PKType pk_type, auto key) { - std::shared_ptr k(new CKeyPublicKey(pk_type, toByteArray(key->recipient_public_key()))); - k->label = toString(recipient->key_label()); - k->encrypted_fmk = toByteArray(recipient->encrypted_fmk()); + std::shared_ptr k(new CKeyPublicKey(pk_type, toByteArray(key->recipient_public_key()))); + k->label = toString(recipient->key_label()); + k->encrypted_fmk = toByteArray(recipient->encrypted_fmk()); return k; }; switch(recipient->capsule_type()) @@ -472,17 +472,17 @@ CDoc2::CDoc2(const QString &_path) qWarning() << "Unsupported ECC curve: skipping"; continue; } - std::shared_ptr k = fillRecipientPK(CKey::PKType::ECC, key); - k->key_material = toByteArray(key->sender_public_key()); - qDebug() << "Load PK:" << k->rcpt_key.toHex(); + std::shared_ptr k = fillRecipientPK(CKey::PKType::ECC, key); + k->key_material = toByteArray(key->sender_public_key()); + qDebug() << "Load PK:" << k->rcpt_key.toHex(); keys.append(k); } break; case Capsule::RSAPublicKeyCapsule: if(const auto *key = recipient->capsule_as_RSAPublicKeyCapsule()) { - std::shared_ptr k = fillRecipientPK(CKey::PKType::RSA, key); - k->key_material = toByteArray(key->encrypted_kek()); + std::shared_ptr k = fillRecipientPK(CKey::PKType::RSA, key); + k->key_material = toByteArray(key->encrypted_kek()); keys.append(k); } break; @@ -514,7 +514,7 @@ CDoc2::CDoc2(const QString &_path) } if (ckey) { ckey->label = toString(recipient->key_label()); - ckey->encrypted_fmk = toByteArray(recipient->encrypted_fmk()); + ckey->encrypted_fmk = toByteArray(recipient->encrypted_fmk()); ckey->keyserver_id = toString(server->keyserver_id()); ckey->transaction_id = toString(server->transaction_id()); keys.append(ckey); @@ -529,8 +529,8 @@ CDoc2::CDoc2(const QString &_path) auto salt = capsule->salt(); std::shared_ptr key(new CKeySymmetric(toByteArray(salt))); key->label = toString(recipient->key_label()); - key->encrypted_fmk = toByteArray(recipient->encrypted_fmk()); - keys.append(key); + key->encrypted_fmk = toByteArray(recipient->encrypted_fmk()); + keys.append(key); } break; case Capsule::PBKDF2Capsule: @@ -545,7 +545,7 @@ CDoc2::CDoc2(const QString &_path) int32_t kdf_iter = capsule->kdf_iterations(); std::shared_ptr key(new CKeySymmetric(toByteArray(salt))); key->label = toString(recipient->key_label()); - key->encrypted_fmk = toByteArray(recipient->encrypted_fmk()); + key->encrypted_fmk = toByteArray(recipient->encrypted_fmk()); key->pw_salt = toByteArray(pw_salt); key->kdf_iter = kdf_iter; keys.append(key); @@ -567,22 +567,22 @@ CDoc2::load(const QString& path) CKey::DecryptionStatus CDoc2::canDecrypt(const QSslCertificate &cert) const { - CKey::DecryptionStatus status = CKey::DecryptionStatus::CANNOT_DECRYPT; + CKey::DecryptionStatus status = CKey::DecryptionStatus::CANNOT_DECRYPT; for (std::shared_ptr key: keys) { - if (key->isTheSameRecipient(cert)) return CKey::CAN_DECRYPT; - if (key->isSymmetric()) status = CKey::DecryptionStatus::NEED_KEY; + if (key->isTheSameRecipient(cert)) return CKey::CAN_DECRYPT; + if (key->isSymmetric()) status = CKey::DecryptionStatus::NEED_KEY; } - return status; + return status; } std::shared_ptr CDoc2::getDecryptionKey(const QSslCertificate &cert) const { - std::shared_ptr best = {}; - for (std::shared_ptr key: keys) { - if (key->isTheSameRecipient(cert)) return key; - if (key->isSymmetric()) best = key; - } - return best; + std::shared_ptr best = {}; + for (std::shared_ptr key: keys) { + if (key->isTheSameRecipient(cert)) return key; + if (key->isSymmetric()) best = key; + } + return best; } bool CDoc2::decryptPayload(const QByteArray &fmk) @@ -631,132 +631,132 @@ bool CDoc2::save(const QString &_path) flatbuffers::FlatBufferBuilder builder; std::vector> recipients; - auto toVector = [&builder](const QByteArray &data) { + auto toVector = [&builder](const QByteArray &data) { return builder.CreateVector((const uint8_t*)data.data(), size_t(data.length())); }; - auto toString = [&builder](const QString &data) { + auto toString = [&builder](const QString &data) { QByteArray utf8 = data.toUtf8(); return builder.CreateString(utf8.data(), size_t(utf8.length())); }; - auto sendToServer = [this] (const QString& keyserver_id, const QByteArray &recipient_id, const QByteArray &key_material, QLatin1String type) -> QString { - if(keyserver_id.isEmpty()) { - setLastError(QStringLiteral("keyserver_id cannot be empty")); - return {}; - } - QNetworkRequest req = cdoc20::req(keyserver_id); - if(req.url().isEmpty()) { - setLastError(QStringLiteral("No valid config found for keyserver_id: %1").arg(keyserver_id)); - return {}; - } - if(!cdoc20::checkConnection()) { - return {}; - } + auto sendToServer = [this] (const QString& keyserver_id, const QByteArray &recipient_id, const QByteArray &key_material, QLatin1String type) -> QString { + if(keyserver_id.isEmpty()) { + setLastError(QStringLiteral("keyserver_id cannot be empty")); + return {}; + } + QNetworkRequest req = cdoc20::req(keyserver_id); + if(req.url().isEmpty()) { + setLastError(QStringLiteral("No valid config found for keyserver_id: %1").arg(keyserver_id)); + return {}; + } + if(!cdoc20::checkConnection()) { + return {}; + } QScopedPointer nam(CheckConnection::setupNAM(req, Settings::CDOC2_POST_CERT)); QEventLoop e; QNetworkReply *reply = nam->post(req, QJsonDocument({ - {QLatin1String("recipient_id"), QLatin1String(recipient_id.toBase64())}, - {QLatin1String("ephemeral_key_material"), QLatin1String(key_material.toBase64())}, - {QLatin1String("capsule_type"), type}, - }).toJson()); + {QLatin1String("recipient_id"), QLatin1String(recipient_id.toBase64())}, + {QLatin1String("ephemeral_key_material"), QLatin1String(key_material.toBase64())}, + {QLatin1String("capsule_type"), type}, + }).toJson()); connect(reply, &QNetworkReply::finished, &e, &QEventLoop::quit); e.exec(); - QString transaction_id; + QString transaction_id; if(reply->error() == QNetworkReply::NoError && - reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 201) { - transaction_id = QString::fromLatin1(reply->rawHeader("Location")).remove(QLatin1String("/key-capsules/")); - } else { - setLastError(reply->errorString()); - return {}; - } - if(transaction_id.isEmpty()) - setLastError(QStringLiteral("Failed to post key capsule")); - return transaction_id; + reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 201) { + transaction_id = QString::fromLatin1(reply->rawHeader("Location")).remove(QLatin1String("/key-capsules/")); + } else { + setLastError(reply->errorString()); + return {}; + } + if(transaction_id.isEmpty()) + setLastError(QStringLiteral("Failed to post key capsule")); + return transaction_id; }; for(std::shared_ptr key: keys) { - if (!key->isPKI()) { - return setLastError(QStringLiteral("Invalid key type")); - } - const CKeyPKI& pki = static_cast(*key); - if(pki.pk_type == CKey::PKType::RSA) { - QByteArray kek = Crypto::random(fmk.size()); - QByteArray xor_key = Crypto::xor_data(fmk, kek); - auto publicKey = Crypto::fromRSAPublicKeyDer(pki.rcpt_key); - if(!publicKey) - return false; - QByteArray encrytpedKek = Crypto::encrypt(publicKey.get(), RSA_PKCS1_OAEP_PADDING, kek); + if (!key->isPKI()) { + return setLastError(QStringLiteral("Invalid key type")); + } + const CKeyPKI& pki = static_cast(*key); + if(pki.pk_type == CKey::PKType::RSA) { + QByteArray kek = Crypto::random(fmk.size()); + QByteArray xor_key = Crypto::xor_data(fmk, kek); + auto publicKey = Crypto::fromRSAPublicKeyDer(pki.rcpt_key); + if(!publicKey) + return false; + QByteArray encrytpedKek = Crypto::encrypt(publicKey.get(), RSA_PKCS1_OAEP_PADDING, kek); #ifndef NDEBUG - qDebug() << "publicKeyDer" << pki.rcpt_key.toHex(); - qDebug() << "kek" << kek.toHex(); - qDebug() << "xor" << xor_key.toHex(); - qDebug() << "encrytpedKek" << encrytpedKek.toHex(); + qDebug() << "publicKeyDer" << pki.rcpt_key.toHex(); + qDebug() << "kek" << kek.toHex(); + qDebug() << "xor" << xor_key.toHex(); + qDebug() << "encrytpedKek" << encrytpedKek.toHex(); #endif - if(!Settings::CDOC2_USE_KEYSERVER) { - auto rsaPublicKey = cdoc20::recipients::CreateRSAPublicKeyCapsule(builder, - toVector(pki.rcpt_key), toVector(encrytpedKek)); - auto offs = cdoc20::header::CreateRecipientRecord(builder, - cdoc20::recipients::Capsule::RSAPublicKeyCapsule, rsaPublicKey.Union(), - toString(pki.label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); - recipients.push_back(offs); - } else { - QString keyserver_id = Settings::CDOC2_DEFAULT_KEYSERVER; - QString transaction_id = sendToServer(keyserver_id, pki.rcpt_key, encrytpedKek, QLatin1String("rsa")); - if (transaction_id.isEmpty()) return false; - auto rsaKeyServer = cdoc20::recipients::CreateRsaKeyDetails(builder, toVector(pki.rcpt_key)); - auto keyServer = cdoc20::recipients::CreateKeyServerCapsule(builder, - cdoc20::recipients::KeyDetailsUnion::RsaKeyDetails, - rsaKeyServer.Union(), toString(keyserver_id), toString(transaction_id)); - auto offs = cdoc20::header::CreateRecipientRecord(builder, - cdoc20::recipients::Capsule::KeyServerCapsule, keyServer.Union(), - toString(pki.label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); - recipients.push_back(offs); - } - } else { - auto publicKey = Crypto::fromECPublicKeyDer(pki.rcpt_key, NID_secp384r1); - if(!publicKey) return false; - auto ephKey = Crypto::genECKey(publicKey.get()); - QByteArray sharedSecret = Crypto::derive(ephKey.get(), publicKey.get()); - QByteArray ephPublicKeyDer = Crypto::toPublicKeyDer(ephKey.get()); - QByteArray kekPm = Crypto::extract(sharedSecret, KEKPREMASTER); - QByteArray info = KEK + cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) + pki.rcpt_key + ephPublicKeyDer; - QByteArray kek = Crypto::expand(kekPm, info, fmk.size()); - QByteArray xor_key = Crypto::xor_data(fmk, kek); + if(!Settings::CDOC2_USE_KEYSERVER) { + auto rsaPublicKey = cdoc20::recipients::CreateRSAPublicKeyCapsule(builder, + toVector(pki.rcpt_key), toVector(encrytpedKek)); + auto offs = cdoc20::header::CreateRecipientRecord(builder, + cdoc20::recipients::Capsule::RSAPublicKeyCapsule, rsaPublicKey.Union(), + toString(pki.label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); + recipients.push_back(offs); + } else { + QString keyserver_id = Settings::CDOC2_DEFAULT_KEYSERVER; + QString transaction_id = sendToServer(keyserver_id, pki.rcpt_key, encrytpedKek, QLatin1String("rsa")); + if (transaction_id.isEmpty()) return false; + auto rsaKeyServer = cdoc20::recipients::CreateRsaKeyDetails(builder, toVector(pki.rcpt_key)); + auto keyServer = cdoc20::recipients::CreateKeyServerCapsule(builder, + cdoc20::recipients::KeyDetailsUnion::RsaKeyDetails, + rsaKeyServer.Union(), toString(keyserver_id), toString(transaction_id)); + auto offs = cdoc20::header::CreateRecipientRecord(builder, + cdoc20::recipients::Capsule::KeyServerCapsule, keyServer.Union(), + toString(pki.label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); + recipients.push_back(offs); + } + } else { + auto publicKey = Crypto::fromECPublicKeyDer(pki.rcpt_key, NID_secp384r1); + if(!publicKey) return false; + auto ephKey = Crypto::genECKey(publicKey.get()); + QByteArray sharedSecret = Crypto::derive(ephKey.get(), publicKey.get()); + QByteArray ephPublicKeyDer = Crypto::toPublicKeyDer(ephKey.get()); + QByteArray kekPm = Crypto::extract(sharedSecret, KEKPREMASTER); + QByteArray info = KEK + cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) + pki.rcpt_key + ephPublicKeyDer; + QByteArray kek = Crypto::expand(kekPm, info, fmk.size()); + QByteArray xor_key = Crypto::xor_data(fmk, kek); #ifndef NDEBUG - qDebug() << "publicKeyDer" << pki.rcpt_key.toHex(); - qDebug() << "ephPublicKeyDer" << ephPublicKeyDer.toHex(); - qDebug() << "sharedSecret" << sharedSecret.toHex(); - qDebug() << "kekPm" << kekPm.toHex(); - qDebug() << "kek" << kek.toHex(); - qDebug() << "xor" << xor_key.toHex(); + qDebug() << "publicKeyDer" << pki.rcpt_key.toHex(); + qDebug() << "ephPublicKeyDer" << ephPublicKeyDer.toHex(); + qDebug() << "sharedSecret" << sharedSecret.toHex(); + qDebug() << "kekPm" << kekPm.toHex(); + qDebug() << "kek" << kek.toHex(); + qDebug() << "xor" << xor_key.toHex(); #endif - if(!Settings::CDOC2_USE_KEYSERVER) { - auto eccPublicKey = cdoc20::recipients::CreateECCPublicKeyCapsule(builder, - cdoc20::recipients::EllipticCurve::secp384r1, toVector(pki.rcpt_key), toVector(ephPublicKeyDer)); - auto offs = cdoc20::header::CreateRecipientRecord(builder, - cdoc20::recipients::Capsule::ECCPublicKeyCapsule, eccPublicKey.Union(), - toString(pki.label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); - recipients.push_back(offs); - } else { - QString keyserver_id = Settings::CDOC2_DEFAULT_KEYSERVER; - QString transaction_id = sendToServer(keyserver_id, pki.rcpt_key, ephPublicKeyDer, QLatin1String("ecc_secp384r1")); - if (transaction_id.isEmpty()) return false; - auto eccKeyServer = cdoc20::recipients::CreateEccKeyDetails(builder, - cdoc20::recipients::EllipticCurve::secp384r1, toVector(pki.rcpt_key)); - auto keyServer = cdoc20::recipients::CreateKeyServerCapsule(builder, - cdoc20::recipients::KeyDetailsUnion::EccKeyDetails, - eccKeyServer.Union(), toString(keyserver_id), toString(transaction_id)); - auto offs = cdoc20::header::CreateRecipientRecord(builder, - cdoc20::recipients::Capsule::KeyServerCapsule, keyServer.Union(), - toString(pki.label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); - recipients.push_back(offs); - } - } - } + if(!Settings::CDOC2_USE_KEYSERVER) { + auto eccPublicKey = cdoc20::recipients::CreateECCPublicKeyCapsule(builder, + cdoc20::recipients::EllipticCurve::secp384r1, toVector(pki.rcpt_key), toVector(ephPublicKeyDer)); + auto offs = cdoc20::header::CreateRecipientRecord(builder, + cdoc20::recipients::Capsule::ECCPublicKeyCapsule, eccPublicKey.Union(), + toString(pki.label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); + recipients.push_back(offs); + } else { + QString keyserver_id = Settings::CDOC2_DEFAULT_KEYSERVER; + QString transaction_id = sendToServer(keyserver_id, pki.rcpt_key, ephPublicKeyDer, QLatin1String("ecc_secp384r1")); + if (transaction_id.isEmpty()) return false; + auto eccKeyServer = cdoc20::recipients::CreateEccKeyDetails(builder, + cdoc20::recipients::EllipticCurve::secp384r1, toVector(pki.rcpt_key)); + auto keyServer = cdoc20::recipients::CreateKeyServerCapsule(builder, + cdoc20::recipients::KeyDetailsUnion::EccKeyDetails, + eccKeyServer.Union(), toString(keyserver_id), toString(transaction_id)); + auto offs = cdoc20::header::CreateRecipientRecord(builder, + cdoc20::recipients::Capsule::KeyServerCapsule, keyServer.Union(), + toString(pki.label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); + recipients.push_back(offs); + } + } + } auto offset = cdoc20::header::CreateHeader(builder, builder.CreateVector(recipients), - cdoc20::header::PayloadEncryptionMethod::CHACHA20POLY1305); + cdoc20::header::PayloadEncryptionMethod::CHACHA20POLY1305); builder.Finish(offset); QByteArray header = QByteArray::fromRawData((const char*)builder.GetBufferPointer(), int(builder.GetSize())); @@ -771,9 +771,9 @@ bool CDoc2::save(const QString &_path) auto header_len = qToBigEndian(uint32_t(header.size())); /* Use TmpFile wrapper so the original will not be lost during error */ - QSaveFile tmp(_path); - tmp.setDirectWriteFallback(true); - if (!tmp.open(QFile::WriteOnly)) { + QSaveFile tmp(_path); + tmp.setDirectWriteFallback(true); + if (!tmp.open(QFile::WriteOnly)) { setLastError(tmp.errorString()); return false; } @@ -787,14 +787,14 @@ bool CDoc2::save(const QString &_path) return false; } if(!enc.result()) { - return false; + return false; } QByteArray tag = enc.tag(); #ifndef NDEBUG qDebug() << "tag" << tag.toHex(); #endif tmp.write(tag); - tmp.commit(); + tmp.commit(); this->path = _path; return true; } @@ -802,108 +802,107 @@ bool CDoc2::save(const QString &_path) bool CDoc2::save(QString _path, const std::vector& files, const QString& label, const QByteArray& secret, unsigned int kdf_iter) { - //setLastError({}); - QByteArray fmk = Crypto::extract(Crypto::random(KEY_LEN), SALT); - QByteArray cek = Crypto::expand(fmk, CEK); - QByteArray hhk = Crypto::expand(fmk, HMAC); + QByteArray fmk = Crypto::extract(Crypto::random(KEY_LEN), SALT); + QByteArray cek = Crypto::expand(fmk, CEK); + QByteArray hhk = Crypto::expand(fmk, HMAC); #ifndef NDEBUG - qDebug() << "fmk" << fmk.toHex(); - qDebug() << "cek" << cek.toHex(); - qDebug() << "hhk" << hhk.toHex(); + qDebug() << "fmk" << fmk.toHex(); + qDebug() << "cek" << cek.toHex(); + qDebug() << "hhk" << hhk.toHex(); #endif - flatbuffers::FlatBufferBuilder builder; - std::vector> recipients; + flatbuffers::FlatBufferBuilder builder; + std::vector> recipients; - auto toVector = [&builder](const QByteArray &data) { - return builder.CreateVector((const uint8_t*)data.data(), size_t(data.length())); - }; + auto toVector = [&builder](const QByteArray &data) { + return builder.CreateVector((const uint8_t*)data.data(), size_t(data.length())); + }; - auto toString = [&builder](const QString &data) { - QByteArray utf8 = data.toUtf8(); - return builder.CreateString(utf8.data(), size_t(utf8.length())); - }; + auto toString = [&builder](const QString &data) { + QByteArray utf8 = data.toUtf8(); + return builder.CreateString(utf8.data(), size_t(utf8.length())); + }; - if (kdf_iter > 0) { - // PasswordSalt_i = CSRNG() - QByteArray pw_salt = Crypto::random(); - // PasswordKeyMaterial_i = PBKDF2(Password_i, PasswordSalt_i) - QByteArray key_material = Crypto::pbkdf2_sha256(secret, pw_salt, kdf_iter); + if (kdf_iter > 0) { + // PasswordSalt_i = CSRNG() + QByteArray pw_salt = Crypto::random(); + // PasswordKeyMaterial_i = PBKDF2(Password_i, PasswordSalt_i) + QByteArray key_material = Crypto::pbkdf2_sha256(secret, pw_salt, kdf_iter); #ifndef NDEBUG - qDebug() << "Key material: " << key_material.toHex(); + qDebug() << "Key material: " << key_material.toHex(); #endif \ - // KeyMaterialSalt_i = CSRNG() - QByteArray salt = Crypto::random(); - // KEK_i = HKDF(KeyMaterialSalt_i, PasswordKeyMaterial_i) - QByteArray info = KEK + cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) + secret; - QByteArray tmp = Crypto::extract(key_material, salt, 32); - QByteArray kek = Crypto::expand(tmp, info, 32); - - QByteArray xor_key = Crypto::xor_data(fmk, kek); - - auto capsule = cdoc20::recipients::CreatePBKDF2Capsule(builder, toVector(salt), toVector(pw_salt), cdoc20::recipients::KDFAlgorithmIdentifier::PBKDF2WithHmacSHA256, kdf_iter); - auto offs = cdoc20::header::CreateRecipientRecord(builder, - cdoc20::recipients::Capsule::PBKDF2Capsule, capsule.Union(), - toString(label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); - recipients.push_back(offs); - } else { - // KeyMaterialSalt_i = CSRNG() - QByteArray salt = Crypto::random(); - // KeyMaterialSalt_i = CSRNG() - // KEK_i = HKDF(KeyMaterialSalt_i, S_i) - QByteArray info = KEK + cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) + label.toUtf8(); - QByteArray tmp = Crypto::extract(secret, salt, 32); - QByteArray kek = Crypto::expand(tmp, info, 32); - - QByteArray xor_key = Crypto::xor_data(fmk, kek); - - auto capsule = cdoc20::recipients::CreateSymmetricKeyCapsule(builder, toVector(salt)); - auto offs = cdoc20::header::CreateRecipientRecord(builder, - cdoc20::recipients::Capsule::SymmetricKeyCapsule, capsule.Union(), - toString(label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); - recipients.push_back(offs); - } - - auto offset = cdoc20::header::CreateHeader(builder, builder.CreateVector(recipients), - cdoc20::header::PayloadEncryptionMethod::CHACHA20POLY1305); - builder.Finish(offset); - - QByteArray header = QByteArray::fromRawData((const char*)builder.GetBufferPointer(), int(builder.GetSize())); - QByteArray headerHMAC = Crypto::sign_hmac(hhk, header); - QByteArray nonce = Crypto::random(NONCE_LEN); + // KeyMaterialSalt_i = CSRNG() + QByteArray salt = Crypto::random(); + // KEK_i = HKDF(KeyMaterialSalt_i, PasswordKeyMaterial_i) + QByteArray info = KEK + cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) + secret; + QByteArray tmp = Crypto::extract(key_material, salt, 32); + QByteArray kek = Crypto::expand(tmp, info, 32); + + QByteArray xor_key = Crypto::xor_data(fmk, kek); + + auto capsule = cdoc20::recipients::CreatePBKDF2Capsule(builder, toVector(salt), toVector(pw_salt), cdoc20::recipients::KDFAlgorithmIdentifier::PBKDF2WithHmacSHA256, kdf_iter); + auto offs = cdoc20::header::CreateRecipientRecord(builder, + cdoc20::recipients::Capsule::PBKDF2Capsule, capsule.Union(), + toString(label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); + recipients.push_back(offs); + } else { + // KeyMaterialSalt_i = CSRNG() + QByteArray salt = Crypto::random(); + // KeyMaterialSalt_i = CSRNG() + // KEK_i = HKDF(KeyMaterialSalt_i, S_i) + QByteArray info = KEK + cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) + label.toUtf8(); + QByteArray tmp = Crypto::extract(secret, salt, 32); + QByteArray kek = Crypto::expand(tmp, info, 32); + + QByteArray xor_key = Crypto::xor_data(fmk, kek); + + auto capsule = cdoc20::recipients::CreateSymmetricKeyCapsule(builder, toVector(salt)); + auto offs = cdoc20::header::CreateRecipientRecord(builder, + cdoc20::recipients::Capsule::SymmetricKeyCapsule, capsule.Union(), + toString(label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); + recipients.push_back(offs); + } + + auto offset = cdoc20::header::CreateHeader(builder, builder.CreateVector(recipients), + cdoc20::header::PayloadEncryptionMethod::CHACHA20POLY1305); + builder.Finish(offset); + + QByteArray header = QByteArray::fromRawData((const char*)builder.GetBufferPointer(), int(builder.GetSize())); + QByteArray headerHMAC = Crypto::sign_hmac(hhk, header); + QByteArray nonce = Crypto::random(NONCE_LEN); #ifndef NDEBUG - qDebug() << "hmac" << headerHMAC.toHex(); - qDebug() << "nonce" << nonce.toHex(); + qDebug() << "hmac" << headerHMAC.toHex(); + qDebug() << "nonce" << nonce.toHex(); #endif - Crypto::Cipher enc(EVP_chacha20_poly1305(), cek, nonce, true); - enc.updateAAD(PAYLOAD + header + headerHMAC); - auto header_len = qToBigEndian(uint32_t(header.size())); - - /* Use TmpFile wrapper so the original will not be lost during error */ - QSaveFile tmp(_path); - tmp.setDirectWriteFallback(true); - if (!tmp.open(QFile::WriteOnly)) { - return false; - } - /* Write contents to temporary */ - tmp.write(LABEL); - tmp.write((const char*)&header_len, qint64(sizeof(header_len))); - tmp.write(header); - tmp.write(headerHMAC); - tmp.write(nonce); - if(!cdoc20::TAR(std::unique_ptr(new cdoc20::stream(&tmp, &enc))).save(files)) { - return false; - } - if(!enc.result()) { - return false; - } - QByteArray tag = enc.tag(); + Crypto::Cipher enc(EVP_chacha20_poly1305(), cek, nonce, true); + enc.updateAAD(PAYLOAD + header + headerHMAC); + auto header_len = qToBigEndian(uint32_t(header.size())); + + /* Use TmpFile wrapper so the original will not be lost during error */ + QSaveFile tmp(_path); + tmp.setDirectWriteFallback(true); + if (!tmp.open(QFile::WriteOnly)) { + return false; + } + /* Write contents to temporary */ + tmp.write(LABEL); + tmp.write((const char*)&header_len, qint64(sizeof(header_len))); + tmp.write(header); + tmp.write(headerHMAC); + tmp.write(nonce); + if(!cdoc20::TAR(std::unique_ptr(new cdoc20::stream(&tmp, &enc))).save(files)) { + return false; + } + if(!enc.result()) { + return false; + } + QByteArray tag = enc.tag(); #ifndef NDEBUG - qDebug() << "tag" << tag.toHex(); + qDebug() << "tag" << tag.toHex(); #endif - tmp.write(tag); - tmp.commit(); - return true; + tmp.write(tag); + tmp.commit(); + return true; } @@ -919,7 +918,7 @@ QByteArray CDoc2::fetchKeyMaterial(const CKeyServer& key) } auto authKey = dispatchToMain(&QSigner::key, qApp->signer()); QScopedPointer nam( - CheckConnection::setupNAM(req, qApp->signer()->tokenauth().cert(), authKey, Settings::CDOC2_GET_CERT)); + CheckConnection::setupNAM(req, qApp->signer()->tokenauth().cert(), authKey, Settings::CDOC2_GET_CERT)); QEventLoop e; QNetworkReply *reply = nam->get(req); connect(reply, &QNetworkReply::finished, &e, &QEventLoop::quit); @@ -939,83 +938,83 @@ QByteArray CDoc2::fetchKeyMaterial(const CKeyServer& key) QByteArray CDoc2::getFMK(const CKey &key, const QByteArray& secret) { setLastError({}); - QByteArray kek; - if (key.isSymmetric()) { - // Symmetric key - const CKeySymmetric &sk = static_cast(key); - if (sk.kdf_iter > 0) { + QByteArray kek; + if (key.isSymmetric()) { + // Symmetric key + const CKeySymmetric &sk = static_cast(key); + if (sk.kdf_iter > 0) { #ifndef NDEBUG - qDebug() << "Password based symmetric key: " << key.label; + qDebug() << "Password based symmetric key: " << key.label; #endif - // KEY_MATERIAL = PBKDF2(PASSWORD, PASSWORD_SALT) - QByteArray key_material = Crypto::pbkdf2_sha256(secret, sk.pw_salt, sk.kdf_iter); + // KEY_MATERIAL = PBKDF2(PASSWORD, PASSWORD_SALT) + QByteArray key_material = Crypto::pbkdf2_sha256(secret, sk.pw_salt, sk.kdf_iter); #ifndef NDEBUG - qDebug() << "Key material: " << key_material.toHex(); + qDebug() << "Key material: " << key_material.toHex(); #endif - // KEK = HKDF(SALT, KEY_MATERIAL) - QByteArray info = KEK + cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) + secret; - QByteArray tmp = Crypto::extract(key_material, sk.salt, 32); - kek = Crypto::expand(tmp, info, 32); - } else { + // KEK = HKDF(SALT, KEY_MATERIAL) + QByteArray info = KEK + cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) + secret; + QByteArray tmp = Crypto::extract(key_material, sk.salt, 32); + kek = Crypto::expand(tmp, info, 32); + } else { #ifndef NDEBUG - qDebug() << "Plain symmetric key: " << key.label; + qDebug() << "Plain symmetric key: " << key.label; #endif - QByteArray info = KEK + cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) + sk.label.toUtf8(); - QByteArray tmp = Crypto::extract(secret, sk.salt, 32); - kek = Crypto::expand(tmp, info, 32); - } - } else { - // Public/private key - const CKeyPKI &pki = static_cast(key); - QByteArray key_material; - if(key.type == CKey::Type::SERVER) { - const CKeyServer &sk = static_cast(key); - key_material = fetchKeyMaterial(sk); - } else if (key.type == CKey::PUBLIC_KEY) { - const CKeyPublicKey& pk = static_cast(key); - key_material = pk.key_material; - } + QByteArray info = KEK + cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) + sk.label.toUtf8(); + QByteArray tmp = Crypto::extract(secret, sk.salt, 32); + kek = Crypto::expand(tmp, info, 32); + } + } else { + // Public/private key + const CKeyPKI &pki = static_cast(key); + QByteArray key_material; + if(key.type == CKey::Type::SERVER) { + const CKeyServer &sk = static_cast(key); + key_material = fetchKeyMaterial(sk); + } else if (key.type == CKey::PUBLIC_KEY) { + const CKeyPublicKey& pk = static_cast(key); + key_material = pk.key_material; + } #ifndef NDEBUG - qDebug() << "Public key" << pki.rcpt_key.toHex(); - qDebug() << "Key material" << key_material.toHex(); + qDebug() << "Public key" << pki.rcpt_key.toHex(); + qDebug() << "Key material" << key_material.toHex(); #endif - if (pki.pk_type == CKey::PKType::RSA) { - kek = qApp->signer()->decrypt([&key_material](QCryptoBackend *backend) { - return backend->decrypt(key_material, true); - }); - } else { - kek = qApp->signer()->decrypt([&pki, &key_material](QCryptoBackend *backend) { - QByteArray kekPm = backend->deriveHMACExtract(key_material, KEKPREMASTER, KEY_LEN); + if (pki.pk_type == CKey::PKType::RSA) { + kek = qApp->signer()->decrypt([&key_material](QCryptoBackend *backend) { + return backend->decrypt(key_material, true); + }); + } else { + kek = qApp->signer()->decrypt([&pki, &key_material](QCryptoBackend *backend) { + QByteArray kekPm = backend->deriveHMACExtract(key_material, KEKPREMASTER, KEY_LEN); #ifndef NDEBUG - qDebug() << "kekPm" << kekPm.toHex(); + qDebug() << "kekPm" << kekPm.toHex(); #endif - QByteArray info = KEK + cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) + pki.rcpt_key + key_material; - return Crypto::expand(kekPm, info, KEY_LEN); - }); - } - } + QByteArray info = KEK + cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) + pki.rcpt_key + key_material; + return Crypto::expand(kekPm, info, KEY_LEN); + }); + } + } #ifndef NDEBUG - qDebug() << "KEK: " << kek.toHex(); + qDebug() << "KEK: " << kek.toHex(); #endif if(kek.isEmpty()) { setLastError(QStringLiteral("Failed to derive key")); return {}; } - QByteArray fmk = Crypto::xor_data(key.encrypted_fmk, kek); + QByteArray fmk = Crypto::xor_data(key.encrypted_fmk, kek); QByteArray hhk = Crypto::expand(fmk, HMAC); #ifndef NDEBUG qDebug() << "kek" << kek.toHex(); - qDebug() << "xor" << key.encrypted_fmk.toHex(); + qDebug() << "xor" << key.encrypted_fmk.toHex(); qDebug() << "fmk" << fmk.toHex(); qDebug() << "hhk" << hhk.toHex(); qDebug() << "hmac" << headerHMAC.toHex(); #endif - if(Crypto::sign_hmac(hhk, header_data) != headerHMAC) { - setLastError(QStringLiteral("CDoc 2.0 hash mismatch")); - return {}; - } - return fmk; + if(Crypto::sign_hmac(hhk, header_data) != headerHMAC) { + setLastError(QStringLiteral("CDoc 2.0 hash mismatch")); + return {}; + } + return fmk; } int CDoc2::version() diff --git a/client/CDoc2.h b/client/CDoc2.h index b6d82dcf..aae9cb9b 100644 --- a/client/CDoc2.h +++ b/client/CDoc2.h @@ -34,11 +34,11 @@ class CDoc2 final: public CDoc, private QObject /*, private QFile */ { bool decryptPayload(const QByteArray &fmk) final; QByteArray deriveFMK(const QByteArray &priv, const CKey &key); - bool save(const QString &path) final; - // Write payload encrypted with sinbgle symmetric key - static bool save(QString path, const std::vector& files, const QString& label, const QByteArray& secret, unsigned int kdf_iter); + bool save(const QString &path) final; + // Write payload encrypted with sinbgle symmetric key + static bool save(QString path, const std::vector& files, const QString& label, const QByteArray& secret, unsigned int kdf_iter); - QByteArray getFMK(const CKey &key, const QByteArray& secret) final; + QByteArray getFMK(const CKey &key, const QByteArray& secret) final; int version() final; static std::unique_ptr load(const QString& _path); diff --git a/client/Crypto.cpp b/client/Crypto.cpp index 64c4a118..86b9305c 100644 --- a/client/Crypto.cpp +++ b/client/Crypto.cpp @@ -83,7 +83,7 @@ bool Crypto::Cipher::result() const QByteArray Crypto::Cipher::tag() const { if(QByteArray result(tagLen(), 0); - !isError(EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_AEAD_GET_TAG, int(result.size()), result.data()))) + !isError(EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_AEAD_GET_TAG, int(result.size()), result.data()))) return result; return {}; } @@ -95,7 +95,7 @@ bool Crypto::Cipher::setTag(const QByteArray &data) const QByteArray Crypto::aes_wrap(const QByteArray &key, const QByteArray &data) { - Cipher c(key.size() == 32 ? EVP_aes_256_wrap() : EVP_aes_128_wrap(), key, {}, true); + Cipher c(key.size() == 32 ? EVP_aes_256_wrap() : EVP_aes_128_wrap(), key, {}, true); if(QByteArray result = c.update(data); c.result()) return result; return {}; @@ -103,10 +103,10 @@ QByteArray Crypto::aes_wrap(const QByteArray &key, const QByteArray &data) QByteArray Crypto::aes_unwrap(const QByteArray &key, const QByteArray &data) { - Cipher c(key.size() == 32 ? EVP_aes_256_wrap() : EVP_aes_128_wrap(), key, {}, false); - if(QByteArray result = c.update(data); c.result()) - return result; - return {}; + Cipher c(key.size() == 32 ? EVP_aes_256_wrap() : EVP_aes_128_wrap(), key, {}, false); + if(QByteArray result = c.update(data); c.result()) + return result; + return {}; } QByteArray Crypto::cipher(const EVP_CIPHER *cipher, const QByteArray &key, QByteArray &data, bool encrypt) @@ -202,9 +202,9 @@ QByteArray Crypto::derive(EVP_PKEY *priv, EVP_PKEY *pub) auto ctx = SCOPE(EVP_PKEY_CTX, EVP_PKEY_CTX_new(priv, nullptr)); size_t sharedSecretLen = 0; if(!ctx || - isError(EVP_PKEY_derive_init(ctx.get())) || - isError(EVP_PKEY_derive_set_peer(ctx.get(), pub)) || - isError(EVP_PKEY_derive(ctx.get(), nullptr, &sharedSecretLen))) + isError(EVP_PKEY_derive_init(ctx.get())) || + isError(EVP_PKEY_derive_set_peer(ctx.get(), pub)) || + isError(EVP_PKEY_derive(ctx.get(), nullptr, &sharedSecretLen))) return {}; QByteArray sharedSecret(int(sharedSecretLen), 0); if(isError(EVP_PKEY_derive(ctx.get(), puchar(sharedSecret.data()), &sharedSecretLen))) @@ -217,19 +217,19 @@ QByteArray Crypto::encrypt(EVP_PKEY *pub, int padding, const QByteArray &data) auto ctx = SCOPE(EVP_PKEY_CTX, EVP_PKEY_CTX_new(pub, nullptr)); size_t size = 0; if(isError(EVP_PKEY_encrypt_init(ctx.get())) || - isError(EVP_PKEY_CTX_set_rsa_padding(ctx.get(), padding)) || - isError(EVP_PKEY_encrypt(ctx.get(), nullptr, &size, - pcuchar(data.constData()), size_t(data.size())))) + isError(EVP_PKEY_CTX_set_rsa_padding(ctx.get(), padding)) || + isError(EVP_PKEY_encrypt(ctx.get(), nullptr, &size, + pcuchar(data.constData()), size_t(data.size())))) return {}; if(padding == RSA_PKCS1_OAEP_PADDING) { if(isError(EVP_PKEY_CTX_set_rsa_oaep_md(ctx.get(), EVP_sha256())) || - isError(EVP_PKEY_CTX_set_rsa_mgf1_md(ctx.get(), EVP_sha256()))) + isError(EVP_PKEY_CTX_set_rsa_mgf1_md(ctx.get(), EVP_sha256()))) return {}; } QByteArray result(int(size), 0); if(isError(EVP_PKEY_encrypt(ctx.get(), puchar(result.data()), &size, - pcuchar(data.constData()), size_t(data.size())))) + pcuchar(data.constData()), size_t(data.size())))) return {}; return result; } @@ -254,11 +254,11 @@ std::unique_ptr Crypto::fromECPublicKeyDer(const { EVP_PKEY *params = nullptr; if(auto ctx = SCOPE(EVP_PKEY_CTX, EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr)); - !ctx || - isError(EVP_PKEY_paramgen_init(ctx.get())) || - isError(EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx.get(), curveName)) || - isError(EVP_PKEY_CTX_set_ec_param_enc(ctx.get(), OPENSSL_EC_NAMED_CURVE)) || - isError(EVP_PKEY_paramgen(ctx.get(), ¶ms))) + !ctx || + isError(EVP_PKEY_paramgen_init(ctx.get())) || + isError(EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx.get(), curveName)) || + isError(EVP_PKEY_CTX_set_ec_param_enc(ctx.get(), OPENSSL_EC_NAMED_CURVE)) || + isError(EVP_PKEY_paramgen(ctx.get(), ¶ms))) return SCOPE(EVP_PKEY, nullptr); const auto *p = pcuchar(key.constData()); return SCOPE(EVP_PKEY, d2i_PublicKey(EVP_PKEY_EC, ¶ms, &p, long(key.length()))); @@ -276,8 +276,8 @@ std::unique_ptr Crypto::genECKey(EVP_PKEY *param auto ctx = SCOPE(EVP_PKEY_CTX, EVP_PKEY_CTX_new(params, nullptr)); auto result = SCOPE(EVP_PKEY, nullptr); if(ctx && - !isError(EVP_PKEY_keygen_init(ctx.get())) && - !isError(EVP_PKEY_keygen(ctx.get(), &key))) + !isError(EVP_PKEY_keygen_init(ctx.get())) && + !isError(EVP_PKEY_keygen(ctx.get(), &key))) result.reset(key); return result; } @@ -296,7 +296,7 @@ QByteArray Crypto::genKey(const EVP_CIPHER *cipher) RAND_bytes(salt.data(), int(salt.size())); RAND_bytes(indata.data(), int(indata.size())); if(isError(EVP_BytesToKey(cipher, EVP_sha256(), salt.data(), indata.data(), - int(indata.size()), 1, puchar(key.data()), puchar(iv.data())))) + int(indata.size()), 1, puchar(key.data()), puchar(iv.data())))) return {}; return key; } @@ -307,13 +307,13 @@ QByteArray Crypto::hkdf(const QByteArray &key, const QByteArray &salt, const QBy QByteArray out(len, 0); auto outlen = size_t(out.length()); if(!ctx || - isError(EVP_PKEY_derive_init(ctx.get())) || - isError(EVP_PKEY_CTX_hkdf_mode(ctx.get(), mode)) || - isError(EVP_PKEY_CTX_set_hkdf_md(ctx.get(), EVP_sha256())) || - isError(EVP_PKEY_CTX_set1_hkdf_key(ctx.get(), pcuchar(key.data()), int(key.size()))) || - isError(EVP_PKEY_CTX_set1_hkdf_salt(ctx.get(), pcuchar(salt.data()), int(salt.size()))) || - isError(EVP_PKEY_CTX_add1_hkdf_info(ctx.get(), pcuchar(info.data()), int(info.size()))) || - isError(EVP_PKEY_derive(ctx.get(), puchar(out.data()), &outlen))) + isError(EVP_PKEY_derive_init(ctx.get())) || + isError(EVP_PKEY_CTX_hkdf_mode(ctx.get(), mode)) || + isError(EVP_PKEY_CTX_set_hkdf_md(ctx.get(), EVP_sha256())) || + isError(EVP_PKEY_CTX_set1_hkdf_key(ctx.get(), pcuchar(key.data()), int(key.size()))) || + isError(EVP_PKEY_CTX_set1_hkdf_salt(ctx.get(), pcuchar(salt.data()), int(salt.size()))) || + isError(EVP_PKEY_CTX_add1_hkdf_info(ctx.get(), pcuchar(info.data()), int(info.size()))) || + isError(EVP_PKEY_derive(ctx.get(), puchar(out.data()), &outlen))) return {}; return out; } @@ -335,9 +335,9 @@ QByteArray Crypto::sign_hmac(const QByteArray &key, const QByteArray &data) size_t req = 0; auto ctx = SCOPE(EVP_MD_CTX, EVP_MD_CTX_new()); if(!ctx || - isError(EVP_DigestSignInit(ctx.get(), nullptr, EVP_sha256(), nullptr, pkey)) || - isError(EVP_DigestSignUpdate(ctx.get(), data.data(), size_t(data.length()))) || - isError(EVP_DigestSignFinal(ctx.get(), nullptr, &req))) + isError(EVP_DigestSignInit(ctx.get(), nullptr, EVP_sha256(), nullptr, pkey)) || + isError(EVP_DigestSignUpdate(ctx.get(), data.data(), size_t(data.length()))) || + isError(EVP_DigestSignFinal(ctx.get(), nullptr, &req))) return {}; QByteArray sig(int(req), 0); if(isError(EVP_DigestSignFinal(ctx.get(), puchar(sig.data()), &req))) @@ -384,10 +384,10 @@ QByteArray Crypto::xor_data(const QByteArray &a, const QByteArray &b) QByteArray Crypto::pbkdf2_sha256(const QByteArray& pw, const QByteArray& salt, uint32_t iter) { - unsigned char key[32]; - PKCS5_PBKDF2_HMAC(pw.data(), pw.length(), - (const unsigned char *) salt.data(), salt.length(), - iter, EVP_sha256(), 32, key); - return QByteArray((const char *) key, 32); + unsigned char key[32]; + PKCS5_PBKDF2_HMAC(pw.data(), pw.length(), + (const unsigned char *) salt.data(), salt.length(), + iter, EVP_sha256(), 32, key); + return QByteArray((const char *) key, 32); } diff --git a/client/Crypto.h b/client/Crypto.h index 021bac2a..f2a509f1 100644 --- a/client/Crypto.h +++ b/client/Crypto.h @@ -50,12 +50,12 @@ class Crypto bool setTag(const QByteArray &data) const; }; - static QByteArray aes_wrap(const QByteArray &key, const QByteArray &data); - static QByteArray aes_unwrap(const QByteArray &key, const QByteArray &data); - static QByteArray cipher(const EVP_CIPHER *cipher, const QByteArray &key, QByteArray &data, bool encrypt); + static QByteArray aes_wrap(const QByteArray &key, const QByteArray &data); + static QByteArray aes_unwrap(const QByteArray &key, const QByteArray &data); + static QByteArray cipher(const EVP_CIPHER *cipher, const QByteArray &key, QByteArray &data, bool encrypt); static QByteArray curve_oid(EVP_PKEY *key); static QByteArray concatKDF(QCryptographicHash::Algorithm digestMethod, - quint32 keyDataLen, const QByteArray &z, const QByteArray &otherInfo); + quint32 keyDataLen, const QByteArray &z, const QByteArray &otherInfo); static QByteArray derive(EVP_PKEY *priv, EVP_PKEY *pub); static QByteArray encrypt(EVP_PKEY *pub, int padding, const QByteArray &data); static QByteArray expand(const QByteArray &key, const QByteArray &info, int len = 32); @@ -72,7 +72,7 @@ class Crypto static QByteArray random(int len = 32); static QByteArray xor_data(const QByteArray &a, const QByteArray &b); - static QByteArray pbkdf2_sha256(const QByteArray& pw, const QByteArray& salt, uint32_t iter); + static QByteArray pbkdf2_sha256(const QByteArray& pw, const QByteArray& salt, uint32_t iter); private: static bool isError(int err); }; diff --git a/client/CryptoDoc.cpp b/client/CryptoDoc.cpp index a2aff0f6..7f1671be 100644 --- a/client/CryptoDoc.cpp +++ b/client/CryptoDoc.cpp @@ -63,12 +63,12 @@ class CryptoDoc::Private final: public QThread bool isEncrypted = false; CDocumentModel *documents = new CDocumentModel(this); QStringList tempFiles; - // Decryption data - QByteArray fmk; - // Encryption data - QString label; - QByteArray secret; - uint32_t kdf_iter; + // Decryption data + QByteArray fmk; + // Encryption data + QString label; + QByteArray secret; + uint32_t kdf_iter; }; bool CryptoDoc::Private::isEncryptedWarning() const @@ -90,16 +90,16 @@ void CryptoDoc::Private::run() else { qCDebug(CRYPTO) << "Encrypt" << fileName; - if (secret.isEmpty()) { - isEncrypted = cdoc->save(fileName); - } else { - isEncrypted = CDoc2::save(fileName, cdoc->files, label, secret, kdf_iter); - } + if (secret.isEmpty()) { + isEncrypted = cdoc->save(fileName); + } else { + isEncrypted = CDoc2::save(fileName, cdoc->files, label, secret, kdf_iter); + } } } CDocumentModel::CDocumentModel(CryptoDoc::Private *doc) -: d( doc ) + : d( doc ) {} bool CDocumentModel::addFile(const QString &file, const QString &mime) @@ -116,28 +116,28 @@ bool CDocumentModel::addFile(const QString &file, const QString &mime) if(d->cdoc->version() == 1 && info.size() > 120*1024*1024) { WarningDialog::show(tr("Added file(s) exceeds the maximum size limit of the container (∼120MB). " - "Read more about it")); + "Read more about it")); return false; } QString fileName(info.fileName()); if(std::any_of(d->cdoc->files.cbegin(), d->cdoc->files.cend(), - [&fileName](const auto &containerFile) { return containerFile.name == fileName; })) + [&fileName](const auto &containerFile) { return containerFile.name == fileName; })) { WarningDialog::show(DocumentModel::tr("Cannot add the file to the envelope. File '%1' is already in container.") - .arg(FileDialog::normalized(fileName))); + .arg(FileDialog::normalized(fileName))); return false; } auto data = std::make_unique(file); data->open(QFile::ReadOnly); d->cdoc->files.push_back({ - QFileInfo(file).fileName(), - QStringLiteral("D%1").arg(d->cdoc->files.size()), - mime, - data->size(), - std::move(data), - }); + QFileInfo(file).fileName(), + QStringLiteral("D%1").arg(d->cdoc->files.size()), + mime, + data->size(), + std::move(data), + }); emit added(FileDialog::normalized(d->cdoc->files.back().name)); return true; } @@ -229,38 +229,38 @@ QString CDocumentModel::save(int row, const QString &path) const bool CKey::isTheSameRecipient(const CKey& other) const { - QByteArray this_key, other_key; - if (this->isCertificate()) { - const CKeyCert& ckc = static_cast(*this); - this_key = ckc.cert.publicKey().toDer(); - } - if (other.isCertificate()) { - const CKeyCert& ckc = static_cast(other); - other_key = ckc.cert.publicKey().toDer(); - } - if (this_key.isEmpty() || other_key.isEmpty()) return false; - return this_key == other_key; + QByteArray this_key, other_key; + if (this->isCertificate()) { + const CKeyCert& ckc = static_cast(*this); + this_key = ckc.cert.publicKey().toDer(); + } + if (other.isCertificate()) { + const CKeyCert& ckc = static_cast(other); + other_key = ckc.cert.publicKey().toDer(); + } + if (this_key.isEmpty() || other_key.isEmpty()) return false; + return this_key == other_key; } bool CKey::isTheSameRecipient(const QSslCertificate &cert) const { - if (!isPKI()) return false; - const CKeyPKI& pki = static_cast(*this); - QByteArray this_key = pki.rcpt_key; - QSslKey k = cert.publicKey(); - QByteArray other_key = Crypto::toPublicKeyDer(k); - qDebug() << "This key:" << this_key.toHex(); - qDebug() << "Other key:" << other_key.toHex(); - if (this_key.isEmpty() || other_key.isEmpty()) return false; - return this_key == other_key; + if (!isPKI()) return false; + const CKeyPKI& pki = static_cast(*this); + QByteArray this_key = pki.rcpt_key; + QSslKey k = cert.publicKey(); + QByteArray other_key = Crypto::toPublicKeyDer(k); + qDebug() << "This key:" << this_key.toHex(); + qDebug() << "Other key:" << other_key.toHex(); + if (this_key.isEmpty() || other_key.isEmpty()) return false; + return this_key == other_key; } CKeyCert::CKeyCert(Type _type, const QSslCertificate &c) - : CKeyPKI(_type) + : CKeyPKI(_type) { setCert(c); - label = [](const SslCertificate &c) { + label = [](const SslCertificate &c) { QString cn = c.subjectInfo(QSslCertificate::CommonName); QString gn = c.subjectInfo("GN"); QString sn = c.subjectInfo("SN"); @@ -280,11 +280,11 @@ CKeyCert::CKeyCert(Type _type, const QSslCertificate &c) void CKeyCert::setCert(const QSslCertificate &c) { - cert = c; - QSslKey k = c.publicKey(); - rcpt_key = Crypto::toPublicKeyDer(k); - qDebug() << "Set cert PK:" << rcpt_key.toHex(); - pk_type = (k.algorithm() == QSsl::Rsa) ? PKType::RSA : PKType::ECC; + cert = c; + QSslKey k = c.publicKey(); + rcpt_key = Crypto::toPublicKeyDer(k); + qDebug() << "Set cert PK:" << rcpt_key.toHex(); + pk_type = (k.algorithm() == QSsl::Rsa) ? PKType::RSA : PKType::ECC; } std::shared_ptr @@ -297,7 +297,7 @@ CryptoDoc::CryptoDoc( QObject *parent ) , d(new Private) { const_cast(CRYPTO()).setEnabled(QtDebugMsg, - QFile::exists(QStringLiteral("%1/%2.log").arg(QDir::tempPath(), Application::applicationName()))); + QFile::exists(QStringLiteral("%1/%2.log").arg(QDir::tempPath(), Application::applicationName()))); } CryptoDoc::~CryptoDoc() { clear(); delete d; } @@ -305,7 +305,7 @@ CryptoDoc::~CryptoDoc() { clear(); delete d; } bool CryptoDoc::supportsSymmetricKeys() const { - return d->cdoc->version() >= 2; + return d->cdoc->version() >= 2; } bool CryptoDoc::addKey(std::shared_ptr key ) @@ -313,7 +313,7 @@ bool CryptoDoc::addKey(std::shared_ptr key ) if(d->isEncryptedWarning()) return false; for (std::shared_ptr k: d->cdoc->keys) { - if (k->isTheSameRecipient(*key)) { + if (k->isTheSameRecipient(*key)) { WarningDialog::show(tr("Key already exists")); return false; } @@ -324,8 +324,6 @@ bool CryptoDoc::addKey(std::shared_ptr key ) bool CryptoDoc::canDecrypt(const QSslCertificate &cert) { - //std::shared_ptr ckey = d->cdoc->getDecryptionKey(cert); - //return (ckey != nullptr) && !ckey->key.isEmpty(); CKey::DecryptionStatus dec_stat = d->cdoc->canDecrypt(cert); return (dec_stat == CKey::CAN_DECRYPT) || (dec_stat == CKey::DecryptionStatus::NEED_KEY); } @@ -362,19 +360,19 @@ bool CryptoDoc::decrypt(std::shared_ptr key, const QByteArray& secret) if(!d->isEncrypted) return true; - if (key == nullptr) { - key = d->cdoc->getDecryptionKey(qApp->signer()->tokenauth().cert()); - } - if((key == nullptr) || (key->isSymmetric() && secret.isEmpty())) { + if (key == nullptr) { + key = d->cdoc->getDecryptionKey(qApp->signer()->tokenauth().cert()); + } + if((key == nullptr) || (key->isSymmetric() && secret.isEmpty())) { WarningDialog::show(tr("You do not have the key to decrypt this document")); return false; } - if(d->cdoc->version() == 2 && (key->type == CKey::Type::SERVER) && !Settings::CDOC2_NOTIFICATION.isSet()) + if(d->cdoc->version() == 2 && (key->type == CKey::Type::SERVER) && !Settings::CDOC2_NOTIFICATION.isSet()) { auto *dlg = new WarningDialog(tr("You must enter your PIN code twice in order to decrypt the CDOC2 container. " - "The first PIN entry is required for authentication to the key server referenced in the CDOC2 container. " - "Second PIN entry is required to decrypt the CDOC2 container."), Application::mainWindow()); + "The first PIN entry is required for authentication to the key server referenced in the CDOC2 container. " + "Second PIN entry is required to decrypt the CDOC2 container."), Application::mainWindow()); dlg->setCancelText(WarningDialog::Cancel); dlg->addButton(WarningDialog::OK, QMessageBox::Ok); dlg->addButton(tr("DON'T SHOW AGAIN"), QMessageBox::Ignore); @@ -388,7 +386,7 @@ bool CryptoDoc::decrypt(std::shared_ptr key, const QByteArray& secret) } } - d->fmk = d->cdoc->getFMK(*key, secret); + d->fmk = d->cdoc->getFMK(*key, secret); #ifndef NDEBUG qDebug() << "FMK (Transport key)" << d->fmk.toHex(); #endif @@ -410,30 +408,30 @@ DocumentModel* CryptoDoc::documentModel() const { return d->documents; } bool CryptoDoc::encrypt( const QString &filename, const QString& label, const QByteArray& secret, uint32_t kdf_iter) { - if(!filename.isEmpty()) d->fileName = filename; - if(d->fileName.isEmpty()) { + if(!filename.isEmpty()) d->fileName = filename; + if(d->fileName.isEmpty()) { WarningDialog::show(tr("Container is not open")); return false; } - // I think the correct semantics is to fail if container is already encrypted - if(d->isEncrypted) return false; - if (secret.isEmpty()) { - // Encrypt for address list - if(d->cdoc->keys.isEmpty()) - { - WarningDialog::show(tr("No keys specified")); - return false; - } - } else { - // Encrypt with symmetric key - d->label = label; - d->secret = secret; - d->kdf_iter = kdf_iter; - } + // I think the correct semantics is to fail if container is already encrypted + if(d->isEncrypted) return false; + if (secret.isEmpty()) { + // Encrypt for address list + if(d->cdoc->keys.isEmpty()) + { + WarningDialog::show(tr("No keys specified")); + return false; + } + } else { + // Encrypt with symmetric key + d->label = label; + d->secret = secret; + d->kdf_iter = kdf_iter; + } d->waitForFinished(); - d->label.clear(); - d->secret.clear(); - if(d->isEncrypted) + d->label.clear(); + d->secret.clear(); + if(d->isEncrypted) open(d->fileName); else WarningDialog::show(tr("Failed to encrypt document. Please check your internet connection and network settings."), d->cdoc->lastError); diff --git a/client/CryptoDoc.h b/client/CryptoDoc.h index 405ce5f6..c92ab218 100644 --- a/client/CryptoDoc.h +++ b/client/CryptoDoc.h @@ -33,9 +33,9 @@ struct CKey { public: enum Type { - SYMMETRIC_KEY, - PUBLIC_KEY, - CERTIFICATE, + SYMMETRIC_KEY, + PUBLIC_KEY, + CERTIFICATE, CDOC1, SERVER }; @@ -52,26 +52,26 @@ struct CKey }; Type type; - QString label; + QString label; - // Decryption data - QByteArray encrypted_fmk; + // Decryption data + QByteArray encrypted_fmk; - // Recipients public key - // QByteArray key; + // Recipients public key + // QByteArray key; - bool isSymmetric() const { return type == Type::SYMMETRIC_KEY; } - bool isPKI() const { return (type == Type::CERTIFICATE) || (type == Type::CDOC1) || (type == Type::PUBLIC_KEY) || (type == Type::SERVER); } - bool isCertificate() const { return (type == Type::CERTIFICATE) || (type == Type::CDOC1); } - bool isCDoc1() const { return type == Type::CDOC1; } + bool isSymmetric() const { return type == Type::SYMMETRIC_KEY; } + bool isPKI() const { return (type == Type::CERTIFICATE) || (type == Type::CDOC1) || (type == Type::PUBLIC_KEY) || (type == Type::SERVER); } + bool isCertificate() const { return (type == Type::CERTIFICATE) || (type == Type::CDOC1); } + bool isCDoc1() const { return type == Type::CDOC1; } - bool isTheSameRecipient(const CKey &key) const; - bool isTheSameRecipient(const QSslCertificate &cert) const; + bool isTheSameRecipient(const CKey &key) const; + bool isTheSameRecipient(const QSslCertificate &cert) const; protected: - CKey(Type _type) : type(_type) {}; + CKey(Type _type) : type(_type) {}; private: - bool operator==(const CKey &other) const { return false; } + bool operator==(const CKey &other) const { return false; } }; // Symmetric key (plain or PBKDF) @@ -79,13 +79,13 @@ struct CKey // CDoc2:encrypt/decrypt LT struct CKeySymmetric : public CKey { - QByteArray salt; - // PBKDF - QByteArray pw_salt; - // 0 symmetric key, >0 password - int32_t kdf_iter; + QByteArray salt; + // PBKDF + QByteArray pw_salt; + // 0 symmetric key, >0 password + int32_t kdf_iter; - CKeySymmetric(const QByteArray& _salt) : CKey(Type::SYMMETRIC_KEY), salt(_salt), kdf_iter(0) {} + CKeySymmetric(const QByteArray& _salt) : CKey(Type::SYMMETRIC_KEY), salt(_salt), kdf_iter(0) {} }; // Base PKI key @@ -93,13 +93,13 @@ struct CKeySymmetric : public CKey { // CDoc2:encrypt struct CKeyPKI : public CKey { - // Recipient's public key - PKType pk_type; - QByteArray rcpt_key; + // Recipient's public key + PKType pk_type; + QByteArray rcpt_key; protected: - CKeyPKI(Type _type) : CKey(_type), pk_type(PKType::ECC) {}; - CKeyPKI(Type _type, PKType _pk_type, QByteArray _rcpt_key) : CKey(_type), pk_type(_pk_type), rcpt_key(_rcpt_key) {}; + CKeyPKI(Type _type) : CKey(_type), pk_type(PKType::ECC) {}; + CKeyPKI(Type _type, PKType _pk_type, QByteArray _rcpt_key) : CKey(_type), pk_type(_pk_type), rcpt_key(_rcpt_key) {}; }; @@ -108,15 +108,15 @@ struct CKeyPKI : public CKey { // CDoc1:encrypt struct CKeyCert : public CKeyPKI { - QSslCertificate cert; + QSslCertificate cert; - CKeyCert(const QSslCertificate &cert) : CKeyCert(CKey::Type::CERTIFICATE, cert) {}; + CKeyCert(const QSslCertificate &cert) : CKeyCert(CKey::Type::CERTIFICATE, cert) {}; - void setCert(const QSslCertificate &c); + void setCert(const QSslCertificate &c); protected: - CKeyCert(Type _type) : CKeyPKI(_type) {}; - CKeyCert(Type _type, const QSslCertificate &cert); + CKeyCert(Type _type) : CKeyPKI(_type) {}; + CKeyCert(Type _type, const QSslCertificate &cert); }; // CDoc1 decryption key (with additional information from file) @@ -125,11 +125,11 @@ struct CKeyCert : public CKeyPKI { struct CKeyCDoc1 : public CKeyCert { - QByteArray publicKey; - QString concatDigest, method; + QByteArray publicKey; + QString concatDigest, method; QByteArray AlgorithmID, PartyUInfo, PartyVInfo; - CKeyCDoc1() : CKeyCert(Type::CDOC1) {}; + CKeyCDoc1() : CKeyCert(Type::CDOC1) {}; }; // CDoc2 PKI key with key material @@ -137,10 +137,10 @@ struct CKeyCDoc1 : public CKeyCert { // CDoc2: decrypt struct CKeyPublicKey : public CKeyPKI { - // Either ECC public key or RSA encrypted kek - QByteArray key_material; + // Either ECC public key or RSA encrypted kek + QByteArray key_material; - CKeyPublicKey(PKType _pk_type, QByteArray _rcpt_key) : CKeyPKI(Type::PUBLIC_KEY, _pk_type, _rcpt_key) {}; + CKeyPublicKey(PKType _pk_type, QByteArray _rcpt_key) : CKeyPKI(Type::PUBLIC_KEY, _pk_type, _rcpt_key) {}; }; // CDoc2 PKI key with server info @@ -148,12 +148,12 @@ struct CKeyPublicKey : public CKeyPKI { // CDoc2: decrypt struct CKeyServer : public CKeyPKI { - // Server info - QString keyserver_id, transaction_id; + // Server info + QString keyserver_id, transaction_id; static std::shared_ptr fromKey(QByteArray _key, PKType _pk_type); protected: - CKeyServer(QByteArray _rcpt_key, PKType _pk_type) : CKeyPKI(Type::SERVER, _pk_type, _rcpt_key) {}; + CKeyServer(QByteArray _rcpt_key, PKType _pk_type) : CKeyPKI(Type::SERVER, _pk_type, _rcpt_key) {}; }; class CDoc @@ -173,7 +173,7 @@ class CDoc virtual bool decryptPayload(const QByteArray &fmk) = 0; virtual bool save(const QString &path) = 0; bool setLastError(const QString &msg) { return (lastError = msg).isEmpty(); } - virtual QByteArray getFMK(const CKey &key, const QByteArray& secret) = 0; + virtual QByteArray getFMK(const CKey &key, const QByteArray& secret) = 0; virtual int version() = 0; QList> keys; @@ -188,14 +188,14 @@ class CryptoDoc final: public QObject CryptoDoc(QObject *parent = nullptr); ~CryptoDoc() final; - bool supportsSymmetricKeys() const; + bool supportsSymmetricKeys() const; bool addKey(std::shared_ptr key ); bool canDecrypt(const QSslCertificate &cert); void clear(const QString &file = {}); - bool decrypt(std::shared_ptr key, const QByteArray& secret); - bool encrypt(const QString &filename = {}, const QString& label = {}, const QByteArray& secret = {}, uint32_t kdf_iter = 0); - DocumentModel* documentModel() const; - QString fileName() const; + bool decrypt(std::shared_ptr key, const QByteArray& secret); + bool encrypt(const QString &filename = {}, const QString& label = {}, const QByteArray& secret = {}, uint32_t kdf_iter = 0); + DocumentModel* documentModel() const; + QString fileName() const; QList> keys() const; bool move(const QString &to); bool open( const QString &file ); diff --git a/client/MainWindow.cpp b/client/MainWindow.cpp index 2e440820..dd8f31aa 100644 --- a/client/MainWindow.cpp +++ b/client/MainWindow.cpp @@ -160,7 +160,7 @@ MainWindow::MainWindow( QWidget *parent ) connect(ui->cryptoContainerPage, &ContainerPage::keysSelected, this, &MainWindow::updateKeys); connect(ui->cryptoContainerPage, &ContainerPage::removed, this, &MainWindow::removeAddress); - connect(ui->cryptoContainerPage, &ContainerPage::decryptReq, this, &MainWindow::decryptClicked); + connect(ui->cryptoContainerPage, &ContainerPage::decryptReq, this, &MainWindow::decryptClicked); connect(ui->accordion, &Accordion::changePin1Clicked, this, &MainWindow::changePin1Clicked); connect(ui->accordion, &Accordion::changePin2Clicked, this, &MainWindow::changePin2Clicked); @@ -267,31 +267,31 @@ ContainerState MainWindow::currentState() void MainWindow::decrypt(std::shared_ptr key) { - if(!cryptoDoc) return; - - QByteArray secret; - if (key && (key->type == CKey::Type::SYMMETRIC_KEY)) { - std::shared_ptr skey = std::static_pointer_cast(key); - PasswordDialog p; - p.setLabel(key->label); - if (skey->kdf_iter > 0) { - p.setMode(PasswordDialog::Mode::DECRYPT, PasswordDialog::Type::PASSWORD); - if(!p.exec()) return; - secret = p.secret(); - } else { - p.setMode(PasswordDialog::Mode::DECRYPT, PasswordDialog::Type::KEY); - if(!p.exec()) return; - secret = p.secret(); - } - } - - WaitDialogHolder waitDialog(this, tr("Decrypting")); - - if (cryptoDoc->decrypt(key, secret)) { - ui->cryptoContainerPage->transition(cryptoDoc, qApp->signer()->tokenauth().cert()); - auto *notification = new FadeInNotification(this, WHITE, MANTIS, 110); - notification->start( tr("Decryption succeeded!"), 750, 3000, 1200 ); - } + if(!cryptoDoc) return; + + QByteArray secret; + if (key && (key->type == CKey::Type::SYMMETRIC_KEY)) { + std::shared_ptr skey = std::static_pointer_cast(key); + PasswordDialog p; + p.setLabel(key->label); + if (skey->kdf_iter > 0) { + p.setMode(PasswordDialog::Mode::DECRYPT, PasswordDialog::Type::PASSWORD); + if(!p.exec()) return; + secret = p.secret(); + } else { + p.setMode(PasswordDialog::Mode::DECRYPT, PasswordDialog::Type::KEY); + if(!p.exec()) return; + secret = p.secret(); + } + } + + WaitDialogHolder waitDialog(this, tr("Decrypting")); + + if (cryptoDoc->decrypt(key, secret)) { + ui->cryptoContainerPage->transition(cryptoDoc, qApp->signer()->tokenauth().cert()); + auto *notification = new FadeInNotification(this, WHITE, MANTIS, 110); + notification->start( tr("Decryption succeeded!"), 750, 3000, 1200 ); + } } void MainWindow::dragEnterEvent(QDragEnterEvent *event) @@ -346,33 +346,33 @@ bool MainWindow::encrypt(bool askForKey) if(!FileDialog::fileIsWritable(cryptoDoc->fileName())) { auto *dlg = new WarningDialog(tr("Cannot alter container %1. Save different location?") - .arg(FileDialog::normalized(cryptoDoc->fileName())), this); + .arg(FileDialog::normalized(cryptoDoc->fileName())), this); dlg->addButton(WarningDialog::YES, QMessageBox::Yes); if(dlg->exec() == QMessageBox::Yes) { moveCryptoContainer(); - return encrypt(askForKey); + return encrypt(askForKey); } return false; } - if (askForKey) { - PasswordDialog p; - p.setMode(PasswordDialog::Mode::ENCRYPT, PasswordDialog::Type::PASSWORD); - if(!p.exec()) return false; - QString label = p.label(); - QByteArray secret = p.secret(); - bool result; - if (p.type == PasswordDialog::Type::PASSWORD) { - WaitDialogHolder waitDialog(this, tr("Encrypting")); - return cryptoDoc->encrypt(cryptoDoc->fileName(), label, secret, 65536); - } else { - WaitDialogHolder waitDialog(this, tr("Encrypting")); - return cryptoDoc->encrypt(cryptoDoc->fileName(), label, secret, 0); - } - } else { - WaitDialogHolder waitDialog(this, tr("Encrypting")); - return cryptoDoc->encrypt(); - } + if (askForKey) { + PasswordDialog p; + p.setMode(PasswordDialog::Mode::ENCRYPT, PasswordDialog::Type::PASSWORD); + if(!p.exec()) return false; + QString label = p.label(); + QByteArray secret = p.secret(); + bool result; + if (p.type == PasswordDialog::Type::PASSWORD) { + WaitDialogHolder waitDialog(this, tr("Encrypting")); + return cryptoDoc->encrypt(cryptoDoc->fileName(), label, secret, 65536); + } else { + WaitDialogHolder waitDialog(this, tr("Encrypting")); + return cryptoDoc->encrypt(cryptoDoc->fileName(), label, secret, 0); + } + } else { + WaitDialogHolder waitDialog(this, tr("Encrypting")); + return cryptoDoc->encrypt(); + } } void MainWindow::mouseReleaseEvent(QMouseEvent *event) @@ -469,14 +469,14 @@ void MainWindow::onSignAction(int action, const QString &info1, const QString &i sign([this, info1, info2](const QString &city, const QString &state, const QString &zip, const QString &country, const QString &role) { MobileProgress m(this); return m.init(info1, info2) && - digiDoc->sign(city, state, zip, country, role, &m); + digiDoc->sign(city, state, zip, country, role, &m); }); break; case SignatureSmartID: sign([this, info1, info2](const QString &city, const QString &state, const QString &zip, const QString &country, const QString &role) { SmartIDProgress s(this); return s.init(info1, info2, digiDoc->fileName()) && - digiDoc->sign(city, state, zip, country, role, &s); + digiDoc->sign(city, state, zip, country, role, &s); }); break; case ClearSignatureWarning: @@ -545,7 +545,7 @@ void MainWindow::convertToCDoc() auto cardData = qApp->signer()->tokenauth(); if(!cardData.cert().isNull()) - cryptoContainer->addKey(std::make_shared(cardData.cert())); + cryptoContainer->addKey(std::make_shared(cardData.cert())); resetCryptoDoc(cryptoContainer.release()); resetDigiDoc(nullptr, false); @@ -583,8 +583,8 @@ void MainWindow::onCryptoAction(int action, const QString &/*id*/, const QString convertToBDoc(); break; case DecryptContainer: - case DecryptToken: - decrypt(nullptr); + case DecryptToken: + decrypt(nullptr); break; case EncryptContainer: if(encrypt()) @@ -594,15 +594,15 @@ void MainWindow::onCryptoAction(int action, const QString &/*id*/, const QString notification->start( tr("Encryption succeeded!"), 750, 3000, 1200 ); } break; - case EncryptLT: - if(encrypt(true)) - { - ui->cryptoContainerPage->transition(cryptoDoc, qApp->signer()->tokenauth().cert()); - auto *notification = new FadeInNotification(this, WHITE, MANTIS, 110); - notification->start( tr("Encryption succeeded!"), 750, 3000, 1200 ); - } - break; - case ContainerSaveAs: + case EncryptLT: + if(encrypt(true)) + { + ui->cryptoContainerPage->transition(cryptoDoc, qApp->signer()->tokenauth().cert()); + auto *notification = new FadeInNotification(this, WHITE, MANTIS, 110); + notification->start( tr("Encryption succeeded!"), 750, 3000, 1200 ); + } + break; + case ContainerSaveAs: { if(!cryptoDoc) break; @@ -639,7 +639,7 @@ void MainWindow::onCryptoAction(int action, const QString &/*id*/, const QString void MainWindow::openFiles(const QStringList &files, bool addFile, bool forceCreate) { -/* + /* 1. If containers are not open: - If on myEid page and either crypto- or signature document, open corresponding view - If on encrypt page, open encryption view @@ -662,15 +662,15 @@ void MainWindow::openFiles(const QStringList &files, bool addFile, bool forceCre switch(state) { case Uninitialized: - // Case 1. + // Case 1. if(content.size() == 1) { auto fileType = FileDialog::detect(content[0]); if(current == MyEid) page = (fileType == FileDialog::CryptoDocument) ? CryptoDetails : SignDetails; create = forceCreate || ( - (fileType != FileDialog::CryptoDocument || page != CryptoDetails) && - (fileType != FileDialog::SignatureDocument || page != SignDetails)); + (fileType != FileDialog::CryptoDocument || page != CryptoDetails) && + (fileType != FileDialog::SignatureDocument || page != SignDetails)); } break; case ContainerState::UnsignedContainer: @@ -685,7 +685,7 @@ void MainWindow::openFiles(const QStringList &files, bool addFile, bool forceCre if(page != CryptoDetails && digiDoc->isPDF() && !wrap(digiDoc->fileName(), true)) return; DocumentModel* model = (current == CryptoDetails) ? - cryptoDoc->documentModel() : digiDoc->documentModel(); + cryptoDoc->documentModel() : digiDoc->documentModel(); for(const auto &file: content) model->addFile(file); selectPage(page); @@ -747,7 +747,7 @@ void MainWindow::openContainer(bool signature) QString filter = QFileDialog::tr("All Files (*)") + QStringLiteral(";;") + tr("Documents (%1)"); if(signature) filter = filter.arg(QStringLiteral("*.bdoc *.ddoc *.asice *.sce *.asics *.scs *.edoc *.adoc%1") - .arg(Application::confValue(Application::SiVaUrl).toString().isEmpty() ? QLatin1String() : QLatin1String(" *.pdf"))); + .arg(Application::confValue(Application::SiVaUrl).toString().isEmpty() ? QLatin1String() : QLatin1String(" *.pdf"))); else filter = filter.arg(QLatin1String("*.cdoc *.cdoc2")); QStringList files = FileDialog::getOpenFileNames(this, tr("Select documents"), {}, filter); @@ -901,7 +901,7 @@ void MainWindow::showEvent(QShowEvent * /*event*/) static const int height = 94; static const int width = 166; auto *notification = new FadeInNotification(this, WHITE, NONE, - QPoint(this->width() - width - 15, this->height() - height - 70), width, height); + QPoint(this->width() - width - 15, this->height() - height - 70), width, height); notification->setFocusPolicy(Qt::NoFocus); auto *structureFunds = new QSvgWidget(QStringLiteral(":/images/Struktuurifondid.svg"), notification); structureFunds->resize(width, height); @@ -1067,10 +1067,10 @@ bool MainWindow::validateFiles(const QString &container, const QStringList &file // Check that container is not dropped into itself QFileInfo containerInfo(container); if(std::none_of(files.cbegin(), files.cend(), - [containerInfo] (const QString &file) { return containerInfo == QFileInfo(file); })) + [containerInfo] (const QString &file) { return containerInfo == QFileInfo(file); })) return true; auto *dlg = new WarningDialog(tr("Cannot add container to same container\n%1") - .arg(FileDialog::normalized(container)), this); + .arg(FileDialog::normalized(container)), this); dlg->setCancelText(WarningDialog::Cancel); dlg->open(); return false; @@ -1113,8 +1113,8 @@ bool MainWindow::wrap(const QString& wrappedFile, bool enclose) bool MainWindow::wrapContainer(bool signing) { QString msg = signing ? - tr("Files can not be added to the signed container. The system will create a new container which shall contain the signed document and the files you wish to add.") : - tr("Files can not be added to the cryptocontainer. The system will create a new container which shall contain the cypto-document and the files you wish to add."); + tr("Files can not be added to the signed container. The system will create a new container which shall contain the signed document and the files you wish to add.") : + tr("Files can not be added to the cryptocontainer. The system will create a new container which shall contain the cypto-document and the files you wish to add."); auto *dlg = new WarningDialog(msg, this); dlg->setCancelText(WarningDialog::Cancel); dlg->addButton(tr("CONTINUE"), ContainerSave); @@ -1180,7 +1180,7 @@ void MainWindow::containerSummary() if( QPrinterInfo::availablePrinterNames().isEmpty() ) { WarningDialog::show(this, - tr("In order to view Validity Confirmation Sheet there has to be at least one printer installed!")); + tr("In order to view Validity Confirmation Sheet there has to be at least one printer installed!")); return; } #endif @@ -1198,7 +1198,7 @@ void MainWindow::containerSummary() void MainWindow::decryptClicked(std::shared_ptr key) { - qDebug() << "Decrypt clicked:"; - decrypt(key); + qDebug() << "Decrypt clicked:"; + decrypt(key); } diff --git a/client/MainWindow.h b/client/MainWindow.h index 42281fa6..ed15a0b7 100644 --- a/client/MainWindow.h +++ b/client/MainWindow.h @@ -72,9 +72,9 @@ private Q_SLOTS: void convertToBDoc(); void convertToCDoc(); ria::qdigidoc4::ContainerState currentState(); - void decrypt(std::shared_ptr key); - bool encrypt(bool askForKey = false); - void loadPicture(); + void decrypt(std::shared_ptr key); + bool encrypt(bool askForKey = false); + void loadPicture(); void moveCryptoContainer(); void moveSignatureContainer(); void navigateToPage( ria::qdigidoc4::Pages page, const QStringList &files = QStringList(), bool create = true ); @@ -121,5 +121,5 @@ private Q_SLOTS: Ui::MainWindow *ui; WarningList *warnings; - void decryptClicked(std::shared_ptr key); + void decryptClicked(std::shared_ptr key); }; diff --git a/client/dialogs/AddRecipients.cpp b/client/dialogs/AddRecipients.cpp index 3f116c6e..354bea47 100644 --- a/client/dialogs/AddRecipients.cpp +++ b/client/dialogs/AddRecipients.cpp @@ -113,7 +113,7 @@ void AddRecipients::addAllRecipientToRightPane() addRecipientToRightPane(value); std::shared_ptr key = value->getKey(); if (key->type == CKey::Type::CDOC1) { - std::shared_ptr kd = std::static_pointer_cast(key); + std::shared_ptr kd = std::static_pointer_cast(key); history.append(kd->cert); } } @@ -130,7 +130,7 @@ void AddRecipients::addRecipientFromCard() void AddRecipients::addRecipientFromFile() { QString file = FileDialog::getOpenFileName(this, windowTitle(), {}, - tr("Certificates (*.cer *.crt *.pem)") ); + tr("Certificates (*.cer *.crt *.pem)") ); if( file.isEmpty() ) return; @@ -152,7 +152,7 @@ void AddRecipients::addRecipientFromFile() WarningDialog::show(this, tr("Failed to read certificate")); } else if( !SslCertificate( cert ).keyUsage().contains( SslCertificate::KeyEncipherment ) && - !SslCertificate( cert ).keyUsage().contains( SslCertificate::KeyAgreement ) ) + !SslCertificate( cert ).keyUsage().contains( SslCertificate::KeyAgreement ) ) { WarningDialog::show(this, tr("This certificate cannot be used for encryption")); } @@ -175,13 +175,13 @@ AddressItem * AddRecipients::addRecipientToLeftPane(const QSslCertificate& cert) if(leftItem) return leftItem; - leftItem = new AddressItem(std::make_shared(cert), ui->leftPane); + leftItem = new AddressItem(std::make_shared(cert), ui->leftPane); leftList.insert(cert, leftItem); ui->leftPane->addWidget(leftItem); - bool contains = false; + bool contains = false; for (std::shared_ptr k: rightList) { - if (k->isTheSameRecipient(cert)) { + if (k->isTheSameRecipient(cert)) { contains = true; break; } @@ -202,13 +202,13 @@ AddressItem * AddRecipients::addRecipientToLeftPane(const QSslCertificate& cert) bool AddRecipients::addRecipientToRightPane(std::shared_ptr key, bool update) { for (std::shared_ptr k: rightList) { - if (k->isTheSameRecipient(*key)) return false; + if (k->isTheSameRecipient(*key)) return false; } if(update) { if (key->type == CKey::Type::CDOC1) { - std::shared_ptr kd = std::static_pointer_cast(key); + std::shared_ptr kd = std::static_pointer_cast(key); if(auto expiryDate = kd->cert.expiryDate(); expiryDate <= QDateTime::currentDateTime()) { if(Settings::CDOC2_DEFAULT && Settings::CDOC2_USE_KEYSERVER) @@ -217,8 +217,8 @@ bool AddRecipients::addRecipientToRightPane(std::shared_ptr key, bool upda return false; } auto *dlg = new WarningDialog(tr("Are you sure that you want use certificate for encrypting, which expired on %1?
" - "When decrypter has updated certificates then decrypting is impossible.") - .arg(expiryDate.toString(QStringLiteral("dd.MM.yyyy hh:mm:ss"))), this); + "When decrypter has updated certificates then decrypting is impossible.") + .arg(expiryDate.toString(QStringLiteral("dd.MM.yyyy hh:mm:ss"))), this); dlg->setCancelText(WarningDialog::NO); dlg->addButton(WarningDialog::YES, QMessageBox::Yes); if(dlg->exec() != QMessageBox::Yes) @@ -248,7 +248,7 @@ bool AddRecipients::addRecipientToRightPane(std::shared_ptr key, bool upda ui->rightPane->addWidget(rightItem); ui->confirm->setDisabled(rightList.isEmpty()); if (key->type == CKey::Type::CDOC1) { - std::shared_ptr kd = std::static_pointer_cast(key); + std::shared_ptr kd = std::static_pointer_cast(key); historyCertData.addAndSave({kd->cert}); } return true; @@ -306,7 +306,7 @@ void AddRecipients::removeRecipientFromRightPane(Item *toRemove) auto *rightItem = qobject_cast(toRemove); std::shared_ptr key = rightItem->getKey(); if (key->type == CKey::Type::CDOC1) { - std::shared_ptr kd = std::static_pointer_cast(key); + std::shared_ptr kd = std::static_pointer_cast(key); if(auto it = leftList.find(kd->cert); it != leftList.end()) { it.value()->setDisabled(false); @@ -329,16 +329,16 @@ void AddRecipients::search(const QString &term, bool select, const QString &type {QStringLiteral("select"), select} }; QString cleanTerm = term.simplified() -#ifdef Q_OS_WIN - .replace(QStringLiteral("\\"), QStringLiteral("\\5c")) - .replace(QStringLiteral("*"), QStringLiteral("\\2A")) - .replace(QStringLiteral("("), QStringLiteral("\\28")) - .replace(QStringLiteral(")"), QStringLiteral("\\29")); + #ifdef Q_OS_WIN + .replace(QStringLiteral("\\"), QStringLiteral("\\5c")) + .replace(QStringLiteral("*"), QStringLiteral("\\2A")) + .replace(QStringLiteral("("), QStringLiteral("\\28")) + .replace(QStringLiteral(")"), QStringLiteral("\\29")); #else - .replace(QStringLiteral("\\"), QStringLiteral("\\\\")) - .replace(QStringLiteral("*"), QStringLiteral("\\*")) - .replace(QStringLiteral("("), QStringLiteral("\\(")) - .replace(QStringLiteral(")"), QStringLiteral("\\)")); + .replace(QStringLiteral("\\"), QStringLiteral("\\\\")) + .replace(QStringLiteral("*"), QStringLiteral("\\*")) + .replace(QStringLiteral("("), QStringLiteral("\\(")) + .replace(QStringLiteral(")"), QStringLiteral("\\)")); #endif bool isDigit = false; void(cleanTerm.toULongLong(&isDigit)); @@ -378,14 +378,14 @@ void AddRecipients::showResult(const QList &result, int resultC SslCertificate c(k); if((c.keyUsage().contains(SslCertificate::KeyEncipherment) || c.keyUsage().contains(SslCertificate::KeyAgreement)) && - !c.enhancedKeyUsage().contains(SslCertificate::ServerAuth) && - (userData.value(QStringLiteral("personSearch"), false).toBool() || !c.enhancedKeyUsage().contains(SslCertificate::ClientAuth)) && - c.type() != SslCertificate::MobileIDType) + !c.enhancedKeyUsage().contains(SslCertificate::ServerAuth) && + (userData.value(QStringLiteral("personSearch"), false).toBool() || !c.enhancedKeyUsage().contains(SslCertificate::ClientAuth)) && + c.type() != SslCertificate::MobileIDType) { isEmpty = false; AddressItem *item = addRecipientToLeftPane(k); if(userData.value(QStringLiteral("select"), false).toBool() && - (userData.value(QStringLiteral("type")).isNull() || HistoryCertData::toType(SslCertificate(k)) == userData[QStringLiteral("type")])) + (userData.value(QStringLiteral("type")).isNull() || HistoryCertData::toType(SslCertificate(k)) == userData[QStringLiteral("type")])) addRecipientToRightPane(item, true); } } @@ -394,8 +394,8 @@ void AddRecipients::showResult(const QList &result, int resultC else if(isEmpty) { showError(tr("Person or company does not own a valid certificate.
" - "It is necessary to have a valid certificate for encryption.
" - "Read more about it.")); + "It is necessary to have a valid certificate for encryption.
" + "Read more about it.")); } QApplication::restoreOverrideCursor(); } diff --git a/client/dialogs/AddRecipients.h b/client/dialogs/AddRecipients.h index da597820..67ece87e 100644 --- a/client/dialogs/AddRecipients.h +++ b/client/dialogs/AddRecipients.h @@ -49,7 +49,7 @@ class AddRecipients final : public QDialog void addRecipientFromCard(); void addRecipientFromFile(); void addRecipientFromHistory(); - AddressItem * addRecipientToLeftPane(const QSslCertificate& cert); + AddressItem * addRecipientToLeftPane(const QSslCertificate& cert); bool addRecipientToRightPane(std::shared_ptr key, bool update = true); void addRecipientToRightPane(AddressItem *leftItem, bool update = true); void addSelectedCerts(const QList& selectedCertData); diff --git a/client/dialogs/KeyDialog.cpp b/client/dialogs/KeyDialog.cpp index fe4351cc..043e8514 100644 --- a/client/dialogs/KeyDialog.cpp +++ b/client/dialogs/KeyDialog.cpp @@ -71,20 +71,18 @@ KeyDialog::KeyDialog( const CKey &k, QWidget *parent ) bool adjust_size = false; if (k.type == CKey::Type::CDOC1) { const CKeyCDoc1& cd1key = static_cast(k); - addItem(tr("Recipient"), cd1key.label); + addItem(tr("Recipient"), cd1key.label); addItem(tr("Crypto method"), cd1key.method); - //7addItem(tr("Agreement method"), cd1key.agreement); - //addItem(tr("Key derivation method"), cd1key.derive); addItem(tr("ConcatKDF digest method"), cd1key.concatDigest); addItem(tr("Expiry date"), cd1key.cert.expiryDate().toLocalTime().toString(QStringLiteral("dd.MM.yyyy hh:mm:ss"))); addItem(tr("Issuer"), SslCertificate(cd1key.cert).issuerInfo(QSslCertificate::CommonName)); - adjust_size = !cd1key.concatDigest.isEmpty(); - } - addItem(tr("Label"), k.label); - if (k.type == CKey::SERVER) { - const CKeyServer& sk = static_cast(k); - addItem(tr("Key server ID"), sk.keyserver_id); - addItem(tr("Transaction ID"), sk.transaction_id); + adjust_size = !cd1key.concatDigest.isEmpty(); + } + addItem(tr("Label"), k.label); + if (k.type == CKey::SERVER) { + const CKeyServer& sk = static_cast(k); + addItem(tr("Key server ID"), sk.keyserver_id); + addItem(tr("Transaction ID"), sk.transaction_id); } d->view->resizeColumnToContents( 0 ); if(adjust_size) adjustSize(); diff --git a/client/dialogs/PasswordDialog.cpp b/client/dialogs/PasswordDialog.cpp index 9cbc5d3e..c34dc007 100644 --- a/client/dialogs/PasswordDialog.cpp +++ b/client/dialogs/PasswordDialog.cpp @@ -3,134 +3,134 @@ #include "ui_PasswordDialog.h" PasswordDialog::PasswordDialog(QWidget *parent) - : QDialog(parent), mode(Mode::DECRYPT), type(Type::PASSWORD) - , ui(new Ui::PasswordDialog) + : QDialog(parent), mode(Mode::DECRYPT), type(Type::PASSWORD) + , ui(new Ui::PasswordDialog) { - ui->setupUi(this); - connect(ui->generateKey, &QPushButton::clicked, this, &PasswordDialog::genKeyClicked); - connect(ui->typeSelector, &QTabWidget::currentChanged, this, &PasswordDialog::typeChanged); - connect(ui->passwordLine, &QLineEdit::textChanged, this, &PasswordDialog::lineChanged); - connect(ui->password2Line, &QLineEdit::textChanged, this, &PasswordDialog::lineChanged); - connect(ui->keyEdit, &QPlainTextEdit::textChanged, this, &PasswordDialog::editChanged); + ui->setupUi(this); + connect(ui->generateKey, &QPushButton::clicked, this, &PasswordDialog::genKeyClicked); + connect(ui->typeSelector, &QTabWidget::currentChanged, this, &PasswordDialog::typeChanged); + connect(ui->passwordLine, &QLineEdit::textChanged, this, &PasswordDialog::lineChanged); + connect(ui->password2Line, &QLineEdit::textChanged, this, &PasswordDialog::lineChanged); + connect(ui->keyEdit, &QPlainTextEdit::textChanged, this, &PasswordDialog::editChanged); } PasswordDialog::~PasswordDialog() { - delete ui; + delete ui; } void PasswordDialog::setMode(Mode _mode, Type _type) { - mode = _mode; - type = _type; - updateUI(); + mode = _mode; + type = _type; + updateUI(); } void PasswordDialog::setLabel(const QString& label) { - ui->labelLine->setText(label); + ui->labelLine->setText(label); } QString PasswordDialog::label() { - return ui->labelLine->text(); + return ui->labelLine->text(); } QByteArray PasswordDialog::secret() const { - if (type == Type::PASSWORD) { - return ui->passwordLine->text().toUtf8(); - } else { - QString hex = ui->keyEdit->toPlainText(); - return QByteArray::fromHex(hex.toUtf8()); - } + if (type == Type::PASSWORD) { + return ui->passwordLine->text().toUtf8(); + } else { + QString hex = ui->keyEdit->toPlainText(); + return QByteArray::fromHex(hex.toUtf8()); + } } void PasswordDialog::typeChanged(int index) { - Type new_type = static_cast(index); - if (new_type != type) setMode(mode, new_type); + Type new_type = static_cast(index); + if (new_type != type) setMode(mode, new_type); } void PasswordDialog::lineChanged(const QString& text) { - updateOK(); + updateOK(); } void PasswordDialog::editChanged() { - updateOK(); + updateOK(); } void PasswordDialog::genKeyClicked() { - QByteArray key = Crypto::random(); - ui->keyEdit->clear(); - ui->keyEdit->appendPlainText(key.toHex()); + QByteArray key = Crypto::random(); + ui->keyEdit->clear(); + ui->keyEdit->appendPlainText(key.toHex()); } void PasswordDialog::updateUI() { - ui->typeSelector->setCurrentIndex(type); - if (mode == Mode::DECRYPT) { - ui->labelLine->setReadOnly(true); - if (type == Type::PASSWORD) { - ui->typeSelector->setTabEnabled(Type::PASSWORD, true); - ui->typeSelector->setTabEnabled(Type::KEY, false); - ui->passwordLabel->setText("Enter password to decrypt the document"); - ui->passwordLine->setEchoMode(QLineEdit::EchoMode::Password); - ui->password2Label->hide(); - ui->password2Line->hide(); - } else { - ui->typeSelector->setTabEnabled(Type::PASSWORD, false); - ui->typeSelector->setTabEnabled(Type::KEY, true); - ui->keyLabel->setText("Enter key to decrypt the document"); - ui->generateKey->hide(); - } - } else { - ui->labelLine->setReadOnly(false); - if (type == Type::PASSWORD) { - ui->typeSelector->setTabEnabled(Type::PASSWORD, true); - ui->typeSelector->setTabEnabled(Type::KEY, true); - ui->passwordLabel->setText("Enter a password to encrypt the document"); - ui->passwordLine->setEchoMode(QLineEdit::EchoMode::Password); - ui->password2Label->show(); - ui->password2Line->show(); - } else { - ui->typeSelector->setTabEnabled(Type::PASSWORD, true); - ui->typeSelector->setTabEnabled(Type::KEY, true); - ui->keyLabel->setText("Enter a key to encrypt the document"); - ui->generateKey->show(); - } - } - updateOK(); + ui->typeSelector->setCurrentIndex(type); + if (mode == Mode::DECRYPT) { + ui->labelLine->setReadOnly(true); + if (type == Type::PASSWORD) { + ui->typeSelector->setTabEnabled(Type::PASSWORD, true); + ui->typeSelector->setTabEnabled(Type::KEY, false); + ui->passwordLabel->setText("Enter password to decrypt the document"); + ui->passwordLine->setEchoMode(QLineEdit::EchoMode::Password); + ui->password2Label->hide(); + ui->password2Line->hide(); + } else { + ui->typeSelector->setTabEnabled(Type::PASSWORD, false); + ui->typeSelector->setTabEnabled(Type::KEY, true); + ui->keyLabel->setText("Enter key to decrypt the document"); + ui->generateKey->hide(); + } + } else { + ui->labelLine->setReadOnly(false); + if (type == Type::PASSWORD) { + ui->typeSelector->setTabEnabled(Type::PASSWORD, true); + ui->typeSelector->setTabEnabled(Type::KEY, true); + ui->passwordLabel->setText("Enter a password to encrypt the document"); + ui->passwordLine->setEchoMode(QLineEdit::EchoMode::Password); + ui->password2Label->show(); + ui->password2Line->show(); + } else { + ui->typeSelector->setTabEnabled(Type::PASSWORD, true); + ui->typeSelector->setTabEnabled(Type::KEY, true); + ui->keyLabel->setText("Enter a key to encrypt the document"); + ui->generateKey->show(); + } + } + updateOK(); } void PasswordDialog::updateOK() { - bool active = false; - if (mode == Mode::DECRYPT) { - if (type == Type::PASSWORD) { - active = !ui->passwordLine->text().isEmpty(); - } else { - active = !ui->keyEdit->toPlainText().isEmpty(); - } - } else { - if (type == Type::PASSWORD) { - active = !ui->passwordLine->text().isEmpty() && ui->passwordLine->text() == ui->password2Line->text(); - } else { - active = !ui->keyEdit->toPlainText().isEmpty(); - } - } - ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(active); + bool active = false; + if (mode == Mode::DECRYPT) { + if (type == Type::PASSWORD) { + active = !ui->passwordLine->text().isEmpty(); + } else { + active = !ui->keyEdit->toPlainText().isEmpty(); + } + } else { + if (type == Type::PASSWORD) { + active = !ui->passwordLine->text().isEmpty() && ui->passwordLine->text() == ui->password2Line->text(); + } else { + active = !ui->keyEdit->toPlainText().isEmpty(); + } + } + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(active); } diff --git a/client/dialogs/PasswordDialog.h b/client/dialogs/PasswordDialog.h index b85639b3..451144c2 100644 --- a/client/dialogs/PasswordDialog.h +++ b/client/dialogs/PasswordDialog.h @@ -9,39 +9,39 @@ class PasswordDialog; class PasswordDialog : public QDialog { - Q_OBJECT + Q_OBJECT public: - enum Mode { - ENCRYPT, - DECRYPT - }; + enum Mode { + ENCRYPT, + DECRYPT + }; - enum Type { - PASSWORD, - KEY - }; + enum Type { + PASSWORD, + KEY + }; - Mode mode; - Type type; + Mode mode; + Type type; - explicit PasswordDialog(QWidget *parent = nullptr); - ~PasswordDialog(); + explicit PasswordDialog(QWidget *parent = nullptr); + ~PasswordDialog(); - void setMode(Mode mode, Type type); + void setMode(Mode mode, Type type); - void setLabel(const QString& label); - QString label(); - QByteArray secret() const; + void setLabel(const QString& label); + QString label(); + QByteArray secret() const; private: - Ui::PasswordDialog *ui; - - void typeChanged(int index); - void lineChanged(const QString& text); - void editChanged(); - void genKeyClicked(); - void updateUI(); - void updateOK(); + Ui::PasswordDialog *ui; + + void typeChanged(int index); + void lineChanged(const QString& text); + void editChanged(); + void genKeyClicked(); + void updateUI(); + void updateOK(); }; #endif // PASSWORDDIALOG_H diff --git a/client/widgets/AddressItem.cpp b/client/widgets/AddressItem.cpp index 57a9c30d..8fb248d2 100644 --- a/client/widgets/AddressItem.cpp +++ b/client/widgets/AddressItem.cpp @@ -43,7 +43,7 @@ AddressItem::AddressItem(std::shared_ptr key, QWidget *parent, bool showIc : Item(parent) , ui(new Private) { - ui->key = key; + ui->key = key; ui->setupUi(this); if(showIcon) ui->icon->load(QStringLiteral(":/images/icon_Krypto_small.svg")); @@ -54,34 +54,34 @@ AddressItem::AddressItem(std::shared_ptr key, QWidget *parent, bool showIc ui->idType->installEventFilter(this); ui->remove->setIcons(QStringLiteral("/images/icon_remove.svg"), QStringLiteral("/images/icon_remove_hover.svg"), - QStringLiteral("/images/icon_remove_pressed.svg"), 17, 17); + QStringLiteral("/images/icon_remove_pressed.svg"), 17, 17); ui->remove->init(LabelButton::White); connect(ui->add, &QToolButton::clicked, this, [this]{ emit add(this);}); - connect(ui->remove, &LabelButton::clicked, this, [this]{ emit remove(this);}); + connect(ui->remove, &LabelButton::clicked, this, [this]{ emit remove(this);}); - if (key->isSymmetric()) { - ui->decrypt->show(); - connect(ui->decrypt, &QToolButton::clicked, this, [this]{ emit decrypt(ui->key);}); - } else { - ui->decrypt->hide(); - } + if (key->isSymmetric()) { + ui->decrypt->show(); + connect(ui->decrypt, &QToolButton::clicked, this, [this]{ emit decrypt(ui->key);}); + } else { + ui->decrypt->hide(); + } ui->add->setFont(Styles::font(Styles::Condensed, 12)); ui->added->setFont(ui->add->font()); - if (ui->key->isCDoc1()) { - std::shared_ptr key = std::static_pointer_cast(ui->key); - ui->code = SslCertificate(key->cert).personalCode().toHtmlEscaped(); - ui->label = (!key->cert.subjectInfo("GN").isEmpty() && !key->cert.subjectInfo("SN").isEmpty() ? - key->cert.subjectInfo("GN").join(' ') + " " + key->cert.subjectInfo("SN").join(' ') : - key->cert.subjectInfo("CN").join(' ')).toHtmlEscaped(); + if (ui->key->isCDoc1()) { + std::shared_ptr keycd1 = std::static_pointer_cast(ui->key); + ui->code = SslCertificate(keycd1->cert).personalCode().toHtmlEscaped(); + ui->label = (!keycd1->cert.subjectInfo("GN").isEmpty() && !keycd1->cert.subjectInfo("SN").isEmpty() ? + keycd1->cert.subjectInfo("GN").join(' ') + " " + keycd1->cert.subjectInfo("SN").join(' ') : + keycd1->cert.subjectInfo("CN").join(' ')).toHtmlEscaped(); } else { - ui->code.clear(); - ui->label = key->label.toHtmlEscaped(); + ui->code.clear(); + ui->label = key->label.toHtmlEscaped(); } if(ui->label.isEmpty() && ui->key->type == CKey::PUBLIC_KEY) { - const CKeyPublicKey& pk = static_cast(*ui->key); - ui->label = pk.key_material; + const CKeyPublicKey& pk = static_cast(*ui->key); + ui->label = pk.key_material; } setIdType(); showButton(AddressItem::Remove); @@ -120,13 +120,13 @@ const std::shared_ptr AddressItem::getKey() const void AddressItem::idChanged(std::shared_ptr key) { - ui->yourself = key->isTheSameRecipient(*ui->key); + ui->yourself = key->isTheSameRecipient(*ui->key); setName(); } void AddressItem::idChanged(const SslCertificate &cert) { - idChanged(std::make_shared(cert)); + idChanged(std::make_shared(cert)); } void AddressItem::initTabOrder(QWidget *item) @@ -169,52 +169,52 @@ void AddressItem::stateChange(ContainerState state) void AddressItem::setIdType() { - if (ui->key->isPKI()) { - std::shared_ptr pki = std::static_pointer_cast(ui->key); - if (ui->key->isCertificate()) { - std::shared_ptr ckd = std::static_pointer_cast(ui->key); - ui->idType->setHidden(false); - QString str; - SslCertificate cert(ckd->cert); - SslCertificate::CertType type = cert.type(); - if(type & SslCertificate::DigiIDType) - str = tr("digi-ID"); - else if(type & SslCertificate::EstEidType) - str = tr("ID-card"); - else if(type & SslCertificate::MobileIDType) - str = tr("mobile-ID"); - else if(type & SslCertificate::TempelType) - { - if(cert.keyUsage().contains(SslCertificate::NonRepudiation)) - str = tr("e-Seal"); - else if(cert.enhancedKeyUsage().contains(SslCertificate::ClientAuth)) - str = tr("Authentication certificate"); - else - str = tr("Certificate for Encryption"); - } - - if(!str.isEmpty()) - str += QStringLiteral(" - "); - DateTime date(cert.expiryDate().toLocalTime()); - ui->idType->setText(QStringLiteral("%1%2 %3").arg(str, - cert.isValid() ? tr("Expires on") : tr("Expired on"), - date.formatDate(QStringLiteral("dd. MMMM yyyy")))); - } else { - QString type = (pki->pk_type == CKey::PKType::RSA) ? "RSA" : "ECC"; - ui->idType->setHidden(false); - ui->idType->setText(type + " public key"); - } - } else if (ui->key->isSymmetric()) { - std::shared_ptr ckd = std::static_pointer_cast(ui->key); - ui->idType->setHidden(false); - if (ckd->kdf_iter > 0) { - ui->idType->setText("Password derived key"); - } else { - ui->idType->setText("Symmetric key"); - } - } else { - ui->idType->setHidden(false); - ui->idType->setText("Unknown key type"); + if (ui->key->isPKI()) { + std::shared_ptr pki = std::static_pointer_cast(ui->key); + if (ui->key->isCertificate()) { + std::shared_ptr ckd = std::static_pointer_cast(ui->key); + ui->idType->setHidden(false); + QString str; + SslCertificate cert(ckd->cert); + SslCertificate::CertType type = cert.type(); + if(type & SslCertificate::DigiIDType) + str = tr("digi-ID"); + else if(type & SslCertificate::EstEidType) + str = tr("ID-card"); + else if(type & SslCertificate::MobileIDType) + str = tr("mobile-ID"); + else if(type & SslCertificate::TempelType) + { + if(cert.keyUsage().contains(SslCertificate::NonRepudiation)) + str = tr("e-Seal"); + else if(cert.enhancedKeyUsage().contains(SslCertificate::ClientAuth)) + str = tr("Authentication certificate"); + else + str = tr("Certificate for Encryption"); + } + + if(!str.isEmpty()) + str += QStringLiteral(" - "); + DateTime date(cert.expiryDate().toLocalTime()); + ui->idType->setText(QStringLiteral("%1%2 %3").arg(str, + cert.isValid() ? tr("Expires on") : tr("Expired on"), + date.formatDate(QStringLiteral("dd. MMMM yyyy")))); + } else { + QString type = (pki->pk_type == CKey::PKType::RSA) ? "RSA" : "ECC"; + ui->idType->setHidden(false); + ui->idType->setText(type + " public key"); + } + } else if (ui->key->isSymmetric()) { + std::shared_ptr ckd = std::static_pointer_cast(ui->key); + ui->idType->setHidden(false); + if (ckd->kdf_iter > 0) { + ui->idType->setText("Password derived key"); + } else { + ui->idType->setText("Symmetric key"); + } + } else { + ui->idType->setHidden(false); + ui->idType->setText("Unknown key type"); } } diff --git a/client/widgets/AddressItem.h b/client/widgets/AddressItem.h index 1f7b8e32..5fb3a8cc 100644 --- a/client/widgets/AddressItem.h +++ b/client/widgets/AddressItem.h @@ -50,7 +50,7 @@ class AddressItem final : public Item void stateChange(ria::qdigidoc4::ContainerState state) final; signals: - void decrypt(std::shared_ptr key); + void decrypt(std::shared_ptr key); private: void changeEvent(QEvent *event) final; diff --git a/client/widgets/ContainerPage.cpp b/client/widgets/ContainerPage.cpp index 6c06e130..19d84664 100644 --- a/client/widgets/ContainerPage.cpp +++ b/client/widgets/ContainerPage.cpp @@ -42,8 +42,8 @@ using namespace ria::qdigidoc4; ContainerPage::ContainerPage(QWidget *parent) -: QWidget(parent) -, ui(new Ui::ContainerPage) + : QWidget(parent) + , ui(new Ui::ContainerPage) { ui->setupUi( this ); ui->leftPane->init(fileName); @@ -52,7 +52,7 @@ ContainerPage::ContainerPage(QWidget *parent) ui->containerFile->setFont(Styles::font(Styles::Regular, 14)); ui->changeLocation->setIcons(QStringLiteral("/images/icon_Edit.svg"), - QStringLiteral("/images/icon_Edit_hover.svg"), QStringLiteral("/images/icon_Edit_pressed.svg"), 18, 18); + QStringLiteral("/images/icon_Edit_hover.svg"), QStringLiteral("/images/icon_Edit_pressed.svg"), 18, 18); ui->changeLocation->init(LabelButton::BoxedDeepCeruleanWithCuriousBlue, tr("CHANGE")); ui->containerFile->installEventFilter(this); ui->cancel->init(LabelButton::BoxedMojo, tr("CANCEL")); @@ -83,7 +83,7 @@ ContainerPage::ContainerPage(QWidget *parent) connect(ui->rightPane, &ItemList::addressSearch, this, &ContainerPage::addressSearch); connect(ui->rightPane, &ItemList::removed, this, &ContainerPage::removed); connect(ui->containerFile, &QLabel::linkActivated, this, [this](const QString &link) - { emit action(Actions::ContainerNavigate, link); }); + { emit action(Actions::ContainerNavigate, link); }); ui->summary->setVisible(Settings::SHOW_PRINT_SUMMARY); } @@ -127,12 +127,12 @@ bool ContainerPage::checkAction(int code, const QString& selectedCard, const QSt case SignatureMobile: case SignatureSmartID: if(ui->rightPane->hasItem( - [selectedCard, selectedMobile, code](Item* const item) -> bool - { - auto *signatureItem = qobject_cast(item); - return signatureItem && signatureItem->isSelfSigned(selectedCard, (code == SignatureMobile) ? selectedMobile: QString()); - } - )) + [selectedCard, selectedMobile, code](Item* const item) -> bool + { + auto *signatureItem = qobject_cast(item); + return signatureItem && signatureItem->isSelfSigned(selectedCard, (code == SignatureMobile) ? selectedMobile: QString()); + } + )) { auto *dlg = new WarningDialog(tr("The document has already been signed by you."), this); dlg->addButton(tr("CONTINUE SIGNING"), SignatureAdd); @@ -159,7 +159,7 @@ void ContainerPage::clearPopups() void ContainerPage::elideFileName() { ui->containerFile->setText(QStringLiteral("%1") - .arg(ui->containerFile->fontMetrics().elidedText(FileDialog::normalized(fileName).toHtmlEscaped(), Qt::ElideMiddle, ui->containerFile->width()))); + .arg(ui->containerFile->fontMetrics().elidedText(FileDialog::normalized(fileName).toHtmlEscaped(), Qt::ElideMiddle, ui->containerFile->width()))); } bool ContainerPage::eventFilter(QObject *o, QEvent *e) @@ -265,29 +265,29 @@ void ContainerPage::showMainAction(const QList &actions) bool isEncrypt = actions.contains(EncryptContainer) && !ui->rightPane->findChildren().isEmpty(); bool isDecrypt = actions.contains(DecryptContainer) || actions.contains(DecryptToken); mainAction->setButtonEnabled(isSupported && !hasEmptyFile && - (isEncrypt || isSignMobile || (!isBlocked && ((isSignCard && !isExpired) || isDecrypt)))); + (isEncrypt || isSignMobile || (!isBlocked && ((isSignCard && !isExpired) || isDecrypt)))); ui->mainActionSpacer->changeSize(198, 20, QSizePolicy::Fixed); ui->navigationArea->layout()->invalidate(); } void ContainerPage::showMainActionEncrypt(bool showLT) { - if(!mainAction) { - mainAction = std::make_unique(this); - connect(mainAction.get(), &MainAction::action, this, &ContainerPage::forward); - } - if (showLT) { - if (ui->rightPane->findChildren().isEmpty()) { - mainAction->showActions({ EncryptLT }); - } else { - mainAction->showActions({ EncryptContainer, EncryptLT }); - } - } else { - mainAction->showActions({ EncryptContainer }); - } - mainAction->setButtonEnabled(isSupported && !hasEmptyFile); - ui->mainActionSpacer->changeSize(198, 20, QSizePolicy::Fixed); - ui->navigationArea->layout()->invalidate(); + if(!mainAction) { + mainAction = std::make_unique(this); + connect(mainAction.get(), &MainAction::action, this, &ContainerPage::forward); + } + if (showLT) { + if (ui->rightPane->findChildren().isEmpty()) { + mainAction->showActions({ EncryptLT }); + } else { + mainAction->showActions({ EncryptContainer, EncryptLT }); + } + } else { + mainAction->showActions({ EncryptContainer }); + } + mainAction->setButtonEnabled(isSupported && !hasEmptyFile); + ui->mainActionSpacer->changeSize(198, 20, QSizePolicy::Fixed); + ui->navigationArea->layout()->invalidate(); } void ContainerPage::showSigningButton() @@ -316,13 +316,13 @@ void ContainerPage::transition(CryptoDoc *container, const QSslCertificate &cert } isSupported = (container->state() & UnencryptedContainer) || container->canDecrypt(cert); setHeader(container->fileName()); - for(std::shared_ptr& key: container->keys()) { - AddressItem *addr = new AddressItem(key, ui->rightPane, true); - connect(addr, &AddressItem::decrypt, this, [this,key]{emit decryptReq(key);}); - ui->rightPane->addWidget(addr); - } - ui->leftPane->setModel(container->documentModel()); - updatePanes(container->state(), container); + for(std::shared_ptr& key: container->keys()) { + AddressItem *addr = new AddressItem(key, ui->rightPane, true); + connect(addr, &AddressItem::decrypt, this, [this,key]{emit decryptReq(key);}); + ui->rightPane->addWidget(addr); + } + ui->leftPane->setModel(container->documentModel()); + updatePanes(container->state(), container); } void ContainerPage::transition(DigiDoc* container) @@ -380,7 +380,7 @@ void ContainerPage::transition(DigiDoc* container) showSigningButton(); ui->leftPane->setModel(container->documentModel()); - updatePanes(container->state(), nullptr); + updatePanes(container->state(), nullptr); } void ContainerPage::update(bool canDecrypt, CryptoDoc* container) @@ -393,14 +393,14 @@ void ContainerPage::update(bool canDecrypt, CryptoDoc* container) return; hasEmptyFile = false; - ui->rightPane->clear(); - for(std::shared_ptr& key: container->keys()) { - AddressItem *addr = new AddressItem(key, ui->rightPane, true); - connect(addr, &AddressItem::decrypt, this, [this,key]{emit decryptReq(key);}); - ui->rightPane->addWidget(addr); - } + ui->rightPane->clear(); + for(std::shared_ptr& key: container->keys()) { + AddressItem *addr = new AddressItem(key, ui->rightPane, true); + connect(addr, &AddressItem::decrypt, this, [this,key]{emit decryptReq(key);}); + ui->rightPane->addWidget(addr); + } if(container->state() & UnencryptedContainer) - showMainActionEncrypt(container->supportsSymmetricKeys()); + showMainActionEncrypt(container->supportsSymmetricKeys()); } void ContainerPage::updateDecryptionButton() @@ -410,14 +410,14 @@ void ContainerPage::updateDecryptionButton() void ContainerPage::updatePanes(ria::qdigidoc4::ContainerState state, CryptoDoc *crypto_container) { - ui->leftPane->stateChange(state); - ui->rightPane->stateChange(state); + ui->leftPane->stateChange(state); + ui->rightPane->stateChange(state); bool showPrintSummary = Settings::SHOW_PRINT_SUMMARY; auto setButtonsVisible = [](const QVector &buttons, bool visible) { for(QWidget *button: buttons) button->setVisible(visible); }; - switch(state) + switch(state) { case UnsignedContainer: cancelText = QT_TR_NOOP("CANCEL"); @@ -428,7 +428,7 @@ void ContainerPage::updatePanes(ria::qdigidoc4::ContainerState state, CryptoDoc ui->leftPane->init(fileName, QT_TRANSLATE_NOOP("ItemList", "Container files")); showSigningButton(); setButtonsVisible({ ui->cancel, ui->convert, ui->save }, true); - setButtonsVisible({ ui->saveAs, ui->email, ui->summary }, false); + setButtonsVisible({ ui->saveAs, ui->email, ui->summary }, false); break; case UnsignedSavedContainer: cancelText = QT_TR_NOOP("STARTING"); @@ -439,7 +439,7 @@ void ContainerPage::updatePanes(ria::qdigidoc4::ContainerState state, CryptoDoc setButtonsVisible({ ui->cancel, ui->convert, ui->saveAs, ui->email, ui->summary }, true); else setButtonsVisible({ ui->cancel, ui->convert, ui->saveAs, ui->email }, true); - setButtonsVisible({ ui->save }, false); + setButtonsVisible({ ui->save }, false); showRightPane( ItemSignature, QT_TRANSLATE_NOOP("ItemList", "Container is not signed")); break; case SignedContainer: @@ -452,7 +452,7 @@ void ContainerPage::updatePanes(ria::qdigidoc4::ContainerState state, CryptoDoc setButtonsVisible({ ui->cancel, ui->convert, ui->saveAs, ui->email, ui->summary }, true); else setButtonsVisible({ ui->cancel, ui->convert, ui->saveAs, ui->email }, true); - setButtonsVisible({ ui->save }, false); + setButtonsVisible({ ui->save }, false); break; case UnencryptedContainer: cancelText = QT_TR_NOOP("STARTING"); @@ -461,8 +461,8 @@ void ContainerPage::updatePanes(ria::qdigidoc4::ContainerState state, CryptoDoc ui->changeLocation->show(); ui->leftPane->init(fileName); showRightPane(ItemAddress, QT_TRANSLATE_NOOP("ItemList", "Recipients")); - showMainActionEncrypt(crypto_container && crypto_container->supportsSymmetricKeys()); - setButtonsVisible({ ui->cancel, ui->convert }, true); + showMainActionEncrypt(crypto_container && crypto_container->supportsSymmetricKeys()); + setButtonsVisible({ ui->cancel, ui->convert }, true); setButtonsVisible({ ui->save, ui->saveAs, ui->email, ui->summary }, false); break; case EncryptedContainer: @@ -473,8 +473,8 @@ void ContainerPage::updatePanes(ria::qdigidoc4::ContainerState state, CryptoDoc ui->leftPane->init(fileName, QT_TRANSLATE_NOOP("ItemList", "Encrypted files")); showRightPane(ItemAddress, QT_TRANSLATE_NOOP("ItemList", "Recipients")); updateDecryptionButton(); - setButtonsVisible({ ui->save, ui->summary }, false); - setButtonsVisible({ ui->cancel, ui->convert, ui->saveAs, ui->email }, true); + setButtonsVisible({ ui->save, ui->summary }, false); + setButtonsVisible({ ui->cancel, ui->convert, ui->saveAs, ui->email }, true); break; default: // Uninitialized cannot be shown on container page diff --git a/client/widgets/ContainerPage.h b/client/widgets/ContainerPage.h index 13827a1d..248c19f1 100644 --- a/client/widgets/ContainerPage.h +++ b/client/widgets/ContainerPage.h @@ -63,7 +63,7 @@ class ContainerPage final : public QWidget void removed(int row); void warning(const WarningText &warningText); - void decryptReq(std::shared_ptr key); + void decryptReq(std::shared_ptr key); private: void addressSearch(); @@ -74,14 +74,14 @@ class ContainerPage final : public QWidget void forward(int code); void hideRightPane(); void showMainAction(const QList &actions); - void showMainActionEncrypt(bool showLT); - void showRightPane(ria::qdigidoc4::ItemType itemType, const char *header); + void showMainActionEncrypt(bool showLT); + void showRightPane(ria::qdigidoc4::ItemType itemType, const char *header); void showSigningButton(); - void updateDecryptionButton(); - void updatePanes(ria::qdigidoc4::ContainerState state, CryptoDoc *crypto_container); + void updateDecryptionButton(); + void updatePanes(ria::qdigidoc4::ContainerState state, CryptoDoc *crypto_container); void translateLabels(); - Ui::ContainerPage *ui; + Ui::ContainerPage *ui; std::unique_ptr mainAction; QString cardInReader; QString fileName; diff --git a/client/widgets/MainAction.cpp b/client/widgets/MainAction.cpp index fcb30868..a4c2e556 100644 --- a/client/widgets/MainAction.cpp +++ b/client/widgets/MainAction.cpp @@ -135,8 +135,8 @@ QString MainAction::label(Actions action) case SignatureSmartID: return tr("SignatureSmartID"); case SignatureToken: return tr("SignatureToken"); case EncryptContainer: return tr("EncryptContainer"); - case EncryptLT: return tr("EncryptLongTerm"); - case DecryptContainer: return tr("DecryptContainer"); + case EncryptLT: return tr("EncryptLongTerm"); + case DecryptContainer: return tr("DecryptContainer"); case DecryptToken: return tr("DECRYPT"); default: return tr("SignatureAdd"); } @@ -151,10 +151,10 @@ void MainAction::showActions(const QList &actions) { QList order = actions; if(order.size() == 2 && - std::all_of(order.cbegin(), order.cend(), [] (Actions action) { - return action == SignatureMobile || action == SignatureSmartID; - }) && - !Settings::MOBILEID_ORDER) + std::all_of(order.cbegin(), order.cend(), [] (Actions action) { + return action == SignatureMobile || action == SignatureSmartID; + }) && + !Settings::MOBILEID_ORDER) { std::reverse(order.begin(), order.end()); } @@ -178,7 +178,7 @@ void MainAction::showDropdown() other->move(pos() + QPoint(0, (-height() - 1) * (ui->list.size() + 1))); other->show(); other->setStyleSheet(ui->mainAction->styleSheet() + - QStringLiteral("\nborder-top-left-radius: 2px; border-top-right-radius: 2px;")); + QStringLiteral("\nborder-top-left-radius: 2px; border-top-right-radius: 2px;")); if (*i == Actions::SignatureMobile) connect(other, &QPushButton::clicked, this, []{ Settings::MOBILEID_ORDER = true; }); if (*i == Actions::SignatureSmartID) From 1347dfbdd67fba32dbace2be28639ba7a388441c Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Fri, 17 May 2024 14:46:39 +0300 Subject: [PATCH 14/22] Reverted some indentation changes for clarity --- client/Crypto.cpp | 54 ++++++++++++++++---------------- client/CryptoDoc.cpp | 24 +++++++------- client/MainWindow.cpp | 24 +++++++------- client/dialogs/AddRecipients.cpp | 36 ++++++++++----------- client/widgets/ContainerPage.cpp | 24 +++++++------- client/widgets/MainAction.cpp | 10 +++--- 6 files changed, 86 insertions(+), 86 deletions(-) diff --git a/client/Crypto.cpp b/client/Crypto.cpp index 86b9305c..522580a0 100644 --- a/client/Crypto.cpp +++ b/client/Crypto.cpp @@ -83,7 +83,7 @@ bool Crypto::Cipher::result() const QByteArray Crypto::Cipher::tag() const { if(QByteArray result(tagLen(), 0); - !isError(EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_AEAD_GET_TAG, int(result.size()), result.data()))) + !isError(EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_AEAD_GET_TAG, int(result.size()), result.data()))) return result; return {}; } @@ -202,9 +202,9 @@ QByteArray Crypto::derive(EVP_PKEY *priv, EVP_PKEY *pub) auto ctx = SCOPE(EVP_PKEY_CTX, EVP_PKEY_CTX_new(priv, nullptr)); size_t sharedSecretLen = 0; if(!ctx || - isError(EVP_PKEY_derive_init(ctx.get())) || - isError(EVP_PKEY_derive_set_peer(ctx.get(), pub)) || - isError(EVP_PKEY_derive(ctx.get(), nullptr, &sharedSecretLen))) + isError(EVP_PKEY_derive_init(ctx.get())) || + isError(EVP_PKEY_derive_set_peer(ctx.get(), pub)) || + isError(EVP_PKEY_derive(ctx.get(), nullptr, &sharedSecretLen))) return {}; QByteArray sharedSecret(int(sharedSecretLen), 0); if(isError(EVP_PKEY_derive(ctx.get(), puchar(sharedSecret.data()), &sharedSecretLen))) @@ -217,19 +217,19 @@ QByteArray Crypto::encrypt(EVP_PKEY *pub, int padding, const QByteArray &data) auto ctx = SCOPE(EVP_PKEY_CTX, EVP_PKEY_CTX_new(pub, nullptr)); size_t size = 0; if(isError(EVP_PKEY_encrypt_init(ctx.get())) || - isError(EVP_PKEY_CTX_set_rsa_padding(ctx.get(), padding)) || - isError(EVP_PKEY_encrypt(ctx.get(), nullptr, &size, - pcuchar(data.constData()), size_t(data.size())))) + isError(EVP_PKEY_CTX_set_rsa_padding(ctx.get(), padding)) || + isError(EVP_PKEY_encrypt(ctx.get(), nullptr, &size, + pcuchar(data.constData()), size_t(data.size())))) return {}; if(padding == RSA_PKCS1_OAEP_PADDING) { if(isError(EVP_PKEY_CTX_set_rsa_oaep_md(ctx.get(), EVP_sha256())) || - isError(EVP_PKEY_CTX_set_rsa_mgf1_md(ctx.get(), EVP_sha256()))) + isError(EVP_PKEY_CTX_set_rsa_mgf1_md(ctx.get(), EVP_sha256()))) return {}; } QByteArray result(int(size), 0); if(isError(EVP_PKEY_encrypt(ctx.get(), puchar(result.data()), &size, - pcuchar(data.constData()), size_t(data.size())))) + pcuchar(data.constData()), size_t(data.size())))) return {}; return result; } @@ -254,11 +254,11 @@ std::unique_ptr Crypto::fromECPublicKeyDer(const { EVP_PKEY *params = nullptr; if(auto ctx = SCOPE(EVP_PKEY_CTX, EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr)); - !ctx || - isError(EVP_PKEY_paramgen_init(ctx.get())) || - isError(EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx.get(), curveName)) || - isError(EVP_PKEY_CTX_set_ec_param_enc(ctx.get(), OPENSSL_EC_NAMED_CURVE)) || - isError(EVP_PKEY_paramgen(ctx.get(), ¶ms))) + !ctx || + isError(EVP_PKEY_paramgen_init(ctx.get())) || + isError(EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx.get(), curveName)) || + isError(EVP_PKEY_CTX_set_ec_param_enc(ctx.get(), OPENSSL_EC_NAMED_CURVE)) || + isError(EVP_PKEY_paramgen(ctx.get(), ¶ms))) return SCOPE(EVP_PKEY, nullptr); const auto *p = pcuchar(key.constData()); return SCOPE(EVP_PKEY, d2i_PublicKey(EVP_PKEY_EC, ¶ms, &p, long(key.length()))); @@ -276,8 +276,8 @@ std::unique_ptr Crypto::genECKey(EVP_PKEY *param auto ctx = SCOPE(EVP_PKEY_CTX, EVP_PKEY_CTX_new(params, nullptr)); auto result = SCOPE(EVP_PKEY, nullptr); if(ctx && - !isError(EVP_PKEY_keygen_init(ctx.get())) && - !isError(EVP_PKEY_keygen(ctx.get(), &key))) + !isError(EVP_PKEY_keygen_init(ctx.get())) && + !isError(EVP_PKEY_keygen(ctx.get(), &key))) result.reset(key); return result; } @@ -296,7 +296,7 @@ QByteArray Crypto::genKey(const EVP_CIPHER *cipher) RAND_bytes(salt.data(), int(salt.size())); RAND_bytes(indata.data(), int(indata.size())); if(isError(EVP_BytesToKey(cipher, EVP_sha256(), salt.data(), indata.data(), - int(indata.size()), 1, puchar(key.data()), puchar(iv.data())))) + int(indata.size()), 1, puchar(key.data()), puchar(iv.data())))) return {}; return key; } @@ -307,13 +307,13 @@ QByteArray Crypto::hkdf(const QByteArray &key, const QByteArray &salt, const QBy QByteArray out(len, 0); auto outlen = size_t(out.length()); if(!ctx || - isError(EVP_PKEY_derive_init(ctx.get())) || - isError(EVP_PKEY_CTX_hkdf_mode(ctx.get(), mode)) || - isError(EVP_PKEY_CTX_set_hkdf_md(ctx.get(), EVP_sha256())) || - isError(EVP_PKEY_CTX_set1_hkdf_key(ctx.get(), pcuchar(key.data()), int(key.size()))) || - isError(EVP_PKEY_CTX_set1_hkdf_salt(ctx.get(), pcuchar(salt.data()), int(salt.size()))) || - isError(EVP_PKEY_CTX_add1_hkdf_info(ctx.get(), pcuchar(info.data()), int(info.size()))) || - isError(EVP_PKEY_derive(ctx.get(), puchar(out.data()), &outlen))) + isError(EVP_PKEY_derive_init(ctx.get())) || + isError(EVP_PKEY_CTX_hkdf_mode(ctx.get(), mode)) || + isError(EVP_PKEY_CTX_set_hkdf_md(ctx.get(), EVP_sha256())) || + isError(EVP_PKEY_CTX_set1_hkdf_key(ctx.get(), pcuchar(key.data()), int(key.size()))) || + isError(EVP_PKEY_CTX_set1_hkdf_salt(ctx.get(), pcuchar(salt.data()), int(salt.size()))) || + isError(EVP_PKEY_CTX_add1_hkdf_info(ctx.get(), pcuchar(info.data()), int(info.size()))) || + isError(EVP_PKEY_derive(ctx.get(), puchar(out.data()), &outlen))) return {}; return out; } @@ -335,9 +335,9 @@ QByteArray Crypto::sign_hmac(const QByteArray &key, const QByteArray &data) size_t req = 0; auto ctx = SCOPE(EVP_MD_CTX, EVP_MD_CTX_new()); if(!ctx || - isError(EVP_DigestSignInit(ctx.get(), nullptr, EVP_sha256(), nullptr, pkey)) || - isError(EVP_DigestSignUpdate(ctx.get(), data.data(), size_t(data.length()))) || - isError(EVP_DigestSignFinal(ctx.get(), nullptr, &req))) + isError(EVP_DigestSignInit(ctx.get(), nullptr, EVP_sha256(), nullptr, pkey)) || + isError(EVP_DigestSignUpdate(ctx.get(), data.data(), size_t(data.length()))) || + isError(EVP_DigestSignFinal(ctx.get(), nullptr, &req))) return {}; QByteArray sig(int(req), 0); if(isError(EVP_DigestSignFinal(ctx.get(), puchar(sig.data()), &req))) diff --git a/client/CryptoDoc.cpp b/client/CryptoDoc.cpp index 7f1671be..36dbcf88 100644 --- a/client/CryptoDoc.cpp +++ b/client/CryptoDoc.cpp @@ -99,7 +99,7 @@ void CryptoDoc::Private::run() } CDocumentModel::CDocumentModel(CryptoDoc::Private *doc) - : d( doc ) +: d( doc ) {} bool CDocumentModel::addFile(const QString &file, const QString &mime) @@ -116,13 +116,13 @@ bool CDocumentModel::addFile(const QString &file, const QString &mime) if(d->cdoc->version() == 1 && info.size() > 120*1024*1024) { WarningDialog::show(tr("Added file(s) exceeds the maximum size limit of the container (∼120MB). " - "Read more about it")); + "Read more about it")); return false; } QString fileName(info.fileName()); if(std::any_of(d->cdoc->files.cbegin(), d->cdoc->files.cend(), - [&fileName](const auto &containerFile) { return containerFile.name == fileName; })) + [&fileName](const auto &containerFile) { return containerFile.name == fileName; })) { WarningDialog::show(DocumentModel::tr("Cannot add the file to the envelope. File '%1' is already in container.") .arg(FileDialog::normalized(fileName))); @@ -132,12 +132,12 @@ bool CDocumentModel::addFile(const QString &file, const QString &mime) auto data = std::make_unique(file); data->open(QFile::ReadOnly); d->cdoc->files.push_back({ - QFileInfo(file).fileName(), - QStringLiteral("D%1").arg(d->cdoc->files.size()), - mime, - data->size(), - std::move(data), - }); + QFileInfo(file).fileName(), + QStringLiteral("D%1").arg(d->cdoc->files.size()), + mime, + data->size(), + std::move(data), + }); emit added(FileDialog::normalized(d->cdoc->files.back().name)); return true; } @@ -297,7 +297,7 @@ CryptoDoc::CryptoDoc( QObject *parent ) , d(new Private) { const_cast(CRYPTO()).setEnabled(QtDebugMsg, - QFile::exists(QStringLiteral("%1/%2.log").arg(QDir::tempPath(), Application::applicationName()))); + QFile::exists(QStringLiteral("%1/%2.log").arg(QDir::tempPath(), Application::applicationName()))); } CryptoDoc::~CryptoDoc() { clear(); delete d; } @@ -371,8 +371,8 @@ bool CryptoDoc::decrypt(std::shared_ptr key, const QByteArray& secret) if(d->cdoc->version() == 2 && (key->type == CKey::Type::SERVER) && !Settings::CDOC2_NOTIFICATION.isSet()) { auto *dlg = new WarningDialog(tr("You must enter your PIN code twice in order to decrypt the CDOC2 container. " - "The first PIN entry is required for authentication to the key server referenced in the CDOC2 container. " - "Second PIN entry is required to decrypt the CDOC2 container."), Application::mainWindow()); + "The first PIN entry is required for authentication to the key server referenced in the CDOC2 container. " + "Second PIN entry is required to decrypt the CDOC2 container."), Application::mainWindow()); dlg->setCancelText(WarningDialog::Cancel); dlg->addButton(WarningDialog::OK, QMessageBox::Ok); dlg->addButton(tr("DON'T SHOW AGAIN"), QMessageBox::Ignore); diff --git a/client/MainWindow.cpp b/client/MainWindow.cpp index dd8f31aa..64decc8c 100644 --- a/client/MainWindow.cpp +++ b/client/MainWindow.cpp @@ -346,7 +346,7 @@ bool MainWindow::encrypt(bool askForKey) if(!FileDialog::fileIsWritable(cryptoDoc->fileName())) { auto *dlg = new WarningDialog(tr("Cannot alter container %1. Save different location?") - .arg(FileDialog::normalized(cryptoDoc->fileName())), this); + .arg(FileDialog::normalized(cryptoDoc->fileName())), this); dlg->addButton(WarningDialog::YES, QMessageBox::Yes); if(dlg->exec() == QMessageBox::Yes) { moveCryptoContainer(); @@ -469,14 +469,14 @@ void MainWindow::onSignAction(int action, const QString &info1, const QString &i sign([this, info1, info2](const QString &city, const QString &state, const QString &zip, const QString &country, const QString &role) { MobileProgress m(this); return m.init(info1, info2) && - digiDoc->sign(city, state, zip, country, role, &m); + digiDoc->sign(city, state, zip, country, role, &m); }); break; case SignatureSmartID: sign([this, info1, info2](const QString &city, const QString &state, const QString &zip, const QString &country, const QString &role) { SmartIDProgress s(this); return s.init(info1, info2, digiDoc->fileName()) && - digiDoc->sign(city, state, zip, country, role, &s); + digiDoc->sign(city, state, zip, country, role, &s); }); break; case ClearSignatureWarning: @@ -669,8 +669,8 @@ void MainWindow::openFiles(const QStringList &files, bool addFile, bool forceCre if(current == MyEid) page = (fileType == FileDialog::CryptoDocument) ? CryptoDetails : SignDetails; create = forceCreate || ( - (fileType != FileDialog::CryptoDocument || page != CryptoDetails) && - (fileType != FileDialog::SignatureDocument || page != SignDetails)); + (fileType != FileDialog::CryptoDocument || page != CryptoDetails) && + (fileType != FileDialog::SignatureDocument || page != SignDetails)); } break; case ContainerState::UnsignedContainer: @@ -685,7 +685,7 @@ void MainWindow::openFiles(const QStringList &files, bool addFile, bool forceCre if(page != CryptoDetails && digiDoc->isPDF() && !wrap(digiDoc->fileName(), true)) return; DocumentModel* model = (current == CryptoDetails) ? - cryptoDoc->documentModel() : digiDoc->documentModel(); + cryptoDoc->documentModel() : digiDoc->documentModel(); for(const auto &file: content) model->addFile(file); selectPage(page); @@ -747,7 +747,7 @@ void MainWindow::openContainer(bool signature) QString filter = QFileDialog::tr("All Files (*)") + QStringLiteral(";;") + tr("Documents (%1)"); if(signature) filter = filter.arg(QStringLiteral("*.bdoc *.ddoc *.asice *.sce *.asics *.scs *.edoc *.adoc%1") - .arg(Application::confValue(Application::SiVaUrl).toString().isEmpty() ? QLatin1String() : QLatin1String(" *.pdf"))); + .arg(Application::confValue(Application::SiVaUrl).toString().isEmpty() ? QLatin1String() : QLatin1String(" *.pdf"))); else filter = filter.arg(QLatin1String("*.cdoc *.cdoc2")); QStringList files = FileDialog::getOpenFileNames(this, tr("Select documents"), {}, filter); @@ -901,7 +901,7 @@ void MainWindow::showEvent(QShowEvent * /*event*/) static const int height = 94; static const int width = 166; auto *notification = new FadeInNotification(this, WHITE, NONE, - QPoint(this->width() - width - 15, this->height() - height - 70), width, height); + QPoint(this->width() - width - 15, this->height() - height - 70), width, height); notification->setFocusPolicy(Qt::NoFocus); auto *structureFunds = new QSvgWidget(QStringLiteral(":/images/Struktuurifondid.svg"), notification); structureFunds->resize(width, height); @@ -1067,7 +1067,7 @@ bool MainWindow::validateFiles(const QString &container, const QStringList &file // Check that container is not dropped into itself QFileInfo containerInfo(container); if(std::none_of(files.cbegin(), files.cend(), - [containerInfo] (const QString &file) { return containerInfo == QFileInfo(file); })) + [containerInfo] (const QString &file) { return containerInfo == QFileInfo(file); })) return true; auto *dlg = new WarningDialog(tr("Cannot add container to same container\n%1") .arg(FileDialog::normalized(container)), this); @@ -1113,8 +1113,8 @@ bool MainWindow::wrap(const QString& wrappedFile, bool enclose) bool MainWindow::wrapContainer(bool signing) { QString msg = signing ? - tr("Files can not be added to the signed container. The system will create a new container which shall contain the signed document and the files you wish to add.") : - tr("Files can not be added to the cryptocontainer. The system will create a new container which shall contain the cypto-document and the files you wish to add."); + tr("Files can not be added to the signed container. The system will create a new container which shall contain the signed document and the files you wish to add.") : + tr("Files can not be added to the cryptocontainer. The system will create a new container which shall contain the cypto-document and the files you wish to add."); auto *dlg = new WarningDialog(msg, this); dlg->setCancelText(WarningDialog::Cancel); dlg->addButton(tr("CONTINUE"), ContainerSave); @@ -1180,7 +1180,7 @@ void MainWindow::containerSummary() if( QPrinterInfo::availablePrinterNames().isEmpty() ) { WarningDialog::show(this, - tr("In order to view Validity Confirmation Sheet there has to be at least one printer installed!")); + tr("In order to view Validity Confirmation Sheet there has to be at least one printer installed!")); return; } #endif diff --git a/client/dialogs/AddRecipients.cpp b/client/dialogs/AddRecipients.cpp index 354bea47..201099b4 100644 --- a/client/dialogs/AddRecipients.cpp +++ b/client/dialogs/AddRecipients.cpp @@ -130,7 +130,7 @@ void AddRecipients::addRecipientFromCard() void AddRecipients::addRecipientFromFile() { QString file = FileDialog::getOpenFileName(this, windowTitle(), {}, - tr("Certificates (*.cer *.crt *.pem)") ); + tr("Certificates (*.cer *.crt *.pem)") ); if( file.isEmpty() ) return; @@ -217,8 +217,8 @@ bool AddRecipients::addRecipientToRightPane(std::shared_ptr key, bool upda return false; } auto *dlg = new WarningDialog(tr("Are you sure that you want use certificate for encrypting, which expired on %1?
" - "When decrypter has updated certificates then decrypting is impossible.") - .arg(expiryDate.toString(QStringLiteral("dd.MM.yyyy hh:mm:ss"))), this); + "When decrypter has updated certificates then decrypting is impossible.") + .arg(expiryDate.toString(QStringLiteral("dd.MM.yyyy hh:mm:ss"))), this); dlg->setCancelText(WarningDialog::NO); dlg->addButton(WarningDialog::YES, QMessageBox::Yes); if(dlg->exec() != QMessageBox::Yes) @@ -329,16 +329,16 @@ void AddRecipients::search(const QString &term, bool select, const QString &type {QStringLiteral("select"), select} }; QString cleanTerm = term.simplified() - #ifdef Q_OS_WIN - .replace(QStringLiteral("\\"), QStringLiteral("\\5c")) - .replace(QStringLiteral("*"), QStringLiteral("\\2A")) - .replace(QStringLiteral("("), QStringLiteral("\\28")) - .replace(QStringLiteral(")"), QStringLiteral("\\29")); +#ifdef Q_OS_WIN + .replace(QStringLiteral("\\"), QStringLiteral("\\5c")) + .replace(QStringLiteral("*"), QStringLiteral("\\2A")) + .replace(QStringLiteral("("), QStringLiteral("\\28")) + .replace(QStringLiteral(")"), QStringLiteral("\\29")); #else - .replace(QStringLiteral("\\"), QStringLiteral("\\\\")) - .replace(QStringLiteral("*"), QStringLiteral("\\*")) - .replace(QStringLiteral("("), QStringLiteral("\\(")) - .replace(QStringLiteral(")"), QStringLiteral("\\)")); + .replace(QStringLiteral("\\"), QStringLiteral("\\\\")) + .replace(QStringLiteral("*"), QStringLiteral("\\*")) + .replace(QStringLiteral("("), QStringLiteral("\\(")) + .replace(QStringLiteral(")"), QStringLiteral("\\)")); #endif bool isDigit = false; void(cleanTerm.toULongLong(&isDigit)); @@ -378,14 +378,14 @@ void AddRecipients::showResult(const QList &result, int resultC SslCertificate c(k); if((c.keyUsage().contains(SslCertificate::KeyEncipherment) || c.keyUsage().contains(SslCertificate::KeyAgreement)) && - !c.enhancedKeyUsage().contains(SslCertificate::ServerAuth) && - (userData.value(QStringLiteral("personSearch"), false).toBool() || !c.enhancedKeyUsage().contains(SslCertificate::ClientAuth)) && - c.type() != SslCertificate::MobileIDType) + !c.enhancedKeyUsage().contains(SslCertificate::ServerAuth) && + (userData.value(QStringLiteral("personSearch"), false).toBool() || !c.enhancedKeyUsage().contains(SslCertificate::ClientAuth)) && + c.type() != SslCertificate::MobileIDType) { isEmpty = false; AddressItem *item = addRecipientToLeftPane(k); if(userData.value(QStringLiteral("select"), false).toBool() && - (userData.value(QStringLiteral("type")).isNull() || HistoryCertData::toType(SslCertificate(k)) == userData[QStringLiteral("type")])) + (userData.value(QStringLiteral("type")).isNull() || HistoryCertData::toType(SslCertificate(k)) == userData[QStringLiteral("type")])) addRecipientToRightPane(item, true); } } @@ -394,8 +394,8 @@ void AddRecipients::showResult(const QList &result, int resultC else if(isEmpty) { showError(tr("Person or company does not own a valid certificate.
" - "It is necessary to have a valid certificate for encryption.
" - "Read more about it.")); + "It is necessary to have a valid certificate for encryption.
" + "Read more about it.")); } QApplication::restoreOverrideCursor(); } diff --git a/client/widgets/ContainerPage.cpp b/client/widgets/ContainerPage.cpp index 19d84664..4ea38b3b 100644 --- a/client/widgets/ContainerPage.cpp +++ b/client/widgets/ContainerPage.cpp @@ -42,8 +42,8 @@ using namespace ria::qdigidoc4; ContainerPage::ContainerPage(QWidget *parent) - : QWidget(parent) - , ui(new Ui::ContainerPage) +: QWidget(parent) +, ui(new Ui::ContainerPage) { ui->setupUi( this ); ui->leftPane->init(fileName); @@ -52,7 +52,7 @@ ContainerPage::ContainerPage(QWidget *parent) ui->containerFile->setFont(Styles::font(Styles::Regular, 14)); ui->changeLocation->setIcons(QStringLiteral("/images/icon_Edit.svg"), - QStringLiteral("/images/icon_Edit_hover.svg"), QStringLiteral("/images/icon_Edit_pressed.svg"), 18, 18); + QStringLiteral("/images/icon_Edit_hover.svg"), QStringLiteral("/images/icon_Edit_pressed.svg"), 18, 18); ui->changeLocation->init(LabelButton::BoxedDeepCeruleanWithCuriousBlue, tr("CHANGE")); ui->containerFile->installEventFilter(this); ui->cancel->init(LabelButton::BoxedMojo, tr("CANCEL")); @@ -83,7 +83,7 @@ ContainerPage::ContainerPage(QWidget *parent) connect(ui->rightPane, &ItemList::addressSearch, this, &ContainerPage::addressSearch); connect(ui->rightPane, &ItemList::removed, this, &ContainerPage::removed); connect(ui->containerFile, &QLabel::linkActivated, this, [this](const QString &link) - { emit action(Actions::ContainerNavigate, link); }); + { emit action(Actions::ContainerNavigate, link); }); ui->summary->setVisible(Settings::SHOW_PRINT_SUMMARY); } @@ -127,12 +127,12 @@ bool ContainerPage::checkAction(int code, const QString& selectedCard, const QSt case SignatureMobile: case SignatureSmartID: if(ui->rightPane->hasItem( - [selectedCard, selectedMobile, code](Item* const item) -> bool - { - auto *signatureItem = qobject_cast(item); - return signatureItem && signatureItem->isSelfSigned(selectedCard, (code == SignatureMobile) ? selectedMobile: QString()); - } - )) + [selectedCard, selectedMobile, code](Item* const item) -> bool + { + auto *signatureItem = qobject_cast(item); + return signatureItem && signatureItem->isSelfSigned(selectedCard, (code == SignatureMobile) ? selectedMobile: QString()); + } + )) { auto *dlg = new WarningDialog(tr("The document has already been signed by you."), this); dlg->addButton(tr("CONTINUE SIGNING"), SignatureAdd); @@ -159,7 +159,7 @@ void ContainerPage::clearPopups() void ContainerPage::elideFileName() { ui->containerFile->setText(QStringLiteral("%1") - .arg(ui->containerFile->fontMetrics().elidedText(FileDialog::normalized(fileName).toHtmlEscaped(), Qt::ElideMiddle, ui->containerFile->width()))); + .arg(ui->containerFile->fontMetrics().elidedText(FileDialog::normalized(fileName).toHtmlEscaped(), Qt::ElideMiddle, ui->containerFile->width()))); } bool ContainerPage::eventFilter(QObject *o, QEvent *e) @@ -265,7 +265,7 @@ void ContainerPage::showMainAction(const QList &actions) bool isEncrypt = actions.contains(EncryptContainer) && !ui->rightPane->findChildren().isEmpty(); bool isDecrypt = actions.contains(DecryptContainer) || actions.contains(DecryptToken); mainAction->setButtonEnabled(isSupported && !hasEmptyFile && - (isEncrypt || isSignMobile || (!isBlocked && ((isSignCard && !isExpired) || isDecrypt)))); + (isEncrypt || isSignMobile || (!isBlocked && ((isSignCard && !isExpired) || isDecrypt)))); ui->mainActionSpacer->changeSize(198, 20, QSizePolicy::Fixed); ui->navigationArea->layout()->invalidate(); } diff --git a/client/widgets/MainAction.cpp b/client/widgets/MainAction.cpp index a4c2e556..442ac23d 100644 --- a/client/widgets/MainAction.cpp +++ b/client/widgets/MainAction.cpp @@ -151,10 +151,10 @@ void MainAction::showActions(const QList &actions) { QList order = actions; if(order.size() == 2 && - std::all_of(order.cbegin(), order.cend(), [] (Actions action) { - return action == SignatureMobile || action == SignatureSmartID; - }) && - !Settings::MOBILEID_ORDER) + std::all_of(order.cbegin(), order.cend(), [] (Actions action) { + return action == SignatureMobile || action == SignatureSmartID; + }) && + !Settings::MOBILEID_ORDER) { std::reverse(order.begin(), order.end()); } @@ -178,7 +178,7 @@ void MainAction::showDropdown() other->move(pos() + QPoint(0, (-height() - 1) * (ui->list.size() + 1))); other->show(); other->setStyleSheet(ui->mainAction->styleSheet() + - QStringLiteral("\nborder-top-left-radius: 2px; border-top-right-radius: 2px;")); + QStringLiteral("\nborder-top-left-radius: 2px; border-top-right-radius: 2px;")); if (*i == Actions::SignatureMobile) connect(other, &QPushButton::clicked, this, []{ Settings::MOBILEID_ORDER = true; }); if (*i == Actions::SignatureSmartID) From c9ed4331a19624ee632949dd534ac4d021e4b651 Mon Sep 17 00:00:00 2001 From: lauris71 Date: Mon, 3 Jun 2024 15:46:06 +0300 Subject: [PATCH 15/22] Apply suggestions from code review Fixed whitespace Co-authored-by: Raul Metsma --- client/CDoc1.cpp | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/client/CDoc1.cpp b/client/CDoc1.cpp index 22bc9c63..b18d20bc 100644 --- a/client/CDoc1.cpp +++ b/client/CDoc1.cpp @@ -99,12 +99,12 @@ CDoc1::CDoc1(const QString &path) { QStringList fileparts = xml.readElementText().split('|'); files.push_back({ - fileparts.value(0), - fileparts.value(3), - fileparts.value(2), - fileparts.value(1).toUInt(), - {} - }); + fileparts.value(0), + fileparts.value(3), + fileparts.value(2), + fileparts.value(1).toUInt(), + {} + }); } else properties[attr.value().toString()] = xml.readElementText(); @@ -187,12 +187,12 @@ CDoc1::CDoc1(const QString &path) if(files.empty() && properties.contains(QStringLiteral("Filename"))) { files.push_back({ - properties.value(QStringLiteral("Filename")), - {}, - mime == MIME_ZLIB ? properties.value(QStringLiteral("OriginalMimeType")) : mime, - properties.value(QStringLiteral("OriginalSize")).toUInt(), - {} - }); + properties.value(QStringLiteral("Filename")), + {}, + mime == MIME_ZLIB ? properties.value(QStringLiteral("OriginalMimeType")) : mime, + properties.value(QStringLiteral("OriginalSize")).toUInt(), + {} + }); } } @@ -281,12 +281,12 @@ bool CDoc1::decryptPayload(const QByteArray &key) else if(properties.contains(QStringLiteral("Filename"))) { files.push_back({ - properties.value(QStringLiteral("Filename")), - {}, - mime, - data.size(), - std::move(buffer), - }); + properties.value(QStringLiteral("Filename")), + {}, + mime, + data.size(), + std::move(buffer), + }); } else return setLastError(CryptoDoc::tr("Error parsing document")); @@ -462,8 +462,8 @@ bool CDoc1::save(const QString &path) if(!mime.isEmpty()) w.writeAttribute(QStringLiteral("MimeType"), mime); writeElement(w, DENC, QStringLiteral("EncryptionMethod"), { - {QStringLiteral("Algorithm"), method}, - }); + {QStringLiteral("Algorithm"), method}, + }); w.writeNamespace(DS, QStringLiteral("ds")); writeElement(w, DS, QStringLiteral("KeyInfo"), [&]{ for(std::shared_ptr key: qAsConst(keys)) @@ -484,8 +484,8 @@ bool CDoc1::save(const QString &path) if(cipher.isEmpty()) return; writeElement(w, DENC, QStringLiteral("EncryptionMethod"), { - {QStringLiteral("Algorithm"), RSA_MTH}, - }); + {QStringLiteral("Algorithm"), RSA_MTH}, + }); writeElement(w, DS, QStringLiteral("KeyInfo"), [&]{ // Name is never used //if(!k->name.isEmpty()) From 5a0d798f26ae1a2fb2073a490823fb05c0cb9125 Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Fri, 7 Jun 2024 10:56:27 +0300 Subject: [PATCH 16/22] Reverted more cdoc1/cdoc2 whitespace --- client/CDoc1.cpp | 66 ++--- client/CDoc2.cpp | 684 +++++++++++++++++++++++------------------------ 2 files changed, 375 insertions(+), 375 deletions(-) diff --git a/client/CDoc1.cpp b/client/CDoc1.cpp index b18d20bc..cd4e8498 100644 --- a/client/CDoc1.cpp +++ b/client/CDoc1.cpp @@ -309,17 +309,17 @@ std::shared_ptr CDoc1::getDecryptionKey(const QSslCertificate &cert) const if (key->type != CKey::Type::CDOC1) continue; std::shared_ptr k = std::static_pointer_cast(key); if(!ENC_MTH.contains(method) || - k->cert != cert || - k->encrypted_fmk.isEmpty()) + k->cert != cert || + k->encrypted_fmk.isEmpty()) continue; if(cert.publicKey().algorithm() == QSsl::Rsa && k->method == RSA_MTH) return k; if(cert.publicKey().algorithm() == QSsl::Ec && - !k->publicKey.isEmpty() && - KWAES_SIZE.contains(k->method) /* && - k->derive == CONCATKDF_MTH && - k->agreement == AGREEMENT_MTH*/ ) + !k->publicKey.isEmpty() && + KWAES_SIZE.contains(k->method) /* && + k->derive == CONCATKDF_MTH && + k->agreement == AGREEMENT_MTH*/ ) return k; } return {}; @@ -514,7 +514,7 @@ bool CDoc1::save(const QString &path) default: concatDigest = SHA512_MTH; break; } QByteArray encryptionKey = Crypto::concatKDF(SHA_MTH[concatDigest], KWAES_SIZE[encryptionMethod], - sharedSecret, props.value(QStringLiteral("DocumentFormat")).toUtf8() + SsDer + ckey->cert.toDer()); + sharedSecret, props.value(QStringLiteral("DocumentFormat")).toUtf8() + SsDer + ckey->cert.toDer()); #ifndef NDEBUG qDebug() << "ENC Ss" << SsDer.toHex(); qDebug() << "ENC Ksr" << sharedSecret.toHex(); @@ -526,24 +526,24 @@ bool CDoc1::save(const QString &path) return; writeElement(w, DENC, QStringLiteral("EncryptionMethod"), { - {QStringLiteral("Algorithm"), encryptionMethod}, - }); + {QStringLiteral("Algorithm"), encryptionMethod}, + }); writeElement(w, DS, QStringLiteral("KeyInfo"), [&]{ writeElement(w, DENC, QStringLiteral("AgreementMethod"), { {QStringLiteral("Algorithm"), AGREEMENT_MTH}, }, [&]{ w.writeNamespace(XENC11, QStringLiteral("xenc11")); writeElement(w, XENC11, QStringLiteral("KeyDerivationMethod"), { - {QStringLiteral("Algorithm"), CONCATKDF_MTH}, - }, [&]{ + {QStringLiteral("Algorithm"), CONCATKDF_MTH}, + }, [&]{ writeElement(w, XENC11, QStringLiteral("ConcatKDFParams"), { {QStringLiteral("AlgorithmID"), QStringLiteral("00") + props.value(QStringLiteral("DocumentFormat")).toUtf8().toHex()}, {QStringLiteral("PartyUInfo"), QStringLiteral("00") + SsDer.toHex()}, {QStringLiteral("PartyVInfo"), QStringLiteral("00") + ckey->cert.toDer().toHex()}, }, [&]{ writeElement(w, DS, QStringLiteral("DigestMethod"), { - {QStringLiteral("Algorithm"), concatDigest}, - }); + {QStringLiteral("Algorithm"), concatDigest}, + }); }); }); writeElement(w, DENC, QStringLiteral("OriginatorKeyInfo"), [&]{ @@ -551,8 +551,8 @@ bool CDoc1::save(const QString &path) w.writeNamespace(DSIG11, QStringLiteral("dsig11")); writeElement(w, DSIG11, QStringLiteral("ECKeyValue"), [&]{ writeElement(w, DSIG11, QStringLiteral("NamedCurve"), { - {QStringLiteral("URI"), QStringLiteral("urn:oid:") + oid}, - }); + {QStringLiteral("URI"), QStringLiteral("urn:oid:") + oid}, + }); writeBase64Element(w, DSIG11, QStringLiteral("PublicKey"), SsDer); }); }); @@ -573,14 +573,14 @@ bool CDoc1::save(const QString &path) // This is actual content, for some weird reason named cipherData/cipherValue writeElement(w,DENC, QStringLiteral("CipherData"), [&]{ writeBase64Element(w, DENC, QStringLiteral("CipherValue"), - Crypto::cipher(ENC_MTH[method], transportKey, data.buffer(), true) - ); + Crypto::cipher(ENC_MTH[method], transportKey, data.buffer(), true) + ); }); writeElement(w, DENC, QStringLiteral("EncryptionProperties"), [&]{ for(QMultiHash::const_iterator i = props.constBegin(); i != props.constEnd(); ++i) writeElement(w, DENC, QStringLiteral("EncryptionProperty"), { - {QStringLiteral("Name"), i.key()}, - }, [&]{ + {QStringLiteral("Name"), i.key()}, + }, [&]{ w.writeCharacters(i.value()); }); }); @@ -598,9 +598,9 @@ QByteArray CDoc1::getFMK(const CKey &key, const QByteArray& secret) const CKeyCDoc1& ckey = static_cast(key); setLastError({}); QByteArray decryptedKey = qApp->signer()->decrypt([&ckey](QCryptoBackend *backend) { - if(ckey.pk_type == CKey::PKType::RSA) - return backend->decrypt(ckey.encrypted_fmk, false); - return backend->deriveConcatKDF(ckey.publicKey, SHA_MTH[ckey.concatDigest], + if(ckey.pk_type == CKey::PKType::RSA) + return backend->decrypt(ckey.encrypted_fmk, false); + return backend->deriveConcatKDF(ckey.publicKey, SHA_MTH[ckey.concatDigest], int(KWAES_SIZE[ckey.method]), ckey.AlgorithmID, ckey.PartyUInfo, ckey.PartyVInfo); }); if(decryptedKey.isEmpty()) @@ -611,8 +611,8 @@ if(decryptedKey.isEmpty()) if(ckey.pk_type == CKey::PKType::RSA) return decryptedKey; #ifndef NDEBUG -qDebug() << "DEC Ss" << ckey.publicKey.toHex(); -qDebug() << "DEC ConcatKDF" << decryptedKey.toHex(); + qDebug() << "DEC Ss" << ckey.publicKey.toHex(); + qDebug() << "DEC ConcatKDF" << decryptedKey.toHex(); #endif return Crypto::aes_unwrap(decryptedKey, ckey.encrypted_fmk); } @@ -645,20 +645,20 @@ void CDoc1::writeDDoc(QIODevice *ddoc) x.writeDefaultNamespace(QStringLiteral("http://www.sk.ee/DigiDoc/v1.3.0#")); x.writeStartElement(QStringLiteral("SignedDoc")); writeAttributes(x, { - {QStringLiteral("format"), QStringLiteral("DIGIDOC-XML")}, - {QStringLiteral("version"), QStringLiteral("1.3")}, - }); + {QStringLiteral("format"), QStringLiteral("DIGIDOC-XML")}, + {QStringLiteral("version"), QStringLiteral("1.3")}, + }); for(const File &file: qAsConst(files)) { x.writeStartElement(QStringLiteral("DataFile")); writeAttributes(x, { - {QStringLiteral("ContentType"), QStringLiteral("EMBEDDED_BASE64")}, - {QStringLiteral("Filename"), file.name}, - {QStringLiteral("Id"), file.id}, - {QStringLiteral("MimeType"), file.mime}, - {QStringLiteral("Size"), QString::number(file.size)}, - }); + {QStringLiteral("ContentType"), QStringLiteral("EMBEDDED_BASE64")}, + {QStringLiteral("Filename"), file.name}, + {QStringLiteral("Id"), file.id}, + {QStringLiteral("MimeType"), file.mime}, + {QStringLiteral("Size"), QString::number(file.size)}, + }); std::array buf{}; file.data->seek(0); for(auto size = file.data->read(buf.data(), buf.size()); size > 0; size = file.data->read(buf.data(), buf.size())) diff --git a/client/CDoc2.cpp b/client/CDoc2.cpp index 90bd5d98..626c36a2 100644 --- a/client/CDoc2.cpp +++ b/client/CDoc2.cpp @@ -61,344 +61,344 @@ const QByteArray CDoc2::PAYLOAD = "CDOC20payload"; const QByteArray CDoc2::SALT = "CDOC20salt"; namespace cdoc20 { -bool checkConnection() { - if(CheckConnection().check()) - return true; - return dispatchToMain([] { - auto *notification = new FadeInNotification(Application::mainWindow(), - ria::qdigidoc4::colors::WHITE, ria::qdigidoc4::colors::MANTIS, 110); - notification->start(QCoreApplication::translate("MainWindow", "Check internet connection"), 750, 3000, 1200); - return false; - }); -} + bool checkConnection() { + if(CheckConnection().check()) + return true; + return dispatchToMain([] { + auto *notification = new FadeInNotification(Application::mainWindow(), + ria::qdigidoc4::colors::WHITE, ria::qdigidoc4::colors::MANTIS, 110); + notification->start(QCoreApplication::translate("MainWindow", "Check internet connection"), 750, 3000, 1200); + return false; + }); + } -QNetworkRequest req(const QString &keyserver_id, const QString &transaction_id = {}) { + QNetworkRequest req(const QString &keyserver_id, const QString &transaction_id = {}) { #ifdef CONFIG_URL - QJsonObject list = Application::confValue(QLatin1String("CDOC2-CONF")).toObject(); - QJsonObject data = list.value(keyserver_id).toObject(); - QString url = transaction_id.isEmpty() ? - data.value(QLatin1String("POST")).toString(Settings::CDOC2_POST) : - data.value(QLatin1String("FETCH")).toString(Settings::CDOC2_GET); + QJsonObject list = Application::confValue(QLatin1String("CDOC2-CONF")).toObject(); + QJsonObject data = list.value(keyserver_id).toObject(); + QString url = transaction_id.isEmpty() ? + data.value(QLatin1String("POST")).toString(Settings::CDOC2_POST) : + data.value(QLatin1String("FETCH")).toString(Settings::CDOC2_GET); #else - QString url = transaction_id.isEmpty() ? Settings::CDOC2_POST : Settings::CDOC2_GET; + QString url = transaction_id.isEmpty() ? Settings::CDOC2_POST : Settings::CDOC2_GET; #endif - if(url.isEmpty()) - return QNetworkRequest{}; - QNetworkRequest req(QStringLiteral("%1/key-capsules%2").arg(url, - transaction_id.isEmpty() ? QString(): QStringLiteral("/%1").arg(transaction_id))); - req.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/json")); - return req; -} + if(url.isEmpty()) + return QNetworkRequest{}; + QNetworkRequest req(QStringLiteral("%1/key-capsules%2").arg(url, + transaction_id.isEmpty() ? QString(): QStringLiteral("/%1").arg(transaction_id))); + req.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/json")); + return req; + } -struct stream final: public QIODevice -{ - static constexpr qint64 CHUNK = 16LL * 1024LL; - QIODevice *io {}; - Crypto::Cipher *cipher {}; - z_stream s {}; - QByteArray buf; - int flush = Z_NO_FLUSH; - - stream(QIODevice *_io, Crypto::Cipher *_cipher) - : io(_io) - , cipher(_cipher) + struct stream final: public QIODevice { - if(io->isReadable()) + static constexpr qint64 CHUNK = 16LL * 1024LL; + QIODevice *io {}; + Crypto::Cipher *cipher {}; + z_stream s {}; + QByteArray buf; + int flush = Z_NO_FLUSH; + + stream(QIODevice *_io, Crypto::Cipher *_cipher) + : io(_io) + , cipher(_cipher) { - if(inflateInit2(&s, MAX_WBITS) == Z_OK) - open(QIODevice::ReadOnly); + if(io->isReadable()) + { + if(inflateInit2(&s, MAX_WBITS) == Z_OK) + open(QIODevice::ReadOnly); + } + if(io->isWritable()) + { + if(deflateInit(&s, Z_DEFAULT_COMPRESSION) == Z_OK) + open(QIODevice::WriteOnly); + } } - if(io->isWritable()) + + ~stream() final { - if(deflateInit(&s, Z_DEFAULT_COMPRESSION) == Z_OK) - open(QIODevice::WriteOnly); + if(io->isReadable()) + inflateEnd(&s); + if(io->isWritable()) + { + flush = Z_FINISH; + writeData(nullptr, 0); + deflateEnd(&s); + } } - } - ~stream() final - { - if(io->isReadable()) - inflateEnd(&s); - if(io->isWritable()) + bool isSequential() const final { - flush = Z_FINISH; - writeData(nullptr, 0); - deflateEnd(&s); + return true; } - } - bool isSequential() const final - { - return true; - } - - qint64 bytesAvailable() const final - { - return (io->bytesAvailable() - Crypto::Cipher::tagLen()) + buf.size() + QIODevice::bytesAvailable(); - } + qint64 bytesAvailable() const final + { + return (io->bytesAvailable() - Crypto::Cipher::tagLen()) + buf.size() + QIODevice::bytesAvailable(); + } - qint64 readData(char *data, qint64 maxlen) final - { - s.next_out = (Bytef*)data; - s.avail_out = uInt(maxlen); - std::array in{}; - for(int res = Z_OK; s.avail_out > 0 && res == Z_OK;) + qint64 readData(char *data, qint64 maxlen) final { - if(auto insize = io->bytesAvailable() - Crypto::Cipher::tagLen(), - size = io->read(in.data(), qMin(insize, in.size())); size > 0) + s.next_out = (Bytef*)data; + s.avail_out = uInt(maxlen); + std::array in{}; + for(int res = Z_OK; s.avail_out > 0 && res == Z_OK;) { - if(!cipher->update(in.data(), int(size))) - return -1; - buf.append(in.data(), size); - } - s.next_in = (z_const Bytef*)buf.data(); - s.avail_in = uInt(buf.size()); - switch(res = inflate(&s, flush)) - { - case Z_OK: - buf = buf.right(s.avail_in); - break; - case Z_STREAM_END: - buf.clear(); - break; - default: return -1; + if(auto insize = io->bytesAvailable() - Crypto::Cipher::tagLen(), + size = io->read(in.data(), qMin(insize, in.size())); size > 0) + { + if(!cipher->update(in.data(), int(size))) + return -1; + buf.append(in.data(), size); + } + s.next_in = (z_const Bytef*)buf.data(); + s.avail_in = uInt(buf.size()); + switch(res = inflate(&s, flush)) + { + case Z_OK: + buf = buf.right(s.avail_in); + break; + case Z_STREAM_END: + buf.clear(); + break; + default: return -1; + } } + return qint64(maxlen - s.avail_out); } - return qint64(maxlen - s.avail_out); - } - qint64 writeData(const char *data, qint64 len) final - { - s.next_in = (z_const Bytef *)data; - s.avail_in = uInt(len); - std::array out{}; - while(true) { - s.next_out = (Bytef *)out.data(); - s.avail_out = out.size(); - int res = deflate(&s, flush); - if(res == Z_STREAM_ERROR) - return -1; - if(auto size = out.size() - s.avail_out; size > 0) - { - if(!cipher->update(out.data(), int(size)) || - io->write(out.data(), size) != size) + qint64 writeData(const char *data, qint64 len) final + { + s.next_in = (z_const Bytef *)data; + s.avail_in = uInt(len); + std::array out{}; + while(true) { + s.next_out = (Bytef *)out.data(); + s.avail_out = out.size(); + int res = deflate(&s, flush); + if(res == Z_STREAM_ERROR) return -1; + if(auto size = out.size() - s.avail_out; size > 0) + { + if(!cipher->update(out.data(), int(size)) || + io->write(out.data(), size) != size) + return -1; + } + if(res == Z_STREAM_END) + break; + if(flush == Z_FINISH) + continue; + if(s.avail_in == 0) + break; } - if(res == Z_STREAM_END) - break; - if(flush == Z_FINISH) - continue; - if(s.avail_in == 0) - break; + return len; } - return len; - } -}; + }; -struct TAR { - std::unique_ptr io; - explicit TAR(std::unique_ptr &&in) - : io(std::move(in)) - {} + struct TAR { + std::unique_ptr io; + explicit TAR(std::unique_ptr &&in) + : io(std::move(in)) + {} - bool save(const std::vector &files) - { - auto writeHeader = [this](Header &h, qint64 size) { - h.chksum.fill(' '); - toOctal(h.size, size); - toOctal(h.chksum, h.checksum().first); - return io->write((const char*)&h, Header::Size) == Header::Size; - }; - auto writePadding = [this](qint64 size) { - QByteArray pad(padding(size), 0); - return io->write(pad) == pad.size(); - }; - auto toPaxRecord = [](const QByteArray &keyword, const QByteArray &value) { - QByteArray record = ' ' + keyword + '=' + value + '\n'; - QByteArray result; - for(auto len = record.size(); result.size() != len; ++len) - result = QByteArray::number(len + 1) + record; - return result; - }; - for(const CDoc::File &file: files) + bool save(const std::vector &files) { - Header h {}; - QByteArray filename = file.name.toUtf8(); - QByteArray filenameTruncated = filename.left(h.name.size()); - std::copy(filenameTruncated.cbegin(), filenameTruncated.cend(), h.name.begin()); + auto writeHeader = [this](Header &h, qint64 size) { + h.chksum.fill(' '); + toOctal(h.size, size); + toOctal(h.chksum, h.checksum().first); + return io->write((const char*)&h, Header::Size) == Header::Size; + }; + auto writePadding = [this](qint64 size) { + QByteArray pad(padding(size), 0); + return io->write(pad) == pad.size(); + }; + auto toPaxRecord = [](const QByteArray &keyword, const QByteArray &value) { + QByteArray record = ' ' + keyword + '=' + value + '\n'; + QByteArray result; + for(auto len = record.size(); result.size() != len; ++len) + result = QByteArray::number(len + 1) + record; + return result; + }; + for(const CDoc::File &file: files) + { + Header h {}; + QByteArray filename = file.name.toUtf8(); + QByteArray filenameTruncated = filename.left(h.name.size()); + std::copy(filenameTruncated.cbegin(), filenameTruncated.cend(), h.name.begin()); - if(filename.size() > 100 || + if(filename.size() > 100 || file.size > 07777777) - { - h.typeflag = 'x'; - QByteArray paxData; - if(filename.size() > 100) - paxData += toPaxRecord("path", filename); - if(file.size > 07777777) - paxData += toPaxRecord("size", QByteArray::number(file.size)); - if(!writeHeader(h, paxData.size()) || + { + h.typeflag = 'x'; + QByteArray paxData; + if(filename.size() > 100) + paxData += toPaxRecord("path", filename); + if(file.size > 07777777) + paxData += toPaxRecord("size", QByteArray::number(file.size)); + if(!writeHeader(h, paxData.size()) || io->write(paxData) != paxData.size() || !writePadding(paxData.size())) + return false; + } + + h.typeflag = '0'; + if(!writeHeader(h, file.size)) + return false; + file.data->seek(0); + if(auto size = copyIODevice(file.data.get(), io.get()); size < 0 || !writePadding(size)) return false; } - - h.typeflag = '0'; - if(!writeHeader(h, file.size)) - return false; - file.data->seek(0); - if(auto size = copyIODevice(file.data.get(), io.get()); size < 0 || !writePadding(size)) - return false; - } - return io->write((const char*)&Header::Empty, Header::Size) == Header::Size && + return io->write((const char*)&Header::Empty, Header::Size) == Header::Size && io->write((const char*)&Header::Empty, Header::Size) == Header::Size; - } + } - std::vector files(bool &warning) const - { - std::vector result; - Header h {}; - auto readHeader = [&h, this] { return io->read((char*)&h, Header::Size) == Header::Size; }; - while(io->bytesAvailable() > 0) + std::vector files(bool &warning) const { - if(!readHeader()) - return {}; - if(h.isNull()) - { - if(!readHeader() && !h.isNull()) - return {}; - warning = io->bytesAvailable() > 0; - return result; - } - if(!h.verify()) - return {}; - - CDoc::File f; - f.name = QString::fromUtf8(h.name.data(), std::min(h.name.size(), int(strlen(h.name.data())))); - f.size = fromOctal(h.size); - if(h.typeflag == 'x') + std::vector result; + Header h {}; + auto readHeader = [&h, this] { return io->read((char*)&h, Header::Size) == Header::Size; }; + while(io->bytesAvailable() > 0) { - QByteArray paxData = io->read(f.size); - if(paxData.size() != f.size) + if(!readHeader()) return {}; - io->skip(padding(f.size)); - if(!readHeader() || h.isNull() || !h.verify()) + if(h.isNull()) + { + if(!readHeader() && !h.isNull()) + return {}; + warning = io->bytesAvailable() > 0; + return result; + } + if(!h.verify()) return {}; + + CDoc::File f; + f.name = QString::fromUtf8(h.name.data(), std::min(h.name.size(), int(strlen(h.name.data())))); f.size = fromOctal(h.size); - for(const QByteArray &data: paxData.split('\n')) + if(h.typeflag == 'x') { - if(data.isEmpty()) - break; - const auto &headerValue = data.split('='); - const auto &lenKeyword = headerValue[0].split(' '); - if(data.size() + 1 != lenKeyword[0].toUInt()) + QByteArray paxData = io->read(f.size); + if(paxData.size() != f.size) return {}; - if(lenKeyword[1] == "path") - f.name = QString::fromUtf8(headerValue[1]); - if(lenKeyword[1] == "size") - f.size = headerValue[1].toUInt(); + io->skip(padding(f.size)); + if(!readHeader() || h.isNull() || !h.verify()) + return {}; + f.size = fromOctal(h.size); + for(const QByteArray &data: paxData.split('\n')) + { + if(data.isEmpty()) + break; + const auto &headerValue = data.split('='); + const auto &lenKeyword = headerValue[0].split(' '); + if(data.size() + 1 != lenKeyword[0].toUInt()) + return {}; + if(lenKeyword[1] == "path") + f.name = QString::fromUtf8(headerValue[1]); + if(lenKeyword[1] == "size") + f.size = headerValue[1].toUInt(); + } } - } - if(h.typeflag == '0' || h.typeflag == 0) - { - if(f.size > 500L * 1024L * 1024L) - f.data = std::make_unique(); + if(h.typeflag == '0' || h.typeflag == 0) + { + if(f.size > 500L * 1024L * 1024L) + f.data = std::make_unique(); + else + f.data = std::make_unique(); + f.data->open(QIODevice::ReadWrite); + if(f.size != copyIODevice(io.get(), f.data.get(), f.size)) + return {}; + io->skip(padding(f.size)); + result.push_back(std::move(f)); + } else - f.data = std::make_unique(); - f.data->open(QIODevice::ReadWrite); - if(f.size != copyIODevice(io.get(), f.data.get(), f.size)) - return {}; - io->skip(padding(f.size)); - result.push_back(std::move(f)); + io->skip(f.size + padding(f.size)); } - else - io->skip(f.size + padding(f.size)); + return result; } - return result; - } -private: - struct Header { - std::array name; - std::array mode; - std::array uid; - std::array gid; - std::array size; - std::array mtime; - std::array chksum; - char typeflag; - std::array linkname; - std::array magic; - std::array version; - std::array uname; - std::array gname; - std::array devmajor; - std::array devminor; - std::array prefix; - std::array padding; - - std::pair checksum() const - { - int64_t unsignedSum = 0; - int64_t signedSum = 0; - for (size_t i = 0, size = sizeof(Header); i < size; i++) { - unsignedSum += ((unsigned char*) this)[i]; - signedSum += ((signed char*) this)[i]; + private: + struct Header { + std::array name; + std::array mode; + std::array uid; + std::array gid; + std::array size; + std::array mtime; + std::array chksum; + char typeflag; + std::array linkname; + std::array magic; + std::array version; + std::array uname; + std::array gname; + std::array devmajor; + std::array devminor; + std::array prefix; + std::array padding; + + std::pair checksum() const + { + int64_t unsignedSum = 0; + int64_t signedSum = 0; + for (size_t i = 0, size = sizeof(Header); i < size; i++) { + unsignedSum += ((unsigned char*) this)[i]; + signedSum += ((signed char*) this)[i]; + } + return {unsignedSum, signedSum}; } - return {unsignedSum, signedSum}; - } - bool isNull() { - return memcmp(this, &Empty, sizeof(Header)) == 0; - } + bool isNull() { + return memcmp(this, &Empty, sizeof(Header)) == 0; + } - bool verify() { - auto copy = chksum; - chksum.fill(' '); - auto checkSum = checksum(); - chksum.swap(copy); - int64_t referenceChecksum = fromOctal(chksum); - return referenceChecksum == checkSum.first || - referenceChecksum == checkSum.second; - } + bool verify() { + auto copy = chksum; + chksum.fill(' '); + auto checkSum = checksum(); + chksum.swap(copy); + int64_t referenceChecksum = fromOctal(chksum); + return referenceChecksum == checkSum.first || + referenceChecksum == checkSum.second; + } - static const Header Empty; - static const int Size; - }; + static const Header Empty; + static const int Size; + }; - template - static int64_t fromOctal(const std::array &data) - { - int64_t i = 0; - for(const char c: data) + template + static int64_t fromOctal(const std::array &data) { - if(c < '0' || c > '7') - continue; - i <<= 3; - i += c - '0'; + int64_t i = 0; + for(const char c: data) + { + if(c < '0' || c > '7') + continue; + i <<= 3; + i += c - '0'; + } + return i; } - return i; - } - template - static void toOctal(std::array &data, int64_t value) - { - data.fill(' '); - for(auto it = data.rbegin() + 1; it != data.rend(); ++it) + template + static void toOctal(std::array &data, int64_t value) { - *it = char(value & 7) + '0'; - value >>= 3; + data.fill(' '); + for(auto it = data.rbegin() + 1; it != data.rend(); ++it) + { + *it = char(value & 7) + '0'; + value >>= 3; + } } - } - static constexpr int padding(int64_t size) - { - return Header::Size - size % Header::Size; - } -}; + static constexpr int padding(int64_t size) + { + return Header::Size - size % Header::Size; + } + }; -const TAR::Header TAR::Header::Empty {}; -const int TAR::Header::Size = int(sizeof(TAR::Header)); + const TAR::Header TAR::Header::Empty {}; + const int TAR::Header::Size = int(sizeof(TAR::Header)); } bool @@ -420,8 +420,8 @@ CDoc2::CDoc2(const QString &_path) uint32_t header_len = 0; QFile ifs(path); if(!ifs.open(QFile::ReadOnly) || - ifs.read(LABEL.length()) != LABEL || - ifs.read((char*)&header_len, int(sizeof(header_len))) != int(sizeof(header_len))) + ifs.read(LABEL.length()) != LABEL || + ifs.read((char*)&header_len, int(sizeof(header_len))) != int(sizeof(header_len))) return; header_data = ifs.read(qFromBigEndian(header_len)); if(header_data.size() != qFromBigEndian(header_len)) @@ -640,39 +640,39 @@ bool CDoc2::save(const QString &_path) return builder.CreateString(utf8.data(), size_t(utf8.length())); }; - auto sendToServer = [this] (const QString& keyserver_id, const QByteArray &recipient_id, const QByteArray &key_material, QLatin1String type) -> QString { - if(keyserver_id.isEmpty()) { - setLastError(QStringLiteral("keyserver_id cannot be empty")); - return {}; - } - QNetworkRequest req = cdoc20::req(keyserver_id); - if(req.url().isEmpty()) { - setLastError(QStringLiteral("No valid config found for keyserver_id: %1").arg(keyserver_id)); - return {}; - } - if(!cdoc20::checkConnection()) { - return {}; - } + auto sendToServer = [this] (const QString& keyserver_id, const QByteArray &recipient_id, const QByteArray &key_material, QLatin1String type) -> QString { + if(keyserver_id.isEmpty()) { + setLastError(QStringLiteral("keyserver_id cannot be empty")); + return {}; + } + QNetworkRequest req = cdoc20::req(keyserver_id); + if(req.url().isEmpty()) { + setLastError(QStringLiteral("No valid config found for keyserver_id: %1").arg(keyserver_id)); + return {}; + } + if(!cdoc20::checkConnection()) { + return {}; + } QScopedPointer nam(CheckConnection::setupNAM(req, Settings::CDOC2_POST_CERT)); QEventLoop e; QNetworkReply *reply = nam->post(req, QJsonDocument({ - {QLatin1String("recipient_id"), QLatin1String(recipient_id.toBase64())}, - {QLatin1String("ephemeral_key_material"), QLatin1String(key_material.toBase64())}, - {QLatin1String("capsule_type"), type}, - }).toJson()); + {QLatin1String("recipient_id"), QLatin1String(recipient_id.toBase64())}, + {QLatin1String("ephemeral_key_material"), QLatin1String(key_material.toBase64())}, + {QLatin1String("capsule_type"), type}, + }).toJson()); connect(reply, &QNetworkReply::finished, &e, &QEventLoop::quit); e.exec(); QString transaction_id; if(reply->error() == QNetworkReply::NoError && - reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 201) { - transaction_id = QString::fromLatin1(reply->rawHeader("Location")).remove(QLatin1String("/key-capsules/")); - } else { - setLastError(reply->errorString()); - return {}; - } - if(transaction_id.isEmpty()) - setLastError(QStringLiteral("Failed to post key capsule")); - return transaction_id; + reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 201) { + transaction_id = QString::fromLatin1(reply->rawHeader("Location")).remove(QLatin1String("/key-capsules/")); + } else { + setLastError(reply->errorString()); + return {}; + } + if(transaction_id.isEmpty()) + setLastError(QStringLiteral("Failed to post key capsule")); + return transaction_id; }; for(std::shared_ptr key: keys) { @@ -693,36 +693,36 @@ bool CDoc2::save(const QString &_path) qDebug() << "xor" << xor_key.toHex(); qDebug() << "encrytpedKek" << encrytpedKek.toHex(); #endif - if(!Settings::CDOC2_USE_KEYSERVER) { - auto rsaPublicKey = cdoc20::recipients::CreateRSAPublicKeyCapsule(builder, - toVector(pki.rcpt_key), toVector(encrytpedKek)); - auto offs = cdoc20::header::CreateRecipientRecord(builder, - cdoc20::recipients::Capsule::RSAPublicKeyCapsule, rsaPublicKey.Union(), - toString(pki.label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); - recipients.push_back(offs); - } else { - QString keyserver_id = Settings::CDOC2_DEFAULT_KEYSERVER; - QString transaction_id = sendToServer(keyserver_id, pki.rcpt_key, encrytpedKek, QLatin1String("rsa")); - if (transaction_id.isEmpty()) return false; - auto rsaKeyServer = cdoc20::recipients::CreateRsaKeyDetails(builder, toVector(pki.rcpt_key)); - auto keyServer = cdoc20::recipients::CreateKeyServerCapsule(builder, - cdoc20::recipients::KeyDetailsUnion::RsaKeyDetails, - rsaKeyServer.Union(), toString(keyserver_id), toString(transaction_id)); - auto offs = cdoc20::header::CreateRecipientRecord(builder, - cdoc20::recipients::Capsule::KeyServerCapsule, keyServer.Union(), - toString(pki.label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); - recipients.push_back(offs); - } - } else { - auto publicKey = Crypto::fromECPublicKeyDer(pki.rcpt_key, NID_secp384r1); - if(!publicKey) return false; - auto ephKey = Crypto::genECKey(publicKey.get()); - QByteArray sharedSecret = Crypto::derive(ephKey.get(), publicKey.get()); - QByteArray ephPublicKeyDer = Crypto::toPublicKeyDer(ephKey.get()); - QByteArray kekPm = Crypto::extract(sharedSecret, KEKPREMASTER); - QByteArray info = KEK + cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) + pki.rcpt_key + ephPublicKeyDer; - QByteArray kek = Crypto::expand(kekPm, info, fmk.size()); - QByteArray xor_key = Crypto::xor_data(fmk, kek); + if(!Settings::CDOC2_USE_KEYSERVER) { + auto rsaPublicKey = cdoc20::recipients::CreateRSAPublicKeyCapsule(builder, + toVector(pki.rcpt_key), toVector(encrytpedKek)); + auto offs = cdoc20::header::CreateRecipientRecord(builder, + cdoc20::recipients::Capsule::RSAPublicKeyCapsule, rsaPublicKey.Union(), + toString(pki.label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); + recipients.push_back(offs); + } else { + QString keyserver_id = Settings::CDOC2_DEFAULT_KEYSERVER; + QString transaction_id = sendToServer(keyserver_id, pki.rcpt_key, encrytpedKek, QLatin1String("rsa")); + if (transaction_id.isEmpty()) return false; + auto rsaKeyServer = cdoc20::recipients::CreateRsaKeyDetails(builder, toVector(pki.rcpt_key)); + auto keyServer = cdoc20::recipients::CreateKeyServerCapsule(builder, + cdoc20::recipients::KeyDetailsUnion::RsaKeyDetails, + rsaKeyServer.Union(), toString(keyserver_id), toString(transaction_id)); + auto offs = cdoc20::header::CreateRecipientRecord(builder, + cdoc20::recipients::Capsule::KeyServerCapsule, keyServer.Union(), + toString(pki.label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); + recipients.push_back(offs); + } + } else { + auto publicKey = Crypto::fromECPublicKeyDer(pki.rcpt_key, NID_secp384r1); + if(!publicKey) return false; + auto ephKey = Crypto::genECKey(publicKey.get()); + QByteArray sharedSecret = Crypto::derive(ephKey.get(), publicKey.get()); + QByteArray ephPublicKeyDer = Crypto::toPublicKeyDer(ephKey.get()); + QByteArray kekPm = Crypto::extract(sharedSecret, KEKPREMASTER); + QByteArray info = KEK + cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) + pki.rcpt_key + ephPublicKeyDer; + QByteArray kek = Crypto::expand(kekPm, info, fmk.size()); + QByteArray xor_key = Crypto::xor_data(fmk, kek); #ifndef NDEBUG qDebug() << "publicKeyDer" << pki.rcpt_key.toHex(); qDebug() << "ephPublicKeyDer" << ephPublicKeyDer.toHex(); @@ -1010,11 +1010,11 @@ QByteArray CDoc2::getFMK(const CKey &key, const QByteArray& secret) qDebug() << "hhk" << hhk.toHex(); qDebug() << "hmac" << headerHMAC.toHex(); #endif - if(Crypto::sign_hmac(hhk, header_data) != headerHMAC) { - setLastError(QStringLiteral("CDoc 2.0 hash mismatch")); - return {}; - } - return fmk; + if(Crypto::sign_hmac(hhk, header_data) != headerHMAC) { + setLastError(QStringLiteral("CDoc 2.0 hash mismatch")); + return {}; + } + return fmk; } int CDoc2::version() From cc1203e59dd84af1d12eaa8b693aec895ac988ac Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Fri, 7 Jun 2024 12:55:25 +0300 Subject: [PATCH 17/22] More whitespace reverts --- client/CDoc1.cpp | 12 ++++++------ client/CDoc2.cpp | 2 +- client/Crypto.h | 2 +- client/MainWindow.cpp | 4 ++-- client/dialogs/AddRecipients.cpp | 2 +- client/widgets/AddressItem.cpp | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/client/CDoc1.cpp b/client/CDoc1.cpp index cd4e8498..8b94407c 100644 --- a/client/CDoc1.cpp +++ b/client/CDoc1.cpp @@ -530,17 +530,17 @@ bool CDoc1::save(const QString &path) }); writeElement(w, DS, QStringLiteral("KeyInfo"), [&]{ writeElement(w, DENC, QStringLiteral("AgreementMethod"), { - {QStringLiteral("Algorithm"), AGREEMENT_MTH}, - }, [&]{ + {QStringLiteral("Algorithm"), AGREEMENT_MTH}, + }, [&]{ w.writeNamespace(XENC11, QStringLiteral("xenc11")); writeElement(w, XENC11, QStringLiteral("KeyDerivationMethod"), { {QStringLiteral("Algorithm"), CONCATKDF_MTH}, }, [&]{ writeElement(w, XENC11, QStringLiteral("ConcatKDFParams"), { - {QStringLiteral("AlgorithmID"), QStringLiteral("00") + props.value(QStringLiteral("DocumentFormat")).toUtf8().toHex()}, - {QStringLiteral("PartyUInfo"), QStringLiteral("00") + SsDer.toHex()}, - {QStringLiteral("PartyVInfo"), QStringLiteral("00") + ckey->cert.toDer().toHex()}, - }, [&]{ + {QStringLiteral("AlgorithmID"), QStringLiteral("00") + props.value(QStringLiteral("DocumentFormat")).toUtf8().toHex()}, + {QStringLiteral("PartyUInfo"), QStringLiteral("00") + SsDer.toHex()}, + {QStringLiteral("PartyVInfo"), QStringLiteral("00") + ckey->cert.toDer().toHex()}, + }, [&]{ writeElement(w, DS, QStringLiteral("DigestMethod"), { {QStringLiteral("Algorithm"), concatDigest}, }); diff --git a/client/CDoc2.cpp b/client/CDoc2.cpp index 626c36a2..0e0c4336 100644 --- a/client/CDoc2.cpp +++ b/client/CDoc2.cpp @@ -986,7 +986,7 @@ QByteArray CDoc2::getFMK(const CKey &key, const QByteArray& secret) kek = qApp->signer()->decrypt([&pki, &key_material](QCryptoBackend *backend) { QByteArray kekPm = backend->deriveHMACExtract(key_material, KEKPREMASTER, KEY_LEN); #ifndef NDEBUG - qDebug() << "kekPm" << kekPm.toHex(); + qDebug() << "kekPm" << kekPm.toHex(); #endif QByteArray info = KEK + cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) + pki.rcpt_key + key_material; return Crypto::expand(kekPm, info, KEY_LEN); diff --git a/client/Crypto.h b/client/Crypto.h index f2a509f1..94f1635e 100644 --- a/client/Crypto.h +++ b/client/Crypto.h @@ -55,7 +55,7 @@ class Crypto static QByteArray cipher(const EVP_CIPHER *cipher, const QByteArray &key, QByteArray &data, bool encrypt); static QByteArray curve_oid(EVP_PKEY *key); static QByteArray concatKDF(QCryptographicHash::Algorithm digestMethod, - quint32 keyDataLen, const QByteArray &z, const QByteArray &otherInfo); + quint32 keyDataLen, const QByteArray &z, const QByteArray &otherInfo); static QByteArray derive(EVP_PKEY *priv, EVP_PKEY *pub); static QByteArray encrypt(EVP_PKEY *pub, int padding, const QByteArray &data); static QByteArray expand(const QByteArray &key, const QByteArray &info, int len = 32); diff --git a/client/MainWindow.cpp b/client/MainWindow.cpp index 64decc8c..8861172e 100644 --- a/client/MainWindow.cpp +++ b/client/MainWindow.cpp @@ -639,7 +639,7 @@ void MainWindow::onCryptoAction(int action, const QString &/*id*/, const QString void MainWindow::openFiles(const QStringList &files, bool addFile, bool forceCreate) { - /* +/* 1. If containers are not open: - If on myEid page and either crypto- or signature document, open corresponding view - If on encrypt page, open encryption view @@ -662,7 +662,7 @@ void MainWindow::openFiles(const QStringList &files, bool addFile, bool forceCre switch(state) { case Uninitialized: - // Case 1. + // Case 1. if(content.size() == 1) { auto fileType = FileDialog::detect(content[0]); diff --git a/client/dialogs/AddRecipients.cpp b/client/dialogs/AddRecipients.cpp index 201099b4..2caebd1a 100644 --- a/client/dialogs/AddRecipients.cpp +++ b/client/dialogs/AddRecipients.cpp @@ -152,7 +152,7 @@ void AddRecipients::addRecipientFromFile() WarningDialog::show(this, tr("Failed to read certificate")); } else if( !SslCertificate( cert ).keyUsage().contains( SslCertificate::KeyEncipherment ) && - !SslCertificate( cert ).keyUsage().contains( SslCertificate::KeyAgreement ) ) + !SslCertificate( cert ).keyUsage().contains( SslCertificate::KeyAgreement ) ) { WarningDialog::show(this, tr("This certificate cannot be used for encryption")); } diff --git a/client/widgets/AddressItem.cpp b/client/widgets/AddressItem.cpp index 8fb248d2..bef232e9 100644 --- a/client/widgets/AddressItem.cpp +++ b/client/widgets/AddressItem.cpp @@ -54,7 +54,7 @@ AddressItem::AddressItem(std::shared_ptr key, QWidget *parent, bool showIc ui->idType->installEventFilter(this); ui->remove->setIcons(QStringLiteral("/images/icon_remove.svg"), QStringLiteral("/images/icon_remove_hover.svg"), - QStringLiteral("/images/icon_remove_pressed.svg"), 17, 17); + QStringLiteral("/images/icon_remove_pressed.svg"), 17, 17); ui->remove->init(LabelButton::White); connect(ui->add, &QToolButton::clicked, this, [this]{ emit add(this);}); connect(ui->remove, &LabelButton::clicked, this, [this]{ emit remove(this);}); From d79dc50eae3e8e40885bac938a0cbf295e05d687 Mon Sep 17 00:00:00 2001 From: lauris71 Date: Mon, 15 Jul 2024 00:42:09 +0300 Subject: [PATCH 18/22] Apply suggestions from code review Co-authored-by: Raul Metsma --- client/CDoc1.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/client/CDoc1.cpp b/client/CDoc1.cpp index 8b94407c..c381c41f 100644 --- a/client/CDoc1.cpp +++ b/client/CDoc1.cpp @@ -199,10 +199,10 @@ CDoc1::CDoc1(const QString &path) std::unique_ptr CDoc1::load(const QString& path) { - CDoc1 *cdoc = new CDoc1(path); - if (!cdoc->keys.isEmpty()) return std::unique_ptr(cdoc); - delete cdoc; - return nullptr; + auto cdoc = std::make_unique(path); + if (cdoc->keys.isEmpty()) + cdoc.reset(); + return cdoc; } bool CDoc1::decryptPayload(const QByteArray &key) @@ -297,8 +297,8 @@ bool CDoc1::decryptPayload(const QByteArray &key) CKey::DecryptionStatus CDoc1::canDecrypt(const QSslCertificate &cert) const { - std::shared_ptr key = getDecryptionKey(cert); - if (key) return CKey::DecryptionStatus::CAN_DECRYPT; + if(getDecryptionKey(cert)) + return CKey::DecryptionStatus::CAN_DECRYPT; return CKey::DecryptionStatus::CANNOT_DECRYPT; } From b1230a08d04026ad34df381cf5ae3c5ab20f8800 Mon Sep 17 00:00:00 2001 From: lauris71 Date: Mon, 15 Jul 2024 00:45:19 +0300 Subject: [PATCH 19/22] Update client/CDoc1.cpp Co-authored-by: Raul Metsma --- client/CDoc1.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/CDoc1.cpp b/client/CDoc1.cpp index c381c41f..bf7a51a4 100644 --- a/client/CDoc1.cpp +++ b/client/CDoc1.cpp @@ -72,10 +72,10 @@ const QByteArray XML_TAG = ""; bool CDoc1::isCDoc1File(const QString& path) { - QFile f(path); - if(!f.open(QFile::ReadOnly)) return false; - if (f.read(XML_TAG.length()) != XML_TAG) return false; - return true; + + if(QFile f(path); f.open(QFile::ReadOnly)) + return f.read(XML_TAG.length()) == XML_TAG); + return false; } CDoc1::CDoc1(const QString &path) From f9c585a0bf9f0f77358ddc7ca7103b427d914eb3 Mon Sep 17 00:00:00 2001 From: lauris71 Date: Mon, 15 Jul 2024 23:52:43 +0300 Subject: [PATCH 20/22] Update client/Crypto.cpp Co-authored-by: Raul Metsma --- client/Crypto.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/Crypto.cpp b/client/Crypto.cpp index 522580a0..5c976a41 100644 --- a/client/Crypto.cpp +++ b/client/Crypto.cpp @@ -384,10 +384,10 @@ QByteArray Crypto::xor_data(const QByteArray &a, const QByteArray &b) QByteArray Crypto::pbkdf2_sha256(const QByteArray& pw, const QByteArray& salt, uint32_t iter) { - unsigned char key[32]; + QByteArray key(32, 0); PKCS5_PBKDF2_HMAC(pw.data(), pw.length(), - (const unsigned char *) salt.data(), salt.length(), - iter, EVP_sha256(), 32, key); - return QByteArray((const char *) key, 32); + (const unsigned char *) salt.data(), int(salt.length()), + iter, EVP_sha256(), int(key.size()), (unsigned char *)key.data()); + return data; } From 3e9db8879158eae1388a9bbb1494339ee5135e4e Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Mon, 15 Jul 2024 23:55:48 +0300 Subject: [PATCH 21/22] Fixed some typos in review ;) --- client/CDoc1.cpp | 4 ++-- client/Crypto.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/CDoc1.cpp b/client/CDoc1.cpp index bf7a51a4..2fe10d39 100644 --- a/client/CDoc1.cpp +++ b/client/CDoc1.cpp @@ -74,7 +74,7 @@ bool CDoc1::isCDoc1File(const QString& path) { if(QFile f(path); f.open(QFile::ReadOnly)) - return f.read(XML_TAG.length()) == XML_TAG); + return f.read(XML_TAG.length()) == XML_TAG; return false; } @@ -199,7 +199,7 @@ CDoc1::CDoc1(const QString &path) std::unique_ptr CDoc1::load(const QString& path) { - auto cdoc = std::make_unique(path); + auto cdoc = std::unique_ptr(new CDoc1(path)); if (cdoc->keys.isEmpty()) cdoc.reset(); return cdoc; diff --git a/client/Crypto.cpp b/client/Crypto.cpp index 5c976a41..92784abf 100644 --- a/client/Crypto.cpp +++ b/client/Crypto.cpp @@ -388,6 +388,6 @@ Crypto::pbkdf2_sha256(const QByteArray& pw, const QByteArray& salt, uint32_t ite PKCS5_PBKDF2_HMAC(pw.data(), pw.length(), (const unsigned char *) salt.data(), int(salt.length()), iter, EVP_sha256(), int(key.size()), (unsigned char *)key.data()); - return data; + return key; } From f0e9c7761579893fb3fe1d13dbecd435e7347c92 Mon Sep 17 00:00:00 2001 From: Raul Metsma Date: Mon, 11 Nov 2024 15:09:51 +0200 Subject: [PATCH 22/22] Fix whitespace issues and apply static code formating changes Signed-off-by: Raul Metsma --- .github/workflows/build.yml | 4 +- client/CDoc1.cpp | 38 +++---- client/CDoc2.cpp | 138 ++++++++++++------------- client/CDoc2.h | 4 +- client/CMakeLists.txt | 1 - client/CryptoDoc.cpp | 14 +-- client/CryptoDoc.h | 24 ++--- client/MainWindow.cpp | 15 ++- client/widgets/AddressItem.cpp | 23 ++--- client/widgets/AddressItem.h | 6 +- client/widgets/AddressItem.ui | 170 +++++++++++++++---------------- client/widgets/ContainerPage.cpp | 3 +- client/widgets/ContainerPage.ui | 20 +--- common | 2 +- 14 files changed, 222 insertions(+), 240 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f7742071..b6b7a53d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -143,10 +143,8 @@ jobs: runs-on: ${{ matrix.image }} strategy: matrix: - vcver: [142, 143] + vcver: [143] include: - - vcver: 142 - image: windows-2019 - vcver: 143 image: windows-2022 env: diff --git a/client/CDoc1.cpp b/client/CDoc1.cpp index 2fe10d39..d99e495d 100644 --- a/client/CDoc1.cpp +++ b/client/CDoc1.cpp @@ -133,7 +133,7 @@ CDoc1::CDoc1(const QString &path) //if(xml.name() == QLatin1String("KeyName")) // key->name = xml.readElementText(); // EncryptedData/KeyInfo/EncryptedKey/EncryptionMethod - else if(xml.name() == QLatin1String("EncryptionMethod")) + if(xml.name() == QLatin1String("EncryptionMethod")) key->method = xml.attributes().value(QLatin1String("Algorithm")).toString(); // EncryptedData/KeyInfo/EncryptedKey/KeyInfo/AgreementMethod else if(xml.name() == QLatin1String("AgreementMethod")) { @@ -310,16 +310,16 @@ std::shared_ptr CDoc1::getDecryptionKey(const QSslCertificate &cert) const std::shared_ptr k = std::static_pointer_cast(key); if(!ENC_MTH.contains(method) || k->cert != cert || - k->encrypted_fmk.isEmpty()) + k->encrypted_fmk.isEmpty()) continue; if(cert.publicKey().algorithm() == QSsl::Rsa && k->method == RSA_MTH) return k; if(cert.publicKey().algorithm() == QSsl::Ec && !k->publicKey.isEmpty() && - KWAES_SIZE.contains(k->method) /* && + KWAES_SIZE.contains(k->method) /* && k->derive == CONCATKDF_MTH && - k->agreement == AGREEMENT_MTH*/ ) + k->agreement == AGREEMENT_MTH*/ ) return k; } return {}; @@ -514,7 +514,7 @@ bool CDoc1::save(const QString &path) default: concatDigest = SHA512_MTH; break; } QByteArray encryptionKey = Crypto::concatKDF(SHA_MTH[concatDigest], KWAES_SIZE[encryptionMethod], - sharedSecret, props.value(QStringLiteral("DocumentFormat")).toUtf8() + SsDer + ckey->cert.toDer()); + sharedSecret, props.value(QStringLiteral("DocumentFormat")).toUtf8() + SsDer + ckey->cert.toDer()); #ifndef NDEBUG qDebug() << "ENC Ss" << SsDer.toHex(); qDebug() << "ENC Ksr" << sharedSecret.toHex(); @@ -539,7 +539,7 @@ bool CDoc1::save(const QString &path) writeElement(w, XENC11, QStringLiteral("ConcatKDFParams"), { {QStringLiteral("AlgorithmID"), QStringLiteral("00") + props.value(QStringLiteral("DocumentFormat")).toUtf8().toHex()}, {QStringLiteral("PartyUInfo"), QStringLiteral("00") + SsDer.toHex()}, - {QStringLiteral("PartyVInfo"), QStringLiteral("00") + ckey->cert.toDer().toHex()}, + {QStringLiteral("PartyVInfo"), QStringLiteral("00") + ckey->cert.toDer().toHex()}, }, [&]{ writeElement(w, DS, QStringLiteral("DigestMethod"), { {QStringLiteral("Algorithm"), concatDigest}, @@ -589,32 +589,32 @@ bool CDoc1::save(const QString &path) return true; } -QByteArray CDoc1::getFMK(const CKey &key, const QByteArray& secret) +QByteArray CDoc1::getFMK(const CKey &key, const QByteArray& /*secret*/) { if (key.type != CKey::Type::CDOC1) { setLastError(QStringLiteral("Not a CDoc1 key")); return {}; } - const CKeyCDoc1& ckey = static_cast(key); + const auto& ckey = static_cast(key); setLastError({}); QByteArray decryptedKey = qApp->signer()->decrypt([&ckey](QCryptoBackend *backend) { if(ckey.pk_type == CKey::PKType::RSA) - return backend->decrypt(ckey.encrypted_fmk, false); + return backend->decrypt(ckey.encrypted_fmk, false); return backend->deriveConcatKDF(ckey.publicKey, SHA_MTH[ckey.concatDigest], int(KWAES_SIZE[ckey.method]), ckey.AlgorithmID, ckey.PartyUInfo, ckey.PartyVInfo); -}); -if(decryptedKey.isEmpty()) -{ - setLastError(QStringLiteral("Failed to decrypt/derive key")); - return {}; -} -if(ckey.pk_type == CKey::PKType::RSA) -return decryptedKey; + }); + if(decryptedKey.isEmpty()) + { + setLastError(QStringLiteral("Failed to decrypt/derive key")); + return {}; + } + if(ckey.pk_type == CKey::PKType::RSA) + return decryptedKey; #ifndef NDEBUG - qDebug() << "DEC Ss" << ckey.publicKey.toHex(); + qDebug() << "DEC Ss" << ckey.publicKey.toHex(); qDebug() << "DEC ConcatKDF" << decryptedKey.toHex(); #endif -return Crypto::aes_unwrap(decryptedKey, ckey.encrypted_fmk); + return Crypto::aes_unwrap(decryptedKey, ckey.encrypted_fmk); } int CDoc1::version() diff --git a/client/CDoc2.cpp b/client/CDoc2.cpp index 6b88899d..7b7d310c 100644 --- a/client/CDoc2.cpp +++ b/client/CDoc2.cpp @@ -408,8 +408,8 @@ CDoc2::isCDoc2File(const QString& path) return true; } -CDoc2::CDoc2(const QString &_path) - : path(_path) +CDoc2::CDoc2(QString _path) + : path(std::move(_path)) /* : QFile(path) */ { using namespace cdoc20::recipients; @@ -558,7 +558,7 @@ CDoc2::CDoc2(const QString &_path) std::unique_ptr CDoc2::load(const QString& path) { - CDoc2 *cdoc = new CDoc2(path); + auto *cdoc = new CDoc2(path); return std::unique_ptr(cdoc); } @@ -566,7 +566,7 @@ CKey::DecryptionStatus CDoc2::canDecrypt(const QSslCertificate &cert) const { CKey::DecryptionStatus status = CKey::DecryptionStatus::CANNOT_DECRYPT; - for (std::shared_ptr key: keys) { + for (const std::shared_ptr &key: keys) { if (key->isTheSameRecipient(cert)) return CKey::CAN_DECRYPT; if (key->isSymmetric()) status = CKey::DecryptionStatus::NEED_KEY; } @@ -638,19 +638,19 @@ bool CDoc2::save(const QString &_path) return builder.CreateString(utf8.data(), size_t(utf8.length())); }; - auto sendToServer = [this] (const QString& keyserver_id, const QByteArray &recipient_id, const QByteArray &key_material, QLatin1String type) -> QString { - if(keyserver_id.isEmpty()) { - setLastError(QStringLiteral("keyserver_id cannot be empty")); - return {}; - } - QNetworkRequest req = cdoc20::req(keyserver_id); - if(req.url().isEmpty()) { - setLastError(QStringLiteral("No valid config found for keyserver_id: %1").arg(keyserver_id)); - return {}; - } - if(!cdoc20::checkConnection()) { - return {}; - } + auto sendToServer = [this] (const QString& keyserver_id, const QByteArray &recipient_id, const QByteArray &key_material, QLatin1String type) -> QString { + if(keyserver_id.isEmpty()) { + setLastError(QStringLiteral("keyserver_id cannot be empty")); + return {}; + } + QNetworkRequest req = cdoc20::req(keyserver_id); + if(req.url().isEmpty()) { + setLastError(QStringLiteral("No valid config found for keyserver_id: %1").arg(keyserver_id)); + return {}; + } + if(!cdoc20::checkConnection()) { + return {}; + } QScopedPointer nam(CheckConnection::setupNAM(req, Settings::CDOC2_POST_CERT)); QEventLoop e; QNetworkReply *reply = nam->post(req, QJsonDocument({ @@ -662,22 +662,22 @@ bool CDoc2::save(const QString &_path) e.exec(); QString transaction_id; if(reply->error() == QNetworkReply::NoError && - reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 201) { - transaction_id = QString::fromLatin1(reply->rawHeader("Location")).remove(QLatin1String("/key-capsules/")); - } else { - setLastError(reply->errorString()); - return {}; - } - if(transaction_id.isEmpty()) - setLastError(QStringLiteral("Failed to post key capsule")); - return transaction_id; + reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 201) { + transaction_id = QString::fromLatin1(reply->rawHeader("Location")).remove(QLatin1String("/key-capsules/")); + } else { + setLastError(reply->errorString()); + return {}; + } + if(transaction_id.isEmpty()) + setLastError(QStringLiteral("Failed to post key capsule")); + return transaction_id; }; - for(std::shared_ptr key: keys) { + for(const std::shared_ptr &key: keys) { if (!key->isPKI()) { return setLastError(QStringLiteral("Invalid key type")); } - const CKeyPKI& pki = static_cast(*key); + const auto& pki = static_cast(*key); if(pki.pk_type == CKey::PKType::RSA) { QByteArray kek = Crypto::random(fmk.size()); QByteArray xor_key = Crypto::xor_data(fmk, kek); @@ -691,36 +691,36 @@ bool CDoc2::save(const QString &_path) qDebug() << "xor" << xor_key.toHex(); qDebug() << "encrytpedKek" << encrytpedKek.toHex(); #endif - if(!Settings::CDOC2_USE_KEYSERVER) { - auto rsaPublicKey = cdoc20::recipients::CreateRSAPublicKeyCapsule(builder, - toVector(pki.rcpt_key), toVector(encrytpedKek)); - auto offs = cdoc20::header::CreateRecipientRecord(builder, - cdoc20::recipients::Capsule::RSAPublicKeyCapsule, rsaPublicKey.Union(), - toString(pki.label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); - recipients.push_back(offs); - } else { - QString keyserver_id = Settings::CDOC2_DEFAULT_KEYSERVER; - QString transaction_id = sendToServer(keyserver_id, pki.rcpt_key, encrytpedKek, QLatin1String("rsa")); - if (transaction_id.isEmpty()) return false; - auto rsaKeyServer = cdoc20::recipients::CreateRsaKeyDetails(builder, toVector(pki.rcpt_key)); - auto keyServer = cdoc20::recipients::CreateKeyServerCapsule(builder, - cdoc20::recipients::KeyDetailsUnion::RsaKeyDetails, - rsaKeyServer.Union(), toString(keyserver_id), toString(transaction_id)); - auto offs = cdoc20::header::CreateRecipientRecord(builder, - cdoc20::recipients::Capsule::KeyServerCapsule, keyServer.Union(), - toString(pki.label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); - recipients.push_back(offs); - } - } else { - auto publicKey = Crypto::fromECPublicKeyDer(pki.rcpt_key, NID_secp384r1); - if(!publicKey) return false; - auto ephKey = Crypto::genECKey(publicKey.get()); - QByteArray sharedSecret = Crypto::derive(ephKey.get(), publicKey.get()); - QByteArray ephPublicKeyDer = Crypto::toPublicKeyDer(ephKey.get()); - QByteArray kekPm = Crypto::extract(sharedSecret, KEKPREMASTER); - QByteArray info = KEK + cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) + pki.rcpt_key + ephPublicKeyDer; - QByteArray kek = Crypto::expand(kekPm, info, fmk.size()); - QByteArray xor_key = Crypto::xor_data(fmk, kek); + if(!Settings::CDOC2_USE_KEYSERVER) { + auto rsaPublicKey = cdoc20::recipients::CreateRSAPublicKeyCapsule(builder, + toVector(pki.rcpt_key), toVector(encrytpedKek)); + auto offs = cdoc20::header::CreateRecipientRecord(builder, + cdoc20::recipients::Capsule::RSAPublicKeyCapsule, rsaPublicKey.Union(), + toString(pki.label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); + recipients.push_back(offs); + } else { + QString keyserver_id = Settings::CDOC2_DEFAULT_KEYSERVER; + QString transaction_id = sendToServer(keyserver_id, pki.rcpt_key, encrytpedKek, QLatin1String("rsa")); + if (transaction_id.isEmpty()) return false; + auto rsaKeyServer = cdoc20::recipients::CreateRsaKeyDetails(builder, toVector(pki.rcpt_key)); + auto keyServer = cdoc20::recipients::CreateKeyServerCapsule(builder, + cdoc20::recipients::KeyDetailsUnion::RsaKeyDetails, + rsaKeyServer.Union(), toString(keyserver_id), toString(transaction_id)); + auto offs = cdoc20::header::CreateRecipientRecord(builder, + cdoc20::recipients::Capsule::KeyServerCapsule, keyServer.Union(), + toString(pki.label), toVector(xor_key), cdoc20::header::FMKEncryptionMethod::XOR); + recipients.push_back(offs); + } + } else { + auto publicKey = Crypto::fromECPublicKeyDer(pki.rcpt_key, NID_secp384r1); + if(!publicKey) return false; + auto ephKey = Crypto::genECKey(publicKey.get()); + QByteArray sharedSecret = Crypto::derive(ephKey.get(), publicKey.get()); + QByteArray ephPublicKeyDer = Crypto::toPublicKeyDer(ephKey.get()); + QByteArray kekPm = Crypto::extract(sharedSecret, KEKPREMASTER); + QByteArray info = KEK + cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) + pki.rcpt_key + ephPublicKeyDer; + QByteArray kek = Crypto::expand(kekPm, info, fmk.size()); + QByteArray xor_key = Crypto::xor_data(fmk, kek); #ifndef NDEBUG qDebug() << "publicKeyDer" << pki.rcpt_key.toHex(); qDebug() << "ephPublicKeyDer" << ephPublicKeyDer.toHex(); @@ -798,7 +798,7 @@ bool CDoc2::save(const QString &_path) } bool -CDoc2::save(QString _path, const std::vector& files, const QString& label, const QByteArray& secret, unsigned int kdf_iter) +CDoc2::save(const QString& _path, const std::vector& files, const QString& label, const QByteArray& secret, unsigned int kdf_iter) { QByteArray fmk = Crypto::extract(Crypto::random(KEY_LEN), SALT); QByteArray cek = Crypto::expand(fmk, CEK); @@ -939,7 +939,7 @@ QByteArray CDoc2::getFMK(const CKey &key, const QByteArray& secret) QByteArray kek; if (key.isSymmetric()) { // Symmetric key - const CKeySymmetric &sk = static_cast(key); + const auto &sk = static_cast(key); if (sk.kdf_iter > 0) { #ifndef NDEBUG qDebug() << "Password based symmetric key: " << key.label; @@ -963,13 +963,13 @@ QByteArray CDoc2::getFMK(const CKey &key, const QByteArray& secret) } } else { // Public/private key - const CKeyPKI &pki = static_cast(key); + const auto &pki = static_cast(key); QByteArray key_material; if(key.type == CKey::Type::SERVER) { - const CKeyServer &sk = static_cast(key); + const auto &sk = static_cast(key); key_material = fetchKeyMaterial(sk); } else if (key.type == CKey::PUBLIC_KEY) { - const CKeyPublicKey& pk = static_cast(key); + const auto& pk = static_cast(key); key_material = pk.key_material; } #ifndef NDEBUG @@ -984,7 +984,7 @@ QByteArray CDoc2::getFMK(const CKey &key, const QByteArray& secret) kek = qApp->signer()->decrypt([&pki, &key_material](QCryptoBackend *backend) { QByteArray kekPm = backend->deriveHMACExtract(key_material, KEKPREMASTER, KEY_LEN); #ifndef NDEBUG - qDebug() << "kekPm" << kekPm.toHex(); + qDebug() << "kekPm" << kekPm.toHex(); #endif QByteArray info = KEK + cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) + pki.rcpt_key + key_material; return Crypto::expand(kekPm, info, KEY_LEN); @@ -1008,11 +1008,11 @@ QByteArray CDoc2::getFMK(const CKey &key, const QByteArray& secret) qDebug() << "hhk" << hhk.toHex(); qDebug() << "hmac" << headerHMAC.toHex(); #endif - if(Crypto::sign_hmac(hhk, header_data) != headerHMAC) { - setLastError(QStringLiteral("CDoc 2.0 hash mismatch")); - return {}; - } - return fmk; + if(Crypto::sign_hmac(hhk, header_data) != headerHMAC) { + setLastError(QStringLiteral("CDoc 2.0 hash mismatch")); + return {}; + } + return fmk; } int CDoc2::version() diff --git a/client/CDoc2.h b/client/CDoc2.h index aae9cb9b..5213c200 100644 --- a/client/CDoc2.h +++ b/client/CDoc2.h @@ -36,7 +36,7 @@ class CDoc2 final: public CDoc, private QObject /*, private QFile */ { bool save(const QString &path) final; // Write payload encrypted with sinbgle symmetric key - static bool save(QString path, const std::vector& files, const QString& label, const QByteArray& secret, unsigned int kdf_iter); + static bool save(const QString& path, const std::vector& files, const QString& label, const QByteArray& secret, unsigned int kdf_iter); QByteArray getFMK(const CKey &key, const QByteArray& secret) final; int version() final; @@ -44,7 +44,7 @@ class CDoc2 final: public CDoc, private QObject /*, private QFile */ { static std::unique_ptr load(const QString& _path); private: - CDoc2(const QString &path); + CDoc2(QString path); QString path; QByteArray header_data, headerHMAC; diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 1ad15cc8..44377dff 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -89,7 +89,6 @@ add_executable(${PROJECT_NAME} WIN32 MACOSX_BUNDLE TokenData.cpp TokenData.h Utils.h - dialogs/PasswordDialog.h dialogs/PasswordDialog.cpp dialogs/PasswordDialog.ui ) target_link_libraries(${PROJECT_NAME} diff --git a/client/CryptoDoc.cpp b/client/CryptoDoc.cpp index 36dbcf88..fcba866f 100644 --- a/client/CryptoDoc.cpp +++ b/client/CryptoDoc.cpp @@ -125,7 +125,7 @@ bool CDocumentModel::addFile(const QString &file, const QString &mime) [&fileName](const auto &containerFile) { return containerFile.name == fileName; })) { WarningDialog::show(DocumentModel::tr("Cannot add the file to the envelope. File '%1' is already in container.") - .arg(FileDialog::normalized(fileName))); + .arg(FileDialog::normalized(fileName))); return false; } @@ -231,11 +231,11 @@ CKey::isTheSameRecipient(const CKey& other) const { QByteArray this_key, other_key; if (this->isCertificate()) { - const CKeyCert& ckc = static_cast(*this); + const auto& ckc = static_cast(*this); this_key = ckc.cert.publicKey().toDer(); } if (other.isCertificate()) { - const CKeyCert& ckc = static_cast(other); + const auto& ckc = static_cast(other); other_key = ckc.cert.publicKey().toDer(); } if (this_key.isEmpty() || other_key.isEmpty()) return false; @@ -246,7 +246,7 @@ bool CKey::isTheSameRecipient(const QSslCertificate &cert) const { if (!isPKI()) return false; - const CKeyPKI& pki = static_cast(*this); + const auto& pki = static_cast(*this); QByteArray this_key = pki.rcpt_key; QSslKey k = cert.publicKey(); QByteArray other_key = Crypto::toPublicKeyDer(k); @@ -288,7 +288,7 @@ void CKeyCert::setCert(const QSslCertificate &c) } std::shared_ptr -CKeyServer::fromKey(QByteArray _key, PKType _pk_type) { +CKeyServer::fromKey(const QByteArray &_key, PKType _pk_type) { return std::shared_ptr(new CKeyServer(_key, _pk_type)); } @@ -308,11 +308,11 @@ CryptoDoc::supportsSymmetricKeys() const return d->cdoc->version() >= 2; } -bool CryptoDoc::addKey(std::shared_ptr key ) +bool CryptoDoc::addKey(const std::shared_ptr &key) { if(d->isEncryptedWarning()) return false; - for (std::shared_ptr k: d->cdoc->keys) { + for (const std::shared_ptr &k: d->cdoc->keys) { if (k->isTheSameRecipient(*key)) { WarningDialog::show(tr("Key already exists")); return false; diff --git a/client/CryptoDoc.h b/client/CryptoDoc.h index c92ab218..9a834405 100644 --- a/client/CryptoDoc.h +++ b/client/CryptoDoc.h @@ -69,7 +69,7 @@ struct CKey bool isTheSameRecipient(const QSslCertificate &cert) const; protected: - CKey(Type _type) : type(_type) {}; + CKey(Type _type) : type(_type) {} private: bool operator==(const CKey &other) const { return false; } }; @@ -83,9 +83,9 @@ struct CKeySymmetric : public CKey { // PBKDF QByteArray pw_salt; // 0 symmetric key, >0 password - int32_t kdf_iter; + int32_t kdf_iter{}; - CKeySymmetric(const QByteArray& _salt) : CKey(Type::SYMMETRIC_KEY), salt(_salt), kdf_iter(0) {} + CKeySymmetric(QByteArray _salt) : CKey(Type::SYMMETRIC_KEY), salt(std::move(_salt)) {} }; // Base PKI key @@ -98,8 +98,8 @@ struct CKeyPKI : public CKey { QByteArray rcpt_key; protected: - CKeyPKI(Type _type) : CKey(_type), pk_type(PKType::ECC) {}; - CKeyPKI(Type _type, PKType _pk_type, QByteArray _rcpt_key) : CKey(_type), pk_type(_pk_type), rcpt_key(_rcpt_key) {}; + CKeyPKI(Type _type) : CKey(_type), pk_type(PKType::ECC) {} + CKeyPKI(Type _type, PKType _pk_type, QByteArray _rcpt_key) : CKey(_type), pk_type(_pk_type), rcpt_key(std::move(_rcpt_key)) {} }; @@ -110,12 +110,12 @@ struct CKeyPKI : public CKey { struct CKeyCert : public CKeyPKI { QSslCertificate cert; - CKeyCert(const QSslCertificate &cert) : CKeyCert(CKey::Type::CERTIFICATE, cert) {}; + CKeyCert(const QSslCertificate &cert) : CKeyCert(CKey::Type::CERTIFICATE, cert) {} void setCert(const QSslCertificate &c); protected: - CKeyCert(Type _type) : CKeyPKI(_type) {}; + CKeyCert(Type _type) : CKeyPKI(_type) {} CKeyCert(Type _type, const QSslCertificate &cert); }; @@ -129,7 +129,7 @@ struct CKeyCDoc1 : public CKeyCert { QString concatDigest, method; QByteArray AlgorithmID, PartyUInfo, PartyVInfo; - CKeyCDoc1() : CKeyCert(Type::CDOC1) {}; + CKeyCDoc1() : CKeyCert(Type::CDOC1) {} }; // CDoc2 PKI key with key material @@ -140,7 +140,7 @@ struct CKeyPublicKey : public CKeyPKI { // Either ECC public key or RSA encrypted kek QByteArray key_material; - CKeyPublicKey(PKType _pk_type, QByteArray _rcpt_key) : CKeyPKI(Type::PUBLIC_KEY, _pk_type, _rcpt_key) {}; + CKeyPublicKey(PKType _pk_type, QByteArray _rcpt_key) : CKeyPKI(Type::PUBLIC_KEY, _pk_type, std::move(_rcpt_key)) {} }; // CDoc2 PKI key with server info @@ -151,9 +151,9 @@ struct CKeyServer : public CKeyPKI { // Server info QString keyserver_id, transaction_id; - static std::shared_ptr fromKey(QByteArray _key, PKType _pk_type); + static std::shared_ptr fromKey(const QByteArray &_key, PKType _pk_type); protected: - CKeyServer(QByteArray _rcpt_key, PKType _pk_type) : CKeyPKI(Type::SERVER, _pk_type, _rcpt_key) {}; + CKeyServer(QByteArray _rcpt_key, PKType _pk_type) : CKeyPKI(Type::SERVER, _pk_type, std::move(_rcpt_key)) {} }; class CDoc @@ -189,7 +189,7 @@ class CryptoDoc final: public QObject ~CryptoDoc() final; bool supportsSymmetricKeys() const; - bool addKey(std::shared_ptr key ); + bool addKey(const std::shared_ptr &key); bool canDecrypt(const QSslCertificate &cert); void clear(const QString &file = {}); bool decrypt(std::shared_ptr key, const QByteArray& secret); diff --git a/client/MainWindow.cpp b/client/MainWindow.cpp index 8eaa0366..a9b42b73 100644 --- a/client/MainWindow.cpp +++ b/client/MainWindow.cpp @@ -260,8 +260,7 @@ void MainWindow::decrypt(std::shared_ptr key) if (cryptoDoc->decrypt(key, secret)) { ui->cryptoContainerPage->transition(cryptoDoc, qApp->signer()->tokenauth().cert()); - auto *notification = new FadeInNotification(this, WHITE, MANTIS, 110); - notification->start( tr("Decryption succeeded!"), 750, 3000, 1200 ); + FadeInNotification::success(ui->topBar, tr("Decryption succeeded!")); } } @@ -332,18 +331,16 @@ bool MainWindow::encrypt(bool askForKey) if(!p.exec()) return false; QString label = p.label(); QByteArray secret = p.secret(); - bool result; if (p.type == PasswordDialog::Type::PASSWORD) { WaitDialogHolder waitDialog(this, tr("Encrypting")); return cryptoDoc->encrypt(cryptoDoc->fileName(), label, secret, 65536); - } else { - WaitDialogHolder waitDialog(this, tr("Encrypting")); - return cryptoDoc->encrypt(cryptoDoc->fileName(), label, secret, 0); } - } else { WaitDialogHolder waitDialog(this, tr("Encrypting")); - return cryptoDoc->encrypt(); + return cryptoDoc->encrypt(cryptoDoc->fileName(), label, secret, 0); } + WaitDialogHolder waitDialog(this, tr("Encrypting")); + return cryptoDoc->encrypt(); + } void MainWindow::mouseReleaseEvent(QMouseEvent *event) @@ -1018,7 +1015,7 @@ bool MainWindow::validateFiles(const QString &container, const QStringList &file [containerInfo] (const QString &file) { return containerInfo == QFileInfo(file); })) return true; auto *dlg = new WarningDialog(tr("Cannot add container to same container\n%1") - .arg(FileDialog::normalized(container)), this); + .arg(FileDialog::normalized(container)), this); dlg->setCancelText(WarningDialog::Cancel); dlg->open(); return false; diff --git a/client/widgets/AddressItem.cpp b/client/widgets/AddressItem.cpp index bef232e9..cb4fa92a 100644 --- a/client/widgets/AddressItem.cpp +++ b/client/widgets/AddressItem.cpp @@ -39,7 +39,7 @@ class AddressItem::Private: public Ui::AddressItem bool yourself = false; }; -AddressItem::AddressItem(std::shared_ptr key, QWidget *parent, bool showIcon) +AddressItem::AddressItem(const std::shared_ptr& key, QWidget *parent, bool showIcon) : Item(parent) , ui(new Private) { @@ -80,7 +80,7 @@ AddressItem::AddressItem(std::shared_ptr key, QWidget *parent, bool showIc ui->label = key->label.toHtmlEscaped(); } if(ui->label.isEmpty() && ui->key->type == CKey::PUBLIC_KEY) { - const CKeyPublicKey& pk = static_cast(*ui->key); + const auto& pk = static_cast(*ui->key); ui->label = pk.key_material; } setIdType(); @@ -113,12 +113,12 @@ bool AddressItem::eventFilter(QObject *o, QEvent *e) return Item::eventFilter(o, e); } -const std::shared_ptr AddressItem::getKey() const +std::shared_ptr AddressItem::getKey() const { return ui->key; } -void AddressItem::idChanged(std::shared_ptr key) +void AddressItem::idChanged(const std::shared_ptr& key) { ui->yourself = key->isTheSameRecipient(*ui->key); setName(); @@ -197,24 +197,23 @@ void AddressItem::setIdType() str += QStringLiteral(" - "); DateTime date(cert.expiryDate().toLocalTime()); ui->idType->setText(QStringLiteral("%1%2 %3").arg(str, - cert.isValid() ? tr("Expires on") : tr("Expired on"), - date.formatDate(QStringLiteral("dd. MMMM yyyy")))); + cert.isValid() ? tr("Expires on") : tr("Expired on"), + date.formatDate(QStringLiteral("dd. MMMM yyyy")))); } else { - QString type = (pki->pk_type == CKey::PKType::RSA) ? "RSA" : "ECC"; + QString type = (pki->pk_type == CKey::PKType::RSA) ? QStringLiteral("RSA") : QStringLiteral("ECC"); ui->idType->setHidden(false); - ui->idType->setText(type + " public key"); + ui->idType->setText(type + tr(" public key")); } } else if (ui->key->isSymmetric()) { std::shared_ptr ckd = std::static_pointer_cast(ui->key); ui->idType->setHidden(false); if (ckd->kdf_iter > 0) { - ui->idType->setText("Password derived key"); + ui->idType->setText(tr("Password derived key")); } else { - ui->idType->setText("Symmetric key"); + ui->idType->setText(tr("Symmetric key")); } } else { ui->idType->setHidden(false); - ui->idType->setText("Unknown key type"); + ui->idType->setText(tr("Unknown key type")); } } - diff --git a/client/widgets/AddressItem.h b/client/widgets/AddressItem.h index 5fb3a8cc..a88ed531 100644 --- a/client/widgets/AddressItem.h +++ b/client/widgets/AddressItem.h @@ -38,11 +38,11 @@ class AddressItem final : public Item Added, }; - explicit AddressItem(std::shared_ptr k, QWidget *parent = {}, bool showIcon = false); + explicit AddressItem(const std::shared_ptr& k, QWidget *parent = {}, bool showIcon = false); ~AddressItem() final; - const std::shared_ptr getKey() const; - void idChanged(std::shared_ptr cert); + std::shared_ptr getKey() const; + void idChanged(const std::shared_ptr& cert); void idChanged(const SslCertificate &cert) final; void initTabOrder(QWidget *item) final; QWidget* lastTabWidget() final; diff --git a/client/widgets/AddressItem.ui b/client/widgets/AddressItem.ui index be7d9adf..de3704ed 100644 --- a/client/widgets/AddressItem.ui +++ b/client/widgets/AddressItem.ui @@ -81,31 +81,6 @@ background-color: #00AEE5; 0 - - - - false - - - - 0 - 25 - - - - - 16777215 - 25 - - - - Added - - - ADDED - - - @@ -125,28 +100,55 @@ background-color: #00AEE5; - - - - - 0 - 25 - + + + + + Roboto + 14 + false + false + - - - 16777215 - 25 - + + Qt::TabFocus - - PointingHandCursor + + border: none; - - Add + + MARI MAASIKAS MUSTIKAS 48405050123 (Sina ise) + + + true + + + + + + + + Roboto + 11 + false + false + + + + Qt::TabFocus + + + color: #727679;border: none; - ADD + ID-card + + + + + + + DECRYPT @@ -186,72 +188,70 @@ background-color: #00AEE5; - - - - - Roboto - 11 - false - false - + + + + false - - Qt::TabFocus + + + 0 + 25 + - - color: #727679;border: none; + + + 16777215 + 25 + + + + Added - ID-card + ADDED - - - - - Roboto - 14 - false - false - - - - Qt::TabFocus + + + + + 0 + 25 + - - border: none; + + + 16777215 + 25 + - - MARI MAASIKAS MUSTIKAS 48405050123 (Sina ise) + + PointingHandCursor - - true + + Add - - - - - DECRYPT + ADD
- - QSvgWidget - QWidget -
QSvgWidget
- 1 -
LabelButton QToolButton
widgets/LabelButton.h
+ + QSvgWidget + QWidget +
QtSvg/QSvgWidget
+ 1 +
diff --git a/client/widgets/ContainerPage.cpp b/client/widgets/ContainerPage.cpp index 7b078301..bdd78397 100644 --- a/client/widgets/ContainerPage.cpp +++ b/client/widgets/ContainerPage.cpp @@ -417,7 +417,7 @@ void ContainerPage::updatePanes(ria::qdigidoc4::ContainerState state, CryptoDoc for(QWidget *button: buttons) button->setVisible(visible); }; - switch(state) + switch( state ) { case UnsignedContainer: cancelText = QT_TR_NOOP("CANCEL"); @@ -498,4 +498,3 @@ void ContainerPage::translateLabels() ui->convert->setText(tr(convertText)); ui->convert->setAccessibleName(tr(convertText).toLower()); } - diff --git a/client/widgets/ContainerPage.ui b/client/widgets/ContainerPage.ui index 05cfd153..a1832cc1 100644 --- a/client/widgets/ContainerPage.ui +++ b/client/widgets/ContainerPage.ui @@ -214,8 +214,6 @@ background-color: #F4F5F6; Roboto Condensed 12 - false - false @@ -305,8 +303,6 @@ border: none; Roboto Condensed 12 - false - false @@ -344,8 +340,6 @@ border: none; Roboto Condensed 12 - false - false @@ -382,8 +376,6 @@ border: none; 12 - false - false @@ -422,8 +414,6 @@ border: none; Roboto Condensed 12 - false - false @@ -466,17 +456,17 @@ border: none;
+ + LabelButton + QToolButton +
widgets/LabelButton.h
+
ItemList QScrollArea
widgets/ItemList.h
1
- - LabelButton - QToolButton -
widgets/LabelButton.h
-
FileList QScrollArea diff --git a/common b/common index 27319130..fbe6fab8 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 27319130f517cc085531d59beda5779b5b223b4e +Subproject commit fbe6fab8bddab463e101b857c31c69ee9595c16b