Skip to content

Commit

Permalink
Show improved AddressItem widget (#1306)
Browse files Browse the repository at this point in the history
* Icon

* Unsupported algorithm or recipient type

CDOC-3

Signed-off-by: Raul Metsma <raul@metsma.ee>

* Update AddressItem UI

Signed-off-by: Raul Metsma <raul@metsma.ee>

* Update to new UI

Signed-off-by: Raul Metsma <raul@metsma.ee>

---------

Signed-off-by: Raul Metsma <raul@metsma.ee>
  • Loading branch information
metsma authored Dec 9, 2024
1 parent 86eb82d commit 2646a7a
Show file tree
Hide file tree
Showing 42 changed files with 547 additions and 477 deletions.
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

0 comments on commit 2646a7a

Please sign in to comment.