transcrypto 1.0.2__py3-none-any.whl → 1.1.1__py3-none-any.whl

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.
@@ -0,0 +1,2257 @@
1
+ Metadata-Version: 2.4
2
+ Name: transcrypto
3
+ Version: 1.1.1
4
+ Summary: Basic crypto primitives, not intended for actual use, but as a companion to --Criptografia, Métodos e Algoritmos--
5
+ Author-email: Daniel Balparda <balparda@github.com>
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/balparda/transcrypto
8
+ Project-URL: PyPI, https://pypi.org/project/transcrypto/
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.13
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Topic :: Utilities
13
+ Classifier: Topic :: Security :: Cryptography
14
+ Requires-Python: >=3.13.5
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Dynamic: license-file
18
+
19
+ # TransCrypto
20
+
21
+ Basic crypto primitives, not intended for actual use, but as a companion to *"Criptografia, Métodos e Algoritmos"*.
22
+
23
+ Started in July/2025, by Daniel Balparda. Since version 1.0.2 it is PyPI package:
24
+
25
+ <https://pypi.org/project/transcrypto/>
26
+
27
+ - [TransCrypto](#transcrypto)
28
+ - [License](#license)
29
+ - [Design assumptions / Disclaimers](#design-assumptions--disclaimers)
30
+ - [Install](#install)
31
+ - [Command-Line Interface](#command-line-interface)
32
+ - [Global Options](#global-options)
33
+ - [Top-Level Commands](#top-level-commands)
34
+ - [`random`](#random)
35
+ - [`random bits`](#random-bits)
36
+ - [`random int`](#random-int)
37
+ - [`random bytes`](#random-bytes)
38
+ - [`random prime`](#random-prime)
39
+ - [`isprime`](#isprime)
40
+ - [`primegen`](#primegen)
41
+ - [`mersenne`](#mersenne)
42
+ - [`gcd`](#gcd)
43
+ - [`xgcd`](#xgcd)
44
+ - [`mod`](#mod)
45
+ - [`mod inv`](#mod-inv)
46
+ - [`mod div`](#mod-div)
47
+ - [`mod exp`](#mod-exp)
48
+ - [`mod poly`](#mod-poly)
49
+ - [`mod lagrange`](#mod-lagrange)
50
+ - [`mod crt`](#mod-crt)
51
+ - [`hash`](#hash)
52
+ - [`hash sha256`](#hash-sha256)
53
+ - [`hash sha512`](#hash-sha512)
54
+ - [`hash file`](#hash-file)
55
+ - [`aes`](#aes)
56
+ - [`aes key`](#aes-key)
57
+ - [`aes encrypt`](#aes-encrypt)
58
+ - [`aes decrypt`](#aes-decrypt)
59
+ - [`aes ecb`](#aes-ecb)
60
+ - [`aes ecb encrypt`](#aes-ecb-encrypt)
61
+ - [`aes ecb decrypt`](#aes-ecb-decrypt)
62
+ - [`rsa`](#rsa)
63
+ - [`rsa new`](#rsa-new)
64
+ - [`rsa encrypt`](#rsa-encrypt)
65
+ - [`rsa decrypt`](#rsa-decrypt)
66
+ - [`rsa sign`](#rsa-sign)
67
+ - [`rsa verify`](#rsa-verify)
68
+ - [`elgamal`](#elgamal)
69
+ - [`elgamal shared`](#elgamal-shared)
70
+ - [`elgamal new`](#elgamal-new)
71
+ - [`elgamal encrypt`](#elgamal-encrypt)
72
+ - [`elgamal decrypt`](#elgamal-decrypt)
73
+ - [`elgamal sign`](#elgamal-sign)
74
+ - [`elgamal verify`](#elgamal-verify)
75
+ - [`dsa`](#dsa)
76
+ - [`dsa shared`](#dsa-shared)
77
+ - [`dsa new`](#dsa-new)
78
+ - [`dsa sign`](#dsa-sign)
79
+ - [`dsa verify`](#dsa-verify)
80
+ - [`bid`](#bid)
81
+ - [`bid new`](#bid-new)
82
+ - [`bid verify`](#bid-verify)
83
+ - [`sss`](#sss)
84
+ - [`sss new`](#sss-new)
85
+ - [`sss shares`](#sss-shares)
86
+ - [`sss recover`](#sss-recover)
87
+ - [`sss verify`](#sss-verify)
88
+ - [`doc`](#doc)
89
+ - [`doc md`](#doc-md)
90
+ - [Base Library](#base-library)
91
+ - [Humanized Sizes (IEC binary)](#humanized-sizes-iec-binary)
92
+ - [Humanized Decimal Quantities (SI)](#humanized-decimal-quantities-si)
93
+ - [Humanized Durations](#humanized-durations)
94
+ - [Cryptographically Secure Randomness](#cryptographically-secure-randomness)
95
+ - [Fixed-size random integers](#fixed-size-random-integers)
96
+ - [Uniform random integers in a range](#uniform-random-integers-in-a-range)
97
+ - [In-place secure shuffle](#in-place-secure-shuffle)
98
+ - [Random byte strings](#random-byte-strings)
99
+ - [Computing the Greatest Common Divisor](#computing-the-greatest-common-divisor)
100
+ - [Cryptographic Hashing](#cryptographic-hashing)
101
+ - [SHA-256 hashing](#sha-256-hashing)
102
+ - [SHA-512 hashing](#sha-512-hashing)
103
+ - [File hashing](#file-hashing)
104
+ - [Execution Timing](#execution-timing)
105
+ - [Context manager](#context-manager)
106
+ - [Decorator](#decorator)
107
+ - [Manual use](#manual-use)
108
+ - [Key points](#key-points)
109
+ - [Symmetric Encryption Interface](#symmetric-encryption-interface)
110
+ - [Serialization Pipeline](#serialization-pipeline)
111
+ - [Serialize](#serialize)
112
+ - [DeSerialize](#deserialize)
113
+ - [Crypto Objects General Properties (`CryptoKey`)](#crypto-objects-general-properties-cryptokey)
114
+ - [AES-256 Symmetric Encryption](#aes-256-symmetric-encryption)
115
+ - [Key creation](#key-creation)
116
+ - [AES-256 + GCM (default)](#aes-256--gcm-default)
117
+ - [AES-256 + ECB (unsafe, fixed block only)](#aes-256--ecb-unsafe-fixed-block-only)
118
+ - [Fast Modular Arithmetic](#fast-modular-arithmetic)
119
+ - [Chinese Remainder Theorem (CRT) – Pair](#chinese-remainder-theorem-crt--pair)
120
+ - [Modular Polynomials \& Lagrange Interpolation](#modular-polynomials--lagrange-interpolation)
121
+ - [Primality testing \& Prime generators, Mersenne primes](#primality-testing--prime-generators-mersenne-primes)
122
+ - [RSA (Rivest-Shamir-Adleman) Public Cryptography](#rsa-rivest-shamir-adleman-public-cryptography)
123
+ - [El-Gamal Public-Key Cryptography](#el-gamal-public-key-cryptography)
124
+ - [Shared Public Key](#shared-public-key)
125
+ - [Public Key](#public-key)
126
+ - [Private Key](#private-key)
127
+ - [DSA (Digital Signature Algorithm)](#dsa-digital-signature-algorithm)
128
+ - [Security notes](#security-notes)
129
+ - [Advanced: custom primes generator](#advanced-custom-primes-generator)
130
+ - [Public Bidding](#public-bidding)
131
+ - [SSS (Shamir Shared Secret)](#sss-shamir-shared-secret)
132
+ - [Appendix: Development Instructions](#appendix-development-instructions)
133
+ - [Setup](#setup)
134
+ - [Updating Dependencies](#updating-dependencies)
135
+ - [Creating a New Version](#creating-a-new-version)
136
+
137
+ ## License
138
+
139
+ Copyright 2025 Daniel Balparda <balparda@github.com>
140
+
141
+ Licensed under the ***Apache License, Version 2.0*** (the "License"); you may not use this file except in compliance with the License. You may obtain a [copy of the License here](http://www.apache.org/licenses/LICENSE-2.0).
142
+
143
+ Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
144
+
145
+ ## Design assumptions / Disclaimers
146
+
147
+ - The library is built to have reference, reliable, simple implementations of math and crypto primitives.
148
+ - All library methods' `int` are tailored to be efficient with arbitrarily large integers.
149
+ - Everything **should work**, as the library is **extensively tested**, *but not necessarily the most efficient or safe for real-world cryptographic use.* For real-world crypto use *other optimized/safe libraries* that were built to be resistant to malicious attacks.
150
+ - *All operations in this library may be vulnerable to timing attacks.*
151
+ - There is some logging and error messages that were written to be clear but in real-life security applications could leak private secrets. Again, this library is not build to be crypto safe. It was built as a simple tested reference implementation.
152
+
153
+ That being said, all care was taken that this is a good library with a solid implementation. *Have fun!*
154
+
155
+ ## Install
156
+
157
+ To use in your project just do:
158
+
159
+ ```sh
160
+ pip3 install transcrypto
161
+ ```
162
+
163
+ and then `from transcrypto import rsa` (or other parts of the library) for using it.
164
+
165
+ Known dependencies:
166
+
167
+ - [zstandard](https://pypi.org/project/zstandard/) ([docs](https://python-zstandard.readthedocs.org/))
168
+ - [cryptography](https://pypi.org/project/cryptography/) ([docs](https://cryptography.io/en/latest/))
169
+
170
+ <!-- cspell:disable -->
171
+
172
+ <!-- (auto-generated; do not edit between START/END) -->
173
+ <!-- INCLUDE:CLI.md START -->
174
+
175
+ ## Command-Line Interface
176
+
177
+ `transcrypto` is a command-line utility that provides access to all core functionality described in this documentation. It serves as a convenient wrapper over the Python APIs, enabling **cryptographic operations**, **number theory functions**, **secure randomness generation**, **hashing**, **AES**, **RSA**, **El-Gamal**, **DSA**, **bidding**, **SSS**, and other utilities without writing code.
178
+
179
+ Invoke with:
180
+
181
+ ```bash
182
+ poetry run transcrypto <command> [sub-command] [options...]
183
+ ```
184
+
185
+ ### Global Options
186
+
187
+ | Option/Arg | Description |
188
+ |---|---|
189
+ | `-v, --verbose` | Increase verbosity (use -v/-vv/-vvv/-vvvv for ERROR/WARN/INFO/DEBUG) |
190
+ | `--hex` | Treat inputs as hex string (default) |
191
+ | `--b64` | Treat inputs as base64url |
192
+ | `--bin` | Treat inputs as binary (bytes) |
193
+ | `--out-hex` | Outputs as hex (default) |
194
+ | `--out-b64` | Outputs as base64url |
195
+ | `--out-bin` | Outputs as binary (bytes) |
196
+ | `-p, --key-path` | File path to serialized key object, if key is needed for operation [type: str] |
197
+ | `--protect` | Password to encrypt/decrypt key file if using the `-p`/`--key-path` option [type: str] |
198
+
199
+ ### Top-Level Commands
200
+
201
+ - **`random`** — `poetry run transcrypto random [-h] {bits,int,bytes,prime} ...`
202
+ - **`isprime`** — `poetry run transcrypto isprime [-h] n`
203
+ - **`primegen`** — `poetry run transcrypto primegen [-h] [-c COUNT] start`
204
+ - **`mersenne`** — `poetry run transcrypto mersenne [-h] [-k MIN_K] [-C CUTOFF_K]`
205
+ - **`gcd`** — `poetry run transcrypto gcd [-h] a b`
206
+ - **`xgcd`** — `poetry run transcrypto xgcd [-h] a b`
207
+ - **`mod`** — `poetry run transcrypto mod [-h] {inv,div,exp,poly,lagrange,crt} ...`
208
+ - **`hash`** — `poetry run transcrypto hash [-h] {sha256,sha512,file} ...`
209
+ - **`aes`** — `poetry run transcrypto aes [-h] {key,encrypt,decrypt,ecb} ...`
210
+ - **`rsa`** — `poetry run transcrypto rsa [-h] {new,encrypt,decrypt,sign,verify} ...`
211
+ - **`elgamal`** — `poetry run transcrypto elgamal [-h]`
212
+ - **`dsa`** — `poetry run transcrypto dsa [-h] {shared,new,sign,verify} ...`
213
+ - **`bid`** — `poetry run transcrypto bid [-h] {new,verify} ...`
214
+ - **`sss`** — `poetry run transcrypto sss [-h] {new,shares,recover,verify} ...`
215
+ - **`doc`** — `poetry run transcrypto doc [-h] {md} ...`
216
+
217
+ ```bash
218
+ Examples:
219
+
220
+ # --- Randomness ---
221
+ poetry run transcrypto random bits 16
222
+ poetry run transcrypto random int 1000 2000
223
+ poetry run transcrypto random bytes 32
224
+ poetry run transcrypto random prime 64
225
+
226
+ # --- Primes ---
227
+ poetry run transcrypto isprime 428568761
228
+ poetry run transcrypto primegen 100 -c 3
229
+ poetry run transcrypto mersenne -k 2 -C 17
230
+
231
+ # --- Integer / Modular Math ---
232
+ poetry run transcrypto gcd 462 1071
233
+ poetry run transcrypto xgcd 127 13
234
+ poetry run transcrypto mod inv 17 97
235
+ poetry run transcrypto mod div 6 127 13
236
+ poetry run transcrypto mod exp 438 234 127
237
+ poetry run transcrypto mod poly 12 17 10 20 30
238
+ poetry run transcrypto mod lagrange 5 13 2:4 6:3 7:1
239
+ poetry run transcrypto mod crt 6 7 127 13
240
+
241
+ # --- Hashing ---
242
+ poetry run transcrypto hash sha256 xyz
243
+ poetry run transcrypto --b64 hash sha512 eHl6
244
+ poetry run transcrypto hash file /etc/passwd --digest sha512
245
+
246
+ # --- AES ---
247
+ poetry run transcrypto --out-b64 aes key "correct horse battery staple"
248
+ poetry run transcrypto --b64 --out-b64 aes encrypt -k "<b64key>" "secret"
249
+ poetry run transcrypto --b64 --out-b64 aes decrypt -k "<b64key>" "<ciphertext>"
250
+ poetry run transcrypto aes ecb -k "<b64key>" encrypt "<128bithexblock>"
251
+ poetry run transcrypto aes ecb -k "<b64key>" decrypt "<128bithexblock>"
252
+
253
+ # --- RSA ---
254
+ poetry run transcrypto -p rsa-key rsa new --bits 2048
255
+ poetry run transcrypto -p rsa-key.pub rsa encrypt <plaintext>
256
+ poetry run transcrypto -p rsa-key.priv rsa decrypt <ciphertext>
257
+ poetry run transcrypto -p rsa-key.priv rsa sign <message>
258
+ poetry run transcrypto -p rsa-key.pub rsa verify <message> <signature>
259
+
260
+ # --- ElGamal ---
261
+ poetry run transcrypto -p eg-key elgamal shared --bits 2048
262
+ poetry run transcrypto -p eg-key elgamal new
263
+ poetry run transcrypto -p eg-key.pub elgamal encrypt <plaintext>
264
+ poetry run transcrypto -p eg-key.priv elgamal decrypt <c1:c2>
265
+ poetry run transcrypto -p eg-key.priv elgamal sign <message>
266
+ poetry run transcrypto-p eg-key.pub elgamal verify <message> <s1:s2>
267
+
268
+ # --- DSA ---
269
+ poetry run transcrypto -p dsa-key dsa shared --p-bits 2048 --q-bits 256
270
+ poetry run transcrypto -p dsa-key dsa new
271
+ poetry run transcrypto -p dsa-key.priv dsa sign <message>
272
+ poetry run transcrypto -p dsa-key.pub dsa verify <message> <s1:s2>
273
+
274
+ # --- Public Bid ---
275
+ poetry run transcrypto --bin bid new "tomorrow it will rain"
276
+ poetry run transcrypto --out-bin bid verify
277
+
278
+ # --- Shamir Secret Sharing (SSS) ---
279
+ poetry run transcrypto -p sss-key sss new 3 --bits 1024
280
+ poetry run transcrypto -p sss-key sss shares <secret> 5
281
+ poetry run transcrypto -p sss-key sss recover
282
+ poetry run transcrypto -p sss-key sss verify <secret>
283
+ ```
284
+
285
+ ---
286
+
287
+ ### `random`
288
+
289
+ Cryptographically secure randomness, from the OS CSPRNG.
290
+
291
+ ```bash
292
+ poetry run transcrypto random [-h] {bits,int,bytes,prime} ...
293
+ ```
294
+
295
+ #### `random bits`
296
+
297
+ Random integer with exact bit length = `bits` (MSB will be 1).
298
+
299
+ ```bash
300
+ poetry run transcrypto random bits [-h] bits
301
+ ```
302
+
303
+ | Option/Arg | Description |
304
+ |---|---|
305
+ | `bits` | Number of bits, ≥ 8 [type: int] |
306
+
307
+ **Example:**
308
+
309
+ ```bash
310
+ $ poetry run transcrypto random bits 16
311
+ 36650
312
+ ```
313
+
314
+ #### `random int`
315
+
316
+ Uniform random integer in `[min, max]` range, inclusive.
317
+
318
+ ```bash
319
+ poetry run transcrypto random int [-h] min max
320
+ ```
321
+
322
+ | Option/Arg | Description |
323
+ |---|---|
324
+ | `min` | Minimum, ≥ 0 [type: str] |
325
+ | `max` | Maximum, > `min` [type: str] |
326
+
327
+ **Example:**
328
+
329
+ ```bash
330
+ $ poetry run transcrypto random int 1000 2000
331
+ 1628
332
+ ```
333
+
334
+ #### `random bytes`
335
+
336
+ Generates `n` cryptographically secure random bytes.
337
+
338
+ ```bash
339
+ poetry run transcrypto random bytes [-h] n
340
+ ```
341
+
342
+ | Option/Arg | Description |
343
+ |---|---|
344
+ | `n` | Number of bytes, ≥ 1 [type: int] |
345
+
346
+ **Example:**
347
+
348
+ ```bash
349
+ $ poetry run transcrypto random bytes 32
350
+ 6c6f1f88cb93c4323285a2224373d6e59c72a9c2b82e20d1c376df4ffbe9507f
351
+ ```
352
+
353
+ #### `random prime`
354
+
355
+ Generate a random prime with exact bit length = `bits` (MSB will be 1).
356
+
357
+ ```bash
358
+ poetry run transcrypto random prime [-h] bits
359
+ ```
360
+
361
+ | Option/Arg | Description |
362
+ |---|---|
363
+ | `bits` | Bit length, ≥ 11 [type: int] |
364
+
365
+ **Example:**
366
+
367
+ ```bash
368
+ $ poetry run transcrypto random prime 32
369
+ 2365910551
370
+ ```
371
+
372
+ ---
373
+
374
+ ### `isprime`
375
+
376
+ Primality test with safe defaults, useful for any integer size.
377
+
378
+ ```bash
379
+ poetry run transcrypto isprime [-h] n
380
+ ```
381
+
382
+ | Option/Arg | Description |
383
+ |---|---|
384
+ | `n` | Integer to test, ≥ 1 [type: str] |
385
+
386
+ **Example:**
387
+
388
+ ```bash
389
+ $ poetry run transcrypto isprime 2305843009213693951
390
+ True
391
+ $ poetry run transcrypto isprime 2305843009213693953
392
+ False
393
+ ```
394
+
395
+ ---
396
+
397
+ ### `primegen`
398
+
399
+ Generate (stream) primes ≥ `start` (prints a limited `count` by default).
400
+
401
+ ```bash
402
+ poetry run transcrypto primegen [-h] [-c COUNT] start
403
+ ```
404
+
405
+ | Option/Arg | Description |
406
+ |---|---|
407
+ | `start` | Starting integer (inclusive) [type: str] |
408
+ | `-c, --count` | How many to print (0 = unlimited) [type: int (default: 10)] |
409
+
410
+ **Example:**
411
+
412
+ ```bash
413
+ $ poetry run transcrypto primegen 100 -c 3
414
+ 101
415
+ 103
416
+ 107
417
+ ```
418
+
419
+ ---
420
+
421
+ ### `mersenne`
422
+
423
+ Generate (stream) Mersenne prime exponents `k`, also outputting `2^k-1` (the Mersenne prime, `M`) and `M×2^(k-1)` (the associated perfect number), starting at `min-k` and stopping once `k` > `cutoff-k`.
424
+
425
+ ```bash
426
+ poetry run transcrypto mersenne [-h] [-k MIN_K] [-C CUTOFF_K]
427
+ ```
428
+
429
+ | Option/Arg | Description |
430
+ |---|---|
431
+ | `-k, --min-k` | Starting exponent `k`, ≥ 1 [type: int (default: 1)] |
432
+ | `-C, --cutoff-k` | Stop once `k` > `cutoff-k` [type: int (default: 10000)] |
433
+
434
+ **Example:**
435
+
436
+ ```bash
437
+ $ poetry run transcrypto mersenne -k 0 -C 15
438
+ k=2 M=3 perfect=6
439
+ k=3 M=7 perfect=28
440
+ k=5 M=31 perfect=496
441
+ k=7 M=127 perfect=8128
442
+ k=13 M=8191 perfect=33550336
443
+ k=17 M=131071 perfect=8589869056
444
+ ```
445
+
446
+ ---
447
+
448
+ ### `gcd`
449
+
450
+ Greatest Common Divisor (GCD) of integers `a` and `b`.
451
+
452
+ ```bash
453
+ poetry run transcrypto gcd [-h] a b
454
+ ```
455
+
456
+ | Option/Arg | Description |
457
+ |---|---|
458
+ | `a` | Integer, ≥ 0 [type: str] |
459
+ | `b` | Integer, ≥ 0 (can't be both zero) [type: str] |
460
+
461
+ **Example:**
462
+
463
+ ```bash
464
+ $ poetry run transcrypto gcd 462 1071
465
+ 21
466
+ $ poetry run transcrypto gcd 0 5
467
+ 5
468
+ $ poetry run transcrypto gcd 127 13
469
+ 1
470
+ ```
471
+
472
+ ---
473
+
474
+ ### `xgcd`
475
+
476
+ Extended Greatest Common Divisor (x-GCD) of integers `a` and `b`, will return `(g, x, y)` where `a×x+b×y==g`.
477
+
478
+ ```bash
479
+ poetry run transcrypto xgcd [-h] a b
480
+ ```
481
+
482
+ | Option/Arg | Description |
483
+ |---|---|
484
+ | `a` | Integer, ≥ 0 [type: str] |
485
+ | `b` | Integer, ≥ 0 (can't be both zero) [type: str] |
486
+
487
+ **Example:**
488
+
489
+ ```bash
490
+ $ poetry run transcrypto xgcd 462 1071
491
+ (21, 7, -3)
492
+ $ poetry run transcrypto xgcd 0 5
493
+ (5, 0, 1)
494
+ $ poetry run transcrypto xgcd 127 13
495
+ (1, 4, -39)
496
+ ```
497
+
498
+ ---
499
+
500
+ ### `mod`
501
+
502
+ Modular arithmetic helpers.
503
+
504
+ ```bash
505
+ poetry run transcrypto mod [-h] {inv,div,exp,poly,lagrange,crt} ...
506
+ ```
507
+
508
+ #### `mod inv`
509
+
510
+ Modular inverse: find integer 0≤`i`<`m` such that `a×i ≡ 1 (mod m)`. Will only work if `gcd(a,m)==1`, else will fail with a message.
511
+
512
+ ```bash
513
+ poetry run transcrypto mod inv [-h] a m
514
+ ```
515
+
516
+ | Option/Arg | Description |
517
+ |---|---|
518
+ | `a` | Integer to invert [type: str] |
519
+ | `m` | Modulus `m`, ≥ 2 [type: str] |
520
+
521
+ **Example:**
522
+
523
+ ```bash
524
+ $ poetry run transcrypto mod inv 127 13
525
+ 4
526
+ $ poetry run transcrypto mod inv 17 3120
527
+ 2753
528
+ $ poetry run transcrypto mod inv 462 1071
529
+ <<INVALID>> no modular inverse exists (ModularDivideError)
530
+ ```
531
+
532
+ #### `mod div`
533
+
534
+ Modular division: find integer 0≤`z`<`m` such that `z×y ≡ x (mod m)`. Will only work if `gcd(y,m)==1` and `y!=0`, else will fail with a message.
535
+
536
+ ```bash
537
+ poetry run transcrypto mod div [-h] x y m
538
+ ```
539
+
540
+ | Option/Arg | Description |
541
+ |---|---|
542
+ | `x` | Integer [type: str] |
543
+ | `y` | Integer, cannot be zero [type: str] |
544
+ | `m` | Modulus `m`, ≥ 2 [type: str] |
545
+
546
+ **Example:**
547
+
548
+ ```bash
549
+ $ poetry run transcrypto mod div 6 127 13
550
+ 11
551
+ $ poetry run transcrypto mod div 6 0 13
552
+ <<INVALID>> no modular inverse exists (ModularDivideError)
553
+ ```
554
+
555
+ #### `mod exp`
556
+
557
+ Modular exponentiation: `a^e mod m`. Efficient, can handle huge values.
558
+
559
+ ```bash
560
+ poetry run transcrypto mod exp [-h] a e m
561
+ ```
562
+
563
+ | Option/Arg | Description |
564
+ |---|---|
565
+ | `a` | Integer [type: str] |
566
+ | `e` | Integer, ≥ 0 [type: str] |
567
+ | `m` | Modulus `m`, ≥ 2 [type: str] |
568
+
569
+ **Example:**
570
+
571
+ ```bash
572
+ $ poetry run transcrypto mod exp 438 234 127
573
+ 32
574
+ $ poetry run transcrypto mod exp 438 234 89854
575
+ 60622
576
+ ```
577
+
578
+ #### `mod poly`
579
+
580
+ Efficiently evaluate polynomial with `coeff` coefficients at point `x` modulo `m` (`c₀+c₁×x+c₂×x²+…+cₙ×xⁿ mod m`).
581
+
582
+ ```bash
583
+ poetry run transcrypto mod poly [-h] x m coeff [coeff ...]
584
+ ```
585
+
586
+ | Option/Arg | Description |
587
+ |---|---|
588
+ | `x` | Evaluation point `x` [type: str] |
589
+ | `m` | Modulus `m`, ≥ 2 [type: str] |
590
+ | `coeff` | Coefficients (constant-term first: `c₀+c₁×x+c₂×x²+…+cₙ×xⁿ`) [nargs: +] |
591
+
592
+ **Example:**
593
+
594
+ ```bash
595
+ $ poetry run transcrypto mod poly 12 17 10 20 30
596
+ 14 # (10+20×12+30×12² ≡ 14 (mod 17))
597
+ $ poetry run transcrypto mod poly 10 97 3 0 0 1 1
598
+ 42 # (3+1×10³+1×10⁴ ≡ 42 (mod 97))
599
+ ```
600
+
601
+ #### `mod lagrange`
602
+
603
+ Lagrange interpolation over modulus `m`: find the `f(x)` solution for the given `x` and `zₙ:f(zₙ)` points `pt`. The modulus `m` must be a prime.
604
+
605
+ ```bash
606
+ poetry run transcrypto mod lagrange [-h] x m pt [pt ...]
607
+ ```
608
+
609
+ | Option/Arg | Description |
610
+ |---|---|
611
+ | `x` | Evaluation point `x` [type: str] |
612
+ | `m` | Modulus `m`, ≥ 2 [type: str] |
613
+ | `pt` | Points `zₙ:f(zₙ)` as `key:value` pairs (e.g., `2:4 5:3 7:1`) [nargs: +] |
614
+
615
+ **Example:**
616
+
617
+ ```bash
618
+ $ poetry run transcrypto mod lagrange 5 13 2:4 6:3 7:1
619
+ 3 # passes through (2,4), (6,3), (7,1)
620
+ $ poetry run transcrypto mod lagrange 11 97 1:1 2:4 3:9 4:16 5:25
621
+ 24 # passes through (1,1), (2,4), (3,9), (4,16), (5,25)
622
+ ```
623
+
624
+ #### `mod crt`
625
+
626
+ Solves Chinese Remainder Theorem (CRT) Pair: finds the unique integer 0≤`x`<`(m1×m2)` satisfying both `x ≡ a1 (mod m1)` and `x ≡ a2 (mod m2)`, if `gcd(m1,m2)==1`.
627
+
628
+ ```bash
629
+ poetry run transcrypto mod crt [-h] a1 m1 a2 m2
630
+ ```
631
+
632
+ | Option/Arg | Description |
633
+ |---|---|
634
+ | `a1` | Integer residue for first congruence [type: str] |
635
+ | `m1` | Modulus `m1`, ≥ 2 and `gcd(m1,m2)==1` [type: str] |
636
+ | `a2` | Integer residue for second congruence [type: str] |
637
+ | `m2` | Modulus `m2`, ≥ 2 and `gcd(m1,m2)==1` [type: str] |
638
+
639
+ **Example:**
640
+
641
+ ```bash
642
+ $ poetry run transcrypto mod crt 6 7 127 13
643
+ 62
644
+ $ poetry run transcrypto mod crt 12 56 17 19
645
+ 796
646
+ $ poetry run transcrypto mod crt 6 7 462 1071
647
+ <<INVALID>> moduli m1/m2 not co-prime (ModularDivideError)
648
+ ```
649
+
650
+ ---
651
+
652
+ ### `hash`
653
+
654
+ Cryptographic Hashing (SHA-256 / SHA-512 / file).
655
+
656
+ ```bash
657
+ poetry run transcrypto hash [-h] {sha256,sha512,file} ...
658
+ ```
659
+
660
+ #### `hash sha256`
661
+
662
+ SHA-256 of input `data`.
663
+
664
+ ```bash
665
+ poetry run transcrypto hash sha256 [-h] data
666
+ ```
667
+
668
+ | Option/Arg | Description |
669
+ |---|---|
670
+ | `data` | Input data (raw text; or use --hex/--b64/--bin) [type: str] |
671
+
672
+ **Example:**
673
+
674
+ ```bash
675
+ $ poetry run transcrypto --bin hash sha256 xyz
676
+ 3608bca1e44ea6c4d268eb6db02260269892c0b42b86bbf1e77a6fa16c3c9282
677
+ $ poetry run transcrypto --b64 hash sha256 eHl6 # "xyz" in base-64
678
+ 3608bca1e44ea6c4d268eb6db02260269892c0b42b86bbf1e77a6fa16c3c9282
679
+ ```
680
+
681
+ #### `hash sha512`
682
+
683
+ SHA-512 of input `data`.
684
+
685
+ ```bash
686
+ poetry run transcrypto hash sha512 [-h] data
687
+ ```
688
+
689
+ | Option/Arg | Description |
690
+ |---|---|
691
+ | `data` | Input data (raw text; or use --hex/--b64/--bin) [type: str] |
692
+
693
+ **Example:**
694
+
695
+ ```bash
696
+ $ poetry run transcrypto --bin hash sha512 xyz
697
+ 4a3ed8147e37876adc8f76328e5abcc1b470e6acfc18efea0135f983604953a58e183c1a6086e91ba3e821d926f5fdeb37761c7ca0328a963f5e92870675b728
698
+ $ poetry run transcrypto --b64 hash sha512 eHl6 # "xyz" in base-64
699
+ 4a3ed8147e37876adc8f76328e5abcc1b470e6acfc18efea0135f983604953a58e183c1a6086e91ba3e821d926f5fdeb37761c7ca0328a963f5e92870675b728
700
+ ```
701
+
702
+ #### `hash file`
703
+
704
+ SHA-256/512 hash of file contents, defaulting to SHA-256.
705
+
706
+ ```bash
707
+ poetry run transcrypto hash file [-h] [--digest {sha256,sha512}] path
708
+ ```
709
+
710
+ | Option/Arg | Description |
711
+ |---|---|
712
+ | `path` | Path to existing file [type: str] |
713
+ | `--digest` | Digest type, SHA-256 ("sha256") or SHA-512 ("sha512") [choices: ['sha256', 'sha512'] (default: sha256)] |
714
+
715
+ **Example:**
716
+
717
+ ```bash
718
+ $ poetry run transcrypto hash file /etc/passwd --digest sha512
719
+ 8966f5953e79f55dfe34d3dc5b160ac4a4a3f9cbd1c36695a54e28d77c7874dff8595502f8a420608911b87d336d9e83c890f0e7ec11a76cb10b03e757f78aea
720
+ ```
721
+
722
+ ---
723
+
724
+ ### `aes`
725
+
726
+ AES-256 operations (GCM/ECB) and key derivation. No measures are taken here to prevent timing attacks.
727
+
728
+ ```bash
729
+ poetry run transcrypto aes [-h] {key,encrypt,decrypt,ecb} ...
730
+ ```
731
+
732
+ #### `aes key`
733
+
734
+ Derive key from a password (PBKDF2-HMAC-SHA256) with custom expensive salt and iterations. Very good/safe for simple password-to-key but not for passwords databases (because of constant salt).
735
+
736
+ ```bash
737
+ poetry run transcrypto aes key [-h] password
738
+ ```
739
+
740
+ | Option/Arg | Description |
741
+ |---|---|
742
+ | `password` | Password (leading/trailing spaces ignored) [type: str] |
743
+
744
+ **Example:**
745
+
746
+ ```bash
747
+ $ poetry run transcrypto --out-b64 aes key "correct horse battery staple"
748
+ DbWJ_ZrknLEEIoq_NpoCQwHYfjskGokpueN2O_eY0es=
749
+ $ poetry run transcrypto -p keyfile.out --protect hunter aes key "correct horse battery staple"
750
+ AES key saved to 'keyfile.out'
751
+ ```
752
+
753
+ #### `aes encrypt`
754
+
755
+ AES-256-GCM: safely encrypt `plaintext` with `-k`/`--key` or with `-p`/`--key-path` keyfile. All inputs are raw, or you can use `--bin`/`--hex`/`--b64` flags. Attention: if you provide `-a`/`--aad` (associated data, AAD), you will need to provide the same AAD when decrypting and it is NOT included in the `ciphertext`/CT returned by this method!
756
+
757
+ ```bash
758
+ poetry run transcrypto aes encrypt [-h] [-k KEY] [-a AAD] plaintext
759
+ ```
760
+
761
+ | Option/Arg | Description |
762
+ |---|---|
763
+ | `plaintext` | Input data to encrypt (PT) [type: str] |
764
+ | `-k, --key` | Key if `-p`/`--key-path` wasn't used (32 bytes) [type: str] |
765
+ | `-a, --aad` | Associated data (optional; has to be separately sent to receiver/stored) [type: str] |
766
+
767
+ **Example:**
768
+
769
+ ```bash
770
+ $ poetry run transcrypto --b64 --out-b64 aes encrypt -k DbWJ_ZrknLEEIoq_NpoCQwHYfjskGokpueN2O_eY0es= AAAAAAB4eXo=
771
+ F2_ZLrUw5Y8oDnbTP5t5xCUWX8WtVILLD0teyUi_37_4KHeV-YowVA==
772
+ $ poetry run transcrypto --b64 --out-b64 aes encrypt -k DbWJ_ZrknLEEIoq_NpoCQwHYfjskGokpueN2O_eY0es= -a eHl6 AAAAAAB4eXo=
773
+ xOlAHPUPpeyZHId-f3VQ_QKKMxjIW0_FBo9WOfIBrzjn0VkVV6xTRA==
774
+ ```
775
+
776
+ #### `aes decrypt`
777
+
778
+ AES-256-GCM: safely decrypt `ciphertext` with `-k`/`--key` or with `-p`/`--key-path` keyfile. All inputs are raw, or you can use `--bin`/`--hex`/`--b64` flags. Attention: if you provided `-a`/`--aad` (associated data, AAD) during encryption, you will need to provide the same AAD now!
779
+
780
+ ```bash
781
+ poetry run transcrypto aes decrypt [-h] [-k KEY] [-a AAD] ciphertext
782
+ ```
783
+
784
+ | Option/Arg | Description |
785
+ |---|---|
786
+ | `ciphertext` | Input data to decrypt (CT) [type: str] |
787
+ | `-k, --key` | Key if `-p`/`--key-path` wasn't used (32 bytes) [type: str] |
788
+ | `-a, --aad` | Associated data (optional; has to be exactly the same as used during encryption) [type: str] |
789
+
790
+ **Example:**
791
+
792
+ ```bash
793
+ $ poetry run transcrypto --b64 --out-b64 aes decrypt -k DbWJ_ZrknLEEIoq_NpoCQwHYfjskGokpueN2O_eY0es= F2_ZLrUw5Y8oDnbTP5t5xCUWX8WtVILLD0teyUi_37_4KHeV-YowVA==
794
+ AAAAAAB4eXo=
795
+ $ poetry run transcrypto --b64 --out-b64 aes decrypt -k DbWJ_ZrknLEEIoq_NpoCQwHYfjskGokpueN2O_eY0es= -a eHl6 xOlAHPUPpeyZHId-f3VQ_QKKMxjIW0_FBo9WOfIBrzjn0VkVV6xTRA==
796
+ AAAAAAB4eXo=
797
+ ```
798
+
799
+ #### `aes ecb`
800
+
801
+ AES-256-ECB: encrypt/decrypt 128 bit (16 bytes) hexadecimal blocks. UNSAFE, except for specifically encrypting hash blocks which are very much expected to look random. ECB mode will have the same output for the same input (no IV/nonce is used).
802
+
803
+ ```bash
804
+ poetry run transcrypto aes ecb [-h] [-k KEY] {encrypt,decrypt} ...
805
+ ```
806
+
807
+ | Option/Arg | Description |
808
+ |---|---|
809
+ | `-k, --key` | Key if `-p`/`--key-path` wasn't used (32 bytes; raw, or you can use `--bin`/`--hex`/`--b64` flags) [type: str] |
810
+
811
+ #### `aes ecb encrypt`
812
+
813
+ AES-256-ECB: encrypt 16-bytes hex `plaintext` with `-k`/`--key` or with `-p`/`--key-path` keyfile. UNSAFE, except for specifically encrypting hash blocks.
814
+
815
+ ```bash
816
+ poetry run transcrypto aes ecb encrypt [-h] plaintext
817
+ ```
818
+
819
+ | Option/Arg | Description |
820
+ |---|---|
821
+ | `plaintext` | Plaintext block as 32 hex chars (16-bytes) [type: str] |
822
+
823
+ **Example:**
824
+
825
+ ```bash
826
+ $ poetry run transcrypto --b64 aes ecb -k DbWJ_ZrknLEEIoq_NpoCQwHYfjskGokpueN2O_eY0es= encrypt 00112233445566778899aabbccddeeff
827
+ 54ec742ca3da7b752e527b74e3a798d7
828
+ ```
829
+
830
+ #### `aes ecb decrypt`
831
+
832
+ AES-256-ECB: decrypt 16-bytes hex `ciphertext` with `-k`/`--key` or with `-p`/`--key-path` keyfile. UNSAFE, except for specifically encrypting hash blocks.
833
+
834
+ ```bash
835
+ poetry run transcrypto aes ecb decrypt [-h] ciphertext
836
+ ```
837
+
838
+ | Option/Arg | Description |
839
+ |---|---|
840
+ | `ciphertext` | Ciphertext block as 32 hex chars (16-bytes) [type: str] |
841
+
842
+ **Example:**
843
+
844
+ ```bash
845
+ $ poetry run transcrypto --b64 aes ecb -k DbWJ_ZrknLEEIoq_NpoCQwHYfjskGokpueN2O_eY0es= decrypt 54ec742ca3da7b752e527b74e3a798d7
846
+ 00112233445566778899aabbccddeeff
847
+ ```
848
+
849
+ ---
850
+
851
+ ### `rsa`
852
+
853
+ Raw RSA (Rivest-Shamir-Adleman) asymmetric cryptography over *integers* (BEWARE: no OAEP/PSS padding or validation). These are pedagogical/raw primitives; do not use for new protocols. No measures are taken here to prevent timing attacks. All methods require file key(s) as `-p`/`--key-path` (see provided examples).
854
+
855
+ ```bash
856
+ poetry run transcrypto rsa [-h] {new,encrypt,decrypt,sign,verify} ...
857
+ ```
858
+
859
+ #### `rsa new`
860
+
861
+ Generate RSA private/public key pair with `bits` modulus size (prime sizes will be `bits`/2). Requires `-p`/`--key-path` to set the basename for output files.
862
+
863
+ ```bash
864
+ poetry run transcrypto rsa new [-h] [--bits BITS]
865
+ ```
866
+
867
+ | Option/Arg | Description |
868
+ |---|---|
869
+ | `--bits` | Modulus size in bits; the default is a safe size [type: int (default: 3332)] |
870
+
871
+ **Example:**
872
+
873
+ ```bash
874
+ $ poetry run transcrypto -p rsa-key rsa new --bits 64 # NEVER use such a small key: example only!
875
+ RSA private/public keys saved to 'rsa-key.priv/.pub'
876
+ ```
877
+
878
+ #### `rsa encrypt`
879
+
880
+ Encrypt integer `message` with public key.
881
+
882
+ ```bash
883
+ poetry run transcrypto rsa encrypt [-h] message
884
+ ```
885
+
886
+ | Option/Arg | Description |
887
+ |---|---|
888
+ | `message` | Integer message to encrypt, 1≤`message`<*modulus* [type: str] |
889
+
890
+ **Example:**
891
+
892
+ ```bash
893
+ $ poetry run transcrypto -p rsa-key.pub rsa encrypt 999
894
+ 6354905961171348600
895
+ ```
896
+
897
+ #### `rsa decrypt`
898
+
899
+ Decrypt integer `ciphertext` with private key.
900
+
901
+ ```bash
902
+ poetry run transcrypto rsa decrypt [-h] ciphertext
903
+ ```
904
+
905
+ | Option/Arg | Description |
906
+ |---|---|
907
+ | `ciphertext` | Integer ciphertext to decrypt, 1≤`ciphertext`<*modulus* [type: str] |
908
+
909
+ **Example:**
910
+
911
+ ```bash
912
+ $ poetry run transcrypto -p rsa-key.priv rsa decrypt 6354905961171348600
913
+ 999
914
+ ```
915
+
916
+ #### `rsa sign`
917
+
918
+ Sign integer `message` with private key.
919
+
920
+ ```bash
921
+ poetry run transcrypto rsa sign [-h] message
922
+ ```
923
+
924
+ | Option/Arg | Description |
925
+ |---|---|
926
+ | `message` | Integer message to sign, 1≤`message`<*modulus* [type: str] |
927
+
928
+ **Example:**
929
+
930
+ ```bash
931
+ $ poetry run transcrypto -p rsa-key.priv rsa sign 999
932
+ 7632909108672871784
933
+ ```
934
+
935
+ #### `rsa verify`
936
+
937
+ Verify integer `signature` for integer `message` with public key.
938
+
939
+ ```bash
940
+ poetry run transcrypto rsa verify [-h] message signature
941
+ ```
942
+
943
+ | Option/Arg | Description |
944
+ |---|---|
945
+ | `message` | Integer message that was signed earlier, 1≤`message`<*modulus* [type: str] |
946
+ | `signature` | Integer putative signature for `message`, 1≤`signature`<*modulus* [type: str] |
947
+
948
+ **Example:**
949
+
950
+ ```bash
951
+ $ poetry run transcrypto -p rsa-key.pub rsa verify 999 7632909108672871784
952
+ RSA signature: OK
953
+ $ poetry run transcrypto -p rsa-key.pub rsa verify 999 7632909108672871785
954
+ RSA signature: INVALID
955
+ ```
956
+
957
+ ---
958
+
959
+ ### `elgamal`
960
+
961
+ Raw El-Gamal asymmetric cryptography over *integers* (BEWARE: no ECIES-style KEM/DEM padding or validation). These are pedagogical/raw primitives; do not use for new protocols. No measures are taken here to prevent timing attacks. All methods require file key(s) as `-p`/`--key-path` (see provided examples).
962
+
963
+ ```bash
964
+ poetry run transcrypto elgamal [-h]
965
+ {shared,new,encrypt,decrypt,sign,verify} ...
966
+ ```
967
+
968
+ #### `elgamal shared`
969
+
970
+ Generate a shared El-Gamal key with `bits` prime modulus size, which is the first step in key generation. The shared key can safely be used by any number of users to generate their private/public key pairs (with the `new` command). The shared keys are "public". Requires `-p`/`--key-path` to set the basename for output files.
971
+
972
+ ```bash
973
+ poetry run transcrypto elgamal shared [-h] [--bits BITS]
974
+ ```
975
+
976
+ | Option/Arg | Description |
977
+ |---|---|
978
+ | `--bits` | Prime modulus (`p`) size in bits; the default is a safe size [type: int (default: 3332)] |
979
+
980
+ **Example:**
981
+
982
+ ```bash
983
+ $ poetry run transcrypto -p eg-key elgamal shared --bits 64 # NEVER use such a small key: example only!
984
+ El-Gamal shared key saved to 'eg-key.shared'
985
+ ```
986
+
987
+ #### `elgamal new`
988
+
989
+ Generate an individual El-Gamal private/public key pair from a shared key.
990
+
991
+ ```bash
992
+ poetry run transcrypto elgamal new [-h]
993
+ ```
994
+
995
+ **Example:**
996
+
997
+ ```bash
998
+ $ poetry run transcrypto -p eg-key elgamal new
999
+ El-Gamal private/public keys saved to 'eg-key.priv/.pub'
1000
+ ```
1001
+
1002
+ #### `elgamal encrypt`
1003
+
1004
+ Encrypt integer `message` with public key.
1005
+
1006
+ ```bash
1007
+ poetry run transcrypto elgamal encrypt [-h] message
1008
+ ```
1009
+
1010
+ | Option/Arg | Description |
1011
+ |---|---|
1012
+ | `message` | Integer message to encrypt, 1≤`message`<*modulus* [type: str] |
1013
+
1014
+ **Example:**
1015
+
1016
+ ```bash
1017
+ $ poetry run transcrypto -p eg-key.pub elgamal encrypt 999
1018
+ 2948854810728206041:15945988196340032688
1019
+ ```
1020
+
1021
+ #### `elgamal decrypt`
1022
+
1023
+ Decrypt integer `ciphertext` with private key.
1024
+
1025
+ ```bash
1026
+ poetry run transcrypto elgamal decrypt [-h] ciphertext
1027
+ ```
1028
+
1029
+ | Option/Arg | Description |
1030
+ |---|---|
1031
+ | `ciphertext` | Integer ciphertext to decrypt; expects `c1:c2` format with 2 integers, 2≤`c1`,`c2`<*modulus* [type: str] |
1032
+
1033
+ **Example:**
1034
+
1035
+ ```bash
1036
+ $ poetry run transcrypto -p eg-key.priv elgamal decrypt 2948854810728206041:15945988196340032688
1037
+ 999
1038
+ ```
1039
+
1040
+ #### `elgamal sign`
1041
+
1042
+ Sign integer message with private key. Output will 2 integers in a `s1:s2` format.
1043
+
1044
+ ```bash
1045
+ poetry run transcrypto elgamal sign [-h] message
1046
+ ```
1047
+
1048
+ | Option/Arg | Description |
1049
+ |---|---|
1050
+ | `message` | Integer message to sign, 1≤`message`<*modulus* [type: str] |
1051
+
1052
+ **Example:**
1053
+
1054
+ ```bash
1055
+ $ poetry run transcrypto -p eg-key.priv elgamal sign 999
1056
+ 4674885853217269088:14532144906178302633
1057
+ ```
1058
+
1059
+ #### `elgamal verify`
1060
+
1061
+ Verify integer `signature` for integer `message` with public key.
1062
+
1063
+ ```bash
1064
+ poetry run transcrypto elgamal verify [-h] message signature
1065
+ ```
1066
+
1067
+ | Option/Arg | Description |
1068
+ |---|---|
1069
+ | `message` | Integer message that was signed earlier, 1≤`message`<*modulus* [type: str] |
1070
+ | `signature` | Integer putative signature for `message`; expects `s1:s2` format with 2 integers, 2≤`s1`,`s2`<*modulus* [type: str] |
1071
+
1072
+ **Example:**
1073
+
1074
+ ```bash
1075
+ $ poetry run transcrypto -p eg-key.pub elgamal verify 999 4674885853217269088:14532144906178302633
1076
+ El-Gamal signature: OK
1077
+ $ poetry run transcrypto -p eg-key.pub elgamal verify 999 4674885853217269088:14532144906178302632
1078
+ El-Gamal signature: INVALID
1079
+ ```
1080
+
1081
+ ---
1082
+
1083
+ ### `dsa`
1084
+
1085
+ Raw DSA (Digital Signature Algorithm) asymmetric signing over *integers* (BEWARE: no ECDSA/EdDSA padding or validation). These are pedagogical/raw primitives; do not use for new protocols. No measures are taken here to prevent timing attacks. All methods require file key(s) as `-p`/`--key-path` (see provided examples).
1086
+
1087
+ ```bash
1088
+ poetry run transcrypto dsa [-h] {shared,new,sign,verify} ...
1089
+ ```
1090
+
1091
+ #### `dsa shared`
1092
+
1093
+ Generate a shared DSA key with `p-bits`/`q-bits` prime modulus sizes, which is the first step in key generation. `q-bits` should be larger than the secrets that will be protected and `p-bits` should be much larger than `q-bits` (e.g. 3584/256). The shared key can safely be used by any number of users to generate their private/public key pairs (with the `new` command). The shared keys are "public". Requires `-p`/`--key-path` to set the basename for output files.
1094
+
1095
+ ```bash
1096
+ poetry run transcrypto dsa shared [-h] [--p-bits P_BITS]
1097
+ [--q-bits Q_BITS]
1098
+ ```
1099
+
1100
+ | Option/Arg | Description |
1101
+ |---|---|
1102
+ | `--p-bits` | Prime modulus (`p`) size in bits; the default is a safe size [type: int (default: 3584)] |
1103
+ | `--q-bits` | Prime modulus (`q`) size in bits; the default is a safe size ***IFF*** you are protecting symmetric keys or regular hashes [type: int (default: 256)] |
1104
+
1105
+ **Example:**
1106
+
1107
+ ```bash
1108
+ $ poetry run transcrypto -p dsa-key dsa shared --p-bits 128 --q-bits 32 # NEVER use such a small key: example only!
1109
+ DSA shared key saved to 'dsa-key.shared'
1110
+ ```
1111
+
1112
+ #### `dsa new`
1113
+
1114
+ Generate an individual DSA private/public key pair from a shared key.
1115
+
1116
+ ```bash
1117
+ poetry run transcrypto dsa new [-h]
1118
+ ```
1119
+
1120
+ **Example:**
1121
+
1122
+ ```bash
1123
+ $ poetry run transcrypto -p dsa-key dsa new
1124
+ DSA private/public keys saved to 'dsa-key.priv/.pub'
1125
+ ```
1126
+
1127
+ #### `dsa sign`
1128
+
1129
+ Sign integer message with private key. Output will 2 integers in a `s1:s2` format.
1130
+
1131
+ ```bash
1132
+ poetry run transcrypto dsa sign [-h] message
1133
+ ```
1134
+
1135
+ | Option/Arg | Description |
1136
+ |---|---|
1137
+ | `message` | Integer message to sign, 1≤`message`<`q` [type: str] |
1138
+
1139
+ **Example:**
1140
+
1141
+ ```bash
1142
+ $ poetry run transcrypto -p dsa-key.priv dsa sign 999
1143
+ 2395961484:3435572290
1144
+ ```
1145
+
1146
+ #### `dsa verify`
1147
+
1148
+ Verify integer `signature` for integer `message` with public key.
1149
+
1150
+ ```bash
1151
+ poetry run transcrypto dsa verify [-h] message signature
1152
+ ```
1153
+
1154
+ | Option/Arg | Description |
1155
+ |---|---|
1156
+ | `message` | Integer message that was signed earlier, 1≤`message`<`q` [type: str] |
1157
+ | `signature` | Integer putative signature for `message`; expects `s1:s2` format with 2 integers, 2≤`s1`,`s2`<`q` [type: str] |
1158
+
1159
+ **Example:**
1160
+
1161
+ ```bash
1162
+ $ poetry run transcrypto -p dsa-key.pub dsa verify 999 2395961484:3435572290
1163
+ DSA signature: OK
1164
+ $ poetry run transcrypto -p dsa-key.pub dsa verify 999 2395961484:3435572291
1165
+ DSA signature: INVALID
1166
+ ```
1167
+
1168
+ ---
1169
+
1170
+ ### `bid`
1171
+
1172
+ Bidding on a `secret` so that you can cryptographically convince a neutral party that the `secret` that was committed to previously was not changed. All methods require file key(s) as `-p`/`--key-path` (see provided examples).
1173
+
1174
+ ```bash
1175
+ poetry run transcrypto bid [-h] {new,verify} ...
1176
+ ```
1177
+
1178
+ #### `bid new`
1179
+
1180
+ Generate the bid files for `secret`. Requires `-p`/`--key-path` to set the basename for output files.
1181
+
1182
+ ```bash
1183
+ poetry run transcrypto bid new [-h] secret
1184
+ ```
1185
+
1186
+ | Option/Arg | Description |
1187
+ |---|---|
1188
+ | `secret` | Input data to bid to, the protected "secret" [type: str] |
1189
+
1190
+ **Example:**
1191
+
1192
+ ```bash
1193
+ $ poetry run transcrypto --bin -p my-bid bid new "tomorrow it will rain"
1194
+ Bid private/public commitments saved to 'my-bid.priv/.pub'
1195
+ ```
1196
+
1197
+ #### `bid verify`
1198
+
1199
+ Verify the bid files for correctness and reveal the `secret`. Requires `-p`/`--key-path` to set the basename for output files.
1200
+
1201
+ ```bash
1202
+ poetry run transcrypto bid verify [-h]
1203
+ ```
1204
+
1205
+ **Example:**
1206
+
1207
+ ```bash
1208
+ $ poetry run transcrypto --out-bin -p my-bid bid verify
1209
+ Bid commitment: OK
1210
+ Bid secret:
1211
+ tomorrow it will rain
1212
+ ```
1213
+
1214
+ ---
1215
+
1216
+ ### `sss`
1217
+
1218
+ Raw SSS (Shamir Shared Secret) secret sharing crypto scheme over *integers* (BEWARE: no modern message wrapping, padding or validation). These are pedagogical/raw primitives; do not use for new protocols. No measures are taken here to prevent timing attacks. All methods require file key(s) as `-p`/`--key-path` (see provided examples).
1219
+
1220
+ ```bash
1221
+ poetry run transcrypto sss [-h] {new,shares,recover,verify} ...
1222
+ ```
1223
+
1224
+ #### `sss new`
1225
+
1226
+ Generate the private keys with `bits` prime modulus size and so that at least a `minimum` number of shares are needed to recover the secret. This key will be used to generate the shares later (with the `shares` command). Requires `-p`/`--key-path` to set the basename for output files.
1227
+
1228
+ ```bash
1229
+ poetry run transcrypto sss new [-h] [--bits BITS] minimum
1230
+ ```
1231
+
1232
+ | Option/Arg | Description |
1233
+ |---|---|
1234
+ | `minimum` | Minimum number of shares required to recover secret, ≥ 2 [type: int] |
1235
+ | `--bits` | Prime modulus (`p`) size in bits; the default is a safe size ***IFF*** you are protecting symmetric keys; the number of bits should be comfortably larger than the size of the secret you want to protect with this scheme [type: int (default: 1024)] |
1236
+
1237
+ **Example:**
1238
+
1239
+ ```bash
1240
+ $ poetry run transcrypto -p sss-key sss new 3 --bits 64 # NEVER use such a small key: example only!
1241
+ SSS private/public keys saved to 'sss-key.priv/.pub'
1242
+ ```
1243
+
1244
+ #### `sss shares`
1245
+
1246
+ Issue `count` private shares for an integer `secret`.
1247
+
1248
+ ```bash
1249
+ poetry run transcrypto sss shares [-h] secret count
1250
+ ```
1251
+
1252
+ | Option/Arg | Description |
1253
+ |---|---|
1254
+ | `secret` | Integer secret to be protected, 1≤`secret`<*modulus* [type: str] |
1255
+ | `count` | How many shares to produce; must be ≥ `minimum` used in `new` command or else the `secret` would become unrecoverable [type: int] |
1256
+
1257
+ **Example:**
1258
+
1259
+ ```bash
1260
+ $ poetry run transcrypto -p sss-key sss shares 999 5
1261
+ SSS 5 individual (private) shares saved to 'sss-key.share.1…5'
1262
+ $ rm sss-key.share.2 sss-key.share.4 # this is to simulate only having shares 1,3,5
1263
+ ```
1264
+
1265
+ #### `sss recover`
1266
+
1267
+ Recover secret from shares; will use any available shares that were found.
1268
+
1269
+ ```bash
1270
+ poetry run transcrypto sss recover [-h]
1271
+ ```
1272
+
1273
+ **Example:**
1274
+
1275
+ ```bash
1276
+ $ poetry run transcrypto -p sss-key sss recover
1277
+ Loaded SSS share: 'sss-key.share.3'
1278
+ Loaded SSS share: 'sss-key.share.5'
1279
+ Loaded SSS share: 'sss-key.share.1' # using only 3 shares: number 2/4 are missing
1280
+ Secret:
1281
+ 999
1282
+ ```
1283
+
1284
+ #### `sss verify`
1285
+
1286
+ Verify shares against a secret (private params).
1287
+
1288
+ ```bash
1289
+ poetry run transcrypto sss verify [-h] secret
1290
+ ```
1291
+
1292
+ | Option/Arg | Description |
1293
+ |---|---|
1294
+ | `secret` | Integer secret used to generate the shares, 1≤`secret`<*modulus* [type: str] |
1295
+
1296
+ **Example:**
1297
+
1298
+ ```bash
1299
+ $ poetry run transcrypto -p sss-key sss verify 999
1300
+ SSS share 'sss-key.share.3' verification: OK
1301
+ SSS share 'sss-key.share.5' verification: OK
1302
+ SSS share 'sss-key.share.1' verification: OK
1303
+ $ poetry run transcrypto -p sss-key sss verify 998
1304
+ SSS share 'sss-key.share.3' verification: INVALID
1305
+ SSS share 'sss-key.share.5' verification: INVALID
1306
+ SSS share 'sss-key.share.1' verification: INVALID
1307
+ ```
1308
+
1309
+ ---
1310
+
1311
+ ### `doc`
1312
+
1313
+ Documentation utilities. (Not for regular use: these are developer utils.)
1314
+
1315
+ ```bash
1316
+ poetry run transcrypto doc [-h] {md} ...
1317
+ ```
1318
+
1319
+ #### `doc md`
1320
+
1321
+ Emit Markdown docs for the CLI (see README.md section "Creating a New Version").
1322
+
1323
+ ```bash
1324
+ poetry run transcrypto doc md [-h]
1325
+ ```
1326
+
1327
+ **Example:**
1328
+
1329
+ ```bash
1330
+ $ poetry run transcrypto doc md > CLI.md
1331
+ $ ./tools/inject_md_includes.py
1332
+ inject: README.md updated with included content
1333
+ ```
1334
+ <!-- INCLUDE:CLI.md END -->
1335
+ <!-- (auto-generated; do not edit between START/END) -->
1336
+
1337
+ <!-- cspell:enable -->
1338
+
1339
+ ### Base Library
1340
+
1341
+ #### Humanized Sizes (IEC binary)
1342
+
1343
+ ```py
1344
+ from transcrypto import utils
1345
+
1346
+ utils.HumanizedBytes(512) # '512 B'
1347
+ utils.HumanizedBytes(2048) # '2.00 KiB'
1348
+ utils.HumanizedBytes(5 * 1024**3) # '5.00 GiB'
1349
+ ```
1350
+
1351
+ Converts raw byte counts to binary-prefixed strings (`B`, `KiB`, `MiB`, `GiB`, `TiB`, `PiB`, `EiB`). Values under 1024 bytes are returned as integers with `B`; larger values use two decimals.
1352
+
1353
+ - standard: 1 KiB = 1024 B, 1 MiB = 1024 KiB, …
1354
+ - errors: negative inputs raise `InputError`
1355
+
1356
+ #### Humanized Decimal Quantities (SI)
1357
+
1358
+ ```py
1359
+ # Base (unitless)
1360
+ utils.HumanizedDecimal(950) # '950'
1361
+ utils.HumanizedDecimal(1500) # '1.50 k'
1362
+
1363
+ # With a unit (trimmed and attached)
1364
+ utils.HumanizedDecimal(1500, ' Hz ') # '1.50 kHz'
1365
+ utils.HumanizedDecimal(0.123456, 'V') # '0.1235 V'
1366
+
1367
+ # Large magnitudes
1368
+ utils.HumanizedDecimal(3_200_000) # '3.20 M'
1369
+ utils.HumanizedDecimal(7.2e12, 'B/s') # '7.20 TB/s'
1370
+ ```
1371
+
1372
+ Scales by powers of 1000 using SI prefixes (`k`, `M`, `G`, `T`, `P`, `E`). For values `<1000`, integers are shown as-is; small floats show four decimals. For scaled values, two decimals are used and the unit (if provided) is attached without a space (e.g., `kHz`).
1373
+
1374
+ - unit handling: `unit` is stripped; `<1000` values include a space before the unit (`'950 Hz'`)
1375
+ - errors: negative or non-finite inputs raise `InputError`
1376
+
1377
+ #### Humanized Durations
1378
+
1379
+ ```py
1380
+ utils.HumanizedSeconds(0) # '0.00 s'
1381
+ utils.HumanizedSeconds(0.000004) # '4.000 µs'
1382
+ utils.HumanizedSeconds(0.25) # '250.000 ms'
1383
+ utils.HumanizedSeconds(42) # '42.00 s'
1384
+ utils.HumanizedSeconds(3661) # '1.02 h'
1385
+ utils.HumanizedSeconds(172800) # '2.00 d'
1386
+ ```
1387
+
1388
+ Chooses an appropriate time unit based on magnitude and formats with fixed precision:
1389
+
1390
+ - `< 1 ms`: microseconds with three decimals (`µs`)
1391
+ - `< 1 s`: milliseconds with three decimals (`ms`)
1392
+ - `< 60 s`: seconds with two decimals (`s`)
1393
+ - `< 60 min`: minutes with two decimals (`min`)
1394
+ - `< 24 h`: hours with two decimals (`h`)
1395
+ - `≥ 24 h`: days with two decimals (`d`)
1396
+ - special case: `0 → '0.00 s'`
1397
+ - errors: negative or non-finite inputs raise `InputError`
1398
+
1399
+ #### Cryptographically Secure Randomness
1400
+
1401
+ These helpers live in `base` and wrap Python’s `secrets` with additional checks and guarantees for crypto use-cases.
1402
+
1403
+ ```py
1404
+ from transcrypto import base
1405
+ ```
1406
+
1407
+ ##### Fixed-size random integers
1408
+
1409
+ ```py
1410
+ # Generate a 256-bit integer (first bit always set)
1411
+ r = base.RandBits(256)
1412
+ assert r.bit_length() == 256
1413
+ ```
1414
+
1415
+ Produces a crypto-secure random integer with exactly `n_bits` bits (`≥ 8`). The most significant bit is guaranteed to be `1`, so entropy is \~`n_bits−1` — negligible for large crypto sizes.
1416
+
1417
+ - errors: `n_bits < 8` → `InputError`
1418
+
1419
+ ##### Uniform random integers in a range
1420
+
1421
+ ```py
1422
+ # Uniform between [10, 20] inclusive
1423
+ n = base.RandInt(10, 20)
1424
+ assert 10 <= n <= 20
1425
+ ```
1426
+
1427
+ Returns a crypto-secure integer uniformly distributed over the closed interval `[min_int, max_int]`.
1428
+
1429
+ - constraints: `min_int ≥ 0` and `< max_int`
1430
+ - errors: invalid bounds → `InputError`
1431
+
1432
+ ##### In-place secure shuffle
1433
+
1434
+ ```py
1435
+ deck = list(range(10))
1436
+ base.RandShuffle(deck)
1437
+ print(deck) # securely shuffled order
1438
+ ```
1439
+
1440
+ Performs an in-place Fisher–Yates shuffle using `secrets.randbelow`. Suitable for sensitive data ordering.
1441
+
1442
+ - constraints: sequence length ≥ 2
1443
+ - errors: shorter sequences → `InputError`
1444
+
1445
+ ##### Random byte strings
1446
+
1447
+ ```py
1448
+ # 32 random bytes
1449
+ b = base.RandBytes(32)
1450
+ assert len(b) == 32
1451
+ ```
1452
+
1453
+ Generates `n_bytes` of high-quality crypto-secure random data.
1454
+
1455
+ - constraints: `n_bytes ≥ 1`
1456
+ - errors: smaller values → `InputError`
1457
+
1458
+ #### Computing the Greatest Common Divisor
1459
+
1460
+ ```py
1461
+ >>> from transcrypto import base
1462
+ >>> base.GCD(462, 1071)
1463
+ 21
1464
+ >>> base.GCD(0, 17)
1465
+ 17
1466
+ ```
1467
+
1468
+ The function is `O(log(min(a, b)))` and handles arbitrarily large integers. To find Bézout coefficients `(x, y)` such that `ax + by = gcd(a, b)` do:
1469
+
1470
+ ```py
1471
+ >>> base.ExtendedGCD(462, 1071)
1472
+ (21, -2, 1)
1473
+ >>> 462 * -2 + 1071 * 1
1474
+ 21
1475
+ ```
1476
+
1477
+ Use-cases:
1478
+
1479
+ - modular inverses: `inv = x % m` when `gcd(a, m) == 1`
1480
+ - solving linear Diophantine equations
1481
+ - RSA / ECC key generation internals
1482
+
1483
+ #### Cryptographic Hashing
1484
+
1485
+ Simple, fixed-output-size wrappers over Python’s `hashlib` for common digest operations, plus file hashing.
1486
+
1487
+ ```py
1488
+ from transcrypto import base
1489
+ ```
1490
+
1491
+ ##### SHA-256 hashing
1492
+
1493
+ ```py
1494
+ h = base.Hash256(b'hello world')
1495
+ assert len(h) == 32 # bytes
1496
+ print(h.hex()) # 64 hex chars
1497
+ ```
1498
+
1499
+ Computes the SHA-256 digest of a byte string, returning exactly 32 bytes (256 bits). Suitable for fingerprints, commitments, or internal crypto primitives.
1500
+
1501
+ ##### SHA-512 hashing
1502
+
1503
+ ```py
1504
+ h = base.Hash512(b'hello world')
1505
+ assert len(h) == 64 # bytes
1506
+ print(h.hex()) # 128 hex chars
1507
+ ```
1508
+
1509
+ Computes the SHA-512 digest of a byte string, returning exactly 64 bytes (512 bits). Higher collision resistance and larger output space than SHA-256.
1510
+
1511
+ ##### File hashing
1512
+
1513
+ ```py
1514
+ # Default SHA-256
1515
+ fh = base.FileHash('/path/to/file')
1516
+ print(fh.hex())
1517
+
1518
+ # SHA-512
1519
+ fh2 = base.FileHash('/path/to/file', digest='sha512')
1520
+ ```
1521
+
1522
+ Hashes a file from disk in streaming mode. By default uses SHA-256; `digest='sha512'` switches to SHA-512.
1523
+
1524
+ - constraints:
1525
+ - `digest` must be `'sha256'` or `'sha512'`
1526
+ - `full_path` must exist
1527
+ - errors: invalid digest or missing file → `InputError`
1528
+
1529
+ #### Execution Timing
1530
+
1531
+ A flexible timing utility that works as a **context manager**, **decorator**, or **manual timer object**.
1532
+
1533
+ ```py
1534
+ from transcrypto import base
1535
+ import time
1536
+ ```
1537
+
1538
+ ##### Context manager
1539
+
1540
+ ```py
1541
+ with base.Timer('Block timing'):
1542
+ time.sleep(1.2)
1543
+ # → logs: "Block timing: 1.20 s" (default via logging.info)
1544
+ ```
1545
+
1546
+ Starts timing on entry, stops on exit, and reports elapsed time automatically.
1547
+
1548
+ ##### Decorator
1549
+
1550
+ ```py
1551
+ @base.Timer('Function timing')
1552
+ def slow_function():
1553
+ time.sleep(0.8)
1554
+
1555
+ slow_function()
1556
+ # → logs: "Function timing: 0.80 s"
1557
+ ```
1558
+
1559
+ Wraps a function so that each call is automatically timed.
1560
+
1561
+ ##### Manual use
1562
+
1563
+ ```py
1564
+ tm = base.Timer('Inline timing', emit_print=True)
1565
+ tm.Start()
1566
+ time.sleep(0.1)
1567
+ tm.Stop() # prints: "Inline timing: 0.10 s"
1568
+ ```
1569
+
1570
+ Manual control over `Start()` and `Stop()` for precise measurement of custom intervals.
1571
+
1572
+ ##### Key points
1573
+
1574
+ - **Label**: required, shown in output; empty labels raise `InputError`
1575
+ - **Output**:
1576
+ - `emit_log=True` → `logging.info()` (default)
1577
+ - `emit_print=True` → direct `print()`
1578
+ - Both can be enabled
1579
+ - **Format**: elapsed time is shown using `HumanizedSeconds()`
1580
+ - **Safety**:
1581
+ - Cannot start an already started timer
1582
+ - Cannot stop an unstarted or already stopped timer
1583
+ (raises `Error`)
1584
+
1585
+ #### Symmetric Encryption Interface
1586
+
1587
+ `SymmetricCrypto` is an abstract base class that defines the **byte-in / byte-out** contract for symmetric ciphers.
1588
+
1589
+ - **Metadata handling** — if the algorithm uses a `nonce` or `tag`, the implementation must handle it internally (e.g., append it to ciphertext).
1590
+ - **AEAD modes** — if supported, `associated_data` must be authenticated; otherwise, a non-`None` value should raise `InputError`.
1591
+
1592
+ ```py
1593
+ class MyAES(base.SymmetricCrypto):
1594
+ def Encrypt(self, plaintext: bytes, *, associated_data=None) -> bytes:
1595
+ ...
1596
+ def Decrypt(self, ciphertext: bytes, *, associated_data=None) -> bytes:
1597
+ ...
1598
+ ```
1599
+
1600
+ #### Serialization Pipeline
1601
+
1602
+ These helpers turn arbitrary Python objects into compressed and/or encrypted binary blobs, and back again — with detailed timing and size logging.
1603
+
1604
+ ```py
1605
+ from transcrypto import base
1606
+ ```
1607
+
1608
+ ##### Serialize
1609
+
1610
+ ```py
1611
+ data = {'x': 42, 'y': 'hello'}
1612
+
1613
+ # Basic serialization
1614
+ blob = base.Serialize(data)
1615
+
1616
+ # With compression and encryption
1617
+ blob = base.Serialize(
1618
+ data,
1619
+ compress=9, # compression level (-22..22, default=3)
1620
+ key=my_symmetric_key # must implement SymmetricCrypto
1621
+ )
1622
+
1623
+ # Save directly to file
1624
+ base.Serialize(data, file_path='/tmp/data.blob')
1625
+ ```
1626
+
1627
+ Serialization path:
1628
+
1629
+ ```text
1630
+ obj → pickle → (compress) → (encrypt) → (save)
1631
+ ```
1632
+
1633
+ At each stage:
1634
+
1635
+ - Data size is measured using `HumanizedBytes`
1636
+ - Duration is timed with `Timer`
1637
+ - Results are logged once at the end
1638
+
1639
+ Compression levels:
1640
+
1641
+ `compress` uses `zstandard`; see table below for speed/ratio trade-offs:
1642
+
1643
+ | Level | Speed | Compression ratio | Typical use case |
1644
+ | -------- | ------------| --------------------------------- | --------------------------------------- |
1645
+ | -5 to -1 | Fastest | Poor (better than no compression) | Real-time or very latency-sensitive |
1646
+ | 0…3 | Very fast | Good ratio | Default CLI choice, safe baseline |
1647
+ | 4…6 | Moderate | Better ratio | Good compromise for general persistence |
1648
+ | 7…10 | Slower | Marginally better ratio | Only if storage space is precious |
1649
+ | 11…15 | Much slower | Slight gains | Large archives, not for runtime use |
1650
+ | 16…22 | Very slow | Tiny gains | Archival-only, multi-GB datasets |
1651
+
1652
+ Errors: invalid compression level is clamped to range; other input errors raise `InputError`.
1653
+
1654
+ ##### DeSerialize
1655
+
1656
+ ```py
1657
+ # From in-memory blob
1658
+ obj = base.DeSerialize(data=blob)
1659
+
1660
+ # From file
1661
+ obj = base.DeSerialize(file_path='/tmp/data.blob')
1662
+
1663
+ # With decryption
1664
+ obj = base.DeSerialize(data=blob, key=my_symmetric_key)
1665
+ ```
1666
+
1667
+ Deserialization path:
1668
+
1669
+ ```text
1670
+ data/file → (decrypt) → (decompress if Zstd) → unpickle
1671
+ ```
1672
+
1673
+ - Compression is auto-detected via Zstandard magic numbers.
1674
+ - All steps are timed/logged like in `Serialize`.
1675
+
1676
+ **Constraints & errors**:
1677
+
1678
+ - Exactly one of `data` or `file_path` must be provided.
1679
+ - `file_path` must exist; `data` must be at least 4 bytes.
1680
+ - Wrong key or corrupted data can raise `CryptoError`.
1681
+
1682
+ #### Crypto Objects General Properties (`CryptoKey`)
1683
+
1684
+ Cryptographic objects all derive from the `CryptoKey` class and will all have some important characteristics:
1685
+
1686
+ - Will be safe to log and print, i.e., implement safe `__str__()` and `__repr__()` methods (in actuality `repr` will be exactly the same as `str`). The `__str__()` should always fully print the public parts of the object and obfuscate the private ones. This obfuscation allows for some debugging, if needed, but if the secrets are "too short" then it can be defeated by brute force. For usual crypto defaults the obfuscation is fine. The obfuscation is the fist 4 bytes of the SHA-512 for the value followed by an ellipsis (e.g. `c9626f16…`).
1687
+ - It will have a `_DebugDump()` method that **does print secrets** and can be used for **debugging only**.
1688
+ - Can be easily serialized to `bytes` by the `blob` property and to base-64 encoded `str` by the `encoded` property.
1689
+ - Can be serialized encrypted to `bytes` by the `Blob(key=[SymmetricCrypto])` method and to encrypted base-64 encoded `str` by the `Encoded(key=[SymmetricCrypto])` method.
1690
+ - Can be instantiated back as an object from `str` or `bytes` using the `Load(data, key=[SymmetricCrypto] | None)` method. The `Load()` will decide how to build the object and will work universally with all the serialization options discussed above.
1691
+
1692
+ Example:
1693
+
1694
+ <!-- cspell:disable -->
1695
+ ```py
1696
+ from transcrypto import base, rsa, aes
1697
+
1698
+ priv = rsa.RSAPrivateKey.New(512) # small key, but good for this example
1699
+ print(str(priv)) # safe, no secrets
1700
+ # ▶ RSAPrivateKey(RSAPublicKey(public_modulus=pQaoxy-QeXSds1k9WsGjJw==, encrypt_exp=AQAB), modulus_p=f18141aa…, modulus_q=67494eb9…, decrypt_exp=c96db24a…)
1701
+
1702
+ print(priv._DebugDump()) # UNSAFE: prints secrets
1703
+ # ▶ RSAPrivateKey(public_modulus=219357196311600536151291741191131996967, encrypt_exp=65537, modulus_p=13221374197986739361, modulus_q=16591104148992527047, decrypt_exp=37805202135275158391322585315542443073, remainder_p=9522084656682089473, remainder_q=8975656462800098363, q_inverse_p=11965562396596149292)
1704
+
1705
+ print(priv.blob)
1706
+ # ▶ b"(\xb5/\xfd \x98\xc1\x04\x00\x80\x04\x95\x8d\x00\x00\x00\x00\x00\x00\x00\x8c\x0ftranscrypto.rsa\x94\x8c\rRSAPrivateKey\x94\x93\x94)\x81\x94]\x94(\x8a\x11'\xa3\xc1Z=Y\xb3\x9dty\x90/\xc7\xa8\x06\xa5\x00J\x01\x00\x01\x00\x8a\t\xa1\xc4\x83\x81\xc8\xc1{\xb7\x00\x8a\t\xc7\x8a5\xf0Qq?\xe6\x00\x8a\x10A$&\x82!\x1cy\x89r\xef\xeb\xa7_\x04q\x1c\x8a\t\x01\xbc\xbb\x8a\x8b=%\x84\x00\x8a\x08;\x94#s\xff\xef\x8f|\x8a\t,\x9c\xe2z\x9a7\x0e\xa6\x00eb."
1707
+
1708
+ print(priv.encoded)
1709
+ # ▶ KLUv_WBwAIELAIAElWUBAAAAAAAAjA90cmFuc2NyeXB0by5yc2GUjA1SU0FQcml2YXRlS2V5lJOUKYGUXZQoikHf1EvsmZedAZve7TrLmobLAwuRIr_77TLG6G_0fsLGThERVJu075be8PLjUQYnLXcacZFQ5Fb1Iy1WtiE985euAEoBAAEAiiFR9ngiXMzkf41o5CRBY3h0D4DJVisDDhLmAWsiaHggzQCKIS_cmQ6MKXCtROtC7c_Mrsi9A-9NM8DksaHaRwvy6uTZAIpB4TVbsLxc41TEc19wIzpxbi9y5dW5gdfTkRQSSiz0ijmb8Xk3pyBfKAv8JbHp8Yv48gNZUfX67qq0J7yhJqeUoACKIbFb2kTNRzSqm3JRtjc2BPS-FnLFdadlFcV4-6IW7eqLAIogFZfzDN39gZLR9uTz4KHSTaqxWrJgP8-YYssjss6FlFKKIIItgCDv7ompNpY8gBs5bibN8XTsr-JOYSntDVT5Fe5vZWIu
1710
+
1711
+ key = aes.AESKey(key256=b'x' * 32)
1712
+ print(key)
1713
+ # ▶ AESKey(key256=86a86df7…)
1714
+
1715
+ encrypted = priv.Blob(key=key)
1716
+ print(priv == rsa.RSAPrivateKey.Load(encrypted, key=key))
1717
+ # ▶ True
1718
+ ```
1719
+ <!-- cspell:enable -->
1720
+
1721
+ #### AES-256 Symmetric Encryption
1722
+
1723
+ Implements AES-256 in **GCM mode** for authenticated encryption and decryption, plus an **ECB mode** helper for fixed-size block encoding.
1724
+ Also includes a high-iteration PBKDF2-based key derivation from static passwords.
1725
+
1726
+ ##### Key creation
1727
+
1728
+ ```py
1729
+ from transcrypto import aes
1730
+
1731
+ # From raw bytes (must be exactly 32 bytes)
1732
+ key = aes.AESKey(key256=b'\x00' * 32)
1733
+
1734
+ # From a static password (slow, high-iteration PBKDF2-SHA256)
1735
+ key = aes.AESKey.FromStaticPassword('correct horse battery staple')
1736
+ print(key.encoded) # URL-safe Base64
1737
+ ```
1738
+
1739
+ - **Length**: `key256` must be exactly 32 bytes
1740
+ - `FromStaticPassword()`:
1741
+ - Uses PBKDF2-HMAC-SHA256 with **fixed** salt and \~2 million iterations
1742
+ - Designed for **interactive** password entry, **not** for password databases
1743
+
1744
+ ##### AES-256 + GCM (default)
1745
+
1746
+ ```py
1747
+ data = b'secret message'
1748
+ aad = b'metadata'
1749
+
1750
+ # Encrypt (returns IV + ciphertext + tag)
1751
+ ct = key.Encrypt(data, associated_data=aad)
1752
+
1753
+ # Decrypt
1754
+ pt = key.Decrypt(ct, associated_data=aad)
1755
+ assert pt == data
1756
+ ```
1757
+
1758
+ - **Security**:
1759
+ - Random 128-bit IV (`iv`) per encryption
1760
+ - Authenticated tag (128-bit) ensures integrity
1761
+ - Optional `associated_data` is authenticated but not encrypted
1762
+ - **Errors**:
1763
+ - Tag mismatch or wrong key → `CryptoError`
1764
+
1765
+ ##### AES-256 + ECB (unsafe, fixed block only)
1766
+
1767
+ ```py
1768
+ # ECB mode is for 16-byte block encoding ONLY
1769
+ ecb = key.ECBEncoder()
1770
+
1771
+ block = b'16-byte string!!'
1772
+ ct_block = ecb.Encrypt(block)
1773
+ pt_block = ecb.Decrypt(ct_block)
1774
+ assert pt_block == block
1775
+
1776
+ # Hex helpers
1777
+ hex_ct = ecb.EncryptHex('00112233445566778899aabbccddeeff')
1778
+ ```
1779
+
1780
+ - **ECB mode**:
1781
+ - 16-byte plaintext ↔ 16-byte ciphertext
1782
+ - No padding, no IV, no integrity — **do not use for general encryption**
1783
+ - `associated_data` not supported
1784
+
1785
+ Key points:
1786
+
1787
+ - **GCM mode** is secure for general use; ECB mode is for special low-level operations
1788
+ - **Static password derivation** is intentionally slow to resist brute force
1789
+ - All sizes and parameters are validated with `InputError` on misuse
1790
+
1791
+ #### Fast Modular Arithmetic
1792
+
1793
+ ```py
1794
+ from transcrypto import modmath
1795
+
1796
+ m = 2**256 - 189 # a large prime modulus
1797
+
1798
+ # Inverse ──────────────────────────────
1799
+ x = 123456789
1800
+ x_inv = modmath.ModInv(x, m)
1801
+ assert (x * x_inv) % m == 1
1802
+
1803
+ # Division (x / y) mod m ──────────────
1804
+ y = 987654321
1805
+ z = modmath.ModDiv(x, y, m) # solves z·y ≡ x (mod m)
1806
+ assert (z * y) % m == x % m
1807
+
1808
+ # Exponentiation ──────────────────────
1809
+ exp = modmath.ModExp(3, 10**20, m) # ≈ log₂(y) time, handles huge exponents
1810
+ ```
1811
+
1812
+ #### Chinese Remainder Theorem (CRT) – Pair
1813
+
1814
+ ```py
1815
+ from transcrypto import modmath
1816
+
1817
+ # Solve:
1818
+ # x ≡ 2 (mod 3)
1819
+ # x ≡ 3 (mod 5)
1820
+ x = modmath.CRTPair(2, 3, 3, 5)
1821
+ print(x) # 8
1822
+ assert x % 3 == 2
1823
+ assert x % 5 == 3
1824
+ ```
1825
+
1826
+ Solves a system of two simultaneous congruences with **pairwise co-prime** moduli, returning the **least non-negative solution** `x` such that:
1827
+
1828
+ ```text
1829
+ x ≡ a1 (mod m1)
1830
+ x ≡ a2 (mod m2)
1831
+ 0 ≤ x < m1 * m2
1832
+ ```
1833
+
1834
+ - **Requirements**:
1835
+ - `m1 ≥ 2`, `m2 ≥ 2`, `m1 != m2`
1836
+ - `gcd(m1, m2) == 1` (co-prime)
1837
+ - **Errors**:
1838
+ - invalid modulus values → `InputError`
1839
+ - non co-prime moduli → `ModularDivideError`
1840
+
1841
+ This function is a 2-modulus variant; for multiple moduli, apply it iteratively or use a general CRT solver.
1842
+
1843
+ #### Modular Polynomials & Lagrange Interpolation
1844
+
1845
+ ```py
1846
+ # f(t) = 7t³ − 3t² + 2t + 5 (coefficients constant-term first)
1847
+ coefficients = [5, 2, -3, 7]
1848
+ print(modmath.ModPolynomial(11, coefficients, 97)) # → 19
1849
+
1850
+ # Given three points build the degree-≤2 polynomial and evaluate it.
1851
+ pts = {2: 4, 5: 3, 7: 1}
1852
+ print(modmath.ModLagrangeInterpolate(9, pts, 11)) # → 2
1853
+ ```
1854
+
1855
+ #### Primality testing & Prime generators, Mersenne primes
1856
+
1857
+ ```py
1858
+ modmath.IsPrime(2**127 - 1) # True (Mersenne prime)
1859
+ modmath.IsPrime(3825123056546413051) # False (strong pseudo-prime)
1860
+
1861
+ # Direct Miller–Rabin with custom witnesses
1862
+ modmath.MillerRabinIsPrime(961748941, witnesses={2,7,61})
1863
+
1864
+ # Infinite iterator of primes ≥ 10⁶
1865
+ for p in modmath.PrimeGenerator(1_000_000):
1866
+ print(p)
1867
+ if p > 1_000_100:
1868
+ break
1869
+
1870
+ # Secure random 384-bit prime (for RSA/ECC experiments)
1871
+ p384 = modmath.NBitRandomPrime(384)
1872
+
1873
+ for k, m_p, perfect in modmath.MersennePrimesGenerator(0):
1874
+ print(f'p = {k:>8} M = {m_p} perfect = {perfect}')
1875
+ if k > 10000: # stop after a few
1876
+ break
1877
+ ```
1878
+
1879
+ #### RSA (Rivest-Shamir-Adleman) Public Cryptography
1880
+
1881
+ <https://en.wikipedia.org/wiki/RSA_cryptosystem>
1882
+
1883
+ This implementation is raw RSA, no OAEP or PSS! It works on the actual integers. For real uses you should look for higher-level implementations.
1884
+
1885
+ By default and deliberate choice the *encryption exponent* will be either 7 or 65537, depending on the size of `phi=(p-1)*(q-1)`. If `phi` allows it the larger one will be chosen to avoid Coppersmith attacks.
1886
+
1887
+ ```py
1888
+ from transcrypto import rsa
1889
+
1890
+ # Generate a key pair
1891
+ priv = rsa.RSAPrivateKey.New(2048) # 2048-bit modulus
1892
+ pub = rsa.RSAPublicKey.Copy(priv) # public half
1893
+ print(priv.public_modulus.bit_length()) # 2048
1894
+
1895
+ # Encrypt & decrypt
1896
+ msg = 123456789 # (Zero is forbidden by design; smallest valid message is 1.)
1897
+ cipher = pub.Encrypt(msg)
1898
+ plain = priv.Decrypt(cipher)
1899
+ assert plain == msg
1900
+
1901
+ # Sign & verify
1902
+ signature = priv.Sign(msg)
1903
+ assert pub.VerifySignature(msg, signature)
1904
+
1905
+ # Blind signatures (obfuscation pair) - only works on raw RSA
1906
+ pair = rsa.RSAObfuscationPair.New(pub)
1907
+
1908
+ blind_msg = pair.ObfuscateMessage(msg) # what you send to signer
1909
+ blind_sig = priv.Sign(blind_msg) # signer’s output
1910
+
1911
+ sig = pair.RevealOriginalSignature(msg, blind_sig)
1912
+ assert pub.VerifySignature(msg, sig)
1913
+ ```
1914
+
1915
+ #### El-Gamal Public-Key Cryptography
1916
+
1917
+ [https://en.wikipedia.org/wiki/ElGamal\_encryption](https://en.wikipedia.org/wiki/ElGamal_encryption)
1918
+
1919
+ This is **raw El-Gamal** over a prime field — no padding, no hashing — and is **not** DSA.
1920
+ For real-world deployments, use a high-level library with authenticated encryption and proper encoding.
1921
+
1922
+ ##### Shared Public Key
1923
+
1924
+ ```py
1925
+ from transcrypto import elgamal
1926
+
1927
+ # ➊ Shared parameters (prime modulus, group base) for a group
1928
+ shared = elgamal.ElGamalSharedPublicKey.New(256)
1929
+ print(shared.prime_modulus)
1930
+ print(shared.group_base)
1931
+ ```
1932
+
1933
+ - `prime_modulus`: large prime `p ≥ 7`
1934
+ - `group_base`: integer `3 ≤ g < p`
1935
+ - Used to derive individual public/private keys.
1936
+
1937
+ ##### Public Key
1938
+
1939
+ ```py
1940
+ # ➋ Public key from private
1941
+ priv = elgamal.ElGamalPrivateKey.New(shared)
1942
+ pub = elgamal.ElGamalPublicKey.Copy(priv)
1943
+
1944
+ # Encryption
1945
+ msg = 42
1946
+ cipher = pub.Encrypt(msg)
1947
+ plain = priv.Decrypt(cipher)
1948
+ assert plain == msg
1949
+
1950
+ # Signature verify
1951
+ sig = priv.Sign(msg)
1952
+ assert pub.VerifySignature(msg, sig)
1953
+ ```
1954
+
1955
+ - `Encrypt(message)` → `(c1, c2)`, both in `[2, p-1]`
1956
+ - `VerifySignature(message, signature)` → `True` or `False`
1957
+ - `Copy()` extracts public portion from a private key
1958
+
1959
+ ##### Private Key
1960
+
1961
+ ```py
1962
+ # ➌ Private key generation
1963
+ priv = elgamal.ElGamalPrivateKey.New(shared)
1964
+
1965
+ # Decryption
1966
+ plain = priv.Decrypt(cipher)
1967
+
1968
+ # Signing
1969
+ sig = priv.Sign(msg)
1970
+ assert pub.VerifySignature(msg, sig)
1971
+ ```
1972
+
1973
+ - `decrypt_exp`: secret exponent `3 ≤ e < p`
1974
+ - `Decrypt((c1, c2))` recovers `m`
1975
+ - `Sign(m)` returns `(s1, s2)`; both satisfy the modulus constraints
1976
+
1977
+ Key points:
1978
+
1979
+ - **Security parameters**:
1980
+ - Recommended `prime_modulus` bit length ≥ 2048 for real security
1981
+ - Random values from `base.RandBits`
1982
+ - **Ephemeral keys**:
1983
+ - Fresh per encryption/signature
1984
+ - Must satisfy `gcd(k, p-1) == 1`
1985
+ - **Errors**:
1986
+ - Bad ranges → `InputError`
1987
+ - Invalid math relationships → `CryptoError`
1988
+ - **Group sharing**:
1989
+ - Multiple parties can share `(p, g)` but have different `(individual_base, decrypt_exp)`
1990
+
1991
+ #### DSA (Digital Signature Algorithm)
1992
+
1993
+ [https://en.wikipedia.org/wiki/Digital\_Signature\_Algorithm](https://en.wikipedia.org/wiki/Digital_Signature_Algorithm)
1994
+
1995
+ This is **raw DSA** over a prime field — **no hashing or padding**. You sign/verify **integers** modulo `q` (`prime_seed`). For real use, hash the message first (e.g., SHA-256) and then map to an integer `< q`.
1996
+
1997
+ ```py
1998
+ from transcrypto import dsa
1999
+
2000
+ # ➊ Shared parameters (p, q, g)
2001
+ shared = dsa.DSASharedPublicKey.New(p_bits=1024, q_bits=160)
2002
+ print(shared.prime_modulus) # p
2003
+ print(shared.prime_seed) # q (q | p-1)
2004
+ print(shared.group_base) # g
2005
+
2006
+ # ➋ Individual key pair
2007
+ priv = dsa.DSAPrivateKey.New(shared)
2008
+ pub = dsa.DSAPublicKey.Copy(priv)
2009
+
2010
+ # ➌ Sign & verify (message must be 1 ≤ m < q)
2011
+ msg = 123456789 % shared.prime_seed
2012
+ sig = priv.Sign(msg)
2013
+ assert pub.VerifySignature(msg, sig)
2014
+ ```
2015
+
2016
+ - ranges:
2017
+ - `1 ≤ message < q`
2018
+ - signatures: `(s1, s2)` with `2 ≤ s1, s2 < q`
2019
+ - errors:
2020
+ - invalid ranges → `InputError`
2021
+ - inconsistent parameters → `CryptoError`
2022
+
2023
+ ##### Security notes
2024
+
2025
+ - Choose **large** parameters (e.g., `p ≥ 2048 bits`, `q ≥ 224 bits`) for non-toy settings.
2026
+ - In practice, compute `m = int.from_bytes(Hash(message), 'big') % q` before calling `Sign(m)`.
2027
+
2028
+ ##### Advanced: custom primes generator
2029
+
2030
+ ```py
2031
+ # Generate primes (p, q) with q | (p-1); also returns m = (p-1)//q
2032
+ p, q, m = dsa.NBitRandomDSAPrimes(p_bits=1024, q_bits=160)
2033
+ assert (p - 1) % q == 0
2034
+ ```
2035
+
2036
+ Used internally by `DSASharedPublicKey.New()`.
2037
+ Search breadth and retry caps are bounded; repeated failures raise `CryptoError`.
2038
+
2039
+ #### Public Bidding
2040
+
2041
+ This is a way of bidding on some commitment (the `secret`) that can be cryptographically proved later to not have been changed. To do that the secret is combined with 2 nonces (random values, `n1` & `n2`) and a hash of it is taken (`H=SHA-512(n1||n2||secret)`). The hash `H` and one nonce `n1` are public and divulged. The other nonce `n2` and the `secret` are kept private and will be used to show `secret` was not changed since the beginning of the process. The nonces guarantee the `secret` cannot be brute-forced or changed after-the-fact. The whole process is as strong as SHA-512 collisions.
2042
+
2043
+ ```py
2044
+ from transcrypto import base
2045
+
2046
+ # Generate the private and public bids
2047
+ bid_priv = base.PrivateBid.New(secret) # this one you keep private
2048
+ bid_pub = base.PublicBid.Copy(bid_priv) # this one you publish
2049
+
2050
+ # Checking that a bid is genuine requires the public bid and knowing the nonce and the secret:
2051
+ print(bid_pub.VerifyBid(private_key, secret_bid)) # these come from a divulged private bid
2052
+ # of course, you want to also make sure the provided private data matches your version of it, e.g.:
2053
+ bid_pub_expected = base.PublicBid.Copy(bid_priv)
2054
+ print(bid_pub == bid_pub_expected)
2055
+ ```
2056
+
2057
+ #### SSS (Shamir Shared Secret)
2058
+
2059
+ <https://en.wikipedia.org/wiki/Shamir's_secret_sharing>
2060
+
2061
+ This is the information-theoretic SSS but with no authentication or binding between share and secret. Malicious share injection is possible! Add MAC or digital signature in hostile settings. Use at least 128-bit modulus for non-toy deployments.
2062
+
2063
+ ```py
2064
+ from transcrypto import sss
2065
+
2066
+ # ➊ Generate parameters: at least 3 of 5 shares needed,
2067
+ # coefficients & modulus are 128-bit primes
2068
+ priv = sss.ShamirSharedSecretPrivate.New(minimum_shares=3, bit_length=128)
2069
+ pub = sss.ShamirSharedSecretPublic.Copy(priv) # what you publish
2070
+
2071
+ print(f'threshold : {pub.minimum}')
2072
+ print(f'prime mod : {pub.modulus}')
2073
+ print(f'poly coefficients: {priv.polynomial}') # keep these private!
2074
+
2075
+ # Issuing shares
2076
+
2077
+ secret = 0xC0FFEE
2078
+ # Generate an unlimited stream; here we take 5
2079
+ five_shares = list(priv.Shares(secret, max_shares=5))
2080
+ for sh in five_shares:
2081
+ print(f'share {sh.share_key} → {sh.share_value}')
2082
+ ```
2083
+
2084
+ A single share object looks like `sss.ShamirSharePrivate(minimum=3, modulus=..., share_key=42, share_value=123456789)`.
2085
+
2086
+ ```py
2087
+ # Re-constructing the secret
2088
+
2089
+ subset = five_shares[:3] # any 3 distinct shares
2090
+ recovered = pub.RecoverSecret(subset)
2091
+ assert recovered == secret
2092
+ ```
2093
+
2094
+ If you supply fewer than minimum shares you get a `CryptoError`, unless you explicitly override:
2095
+
2096
+ ```py
2097
+ try:
2098
+ pub.RecoverSecret(five_shares[:2]) # raises
2099
+ except Exception as e:
2100
+ print(e) # "unrecoverable secret …"
2101
+
2102
+ # Force the interpolation even with 2 points (gives a wrong secret, of course)
2103
+ print(pub.RecoverSecret(five_shares[:2], force_recover=True))
2104
+
2105
+ # Checking that a share is genuine
2106
+
2107
+ share = five_shares[0]
2108
+ ok = priv.VerifyShare(secret, share) # ▶ True
2109
+ tampered = sss.ShamirSharePrivate(
2110
+ minimum=share.minimum,
2111
+ modulus=share.modulus,
2112
+ share_key=share.share_key,
2113
+ share_value=(share.share_value + 1) % share.modulus)
2114
+ print(priv.VerifyShare(secret, tampered)) # ▶ False
2115
+ ```
2116
+
2117
+ ## Appendix: Development Instructions
2118
+
2119
+ ### Setup
2120
+
2121
+ If you want to develop for this project, first install python 3.13 and [Poetry](https://python-poetry.org/docs/cli/), but to get the versions you will need, we suggest you do it like this (*Linux*):
2122
+
2123
+ ```sh
2124
+ sudo apt-get update
2125
+ sudo apt-get upgrade
2126
+ sudo apt-get install git python3 python3-pip pipx python3-dev python3-venv build-essential software-properties-common
2127
+
2128
+ sudo add-apt-repository ppa:deadsnakes/ppa # install arbitrary python version
2129
+ sudo apt-get update
2130
+ sudo apt-get install python3.13
2131
+
2132
+ sudo apt-get remove python3-poetry
2133
+ python3.13 -m pipx ensurepath
2134
+ # re-open terminal
2135
+ pipx install poetry
2136
+ poetry --version # should be >=2.1
2137
+
2138
+ poetry config virtualenvs.in-project true # creates .venv inside project directory
2139
+ poetry config pypi-token.pypi <TOKEN> # add your personal PyPI project token, if any
2140
+ ```
2141
+
2142
+ or this (*Mac*):
2143
+
2144
+ ```sh
2145
+ brew update
2146
+ brew upgrade
2147
+ brew cleanup -s
2148
+
2149
+ brew install git python@3.13 # install arbitrary python version
2150
+
2151
+ brew uninstall poetry
2152
+ python3.13 -m pip install --user pipx
2153
+ python3.13 -m pipx ensurepath
2154
+ # re-open terminal
2155
+ pipx install poetry
2156
+ poetry --version # should be >=2.1
2157
+
2158
+ poetry config virtualenvs.in-project true # creates .venv inside project directory
2159
+ poetry config pypi-token.pypi <TOKEN> # add your personal PyPI project token, if any
2160
+ ```
2161
+
2162
+ Now install the project:
2163
+
2164
+ ```sh
2165
+ git clone https://github.com/balparda/transcrypto.git transcrypto
2166
+ cd transcrypto
2167
+
2168
+ poetry env use python3.13 # creates the venv
2169
+ poetry install --sync # HONOR the project's poetry.lock file, uninstalls stray packages
2170
+ poetry env info # no-op: just to check
2171
+
2172
+ poetry run pytest -vvv
2173
+ # or any command as:
2174
+ poetry run <any-command>
2175
+ ```
2176
+
2177
+ To activate like a regular environment do:
2178
+
2179
+ ```sh
2180
+ poetry env activate
2181
+ # will print activation command which you next execute, or you can do:
2182
+ source .env/bin/activate # if .env is local to the project
2183
+ source "$(poetry env info --path)/bin/activate" # for other paths
2184
+
2185
+ pytest # or other commands
2186
+
2187
+ deactivate
2188
+ ```
2189
+
2190
+ ### Updating Dependencies
2191
+
2192
+ To update `poetry.lock` file to more current versions do `poetry update`, it will ignore the current lock, update, and rewrite the `poetry.lock` file.
2193
+
2194
+ To add a new dependency you should do:
2195
+
2196
+ ```sh
2197
+ poetry add "pkg>=1.2.3" # regenerates lock, updates env (adds dep to prod code)
2198
+ poetry add -G dev "pkg>=1.2.3" # adds dep to dev code ("group" dev)
2199
+ # also remember: "pkg@^1.2.3" = latest 1.* ; "pkg@~1.2.3" = latest 1.2.* ; "pkg@1.2.3" exact
2200
+ ```
2201
+
2202
+ If you manually added a dependency to `pyproject.toml` you should ***very carefully*** recreate the environment and files:
2203
+
2204
+ ```sh
2205
+ rm -rf .venv .poetry poetry.lock
2206
+ poetry env use python3.13
2207
+ poetry install
2208
+ ```
2209
+
2210
+ Remember to check your diffs before submitting (especially `poetry.lock`) to avoid surprises!
2211
+
2212
+ When dependencies change, always regenerate `requirements.txt` by running:
2213
+
2214
+ ```sh
2215
+ poetry export --format requirements.txt --without-hashes --output requirements.txt
2216
+ ```
2217
+
2218
+ ### Creating a New Version
2219
+
2220
+ ```sh
2221
+ # bump the version!
2222
+ poetry version minor # updates 1.6 to 1.7, for example
2223
+ # or:
2224
+ poetry version patch # updates 1.6 to 1.6.1
2225
+ # or:
2226
+ poetry version <version-number>
2227
+ # (also updates `pyproject.toml` and `poetry.lock`)
2228
+
2229
+ # publish to GIT, including a TAG
2230
+ git commit -a -m "release version 1.0.2"
2231
+ git tag 1.0.2
2232
+ git push
2233
+ git push --tags
2234
+
2235
+ # prepare package for PyPI
2236
+ poetry build
2237
+ poetry publish
2238
+ ```
2239
+
2240
+ If you changed the CLI interface at all run:
2241
+
2242
+ ```sh
2243
+ poetry run transcrypto doc md > CLI.md
2244
+ ./tools/inject_md_includes.py
2245
+ ```
2246
+
2247
+ You can find the 10 top slowest tests by running:
2248
+
2249
+ ```sh
2250
+ poetry run pytest -vvv -q --durations=10
2251
+ ```
2252
+
2253
+ You can search for flaky tests by running all tests 100 times:
2254
+
2255
+ ```sh
2256
+ poetry run pytest --flake-finder --flake-runs=100
2257
+ ```