Created
March 25, 2026 20:06
-
-
Save nilsreichardt/a3ac142263c19387bccb48a35c8cb51a to your computer and use it in GitHub Desktop.
Proof of concept to predict next values if you use Random() instead of Random.secure() in Dart.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Generated with Gemini 3 Flash | |
| // | |
| // Usage: dart run predict_random.dart | |
| import 'dart:math'; | |
| void main() { | |
| final random = Random(); | |
| final A = 0xFFFFDA61; | |
| final MASK_32 = 0xFFFFFFFF; | |
| print('--- Dart Random Predictor PoC ---'); | |
| // 1. Observe two consecutive values | |
| // We use nextInt(1 << 32) to get the full 32-bit output of the generator | |
| // Note: 1 << 32 is 4294967296. nextInt allows up to this value. | |
| final maxVal = 1 << 32; | |
| final v1 = random.nextInt(maxVal); | |
| final v2 = random.nextInt(maxVal); | |
| print('Observed value 1: $v1'); | |
| print('Observed value 2: $v2'); | |
| // 2. Recover the state | |
| // v2 = (A * v1 + state1_upper) % 2^32 | |
| // so state1_upper = (v2 - A * v1) % 2^32 | |
| // In Dart, we need to be careful with large integers. | |
| // Dart's int is 64-bit signed. | |
| BigInt bigA = BigInt.from(A); | |
| BigInt bigV1 = BigInt.from(v1); | |
| BigInt bigV2 = BigInt.from(v2); | |
| BigInt bigMask32 = BigInt.from(MASK_32); | |
| BigInt big2pow32 = BigInt.from(1) << 32; | |
| // state1_upper = (v2 - (A * v1)) % 2^32 | |
| BigInt term = bigA * bigV1; | |
| BigInt state1Upper = (bigV2 - term) % big2pow32; | |
| // Since state1_upper must be < A, and bigV2 = (A*v1 + state1_upper) % 2^32, | |
| // we can confirm if we got it right. | |
| // The result of % in Dart/BigInt can be negative if the input is negative. | |
| if (state1Upper < BigInt.zero) { | |
| state1Upper += big2pow32; | |
| } | |
| print('Recovered state1_upper: $state1Upper'); | |
| // The state AFTER producing v1 was state1 = (state1Upper << 32) | v1 | |
| // To get state2 (the state after producing v2): | |
| // state2 = A * (state1 & MASK_32) + (state1 >> 32) | |
| // state2 = A * v1 + state1Upper | |
| BigInt state2 = bigA * bigV1 + state1Upper; | |
| // Verify state2 produces v2 | |
| if ((state2 & bigMask32) != bigV2) { | |
| print('Error: Recovered state does not match v2!'); | |
| return; | |
| } | |
| // 3. Predict next values | |
| print('\nPredicting next 5 values:'); | |
| BigInt currentState = state2; | |
| List<int> predictions = []; | |
| for (int i = 0; i < 5; i++) { | |
| // next_state = A * (current_state & MASK_32) + (current_state >> 32) | |
| BigInt lower = currentState & bigMask32; | |
| BigInt upper = currentState >> 32; | |
| currentState = bigA * lower + upper; | |
| predictions.add((currentState & bigMask32).toInt()); | |
| print('Prediction ${i + 1}: ${predictions.last}'); | |
| } | |
| // 4. Verify against actual values | |
| print('\nActual next 5 values:'); | |
| for (int i = 0; i < 5; i++) { | |
| final actual = random.nextInt(maxVal); | |
| print('Actual ${i + 1}: $actual'); | |
| if (actual == predictions[i]) { | |
| print(' -> MATCH! ✅'); | |
| } else { | |
| print(' -> MISMATCH! ❌'); | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment