cryptoexp 0.1.0a1__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 (81) hide show
  1. cryptoexp-0.1.0a1/LICENSE +21 -0
  2. cryptoexp-0.1.0a1/MANIFEST.in +19 -0
  3. cryptoexp-0.1.0a1/PKG-INFO +326 -0
  4. cryptoexp-0.1.0a1/README.md +284 -0
  5. cryptoexp-0.1.0a1/README.zh-CN.md +249 -0
  6. cryptoexp-0.1.0a1/challenges/classical_caesar.txt +2 -0
  7. cryptoexp-0.1.0a1/challenges/classical_custom_prefix.txt +3 -0
  8. cryptoexp-0.1.0a1/challenges/classical_morse.txt +2 -0
  9. cryptoexp-0.1.0a1/challenges/classical_vigenere.txt +2 -0
  10. cryptoexp-0.1.0a1/challenges/crc_preimage.txt +8 -0
  11. cryptoexp-0.1.0a1/challenges/encoding_chain.txt +2 -0
  12. cryptoexp-0.1.0a1/challenges/knapsack.txt +3 -0
  13. cryptoexp-0.1.0a1/challenges/lcg.txt +2 -0
  14. cryptoexp-0.1.0a1/challenges/lfsr_predict.txt +6 -0
  15. cryptoexp-0.1.0a1/challenges/make_challenges.py +334 -0
  16. cryptoexp-0.1.0a1/challenges/rsa_dp_leak.txt +5 -0
  17. cryptoexp-0.1.0a1/challenges/rsa_factor.txt +4 -0
  18. cryptoexp-0.1.0a1/challenges/rsa_fermat.txt +4 -0
  19. cryptoexp-0.1.0a1/challenges/rsa_high_bits.txt +5 -0
  20. cryptoexp-0.1.0a1/challenges/rsa_shared_prime.txt +6 -0
  21. cryptoexp-0.1.0a1/challenges/rsa_small_e.txt +4 -0
  22. cryptoexp-0.1.0a1/challenges/rsa_wiener.txt +4 -0
  23. cryptoexp-0.1.0a1/challenges/symmetric_ecb.txt +2 -0
  24. cryptoexp-0.1.0a1/challenges/xor_repeating.txt +2 -0
  25. cryptoexp-0.1.0a1/challenges/xor_single.txt +2 -0
  26. cryptoexp-0.1.0a1/pyproject.toml +58 -0
  27. cryptoexp-0.1.0a1/schema/cryptoexp.schema.json +183 -0
  28. cryptoexp-0.1.0a1/setup.cfg +4 -0
  29. cryptoexp-0.1.0a1/src/cryptoexp/__init__.py +397 -0
  30. cryptoexp-0.1.0a1/src/cryptoexp/cli.py +229 -0
  31. cryptoexp-0.1.0a1/src/cryptoexp/core/__init__.py +0 -0
  32. cryptoexp-0.1.0a1/src/cryptoexp/core/analysis/__init__.py +147 -0
  33. cryptoexp-0.1.0a1/src/cryptoexp/core/analysis/classical.py +115 -0
  34. cryptoexp-0.1.0a1/src/cryptoexp/core/analysis/crc_scan.py +155 -0
  35. cryptoexp-0.1.0a1/src/cryptoexp/core/analysis/encoding_scan.py +123 -0
  36. cryptoexp-0.1.0a1/src/cryptoexp/core/analysis/findings.py +106 -0
  37. cryptoexp-0.1.0a1/src/cryptoexp/core/analysis/lattice_analysis.py +143 -0
  38. cryptoexp-0.1.0a1/src/cryptoexp/core/analysis/lfsr_scan.py +130 -0
  39. cryptoexp-0.1.0a1/src/cryptoexp/core/analysis/numbertheory.py +119 -0
  40. cryptoexp-0.1.0a1/src/cryptoexp/core/analysis/rsa.py +272 -0
  41. cryptoexp-0.1.0a1/src/cryptoexp/core/analysis/symmetric.py +105 -0
  42. cryptoexp-0.1.0a1/src/cryptoexp/core/context.py +192 -0
  43. cryptoexp-0.1.0a1/src/cryptoexp/core/report.py +290 -0
  44. cryptoexp-0.1.0a1/src/cryptoexp/core/solve.py +1204 -0
  45. cryptoexp-0.1.0a1/src/cryptoexp/core/verify.py +157 -0
  46. cryptoexp-0.1.0a1/src/cryptoexp/hypothesis.py +544 -0
  47. cryptoexp-0.1.0a1/src/cryptoexp/lab.py +145 -0
  48. cryptoexp-0.1.0a1/src/cryptoexp/oracle.py +242 -0
  49. cryptoexp-0.1.0a1/src/cryptoexp/utils/__init__.py +0 -0
  50. cryptoexp-0.1.0a1/src/cryptoexp/utils/aes.py +234 -0
  51. cryptoexp-0.1.0a1/src/cryptoexp/utils/algebra.py +508 -0
  52. cryptoexp-0.1.0a1/src/cryptoexp/utils/classic_extra.py +713 -0
  53. cryptoexp-0.1.0a1/src/cryptoexp/utils/dlp.py +124 -0
  54. cryptoexp-0.1.0a1/src/cryptoexp/utils/encoding.py +949 -0
  55. cryptoexp-0.1.0a1/src/cryptoexp/utils/forensics.py +1224 -0
  56. cryptoexp-0.1.0a1/src/cryptoexp/utils/gf.py +176 -0
  57. cryptoexp-0.1.0a1/src/cryptoexp/utils/gf2.py +1052 -0
  58. cryptoexp-0.1.0a1/src/cryptoexp/utils/hashes.py +452 -0
  59. cryptoexp-0.1.0a1/src/cryptoexp/utils/keys.py +608 -0
  60. cryptoexp-0.1.0a1/src/cryptoexp/utils/lattice.py +494 -0
  61. cryptoexp-0.1.0a1/src/cryptoexp/utils/modular.py +829 -0
  62. cryptoexp-0.1.0a1/src/cryptoexp/utils/output.py +65 -0
  63. cryptoexp-0.1.0a1/src/cryptoexp/utils/pad.py +73 -0
  64. cryptoexp-0.1.0a1/src/cryptoexp/utils/polytools.py +772 -0
  65. cryptoexp-0.1.0a1/src/cryptoexp/utils/prng.py +128 -0
  66. cryptoexp-0.1.0a1/src/cryptoexp/utils/prng_extra.py +599 -0
  67. cryptoexp-0.1.0a1/src/cryptoexp/utils/rsa_attacks.py +696 -0
  68. cryptoexp-0.1.0a1/src/cryptoexp/utils/rsa_ops.py +372 -0
  69. cryptoexp-0.1.0a1/src/cryptoexp/utils/signatures.py +244 -0
  70. cryptoexp-0.1.0a1/src/cryptoexp/utils/stream.py +316 -0
  71. cryptoexp-0.1.0a1/src/cryptoexp/utils/symtools.py +108 -0
  72. cryptoexp-0.1.0a1/src/cryptoexp.egg-info/PKG-INFO +326 -0
  73. cryptoexp-0.1.0a1/src/cryptoexp.egg-info/SOURCES.txt +79 -0
  74. cryptoexp-0.1.0a1/src/cryptoexp.egg-info/dependency_links.txt +1 -0
  75. cryptoexp-0.1.0a1/src/cryptoexp.egg-info/entry_points.txt +2 -0
  76. cryptoexp-0.1.0a1/src/cryptoexp.egg-info/requires.txt +18 -0
  77. cryptoexp-0.1.0a1/src/cryptoexp.egg-info/top_level.txt +1 -0
  78. cryptoexp-0.1.0a1/tests/test_challenges.py +322 -0
  79. cryptoexp-0.1.0a1/tests/test_forensics.py +227 -0
  80. cryptoexp-0.1.0a1/tests/test_library.py +597 -0
  81. cryptoexp-0.1.0a1/tests/test_toolkit.py +505 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 coolmoon & guaidao2
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,19 @@
1
+ # Source distribution contents. Explicit on purpose: without this, a `python -m build`
2
+ # run would sweep the third-party `intelpwn` clone, the internal `evidence` notes and
3
+ # the scratch `.tmp` directory into the published sdist.
4
+ include README.md
5
+ include README.zh-CN.md
6
+ include LICENSE
7
+ include pyproject.toml
8
+ recursive-include src/cryptoexp *.py
9
+ recursive-include tests *.py
10
+ recursive-include challenges *.py *.txt
11
+ recursive-include schema *.json
12
+ prune intelpwn
13
+ prune evidence
14
+ prune .tmp
15
+ prune dist
16
+ prune build
17
+ prune solves
18
+ prune lab
19
+ global-exclude __pycache__ *.py[cod] *.egg-info
@@ -0,0 +1,326 @@
1
+ Metadata-Version: 2.4
2
+ Name: cryptoexp
3
+ Version: 0.1.0a1
4
+ Summary: Crypto toolkit: dependency-free primitives, attack automation and analysis pipeline for CTF, security assessment and research
5
+ Author: coolmoon, guaidao2
6
+ Maintainer: coolmoon, guaidao2
7
+ License: MIT
8
+ Project-URL: Homepage, https://github.com/guaidao2/cryptoexp
9
+ Project-URL: Repository, https://github.com/guaidao2/cryptoexp
10
+ Project-URL: Issues, https://github.com/guaidao2/cryptoexp/issues
11
+ Project-URL: Changelog, https://github.com/guaidao2/cryptoexp/releases
12
+ Keywords: ctf,cryptography,security,pentest,rsa,ecdsa,lattice,coppersmith,oracle,lfsr,crc,prng,forensics,jwt
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Intended Audience :: Information Technology
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Topic :: Security
23
+ Classifier: Topic :: Security :: Cryptography
24
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
25
+ Requires-Python: >=3.10
26
+ Description-Content-Type: text/markdown
27
+ License-File: LICENSE
28
+ Provides-Extra: fast
29
+ Requires-Dist: gmpy2; extra == "fast"
30
+ Provides-Extra: symbolic
31
+ Requires-Dist: sympy; extra == "symbolic"
32
+ Provides-Extra: aes
33
+ Requires-Dist: pycryptodome; extra == "aes"
34
+ Provides-Extra: solver
35
+ Requires-Dist: z3-solver; extra == "solver"
36
+ Provides-Extra: all
37
+ Requires-Dist: gmpy2; extra == "all"
38
+ Requires-Dist: sympy; extra == "all"
39
+ Requires-Dist: pycryptodome; extra == "all"
40
+ Requires-Dist: z3-solver; extra == "all"
41
+ Dynamic: license-file
42
+
43
+ # cryptoexp
44
+
45
+ **Crypto toolkit for CTF, security assessment and research** — a pwntools-style
46
+ toolbox of dependency-free primitives, plus an analysis pipeline on top.
47
+
48
+ **Authors: coolmoon & guaidao2** — MIT. Import name and PyPI distribution name are
49
+ both `cryptoexp` (the plain `cryptokit` name was already taken by an unrelated 2022
50
+ project, so this one is `cryptoexp`).
51
+
52
+ [中文说明](https://github.com/guaidao2/cryptoexp/blob/main/README.zh-CN.md) ·
53
+ [repository](https://github.com/guaidao2/cryptoexp)
54
+
55
+ - Library: `import cryptoexp as cx` — number theory, modular arithmetic, polynomial
56
+ and GF(2) algebra, encodings, classic ciphers, block/stream ciphers, hash length
57
+ extension, PRNG recovery, RSA/signature attacks, lattice tools, oracle attacks, and
58
+ a forensics/assessment layer (batch GCD key audits, DER signature and JWT parsing,
59
+ weak-PRNG fingerprinting).
60
+ - CLI: `python3 cryptoexp_cli.py analyze|hypotheses|lab|list` (analysis pipeline, JSON
61
+ contract, solve-script generation).
62
+ - **Zero dependencies**: the core uses only the standard library (AES, LLL,
63
+ Coppersmith, SHA-1/2 length extension, ChaCha20, MT19937 … are all implemented
64
+ here). Optional accelerators (`gmpy2`, `sympy`, `pycryptodome`, `z3`) are
65
+ auto-detected and never required.
66
+ - **English-first** source and tool output (reports, generated scripts, docstrings);
67
+ this document is English, with a [Chinese README](https://github.com/guaidao2/cryptoexp/blob/main/README.zh-CN.md)
68
+ alongside for readers who prefer it.
69
+ - `from cryptoexp import *` exists but is meant for throwaway solve scripts: it only
70
+ brings names that are unmistakably ours. Generic primitives (`gcd`, `sha256`,
71
+ `AES`, …) stay reachable as `cx.gcd` so a star import cannot shadow your own names.
72
+ - CTF solving is the core target, **not the only one** — see
73
+ [Beyond CTF](#beyond-ctf-security-assessment-and-research).
74
+ - No web UI: this is a library plus a CLI.
75
+
76
+ ---
77
+
78
+ ## Custom flag formats (library-first)
79
+
80
+ "What a flag looks like" belongs to the engagement, not to the library, so it is
81
+ configurable in three layers. All of them work straight from `pip install cryptoexp`:
82
+
83
+ ```python
84
+ from cryptoexp import flag_candidates, find_flags, set_flag_prefixes
85
+
86
+ # 1) per call (no hidden state - the right way inside a library or a service)
87
+ flag_candidates(payload, prefixes=["DH", "corp_"]) # DH{...}, corp_...{...}
88
+ flag_candidates(payload, pattern=r"ACME-\d{4}-[a-z0-9]{8}") # arbitrary format
89
+
90
+ # 2) process default (scripts, notebooks)
91
+ set_flag_prefixes(["DH"], merge=True)
92
+ flag_candidates(payload) # now uses DH, plus the built-ins
93
+
94
+ # 3) environment (CLI, CI)
95
+ # CRYPTOEXP_FLAG_PREFIXES=DH,corp_ cryptoexp analyze challenge.txt
96
+ ```
97
+
98
+ The CLI mirrors it: `--flag-prefix DH` (repeatable) and `--flag-regex '<regex>'`.
99
+
100
+ `find_flags(data, prefixes=[...])` is the general entry point: it returns every
101
+ marker-shaped string with `{"match", "kind": "strict"|"loose", "offset"}`, which is
102
+ what you want on a dump, a config file or a log rather than on a challenge
103
+ statement. Prefixes are matched literally (a prefix containing `.` cannot match
104
+ `abc{...}`), matching is case-insensitive, and placeholder shapes such as `DH{...}`
105
+ inside a statement are deliberately *not* treated as flags — otherwise a challenge
106
+ would confirm itself.
107
+
108
+ `analyze_all(path, flag_prefixes=["DH"])` scopes the format to that one run
109
+ (a `ContextVar`, so parallel calls cannot leak into each other) and records it in
110
+ the result, so verification and the JSON report agree with the caller.
111
+
112
+ ---
113
+
114
+ ## Beyond CTF: security assessment and research
115
+
116
+ The same primitives answer real engagement questions. Library first, everything
117
+ importable from the package root:
118
+
119
+ | Question | Call |
120
+ |---|---|
121
+ | Do any of our public keys share a prime factor? | `batch_gcd(moduli)` → pairs + factor groups, product-tree based |
122
+ | Is this RSA key weak? | `audit_rsa_key(n, e, extra_moduli=[...])` → findings with severity (`fermat_close_primes`, `wiener_vulnerable`, `shared_factor`, `small_exponent`, …) |
123
+ | Is this token really random? | `detect_weak_prng(outputs)` → identifies LCG / glibc `rand` / xorshift / Java `Random` / MT19937, only when the recovery replays the observations |
124
+ | What is in this capture? | `parse_jwt(token)`, `parse_der_signature(der)`, `parse_pem`/`parse_ssh_public_key`, `key_fingerprint_sha256` |
125
+ | Where are the marked strings in this dump? | `find_flags(data, prefixes=[...])` |
126
+ | Two signatures, same nonce? | `ecdsa_nonce_reuse(...)` / `dsa_nonce_reuse(...)` → private key |
127
+ | Is this checksum forgeable? | `crc_forge_append(...)`, `crc_solve_unknown(...)` |
128
+ | Can I extend this MAC? | `length_extension(...)`, `HashState` |
129
+
130
+ Honest scope, because overclaiming here is worse than a missing feature: cryptoexp
131
+ does no network I/O, no scanning and no HTTP/TLS session parsing. Oracle attacks
132
+ take a callable you supply (write a ten-line adapter for the target), and the
133
+ analysis pipeline expects "a statement plus ciphertext blobs" as input. For research
134
+ it gives you the primitives (LLL, Coppersmith, GF(2)/GF(p) algebra, DLP, PRNG state
135
+ recovery, CRC/JWT/DER handling) as composable functions with published-vector tests.
136
+
137
+ ---
138
+
139
+ ## Install
140
+
141
+ ```bash
142
+ pip install cryptoexp # once a stable release exists (see the alpha note)
143
+ pipx install cryptoexp # if you only want the CLI
144
+ ```
145
+
146
+ The first release is a **pre-release** (`0.1.0a1`), so pip needs `--pre`:
147
+
148
+ ```bash
149
+ pip install --pre cryptoexp
150
+ ```
151
+
152
+ From a clone, nothing needs installing — the package is importable through the
153
+ repository entry point and the source tree:
154
+
155
+ ```bash
156
+ git clone https://github.com/guaidao2/cryptoexp.git
157
+ cd cryptoexp
158
+ python cryptoexp_cli.py analyze challenges/rsa_wiener.txt # CLI, no install
159
+ python -c "import sys; sys.path.insert(0, 'src'); import cryptoexp" # or add src/ to PYTHONPATH
160
+ ```
161
+
162
+ Zero runtime dependencies: the core is standard-library only, so it also works on
163
+ locked-down machines without a package index.
164
+
165
+ ---
166
+
167
+ ## Quick start
168
+
169
+ ```bash
170
+ # nothing to install
171
+ python3 cryptoexp_cli.py analyze challenges/rsa_wiener.txt
172
+ python3 cryptoexp_cli.py analyze challenges/xor_repeating.txt --solve --run
173
+ python3 cryptoexp_cli.py hypotheses challenges/rsa_high_bits.txt # attack surface + gaps
174
+ python3 cryptoexp_cli.py lab "paste the whole statement here" # workbench scaffold
175
+ python3 cryptoexp_cli.py list # analyzers + solvers + API map
176
+ ```
177
+
178
+ ```python
179
+ import cryptoexp as cx
180
+
181
+ cx.long_to_bytes(0x4142) # b'AB'
182
+ cx.b64d('ZmxhZ3thfQ==') # b'flag{a}'
183
+ cx.algebra.gcd(24, 36) # 12
184
+ cx.lll([[1, 1, 1], [1, 0, 1], [0, 1, 1]]) # pure-Python lattice reduction
185
+ cx.known_high_bits_factor(n, p_high, 160) # Coppersmith
186
+ cx.wiener_attack(e, n, c) # {'ok':..., 'plaintext':..., 'factors':...}
187
+ cx.ecb_byte_at_a_time(cx.make_ecb_oracle(key, secret)) # oracle attacks
188
+ cx.padding_oracle_attack(valid, iv + ct)
189
+ cx.clone_from_outputs(six_hundred_and_twenty_four_words) # MT19937 state clone
190
+ cx.discrete_log(g, h, p)['x'] # BSGS -> Pohlig-Hellman
191
+ cx.dsa_nonce_reuse(p, q, g, y, r, s1, s2, h1, h2) # nonce-reuse key recovery
192
+ cx.crc_compute(b"123456789", 32, 0x04C11DB7, 0xFFFFFFFF, True, True, 0xFFFFFFFF) # 0xCBF43926
193
+ cx.length_extension(h1, len(data), b"&admin=1", secret_len=len(secret))
194
+ cx.audit_rsa_key(n, e, extra_moduli=[other_n]) # weak-key findings with severity
195
+ cx.berlekamp_massey(bits) # recover LFSR taps from output bits
196
+ cx.java_random_predict(outputs, 4) # java.util.Random state recovery
197
+ ```
198
+
199
+ ---
200
+
201
+ ## Coverage (the mechanical operations)
202
+
203
+ | Domain | Module | What you get |
204
+ |---|---|---|
205
+ | Number theory | `utils/algebra.py` | gcd/egcd/lcm, modinv, CRT, integer roots, Miller-Rabin, Pollard rho, continued fractions, Wiener, Fermat, BSGS, Tonelli-Shanks, LCG recovery |
206
+ | Modular extras | `utils/modular.py` | Legendre/Jacobi/Kronecker, `sqrt_mod` (prime and composite), totient/Carmichael/Möbius, `order_mod`, primitive roots, non-coprime `crt_general`, gcd/lcm of lists, Pollard p-1, Williams p+1, smoothness, `binomial_mod` |
207
+ | Polynomials | `utils/polytools.py` | poly divmod/gcd/derivative/roots-mod-p/from-roots/compose/powmod, irreducibility (Rabin), resultant, GF(2) polynomial arithmetic (bit-encoded) |
208
+ | GF(p) linear algebra | `utils/gf.py` | RREF, solve, nullspace, inverse, matrix product, LCG parameter solving, linear-map recovery (Hill / LFSR-style) |
209
+ | GF(2) + LFSR + CRC | `utils/gf2.py` | GF(2) RREF/rank/solve/nullspace/inverse, `LFSR`, Berlekamp-Massey tap recovery, CRC compute/catalog/parameter recovery/**forgery**/**preimage with an unknown field**, bit-ordering helpers |
210
+ | Lattice | `utils/lattice.py` | pure-Python LLL, univariate Coppersmith, known-high-bits factoring, low-density subset sum (LLL) and meet-in-the-middle |
211
+ | Encodings | `utils/encoding.py` | hex/base64/base32/base58/base85/binary, decode-chain search, single-byte and repeating-key XOR, Caesar/affine/Vigenère/Morse/fence with scoring |
212
+ | Classic ciphers | `utils/classic_extra.py` | Atbash, ROT47/ROT-N, Bacon, Playfair, Hill, columnar transposition, rail fence, autokey, substitution, base62/base91, URL/HTML decoding, cipher fingerprinting |
213
+ | Block ciphers | `utils/aes.py`, `utils/pad.py`, `utils/symtools.py` | pure-Python AES-128/192/256 in ECB/CBC, PKCS#7, XOR, CBC byte flipping, ECB detection from ciphertext alone, block-size inference |
214
+ | Stream ciphers | `utils/stream.py` | RC4, ChaCha20 (RFC 8439), AES-CTR, keystream reuse / crib dragging |
215
+ | Hashes | `utils/hashes.py` | pure-Python SHA-1/SHA-256 and **length extension** (`length_extension`, resumable `HashState`) |
216
+ | PRNG | `utils/prng.py`, `utils/prng_extra.py` | MT19937 clone/predict/seed-crack, Java `Random` recovery+prediction, glibc `rand` state recovery, xorshift recovery, truncated-LCG (lattice) |
217
+ | RSA attacks | `utils/rsa_ops.py`, `utils/rsa_attacks.py` | small-e, broadcast, common modulus, shared prime, Wiener, Fermat, Pollard, dp leak, phi leak, known high bits, Franklin-Reiter, Håstad with padding, stereotyped message, parity/LSB oracle |
218
+ | Keys | `utils/keys.py` | PEM/DER RSA public+private parsing, OpenSSH public/private keys, DER encoder, SHA256 fingerprints |
219
+ | Signatures | `utils/signatures.py` | ECDSA/DSA nonce-reuse recovery, toy-curve sign/verify, e=3 signature forgery, PKCS#1 v1.5 padding |
220
+ | Oracle attacks | `oracle.py` | block-size/mode detection, unknown-prefix alignment, ECB byte-at-a-time, CBC padding oracle, local oracles for practice |
221
+ | Analysis | `core/`, `hypothesis.py`, `lab.py` | blackboard context, two registries, 27 attack hypotheses with gap reporting, workbench scaffold generator, three-state verification; analyzer coverage now includes **CRC** (catalog lookup, preimage, forgery) and **LFSR** (tap recovery + keystream), each with a solve template |
222
+
223
+ ---
224
+
225
+ ## Why it is not a template bank
226
+
227
+ intelpwn's "fixed vulnerability class → fixed exploit chain" works for pwn because
228
+ the classes are finite. Crypto is the opposite: you read the construction, find the
229
+ structural weakness, and often write the last step. So cryptoexp is layered:
230
+
231
+ | Layer | Role | Where |
232
+ |---|---|---|
233
+ | Primitives | reusable building blocks (everything in the table above) | `src/cryptoexp/utils/*` |
234
+ | Analysis | extract parameters, detect the attack surface, attempt attacks | `src/cryptoexp/core/analysis/*` |
235
+ | Hypothesis engine | 27 explicit hypotheses with `needs`/gaps: which apply, why not, **what piece is missing** | `src/cryptoexp/hypothesis.py` |
236
+ | Workbench | runnable scaffold: params inlined, hypotheses and gaps as comments, starter snippets, a seam for your idea | `src/cryptoexp/lab.py` |
237
+ | Solve templates | fast path for well-known families; emits a standalone script | `src/cryptoexp/core/solve.py` |
238
+ | Verification | confirmed / candidate / not_reproduced, with RSA re-encryption and strict flag matching | `src/cryptoexp/core/verify.py` |
239
+
240
+ Templates are a convenience, not the answer. Mechanical operations live in the
241
+ library so you can compose them; for the long tail the CLI hands you the ranked
242
+ hypotheses, the missing-information list and a workbench.
243
+
244
+ ---
245
+
246
+ ## Design notes (inherited discipline)
247
+
248
+ - **Blackboard**: `analyze_all` materialises the context once (text, files, named
249
+ parameters, ints, blobs, decoded bytes); analyzers consume it instead of
250
+ re-parsing. Failures degrade to warnings and never abort the run.
251
+ - **Two registries**: `register_analyzer(name)` and
252
+ `register_solver(name, predicate, gen, priority)`; unknown keys render
253
+ automatically, so a new analyzer needs no changes in the presentation layer.
254
+ - **Evidence grading**: every conclusion carries severity + confidence; "rule guess"
255
+ is labelled as such.
256
+ - **Unknown is a first-class state**: hypotheses declare what they need and the
257
+ engine reports the gap instead of staying silent.
258
+ - **Bounded work**: expensive steps take explicit budgets; a budget hit is reported.
259
+ - **Verification beats heuristics**: every bug found while building this is now a
260
+ regression test. Examples: search objectives must not contain the flag bonus (hill
261
+ climbing otherwise fabricates a flag), Coppersmith must reject the trivial
262
+ `p = n` "factor", LLL parameters `m=t=3` are required for 160-of-256 known bits,
263
+ generated scripts must actually run and print the flag.
264
+
265
+ ---
266
+
267
+ ## Tests
268
+
269
+ ```bash
270
+ python -m unittest discover -s tests -v
271
+ python challenges/make_challenges.py # regenerate the sample corpus (optional)
272
+ ```
273
+
274
+ `tests/test_library.py` checks official vectors (FIPS-197 AES, RFC 8439 ChaCha20,
275
+ CRC-32/CRC-16 check values, SHA-1/SHA-256 test vectors), algebra and lattice
276
+ identities, oracle attacks against locally built oracles, signature nonce reuse and
277
+ the MT19937 clone. `tests/test_toolkit.py` covers the mechanical layer added in v0.3:
278
+ modular symbols and non-coprime CRT, polynomial/GF(2) arithmetic, CRC forgery,
279
+ LFSR tap recovery, length extension, RC4/ChaCha20/AES-CTR vectors, Java/glibc PRNG
280
+ recovery, RSA attack wrappers, key parsing and the signature primitives.
281
+ `tests/test_challenges.py` is end-to-end: 19 samples must yield their flags, generated
282
+ solve scripts must compile **and run to the flag**, and the JSON contract must hold.
283
+ `tests/test_forensics.py` covers the assessment layer (batch GCD, key audit, JWT/DER
284
+ parsing, weak-PRNG identification) on constructed real-world material.
285
+
286
+ Current total: **153 tests, all green** (`python -m unittest discover -s tests`).
287
+
288
+ ---
289
+
290
+ ## Honest limitations
291
+
292
+ - `glibc_rand_recover` needs roughly **96+ consecutive outputs**. Below that the
293
+ hidden low bits are genuinely underdetermined: the function enumerates *all*
294
+ states consistent with the observations and returns a state only when they agree
295
+ on the next outputs, otherwise `None`. It never guesses.
296
+ - `lcg_recover_truncated` is **not** implemented as a lattice. When the visible high
297
+ bits do not uniquely determine the parameters it returns `None` (documented in its
298
+ docstring) instead of inventing a sequence. The lattice formulation was attempted
299
+ and did not reach a reliable state.
300
+ - Coppersmith with degree > 1 only works at small lattice sizes in pure Python:
301
+ `stereotyped_message` and `hastad_padded` therefore try the exact integer-root path
302
+ first (when no reduction modulo n happens, which is the common CTF shape) and fall
303
+ back to the lattice, which is where the time budget can run out on large moduli.
304
+ - ECC beyond toy curves, multivariate Coppersmith, Boneh-Durfee, Bleichenbacher's
305
+ full attack: documented skeletons or absent — the tool reports the hypothesis and
306
+ the gap rather than pretending.
307
+ - Discrete log only via BSGS / Pohlig-Hellman; group orders with large prime factors
308
+ are reported as infeasible.
309
+ - Pure-Python Fraction LLL is comfortable up to dimension ~8; Coppersmith defaults
310
+ for degree 1 are tuned to `m=t=3` (~6 s on a 512-bit N with 160 known bits).
311
+ - Oracle attacks need a callable; the workbench ships a remote-oracle template.
312
+ - The analyzer routes **CRC** and **LFSR** challenges end to end (catalog lookup,
313
+ preimage, forgery; tap recovery + keystream) with generated solve scripts. The
314
+ remaining new families (ChaCha20/RC4, hash length extension, Java/glibc PRNG, keys,
315
+ signatures) are **library + unit-test** coverage with published vectors, not yet
316
+ routed by the CLI: compose the library calls (or use the hypothesis/workbench
317
+ layers) for those.
318
+ - CRC preimage solving pins the unknown field only when `8 * unknown_len <= width`
319
+ (4 bytes under CRC-32, 2 under CRC-16). With more unknowns it returns one solution
320
+ with the CRC matched and says `unique: False` — several byte strings fit, so it
321
+ cannot claim to have found the original one.
322
+ - Playfair / Hill / columnar / bacon are lossy by design (X padding, I/J and U/V
323
+ folding), so "round-trip" there means `decrypt(encrypt(x)) == prepared(x)`.
324
+ - No web interface and no CI yet.
325
+
326
+ License: MIT — coolmoon & guaidao2.
@@ -0,0 +1,284 @@
1
+ # cryptoexp
2
+
3
+ **Crypto toolkit for CTF, security assessment and research** — a pwntools-style
4
+ toolbox of dependency-free primitives, plus an analysis pipeline on top.
5
+
6
+ **Authors: coolmoon & guaidao2** — MIT. Import name and PyPI distribution name are
7
+ both `cryptoexp` (the plain `cryptokit` name was already taken by an unrelated 2022
8
+ project, so this one is `cryptoexp`).
9
+
10
+ [中文说明](https://github.com/guaidao2/cryptoexp/blob/main/README.zh-CN.md) ·
11
+ [repository](https://github.com/guaidao2/cryptoexp)
12
+
13
+ - Library: `import cryptoexp as cx` — number theory, modular arithmetic, polynomial
14
+ and GF(2) algebra, encodings, classic ciphers, block/stream ciphers, hash length
15
+ extension, PRNG recovery, RSA/signature attacks, lattice tools, oracle attacks, and
16
+ a forensics/assessment layer (batch GCD key audits, DER signature and JWT parsing,
17
+ weak-PRNG fingerprinting).
18
+ - CLI: `python3 cryptoexp_cli.py analyze|hypotheses|lab|list` (analysis pipeline, JSON
19
+ contract, solve-script generation).
20
+ - **Zero dependencies**: the core uses only the standard library (AES, LLL,
21
+ Coppersmith, SHA-1/2 length extension, ChaCha20, MT19937 … are all implemented
22
+ here). Optional accelerators (`gmpy2`, `sympy`, `pycryptodome`, `z3`) are
23
+ auto-detected and never required.
24
+ - **English-first** source and tool output (reports, generated scripts, docstrings);
25
+ this document is English, with a [Chinese README](https://github.com/guaidao2/cryptoexp/blob/main/README.zh-CN.md)
26
+ alongside for readers who prefer it.
27
+ - `from cryptoexp import *` exists but is meant for throwaway solve scripts: it only
28
+ brings names that are unmistakably ours. Generic primitives (`gcd`, `sha256`,
29
+ `AES`, …) stay reachable as `cx.gcd` so a star import cannot shadow your own names.
30
+ - CTF solving is the core target, **not the only one** — see
31
+ [Beyond CTF](#beyond-ctf-security-assessment-and-research).
32
+ - No web UI: this is a library plus a CLI.
33
+
34
+ ---
35
+
36
+ ## Custom flag formats (library-first)
37
+
38
+ "What a flag looks like" belongs to the engagement, not to the library, so it is
39
+ configurable in three layers. All of them work straight from `pip install cryptoexp`:
40
+
41
+ ```python
42
+ from cryptoexp import flag_candidates, find_flags, set_flag_prefixes
43
+
44
+ # 1) per call (no hidden state - the right way inside a library or a service)
45
+ flag_candidates(payload, prefixes=["DH", "corp_"]) # DH{...}, corp_...{...}
46
+ flag_candidates(payload, pattern=r"ACME-\d{4}-[a-z0-9]{8}") # arbitrary format
47
+
48
+ # 2) process default (scripts, notebooks)
49
+ set_flag_prefixes(["DH"], merge=True)
50
+ flag_candidates(payload) # now uses DH, plus the built-ins
51
+
52
+ # 3) environment (CLI, CI)
53
+ # CRYPTOEXP_FLAG_PREFIXES=DH,corp_ cryptoexp analyze challenge.txt
54
+ ```
55
+
56
+ The CLI mirrors it: `--flag-prefix DH` (repeatable) and `--flag-regex '<regex>'`.
57
+
58
+ `find_flags(data, prefixes=[...])` is the general entry point: it returns every
59
+ marker-shaped string with `{"match", "kind": "strict"|"loose", "offset"}`, which is
60
+ what you want on a dump, a config file or a log rather than on a challenge
61
+ statement. Prefixes are matched literally (a prefix containing `.` cannot match
62
+ `abc{...}`), matching is case-insensitive, and placeholder shapes such as `DH{...}`
63
+ inside a statement are deliberately *not* treated as flags — otherwise a challenge
64
+ would confirm itself.
65
+
66
+ `analyze_all(path, flag_prefixes=["DH"])` scopes the format to that one run
67
+ (a `ContextVar`, so parallel calls cannot leak into each other) and records it in
68
+ the result, so verification and the JSON report agree with the caller.
69
+
70
+ ---
71
+
72
+ ## Beyond CTF: security assessment and research
73
+
74
+ The same primitives answer real engagement questions. Library first, everything
75
+ importable from the package root:
76
+
77
+ | Question | Call |
78
+ |---|---|
79
+ | Do any of our public keys share a prime factor? | `batch_gcd(moduli)` → pairs + factor groups, product-tree based |
80
+ | Is this RSA key weak? | `audit_rsa_key(n, e, extra_moduli=[...])` → findings with severity (`fermat_close_primes`, `wiener_vulnerable`, `shared_factor`, `small_exponent`, …) |
81
+ | Is this token really random? | `detect_weak_prng(outputs)` → identifies LCG / glibc `rand` / xorshift / Java `Random` / MT19937, only when the recovery replays the observations |
82
+ | What is in this capture? | `parse_jwt(token)`, `parse_der_signature(der)`, `parse_pem`/`parse_ssh_public_key`, `key_fingerprint_sha256` |
83
+ | Where are the marked strings in this dump? | `find_flags(data, prefixes=[...])` |
84
+ | Two signatures, same nonce? | `ecdsa_nonce_reuse(...)` / `dsa_nonce_reuse(...)` → private key |
85
+ | Is this checksum forgeable? | `crc_forge_append(...)`, `crc_solve_unknown(...)` |
86
+ | Can I extend this MAC? | `length_extension(...)`, `HashState` |
87
+
88
+ Honest scope, because overclaiming here is worse than a missing feature: cryptoexp
89
+ does no network I/O, no scanning and no HTTP/TLS session parsing. Oracle attacks
90
+ take a callable you supply (write a ten-line adapter for the target), and the
91
+ analysis pipeline expects "a statement plus ciphertext blobs" as input. For research
92
+ it gives you the primitives (LLL, Coppersmith, GF(2)/GF(p) algebra, DLP, PRNG state
93
+ recovery, CRC/JWT/DER handling) as composable functions with published-vector tests.
94
+
95
+ ---
96
+
97
+ ## Install
98
+
99
+ ```bash
100
+ pip install cryptoexp # once a stable release exists (see the alpha note)
101
+ pipx install cryptoexp # if you only want the CLI
102
+ ```
103
+
104
+ The first release is a **pre-release** (`0.1.0a1`), so pip needs `--pre`:
105
+
106
+ ```bash
107
+ pip install --pre cryptoexp
108
+ ```
109
+
110
+ From a clone, nothing needs installing — the package is importable through the
111
+ repository entry point and the source tree:
112
+
113
+ ```bash
114
+ git clone https://github.com/guaidao2/cryptoexp.git
115
+ cd cryptoexp
116
+ python cryptoexp_cli.py analyze challenges/rsa_wiener.txt # CLI, no install
117
+ python -c "import sys; sys.path.insert(0, 'src'); import cryptoexp" # or add src/ to PYTHONPATH
118
+ ```
119
+
120
+ Zero runtime dependencies: the core is standard-library only, so it also works on
121
+ locked-down machines without a package index.
122
+
123
+ ---
124
+
125
+ ## Quick start
126
+
127
+ ```bash
128
+ # nothing to install
129
+ python3 cryptoexp_cli.py analyze challenges/rsa_wiener.txt
130
+ python3 cryptoexp_cli.py analyze challenges/xor_repeating.txt --solve --run
131
+ python3 cryptoexp_cli.py hypotheses challenges/rsa_high_bits.txt # attack surface + gaps
132
+ python3 cryptoexp_cli.py lab "paste the whole statement here" # workbench scaffold
133
+ python3 cryptoexp_cli.py list # analyzers + solvers + API map
134
+ ```
135
+
136
+ ```python
137
+ import cryptoexp as cx
138
+
139
+ cx.long_to_bytes(0x4142) # b'AB'
140
+ cx.b64d('ZmxhZ3thfQ==') # b'flag{a}'
141
+ cx.algebra.gcd(24, 36) # 12
142
+ cx.lll([[1, 1, 1], [1, 0, 1], [0, 1, 1]]) # pure-Python lattice reduction
143
+ cx.known_high_bits_factor(n, p_high, 160) # Coppersmith
144
+ cx.wiener_attack(e, n, c) # {'ok':..., 'plaintext':..., 'factors':...}
145
+ cx.ecb_byte_at_a_time(cx.make_ecb_oracle(key, secret)) # oracle attacks
146
+ cx.padding_oracle_attack(valid, iv + ct)
147
+ cx.clone_from_outputs(six_hundred_and_twenty_four_words) # MT19937 state clone
148
+ cx.discrete_log(g, h, p)['x'] # BSGS -> Pohlig-Hellman
149
+ cx.dsa_nonce_reuse(p, q, g, y, r, s1, s2, h1, h2) # nonce-reuse key recovery
150
+ cx.crc_compute(b"123456789", 32, 0x04C11DB7, 0xFFFFFFFF, True, True, 0xFFFFFFFF) # 0xCBF43926
151
+ cx.length_extension(h1, len(data), b"&admin=1", secret_len=len(secret))
152
+ cx.audit_rsa_key(n, e, extra_moduli=[other_n]) # weak-key findings with severity
153
+ cx.berlekamp_massey(bits) # recover LFSR taps from output bits
154
+ cx.java_random_predict(outputs, 4) # java.util.Random state recovery
155
+ ```
156
+
157
+ ---
158
+
159
+ ## Coverage (the mechanical operations)
160
+
161
+ | Domain | Module | What you get |
162
+ |---|---|---|
163
+ | Number theory | `utils/algebra.py` | gcd/egcd/lcm, modinv, CRT, integer roots, Miller-Rabin, Pollard rho, continued fractions, Wiener, Fermat, BSGS, Tonelli-Shanks, LCG recovery |
164
+ | Modular extras | `utils/modular.py` | Legendre/Jacobi/Kronecker, `sqrt_mod` (prime and composite), totient/Carmichael/Möbius, `order_mod`, primitive roots, non-coprime `crt_general`, gcd/lcm of lists, Pollard p-1, Williams p+1, smoothness, `binomial_mod` |
165
+ | Polynomials | `utils/polytools.py` | poly divmod/gcd/derivative/roots-mod-p/from-roots/compose/powmod, irreducibility (Rabin), resultant, GF(2) polynomial arithmetic (bit-encoded) |
166
+ | GF(p) linear algebra | `utils/gf.py` | RREF, solve, nullspace, inverse, matrix product, LCG parameter solving, linear-map recovery (Hill / LFSR-style) |
167
+ | GF(2) + LFSR + CRC | `utils/gf2.py` | GF(2) RREF/rank/solve/nullspace/inverse, `LFSR`, Berlekamp-Massey tap recovery, CRC compute/catalog/parameter recovery/**forgery**/**preimage with an unknown field**, bit-ordering helpers |
168
+ | Lattice | `utils/lattice.py` | pure-Python LLL, univariate Coppersmith, known-high-bits factoring, low-density subset sum (LLL) and meet-in-the-middle |
169
+ | Encodings | `utils/encoding.py` | hex/base64/base32/base58/base85/binary, decode-chain search, single-byte and repeating-key XOR, Caesar/affine/Vigenère/Morse/fence with scoring |
170
+ | Classic ciphers | `utils/classic_extra.py` | Atbash, ROT47/ROT-N, Bacon, Playfair, Hill, columnar transposition, rail fence, autokey, substitution, base62/base91, URL/HTML decoding, cipher fingerprinting |
171
+ | Block ciphers | `utils/aes.py`, `utils/pad.py`, `utils/symtools.py` | pure-Python AES-128/192/256 in ECB/CBC, PKCS#7, XOR, CBC byte flipping, ECB detection from ciphertext alone, block-size inference |
172
+ | Stream ciphers | `utils/stream.py` | RC4, ChaCha20 (RFC 8439), AES-CTR, keystream reuse / crib dragging |
173
+ | Hashes | `utils/hashes.py` | pure-Python SHA-1/SHA-256 and **length extension** (`length_extension`, resumable `HashState`) |
174
+ | PRNG | `utils/prng.py`, `utils/prng_extra.py` | MT19937 clone/predict/seed-crack, Java `Random` recovery+prediction, glibc `rand` state recovery, xorshift recovery, truncated-LCG (lattice) |
175
+ | RSA attacks | `utils/rsa_ops.py`, `utils/rsa_attacks.py` | small-e, broadcast, common modulus, shared prime, Wiener, Fermat, Pollard, dp leak, phi leak, known high bits, Franklin-Reiter, Håstad with padding, stereotyped message, parity/LSB oracle |
176
+ | Keys | `utils/keys.py` | PEM/DER RSA public+private parsing, OpenSSH public/private keys, DER encoder, SHA256 fingerprints |
177
+ | Signatures | `utils/signatures.py` | ECDSA/DSA nonce-reuse recovery, toy-curve sign/verify, e=3 signature forgery, PKCS#1 v1.5 padding |
178
+ | Oracle attacks | `oracle.py` | block-size/mode detection, unknown-prefix alignment, ECB byte-at-a-time, CBC padding oracle, local oracles for practice |
179
+ | Analysis | `core/`, `hypothesis.py`, `lab.py` | blackboard context, two registries, 27 attack hypotheses with gap reporting, workbench scaffold generator, three-state verification; analyzer coverage now includes **CRC** (catalog lookup, preimage, forgery) and **LFSR** (tap recovery + keystream), each with a solve template |
180
+
181
+ ---
182
+
183
+ ## Why it is not a template bank
184
+
185
+ intelpwn's "fixed vulnerability class → fixed exploit chain" works for pwn because
186
+ the classes are finite. Crypto is the opposite: you read the construction, find the
187
+ structural weakness, and often write the last step. So cryptoexp is layered:
188
+
189
+ | Layer | Role | Where |
190
+ |---|---|---|
191
+ | Primitives | reusable building blocks (everything in the table above) | `src/cryptoexp/utils/*` |
192
+ | Analysis | extract parameters, detect the attack surface, attempt attacks | `src/cryptoexp/core/analysis/*` |
193
+ | Hypothesis engine | 27 explicit hypotheses with `needs`/gaps: which apply, why not, **what piece is missing** | `src/cryptoexp/hypothesis.py` |
194
+ | Workbench | runnable scaffold: params inlined, hypotheses and gaps as comments, starter snippets, a seam for your idea | `src/cryptoexp/lab.py` |
195
+ | Solve templates | fast path for well-known families; emits a standalone script | `src/cryptoexp/core/solve.py` |
196
+ | Verification | confirmed / candidate / not_reproduced, with RSA re-encryption and strict flag matching | `src/cryptoexp/core/verify.py` |
197
+
198
+ Templates are a convenience, not the answer. Mechanical operations live in the
199
+ library so you can compose them; for the long tail the CLI hands you the ranked
200
+ hypotheses, the missing-information list and a workbench.
201
+
202
+ ---
203
+
204
+ ## Design notes (inherited discipline)
205
+
206
+ - **Blackboard**: `analyze_all` materialises the context once (text, files, named
207
+ parameters, ints, blobs, decoded bytes); analyzers consume it instead of
208
+ re-parsing. Failures degrade to warnings and never abort the run.
209
+ - **Two registries**: `register_analyzer(name)` and
210
+ `register_solver(name, predicate, gen, priority)`; unknown keys render
211
+ automatically, so a new analyzer needs no changes in the presentation layer.
212
+ - **Evidence grading**: every conclusion carries severity + confidence; "rule guess"
213
+ is labelled as such.
214
+ - **Unknown is a first-class state**: hypotheses declare what they need and the
215
+ engine reports the gap instead of staying silent.
216
+ - **Bounded work**: expensive steps take explicit budgets; a budget hit is reported.
217
+ - **Verification beats heuristics**: every bug found while building this is now a
218
+ regression test. Examples: search objectives must not contain the flag bonus (hill
219
+ climbing otherwise fabricates a flag), Coppersmith must reject the trivial
220
+ `p = n` "factor", LLL parameters `m=t=3` are required for 160-of-256 known bits,
221
+ generated scripts must actually run and print the flag.
222
+
223
+ ---
224
+
225
+ ## Tests
226
+
227
+ ```bash
228
+ python -m unittest discover -s tests -v
229
+ python challenges/make_challenges.py # regenerate the sample corpus (optional)
230
+ ```
231
+
232
+ `tests/test_library.py` checks official vectors (FIPS-197 AES, RFC 8439 ChaCha20,
233
+ CRC-32/CRC-16 check values, SHA-1/SHA-256 test vectors), algebra and lattice
234
+ identities, oracle attacks against locally built oracles, signature nonce reuse and
235
+ the MT19937 clone. `tests/test_toolkit.py` covers the mechanical layer added in v0.3:
236
+ modular symbols and non-coprime CRT, polynomial/GF(2) arithmetic, CRC forgery,
237
+ LFSR tap recovery, length extension, RC4/ChaCha20/AES-CTR vectors, Java/glibc PRNG
238
+ recovery, RSA attack wrappers, key parsing and the signature primitives.
239
+ `tests/test_challenges.py` is end-to-end: 19 samples must yield their flags, generated
240
+ solve scripts must compile **and run to the flag**, and the JSON contract must hold.
241
+ `tests/test_forensics.py` covers the assessment layer (batch GCD, key audit, JWT/DER
242
+ parsing, weak-PRNG identification) on constructed real-world material.
243
+
244
+ Current total: **153 tests, all green** (`python -m unittest discover -s tests`).
245
+
246
+ ---
247
+
248
+ ## Honest limitations
249
+
250
+ - `glibc_rand_recover` needs roughly **96+ consecutive outputs**. Below that the
251
+ hidden low bits are genuinely underdetermined: the function enumerates *all*
252
+ states consistent with the observations and returns a state only when they agree
253
+ on the next outputs, otherwise `None`. It never guesses.
254
+ - `lcg_recover_truncated` is **not** implemented as a lattice. When the visible high
255
+ bits do not uniquely determine the parameters it returns `None` (documented in its
256
+ docstring) instead of inventing a sequence. The lattice formulation was attempted
257
+ and did not reach a reliable state.
258
+ - Coppersmith with degree > 1 only works at small lattice sizes in pure Python:
259
+ `stereotyped_message` and `hastad_padded` therefore try the exact integer-root path
260
+ first (when no reduction modulo n happens, which is the common CTF shape) and fall
261
+ back to the lattice, which is where the time budget can run out on large moduli.
262
+ - ECC beyond toy curves, multivariate Coppersmith, Boneh-Durfee, Bleichenbacher's
263
+ full attack: documented skeletons or absent — the tool reports the hypothesis and
264
+ the gap rather than pretending.
265
+ - Discrete log only via BSGS / Pohlig-Hellman; group orders with large prime factors
266
+ are reported as infeasible.
267
+ - Pure-Python Fraction LLL is comfortable up to dimension ~8; Coppersmith defaults
268
+ for degree 1 are tuned to `m=t=3` (~6 s on a 512-bit N with 160 known bits).
269
+ - Oracle attacks need a callable; the workbench ships a remote-oracle template.
270
+ - The analyzer routes **CRC** and **LFSR** challenges end to end (catalog lookup,
271
+ preimage, forgery; tap recovery + keystream) with generated solve scripts. The
272
+ remaining new families (ChaCha20/RC4, hash length extension, Java/glibc PRNG, keys,
273
+ signatures) are **library + unit-test** coverage with published vectors, not yet
274
+ routed by the CLI: compose the library calls (or use the hypothesis/workbench
275
+ layers) for those.
276
+ - CRC preimage solving pins the unknown field only when `8 * unknown_len <= width`
277
+ (4 bytes under CRC-32, 2 under CRC-16). With more unknowns it returns one solution
278
+ with the CRC matched and says `unique: False` — several byte strings fit, so it
279
+ cannot claim to have found the original one.
280
+ - Playfair / Hill / columnar / bacon are lossy by design (X padding, I/J and U/V
281
+ folding), so "round-trip" there means `decrypt(encrypt(x)) == prepared(x)`.
282
+ - No web interface and no CI yet.
283
+
284
+ License: MIT — coolmoon & guaidao2.