Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show improved AddressItem widget #1306

Merged
merged 4 commits into from
Dec 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 14 additions & 27 deletions client/CDoc1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,6 @@ const QString CDoc1::AES128GCM_MTH = QStringLiteral("http://www.w3.org/2009/xmle
const QString CDoc1::AES192GCM_MTH = QStringLiteral("http://www.w3.org/2009/xmlenc11#aes192-gcm");
const QString CDoc1::AES256GCM_MTH = QStringLiteral("http://www.w3.org/2009/xmlenc11#aes256-gcm");
const QString CDoc1::RSA_MTH = QStringLiteral("http://www.w3.org/2001/04/xmlenc#rsa-1_5");
const QString CDoc1::KWAES128_MTH = QStringLiteral("http://www.w3.org/2001/04/xmlenc#kw-aes128");
const QString CDoc1::KWAES192_MTH = QStringLiteral("http://www.w3.org/2001/04/xmlenc#kw-aes192");
const QString CDoc1::KWAES256_MTH = QStringLiteral("http://www.w3.org/2001/04/xmlenc#kw-aes256");
const QString CDoc1::CONCATKDF_MTH = QStringLiteral("http://www.w3.org/2009/xmlenc11#ConcatKDF");
const QString CDoc1::AGREEMENT_MTH = QStringLiteral("http://www.w3.org/2009/xmlenc11#ECDH-ES");
Expand All @@ -66,7 +64,6 @@ const QHash<QString, const EVP_CIPHER*> CDoc1::ENC_MTH{
const QHash<QString, QCryptographicHash::Algorithm> CDoc1::SHA_MTH{
{SHA256_MTH, QCryptographicHash::Sha256}, {SHA384_MTH, QCryptographicHash::Sha384}, {SHA512_MTH, QCryptographicHash::Sha512}
};
const QHash<QString, quint32> CDoc1::KWAES_SIZE{{KWAES128_MTH, 16}, {KWAES192_MTH, 24}, {KWAES256_MTH, 32}};

CDoc1::CDoc1(const QString &path)
: QFile(path)
Expand Down Expand Up @@ -108,7 +105,6 @@ CDoc1::CDoc1(const QString &path)
return;

CKey key;
key.id = xml.attributes().value(QLatin1String("Id")).toString();
key.recipient = xml.attributes().value(QLatin1String("Recipient")).toString();
while(!xml.atEnd())
{
Expand All @@ -117,18 +113,17 @@ CDoc1::CDoc1(const QString &path)
break;
if(!xml.isStartElement())
continue;
// EncryptedData/KeyInfo/KeyName
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();
if(xml.name() == QLatin1String("EncryptionMethod"))
{
auto method = xml.attributes().value(QLatin1String("Algorithm"));
key.unsupported = std::max(key.unsupported, method != KWAES256_MTH && method != RSA_MTH);
}
// EncryptedData/KeyInfo/EncryptedKey/KeyInfo/AgreementMethod
else if(xml.name() == QLatin1String("AgreementMethod"))
key.agreement = xml.attributes().value(QLatin1String("Algorithm")).toString();
key.unsupported = std::max(key.unsupported, xml.attributes().value(QLatin1String("Algorithm")) != AGREEMENT_MTH);
// EncryptedData/KeyInfo/EncryptedKey/KeyInfo/AgreementMethod/KeyDerivationMethod
else if(xml.name() == QLatin1String("KeyDerivationMethod"))
key.derive = xml.attributes().value(QLatin1String("Algorithm")).toString();
key.unsupported = std::max(key.unsupported, xml.attributes().value(QLatin1String("Algorithm")) != CONCATKDF_MTH);
// EncryptedData/KeyInfo/EncryptedKey/KeyInfo/AgreementMethod/KeyDerivationMethod/ConcatKDFParams
else if(xml.name() == QLatin1String("ConcatKDFParams"))
{
Expand Down Expand Up @@ -273,16 +268,13 @@ CKey CDoc1::canDecrypt(const QSslCertificate &cert) const
{
if(!ENC_MTH.contains(method) ||
k.cert != cert ||
k.cipher.isEmpty())
k.cipher.isEmpty() ||
k.unsupported)
continue;
if(cert.publicKey().algorithm() == QSsl::Rsa &&
k.method == RSA_MTH)
if(cert.publicKey().algorithm() == QSsl::Rsa)
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())
return k;
}
return {};
Expand Down Expand Up @@ -432,8 +424,6 @@ bool CDoc1::save(const QString &path)
for(const CKey &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);
QByteArray cipher;
Expand All @@ -446,8 +436,6 @@ 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);
writeElement(w, DS, QStringLiteral("X509Data"), [&]{
writeBase64Element(w, DS, QStringLiteral("X509Certificate"), k.cert.toDer());
});
Expand All @@ -464,14 +452,13 @@ bool CDoc1::save(const QString &path)
QByteArray oid = Crypto::curve_oid(peerPKey);
QByteArray SsDer = Crypto::toPublicKeyDer(priv.get());

const QString encryptionMethod = KWAES256_MTH;
QString concatDigest = SHA384_MTH;
switch((SsDer.size() - 1) / 2) {
case 32: concatDigest = SHA256_MTH; break;
case 48: concatDigest = SHA384_MTH; break;
default: concatDigest = SHA512_MTH; break;
}
QByteArray encryptionKey = Crypto::concatKDF(SHA_MTH[concatDigest], KWAES_SIZE[encryptionMethod],
QByteArray encryptionKey = Crypto::concatKDF(SHA_MTH[concatDigest],
sharedSecret, props.value(QStringLiteral("DocumentFormat")).toUtf8() + SsDer + k.cert.toDer());
#ifndef NDEBUG
qDebug() << "ENC Ss" << SsDer.toHex();
Expand All @@ -484,7 +471,7 @@ bool CDoc1::save(const QString &path)
return;

writeElement(w, DENC, QStringLiteral("EncryptionMethod"), {
{QStringLiteral("Algorithm"), encryptionMethod},
{QStringLiteral("Algorithm"), KWAES256_MTH},
});
writeElement(w, DS, QStringLiteral("KeyInfo"), [&]{
writeElement(w, DENC, QStringLiteral("AgreementMethod"), {
Expand Down Expand Up @@ -553,7 +540,7 @@ QByteArray CDoc1::transportKey(const CKey &key)
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);
key.AlgorithmID, key.PartyUInfo, key.PartyVInfo);
});
if(decryptedKey.isEmpty())
{
Expand Down
4 changes: 1 addition & 3 deletions client/CDoc1.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,10 @@ class CDoc1 final: public CDoc, private QFile
static const QString
AES128CBC_MTH, AES192CBC_MTH, AES256CBC_MTH,
AES128GCM_MTH, AES192GCM_MTH, AES256GCM_MTH,
KWAES128_MTH, KWAES192_MTH, KWAES256_MTH,
SHA256_MTH, SHA384_MTH, SHA512_MTH,
RSA_MTH, CONCATKDF_MTH, AGREEMENT_MTH;
RSA_MTH, CONCATKDF_MTH, AGREEMENT_MTH, KWAES256_MTH;
static const QString DS, DENC, DSIG11, XENC11;
static const QString MIME_ZLIB, MIME_DDOC, MIME_DDOC_OLD;
static const QHash<QString, const EVP_CIPHER*> ENC_MTH;
static const QHash<QString, QCryptographicHash::Algorithm> SHA_MTH;
static const QHash<QString, quint32> KWAES_SIZE;
};
38 changes: 20 additions & 18 deletions client/CDoc2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,8 @@ namespace cdoc20 {
io->skip(padding(f.size));
if(!readHeader() || h.isNull() || !h.verify())
return {};
if(f.name.startsWith(QLatin1String("./PaxHeaders.X")))
f.name = QString::fromUtf8(h.name.data(), std::min<int>(h.name.size(), int(strlen(h.name.data()))));
f.size = fromOctal(h.size);
for(const QByteArray &data: paxData.split('\n'))
{
Expand Down Expand Up @@ -432,26 +434,23 @@ CDoc2::CDoc2(const QString &path)
for(const auto *recipient: *recipients){
if(recipient->fmk_encryption_method() != FMKEncryptionMethod::XOR)
{
keys.append(CKey::Unsupported);
qWarning() << "Unsupported FMK encryption method: skipping";
continue;
}
auto fillRecipient = [&] (auto key, bool isRSA) {
auto fillRecipient = [&] (auto key, bool isRSA, bool unsupported = false) {
CKey k(toByteArray(key->recipient_public_key()), isRSA);
k.recipient = toString(recipient->key_label());
k.cipher = toByteArray(recipient->encrypted_fmk());
k.unsupported = unsupported;
return k;
};
switch(recipient->capsule_type())
{
case Capsule::ECCPublicKeyCapsule:
if(const auto *key = recipient->capsule_as_ECCPublicKeyCapsule())
{
if(key->curve() != EllipticCurve::secp384r1)
{
qWarning() << "Unsupported ECC curve: skipping";
continue;
}
CKey k = fillRecipient(key, false);
CKey k = fillRecipient(key, false, key->curve() != EllipticCurve::secp384r1);
k.publicKey = toByteArray(key->sender_public_key());
keys.append(std::move(k));
}
Expand All @@ -467,8 +466,8 @@ CDoc2::CDoc2(const QString &path)
case Capsule::KeyServerCapsule:
if(const auto *server = recipient->capsule_as_KeyServerCapsule())
{
auto fillKeyServer = [&] (auto key, bool isRSA) {
CKey k = fillRecipient(key, isRSA);
auto fillKeyServer = [&] (auto key, bool isRSA, bool unsupported = false) {
CKey k = fillRecipient(key, isRSA, unsupported);
k.keyserver_id = toString(server->keyserver_id());
k.transaction_id = toString(server->transaction_id());
return k;
Expand All @@ -477,29 +476,31 @@ CDoc2::CDoc2(const QString &path)
{
case ServerDetailsUnion::ServerEccDetails:
if(const auto *eccDetails = server->recipient_key_details_as_ServerEccDetails())
{
if(eccDetails->curve() == EllipticCurve::secp384r1)
keys.append(fillKeyServer(eccDetails, false));
}
keys.append(fillKeyServer(eccDetails, false, eccDetails->curve() != EllipticCurve::secp384r1));
break;
case ServerDetailsUnion::ServerRsaDetails:
if(const auto *rsaDetails = server->recipient_key_details_as_ServerRsaDetails())
keys.append(fillKeyServer(rsaDetails, true));
break;
default:
keys.append(CKey::Unsupported);
qWarning() << "Unsupported Key Server Details: skipping";
}
}
break;
default:
keys.append(CKey::Unsupported);
qWarning() << "Unsupported Key Details: skipping";
}
}
}

CKey CDoc2::canDecrypt(const QSslCertificate &cert) const
{
return keys.value(keys.indexOf(CKey(cert)));
auto key = keys.value(keys.indexOf(CKey(cert)));
if(key.unsupported || (!key.transaction_id.isEmpty() && cert.expiryDate() <= QDateTime::currentDateTimeUtc()))
return {};
return key;
}

bool CDoc2::decryptPayload(const QByteArray &fmk)
Expand Down Expand Up @@ -562,6 +563,7 @@ bool CDoc2::save(const QString &path)
if(!cdoc20::checkConnection())
return false;
QScopedPointer<QNetworkAccessManager,QScopedPointerDeleteLater> nam(CheckConnection::setupNAM(req, Settings::CDOC2_POST_CERT));
req.setRawHeader("x-expiry-time", QDateTime::currentDateTimeUtc().addMonths(6).toString(Qt::ISODate).toLatin1());
QEventLoop e;
QNetworkReply *reply = nam->post(req, QJsonDocument({
{QLatin1String("recipient_id"), QLatin1String(recipient_id.toBase64())},
Expand Down Expand Up @@ -602,7 +604,7 @@ bool CDoc2::save(const QString &path)
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));
toString(key.toKeyLabel()), toVector(xor_key), cdoc20::Header::FMKEncryptionMethod::XOR));
continue;
}

Expand All @@ -614,7 +616,7 @@ bool CDoc2::save(const QString &path)
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));
toString(key.toKeyLabel()), toVector(xor_key), cdoc20::Header::FMKEncryptionMethod::XOR));
continue;
}

Expand Down Expand Up @@ -642,7 +644,7 @@ bool CDoc2::save(const QString &path)
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));
toString(key.toKeyLabel()), toVector(xor_key), cdoc20::Header::FMKEncryptionMethod::XOR));
continue;
}

Expand All @@ -655,7 +657,7 @@ bool CDoc2::save(const QString &path)
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));
toString(key.toKeyLabel()), toVector(xor_key), cdoc20::Header::FMKEncryptionMethod::XOR));
}

auto offset = cdoc20::Header::CreateHeader(builder, builder.CreateVector(recipients),
Expand Down
3 changes: 2 additions & 1 deletion client/Crypto.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,10 +144,11 @@ QByteArray Crypto::cipher(const EVP_CIPHER *cipher, const QByteArray &key, QByte
return data;
}

QByteArray Crypto::concatKDF(QCryptographicHash::Algorithm hashAlg, quint32 keyDataLen, const QByteArray &z, const QByteArray &otherInfo)
QByteArray Crypto::concatKDF(QCryptographicHash::Algorithm hashAlg, const QByteArray &z, const QByteArray &otherInfo)
{
if(z.isEmpty())
return z;
quint32 keyDataLen = 32;
auto hashLen = quint32(QCryptographicHash::hashLength(hashAlg));
auto reps = quint32(std::ceil(double(keyDataLen) / double(hashLen)));
QCryptographicHash md(hashAlg);
Expand Down
3 changes: 1 addition & 2 deletions client/Crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,7 @@ class Crypto
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 curve_oid(EVP_PKEY *key);
static QByteArray concatKDF(QCryptographicHash::Algorithm digestMethod,
quint32 keyDataLen, const QByteArray &z, const QByteArray &otherInfo);
static QByteArray concatKDF(QCryptographicHash::Algorithm digestMethod, 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);
Expand Down
60 changes: 60 additions & 0 deletions client/CryptoDoc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include <QtCore/QRegularExpression>
#include <QtCore/QThread>
#include <QtCore/QUrl>
#include <QtCore/QUrlQuery>
#include <QtGui/QDesktopServices>
#include <QtNetwork/QSslKey>
#include <QtWidgets/QMessageBox>
Expand Down Expand Up @@ -217,6 +218,10 @@ QString CDocumentModel::save(int row, const QString &path) const
return fileName;
}

CKey::CKey(Tag)
: unsupported(true)
{}

CKey::CKey(const QSslCertificate &c)
{
setCert(c);
Expand Down Expand Up @@ -246,6 +251,61 @@ void CKey::setCert(const QSslCertificate &c)
isRSA = k.algorithm() == QSsl::Rsa;
}

QHash<QString, QString> CKey::fromKeyLabel() const
{
QHash<QString,QString> result;
if(!recipient.startsWith(QLatin1String("data:"), Qt::CaseInsensitive))
return result;
QString payload = recipient.mid(5);
QString mimeType;
QString encoding;
if(auto pos = payload.indexOf(','); pos != -1)
{
mimeType = payload.left(pos);
payload = payload.mid(pos + 1);
if(auto header = mimeType.split(';'); header.size() == 2)
{
mimeType = header.value(0);
encoding = header.value(1);
}
}
if(!mimeType.isEmpty() && mimeType != QLatin1String("application/x-www-form-urlencoded"))
return result;
if(encoding == QLatin1String("base64"))
payload = QByteArray::fromBase64(payload.toLatin1());
;
for(const auto &[key,value]: QUrlQuery(payload).queryItems(QUrl::FullyDecoded))
result[key.toLower()] = value;
if(!result.contains(QStringLiteral("type")) || !result.contains(QStringLiteral("v")))
result.clear();
return result;
}

QString CKey::toKeyLabel() const
{
if(cert.isNull())
return recipient;
QDateTime exp = cert.expiryDate();
if(Settings::CDOC2_USE_KEYSERVER)
exp = std::min(exp, QDateTime::currentDateTimeUtc().addMonths(6));
auto escape = [](QString data) { return data.replace(',', QLatin1String("%2C")); };
QString type = QStringLiteral("ID-card");
if(auto t = SslCertificate(cert).type(); t & SslCertificate::EResidentSubType)
type = QStringLiteral("Digi-ID E-RESIDENT");
else if(t & SslCertificate::DigiIDType)
type = QStringLiteral("Digi-ID");
QUrlQuery q;
q.setQueryItems({
{QStringLiteral("v"), QString::number(1)},
{QStringLiteral("type"), type},
{QStringLiteral("serial_number"), escape(cert.subjectInfo("serialNumber").join(','))},
{QStringLiteral("cn"), escape(cert.subjectInfo("CN").join(','))},
{QStringLiteral("server_exp"), QString::number(exp.toSecsSinceEpoch())},
});
return "data:" + q.query(QUrl::FullyEncoded);
}



CryptoDoc::CryptoDoc( QObject *parent )
: QObject(parent)
Expand Down
Loading
Loading