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 "qcryptographichash.h"
#include "qdatastream.h"
#include "qendian.h"
#include "qdebug.h"
#include "qendian.h"
#include "qrandom.h"
#include "private/qtools_p.h"
#ifndef QT_BOOTSTRAPPED
#include "qcryptographichash.h"
#endif
QT_BEGIN_NAMESPACE
// 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()
On any platform other than Windows, this function returns a new
UUID with variant QUuid::DCE and version QUuid::Random. If
the /dev/urandom device exists, then the numbers used to construct
the UUID will be of cryptographic quality, which will make the UUID
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.
On any platform other than Windows, this function returns a new UUID with
variant QUuid::DCE and version QUuid::Random. On Windows, a GUID is
generated using the Windows API and will be of the type that the API
decides to create.
\sa variant(), version()
*/
@ -948,82 +940,12 @@ QUuid QUuid::createUuid()
#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 result;
QUuid result(Qt::Uninitialized);
uint *data = &(result.data1);
#if defined(Q_OS_UNIX)
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;
}
}
enum { AmountToRead = 4 };
QRandomGenerator::fillRange(data, AmountToRead);
result.data4[0] = (result.data4[0] & 0x3F) | 0x80; // UV_DCE
result.data3 = (result.data3 & 0x0FFF) | 0x4000; // UV_Random

View File

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

View File

@ -63,13 +63,9 @@
#ifndef QT_BOOTSTRAPPED
#include <qcoreapplication.h>
#include <qrandom.h>
#endif // QT_BOOTSTRAPPED
#ifdef Q_OS_UNIX
#include <stdio.h>
#include "private/qcore_unix_p.h"
#endif // Q_OS_UNIX
#include <limits.h>
QT_BEGIN_NAMESPACE
@ -298,39 +294,7 @@ static uint qt_create_qhash_seed()
return seed;
}
#ifdef Q_OS_UNIX
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
seed = QRandomGenerator::get32();
#endif // QT_BOOTSTRAPPED
return seed;

View File

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