QUuid, QHttpMultipart and QHash: use QRandomGenerator

QRandomGenerator can produce more than 31 bits of data. And it uses
/dev/urandom for us on Unix, so QHash does not need to duplicate that
part.

Change-Id: Icd0e0d4b27cb4e5eb892fffd14b52a0d91f179eb
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
This commit is contained in:
Thiago Macieira 2017-04-13 13:19:32 -07:00
parent 5483b30868
commit f17095653e
4 changed files with 20 additions and 140 deletions

View File

@ -40,14 +40,13 @@
#include "quuid.h" #include "quuid.h"
#include "qcryptographichash.h"
#include "qdatastream.h" #include "qdatastream.h"
#include "qendian.h"
#include "qdebug.h" #include "qdebug.h"
#include "qendian.h"
#include "qrandom.h"
#include "private/qtools_p.h" #include "private/qtools_p.h"
#ifndef QT_BOOTSTRAPPED
#include "qcryptographichash.h"
#endif
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
// 16 bytes (a uint, two shorts and a uchar[8]), each represented by two hex // 16 bytes (a uint, two shorts and a uchar[8]), each represented by two hex
@ -918,17 +917,10 @@ bool QUuid::operator>(const QUuid &other) const Q_DECL_NOTHROW
/*! /*!
\fn QUuid QUuid::createUuid() \fn QUuid QUuid::createUuid()
On any platform other than Windows, this function returns a new On any platform other than Windows, this function returns a new UUID with
UUID with variant QUuid::DCE and version QUuid::Random. If variant QUuid::DCE and version QUuid::Random. On Windows, a GUID is
the /dev/urandom device exists, then the numbers used to construct generated using the Windows API and will be of the type that the API
the UUID will be of cryptographic quality, which will make the UUID decides to create.
unique. Otherwise, the numbers of the UUID will be obtained from
the local pseudo-random number generator (qrand(), which is seeded
by qsrand()) which is usually not of cryptograhic quality, which
means that the UUID can't be guaranteed to be unique.
On a Windows platform, a GUID is generated, which almost certainly
\e{will} be unique, on this or any other system, networked or not.
\sa variant(), version() \sa variant(), version()
*/ */
@ -948,82 +940,12 @@ QUuid QUuid::createUuid()
#else // Q_OS_WIN #else // Q_OS_WIN
QT_BEGIN_INCLUDE_NAMESPACE
#include "qdatetime.h"
#include "qfile.h"
#include "qthreadstorage.h"
#include <stdlib.h> // for RAND_MAX
QT_END_INCLUDE_NAMESPACE
#if !defined(QT_BOOTSTRAPPED) && defined(Q_OS_UNIX)
Q_GLOBAL_STATIC(QThreadStorage<QFile *>, devUrandomStorage);
#endif
QUuid QUuid::createUuid() QUuid QUuid::createUuid()
{ {
QUuid result; QUuid result(Qt::Uninitialized);
uint *data = &(result.data1); uint *data = &(result.data1);
enum { AmountToRead = 4 };
#if defined(Q_OS_UNIX) QRandomGenerator::fillRange(data, AmountToRead);
QFile *devUrandom;
# if !defined(QT_BOOTSTRAPPED)
devUrandom = devUrandomStorage()->localData();
if (!devUrandom) {
devUrandom = new QFile(QLatin1String("/dev/urandom"));
devUrandom->open(QIODevice::ReadOnly | QIODevice::Unbuffered);
devUrandomStorage()->setLocalData(devUrandom);
}
# else
QFile file(QLatin1String("/dev/urandom"));
devUrandom = &file;
devUrandom->open(QIODevice::ReadOnly | QIODevice::Unbuffered);
# endif
enum { AmountToRead = 4 * sizeof(uint) };
if (devUrandom->isOpen()
&& devUrandom->read((char *) data, AmountToRead) == AmountToRead) {
// we got what we wanted, nothing more to do
;
} else
#endif
{
static const int intbits = sizeof(int)*8;
static int randbits = 0;
if (!randbits) {
int r = 0;
int max = RAND_MAX;
do { ++r; } while ((max=max>>1));
randbits = r;
}
// Seed the PRNG once per thread with a combination of current time, a
// stack address and a serial counter (since thread stack addresses are
// re-used).
#ifndef QT_BOOTSTRAPPED
static QThreadStorage<int *> uuidseed;
if (!uuidseed.hasLocalData())
{
int *pseed = new int;
static QBasicAtomicInt serial = Q_BASIC_ATOMIC_INITIALIZER(0);
qsrand(*pseed = QDateTime::currentSecsSinceEpoch()
+ quintptr(&pseed)
+ 2 + serial.fetchAndAddRelaxed(1));
uuidseed.setLocalData(pseed);
}
#else
static bool seeded = false;
if (!seeded)
qsrand(QDateTime::currentSecsSinceEpoch()
+ quintptr(&seeded));
#endif
int chunks = 16 / sizeof(uint);
while (chunks--) {
uint randNumber = 0;
for (int filled = 0; filled < intbits; filled += randbits)
randNumber |= qrand()<<filled;
*(data+chunks) = randNumber;
}
}
result.data4[0] = (result.data4[0] & 0x3F) | 0x80; // UV_DCE result.data4[0] = (result.data4[0] & 0x3F) | 0x80; // UV_DCE
result.data3 = (result.data3 & 0x0FFF) | 0x4000; // UV_Random result.data3 = (result.data3 & 0x0FFF) | 0x4000; // UV_Random

View File

@ -65,6 +65,7 @@ QT_BEGIN_NAMESPACE
class Q_CORE_EXPORT QUuid class Q_CORE_EXPORT QUuid
{ {
QUuid(Qt::Initialization) {}
public: public:
enum Variant { enum Variant {
VarUnknown =-1, VarUnknown =-1,

View File

@ -63,13 +63,9 @@
#ifndef QT_BOOTSTRAPPED #ifndef QT_BOOTSTRAPPED
#include <qcoreapplication.h> #include <qcoreapplication.h>
#include <qrandom.h>
#endif // QT_BOOTSTRAPPED #endif // QT_BOOTSTRAPPED
#ifdef Q_OS_UNIX
#include <stdio.h>
#include "private/qcore_unix_p.h"
#endif // Q_OS_UNIX
#include <limits.h> #include <limits.h>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@ -298,39 +294,7 @@ static uint qt_create_qhash_seed()
return seed; return seed;
} }
#ifdef Q_OS_UNIX seed = QRandomGenerator::get32();
int randomfd = qt_safe_open("/dev/urandom", O_RDONLY);
if (randomfd == -1)
randomfd = qt_safe_open("/dev/random", O_RDONLY | O_NONBLOCK);
if (randomfd != -1) {
if (qt_safe_read(randomfd, reinterpret_cast<char *>(&seed), sizeof(seed)) == sizeof(seed)) {
qt_safe_close(randomfd);
return seed;
}
qt_safe_close(randomfd);
}
#endif // Q_OS_UNIX
#if defined(Q_OS_WIN32) && !defined(Q_CC_GNU)
errno_t err;
err = rand_s(&seed);
if (err == 0)
return seed;
#endif // Q_OS_WIN32
// general fallback: initialize from the current timestamp, pid,
// and address of a stack-local variable
quint64 timestamp = QDateTime::currentMSecsSinceEpoch();
seed ^= timestamp;
seed ^= (timestamp >> 32);
quint64 pid = QCoreApplication::applicationPid();
seed ^= pid;
seed ^= (pid >> 32);
quintptr seedPtr = reinterpret_cast<quintptr>(&seed);
seed ^= seedPtr;
seed ^= (qulonglong(seedPtr) >> 32); // no-op on 32-bit platforms
#endif // QT_BOOTSTRAPPED #endif // QT_BOOTSTRAPPED
return seed; return seed;

View File

@ -41,7 +41,7 @@
#include "qhttpmultipart_p.h" #include "qhttpmultipart_p.h"
#include "QtCore/qdatetime.h" // for initializing the random number generator with QTime #include "QtCore/qdatetime.h" // for initializing the random number generator with QTime
#include "QtCore/qmutex.h" #include "QtCore/qmutex.h"
#include "QtCore/qthreadstorage.h" #include "QtCore/qrandom.h"
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@ -431,23 +431,16 @@ void QHttpPartPrivate::checkHeaderCreated() const
} }
} }
Q_GLOBAL_STATIC(QThreadStorage<bool *>, seedCreatedStorage);
QHttpMultiPartPrivate::QHttpMultiPartPrivate() : contentType(QHttpMultiPart::MixedType), device(new QHttpMultiPartIODevice(this)) QHttpMultiPartPrivate::QHttpMultiPartPrivate() : contentType(QHttpMultiPart::MixedType), device(new QHttpMultiPartIODevice(this))
{ {
if (!seedCreatedStorage()->hasLocalData()) { // 24 random bytes, becomes 32 characters when encoded to Base64
qsrand(QTime(0,0,0).msecsTo(QTime::currentTime()) ^ reinterpret_cast<quintptr>(this)); quint32 random[6];
seedCreatedStorage()->setLocalData(new bool(true)); QRandomGenerator::fillRange(random);
} boundary = "boundary_.oOo._"
+ QByteArray::fromRawData(reinterpret_cast<char *>(random), sizeof(random)).toBase64();
boundary = QByteArray("boundary_.oOo._")
+ QByteArray::number(qrand()).toBase64()
+ QByteArray::number(qrand()).toBase64()
+ QByteArray::number(qrand()).toBase64();
// boundary must not be longer than 70 characters, see RFC 2046, section 5.1.1 // boundary must not be longer than 70 characters, see RFC 2046, section 5.1.1
if (boundary.count() > 70) Q_ASSERT(boundary.count() <= 70);
boundary = boundary.left(70);
} }
qint64 QHttpMultiPartIODevice::size() const qint64 QHttpMultiPartIODevice::size() const