QRandomGenerator: add securelySeeded(), to ensure appropriate seeding

Since we don't document how many bytes one needs (it's 2496), it's
difficult for the caller to provide just enough data in the seed
sequence. Moreover, since std::mt19937 doesn't make it easy to provide
the ideal size either, we can't actually write code that operates
optimally given a quint32 range either -- we only provide it via
std::seed_seq, which is inefficient.

However, we can do it internally by passing QRandomGenerator to the
std::mersenne_twister_engine constructor, as it's designed to work.

Change-Id: Icaa86fc7b54d4b368c0efffd14f0613c10998321
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
This commit is contained in:
Thiago Macieira 2017-10-23 19:39:45 -07:00
parent af456842e1
commit cfad4e298f
3 changed files with 77 additions and 9 deletions

View File

@ -422,6 +422,9 @@ Q_NEVER_INLINE void QRandomGenerator::SystemGenerator::generate(quint32 *begin,
QRandomGenerator will generate the same sequence of numbers. But given
different seeds, the results should be considerably different.
QRandomGenerator::securelySeeded() can be used to create a QRandomGenerator
that is securely seeded with QRandomGenerator::system(), meaning that the
sequence of numbers it generates cannot be easily predicted. Additionally,
QRandomGenerator::global() returns a global instance of QRandomGenerator
that Qt will ensure to be securely seeded. This object is thread-safe, may
be shared for most uses, and is always seeded from
@ -462,7 +465,8 @@ Q_NEVER_INLINE void QRandomGenerator::SystemGenerator::generate(quint32 *begin,
itself. Due to mixing of the seed data, QRandomGenerator cannot guarantee
that distinct seeds will produce different sequences.
QRandomGenerator::global() is always seeded from
QRandomGenerator::global(), like all generators created by
QRandomGenerator::securelySeeded(), is always seeded from
QRandomGenerator::system(), so it's not possible to make it produce
identical sequences.
@ -576,6 +580,8 @@ Q_NEVER_INLINE void QRandomGenerator::SystemGenerator::generate(quint32 *begin,
Initializes this QRandomGenerator object with the value \a seed as
the seed. Two objects constructed with the same seed value will
produce the same number sequence.
\sa securelySeeded()
*/
/*!
@ -585,6 +591,8 @@ Q_NEVER_INLINE void QRandomGenerator::SystemGenerator::generate(quint32 *begin,
Initializes this QRandomGenerator object with the values found in the
array \a seedBuffer as the seed. Two objects constructed or reseeded with
the same seed value will produce the same number sequence.
\sa securelySeeded()
*/
/*!
@ -600,6 +608,8 @@ Q_NEVER_INLINE void QRandomGenerator::SystemGenerator::generate(quint32 *begin,
std::seed_seq sseq(seedBuffer, seedBuffer + len);
QRandomGenerator generator(sseq);
\endcode
\sa securelySeeded()
*/
/*!
@ -615,6 +625,8 @@ Q_NEVER_INLINE void QRandomGenerator::SystemGenerator::generate(quint32 *begin,
std::seed_seq sseq(begin, end);
QRandomGenerator generator(sseq);
\endcode
\sa securelySeeded()
*/
/*!
@ -624,6 +636,8 @@ Q_NEVER_INLINE void QRandomGenerator::SystemGenerator::generate(quint32 *begin,
Initializes this QRandomGenerator object with the seed sequence \a
sseq as the seed. Two objects constructed or reseeded with the same seed
value will produce the same number sequence.
\sa securelySeeded()
*/
/*!
@ -640,8 +654,8 @@ Q_NEVER_INLINE void QRandomGenerator::SystemGenerator::generate(quint32 *begin,
For that reason, it is not adviseable to create a copy of
QRandomGenerator::global(). If one needs an exclusive deterministic
generator, consider instead creating a new object and seeding it from
QRandomGenerator::system().
generator, consider instead using securelySeeded() to obtain a new object
that shares no relationship with the QRandomGenerator::global().
*/
/*!
@ -882,7 +896,7 @@ Q_NEVER_INLINE void QRandomGenerator::SystemGenerator::generate(quint32 *begin,
QRandomGenerator will also access the operating system facilities, but they
will not generate the same sequence.
\sa global()
\sa securelySeeded(), global()
*/
/*!
@ -890,10 +904,9 @@ Q_NEVER_INLINE void QRandomGenerator::SystemGenerator::generate(quint32 *begin,
\threadsafe
Returns a pointer to a shared QRandomGenerator that was seeded using
QRandomGenerator::system(). This function should be used to create random data
without the expensive creation of a securely-seeded QRandomGenerator for a
specific use or storing the rather large QRandomGenerator object.
large QRandomGenerator object.
securelySeeded(). This function should be used to create random data
without the expensive creation of a securely-seeded QRandomGenerator
for a specific use or storing the rather large QRandomGenerator object.
For example, the following creates a random RGB color:
@ -907,9 +920,28 @@ Q_NEVER_INLINE void QRandomGenerator::SystemGenerator::generate(quint32 *begin,
Note, however, that if there are other threads accessing the global object,
those threads may obtain samples at unpredictable intervals.
\sa system()
\sa securelySeeded(), system()
*/
/*!
\fn QRandomGenerator QRandomGenerator::securelySeeded()
Returns a new QRandomGenerator object that was securely seeded with
QRandomGenerator::system(). This function will obtain the ideal seed size
for the algorithm that QRandomGenerator uses and is therefore the
recommended way for creating a new QRandomGenerator object that will be
kept for some time.
Given the amount of data required to securely seed the deterministic
engine, this function is somewhat expensive and should not be used for
short-term uses of QRandomGenerator (using it to generate fewer than 2600
bytes of random data is effectively a waste of resources). If the use
doesn't require that much data, consider using QRandomGenerator::global()
and not storing a QRandomGenerator object instead.
\sa global(), system()
*/
/*!
\class QRandomGenerator64
\inmodule QtCore
@ -999,6 +1031,16 @@ QRandomGenerator64 *QRandomGenerator64::global()
return &global;
}
QRandomGenerator64 QRandomGenerator64::securelySeeded()
{
QRandomGenerator64 result(System{});
result.type = MersenneTwister;
// seed with the system CSPRNG
new (&result.storage.engine()) RandomEngine(static_cast<SystemGenerator &>(system()->storage.sys));
return result;
}
/// \internal
inline QRandomGenerator::QRandomGenerator(System)
: type(SystemRNG)

View File

@ -156,6 +156,7 @@ public:
static inline QRandomGenerator *system();
static inline QRandomGenerator *global();
static inline QRandomGenerator securelySeeded();
protected:
enum System {};
@ -222,6 +223,7 @@ public:
static Q_DECL_CONSTEXPR result_type max() { return (std::numeric_limits<result_type>::max)(); }
static Q_CORE_EXPORT QRandomGenerator64 *system();
static Q_CORE_EXPORT QRandomGenerator64 *global();
static Q_CORE_EXPORT QRandomGenerator64 securelySeeded();
#endif // Q_QDOC
};
@ -235,6 +237,11 @@ inline QRandomGenerator *QRandomGenerator::global()
return QRandomGenerator64::global();
}
QRandomGenerator QRandomGenerator::securelySeeded()
{
return QRandomGenerator64::securelySeeded();
}
QT_END_NAMESPACE
#endif // QRANDOM_H

View File

@ -77,6 +77,7 @@ private slots:
void copyingGlobal();
void copyingSystem();
void systemRng();
void securelySeeding();
void generate32_data();
void generate32();
@ -161,6 +162,9 @@ void tst_QRandomGenerator::basics()
rng = std::move(rng64);
rng64 = std::move(rng);
rng = QRandomGenerator64::securelySeeded();
rng64 = QRandomGenerator::securelySeeded();
// access global
QRandomGenerator *global = QRandomGenerator::global();
QRandomGenerator globalCopy = *global;
@ -254,6 +258,21 @@ void tst_QRandomGenerator::systemRng()
#endif
}
void tst_QRandomGenerator::securelySeeding()
{
QRandomGenerator rng1 = QRandomGenerator::securelySeeded();
QRandomGenerator rng2 = QRandomGenerator::securelySeeded();
quint32 samples[20];
rng1.fillRange(samples);
// should NOT produce the same sequence, whichever it was
int sameCount = 0;
for (quint32 x : samples)
sameCount += (rng2() == x);
QVERIFY(sameCount < 20);
}
void tst_QRandomGenerator::generate32_data()
{
QTest::addColumn<uint>("control");