transcrypto 1.8.0__py3-none-any.whl → 2.0.0__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: transcrypto
3
- Version: 1.8.0
3
+ Version: 2.0.0
4
4
  Summary: Basic crypto primitives, not intended for actual use, but as a companion to --Criptografia, Métodos e Algoritmos--
5
5
  License-Expression: Apache-2.0
6
6
  License-File: LICENSE
@@ -19,7 +19,6 @@ Requires-Dist: cryptography (>=46.0)
19
19
  Requires-Dist: gmpy2 (>=2.2)
20
20
  Requires-Dist: platformdirs (>=4.5)
21
21
  Requires-Dist: rich (>=14.3)
22
- Requires-Dist: scipy (>=1.17)
23
22
  Requires-Dist: typer (>=0.21)
24
23
  Requires-Dist: zstandard (>=0.25)
25
24
  Project-URL: Changelog, https://github.com/balparda/transcrypto/blob/main/CHANGELOG.md
@@ -120,25 +119,24 @@ To use in your project just do:
120
119
  pip3 install transcrypto
121
120
  ```
122
121
 
123
- and then `from transcrypto import rsa` (or other parts of the library) for using it.
122
+ and then `from transcrypto.core import rsa` (or other parts of the library) for using it.
124
123
 
125
124
  Known dependencies:
126
125
 
127
126
  - [zstandard](https://pypi.org/project/zstandard/) ([docs](https://python-zstandard.readthedocs.org/))
128
127
  - [cryptography](https://pypi.org/project/cryptography/) ([docs](https://cryptography.io/en/latest/))
129
128
  - [gmpy2](https://pypi.org/project/gmpy2/) ([docs](https://gmpy2.readthedocs.io/en/latest/))
130
- - [scipy](https://pypi.org/project/scipy/) ([docs](https://docs.scipy.org/doc/scipy/))
131
129
 
132
130
  ### Base Library
133
131
 
134
132
  #### Humanized Sizes (IEC binary)
135
133
 
136
134
  ```py
137
- from transcrypto import base
135
+ from transcrypto.utils import human
138
136
 
139
- base.HumanizedBytes(512) # '512 B'
140
- base.HumanizedBytes(2048) # '2.000 KiB'
141
- base.HumanizedBytes(5 * 1024**3) # '5.000 GiB'
137
+ human.HumanizedBytes(512) # '512 B'
138
+ human.HumanizedBytes(2048) # '2.000 KiB'
139
+ human.HumanizedBytes(5 * 1024**3) # '5.000 GiB'
142
140
  ```
143
141
 
144
142
  Converts raw byte counts to binary-prefixed strings (`B`, `KiB`, `MiB`, `GiB`, `TiB`, `PiB`, `EiB`).
@@ -148,22 +146,24 @@ Converts raw byte counts to binary-prefixed strings (`B`, `KiB`, `MiB`, `GiB`, `
148
146
  - For values `≥1024`, returns 3 decimals.
149
147
 
150
148
  - standard: 1 KiB = 1024 B, 1 MiB = 1024 KiB, …
151
- - errors: negative inputs raise `InputError`
149
+ - errors: negative inputs raise `base.InputError`
152
150
 
153
151
  #### Humanized Decimal Quantities (SI)
154
152
 
155
153
  ```py
154
+ from transcrypto.utils import human
155
+
156
156
  # Base (unitless)
157
- base.HumanizedDecimal(950) # '950'
158
- base.HumanizedDecimal(1500) # '1.500 k'
157
+ human.HumanizedDecimal(950) # '950'
158
+ human.HumanizedDecimal(1500) # '1.500 k'
159
159
 
160
160
  # With a unit (trimmed and attached)
161
- base.HumanizedDecimal(1500, unit=' Hz ') # '1.500 kHz'
162
- base.HumanizedDecimal(0.123456, unit='V') # '123.456 mV'
161
+ human.HumanizedDecimal(1500, unit=' Hz ') # '1.500 kHz'
162
+ human.HumanizedDecimal(0.123456, unit='V') # '123.456 mV'
163
163
 
164
164
  # Large magnitudes
165
- base.HumanizedDecimal(3_200_000) # '3.200 M'
166
- base.HumanizedDecimal(7.2e12, unit='B/s') # '7.200 TB/s'
165
+ human.HumanizedDecimal(3_200_000) # '3.200 M'
166
+ human.HumanizedDecimal(7.2e12, unit='B/s') # '7.200 TB/s'
167
167
  ```
168
168
 
169
169
  Scales by powers of 1000 using SI prefixes to keep the displayed value in roughly `[1, 1000)` when possible.
@@ -173,17 +173,19 @@ Scales by powers of 1000 using SI prefixes to keep the displayed value in roughl
173
173
  - Formatting uses 3 decimals for non-integer/unscaled values and for scaled values.
174
174
 
175
175
  - unit handling: `unit` is stripped; `<1000` values include a space before the unit (`'950 Hz'`)
176
- - errors: non-finite inputs raise `InputError` (negative values are supported and keep a leading `-`)
176
+ - errors: non-finite inputs raise `base.InputError` (negative values are supported and keep a leading `-`)
177
177
 
178
178
  #### Humanized Durations
179
179
 
180
180
  ```py
181
- base.HumanizedSeconds(0) # '0.000 s'
182
- base.HumanizedSeconds(0.000004) # '4.000 µs'
183
- base.HumanizedSeconds(0.25) # '250.000 ms'
184
- base.HumanizedSeconds(42) # '42.000 s'
185
- base.HumanizedSeconds(3661) # '1.017 h'
186
- base.HumanizedSeconds(172800) # '2.000 d'
181
+ from transcrypto.utils import human
182
+
183
+ human.HumanizedSeconds(0) # '0.000 s'
184
+ human.HumanizedSeconds(0.000004) # '4.000 µs'
185
+ human.HumanizedSeconds(0.25) # '250.000 ms'
186
+ human.HumanizedSeconds(42) # '42.000 s'
187
+ human.HumanizedSeconds(3661) # '1.017 h'
188
+ human.HumanizedSeconds(172800) # '2.000 d'
187
189
  ```
188
190
 
189
191
  Chooses an appropriate time unit based on magnitude and formats with fixed precision:
@@ -195,21 +197,21 @@ Chooses an appropriate time unit based on magnitude and formats with fixed preci
195
197
  - `< 24 h`: hours with three decimals (`h`)
196
198
  - `≥ 24 h`: days with three decimals (`d`)
197
199
  - special case: `0 → '0.000 s'`
198
- - errors: negative or non-finite inputs raise `InputError`
200
+ - errors: negative or non-finite inputs raise `base.InputError`
199
201
 
200
202
  #### Execution Timing
201
203
 
202
204
  A flexible timing utility that works as a **context manager**, **decorator**, or **manual timer object**.
203
205
 
204
206
  ```py
205
- from transcrypto import base
207
+ from transcrypto.utils import timer
206
208
  import time
207
209
  ```
208
210
 
209
211
  ##### Context manager
210
212
 
211
213
  ```py
212
- with base.Timer('Block timing'):
214
+ with timer.Timer('Block timing'):
213
215
  time.sleep(1.2)
214
216
  # → logs: "Block timing: 1.200 s" (default via logging.info)
215
217
  ```
@@ -219,7 +221,7 @@ Starts timing on entry, stops on exit, and reports elapsed time automatically.
219
221
  ##### Decorator
220
222
 
221
223
  ```py
222
- @base.Timer('Function timing')
224
+ @timer.Timer('Function timing')
223
225
  def slow_function():
224
226
  time.sleep(0.8)
225
227
 
@@ -232,7 +234,7 @@ Wraps a function so that each call is automatically timed.
232
234
  ##### Manual use
233
235
 
234
236
  ```py
235
- tm = base.Timer('Inline timing', emit_print=True)
237
+ tm = timer.Timer('Inline timing', emit_print=True)
236
238
  tm.Start()
237
239
  time.sleep(0.1)
238
240
  tm.Stop() # prints: "Inline timing: 0.100 s"
@@ -258,7 +260,7 @@ Manual control over `Start()` and `Stop()` for precise measurement of custom int
258
260
  These helpers turn arbitrary Python objects into compressed and/or encrypted binary blobs, and back again — with detailed timing and size logging.
259
261
 
260
262
  ```py
261
- from transcrypto import base
263
+ from transcrypto.core import key
262
264
  ```
263
265
 
264
266
  ##### Serialize
@@ -267,17 +269,17 @@ from transcrypto import base
267
269
  data = {'x': 42, 'y': 'hello'}
268
270
 
269
271
  # Basic serialization
270
- blob = base.Serialize(data)
272
+ blob = key.Serialize(data)
271
273
 
272
274
  # With compression and encryption
273
- blob = base.Serialize(
275
+ blob = key.Serialize(
274
276
  data,
275
277
  compress=9, # compression level (-22..22, default=3)
276
- key=my_encryptor # must implement `base.Encryptor` (e.g., `aes.AESKey`)
278
+ encryption_key=my_encryptor # must implement `key.Encryptor` (e.g., `aes.AESKey`)
277
279
  )
278
280
 
279
281
  # Save directly to file
280
- base.Serialize(data, file_path='/tmp/data.blob')
282
+ key.Serialize(data, file_path='/tmp/data.blob')
281
283
  ```
282
284
 
283
285
  Serialization path:
@@ -305,19 +307,19 @@ Compression levels:
305
307
  | 11…15 | Much slower | Slight gains | Large archives, not for runtime use |
306
308
  | 16…22 | Very slow | Tiny gains | Archival-only, multi-GB datasets |
307
309
 
308
- Errors: invalid compression level is clamped to range; other input errors raise `InputError`.
310
+ Errors: invalid compression level is clamped to range; other input errors raise `base.InputError`.
309
311
 
310
312
  ##### DeSerialize
311
313
 
312
314
  ```py
313
315
  # From in-memory blob
314
- obj = base.DeSerialize(data=blob)
316
+ obj = key.DeSerialize(data=blob)
315
317
 
316
318
  # From file
317
- obj = base.DeSerialize(file_path='/tmp/data.blob')
319
+ obj = key.DeSerialize(file_path='/tmp/data.blob')
318
320
 
319
321
  # With decryption
320
- obj = base.DeSerialize(data=blob, key=my_decryptor)
322
+ obj = key.DeSerialize(data=blob, decryption_key=my_decryptor)
321
323
  ```
322
324
 
323
325
  Deserialization path:
@@ -333,82 +335,82 @@ data/file → (decrypt) → (decompress if Zstd) → unpickle
333
335
 
334
336
  - Exactly one of `data` or `file_path` must be provided.
335
337
  - `file_path` must exist; `data` must be at least 4 bytes.
336
- - Wrong key / authentication failure can raise `CryptoError`.
338
+ - Wrong key / authentication failure can raise `key.CryptoError`.
337
339
  - Corrupted compressed blobs typically raise `zstandard.ZstdError` during decompression.
338
340
 
339
341
  #### Cryptographically Secure Randomness
340
342
 
341
- These helpers live in `base` and wrap Python’s `secrets` with additional checks and guarantees for crypto use-cases.
343
+ These helpers live in `saferandom` and wrap Python’s `secrets` with additional checks and guarantees for crypto use-cases.
342
344
 
343
345
  ```py
344
- from transcrypto import base
346
+ from transcrypto.core import bid
345
347
  ```
346
348
 
347
349
  ##### Fixed-size random integers
348
350
 
349
351
  ```py
350
352
  # Generate a 256-bit integer (first bit always set)
351
- r = base.RandBits(256)
353
+ r = saferandom.RandBits(256)
352
354
  assert r.bit_length() == 256
353
355
  ```
354
356
 
355
357
  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.
356
358
 
357
- - errors: `n_bits < 8` → `InputError`
359
+ - errors: `n_bits < 8` → `base.InputError`
358
360
 
359
361
  ##### Uniform random integers in a range
360
362
 
361
363
  ```py
362
364
  # Uniform between [10, 20] inclusive
363
- n = base.RandInt(10, 20)
365
+ n = saferandom.RandInt(10, 20)
364
366
  assert 10 <= n <= 20
365
367
  ```
366
368
 
367
369
  Returns a crypto-secure integer uniformly distributed over the closed interval `[min_int, max_int]`.
368
370
 
369
371
  - constraints: `min_int ≥ 0` and `< max_int`
370
- - errors: invalid bounds → `InputError`
372
+ - errors: invalid bounds → `base.InputError`
371
373
 
372
374
  ##### In-place secure shuffle
373
375
 
374
376
  ```py
375
377
  deck = list(range(10))
376
- base.RandShuffle(deck)
378
+ saferandom.RandShuffle(deck)
377
379
  print(deck) # securely shuffled order
378
380
  ```
379
381
 
380
382
  Performs an in-place Fisher–Yates shuffle using `secrets.randbelow`. Suitable for sensitive data ordering.
381
383
 
382
384
  - constraints: sequence length ≥ 2
383
- - errors: shorter sequences → `InputError`
385
+ - errors: shorter sequences → `base.InputError`
384
386
 
385
387
  ##### Random byte strings
386
388
 
387
389
  ```py
388
390
  # 32 random bytes
389
- b = base.RandBytes(32)
391
+ b = saferandom.RandBytes(32)
390
392
  assert len(b) == 32
391
393
  ```
392
394
 
393
395
  Generates `n_bytes` of high-quality crypto-secure random data.
394
396
 
395
397
  - constraints: `n_bytes ≥ 1`
396
- - errors: smaller values → `InputError`
398
+ - errors: smaller values → `base.InputError`
397
399
 
398
400
  #### Computing the Greatest Common Divisor
399
401
 
400
402
  ```py
401
- >>> from transcrypto import base
402
- >>> base.GCD(462, 1071)
403
+ >>> from transcrypto.core import modmath
404
+ >>> modmath.GCD(462, 1071)
403
405
  21
404
- >>> base.GCD(0, 17)
406
+ >>> modmath.GCD(0, 17)
405
407
  17
406
408
  ```
407
409
 
408
410
  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:
409
411
 
410
412
  ```py
411
- >>> base.ExtendedGCD(462, 1071)
413
+ >>> modmath.ExtendedGCD(462, 1071)
412
414
  (21, 7, -3)
413
415
  >>> 462 * 7 + 1071 * (-3)
414
416
  21
@@ -423,7 +425,7 @@ Use-cases:
423
425
  #### Fast Modular Arithmetic
424
426
 
425
427
  ```py
426
- from transcrypto import modmath
428
+ from transcrypto.core import modmath
427
429
 
428
430
  m = 2**256 - 189 # a large prime modulus
429
431
 
@@ -444,7 +446,7 @@ exp = modmath.ModExp(3, 10**20, m) # ≈ log₂(y) time, handles huge exponent
444
446
  ##### Chinese Remainder Theorem (CRT) – Pair
445
447
 
446
448
  ```py
447
- from transcrypto import modmath
449
+ from transcrypto.core import modmath
448
450
 
449
451
  # Solve:
450
452
  # x ≡ 2 (mod 3)
@@ -467,7 +469,7 @@ x ≡ a2 (mod m2)
467
469
  - `m1 ≥ 2`, `m2 ≥ 2`, `m1 != m2`
468
470
  - `gcd(m1, m2) == 1` (co-prime)
469
471
  - **Errors**:
470
- - invalid modulus values → `InputError`
472
+ - invalid modulus values → `base.InputError`
471
473
  - non co-prime moduli → `ModularDivideError`
472
474
 
473
475
  This function is a 2-modulus variant; for multiple moduli, apply it iteratively or use a general CRT solver.
@@ -513,13 +515,13 @@ for k, m_p, perfect in modmath.MersennePrimesGenerator(0):
513
515
  Simple, fixed-output-size wrappers over Python’s `hashlib` for common digest operations, plus file hashing.
514
516
 
515
517
  ```py
516
- from transcrypto import base
518
+ from transcrypto.core import hashes
517
519
  ```
518
520
 
519
521
  ##### SHA-256 hashing
520
522
 
521
523
  ```py
522
- h = base.Hash256(b'hello world')
524
+ h = hashes.Hash256(b'hello world')
523
525
  assert len(h) == 32 # bytes
524
526
  print(h.hex()) # 64 hex chars
525
527
  ```
@@ -529,7 +531,7 @@ Computes the SHA-256 digest of a byte string, returning exactly 32 bytes (256 bi
529
531
  ##### SHA-512 hashing
530
532
 
531
533
  ```py
532
- h = base.Hash512(b'hello world')
534
+ h = hashes.Hash512(b'hello world')
533
535
  assert len(h) == 64 # bytes
534
536
  print(h.hex()) # 128 hex chars
535
537
  ```
@@ -540,11 +542,11 @@ Computes the SHA-512 digest of a byte string, returning exactly 64 bytes (512 bi
540
542
 
541
543
  ```py
542
544
  # Default SHA-256
543
- fh = base.FileHash('/path/to/file')
545
+ fh = hashes.FileHash('/path/to/file')
544
546
  print(fh.hex())
545
547
 
546
548
  # SHA-512
547
- fh2 = base.FileHash('/path/to/file', digest='sha512')
549
+ fh2 = hashes.FileHash('/path/to/file', digest='sha512')
548
550
  ```
549
551
 
550
552
  Hashes a file from disk in streaming mode. By default uses SHA-256; `digest='sha512'` switches to SHA-512.
@@ -552,17 +554,19 @@ Hashes a file from disk in streaming mode. By default uses SHA-256; `digest='sha
552
554
  - constraints:
553
555
  - `digest` must be `'sha256'` or `'sha512'`
554
556
  - `full_path` must exist
555
- - errors: invalid digest or missing file → `InputError`
557
+ - errors: invalid digest or missing file → `base.InputError`
556
558
 
557
559
  #### Symmetric Encryption Interface
558
560
 
559
- `base.Encryptor` and `base.Decryptor` are runtime-checkable protocols that define the **byte-in / byte-out** contract for symmetric ciphers.
561
+ `key.Encryptor` and `key.Decryptor` are runtime-checkable protocols that define the **byte-in / byte-out** contract for symmetric ciphers.
560
562
 
561
563
  - **Metadata handling** — if the algorithm uses a `nonce` or `tag`, the implementation must handle it internally (e.g., append it to ciphertext).
562
- - **AEAD modes** — if supported, `associated_data` must be authenticated; otherwise, a non-`None` value should raise `InputError`.
564
+ - **AEAD modes** — if supported, `associated_data` must be authenticated; otherwise, a non-`None` value should raise `base.InputError`.
563
565
 
564
566
  ```py
565
- class MyAES(base.Encryptor, base.Decryptor):
567
+ from transcrypto.core import key
568
+
569
+ class MyAES(key.Encryptor, key.Decryptor):
566
570
  def Encrypt(self, plaintext: bytes, *, associated_data=None) -> bytes:
567
571
  ...
568
572
  def Decrypt(self, ciphertext: bytes, *, associated_data=None) -> bytes:
@@ -576,14 +580,14 @@ Cryptographic objects all derive from the `CryptoKey` class and will all have so
576
580
  - 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…`).
577
581
  - It will have a `_DebugDump()` method that **does print secrets** and can be used for **debugging only**.
578
582
  - Can be easily serialized to `bytes` by the `blob` property and to base-64 encoded `str` by the `encoded` property.
579
- - Can be serialized encrypted to `bytes` by the `Blob(key=[base.Encryptor])` method and to encrypted base-64 encoded `str` by the `Encoded(key=[base.Encryptor])` method.
580
- - Can be instantiated back as an object from `str` or `bytes` using the `Load(data, key=[base.Decryptor] | None)` method. The `Load()` will decide how to build the object and will work universally with all the serialization options discussed above.
583
+ - Can be serialized encrypted to `bytes` by the `Blob(encryption_key=[key.Encryptor])` method and to encrypted base-64 encoded `str` by the `Encoded(encryption_key=[key.Encryptor])` method.
584
+ - Can be instantiated back as an object from `str` or `bytes` using the `Load(data, decryption_key=[key.Decryptor] | None)` method. The `Load()` will decide how to build the object and will work universally with all the serialization options discussed above.
581
585
 
582
586
  Example:
583
587
 
584
588
  <!-- cspell:disable -->
585
589
  ```py
586
- from transcrypto import base, rsa, aes
590
+ from transcrypto.core import aes, rsa
587
591
 
588
592
  priv = rsa.RSAPrivateKey.New(512) # small key, but good for this example
589
593
  print(str(priv)) # safe, no secrets
@@ -598,12 +602,12 @@ print(priv.blob)
598
602
  print(priv.encoded)
599
603
  # ▶ KLUv_WBwAIELAIAElWUBAAAAAAAAjA90cmFuc2NyeXB0by5yc2GUjA1SU0FQcml2YXRlS2V5lJOUKYGUXZQoikHf1EvsmZedAZve7TrLmobLAwuRIr_77TLG6G_0fsLGThERVJu075be8PLjUQYnLXcacZFQ5Fb1Iy1WtiE985euAEoBAAEAiiFR9ngiXMzkf41o5CRBY3h0D4DJVisDDhLmAWsiaHggzQCKIS_cmQ6MKXCtROtC7c_Mrsi9A-9NM8DksaHaRwvy6uTZAIpB4TVbsLxc41TEc19wIzpxbi9y5dW5gdfTkRQSSiz0ijmb8Xk3pyBfKAv8JbHp8Yv48gNZUfX67qq0J7yhJqeUoACKIbFb2kTNRzSqm3JRtjc2BPS-FnLFdadlFcV4-6IW7eqLAIogFZfzDN39gZLR9uTz4KHSTaqxWrJgP8-YYssjss6FlFKKIIItgCDv7ompNpY8gBs5bibN8XTsr-JOYSntDVT5Fe5vZWIu
600
604
 
601
- key = aes.AESKey(key256=b'x' * 32)
602
- print(key)
605
+ aes_key = aes.AESKey(key256=b'x' * 32)
606
+ print(aes_key)
603
607
  # ▶ AESKey(key256=86a86df7…)
604
608
 
605
- encrypted = priv.Blob(key=key)
606
- print(priv == rsa.RSAPrivateKey.Load(encrypted, key=key))
609
+ encrypted = priv.Blob(encryption_key=aes_key)
610
+ print(priv == rsa.RSAPrivateKey.Load(encrypted, decryption_key=aes_key))
607
611
  # ▶ True
608
612
  ```
609
613
  <!-- cspell:enable -->
@@ -616,14 +620,14 @@ Also includes a high-iteration PBKDF2-based key derivation from static passwords
616
620
  ##### Key creation
617
621
 
618
622
  ```py
619
- from transcrypto import aes
623
+ from transcrypto.core import aes
620
624
 
621
625
  # From raw bytes (must be exactly 32 bytes)
622
- key = aes.AESKey(key256=b'\x00' * 32)
626
+ aes_key = aes.AESKey(key256=b'\x00' * 32)
623
627
 
624
628
  # From a static password (slow, high-iteration PBKDF2-SHA256)
625
- key = aes.AESKey.FromStaticPassword('correct horse battery staple')
626
- print(key.encoded) # URL-safe Base64
629
+ aes_key = aes.AESKey.FromStaticPassword('correct horse battery staple')
630
+ print(aes_key.encoded) # URL-safe Base64
627
631
  ```
628
632
 
629
633
  - **Length**: `key256` must be exactly 32 bytes
@@ -638,10 +642,10 @@ data = b'secret message'
638
642
  aad = b'metadata'
639
643
 
640
644
  # Encrypt (returns IV + ciphertext + tag)
641
- ct = key.Encrypt(data, associated_data=aad)
645
+ ct = aes_key.Encrypt(data, associated_data=aad)
642
646
 
643
647
  # Decrypt
644
- pt = key.Decrypt(ct, associated_data=aad)
648
+ pt = aes_key.Decrypt(ct, associated_data=aad)
645
649
  assert pt == data
646
650
  ```
647
651
 
@@ -650,13 +654,13 @@ assert pt == data
650
654
  - Authenticated tag (128-bit) ensures integrity
651
655
  - Optional `associated_data` is authenticated but not encrypted
652
656
  - **Errors**:
653
- - Tag mismatch or wrong key → `CryptoError`
657
+ - Tag mismatch or wrong key → `key.CryptoError`
654
658
 
655
659
  ##### AES-256 + ECB (unsafe, fixed block only)
656
660
 
657
661
  ```py
658
662
  # ECB mode is for 16-byte block encoding ONLY
659
- ecb = key.ECBEncoder()
663
+ ecb = aes_key.ECBEncoder()
660
664
 
661
665
  block = b'16-byte string!!'
662
666
  ct_block = ecb.Encrypt(block)
@@ -681,7 +685,7 @@ Key points:
681
685
 
682
686
  - **GCM mode** is secure for general use; ECB mode is for special low-level operations
683
687
  - **Static password derivation** is intentionally slow to resist brute force
684
- - All sizes and parameters are validated with `InputError` on misuse
688
+ - All sizes and parameters are validated with `base.InputError` on misuse
685
689
 
686
690
  #### RSA (Rivest-Shamir-Adleman) Public Cryptography
687
691
 
@@ -692,7 +696,7 @@ This implementation is raw RSA, no OAEP or PSS! It works on the actual integers.
692
696
  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.
693
697
 
694
698
  ```py
695
- from transcrypto import rsa
699
+ from transcrypto.core import rsa
696
700
 
697
701
  # Generate a key pair
698
702
  priv = rsa.RSAPrivateKey.New(2048) # 2048-bit modulus
@@ -737,7 +741,7 @@ This is **raw El-Gamal** over a prime field — no padding, no hashing — and i
737
741
  For real-world deployments, use a high-level library with authenticated encryption and proper encoding.
738
742
 
739
743
  ```py
740
- from transcrypto import elgamal
744
+ from transcrypto.core import elgamal
741
745
 
742
746
  # Shared parameters (prime modulus, group base) for a group
743
747
  shared = elgamal.ElGamalSharedPublicKey.NewShared(256)
@@ -773,13 +777,13 @@ Key points:
773
777
 
774
778
  - **Security parameters**:
775
779
  - Recommended `prime_modulus` bit length ≥ 2048 for real security
776
- - Random values from `base.RandBits`
780
+ - Random values from `saferandom.RandBits`
777
781
  - **Ephemeral keys**:
778
782
  - Fresh per encryption/signature
779
783
  - Must satisfy `gcd(k, p-1) == 1`
780
784
  - **Errors**:
781
- - Bad ranges → `InputError`
782
- - Invalid math relationships → `CryptoError`
785
+ - Bad ranges → `base.InputError`
786
+ - Invalid math relationships → `key.CryptoError`
783
787
  - **Group sharing**:
784
788
  - Multiple parties can share `(p, g)` but have different `(individual_base, decrypt_exp)`
785
789
 
@@ -790,7 +794,7 @@ Key points:
790
794
  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`.
791
795
 
792
796
  ```py
793
- from transcrypto import dsa
797
+ from transcrypto.core import dsa
794
798
 
795
799
  # Shared parameters (p, q, g) - Safe Sign/Verify requires q > 512 bits
796
800
  shared = dsa.DSASharedPublicKey.NewShared(2048, 520)
@@ -817,8 +821,8 @@ assert pub.RawVerify(msg, sig)
817
821
  - `1 ≤ message < q`
818
822
  - signatures: `(s1, s2)` with `2 ≤ s1, s2 < q`
819
823
  - errors:
820
- - invalid ranges → `InputError`
821
- - inconsistent parameters → `CryptoError`
824
+ - invalid ranges → `base.InputError`
825
+ - inconsistent parameters → `key.CryptoError`
822
826
 
823
827
  ##### Security notes
824
828
 
@@ -834,23 +838,23 @@ assert (p - 1) % q == 0
834
838
  ```
835
839
 
836
840
  Used internally by `DSASharedPublicKey.NewShared()`.
837
- Search breadth and retry caps are bounded; repeated failures raise `CryptoError`.
841
+ Search breadth and retry caps are bounded; repeated failures raise `key.CryptoError`.
838
842
 
839
843
  #### Public Bidding
840
844
 
841
845
  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.
842
846
 
843
847
  ```py
844
- from transcrypto import base
848
+ from transcrypto.core import bid
845
849
 
846
850
  # Generate the private and public bids
847
- bid_priv = base.PrivateBid512.New(secret) # this one you keep private
848
- bid_pub = base.PublicBid512.Copy(bid_priv) # this one you publish
851
+ bid_priv = bid.PrivateBid512.New(secret) # this one you keep private
852
+ bid_pub = bid.PublicBid512.Copy(bid_priv) # this one you publish
849
853
 
850
854
  # Checking that a bid is genuine requires the public bid and knowing the nonce and the secret:
851
855
  print(bid_pub.VerifyBid(private_key, secret_bid)) # these come from a divulged private bid
852
856
  # of course, you want to also make sure the provided private data matches your version of it, e.g.:
853
- bid_pub_expected = base.PublicBid512.Copy(bid_priv)
857
+ bid_pub_expected = bid.PublicBid512.Copy(bid_priv)
854
858
  print(bid_pub == bid_pub_expected)
855
859
  ```
856
860
 
@@ -861,7 +865,7 @@ print(bid_pub == bid_pub_expected)
861
865
  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; `MakeDataShares()` requires > 256 bits.
862
866
 
863
867
  ```py
864
- from transcrypto import sss
868
+ from transcrypto.core import sss
865
869
 
866
870
  # Generate parameters: at least 3 of 5 shares needed,
867
871
  # coefficients & modulus are 264-bit primes (> 256 bits required for MakeDataShares)
@@ -907,7 +911,7 @@ recovered = pub.RawRecoverSecret(subset)
907
911
  assert recovered == secret
908
912
  ```
909
913
 
910
- If you supply fewer than minimum shares you get a `CryptoError`, unless you explicitly override:
914
+ If you supply fewer than minimum shares you get a `key.CryptoError`, unless you explicitly override:
911
915
 
912
916
  ```py
913
917
  try:
@@ -1028,7 +1032,7 @@ Remember to check your diffs before submitting (especially `poetry.lock`) to avo
1028
1032
  When dependencies change, always regenerate `requirements.txt` by running:
1029
1033
 
1030
1034
  ```sh
1031
- poetry export --format requirements.txt --without-hashes --output requirements.txt
1035
+ make req # or: poetry export --format requirements.txt --without-hashes --output requirements.txt
1032
1036
  ```
1033
1037
 
1034
1038
  ### Creating a New Version
@@ -1053,11 +1057,7 @@ poetry build
1053
1057
  poetry publish
1054
1058
  ```
1055
1059
 
1056
- If you changed the CLI interface at all, in any tool, run:
1057
-
1058
- ```sh
1059
- make docs
1060
- ```
1060
+ If you changed the CLI interface at all, in any tool, run `make docs` or even better `make ci`.
1061
1061
 
1062
1062
  You can find the 10 top slowest tests by running:
1063
1063
 
@@ -0,0 +1,33 @@
1
+ transcrypto/__init__.py,sha256=rR2iNiYIz11plYoFGy8HoLZeXxFDKEGd_tZCgHXE2cU,338
2
+ transcrypto/cli/__init__.py,sha256=VyfdjELABx6yaiAQrFlK7WDa96j2O12umoy1WunqbLw,134
3
+ transcrypto/cli/aeshash.py,sha256=VvL-aIe1u4JOqNRM7khVqTg-p85913HOcRa8FQLwO3I,13784
4
+ transcrypto/cli/bidsecret.py,sha256=0W14gEoNTcBFP_5yk2HiFBUMMRTJgtLAksKMoXkQhDc,12593
5
+ transcrypto/cli/clibase.py,sha256=nGqMhA8Ad9OFovPAsikaYdK1e5CFDmpDsSNyVjhSJtM,5988
6
+ transcrypto/cli/intmath.py,sha256=mtLVEmss5i62q5OyYbCwF2P1MqAssN_cCpq0AOozz6g,14477
7
+ transcrypto/cli/publicalgos.py,sha256=K9MdqsDHCXYzrs9I0cFcgSvTIM25kc1iV02tsb5E05s,31394
8
+ transcrypto/core/__init__.py,sha256=g7Nfe2TgWVTqr8vbEdOmNWhYrIUPgfbDHplq4v_WaJA,142
9
+ transcrypto/core/aes.py,sha256=ZQ9kq0--4FqgvsL8YKfs5zQjSTnP-I2Uvg4SvZoJPY0,14314
10
+ transcrypto/core/bid.py,sha256=eB73pSBQSdK54RG5bjO4l_r8IR6CrBHHnZHfuoYZn-U,4916
11
+ transcrypto/core/constants.py,sha256=qIdXZ90LYZdu5VZ_hKizU04j_wLA0Na0-mSUZ5pOdiY,191233
12
+ transcrypto/core/dsa.py,sha256=lzS1zNsu6KeURBK53_IsmvFJT3Q2p-ZCf8S2HlWzbA4,20092
13
+ transcrypto/core/elgamal.py,sha256=-a203yphm2hXOWPt1-F9CGZ_9ome_3u4tNc-ATe9ais,20696
14
+ transcrypto/core/hashes.py,sha256=39mLuyoJo0J9fxHaKdxZIRJa78QnfBb0_5VsylOgpdc,3195
15
+ transcrypto/core/key.py,sha256=EPkwQVtgu0maBXPdN2Fl4syMqONJPt_gVij78L1h0uc,26620
16
+ transcrypto/core/modmath.py,sha256=bDsK4VxvrAhut0dQjTWwJqzhuuumpmSDspl1IIl0aqA,23051
17
+ transcrypto/core/rsa.py,sha256=UjuTu6KFB_wCH3i2Pe8P6NF-KD4e5jXk1XrijIuFidw,24323
18
+ transcrypto/core/sss.py,sha256=w-oKzOK6uJL8oh5T0IQNW0fWFMjQMBFUNVq4WOv94wg,17084
19
+ transcrypto/profiler.py,sha256=yuZSDfJs-2VsrtLzQgRCR9LR92nER59EmcEYKeSFFW8,7874
20
+ transcrypto/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
+ transcrypto/transcrypto.py,sha256=V89JREdr_8AiLzbrnTjtDUkt-GSBHOBzC-iHk02qyMI,16907
22
+ transcrypto/utils/__init__.py,sha256=ySgjEaInsgHkHlzUKIc6calJ0Or_JzbPaQHlF4TfLsM,140
23
+ transcrypto/utils/base.py,sha256=5vWxOTqS0vmCffgnNBcFfzo_UChed3isyeRn_66nTT4,2329
24
+ transcrypto/utils/human.py,sha256=DGB2rQ9v75Amo767Cp9MPN6192-azAIWHKOdk_ynSvY,8983
25
+ transcrypto/utils/logging.py,sha256=UrW8CLAaVAe-WRDvNEkWeH-viMI98a0w70Ag9aEP-mk,4622
26
+ transcrypto/utils/saferandom.py,sha256=mLP6tpz6Ts1oYtDlcbw0MaNIvVQxTD_WHwKMYv9_hrY,2977
27
+ transcrypto/utils/stats.py,sha256=69Mgi4WMBm1Iqm2HEDUL1qLxfQjle4zcTXHah5wD-bA,11668
28
+ transcrypto/utils/timer.py,sha256=IhCYKq-O1xu-1VrO34wKJ8LN5UZZvA5j1tV1d4BvZf0,4789
29
+ transcrypto-2.0.0.dist-info/METADATA,sha256=j6nL5Phgyux1Qpl3iNO5FrPyu9HoL47etRnh7grXuIo,39462
30
+ transcrypto-2.0.0.dist-info/WHEEL,sha256=3ny-bZhpXrU6vSQ1UPG34FoxZBp3lVcvK0LkgUz6VLk,88
31
+ transcrypto-2.0.0.dist-info/entry_points.txt,sha256=HXJ1yXZJT_9OhLJgEhv2toOpxwhDaIU2HuW91MTJDIg,93
32
+ transcrypto-2.0.0.dist-info/licenses/LICENSE,sha256=DVQuDIgE45qn836wDaWnYhSdxoLXgpRRKH4RuTjpRZQ,10174
33
+ transcrypto-2.0.0.dist-info/RECORD,,