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.
- {multirandom-0.0.8 → multirandom-0.1.5}/PKG-INFO +67 -16
- {multirandom-0.0.8 → multirandom-0.1.5}/README.md +65 -15
- {multirandom-0.0.8 → multirandom-0.1.5}/pyproject.toml +4 -3
- multirandom-0.1.5/src/multirandom/csprng/__init__.py +104 -0
- multirandom-0.1.5/src/multirandom/csprng/_aes.py +148 -0
- multirandom-0.1.5/src/multirandom/csprng/_drbg_base.py +166 -0
- multirandom-0.1.5/src/multirandom/csprng/ansi_x931.py +183 -0
- multirandom-0.1.5/src/multirandom/csprng/blum_blum_shub.py +283 -0
- multirandom-0.1.5/src/multirandom/csprng/chacha20_drbg.py +167 -0
- multirandom-0.1.5/src/multirandom/csprng/ctr_drbg.py +191 -0
- multirandom-0.1.5/src/multirandom/csprng/dual_ec_drbg.py +354 -0
- multirandom-0.1.5/src/multirandom/csprng/fortuna.py +225 -0
- multirandom-0.1.5/src/multirandom/csprng/hash_drbg.py +181 -0
- multirandom-0.1.5/src/multirandom/csprng/hmac_drbg.py +145 -0
- multirandom-0.1.5/src/multirandom/csprng/isaac.py +237 -0
- multirandom-0.1.5/src/multirandom/csprng/os_random.py +238 -0
- multirandom-0.1.5/src/multirandom/csprng/rdrand_hardware.py +377 -0
- multirandom-0.1.5/src/multirandom/csprng/salsa20_drbg.py +152 -0
- {multirandom-0.0.8 → multirandom-0.1.5}/src/multirandom/csprng/sha_rand.py +173 -167
- multirandom-0.1.5/src/multirandom/csprng/sponge_drbg.py +253 -0
- multirandom-0.1.5/src/multirandom/csprng/yarrow.py +195 -0
- multirandom-0.1.5/src/multirandom/prng_unsecure/__init__.py +32 -0
- multirandom-0.1.5/src/multirandom/prng_unsecure/doom_prng.py +339 -0
- multirandom-0.1.5/src/multirandom/prng_unsecure/flash_player_prng.py +484 -0
- multirandom-0.1.5/src/multirandom/prng_unsecure/lagged_fibonacci.py +352 -0
- multirandom-0.1.5/src/multirandom/prng_unsecure/lcg.py +433 -0
- multirandom-0.1.5/src/multirandom/prng_unsecure/mersenne_twister.py +475 -0
- multirandom-0.1.5/src/multirandom/prng_unsecure/middle_square.py +412 -0
- multirandom-0.1.5/src/multirandom/prng_unsecure/pcg.py +327 -0
- multirandom-0.1.5/src/multirandom/prng_unsecure/randu.py +254 -0
- multirandom-0.1.5/src/multirandom/prng_unsecure/splitmix64.py +326 -0
- multirandom-0.1.5/src/multirandom/prng_unsecure/v8_xorshift128plus.py +459 -0
- {multirandom-0.0.8 → multirandom-0.1.5}/src/multirandom/prng_unsecure/xor_shift.py +635 -629
- {multirandom-0.0.8 → multirandom-0.1.5}/src/multirandom/trng/weather_rand.py +544 -539
- {multirandom-0.0.8 → multirandom-0.1.5}/src/multirandom.egg-info/PKG-INFO +67 -16
- multirandom-0.1.5/src/multirandom.egg-info/SOURCES.txt +46 -0
- {multirandom-0.0.8 → multirandom-0.1.5}/src/multirandom.egg-info/requires.txt +1 -0
- multirandom-0.0.8/src/multirandom/csprng/__init__.py +0 -13
- multirandom-0.0.8/src/multirandom/prng_unsecure/__init__.py +0 -12
- multirandom-0.0.8/src/multirandom/prng_unsecure/flash_rand_and_reverse.py +0 -10
- multirandom-0.0.8/src/multirandom/prng_unsecure/rand_using_virt_lcg.py +0 -127
- multirandom-0.0.8/src/multirandom.egg-info/SOURCES.txt +0 -22
- {multirandom-0.0.8 → multirandom-0.1.5}/LICENSE +0 -0
- {multirandom-0.0.8 → multirandom-0.1.5}/setup.cfg +0 -0
- {multirandom-0.0.8 → multirandom-0.1.5}/src/multirandom/__init__.py +0 -0
- {multirandom-0.0.8 → multirandom-0.1.5}/src/multirandom/csprng/original_random.py +0 -0
- {multirandom-0.0.8 → multirandom-0.1.5}/src/multirandom/csprng/secure_rand.py +0 -0
- {multirandom-0.0.8 → multirandom-0.1.5}/src/multirandom/trng/__init__.py +0 -0
- {multirandom-0.0.8 → multirandom-0.1.5}/src/multirandom/trng/rand_using_online_api.py +0 -0
- {multirandom-0.0.8 → multirandom-0.1.5}/src/multirandom/trng/rand_using_real_random.py +0 -0
- {multirandom-0.0.8 → multirandom-0.1.5}/src/multirandom/trng/rand_via_clicks.py +0 -0
- {multirandom-0.0.8 → multirandom-0.1.5}/src/multirandom.egg-info/dependency_links.txt +0 -0
- {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.
|
|
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,
|
|
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
|
-
│ ├──
|
|
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
|
-
├──
|
|
65
|
-
|
|
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.
|
|
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
|
-
###
|
|
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`
|
|
154
|
-
DRBGs on the two classic stream ciphers), `FortunaRandom`
|
|
155
|
-
AES-256-CTR, rekey every 65536 blocks,
|
|
156
|
-
legacy predecessor `YarrowRandom`
|
|
157
|
-
`ISAACRandom` (Bob Jenkins' ISAAC, validated
|
|
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
|
|
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
|
|
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,
|
|
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
|
-
│ ├──
|
|
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
|
-
├──
|
|
46
|
-
|
|
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.
|
|
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
|
-
###
|
|
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`
|
|
135
|
-
DRBGs on the two classic stream ciphers), `FortunaRandom`
|
|
136
|
-
AES-256-CTR, rekey every 65536 blocks,
|
|
137
|
-
legacy predecessor `YarrowRandom`
|
|
138
|
-
`ISAACRandom` (Bob Jenkins' ISAAC, validated
|
|
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
|
|
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
|
|
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.
|
|
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
|
-
|
|
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()
|