Skip to content

Conversation

@Calinou
Copy link
Member

@Calinou Calinou commented Oct 28, 2023

  • Mention which PRNG Godot internally uses.

Thanks to https://www.reddit.com/r/godot/comments/17hh09i/okay_nvm_im_starting_my_first_gdextension_for/k6p9axd/ for the suggestion 🙂

- Mention which PRNG Godot internally uses.
@Calinou Calinou added enhancement area:manual Issues and PRs related to the Manual/Tutorials section of the documentation cherrypick:4.1 labels Oct 28, 2023
self-signed :ref:`class_X509Certificate`\ s.

The downside of :abbr:`CSPRNG (Cryptographically secure pseudorandom number generation)`
is that it's much slower than standard pseudorandom number generation. Its API
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I generated 10 million random ints:

const COUNT = 10 * 1000 * 1000

func test_randi():
	var arr: Array[int] = []
	arr.resize(COUNT)
	
	var t0 = Time.get_ticks_msec()
	for i in range(COUNT):
		arr[i] = randi()
	var t1 = Time.get_ticks_msec()
	prints("test_randi:", t1-t0, "ms")
	return t1-t0
	

func test_random_bytes():
	
	var arr: Array[int] = []
	arr.resize(COUNT)
	
	var t0 = Time.get_ticks_msec()
	var crypto := Crypto.new()
	var byte_array := crypto.generate_random_bytes(COUNT * 4)
	for i in range(COUNT):
		arr[i] = byte_array.decode_u32(i * 4)
	var t1 = Time.get_ticks_msec()
	prints("test_random_bytes:", t1-t0, "ms")
	return t1-t0

Result:

test_randi: 381 ms
test_random_bytes: 550 ms

So the crypto.generate_random_bytes() takes only about 50% more time. In my opinion that is not much slower, just slower. But is this something that could be dependent on operating system? Also, I'm pretty sure that the pseudo rng (randi()) always takes a constant time to generate a new random number, but how about the CSPRNG? If it does use operating system as a randomness source (does it?), generating even a single random byte might take non-constant time?

Copy link
Member

@AThousandShips AThousandShips Oct 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

50% is a lot slower though generally speaking, so this is still a valid statement

It is also very hardware and OS dependent

@miv391
Copy link

miv391 commented Oct 28, 2023

The discussion on Reddit started from one user's goal to shuffle a deck of cards (52 card). Array has a method shuffle() that seems to use the same pseudo number generator as randi(). That rng can be seeded with int (32 bit or 64 bit?). That means that it is possible to generate only 2^32 (or 2^64) different strings of random numbers. Deck of cards, however, can have 2^226 possible combinations, so the int seeded rng cannot generate all possible shuffles (start the game app - seed the rng - shuffle a deck).

It might be good to tell in the Array.shuffle() documentation that if you want to ensure all possible shuffles, your array must not have more than 20 items (Disclaimer: I calculated that quickly by Google, should be rechecked). That should be worded carefully as the shuffle is still good enough for most use cases even if the array is much longer. Only perfectionists should care :) Oh, and I'm assuming here that all different seeds lead to different shuffles. That assumption may be totally wrong.

It would be nice if the documentation told even more specifically how the Crypto.generate_random_bytes() work. Is it using a pseudo random generator with seed (if yes, can the seed be set) or is it using some operating system services and how that is different by using randi() with a seed.

@AThousandShips
Copy link
Member

AThousandShips commented Oct 28, 2023

That means that it is possible to generate only 2^32 (or 2^64) different strings of random numbers

This does not follow, it depends on the period of the RNG, the seed is not related to that, all the length of the seed dictates is how many starting configurations you can have, the seed does not equal the state of the machine

For example the classic Mersenne Twister has a nominal period of 2^19937-1

@miv391
Copy link

miv391 commented Oct 28, 2023

That means that it is possible to generate only 2^32 (or 2^64) different strings of random numbers

This does not follow, it depends on the period of the RNG, the seed is not related to that, all the length of the seed dictates is how many starting configurations you can have, the seed does not equal the state of the machine

I understand that.

  1. start the game app
  2. seed the rng
  3. shuffle a deck first time

It doesn't matter how long the period is. It doesn't matter if you can get all the possible shuffles sometime in the future. You can't get all the possible shuffles in the first shuffle.

@AThousandShips
Copy link
Member

AThousandShips commented Oct 28, 2023

It doesn't matter how long the period is. It doesn't matter if you can get all the possible shuffles sometime in the future. You can't get all the possible shuffles in the first shuffle.

What do you mean "the first shuffle"? Again this doesn't apply, because the seed doesn't control the period, the period is literally how much you can shuffle it

Further to shuffle 20 elements you need no more than 20 numbers, so that doesn't apply either.

The period is exactly what matters for this

While it does matter that you can't seed it freely like that, it isn't relevant to this PR

@miv391
Copy link

miv391 commented Oct 28, 2023

It doesn't matter how long the period is. It doesn't matter if you can get all the possible shuffles sometime in the future. You can't get all the possible shuffles in the first shuffle.

What do you mean "the first shuffle"? Again this doesn't apply, because the seed doesn't control the period, the period is literally how much you can shuffle it

Further to shuffle 20 elements you need no more than 20 numbers, so that doesn't apply either.

The period is exactly what matters for this

I have no idea how could I describe this more clearly. Start the app. Seed the rng. Generate 51 (or so) random numbers required to shuffle the deck. This is the first shuffle. In a perfect world every possible card combination should be possible. Even if the period would be infinite, that wouldn't help. Let's say we have a rng which can be seeded either with 0 or 1. Rng's period is infinite. When you generate the 51 random number for thr first deck shuffle, you have only those two seed options. Seed 0 produces some specific random numbers and seed 1 some others. Your game's first deck can have only two possible shuffles. Rng period doesn't matter at all.

@AThousandShips
Copy link
Member

AThousandShips commented Oct 28, 2023

I am aware, though that's not how the issue was presented at first hence why I clarified those points

But in any case it isn't relevant to this PR (as the CSPRNG isn't seeded this way, but uses entropy to seed), I'd suggest opening an issue to track this detail and discuss it

Edit: Also the fact that the seed is limited like this does not mean you can't achieve more combinations, you can use many tricks to achieve more complexity, for example skipping ahead and discarding random data, though I don't know the period of the RNG used so can't guarantee it has a long enough period.

But the fact that you only have a certain limited set of starting states doesn't mean that's just how many possible starts for randomness you have, for example shuffling from a seed of 0 without extracting any randomness, and doing so after extracting one random number, yields different results (nominally, unless the sequence is a sequence of just the same number)

@mhilbrunner mhilbrunner merged commit 67380df into godotengine:master Oct 29, 2023
@mhilbrunner
Copy link
Member

I'm going to merge this PR, the other discussion should probably be moved to an issue, if necessary. Thanks for contributing!

@Calinou Calinou deleted the random-number-generation-csprng branch October 31, 2023 10:58
mhilbrunner added a commit to mhilbrunner/godot-docs that referenced this pull request Nov 11, 2023
…tion-csprng

Document cryptographically secure random number generation
@mhilbrunner
Copy link
Member

Cherry-picked to 4.1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:manual Issues and PRs related to the Manual/Tutorials section of the documentation enhancement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants