multirandom 0.0.8__tar.gz → 0.1.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. {multirandom-0.0.8 → multirandom-0.1.5}/PKG-INFO +67 -16
  2. {multirandom-0.0.8 → multirandom-0.1.5}/README.md +65 -15
  3. {multirandom-0.0.8 → multirandom-0.1.5}/pyproject.toml +4 -3
  4. multirandom-0.1.5/src/multirandom/csprng/__init__.py +104 -0
  5. multirandom-0.1.5/src/multirandom/csprng/_aes.py +148 -0
  6. multirandom-0.1.5/src/multirandom/csprng/_drbg_base.py +166 -0
  7. multirandom-0.1.5/src/multirandom/csprng/ansi_x931.py +183 -0
  8. multirandom-0.1.5/src/multirandom/csprng/blum_blum_shub.py +283 -0
  9. multirandom-0.1.5/src/multirandom/csprng/chacha20_drbg.py +167 -0
  10. multirandom-0.1.5/src/multirandom/csprng/ctr_drbg.py +191 -0
  11. multirandom-0.1.5/src/multirandom/csprng/dual_ec_drbg.py +354 -0
  12. multirandom-0.1.5/src/multirandom/csprng/fortuna.py +225 -0
  13. multirandom-0.1.5/src/multirandom/csprng/hash_drbg.py +181 -0
  14. multirandom-0.1.5/src/multirandom/csprng/hmac_drbg.py +145 -0
  15. multirandom-0.1.5/src/multirandom/csprng/isaac.py +237 -0
  16. multirandom-0.1.5/src/multirandom/csprng/os_random.py +238 -0
  17. multirandom-0.1.5/src/multirandom/csprng/rdrand_hardware.py +377 -0
  18. multirandom-0.1.5/src/multirandom/csprng/salsa20_drbg.py +152 -0
  19. {multirandom-0.0.8 → multirandom-0.1.5}/src/multirandom/csprng/sha_rand.py +173 -167
  20. multirandom-0.1.5/src/multirandom/csprng/sponge_drbg.py +253 -0
  21. multirandom-0.1.5/src/multirandom/csprng/yarrow.py +195 -0
  22. multirandom-0.1.5/src/multirandom/prng_unsecure/__init__.py +32 -0
  23. multirandom-0.1.5/src/multirandom/prng_unsecure/doom_prng.py +339 -0
  24. multirandom-0.1.5/src/multirandom/prng_unsecure/flash_player_prng.py +484 -0
  25. multirandom-0.1.5/src/multirandom/prng_unsecure/lagged_fibonacci.py +352 -0
  26. multirandom-0.1.5/src/multirandom/prng_unsecure/lcg.py +433 -0
  27. multirandom-0.1.5/src/multirandom/prng_unsecure/mersenne_twister.py +475 -0
  28. multirandom-0.1.5/src/multirandom/prng_unsecure/middle_square.py +412 -0
  29. multirandom-0.1.5/src/multirandom/prng_unsecure/pcg.py +327 -0
  30. multirandom-0.1.5/src/multirandom/prng_unsecure/randu.py +254 -0
  31. multirandom-0.1.5/src/multirandom/prng_unsecure/splitmix64.py +326 -0
  32. multirandom-0.1.5/src/multirandom/prng_unsecure/v8_xorshift128plus.py +459 -0
  33. {multirandom-0.0.8 → multirandom-0.1.5}/src/multirandom/prng_unsecure/xor_shift.py +635 -629
  34. {multirandom-0.0.8 → multirandom-0.1.5}/src/multirandom/trng/weather_rand.py +544 -539
  35. {multirandom-0.0.8 → multirandom-0.1.5}/src/multirandom.egg-info/PKG-INFO +67 -16
  36. multirandom-0.1.5/src/multirandom.egg-info/SOURCES.txt +46 -0
  37. {multirandom-0.0.8 → multirandom-0.1.5}/src/multirandom.egg-info/requires.txt +1 -0
  38. multirandom-0.0.8/src/multirandom/csprng/__init__.py +0 -13
  39. multirandom-0.0.8/src/multirandom/prng_unsecure/__init__.py +0 -12
  40. multirandom-0.0.8/src/multirandom/prng_unsecure/flash_rand_and_reverse.py +0 -10
  41. multirandom-0.0.8/src/multirandom/prng_unsecure/rand_using_virt_lcg.py +0 -127
  42. multirandom-0.0.8/src/multirandom.egg-info/SOURCES.txt +0 -22
  43. {multirandom-0.0.8 → multirandom-0.1.5}/LICENSE +0 -0
  44. {multirandom-0.0.8 → multirandom-0.1.5}/setup.cfg +0 -0
  45. {multirandom-0.0.8 → multirandom-0.1.5}/src/multirandom/__init__.py +0 -0
  46. {multirandom-0.0.8 → multirandom-0.1.5}/src/multirandom/csprng/original_random.py +0 -0
  47. {multirandom-0.0.8 → multirandom-0.1.5}/src/multirandom/csprng/secure_rand.py +0 -0
  48. {multirandom-0.0.8 → multirandom-0.1.5}/src/multirandom/trng/__init__.py +0 -0
  49. {multirandom-0.0.8 → multirandom-0.1.5}/src/multirandom/trng/rand_using_online_api.py +0 -0
  50. {multirandom-0.0.8 → multirandom-0.1.5}/src/multirandom/trng/rand_using_real_random.py +0 -0
  51. {multirandom-0.0.8 → multirandom-0.1.5}/src/multirandom/trng/rand_via_clicks.py +0 -0
  52. {multirandom-0.0.8 → multirandom-0.1.5}/src/multirandom.egg-info/dependency_links.txt +0 -0
  53. {multirandom-0.0.8 → multirandom-0.1.5}/src/multirandom.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: multirandom
3
- Version: 0.0.8
3
+ Version: 0.1.5
4
4
  Summary: 🎲 The truth external PyPI random package, have trng (true randomness), prng_unsecure, csprng, ...
5
5
  Author-email: BlackCatOfficialytb <quanvm0501@gmail.com>
6
6
  License: Apache-2.0
@@ -13,6 +13,7 @@ License-File: LICENSE
13
13
  Requires-Dist: aiohttp
14
14
  Requires-Dist: pyautogui
15
15
  Requires-Dist: geopy
16
+ Requires-Dist: pycryptodome>=3.0
16
17
  Provides-Extra: microphone
17
18
  Requires-Dist: real-random>=0.1.1; extra == "microphone"
18
19
  Dynamic: license-file
@@ -38,8 +39,8 @@ Deep-dive docs live in [`docs/`](docs/):
38
39
  |---|---|
39
40
  | [Getting started](docs/getting-started.md) | installation, quickstart, which generator to pick |
40
41
  | [True randomness (`trng`)](docs/trng.md) | mouse clicks, quantum APIs, microphone, weather |
41
- | [Cryptographic randomness (`csprng`)](docs/csprng.md) | OS CSPRNGs, NIST DRBGs, the Dual_EC backdoor demo, Fortuna/ISAAC |
42
- | [Unsecure PRNGs (`prng_unsecure`)](docs/prng_unsecure.md) | XorShift/Xoshiro, LCGs, and their reversal attacks |
42
+ | [Cryptographic randomness (`csprng`)](docs/csprng.md) | OS CSPRNGs, NIST DRBGs, the Dual_EC backdoor demo, Keccak sponge, Blum Blum Shub, RDRAND |
43
+ | [Unsecure PRNGs (`prng_unsecure`)](docs/prng_unsecure.md) | XorShift/Xoshiro, LCGs, Doom, PCG32, V8's Math.random, and their reversal attacks |
43
44
  | [Self-checks](docs/self-checks.md) | the assert batteries verifying every generator |
44
45
 
45
46
  ---
@@ -56,13 +57,30 @@ multirandom/
56
57
  │ ├── rand_using_real_random.py # Microphone entropy (real-random)
57
58
  │ └── weather_rand.py # Open-Meteo weather entropy (large unhashed seeds)
58
59
  ├── csprng/ # Cryptographically Strong Randomness
59
- │ ├── secure_rand.py # OS CSPRNGs, NIST DRBGs, Fortuna/Yarrow, ISAAC
60
+ │ ├── os_random.py # OS CSPRNGs (secrets, urandom, getrandom, BCrypt, SecRandom)
61
+ │ ├── ctr_drbg.py / hmac_drbg.py / hash_drbg.py # NIST SP 800-90A DRBGs
62
+ │ ├── dual_ec_drbg.py # the backdoored DRBG, live
63
+ │ ├── chacha20_drbg.py / salsa20_drbg.py # stream-cipher DRBGs
64
+ │ ├── fortuna.py / yarrow.py / isaac.py # pools and the classics
65
+ │ ├── sponge_drbg.py # Keccak-f[1600] sponge DRBG (from scratch)
66
+ │ ├── ansi_x931.py # ANS X9.17/X9.31, the banking generator
67
+ │ ├── blum_blum_shub.py # factoring-hard, one bit per squaring
68
+ │ ├── rdrand_hardware.py # RDRAND / RDSEED CPU instructions
69
+ │ ├── _aes.py # the library AES (FIPS-197 KATs)
60
70
  │ ├── sha_rand.py # MD5, SHA-1/2/3, BLAKE2, SHAKE PRNGs
61
71
  │ └── original_random.py # Wrapper for Python's built-in random
62
72
  └── prng_unsecure/ # Mathematical Randomness (UNSECURE PRNGs)
63
73
  ├── xor_shift.py # XorShift & Xoshiro Family
64
- ├── rand_using_virt_lcg.py # Linear Congruential Generators (LCGs)
65
- └── flash_rand_and_reverse.py # [Coming Soon] Specialized PRNGs
74
+ ├── mersenne_twister.py # MT19937 + the 624-output clone attack
75
+ ├── lcg.py # Linear Congruential Generators (LCGs)
76
+ ├── flash_player_prng.py # Flash Player Math.random() + its reversal attacks
77
+ ├── randu.py # IBM's RANDU, the 15-planes generator
78
+ ├── middle_square.py # von Neumann's middle square + MSWS
79
+ ├── doom_prng.py # Doom's 256-entry lookup table
80
+ ├── splitmix64.py # SplitMix64, inverted exactly
81
+ ├── lagged_fibonacci.py # BSD-style LF(31, 3); last j outputs = state
82
+ ├── pcg.py # O'Neill's PCG32, closed-form jump
83
+ └── v8_xorshift128plus.py # Math.random()'s engine + the GF(2) seed solve
66
84
  ```
67
85
 
68
86
  ---
@@ -109,7 +127,31 @@ Run `python -m multirandom.prng_unsecure.xor_shift` to check every generator aga
109
127
  ### 4. Mathematical LCGs (`prng_unsecure`)
110
128
  Classic Linear Congruential Generators with customizable shift parameters. Includes `reverse_roll()` methods to demonstrate the insecurity of simple modular arithmetic.
111
129
 
112
- ### 5. Hash-based Generators (`csprng`)
130
+ ### 5. Iconic & historic PRNGs (`prng_unsecure`)
131
+ The famous, the infamous, and the ones running inside your favourite games —
132
+ each module ships its reference vectors and its own reversal.
133
+
134
+ - **IBM's RANDU** (`randu`): the 15-planes generator; one output recovers the seed via the modular inverse of 65539.
135
+ - **Middle Square** (`middle_square`): von Neumann's 1949 idea, plus the MSWS variant.
136
+ - **Doom's RNG** (`doom_prng`): the 256-entry lookup table behind every shotgun spread, increment-then-index; one draw pins the index.
137
+ - **SplitMix64** (`splitmix64`): the seeding engine behind xoshiro; the mixer is inverted exactly from one output.
138
+ - **Lagged Fibonacci** (`lagged_fibonacci`): the BSD `random()`-style LF(31, 3); the last j outputs ARE the state.
139
+ - **PCG32** (`pcg`): O'Neill's permutation-on-an-LCG — the "good" end, with a closed-form `jump()` for a billion outputs in one call.
140
+ - **V8's XorShift128+** (`v8_xorshift128plus`): JavaScript's `Math.random()` engine; five consecutive doubles recover the exact 128-bit seed by GF(2) elimination.
141
+
142
+ ```python
143
+ from multirandom.prng_unsecure.doom_prng import DoomRandom
144
+ from multirandom.prng_unsecure.v8_xorshift128plus import V8XorShift128Plus
145
+
146
+ doom = DoomRandom()
147
+ doom.P_Random() # the same table the game rolls
148
+
149
+ v8 = V8XorShift128Plus(seed="demo")
150
+ clone = V8XorShift128Plus.from_outputs([v8.random() for _ in range(5)])
151
+ clone.getstate() == v8.getstate() # True: Math.random, predicted exactly
152
+ ```
153
+
154
+ ### 6. Hash-based Generators (`csprng`)
113
155
  Counter-mode hash DRBGs: `H(seed || counter)` drives the full `random.Random` API.
114
156
 
115
157
  - **SHA-2**: SHA-224, SHA-256, SHA-384, SHA-512
@@ -125,7 +167,7 @@ gen = BLAKE2bRandom("my seed")
125
167
  print(gen.randbytes(16).hex(), gen.random(), gen.choice(["a", "b"]))
126
168
  ```
127
169
 
128
- ### 6. Cryptographically Strong Generators (`csprng/secure_rand`)
170
+ ### 7. Cryptographically Strong Generators (`csprng`)
129
171
  The ones to actually guard things with, plus deliberately broken ones that teach.
130
172
 
131
173
  **OS-level CSPRNGs** (live entropy, behave like `random.SystemRandom` — seeding
@@ -150,14 +192,23 @@ the native API is not reachable on the running platform.
150
192
  - `Hash_DRBG` – SHA-256 with Hash_df (440-bit state)
151
193
  - `Dual_EC_DRBG` — *deliberately backdoored*, kept as a teaching exhibit
152
194
 
153
- **Stream / accumulator designs**: `ChaCha20DRBG`, `Salsa20DRBG` (counter-mode
154
- DRBGs on the two classic stream ciphers), `FortunaRandom` (32 seed pools +
155
- AES-256-CTR, rekey every 65536 blocks, `add_entropy(data, pool)`) and its
156
- legacy predecessor `YarrowRandom` (fast/slow pools, AES-192-CTR), plus
157
- `ISAACRandom` (Bob Jenkins' ISAAC, validated against his reference vectors).
195
+ **Stream / accumulator / sponge designs**: `ChaCha20DRBG`, `Salsa20DRBG`
196
+ (counter-mode DRBGs on the two classic stream ciphers), `FortunaRandom`
197
+ (32 seed pools + AES-256-CTR, rekey every 65536 blocks,
198
+ `add_entropy(data, pool)`) and its legacy predecessor `YarrowRandom`
199
+ (fast/slow pools, AES-192-CTR), `ISAACRandom` (Bob Jenkins' ISAAC, validated
200
+ against his reference vectors), `SpongeDRBG` (a sponge-mode DRBG on a
201
+ hand-transcribed Keccak-f[1600], pinned against `hashlib`), and
202
+ `AnsiX931DRBG` (the banking world's ANS X9.17/X9.31 generator).
203
+
204
+ **Factoring-hard and hardware**: `BlumBlumShub`
205
+ (x' = x² mod n — one provably factoring-hard bit per squaring, genuine Blum
206
+ primes enforced by a self-contained Miller-Rabin) and the CPU's own entropy
207
+ instructions `RDRANDRandom` / `RDSEEDRandom` (x86-64 shellcode through
208
+ ctypes, probed availability).
158
209
 
159
210
  ```python
160
- from multirandom.csprng.secure_rand import SecretsRandom, CTR_DRBG, Dual_EC_DRBG
211
+ from multirandom.csprng import SecretsRandom, CTR_DRBG, Dual_EC_DRBG
161
212
 
162
213
  gen = SecretsRandom()
163
214
  print(gen.token_hex(32)) # live kernel entropy
@@ -192,7 +243,7 @@ module ships with this demo running in its own self-checks at
192
243
  `truncate_bits=8`. It warns on first use. Never use it for real randomness.
193
244
 
194
245
  ```python
195
- # Run its assert battery: python -m multirandom.csprng.secure_rand
246
+ # Run the DRBG sweep: python -m multirandom.csprng._drbg_base
196
247
  ```
197
248
 
198
249
  ### The global unsecure-prng warning switch
@@ -214,7 +265,7 @@ multirandom.ignore_unsecure_prng_warning_do_not_set_to_true_unless_you_know_how_
214
265
 
215
266
  ### Requirements
216
267
  - Python 3.9+
217
- - Dependencies listed in `requirements.txt` (e.g., `pyautogui`, `aiohttp`)
268
+ - Dependencies listed in `requirements.txt` (e.g., `pyautogui`, `aiohttp`, `pycryptodome`)
218
269
 
219
270
  ### Installation
220
271
  ```bash
@@ -19,8 +19,8 @@ Deep-dive docs live in [`docs/`](docs/):
19
19
  |---|---|
20
20
  | [Getting started](docs/getting-started.md) | installation, quickstart, which generator to pick |
21
21
  | [True randomness (`trng`)](docs/trng.md) | mouse clicks, quantum APIs, microphone, weather |
22
- | [Cryptographic randomness (`csprng`)](docs/csprng.md) | OS CSPRNGs, NIST DRBGs, the Dual_EC backdoor demo, Fortuna/ISAAC |
23
- | [Unsecure PRNGs (`prng_unsecure`)](docs/prng_unsecure.md) | XorShift/Xoshiro, LCGs, and their reversal attacks |
22
+ | [Cryptographic randomness (`csprng`)](docs/csprng.md) | OS CSPRNGs, NIST DRBGs, the Dual_EC backdoor demo, Keccak sponge, Blum Blum Shub, RDRAND |
23
+ | [Unsecure PRNGs (`prng_unsecure`)](docs/prng_unsecure.md) | XorShift/Xoshiro, LCGs, Doom, PCG32, V8's Math.random, and their reversal attacks |
24
24
  | [Self-checks](docs/self-checks.md) | the assert batteries verifying every generator |
25
25
 
26
26
  ---
@@ -37,13 +37,30 @@ multirandom/
37
37
  │ ├── rand_using_real_random.py # Microphone entropy (real-random)
38
38
  │ └── weather_rand.py # Open-Meteo weather entropy (large unhashed seeds)
39
39
  ├── csprng/ # Cryptographically Strong Randomness
40
- │ ├── secure_rand.py # OS CSPRNGs, NIST DRBGs, Fortuna/Yarrow, ISAAC
40
+ │ ├── os_random.py # OS CSPRNGs (secrets, urandom, getrandom, BCrypt, SecRandom)
41
+ │ ├── ctr_drbg.py / hmac_drbg.py / hash_drbg.py # NIST SP 800-90A DRBGs
42
+ │ ├── dual_ec_drbg.py # the backdoored DRBG, live
43
+ │ ├── chacha20_drbg.py / salsa20_drbg.py # stream-cipher DRBGs
44
+ │ ├── fortuna.py / yarrow.py / isaac.py # pools and the classics
45
+ │ ├── sponge_drbg.py # Keccak-f[1600] sponge DRBG (from scratch)
46
+ │ ├── ansi_x931.py # ANS X9.17/X9.31, the banking generator
47
+ │ ├── blum_blum_shub.py # factoring-hard, one bit per squaring
48
+ │ ├── rdrand_hardware.py # RDRAND / RDSEED CPU instructions
49
+ │ ├── _aes.py # the library AES (FIPS-197 KATs)
41
50
  │ ├── sha_rand.py # MD5, SHA-1/2/3, BLAKE2, SHAKE PRNGs
42
51
  │ └── original_random.py # Wrapper for Python's built-in random
43
52
  └── prng_unsecure/ # Mathematical Randomness (UNSECURE PRNGs)
44
53
  ├── xor_shift.py # XorShift & Xoshiro Family
45
- ├── rand_using_virt_lcg.py # Linear Congruential Generators (LCGs)
46
- └── flash_rand_and_reverse.py # [Coming Soon] Specialized PRNGs
54
+ ├── mersenne_twister.py # MT19937 + the 624-output clone attack
55
+ ├── lcg.py # Linear Congruential Generators (LCGs)
56
+ ├── flash_player_prng.py # Flash Player Math.random() + its reversal attacks
57
+ ├── randu.py # IBM's RANDU, the 15-planes generator
58
+ ├── middle_square.py # von Neumann's middle square + MSWS
59
+ ├── doom_prng.py # Doom's 256-entry lookup table
60
+ ├── splitmix64.py # SplitMix64, inverted exactly
61
+ ├── lagged_fibonacci.py # BSD-style LF(31, 3); last j outputs = state
62
+ ├── pcg.py # O'Neill's PCG32, closed-form jump
63
+ └── v8_xorshift128plus.py # Math.random()'s engine + the GF(2) seed solve
47
64
  ```
48
65
 
49
66
  ---
@@ -90,7 +107,31 @@ Run `python -m multirandom.prng_unsecure.xor_shift` to check every generator aga
90
107
  ### 4. Mathematical LCGs (`prng_unsecure`)
91
108
  Classic Linear Congruential Generators with customizable shift parameters. Includes `reverse_roll()` methods to demonstrate the insecurity of simple modular arithmetic.
92
109
 
93
- ### 5. Hash-based Generators (`csprng`)
110
+ ### 5. Iconic & historic PRNGs (`prng_unsecure`)
111
+ The famous, the infamous, and the ones running inside your favourite games —
112
+ each module ships its reference vectors and its own reversal.
113
+
114
+ - **IBM's RANDU** (`randu`): the 15-planes generator; one output recovers the seed via the modular inverse of 65539.
115
+ - **Middle Square** (`middle_square`): von Neumann's 1949 idea, plus the MSWS variant.
116
+ - **Doom's RNG** (`doom_prng`): the 256-entry lookup table behind every shotgun spread, increment-then-index; one draw pins the index.
117
+ - **SplitMix64** (`splitmix64`): the seeding engine behind xoshiro; the mixer is inverted exactly from one output.
118
+ - **Lagged Fibonacci** (`lagged_fibonacci`): the BSD `random()`-style LF(31, 3); the last j outputs ARE the state.
119
+ - **PCG32** (`pcg`): O'Neill's permutation-on-an-LCG — the "good" end, with a closed-form `jump()` for a billion outputs in one call.
120
+ - **V8's XorShift128+** (`v8_xorshift128plus`): JavaScript's `Math.random()` engine; five consecutive doubles recover the exact 128-bit seed by GF(2) elimination.
121
+
122
+ ```python
123
+ from multirandom.prng_unsecure.doom_prng import DoomRandom
124
+ from multirandom.prng_unsecure.v8_xorshift128plus import V8XorShift128Plus
125
+
126
+ doom = DoomRandom()
127
+ doom.P_Random() # the same table the game rolls
128
+
129
+ v8 = V8XorShift128Plus(seed="demo")
130
+ clone = V8XorShift128Plus.from_outputs([v8.random() for _ in range(5)])
131
+ clone.getstate() == v8.getstate() # True: Math.random, predicted exactly
132
+ ```
133
+
134
+ ### 6. Hash-based Generators (`csprng`)
94
135
  Counter-mode hash DRBGs: `H(seed || counter)` drives the full `random.Random` API.
95
136
 
96
137
  - **SHA-2**: SHA-224, SHA-256, SHA-384, SHA-512
@@ -106,7 +147,7 @@ gen = BLAKE2bRandom("my seed")
106
147
  print(gen.randbytes(16).hex(), gen.random(), gen.choice(["a", "b"]))
107
148
  ```
108
149
 
109
- ### 6. Cryptographically Strong Generators (`csprng/secure_rand`)
150
+ ### 7. Cryptographically Strong Generators (`csprng`)
110
151
  The ones to actually guard things with, plus deliberately broken ones that teach.
111
152
 
112
153
  **OS-level CSPRNGs** (live entropy, behave like `random.SystemRandom` — seeding
@@ -131,14 +172,23 @@ the native API is not reachable on the running platform.
131
172
  - `Hash_DRBG` – SHA-256 with Hash_df (440-bit state)
132
173
  - `Dual_EC_DRBG` — *deliberately backdoored*, kept as a teaching exhibit
133
174
 
134
- **Stream / accumulator designs**: `ChaCha20DRBG`, `Salsa20DRBG` (counter-mode
135
- DRBGs on the two classic stream ciphers), `FortunaRandom` (32 seed pools +
136
- AES-256-CTR, rekey every 65536 blocks, `add_entropy(data, pool)`) and its
137
- legacy predecessor `YarrowRandom` (fast/slow pools, AES-192-CTR), plus
138
- `ISAACRandom` (Bob Jenkins' ISAAC, validated against his reference vectors).
175
+ **Stream / accumulator / sponge designs**: `ChaCha20DRBG`, `Salsa20DRBG`
176
+ (counter-mode DRBGs on the two classic stream ciphers), `FortunaRandom`
177
+ (32 seed pools + AES-256-CTR, rekey every 65536 blocks,
178
+ `add_entropy(data, pool)`) and its legacy predecessor `YarrowRandom`
179
+ (fast/slow pools, AES-192-CTR), `ISAACRandom` (Bob Jenkins' ISAAC, validated
180
+ against his reference vectors), `SpongeDRBG` (a sponge-mode DRBG on a
181
+ hand-transcribed Keccak-f[1600], pinned against `hashlib`), and
182
+ `AnsiX931DRBG` (the banking world's ANS X9.17/X9.31 generator).
183
+
184
+ **Factoring-hard and hardware**: `BlumBlumShub`
185
+ (x' = x² mod n — one provably factoring-hard bit per squaring, genuine Blum
186
+ primes enforced by a self-contained Miller-Rabin) and the CPU's own entropy
187
+ instructions `RDRANDRandom` / `RDSEEDRandom` (x86-64 shellcode through
188
+ ctypes, probed availability).
139
189
 
140
190
  ```python
141
- from multirandom.csprng.secure_rand import SecretsRandom, CTR_DRBG, Dual_EC_DRBG
191
+ from multirandom.csprng import SecretsRandom, CTR_DRBG, Dual_EC_DRBG
142
192
 
143
193
  gen = SecretsRandom()
144
194
  print(gen.token_hex(32)) # live kernel entropy
@@ -173,7 +223,7 @@ module ships with this demo running in its own self-checks at
173
223
  `truncate_bits=8`. It warns on first use. Never use it for real randomness.
174
224
 
175
225
  ```python
176
- # Run its assert battery: python -m multirandom.csprng.secure_rand
226
+ # Run the DRBG sweep: python -m multirandom.csprng._drbg_base
177
227
  ```
178
228
 
179
229
  ### The global unsecure-prng warning switch
@@ -195,7 +245,7 @@ multirandom.ignore_unsecure_prng_warning_do_not_set_to_true_unless_you_know_how_
195
245
 
196
246
  ### Requirements
197
247
  - Python 3.9+
198
- - Dependencies listed in `requirements.txt` (e.g., `pyautogui`, `aiohttp`)
248
+ - Dependencies listed in `requirements.txt` (e.g., `pyautogui`, `aiohttp`, `pycryptodome`)
199
249
 
200
250
  ### Installation
201
251
  ```bash
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "multirandom"
7
- version = "0.0.8"
7
+ version = "0.1.5"
8
8
  authors = [
9
9
  { name="BlackCatOfficialytb", email="quanvm0501@gmail.com" },
10
10
  ]
@@ -18,8 +18,9 @@ classifiers = [
18
18
  ]
19
19
  dependencies = [
20
20
  "aiohttp",
21
- "pyautogui",
22
- "geopy"
21
+ "pyautogui",
22
+ "geopy",
23
+ "pycryptodome>=3.0"
23
24
  ]
24
25
 
25
26
  [project.optional-dependencies]
@@ -0,0 +1,104 @@
1
+ """
2
+ Project: https://pypi.org/project/multirandom/
3
+ Repo: https://github.com/BlackCatOfficialytb/multirandom
4
+ License: Apache 2.0
5
+ Module: multirandom.csprng
6
+ Description:
7
+ Cryptographically strong randomness ("csprng") -- one module per method:
8
+
9
+ 1. OS-level CSPRNGs - the sources you should actually use
10
+ (:mod:`multirandom.csprng.os_random`): :class:`SecretsRandom` (the
11
+ ``secrets`` module), :class:`OSURandom` (``os.urandom``),
12
+ :class:`GetRandomRandom` (Linux ``getrandom(2)``),
13
+ :class:`BCryptGenRandomRandom` (Windows ``BCryptGenRandom``) and
14
+ :class:`SecRandomCopyBytesRandom` (Apple ``SecRandomCopyBytes``). The
15
+ ctypes ones fall back gracefully to ``os.urandom`` (the very same kernel
16
+ entropy pool) when the native API is not reachable on the running
17
+ platform.
18
+
19
+ 2. NIST SP 800-90A DRBGs: :class:`CTR_DRBG` (AES-128, via the crypto
20
+ library), :class:`HMAC_DRBG` and :class:`Hash_DRBG` (SHA-256) - plus the
21
+ infamous *backdoored* :class:`Dual_EC_DRBG`, kept deliberately: it warns
22
+ loudly and shows how its backdoor is exploited with
23
+ ``Dual_EC_DRBG.reveal`` (its own module: ``dual_ec_drbg``).
24
+
25
+ 3. Stream/accumulator designs: :class:`ChaCha20DRBG`
26
+ (``chacha20_drbg``), :class:`Salsa20DRBG` (``salsa20_drbg``),
27
+ :class:`FortunaRandom` (``fortuna``) and its legacy predecessor
28
+ :class:`YarrowRandom` (``yarrow``), plus :class:`ISAACRandom`
29
+ (``isaac`` - Bob Jenkins' ISAAC, transcribed from the author's
30
+ ``readable.c``) and :class:`SpongeDRBG` (``sponge_drbg`` - a sponge-mode
31
+ DRBG on a from-scratch Keccak-f[1600], KAT-pinned against ``hashlib``).
32
+
33
+ 4. The classics: :class:`AnsiX931DRBG` (``ansi_x931`` - ANS X9.17/X9.31,
34
+ the banking-world generator) and :class:`BlumBlumShub`
35
+ (``blum_blum_shub`` - one provably factoring-hard bit per squaring).
36
+
37
+ 5. The CPU's own entropy instructions (``rdrand_hardware``): the
38
+ :class:`RDRANDRandom` and :class:`RDSEEDRandom` hardware TRNGs, run
39
+ through x64 shellcode stubs - probed, never assumed.
40
+
41
+ Every DRBG class is a ``random.Random`` subclass, so ``.random()``,
42
+ ``.randint()``, ``.choice()``, ``.shuffle()`` ... all work on top of the
43
+ cryptographic core. Everything without a restorable state (the OS-level
44
+ sources) behaves like ``random.SystemRandom``: seeding is a no-op and
45
+ ``getstate()``/``setstate()`` raise ``NotImplementedError``.
46
+
47
+ The raw AES used by CTR_DRBG / Fortuna / Yarrow comes from an installed
48
+ crypto library (pycryptodome / pycryptodomex, with ``cryptography`` as a
49
+ fallback) - see :mod:`multirandom.csprng._aes`; the shared ``random.Random``
50
+ plumbing of the seedable DRBGs lives in
51
+ :mod:`multirandom.csprng._drbg_base`.
52
+
53
+ Unsecure- or backdoor-related warnings here are governed by one global
54
+ switch in the package root::
55
+
56
+ import multirandom
57
+ multirandom.ignore_unsecure_prng_warning_do_not_set_to_true_unless_you_know_how_risky_it_is
58
+
59
+ ``False`` (the default) sends those warnings to the console; ``True``
60
+ silences them.
61
+ """
62
+ from .os_random import (
63
+ SecretsRandom, OSURandom, GetRandomRandom,
64
+ BCryptGenRandomRandom, SecRandomCopyBytesRandom,
65
+ )
66
+ from .ctr_drbg import CTR_DRBG
67
+ from .hmac_drbg import HMAC_DRBG
68
+ from .hash_drbg import Hash_DRBG
69
+ from .dual_ec_drbg import Dual_EC_DRBG
70
+ from .chacha20_drbg import ChaCha20DRBG
71
+ from .salsa20_drbg import Salsa20DRBG
72
+ from .fortuna import FortunaRandom
73
+ from .yarrow import YarrowRandom
74
+ from .isaac import ISAACRandom
75
+ from .sponge_drbg import SpongeDRBG
76
+ from .ansi_x931 import AnsiX931DRBG
77
+ from .blum_blum_shub import BlumBlumShub
78
+ from .rdrand_hardware import RDRANDRandom, RDSEEDRandom
79
+
80
+ DRBG_GENERATORS = [
81
+ CTR_DRBG, HMAC_DRBG, Hash_DRBG, Dual_EC_DRBG,
82
+ ChaCha20DRBG, Salsa20DRBG, FortunaRandom, YarrowRandom, ISAACRandom,
83
+ SpongeDRBG, AnsiX931DRBG,
84
+ ]
85
+ # BlumBlumShub is deliberately absent: its constructor demands the Blum
86
+ # prime pair (it is not a seed-only DRBG), so the generic seedable-DRBG
87
+ # sweep cannot construct it. It ships its own battery instead.
88
+
89
+ OS_GENERATORS = [
90
+ SecretsRandom, OSURandom, GetRandomRandom,
91
+ BCryptGenRandomRandom, SecRandomCopyBytesRandom,
92
+ ]
93
+
94
+ HW_GENERATORS = [RDRANDRandom, RDSEEDRandom]
95
+
96
+ __all__ = [
97
+ "SecretsRandom", "OSURandom", "GetRandomRandom",
98
+ "BCryptGenRandomRandom", "SecRandomCopyBytesRandom",
99
+ "CTR_DRBG", "HMAC_DRBG", "Hash_DRBG", "Dual_EC_DRBG",
100
+ "ChaCha20DRBG", "Salsa20DRBG", "FortunaRandom", "YarrowRandom", "ISAACRandom",
101
+ "SpongeDRBG", "AnsiX931DRBG", "BlumBlumShub",
102
+ "RDRANDRandom", "RDSEEDRandom",
103
+ "DRBG_GENERATORS", "OS_GENERATORS", "HW_GENERATORS",
104
+ ]
@@ -0,0 +1,148 @@
1
+ """
2
+ Project: https://pypi.org/project/multirandom/
3
+ Repo: https://github.com/BlackCatOfficialytb/multirandom
4
+ License: Apache 2.0
5
+ Module: multirandom.csprng._aes
6
+ Description:
7
+ The raw AES core shared by the csprng modules that need block-level AES
8
+ (``CTR_DRBG``, ``FortunaRandom``, ``YarrowRandom``, and ANSI X9.31).
9
+
10
+ The arithmetic -- key schedule, SubBytes/MixColumns, the ECB mode -- is
11
+ delegated to an audited library implementation (``pycryptodome``, with
12
+ ``pycryptodomex`` or ``cryptography`` accepted as drop-in alternates),
13
+ replacing the hand-rolled AES that used to live in ``secure_rand.py``.
14
+ What survives here is only the *shape* the DRBG modules are specced
15
+ against: ``encrypt_block(block)`` (one 16-byte ECB encryption -- the
16
+ primitive CTR_DRBG's (K, V) iterator and ANSI X9.31 run on) and
17
+ ``ctr(counter, n)`` (big-endian-counter keystream, Fortuna's generator).
18
+
19
+ FIPS-197 known-answer tests still run in ``__main__`` to prove the wiring::
20
+
21
+ py -3 -m multirandom.csprng._aes
22
+ """
23
+ from __future__ import annotations
24
+
25
+ # --- pick the AES library: pycryptodome > pycryptodomex > cryptography ------
26
+ try:
27
+ from Crypto.Cipher import AES as _pyc_aes # pycryptodome
28
+ except ImportError:
29
+ try:
30
+ from Cryptodome.Cipher import AES as _pyc_aes # pycryptodomex
31
+ except ImportError:
32
+ _pyc_aes = None
33
+
34
+ if _pyc_aes is None:
35
+ try: # the cryptography package as the last resort
36
+ from cryptography.hazmat.primitives.ciphers import (
37
+ Cipher as _cipher, algorithms as _algorithms, modes as _modes)
38
+ except ImportError:
39
+ raise ImportError(
40
+ "multirandom.csprng needs an AES implementation: install "
41
+ "pycryptodome (pip install pycryptodome) or the cryptography "
42
+ "package") from None
43
+
44
+
45
+ class _Aes:
46
+ """AES-128/192/256 single-block encryptor + CTR keystream helper.
47
+
48
+ Interface kept from the legacy hand-rolled AES so the DRBG modules
49
+ read exactly as before; the one-round-of-AES-per-call arithmetic is
50
+ now the installed library's (FIPS-validated implementation, no hand
51
+ transcription to get wrong).
52
+ """
53
+
54
+ def __init__(self, key):
55
+ key = bytes(key)
56
+ if len(key) not in (16, 24, 32):
57
+ raise ValueError("AES key must be 16, 24 or 32 bytes")
58
+ self._key = key
59
+ if _pyc_aes is not None:
60
+ # ECB is stateless: one cipher object serves every block.
61
+ self._ecb = _pyc_aes.new(key, _pyc_aes.MODE_ECB)
62
+ self._backend = "pycryptodome"
63
+ else:
64
+ self._ecb = _cipher(
65
+ _algorithms.AES(key), _modes.ECB()).encryptor()
66
+ self._backend = "cryptography"
67
+
68
+ def encrypt_block(self, block):
69
+ """AES-ECB of exactly one 16-byte block."""
70
+ block = bytes(block)
71
+ if len(block) != 16:
72
+ raise ValueError("AES blocks are 16 bytes")
73
+ if self._backend == "pycryptodome":
74
+ return self._ecb.encrypt(block)
75
+ return self._ecb.update(block)
76
+
77
+ def ctr(self, counter, n):
78
+ """``n`` bytes of AES-CTR keystream for a 128-bit big-endian counter."""
79
+ out = bytearray()
80
+ while len(out) < n:
81
+ out += self.encrypt_block(counter.to_bytes(16, "big"))
82
+ counter = (counter + 1) % (1 << 128)
83
+ return bytes(out[:n])
84
+
85
+
86
+
87
+ def main():
88
+ print("=" * 70)
89
+ print("_aes - FIPS-197 wiring self-check battery")
90
+ print("=" * 70)
91
+
92
+ # --- 1. FIPS-197 appendix C known-answer tests ---------------------------
93
+ fips_pt = bytes.fromhex("00112233445566778899aabbccddeeff")
94
+ fips_key = bytes(range(16))
95
+ aes = _Aes(fips_key)
96
+ assert aes.encrypt_block(fips_pt) == \
97
+ bytes.fromhex("69c4e0d86a7b0430d8cdb78070b4c55a"), "AES-128 KAT failed"
98
+ assert _Aes(bytes(range(24))).encrypt_block(fips_pt) == \
99
+ bytes.fromhex("dda97ca4864cdfe06eaf70a0ec0d7191"), "AES-192 KAT failed"
100
+ assert _Aes(bytes(range(32))).encrypt_block(fips_pt) == \
101
+ bytes.fromhex("8ea2b7ca516745bfeafc49904b496089"), "AES-256 KAT failed"
102
+ print(f"[1] FIPS-197 AES-128/192/256 KATs ({aes._backend} core): OK")
103
+
104
+ # --- 2. ECB eats complete, and only complete, blocks ----------------------
105
+ for bad_len in (0, 1, 15, 17, 32):
106
+ try:
107
+ aes.encrypt_block(b"\x00" * bad_len)
108
+ except ValueError:
109
+ pass
110
+ else:
111
+ raise AssertionError(f"block of {bad_len} bytes accepted")
112
+ for bad_key_len in (0, 8, 17, 33):
113
+ try:
114
+ _Aes(b"\x00" * bad_key_len)
115
+ except ValueError:
116
+ pass
117
+ else:
118
+ raise AssertionError(f"key of {bad_key_len} bytes accepted")
119
+ print("[2] only 16-byte blocks and 16/24/32-byte keys accepted: OK")
120
+
121
+ # --- 3. the CTR keystream is the concatenation of ECB(counter + i) --------
122
+ key = bytes.fromhex("000102030405060708090a0b0c0d0e0f")
123
+ counter = int.from_bytes(
124
+ bytes.fromhex("00112233445566778899aabbccddeeff"), "big")
125
+ runs = _Aes(key)
126
+ manual = (runs.encrypt_block(counter.to_bytes(16, "big"))
127
+ + runs.encrypt_block(((counter + 1) % (1 << 128)).to_bytes(16, "big"))
128
+ + runs.encrypt_block(((counter + 2) % (1 << 128)).to_bytes(16, "big")))
129
+ assert runs.ctr(counter, 40) == manual[:40], "CTR keystream drifted"
130
+ assert runs.ctr(counter, 48) == manual, "CTR keystream wrong at block edges"
131
+ assert runs.ctr(counter, 1) == manual[:1], "CTR short request wrong"
132
+ block4 = runs.encrypt_block(((counter + 3) % (1 << 128)).to_bytes(16, "big"))
133
+ assert runs.ctr(counter, 49) == manual + block4[:1], "CTR carry-over wrong"
134
+ print("[3] ctr() == ECB on counter, counter+1, ... (block edges exact): OK")
135
+
136
+ # --- 4. CTR round trip through XOR (the way the DRBGs use it) -------------
137
+ stream = runs.ctr(counter, 64)
138
+ assert bytes(x ^ y for x, y in zip(stream, stream)) == bytes(64)
139
+ assert len({runs.ctr(counter, 32)[i] for i in range(32)}) > 12, \
140
+ "flat keystream?! the library AES looks broken"
141
+ print("[4] keystream XORs back to zero, byte spread sane: OK")
142
+
143
+ print("-" * 70)
144
+ print("All _aes checks passed!")
145
+
146
+
147
+ if __name__ == "__main__":
148
+ main()