multirandom 0.0.7__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.7 → multirandom-0.1.5}/PKG-INFO +80 -15
- {multirandom-0.0.7 → multirandom-0.1.5}/README.md +78 -14
- {multirandom-0.0.7 → multirandom-0.1.5}/pyproject.toml +4 -3
- {multirandom-0.0.7 → multirandom-0.1.5}/src/multirandom/__init__.py +9 -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/original_random.py +11 -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.7 → multirandom-0.1.5}/src/multirandom/csprng/secure_rand.py +7 -1
- {multirandom-0.0.7 → multirandom-0.1.5}/src/multirandom/csprng/sha_rand.py +173 -161
- 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.7 → multirandom-0.1.5}/src/multirandom/prng_unsecure/xor_shift.py +635 -605
- multirandom-0.1.5/src/multirandom/trng/__init__.py +12 -0
- {multirandom-0.0.7 → multirandom-0.1.5}/src/multirandom/trng/rand_using_online_api.py +14 -0
- {multirandom-0.0.7 → multirandom-0.1.5}/src/multirandom/trng/rand_using_real_random.py +7 -2
- {multirandom-0.0.7 → multirandom-0.1.5}/src/multirandom/trng/rand_via_clicks.py +16 -0
- multirandom-0.1.5/src/multirandom/trng/weather_rand.py +544 -0
- {multirandom-0.0.7 → multirandom-0.1.5}/src/multirandom.egg-info/PKG-INFO +80 -15
- multirandom-0.1.5/src/multirandom.egg-info/SOURCES.txt +46 -0
- {multirandom-0.0.7 → multirandom-0.1.5}/src/multirandom.egg-info/requires.txt +1 -0
- multirandom-0.0.7/src/multirandom/csprng/__init__.py +0 -0
- multirandom-0.0.7/src/multirandom/csprng/original_random.py +0 -1
- multirandom-0.0.7/src/multirandom/prng_unsecure/__init__.py +0 -0
- multirandom-0.0.7/src/multirandom/prng_unsecure/flash_rand_and_reverse.py +0 -1
- multirandom-0.0.7/src/multirandom/prng_unsecure/rand_using_virt_lcg.py +0 -109
- multirandom-0.0.7/src/multirandom/trng/__init__.py +0 -0
- multirandom-0.0.7/src/multirandom/trng/weather_rand.py +0 -281
- multirandom-0.0.7/src/multirandom.egg-info/SOURCES.txt +0 -22
- {multirandom-0.0.7 → multirandom-0.1.5}/LICENSE +0 -0
- {multirandom-0.0.7 → multirandom-0.1.5}/setup.cfg +0 -0
- {multirandom-0.0.7 → multirandom-0.1.5}/src/multirandom.egg-info/dependency_links.txt +0 -0
- {multirandom-0.0.7 → 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
|
|
@@ -30,6 +31,20 @@ A comprehensive library for exploring and generating randomness across the spect
|
|
|
30
31
|
|
|
31
32
|
---
|
|
32
33
|
|
|
34
|
+
## 📚 Full Documentation
|
|
35
|
+
|
|
36
|
+
Deep-dive docs live in [`docs/`](docs/):
|
|
37
|
+
|
|
38
|
+
| Page | Contents |
|
|
39
|
+
|---|---|
|
|
40
|
+
| [Getting started](docs/getting-started.md) | installation, quickstart, which generator to pick |
|
|
41
|
+
| [True randomness (`trng`)](docs/trng.md) | mouse clicks, quantum APIs, microphone, weather |
|
|
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 |
|
|
44
|
+
| [Self-checks](docs/self-checks.md) | the assert batteries verifying every generator |
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
33
48
|
## 🏗 Project Structure
|
|
34
49
|
|
|
35
50
|
```text
|
|
@@ -40,15 +55,32 @@ multirandom/
|
|
|
40
55
|
│ ├── rand_via_clicks.py # Human-in-the-loop entropy
|
|
41
56
|
│ ├── rand_using_online_api.py # Quantum & Hardware APIs (ANU QRNG, Roll-API)
|
|
42
57
|
│ ├── rand_using_real_random.py # Microphone entropy (real-random)
|
|
43
|
-
│ └── weather_rand.py #
|
|
58
|
+
│ └── weather_rand.py # Open-Meteo weather entropy (large unhashed seeds)
|
|
44
59
|
├── csprng/ # Cryptographically Strong Randomness
|
|
45
|
-
│ ├──
|
|
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)
|
|
46
70
|
│ ├── sha_rand.py # MD5, SHA-1/2/3, BLAKE2, SHAKE PRNGs
|
|
47
71
|
│ └── original_random.py # Wrapper for Python's built-in random
|
|
48
72
|
└── prng_unsecure/ # Mathematical Randomness (UNSECURE PRNGs)
|
|
49
73
|
├── xor_shift.py # XorShift & Xoshiro Family
|
|
50
|
-
├──
|
|
51
|
-
|
|
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
|
|
52
84
|
```
|
|
53
85
|
|
|
54
86
|
---
|
|
@@ -95,7 +127,31 @@ Run `python -m multirandom.prng_unsecure.xor_shift` to check every generator aga
|
|
|
95
127
|
### 4. Mathematical LCGs (`prng_unsecure`)
|
|
96
128
|
Classic Linear Congruential Generators with customizable shift parameters. Includes `reverse_roll()` methods to demonstrate the insecurity of simple modular arithmetic.
|
|
97
129
|
|
|
98
|
-
### 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`)
|
|
99
155
|
Counter-mode hash DRBGs: `H(seed || counter)` drives the full `random.Random` API.
|
|
100
156
|
|
|
101
157
|
- **SHA-2**: SHA-224, SHA-256, SHA-384, SHA-512
|
|
@@ -111,7 +167,7 @@ gen = BLAKE2bRandom("my seed")
|
|
|
111
167
|
print(gen.randbytes(16).hex(), gen.random(), gen.choice(["a", "b"]))
|
|
112
168
|
```
|
|
113
169
|
|
|
114
|
-
###
|
|
170
|
+
### 7. Cryptographically Strong Generators (`csprng`)
|
|
115
171
|
The ones to actually guard things with, plus deliberately broken ones that teach.
|
|
116
172
|
|
|
117
173
|
**OS-level CSPRNGs** (live entropy, behave like `random.SystemRandom` — seeding
|
|
@@ -136,14 +192,23 @@ the native API is not reachable on the running platform.
|
|
|
136
192
|
- `Hash_DRBG` – SHA-256 with Hash_df (440-bit state)
|
|
137
193
|
- `Dual_EC_DRBG` — *deliberately backdoored*, kept as a teaching exhibit
|
|
138
194
|
|
|
139
|
-
**Stream / accumulator designs**: `ChaCha20DRBG`, `Salsa20DRBG`
|
|
140
|
-
DRBGs on the two classic stream ciphers), `FortunaRandom`
|
|
141
|
-
AES-256-CTR, rekey every 65536 blocks,
|
|
142
|
-
legacy predecessor `YarrowRandom`
|
|
143
|
-
`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).
|
|
144
209
|
|
|
145
210
|
```python
|
|
146
|
-
from multirandom.csprng
|
|
211
|
+
from multirandom.csprng import SecretsRandom, CTR_DRBG, Dual_EC_DRBG
|
|
147
212
|
|
|
148
213
|
gen = SecretsRandom()
|
|
149
214
|
print(gen.token_hex(32)) # live kernel entropy
|
|
@@ -178,7 +243,7 @@ module ships with this demo running in its own self-checks at
|
|
|
178
243
|
`truncate_bits=8`. It warns on first use. Never use it for real randomness.
|
|
179
244
|
|
|
180
245
|
```python
|
|
181
|
-
# Run
|
|
246
|
+
# Run the DRBG sweep: python -m multirandom.csprng._drbg_base
|
|
182
247
|
```
|
|
183
248
|
|
|
184
249
|
### The global unsecure-prng warning switch
|
|
@@ -200,7 +265,7 @@ multirandom.ignore_unsecure_prng_warning_do_not_set_to_true_unless_you_know_how_
|
|
|
200
265
|
|
|
201
266
|
### Requirements
|
|
202
267
|
- Python 3.9+
|
|
203
|
-
- Dependencies listed in `requirements.txt` (e.g., `pyautogui`, `aiohttp`)
|
|
268
|
+
- Dependencies listed in `requirements.txt` (e.g., `pyautogui`, `aiohttp`, `pycryptodome`)
|
|
204
269
|
|
|
205
270
|
### Installation
|
|
206
271
|
```bash
|
|
@@ -11,6 +11,20 @@ A comprehensive library for exploring and generating randomness across the spect
|
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
14
|
+
## 📚 Full Documentation
|
|
15
|
+
|
|
16
|
+
Deep-dive docs live in [`docs/`](docs/):
|
|
17
|
+
|
|
18
|
+
| Page | Contents |
|
|
19
|
+
|---|---|
|
|
20
|
+
| [Getting started](docs/getting-started.md) | installation, quickstart, which generator to pick |
|
|
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, 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
|
+
| [Self-checks](docs/self-checks.md) | the assert batteries verifying every generator |
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
14
28
|
## 🏗 Project Structure
|
|
15
29
|
|
|
16
30
|
```text
|
|
@@ -21,15 +35,32 @@ multirandom/
|
|
|
21
35
|
│ ├── rand_via_clicks.py # Human-in-the-loop entropy
|
|
22
36
|
│ ├── rand_using_online_api.py # Quantum & Hardware APIs (ANU QRNG, Roll-API)
|
|
23
37
|
│ ├── rand_using_real_random.py # Microphone entropy (real-random)
|
|
24
|
-
│ └── weather_rand.py #
|
|
38
|
+
│ └── weather_rand.py # Open-Meteo weather entropy (large unhashed seeds)
|
|
25
39
|
├── csprng/ # Cryptographically Strong Randomness
|
|
26
|
-
│ ├──
|
|
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)
|
|
27
50
|
│ ├── sha_rand.py # MD5, SHA-1/2/3, BLAKE2, SHAKE PRNGs
|
|
28
51
|
│ └── original_random.py # Wrapper for Python's built-in random
|
|
29
52
|
└── prng_unsecure/ # Mathematical Randomness (UNSECURE PRNGs)
|
|
30
53
|
├── xor_shift.py # XorShift & Xoshiro Family
|
|
31
|
-
├──
|
|
32
|
-
|
|
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
|
|
33
64
|
```
|
|
34
65
|
|
|
35
66
|
---
|
|
@@ -76,7 +107,31 @@ Run `python -m multirandom.prng_unsecure.xor_shift` to check every generator aga
|
|
|
76
107
|
### 4. Mathematical LCGs (`prng_unsecure`)
|
|
77
108
|
Classic Linear Congruential Generators with customizable shift parameters. Includes `reverse_roll()` methods to demonstrate the insecurity of simple modular arithmetic.
|
|
78
109
|
|
|
79
|
-
### 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`)
|
|
80
135
|
Counter-mode hash DRBGs: `H(seed || counter)` drives the full `random.Random` API.
|
|
81
136
|
|
|
82
137
|
- **SHA-2**: SHA-224, SHA-256, SHA-384, SHA-512
|
|
@@ -92,7 +147,7 @@ gen = BLAKE2bRandom("my seed")
|
|
|
92
147
|
print(gen.randbytes(16).hex(), gen.random(), gen.choice(["a", "b"]))
|
|
93
148
|
```
|
|
94
149
|
|
|
95
|
-
###
|
|
150
|
+
### 7. Cryptographically Strong Generators (`csprng`)
|
|
96
151
|
The ones to actually guard things with, plus deliberately broken ones that teach.
|
|
97
152
|
|
|
98
153
|
**OS-level CSPRNGs** (live entropy, behave like `random.SystemRandom` — seeding
|
|
@@ -117,14 +172,23 @@ the native API is not reachable on the running platform.
|
|
|
117
172
|
- `Hash_DRBG` – SHA-256 with Hash_df (440-bit state)
|
|
118
173
|
- `Dual_EC_DRBG` — *deliberately backdoored*, kept as a teaching exhibit
|
|
119
174
|
|
|
120
|
-
**Stream / accumulator designs**: `ChaCha20DRBG`, `Salsa20DRBG`
|
|
121
|
-
DRBGs on the two classic stream ciphers), `FortunaRandom`
|
|
122
|
-
AES-256-CTR, rekey every 65536 blocks,
|
|
123
|
-
legacy predecessor `YarrowRandom`
|
|
124
|
-
`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).
|
|
125
189
|
|
|
126
190
|
```python
|
|
127
|
-
from multirandom.csprng
|
|
191
|
+
from multirandom.csprng import SecretsRandom, CTR_DRBG, Dual_EC_DRBG
|
|
128
192
|
|
|
129
193
|
gen = SecretsRandom()
|
|
130
194
|
print(gen.token_hex(32)) # live kernel entropy
|
|
@@ -159,7 +223,7 @@ module ships with this demo running in its own self-checks at
|
|
|
159
223
|
`truncate_bits=8`. It warns on first use. Never use it for real randomness.
|
|
160
224
|
|
|
161
225
|
```python
|
|
162
|
-
# Run
|
|
226
|
+
# Run the DRBG sweep: python -m multirandom.csprng._drbg_base
|
|
163
227
|
```
|
|
164
228
|
|
|
165
229
|
### The global unsecure-prng warning switch
|
|
@@ -181,7 +245,7 @@ multirandom.ignore_unsecure_prng_warning_do_not_set_to_true_unless_you_know_how_
|
|
|
181
245
|
|
|
182
246
|
### Requirements
|
|
183
247
|
- Python 3.9+
|
|
184
|
-
- Dependencies listed in `requirements.txt` (e.g., `pyautogui`, `aiohttp`)
|
|
248
|
+
- Dependencies listed in `requirements.txt` (e.g., `pyautogui`, `aiohttp`, `pycryptodome`)
|
|
185
249
|
|
|
186
250
|
### Installation
|
|
187
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]
|
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""
|
|
2
|
+
Project: https://pypi.org/project/multirandom/
|
|
3
|
+
Repo: https://github.com/BlackCatOfficialytb/multirandom
|
|
4
|
+
License: Apache 2.0
|
|
5
|
+
Module: multirandom
|
|
6
|
+
Description:
|
|
7
|
+
One random-number package, several philosophies of chance:
|
|
2
8
|
|
|
3
|
-
* ``prng_unsecure/`` - educational PRNGs that must never guard anything (LCGs, xorshift).
|
|
4
|
-
* ``csprng/`` - cryptographically strong generators (OS entropy, NIST DRBGs, stream ciphers).
|
|
5
9
|
* ``trng/`` - true/hardware entropy sources (microphones, weather, clicks, online APIs).
|
|
10
|
+
* ``csprng/`` - cryptographically strong generators (OS entropy, NIST DRBGs, stream ciphers).
|
|
11
|
+
* ``prng_unsecure/`` - educational PRNGs that must never guard anything (LCGs, xorshift).
|
|
6
12
|
|
|
7
13
|
Everything in ``prng_unsecure/`` and the deliberately broken ``Dual_EC_DRBG`` warns the
|
|
8
14
|
first time it is used. Those warnings are controlled by ONE global variable::
|
|
@@ -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()
|