Add qHash(QSslCertificate) overload
qsslsocket_winrt.cpp defined it locally, which runs the risk of clashes with a potential user-defined qHash(QSslCertificate), so make it public. Also, the implementation in qsslsocket_winrt.cpp simply hashed the handle(), which violates the principle that equal instances must hash to the same value. Also, for some platforms, the implementation returns nullptr unconditionally, which, while not violating the above-mentioned principle, will make all users of the hash have worst-case complexity. To calculate a meaningful hash, therefore, the certificate needs to be inspected deeper than just the handle. For OpenSSL, we use X509::sha1_hash, which also X509_cmp uses internally to determine inequality (it checks more stuff, but if X059::sha1_hash is different, X509_cmp() returns non-zero, which is sufficient for the purposes of qHash()). sha1_hash may not be up-to-date, though, so we call X509_cmp to make it valid. Ugh. For WinRT/Qt, we use the DER encoding, as that is the native storage format used in QSslCertificate. This is not equivalent to the implementation used in qsslsocket_winrt.cpp before, but since handle() == handle() => toDer() == toDer(), it should not be a problem. [ChangeLog][QtNetwork][QSslCertificate] Can now be used as a key in QSet/QHash. Change-Id: I10858fe648c70fc9535af6913dd3b7f3b2cf0eba Reviewed-by: Oliver Wolff <oliver.wolff@digia.com>
This commit is contained in:
parent
adadb5e870
commit
e8bdc949fc
@ -662,7 +662,13 @@ QByteArray QSslCertificatePrivate::subjectInfoToString(QSslCertificate::SubjectI
|
||||
return str;
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn uint qHash(const QSslCertificate &key, uint seed)
|
||||
|
||||
Returns the hash value for the \a key, using \a seed to seed the calculation.
|
||||
\since 5.4
|
||||
\relates QHash
|
||||
*/
|
||||
|
||||
#ifndef QT_NO_DEBUG_STREAM
|
||||
QDebug operator<<(QDebug debug, const QSslCertificate &certificate)
|
||||
|
@ -59,6 +59,10 @@ class QSslKey;
|
||||
class QSslCertificateExtension;
|
||||
class QStringList;
|
||||
|
||||
class QSslCertificate;
|
||||
// qHash is a friend, but we can't use default arguments for friends (§8.3.6.4)
|
||||
Q_NETWORK_EXPORT uint qHash(const QSslCertificate &key, uint seed = 0) Q_DECL_NOTHROW;
|
||||
|
||||
class QSslCertificatePrivate;
|
||||
class Q_NETWORK_EXPORT QSslCertificate
|
||||
{
|
||||
@ -145,6 +149,8 @@ private:
|
||||
QExplicitlySharedDataPointer<QSslCertificatePrivate> d;
|
||||
friend class QSslCertificatePrivate;
|
||||
friend class QSslSocketBackendPrivate;
|
||||
|
||||
friend Q_NETWORK_EXPORT uint qHash(const QSslCertificate &key, uint seed) Q_DECL_NOTHROW;
|
||||
};
|
||||
Q_DECLARE_SHARED(QSslCertificate)
|
||||
|
||||
|
@ -62,6 +62,17 @@ bool QSslCertificate::operator==(const QSslCertificate &other) const
|
||||
return false;
|
||||
}
|
||||
|
||||
uint qHash(const QSslCertificate &key, uint seed) Q_DECL_NOTHROW
|
||||
{
|
||||
if (X509 * const x509 = key.d->x509) {
|
||||
(void)q_X509_cmp(x509, x509); // populate x509->sha1_hash
|
||||
// (if someone knows a better way...)
|
||||
return qHashBits(x509->sha1_hash, SHA_DIGEST_LENGTH, seed);
|
||||
} else {
|
||||
return seed;
|
||||
}
|
||||
}
|
||||
|
||||
bool QSslCertificate::isNull() const
|
||||
{
|
||||
return d->null;
|
||||
|
@ -67,6 +67,12 @@ bool QSslCertificate::operator==(const QSslCertificate &other) const
|
||||
return d->derData == other.d->derData;
|
||||
}
|
||||
|
||||
uint qHash(const QSslCertificate &key, uint seed) Q_DECL_NOTHROW
|
||||
{
|
||||
// DER is the native encoding here, so toDer() is just "return d->derData":
|
||||
return qHash(key.toDer(), seed);
|
||||
}
|
||||
|
||||
bool QSslCertificate::isNull() const
|
||||
{
|
||||
return d->null;
|
||||
|
@ -70,11 +70,6 @@ inline uint qHash(const QSslError &error, uint seed)
|
||||
Q_DECL_NOEXCEPT_EXPR(noexcept(qHash(error)))
|
||||
{ return (qHash(error.error()) ^ seed); }
|
||||
|
||||
// For QSet<QSslCertificate>
|
||||
inline uint qHash(const QSslCertificate &certificate, uint seed)
|
||||
Q_DECL_NOEXCEPT_EXPR(noexcept(qHash(certificate)))
|
||||
{ return (qHash(certificate.handle()) ^ seed); }
|
||||
|
||||
bool QSslSocketPrivate::s_libraryLoaded = true;
|
||||
bool QSslSocketPrivate::s_loadRootCertsOnDemand = true;
|
||||
bool QSslSocketPrivate::s_loadedCiphersAndCerts = false;
|
||||
|
@ -70,6 +70,7 @@ public slots:
|
||||
|
||||
#ifndef QT_NO_SSL
|
||||
private slots:
|
||||
void hash();
|
||||
void emptyConstructor();
|
||||
void constructor_data();
|
||||
void constructor();
|
||||
@ -164,6 +165,14 @@ void tst_QSslCertificate::cleanupTestCase()
|
||||
|
||||
#ifndef QT_NO_SSL
|
||||
|
||||
void tst_QSslCertificate::hash()
|
||||
{
|
||||
// mostly a compile-only test, to check that qHash(QSslCertificate) is found.
|
||||
QSet<QSslCertificate> certs;
|
||||
certs << QSslCertificate();
|
||||
QCOMPARE(certs.size(), 1);
|
||||
}
|
||||
|
||||
static QByteArray readFile(const QString &absFilePath)
|
||||
{
|
||||
QFile file(absFilePath);
|
||||
|
Loading…
Reference in New Issue
Block a user