transcrypto 1.8.0__py3-none-any.whl → 2.0.3__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.
- transcrypto/__init__.py +1 -1
- transcrypto/cli/aeshash.py +14 -12
- transcrypto/cli/bidsecret.py +19 -16
- transcrypto/cli/clibase.py +22 -142
- transcrypto/cli/intmath.py +24 -21
- transcrypto/cli/publicalgos.py +28 -26
- transcrypto/core/__init__.py +3 -0
- transcrypto/{aes.py → core/aes.py} +17 -29
- transcrypto/core/bid.py +161 -0
- transcrypto/{dsa.py → core/dsa.py} +28 -27
- transcrypto/{elgamal.py → core/elgamal.py} +33 -32
- transcrypto/core/hashes.py +96 -0
- transcrypto/core/key.py +735 -0
- transcrypto/{modmath.py → core/modmath.py} +91 -17
- transcrypto/{rsa.py → core/rsa.py} +51 -50
- transcrypto/{sss.py → core/sss.py} +27 -26
- transcrypto/profiler.py +25 -11
- transcrypto/transcrypto.py +25 -15
- transcrypto/utils/__init__.py +3 -0
- transcrypto/utils/base.py +72 -0
- transcrypto/utils/human.py +278 -0
- transcrypto/utils/logging.py +139 -0
- transcrypto/utils/saferandom.py +102 -0
- transcrypto/utils/stats.py +360 -0
- transcrypto/utils/timer.py +175 -0
- {transcrypto-1.8.0.dist-info → transcrypto-2.0.3.dist-info}/METADATA +101 -101
- transcrypto-2.0.3.dist-info/RECORD +33 -0
- {transcrypto-1.8.0.dist-info → transcrypto-2.0.3.dist-info}/WHEEL +1 -1
- transcrypto/base.py +0 -1637
- transcrypto-1.8.0.dist-info/RECORD +0 -23
- /transcrypto/{constants.py → core/constants.py} +0 -0
- {transcrypto-1.8.0.dist-info → transcrypto-2.0.3.dist-info}/entry_points.txt +0 -0
- {transcrypto-1.8.0.dist-info → transcrypto-2.0.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: transcrypto
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.3
|
|
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
|
|
@@ -18,8 +18,7 @@ Classifier: Topic :: Security :: Cryptography
|
|
|
18
18
|
Requires-Dist: cryptography (>=46.0)
|
|
19
19
|
Requires-Dist: gmpy2 (>=2.2)
|
|
20
20
|
Requires-Dist: platformdirs (>=4.5)
|
|
21
|
-
Requires-Dist: rich (>=14.3)
|
|
22
|
-
Requires-Dist: scipy (>=1.17)
|
|
21
|
+
Requires-Dist: rich (>=14.3,!=14.3.2)
|
|
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
|
|
135
|
+
from transcrypto.utils import human
|
|
138
136
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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
|
-
|
|
158
|
-
|
|
157
|
+
human.HumanizedDecimal(950) # '950'
|
|
158
|
+
human.HumanizedDecimal(1500) # '1.500 k'
|
|
159
159
|
|
|
160
160
|
# With a unit (trimmed and attached)
|
|
161
|
-
|
|
162
|
-
|
|
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
|
-
|
|
166
|
-
|
|
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
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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
|
|
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
|
|
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
|
-
@
|
|
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 =
|
|
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
|
|
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 =
|
|
272
|
+
blob = key.Serialize(data)
|
|
271
273
|
|
|
272
274
|
# With compression and encryption
|
|
273
|
-
blob =
|
|
275
|
+
blob = key.Serialize(
|
|
274
276
|
data,
|
|
275
277
|
compress=9, # compression level (-22..22, default=3)
|
|
276
|
-
|
|
278
|
+
encryption_key=my_encryptor # must implement `key.Encryptor` (e.g., `aes.AESKey`)
|
|
277
279
|
)
|
|
278
280
|
|
|
279
281
|
# Save directly to file
|
|
280
|
-
|
|
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 =
|
|
316
|
+
obj = key.DeSerialize(data=blob)
|
|
315
317
|
|
|
316
318
|
# From file
|
|
317
|
-
obj =
|
|
319
|
+
obj = key.DeSerialize(file_path='/tmp/data.blob')
|
|
318
320
|
|
|
319
321
|
# With decryption
|
|
320
|
-
obj =
|
|
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 `
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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
|
|
402
|
-
>>>
|
|
403
|
+
>>> from transcrypto.core import modmath
|
|
404
|
+
>>> modmath.GCD(462, 1071)
|
|
403
405
|
21
|
|
404
|
-
>>>
|
|
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
|
-
>>>
|
|
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
|
|
518
|
+
from transcrypto.core import hashes
|
|
517
519
|
```
|
|
518
520
|
|
|
519
521
|
##### SHA-256 hashing
|
|
520
522
|
|
|
521
523
|
```py
|
|
522
|
-
h =
|
|
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 =
|
|
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 =
|
|
545
|
+
fh = hashes.FileHash('/path/to/file')
|
|
544
546
|
print(fh.hex())
|
|
545
547
|
|
|
546
548
|
# SHA-512
|
|
547
|
-
fh2 =
|
|
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
|
-
`
|
|
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
|
-
|
|
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(
|
|
580
|
-
- Can be instantiated back as an object from `str` or `bytes` using the `Load(data,
|
|
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
|
|
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
|
-
|
|
602
|
-
print(
|
|
605
|
+
aes_key = aes.AESKey(key256=b'x' * 32)
|
|
606
|
+
print(aes_key)
|
|
603
607
|
# ▶ AESKey(key256=86a86df7…)
|
|
604
608
|
|
|
605
|
-
encrypted = priv.Blob(
|
|
606
|
-
print(priv == rsa.RSAPrivateKey.Load(encrypted,
|
|
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
|
-
|
|
626
|
+
aes_key = aes.AESKey(key256=b'\x00' * 32)
|
|
623
627
|
|
|
624
628
|
# From a static password (slow, high-iteration PBKDF2-SHA256)
|
|
625
|
-
|
|
626
|
-
print(
|
|
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 =
|
|
645
|
+
ct = aes_key.Encrypt(data, associated_data=aad)
|
|
642
646
|
|
|
643
647
|
# Decrypt
|
|
644
|
-
pt =
|
|
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 =
|
|
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 `
|
|
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
|
|
848
|
+
from transcrypto.core import bid
|
|
845
849
|
|
|
846
850
|
# Generate the private and public bids
|
|
847
|
-
bid_priv =
|
|
848
|
-
bid_pub =
|
|
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 =
|
|
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=KzOwhHreEpfs1UwMqoH9EJaAfKp1_jgdwNTvORRDTM0,338
|
|
2
|
+
transcrypto/cli/__init__.py,sha256=VyfdjELABx6yaiAQrFlK7WDa96j2O12umoy1WunqbLw,134
|
|
3
|
+
transcrypto/cli/aeshash.py,sha256=bO3p5ZRLoqc7pM-FKWR6kvDyrAteVZCCeLdpuRQckWU,13784
|
|
4
|
+
transcrypto/cli/bidsecret.py,sha256=2jxgTyIt0l2VN39kTiMJt7W9H33DfDBM7W0lMnduRiU,12606
|
|
5
|
+
transcrypto/cli/clibase.py,sha256=po0-K_xcbUtF8sJ7shrNWvmonPfUmJfLJg3vhA4LTsA,5988
|
|
6
|
+
transcrypto/cli/intmath.py,sha256=kpVfeWmevG6dq_jLvObKHA2mUtejcwnplwhUK3qZigg,14490
|
|
7
|
+
transcrypto/cli/publicalgos.py,sha256=b2KN2_72EKhVNjiR2fIWgZSQaVhC8TIDpeAvtI9lJNg,31407
|
|
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=UfSTWpvD0TnV6YCPoDvgnQQ4XBuJNZaD813F2glFRhE,7887
|
|
20
|
+
transcrypto/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
21
|
+
transcrypto/transcrypto.py,sha256=aHtbkZNEMfFG_7Ul8UaGI4wwQEZWlPuBTQB38gvQDDs,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.3.dist-info/METADATA,sha256=LUzTGCWDwgp9qkCNGn_y7cZEb14LX1IAyD2zTuuGgg4,39471
|
|
30
|
+
transcrypto-2.0.3.dist-info/WHEEL,sha256=kJCRJT_g0adfAJzTx2GUMmS80rTJIVHRCfG0DQgLq3o,88
|
|
31
|
+
transcrypto-2.0.3.dist-info/entry_points.txt,sha256=HXJ1yXZJT_9OhLJgEhv2toOpxwhDaIU2HuW91MTJDIg,93
|
|
32
|
+
transcrypto-2.0.3.dist-info/licenses/LICENSE,sha256=DVQuDIgE45qn836wDaWnYhSdxoLXgpRRKH4RuTjpRZQ,10174
|
|
33
|
+
transcrypto-2.0.3.dist-info/RECORD,,
|