PcgRandom icon indicating copy to clipboard operation
PcgRandom copied to clipboard

Pcg32Single state can be brute-forced from three outputs

Open bgrainger opened this issue 2 years ago • 0 comments

Based on this code linked from this article, the internal state of Pcg32Single can be recovered (in just seconds) given three successive values from Pcg32Single.GenerateNext.

(Note that this uses reflection to work around the Pcg32Single code scrambling the seed with the equivalent of pcg_oneseq_64_srandom_r; however, once the internal state has been recovered, all future PRNG values can be predicted. This also doesn't recover the initial seed passed to the constructor; however, that's not required to predict future output.)

ulong Recover(uint x0, uint x1, uint x2)
{
	Pcg32Single pcg = new(0);
	var stateField = pcg.GetType().GetField("_state", BindingFlags.NonPublic | BindingFlags.Instance);
	for (ulong upper = 0; upper < (1ul << 32); upper++)
	{
		var rot = (int) (upper >> 27);
		uint rotx0 = (x0 << rot) | (x0 >> (32 - rot));

		if (((rotx0 ^ upper >> 13) & ~31u) == (uint)(upper << 5))
		{
			uint high_lower_bits = (uint) ((rotx0 ^ upper >> 13) & 31u) << 27;
			for (uint lower = 0; lower < (1u << 27); lower++)
			{
				var candidate = (ulong)upper << 32 | high_lower_bits | lower;
				stateField.SetValue(pcg, candidate);

				if (pcg.GenerateNext() == x0 && pcg.GenerateNext() == x1 && pcg.GenerateNext() == x2)
					return candidate;
			}
		}
	}
	// Can't happen	
	throw new InvalidOperationException();
}

var random = new Random();
var seed = (ulong) random.NextInt64();
var pcg = new Pcg32Single(seed);

var x0 = pcg.GenerateNext();
var x1 = pcg.GenerateNext();
var x2 = pcg.GenerateNext();

Console.WriteLine(Recover(x0, x1, x2));

bgrainger avatar May 31 '23 03:05 bgrainger