Skip to content

Switch dice RNG - #2473

Open
mark9064 wants to merge 1 commit into
InfiniTimeOrg:mainfrom
mark9064:dice-rng
Open

Switch dice RNG#2473
mark9064 wants to merge 1 commit into
InfiniTimeOrg:mainfrom
mark9064:dice-rng

Conversation

@mark9064

@mark9064 mark9064 commented Aug 9, 2026

Copy link
Copy Markdown
Member

The mersenne twister uses multiple KBs of state and is not needed for a dice app in terms of RNG quality

When we pick up GCC16, we can switch to philox4x32

This is an alternative to #2385: while I agree that PR is a better RNG than the one here, I don't think it's worth carrying a new dependency / implementation for it

@mark9064 mark9064 added the maintenance Background work label Aug 9, 2026
@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown

Build size and comparison to main:

Section Size Difference
text 385024B -208B
data 944B 0B
bss 22640B 0B

Run in InfiniEmu

@DavisNT

DavisNT commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

@mark9064 Could we just use BT chip RNG?
I would be happy to write something similar to:

/*
* Passkey is a 6 digits code (1'000'000 possibilities).
* It is important every possible value has an equal probability
* of getting generated. Simply applying a modulo creates a bias
* since 2^32 is not a multiple of 1'000'000.
* To prevent that, we can reject values greater than 999'999.
*
* Rejecting values would happen a lot since 2^32-1 is way greater
* than 1'000'000. An optimisation is to use a multiple of 1'000'000.
* The greatest multiple of 1'000'000 lesser than 2^32-1 is
* 4'294'000'000.
*
* Great explanation at:
* https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
*/
uint32_t passkey_rand;
do {
passkey_rand = ble_ll_rand();
} while (passkey_rand > 4293999999);
pkey.passkey = passkey_rand % 1000000;
probably adding a simple XOR of all 4 bytes if the requested random value is a small power of 2 (e.g. a coin toss).

I think using BT chip's RNG would need very little code and also would provide a good (more secure) randomness.

P.S. We could also XOR "in" the existing seeds from motionController and xTaskGetTickCount() as additional sources of entropy. Or we could even do a clock jitter against xTaskGetTickCount() as the additional source - in any case the code should be small and lightweight.

@mark9064

Copy link
Copy Markdown
Member Author

I don't think this complexity is at all required. I think you'll find that this RNG is perfectly fine

Seriously, give it a go if you're worried :)

Reaching out to hardware opens up the risk of more problems in terms of correctness, performance and error handling

@DavisNT

DavisNT commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

@mark9064 You are right that tightly-coupling Dice app to BT chip is a very counter-intuitive thing and has a potential of causing issues later (especially when adopting to new hardware).

The thing that is worrying me when using a simple (non-secure) PRNG in such application is ability to gather enough data to reconstruct the internal state of PRNG (afaik std::minstd_rand has only 32-bits of internal state, so a little more than 13 rolls of 6 side dice should allow to reconstruct the internal state) and predict all subsequent PRNG output.

Could it be possible to reinitialize the PRNG on every roll of the Dice app? It would not only prevent a security issue like this, but also have a pretty cool side effect - the Dice app acting more like a physical dice (being affected by how it is shaken, though in a very hard to predict manner). 😎
I have created #2478 with such change and it does not increase firmware size at all.

@mark9064

Copy link
Copy Markdown
Member Author

I'm not sure how worried we should be about state recovery attacks on a watch dice app. Isn't the goal to have randomly distributed outputs, not randomly distributed and mathematically unpredictable outputs? Like in any situation where the dice app is going to be used, I'm really struggling to think of a case where being able to predict the RNG is a problem

I get it's nice to have things theoretically perfect, but it just seems like extra complexity to me

@DavisNT

DavisNT commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

@mark9064 I think most people imply that a dice roll (even simulated one) is unpredictable. And InfiniTime users might rely on that without even thinking about it.

I don't think it is possible to imagine all possible uses of a Dice app in a smartwatch. I definitely can imagine one scenario where predicting the PRNG numbers could be a big problem - if people would do multiple rounds of any "guess the numbers" game without restarting the Dice app. Also in board games like Monopoly predicting the subsequent dice rolls would give player an advantage.

Reseeding the PRNG on every roll does not increase complexity at all. The #2478 moves the existing PRNG seeding code from constructor to the Roll() method. Do you see any downsides of such change?

P.S. I think reseeding PRNG on every roll is far from perfect - it still doesn't solve at least 2 problems:

  1. Under some circumstances it might be possible to force a certain output from PRNG by manipulating timing and position (or accelerometer saturation) of the watch. This is not a problem if Dice is operated by watch owner, because the owner could always use a modified firmware (hence nothing can prevent from cheating by the watch owner).
  2. If maximum output length 99^9 (around 60 bits) is shorter than the seed (from around 30 to 68 bits) or the internal state of PRNG (AFAIK 32 bits for std::minstd_rand) then not all outputs are possible (i.e. when generating 9 numbers with 99 sides there will be some combinations that cannot happen at all and some others that will require the watch to have certain uptime or position/acceleration).

However I really like the idea mainly due to its simplicity and my own inability to find any downsides for it.

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

Labels

maintenance Background work

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants