transcrypto 1.1.1__tar.gz → 1.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: transcrypto
3
- Version: 1.1.1
3
+ Version: 1.2.0
4
4
  Summary: Basic crypto primitives, not intended for actual use, but as a companion to --Criptografia, Métodos e Algoritmos--
5
5
  Author-email: Daniel Balparda <balparda@github.com>
6
6
  License-Expression: Apache-2.0
@@ -18,7 +18,7 @@ Dynamic: license-file
18
18
 
19
19
  # TransCrypto
20
20
 
21
- Basic crypto primitives, not intended for actual use, but as a companion to *"Criptografia, Métodos e Algoritmos"*.
21
+ Basic cryptography primitives implementation, a companion to *"Criptografia, Métodos e Algoritmos"*.
22
22
 
23
23
  Started in July/2025, by Daniel Balparda. Since version 1.0.2 it is PyPI package:
24
24
 
@@ -61,69 +61,66 @@ Started in July/2025, by Daniel Balparda. Since version 1.0.2 it is PyPI package
61
61
  - [`aes ecb decrypt`](#aes-ecb-decrypt)
62
62
  - [`rsa`](#rsa)
63
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)
64
+ - [`rsa rawencrypt`](#rsa-rawencrypt)
65
+ - [`rsa rawdecrypt`](#rsa-rawdecrypt)
66
+ - [`rsa rawsign`](#rsa-rawsign)
67
+ - [`rsa rawverify`](#rsa-rawverify)
68
68
  - [`elgamal`](#elgamal)
69
69
  - [`elgamal shared`](#elgamal-shared)
70
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)
71
+ - [`elgamal rawencrypt`](#elgamal-rawencrypt)
72
+ - [`elgamal rawdecrypt`](#elgamal-rawdecrypt)
73
+ - [`elgamal rawsign`](#elgamal-rawsign)
74
+ - [`elgamal rawverify`](#elgamal-rawverify)
75
75
  - [`dsa`](#dsa)
76
76
  - [`dsa shared`](#dsa-shared)
77
77
  - [`dsa new`](#dsa-new)
78
- - [`dsa sign`](#dsa-sign)
79
- - [`dsa verify`](#dsa-verify)
78
+ - [`dsa rawsign`](#dsa-rawsign)
79
+ - [`dsa rawverify`](#dsa-rawverify)
80
80
  - [`bid`](#bid)
81
81
  - [`bid new`](#bid-new)
82
82
  - [`bid verify`](#bid-verify)
83
83
  - [`sss`](#sss)
84
84
  - [`sss new`](#sss-new)
85
- - [`sss shares`](#sss-shares)
86
- - [`sss recover`](#sss-recover)
87
- - [`sss verify`](#sss-verify)
85
+ - [`sss rawshares`](#sss-rawshares)
86
+ - [`sss rawrecover`](#sss-rawrecover)
87
+ - [`sss rawverify`](#sss-rawverify)
88
88
  - [`doc`](#doc)
89
89
  - [`doc md`](#doc-md)
90
90
  - [Base Library](#base-library)
91
91
  - [Humanized Sizes (IEC binary)](#humanized-sizes-iec-binary)
92
92
  - [Humanized Decimal Quantities (SI)](#humanized-decimal-quantities-si)
93
93
  - [Humanized Durations](#humanized-durations)
94
+ - [Execution Timing](#execution-timing)
95
+ - [Context manager](#context-manager)
96
+ - [Decorator](#decorator)
97
+ - [Manual use](#manual-use)
98
+ - [Key points](#key-points)
99
+ - [Serialization Pipeline](#serialization-pipeline)
100
+ - [Serialize](#serialize)
101
+ - [DeSerialize](#deserialize)
94
102
  - [Cryptographically Secure Randomness](#cryptographically-secure-randomness)
95
103
  - [Fixed-size random integers](#fixed-size-random-integers)
96
104
  - [Uniform random integers in a range](#uniform-random-integers-in-a-range)
97
105
  - [In-place secure shuffle](#in-place-secure-shuffle)
98
106
  - [Random byte strings](#random-byte-strings)
99
107
  - [Computing the Greatest Common Divisor](#computing-the-greatest-common-divisor)
108
+ - [Fast Modular Arithmetic](#fast-modular-arithmetic)
109
+ - [Chinese Remainder Theorem (CRT) – Pair](#chinese-remainder-theorem-crt--pair)
110
+ - [Modular Polynomials \& Lagrange Interpolation](#modular-polynomials--lagrange-interpolation)
111
+ - [Primality testing \& Prime generators, Mersenne primes](#primality-testing--prime-generators-mersenne-primes)
100
112
  - [Cryptographic Hashing](#cryptographic-hashing)
101
113
  - [SHA-256 hashing](#sha-256-hashing)
102
114
  - [SHA-512 hashing](#sha-512-hashing)
103
115
  - [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
116
  - [Symmetric Encryption Interface](#symmetric-encryption-interface)
110
- - [Serialization Pipeline](#serialization-pipeline)
111
- - [Serialize](#serialize)
112
- - [DeSerialize](#deserialize)
113
117
  - [Crypto Objects General Properties (`CryptoKey`)](#crypto-objects-general-properties-cryptokey)
114
118
  - [AES-256 Symmetric Encryption](#aes-256-symmetric-encryption)
115
119
  - [Key creation](#key-creation)
116
120
  - [AES-256 + GCM (default)](#aes-256--gcm-default)
117
121
  - [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
122
  - [RSA (Rivest-Shamir-Adleman) Public Cryptography](#rsa-rivest-shamir-adleman-public-cryptography)
123
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
124
  - [DSA (Digital Signature Algorithm)](#dsa-digital-signature-algorithm)
128
125
  - [Security notes](#security-notes)
129
126
  - [Advanced: custom primes generator](#advanced-custom-primes-generator)
@@ -144,13 +141,13 @@ Unless required by applicable law or agreed to in writing, software distributed
144
141
 
145
142
  ## Design assumptions / Disclaimers
146
143
 
147
- - The library is built to have reference, reliable, simple implementations of math and crypto primitives.
144
+ - The library is built to have reference, reliable, simple implementations of math and crypto primitives (e.g. `RawEncrypt()`/`RawSign()` and friends plus all the low-level primality and modular arithmetic). The issue is not that the library is unsafe, it is that the library is full of places that allow you to shoot yourself in the foot if you don't know what you are doing.
145
+ - The library also has advanced top-level methods that are cryptographically safe and might be used in real-world scenarios (e.g. `Encrypt()`/`Sign()` and friends).
148
146
  - 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.
147
+ - 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 you might consider *other optimized/safe libraries* that were built to be resistant to malicious attacks.
148
+ - *All operations in this library may be vulnerable to timing attacks.* This may be a problem to your use-case or not depending on the situation.
152
149
 
153
- That being said, all care was taken that this is a good library with a solid implementation. *Have fun!*
150
+ All that being said, extreme care was taken that this is a good library with a solid safe implementation. *Have fun!*
154
151
 
155
152
  ## Install
156
153
 
@@ -207,11 +204,11 @@ poetry run transcrypto <command> [sub-command] [options...]
207
204
  - **`mod`** — `poetry run transcrypto mod [-h] {inv,div,exp,poly,lagrange,crt} ...`
208
205
  - **`hash`** — `poetry run transcrypto hash [-h] {sha256,sha512,file} ...`
209
206
  - **`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} ...`
207
+ - **`rsa`** — `poetry run transcrypto rsa [-h] {new,rawencrypt,encrypt,rawdecrypt,decrypt,rawsign,sign,rawverify,verify} ...`
208
+ - **`elgamal`** — `poetry run transcrypto elgamal [-h] {shared,new,rawencrypt,encrypt,rawdecrypt,decrypt,rawsign,sign,rawverify,verify} ...`
209
+ - **`dsa`** — `poetry run transcrypto dsa [-h] {shared,new,rawsign,sign,rawverify,verify} ...`
213
210
  - **`bid`** — `poetry run transcrypto bid [-h] {new,verify} ...`
214
- - **`sss`** — `poetry run transcrypto sss [-h] {new,shares,recover,verify} ...`
211
+ - **`sss`** — `poetry run transcrypto sss [-h] {new,rawshares,shares,rawrecover,recover,rawverify} ...`
215
212
  - **`doc`** — `poetry run transcrypto doc [-h] {md} ...`
216
213
 
217
214
  ```bash
@@ -252,24 +249,24 @@ Examples:
252
249
 
253
250
  # --- RSA ---
254
251
  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>
252
+ poetry run transcrypto -p rsa-key.pub rsa rawencrypt <plaintext>
253
+ poetry run transcrypto -p rsa-key.priv rsa rawdecrypt <ciphertext>
254
+ poetry run transcrypto -p rsa-key.priv rsa rawsign <message>
255
+ poetry run transcrypto -p rsa-key.pub rsa rawverify <message> <signature>
259
256
 
260
257
  # --- ElGamal ---
261
258
  poetry run transcrypto -p eg-key elgamal shared --bits 2048
262
259
  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>
260
+ poetry run transcrypto -p eg-key.pub elgamal rawencrypt <plaintext>
261
+ poetry run transcrypto -p eg-key.priv elgamal rawdecrypt <c1:c2>
262
+ poetry run transcrypto -p eg-key.priv elgamal rawsign <message>
263
+ poetry run transcrypto-p eg-key.pub elgamal rawverify <message> <s1:s2>
267
264
 
268
265
  # --- DSA ---
269
266
  poetry run transcrypto -p dsa-key dsa shared --p-bits 2048 --q-bits 256
270
267
  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>
268
+ poetry run transcrypto -p dsa-key.priv dsa rawsign <message>
269
+ poetry run transcrypto -p dsa-key.pub dsa rawverify <message> <s1:s2>
273
270
 
274
271
  # --- Public Bid ---
275
272
  poetry run transcrypto --bin bid new "tomorrow it will rain"
@@ -277,9 +274,9 @@ Examples:
277
274
 
278
275
  # --- Shamir Secret Sharing (SSS) ---
279
276
  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>
277
+ poetry run transcrypto -p sss-key sss rawshares <secret> 5
278
+ poetry run transcrypto -p sss-key sss rawrecover
279
+ poetry run transcrypto -p sss-key sss rawverify <secret>
283
280
  ```
284
281
 
285
282
  ---
@@ -850,10 +847,11 @@ $ poetry run transcrypto --b64 aes ecb -k DbWJ_ZrknLEEIoq_NpoCQwHYfjskGokpueN2O_
850
847
 
851
848
  ### `rsa`
852
849
 
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).
850
+ RSA (Rivest-Shamir-Adleman) asymmetric cryptography. No measures are taken here to prevent timing attacks. All methods require file key(s) as `-p`/`--key-path` (see provided examples).
854
851
 
855
852
  ```bash
856
- poetry run transcrypto rsa [-h] {new,encrypt,decrypt,sign,verify} ...
853
+ poetry run transcrypto rsa [-h]
854
+ {new,rawencrypt,encrypt,rawdecrypt,decrypt,rawsign,sign,rawverify,verify} ...
857
855
  ```
858
856
 
859
857
  #### `rsa new`
@@ -875,12 +873,12 @@ $ poetry run transcrypto -p rsa-key rsa new --bits 64 # NEVER use such a small
875
873
  RSA private/public keys saved to 'rsa-key.priv/.pub'
876
874
  ```
877
875
 
878
- #### `rsa encrypt`
876
+ #### `rsa rawencrypt`
879
877
 
880
- Encrypt integer `message` with public key.
878
+ Raw encrypt *integer* `message` with public key (BEWARE: no OAEP/PSS padding or validation).
881
879
 
882
880
  ```bash
883
- poetry run transcrypto rsa encrypt [-h] message
881
+ poetry run transcrypto rsa rawencrypt [-h] message
884
882
  ```
885
883
 
886
884
  | Option/Arg | Description |
@@ -890,16 +888,36 @@ poetry run transcrypto rsa encrypt [-h] message
890
888
  **Example:**
891
889
 
892
890
  ```bash
893
- $ poetry run transcrypto -p rsa-key.pub rsa encrypt 999
891
+ $ poetry run transcrypto -p rsa-key.pub rsa rawencrypt 999
894
892
  6354905961171348600
895
893
  ```
896
894
 
897
- #### `rsa decrypt`
895
+ #### `rsa encrypt`
896
+
897
+ Encrypt `message` with public key.
898
+
899
+ ```bash
900
+ poetry run transcrypto rsa encrypt [-h] [-a AAD] plaintext
901
+ ```
902
+
903
+ | Option/Arg | Description |
904
+ |---|---|
905
+ | `plaintext` | Message to encrypt [type: str] |
906
+ | `-a, --aad` | Associated data (optional; has to be separately sent to receiver/stored) [type: str] |
907
+
908
+ **Example:**
909
+
910
+ ```bash
911
+ $ poetry run transcrypto --bin --out-b64 -p rsa-key.pub rsa encrypt "abcde" -a "xyz"
912
+ AO6knI6xwq6TGR…Qy22jiFhXi1eQ==
913
+ ```
898
914
 
899
- Decrypt integer `ciphertext` with private key.
915
+ #### `rsa rawdecrypt`
916
+
917
+ Raw decrypt *integer* `ciphertext` with private key (BEWARE: no OAEP/PSS padding or validation).
900
918
 
901
919
  ```bash
902
- poetry run transcrypto rsa decrypt [-h] ciphertext
920
+ poetry run transcrypto rsa rawdecrypt [-h] ciphertext
903
921
  ```
904
922
 
905
923
  | Option/Arg | Description |
@@ -909,16 +927,36 @@ poetry run transcrypto rsa decrypt [-h] ciphertext
909
927
  **Example:**
910
928
 
911
929
  ```bash
912
- $ poetry run transcrypto -p rsa-key.priv rsa decrypt 6354905961171348600
930
+ $ poetry run transcrypto -p rsa-key.priv rsa rawdecrypt 6354905961171348600
913
931
  999
914
932
  ```
915
933
 
916
- #### `rsa sign`
934
+ #### `rsa decrypt`
935
+
936
+ Decrypt `ciphertext` with private key.
937
+
938
+ ```bash
939
+ poetry run transcrypto rsa decrypt [-h] [-a AAD] ciphertext
940
+ ```
941
+
942
+ | Option/Arg | Description |
943
+ |---|---|
944
+ | `ciphertext` | Ciphertext to decrypt [type: str] |
945
+ | `-a, --aad` | Associated data (optional; has to be exactly the same as used during encryption) [type: str] |
946
+
947
+ **Example:**
948
+
949
+ ```bash
950
+ $ poetry run transcrypto --b64 --out-bin -p rsa-key.priv rsa decrypt "AO6knI6xwq6TGR…Qy22jiFhXi1eQ==" -a "eHl6"
951
+ abcde
952
+ ```
917
953
 
918
- Sign integer `message` with private key.
954
+ #### `rsa rawsign`
955
+
956
+ Raw sign *integer* `message` with private key (BEWARE: no OAEP/PSS padding or validation).
919
957
 
920
958
  ```bash
921
- poetry run transcrypto rsa sign [-h] message
959
+ poetry run transcrypto rsa rawsign [-h] message
922
960
  ```
923
961
 
924
962
  | Option/Arg | Description |
@@ -928,16 +966,36 @@ poetry run transcrypto rsa sign [-h] message
928
966
  **Example:**
929
967
 
930
968
  ```bash
931
- $ poetry run transcrypto -p rsa-key.priv rsa sign 999
969
+ $ poetry run transcrypto -p rsa-key.priv rsa rawsign 999
932
970
  7632909108672871784
933
971
  ```
934
972
 
935
- #### `rsa verify`
973
+ #### `rsa sign`
974
+
975
+ Sign `message` with private key.
976
+
977
+ ```bash
978
+ poetry run transcrypto rsa sign [-h] [-a AAD] message
979
+ ```
980
+
981
+ | Option/Arg | Description |
982
+ |---|---|
983
+ | `message` | Message to sign [type: str] |
984
+ | `-a, --aad` | Associated data (optional; has to be separately sent to receiver/stored) [type: str] |
985
+
986
+ **Example:**
987
+
988
+ ```bash
989
+ $ poetry run transcrypto --bin --out-b64 -p rsa-key.priv rsa sign "xyz"
990
+ 91TS7gC6LORiL…6RD23Aejsfxlw==
991
+ ```
936
992
 
937
- Verify integer `signature` for integer `message` with public key.
993
+ #### `rsa rawverify`
994
+
995
+ Raw verify *integer* `signature` for *integer* `message` with public key (BEWARE: no OAEP/PSS padding or validation).
938
996
 
939
997
  ```bash
940
- poetry run transcrypto rsa verify [-h] message signature
998
+ poetry run transcrypto rsa rawverify [-h] message signature
941
999
  ```
942
1000
 
943
1001
  | Option/Arg | Description |
@@ -948,9 +1006,32 @@ poetry run transcrypto rsa verify [-h] message signature
948
1006
  **Example:**
949
1007
 
950
1008
  ```bash
951
- $ poetry run transcrypto -p rsa-key.pub rsa verify 999 7632909108672871784
1009
+ $ poetry run transcrypto -p rsa-key.pub rsa rawverify 999 7632909108672871784
1010
+ RSA signature: OK
1011
+ $ poetry run transcrypto -p rsa-key.pub rsa rawverify 999 7632909108672871785
1012
+ RSA signature: INVALID
1013
+ ```
1014
+
1015
+ #### `rsa verify`
1016
+
1017
+ Verify `signature` for `message` with public key.
1018
+
1019
+ ```bash
1020
+ poetry run transcrypto rsa verify [-h] [-a AAD] message signature
1021
+ ```
1022
+
1023
+ | Option/Arg | Description |
1024
+ |---|---|
1025
+ | `message` | Message that was signed earlier [type: str] |
1026
+ | `signature` | Putative signature for `message` [type: str] |
1027
+ | `-a, --aad` | Associated data (optional; has to be exactly the same as used during signing) [type: str] |
1028
+
1029
+ **Example:**
1030
+
1031
+ ```bash
1032
+ $ poetry run transcrypto --b64 -p rsa-key.pub rsa verify "eHl6" "91TS7gC6LORiL…6RD23Aejsfxlw=="
952
1033
  RSA signature: OK
953
- $ poetry run transcrypto -p rsa-key.pub rsa verify 999 7632909108672871785
1034
+ $ poetry run transcrypto --b64 -p rsa-key.pub rsa verify "eLl6" "91TS7gC6LORiL…6RD23Aejsfxlw=="
954
1035
  RSA signature: INVALID
955
1036
  ```
956
1037
 
@@ -958,11 +1039,11 @@ RSA signature: INVALID
958
1039
 
959
1040
  ### `elgamal`
960
1041
 
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).
1042
+ El-Gamal asymmetric cryptography. No measures are taken here to prevent timing attacks. All methods require file key(s) as `-p`/`--key-path` (see provided examples).
962
1043
 
963
1044
  ```bash
964
1045
  poetry run transcrypto elgamal [-h]
965
- {shared,new,encrypt,decrypt,sign,verify} ...
1046
+ {shared,new,rawencrypt,encrypt,rawdecrypt,decrypt,rawsign,sign,rawverify,verify} ...
966
1047
  ```
967
1048
 
968
1049
  #### `elgamal shared`
@@ -999,12 +1080,12 @@ $ poetry run transcrypto -p eg-key elgamal new
999
1080
  El-Gamal private/public keys saved to 'eg-key.priv/.pub'
1000
1081
  ```
1001
1082
 
1002
- #### `elgamal encrypt`
1083
+ #### `elgamal rawencrypt`
1003
1084
 
1004
- Encrypt integer `message` with public key.
1085
+ Raw encrypt *integer* `message` with public key (BEWARE: no ECIES-style KEM/DEM padding or validation).
1005
1086
 
1006
1087
  ```bash
1007
- poetry run transcrypto elgamal encrypt [-h] message
1088
+ poetry run transcrypto elgamal rawencrypt [-h] message
1008
1089
  ```
1009
1090
 
1010
1091
  | Option/Arg | Description |
@@ -1014,16 +1095,36 @@ poetry run transcrypto elgamal encrypt [-h] message
1014
1095
  **Example:**
1015
1096
 
1016
1097
  ```bash
1017
- $ poetry run transcrypto -p eg-key.pub elgamal encrypt 999
1098
+ $ poetry run transcrypto -p eg-key.pub elgamal rawencrypt 999
1018
1099
  2948854810728206041:15945988196340032688
1019
1100
  ```
1020
1101
 
1021
- #### `elgamal decrypt`
1102
+ #### `elgamal encrypt`
1103
+
1104
+ Encrypt `message` with public key.
1105
+
1106
+ ```bash
1107
+ poetry run transcrypto elgamal encrypt [-h] [-a AAD] plaintext
1108
+ ```
1109
+
1110
+ | Option/Arg | Description |
1111
+ |---|---|
1112
+ | `plaintext` | Message to encrypt [type: str] |
1113
+ | `-a, --aad` | Associated data (optional; has to be separately sent to receiver/stored) [type: str] |
1022
1114
 
1023
- Decrypt integer `ciphertext` with private key.
1115
+ **Example:**
1116
+
1117
+ ```bash
1118
+ $ poetry run transcrypto --bin --out-b64 -p eg-key.pub elgamal encrypt "abcde" -a "xyz"
1119
+ CdFvoQ_IIPFPZLua…kqjhcUTspISxURg==
1120
+ ```
1121
+
1122
+ #### `elgamal rawdecrypt`
1123
+
1124
+ Raw decrypt *integer* `ciphertext` with private key (BEWARE: no ECIES-style KEM/DEM padding or validation).
1024
1125
 
1025
1126
  ```bash
1026
- poetry run transcrypto elgamal decrypt [-h] ciphertext
1127
+ poetry run transcrypto elgamal rawdecrypt [-h] ciphertext
1027
1128
  ```
1028
1129
 
1029
1130
  | Option/Arg | Description |
@@ -1033,16 +1134,36 @@ poetry run transcrypto elgamal decrypt [-h] ciphertext
1033
1134
  **Example:**
1034
1135
 
1035
1136
  ```bash
1036
- $ poetry run transcrypto -p eg-key.priv elgamal decrypt 2948854810728206041:15945988196340032688
1137
+ $ poetry run transcrypto -p eg-key.priv elgamal rawdecrypt 2948854810728206041:15945988196340032688
1037
1138
  999
1038
1139
  ```
1039
1140
 
1040
- #### `elgamal sign`
1141
+ #### `elgamal decrypt`
1142
+
1143
+ Decrypt `ciphertext` with private key.
1144
+
1145
+ ```bash
1146
+ poetry run transcrypto elgamal decrypt [-h] [-a AAD] ciphertext
1147
+ ```
1148
+
1149
+ | Option/Arg | Description |
1150
+ |---|---|
1151
+ | `ciphertext` | Ciphertext to decrypt [type: str] |
1152
+ | `-a, --aad` | Associated data (optional; has to be exactly the same as used during encryption) [type: str] |
1041
1153
 
1042
- Sign integer message with private key. Output will 2 integers in a `s1:s2` format.
1154
+ **Example:**
1155
+
1156
+ ```bash
1157
+ $ poetry run transcrypto --b64 --out-bin -p eg-key.priv elgamal decrypt "CdFvoQ_IIPFPZLua…kqjhcUTspISxURg==" -a "eHl6"
1158
+ abcde
1159
+ ```
1160
+
1161
+ #### `elgamal rawsign`
1162
+
1163
+ Raw sign *integer* message with private key (BEWARE: no ECIES-style KEM/DEM padding or validation). Output will 2 *integers* in a `s1:s2` format.
1043
1164
 
1044
1165
  ```bash
1045
- poetry run transcrypto elgamal sign [-h] message
1166
+ poetry run transcrypto elgamal rawsign [-h] message
1046
1167
  ```
1047
1168
 
1048
1169
  | Option/Arg | Description |
@@ -1052,16 +1173,36 @@ poetry run transcrypto elgamal sign [-h] message
1052
1173
  **Example:**
1053
1174
 
1054
1175
  ```bash
1055
- $ poetry run transcrypto -p eg-key.priv elgamal sign 999
1176
+ $ poetry run transcrypto -p eg-key.priv elgamal rawsign 999
1056
1177
  4674885853217269088:14532144906178302633
1057
1178
  ```
1058
1179
 
1059
- #### `elgamal verify`
1180
+ #### `elgamal sign`
1181
+
1182
+ Sign message with private key.
1183
+
1184
+ ```bash
1185
+ poetry run transcrypto elgamal sign [-h] [-a AAD] message
1186
+ ```
1187
+
1188
+ | Option/Arg | Description |
1189
+ |---|---|
1190
+ | `message` | Message to sign [type: str] |
1191
+ | `-a, --aad` | Associated data (optional; has to be separately sent to receiver/stored) [type: str] |
1060
1192
 
1061
- Verify integer `signature` for integer `message` with public key.
1193
+ **Example:**
1194
+
1195
+ ```bash
1196
+ $ poetry run transcrypto --bin --out-b64 -p eg-key.priv elgamal sign "xyz"
1197
+ Xl4hlYK8SHVGw…0fCKJE1XVzA==
1198
+ ```
1199
+
1200
+ #### `elgamal rawverify`
1201
+
1202
+ Raw verify *integer* `signature` for *integer* `message` with public key (BEWARE: no ECIES-style KEM/DEM padding or validation).
1062
1203
 
1063
1204
  ```bash
1064
- poetry run transcrypto elgamal verify [-h] message signature
1205
+ poetry run transcrypto elgamal rawverify [-h] message signature
1065
1206
  ```
1066
1207
 
1067
1208
  | Option/Arg | Description |
@@ -1072,9 +1213,32 @@ poetry run transcrypto elgamal verify [-h] message signature
1072
1213
  **Example:**
1073
1214
 
1074
1215
  ```bash
1075
- $ poetry run transcrypto -p eg-key.pub elgamal verify 999 4674885853217269088:14532144906178302633
1216
+ $ poetry run transcrypto -p eg-key.pub elgamal rawverify 999 4674885853217269088:14532144906178302633
1217
+ El-Gamal signature: OK
1218
+ $ poetry run transcrypto -p eg-key.pub elgamal rawverify 999 4674885853217269088:14532144906178302632
1219
+ El-Gamal signature: INVALID
1220
+ ```
1221
+
1222
+ #### `elgamal verify`
1223
+
1224
+ Verify `signature` for `message` with public key.
1225
+
1226
+ ```bash
1227
+ poetry run transcrypto elgamal verify [-h] [-a AAD] message signature
1228
+ ```
1229
+
1230
+ | Option/Arg | Description |
1231
+ |---|---|
1232
+ | `message` | Message that was signed earlier [type: str] |
1233
+ | `signature` | Putative signature for `message` [type: str] |
1234
+ | `-a, --aad` | Associated data (optional; has to be exactly the same as used during signing) [type: str] |
1235
+
1236
+ **Example:**
1237
+
1238
+ ```bash
1239
+ $ poetry run transcrypto --b64 -p eg-key.pub elgamal verify "eHl6" "Xl4hlYK8SHVGw…0fCKJE1XVzA=="
1076
1240
  El-Gamal signature: OK
1077
- $ poetry run transcrypto -p eg-key.pub elgamal verify 999 4674885853217269088:14532144906178302632
1241
+ $ poetry run transcrypto --b64 -p eg-key.pub elgamal verify "eLl6" "Xl4hlYK8SHVGw…0fCKJE1XVzA=="
1078
1242
  El-Gamal signature: INVALID
1079
1243
  ```
1080
1244
 
@@ -1082,15 +1246,16 @@ El-Gamal signature: INVALID
1082
1246
 
1083
1247
  ### `dsa`
1084
1248
 
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).
1249
+ DSA (Digital Signature Algorithm) asymmetric signing/verifying. No measures are taken here to prevent timing attacks. All methods require file key(s) as `-p`/`--key-path` (see provided examples).
1086
1250
 
1087
1251
  ```bash
1088
- poetry run transcrypto dsa [-h] {shared,new,sign,verify} ...
1252
+ poetry run transcrypto dsa [-h]
1253
+ {shared,new,rawsign,sign,rawverify,verify} ...
1089
1254
  ```
1090
1255
 
1091
1256
  #### `dsa shared`
1092
1257
 
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.
1258
+ 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. 4096/544). 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
1259
 
1095
1260
  ```bash
1096
1261
  poetry run transcrypto dsa shared [-h] [--p-bits P_BITS]
@@ -1099,8 +1264,8 @@ poetry run transcrypto dsa shared [-h] [--p-bits P_BITS]
1099
1264
 
1100
1265
  | Option/Arg | Description |
1101
1266
  |---|---|
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)] |
1267
+ | `--p-bits` | Prime modulus (`p`) size in bits; the default is a safe size [type: int (default: 4096)] |
1268
+ | `--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: 544)] |
1104
1269
 
1105
1270
  **Example:**
1106
1271
 
@@ -1124,12 +1289,12 @@ $ poetry run transcrypto -p dsa-key dsa new
1124
1289
  DSA private/public keys saved to 'dsa-key.priv/.pub'
1125
1290
  ```
1126
1291
 
1127
- #### `dsa sign`
1292
+ #### `dsa rawsign`
1128
1293
 
1129
- Sign integer message with private key. Output will 2 integers in a `s1:s2` format.
1294
+ Raw sign *integer* message with private key (BEWARE: no ECDSA/EdDSA padding or validation). Output will 2 *integers* in a `s1:s2` format.
1130
1295
 
1131
1296
  ```bash
1132
- poetry run transcrypto dsa sign [-h] message
1297
+ poetry run transcrypto dsa rawsign [-h] message
1133
1298
  ```
1134
1299
 
1135
1300
  | Option/Arg | Description |
@@ -1139,16 +1304,36 @@ poetry run transcrypto dsa sign [-h] message
1139
1304
  **Example:**
1140
1305
 
1141
1306
  ```bash
1142
- $ poetry run transcrypto -p dsa-key.priv dsa sign 999
1307
+ $ poetry run transcrypto -p dsa-key.priv dsa rawsign 999
1143
1308
  2395961484:3435572290
1144
1309
  ```
1145
1310
 
1146
- #### `dsa verify`
1311
+ #### `dsa sign`
1312
+
1313
+ Sign message with private key.
1314
+
1315
+ ```bash
1316
+ poetry run transcrypto dsa sign [-h] [-a AAD] message
1317
+ ```
1318
+
1319
+ | Option/Arg | Description |
1320
+ |---|---|
1321
+ | `message` | Message to sign [type: str] |
1322
+ | `-a, --aad` | Associated data (optional; has to be separately sent to receiver/stored) [type: str] |
1323
+
1324
+ **Example:**
1325
+
1326
+ ```bash
1327
+ $ poetry run transcrypto --bin --out-b64 -p dsa-key.priv dsa sign "xyz"
1328
+ yq8InJVpViXh9…BD4par2XuA=
1329
+ ```
1147
1330
 
1148
- Verify integer `signature` for integer `message` with public key.
1331
+ #### `dsa rawverify`
1332
+
1333
+ Raw verify *integer* `signature` for *integer* `message` with public key (BEWARE: no ECDSA/EdDSA padding or validation).
1149
1334
 
1150
1335
  ```bash
1151
- poetry run transcrypto dsa verify [-h] message signature
1336
+ poetry run transcrypto dsa rawverify [-h] message signature
1152
1337
  ```
1153
1338
 
1154
1339
  | Option/Arg | Description |
@@ -1159,9 +1344,32 @@ poetry run transcrypto dsa verify [-h] message signature
1159
1344
  **Example:**
1160
1345
 
1161
1346
  ```bash
1162
- $ poetry run transcrypto -p dsa-key.pub dsa verify 999 2395961484:3435572290
1347
+ $ poetry run transcrypto -p dsa-key.pub dsa rawverify 999 2395961484:3435572290
1348
+ DSA signature: OK
1349
+ $ poetry run transcrypto -p dsa-key.pub dsa rawverify 999 2395961484:3435572291
1350
+ DSA signature: INVALID
1351
+ ```
1352
+
1353
+ #### `dsa verify`
1354
+
1355
+ Verify `signature` for `message` with public key.
1356
+
1357
+ ```bash
1358
+ poetry run transcrypto dsa verify [-h] [-a AAD] message signature
1359
+ ```
1360
+
1361
+ | Option/Arg | Description |
1362
+ |---|---|
1363
+ | `message` | Message that was signed earlier [type: str] |
1364
+ | `signature` | Putative signature for `message` [type: str] |
1365
+ | `-a, --aad` | Associated data (optional; has to be exactly the same as used during signing) [type: str] |
1366
+
1367
+ **Example:**
1368
+
1369
+ ```bash
1370
+ $ poetry run transcrypto --b64 -p dsa-key.pub dsa verify "eHl6" "yq8InJVpViXh9…BD4par2XuA="
1163
1371
  DSA signature: OK
1164
- $ poetry run transcrypto -p dsa-key.pub dsa verify 999 2395961484:3435572291
1372
+ $ poetry run transcrypto --b64 -p dsa-key.pub dsa verify "eLl6" "yq8InJVpViXh9…BD4par2XuA="
1165
1373
  DSA signature: INVALID
1166
1374
  ```
1167
1375
 
@@ -1215,10 +1423,11 @@ tomorrow it will rain
1215
1423
 
1216
1424
  ### `sss`
1217
1425
 
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).
1426
+ SSS (Shamir Shared Secret) secret sharing crypto scheme. No measures are taken here to prevent timing attacks. All methods require file key(s) as `-p`/`--key-path` (see provided examples).
1219
1427
 
1220
1428
  ```bash
1221
- poetry run transcrypto sss [-h] {new,shares,recover,verify} ...
1429
+ poetry run transcrypto sss [-h]
1430
+ {new,rawshares,shares,rawrecover,recover,rawverify} ...
1222
1431
  ```
1223
1432
 
1224
1433
  #### `sss new`
@@ -1241,9 +1450,30 @@ $ poetry run transcrypto -p sss-key sss new 3 --bits 64 # NEVER use such a smal
1241
1450
  SSS private/public keys saved to 'sss-key.priv/.pub'
1242
1451
  ```
1243
1452
 
1453
+ #### `sss rawshares`
1454
+
1455
+ Raw shares: Issue `count` private shares for an *integer* `secret` (BEWARE: no modern message wrapping, padding or validation).
1456
+
1457
+ ```bash
1458
+ poetry run transcrypto sss rawshares [-h] secret count
1459
+ ```
1460
+
1461
+ | Option/Arg | Description |
1462
+ |---|---|
1463
+ | `secret` | Integer secret to be protected, 1≤`secret`<*modulus* [type: str] |
1464
+ | `count` | How many shares to produce; must be ≥ `minimum` used in `new` command or else the `secret` would become unrecoverable [type: int] |
1465
+
1466
+ **Example:**
1467
+
1468
+ ```bash
1469
+ $ poetry run transcrypto -p sss-key sss rawshares 999 5
1470
+ SSS 5 individual (private) shares saved to 'sss-key.share.1…5'
1471
+ $ rm sss-key.share.2 sss-key.share.4 # this is to simulate only having shares 1,3,5
1472
+ ```
1473
+
1244
1474
  #### `sss shares`
1245
1475
 
1246
- Issue `count` private shares for an integer `secret`.
1476
+ Shares: Issue `count` private shares for a `secret`.
1247
1477
 
1248
1478
  ```bash
1249
1479
  poetry run transcrypto sss shares [-h] secret count
@@ -1251,17 +1481,36 @@ poetry run transcrypto sss shares [-h] secret count
1251
1481
 
1252
1482
  | Option/Arg | Description |
1253
1483
  |---|---|
1254
- | `secret` | Integer secret to be protected, 1≤`secret`<*modulus* [type: str] |
1484
+ | `secret` | Secret to be protected [type: str] |
1255
1485
  | `count` | How many shares to produce; must be ≥ `minimum` used in `new` command or else the `secret` would become unrecoverable [type: int] |
1256
1486
 
1257
1487
  **Example:**
1258
1488
 
1259
1489
  ```bash
1260
- $ poetry run transcrypto -p sss-key sss shares 999 5
1490
+ $ poetry run transcrypto --bin -p sss-key sss shares "abcde" 5
1261
1491
  SSS 5 individual (private) shares saved to 'sss-key.share.1…5'
1262
1492
  $ rm sss-key.share.2 sss-key.share.4 # this is to simulate only having shares 1,3,5
1263
1493
  ```
1264
1494
 
1495
+ #### `sss rawrecover`
1496
+
1497
+ Raw recover *integer* secret from shares; will use any available shares that were found (BEWARE: no modern message wrapping, padding or validation).
1498
+
1499
+ ```bash
1500
+ poetry run transcrypto sss rawrecover [-h]
1501
+ ```
1502
+
1503
+ **Example:**
1504
+
1505
+ ```bash
1506
+ $ poetry run transcrypto -p sss-key sss rawrecover
1507
+ Loaded SSS share: 'sss-key.share.3'
1508
+ Loaded SSS share: 'sss-key.share.5'
1509
+ Loaded SSS share: 'sss-key.share.1' # using only 3 shares: number 2/4 are missing
1510
+ Secret:
1511
+ 999
1512
+ ```
1513
+
1265
1514
  #### `sss recover`
1266
1515
 
1267
1516
  Recover secret from shares; will use any available shares that were found.
@@ -1273,34 +1522,34 @@ poetry run transcrypto sss recover [-h]
1273
1522
  **Example:**
1274
1523
 
1275
1524
  ```bash
1276
- $ poetry run transcrypto -p sss-key sss recover
1525
+ $ poetry run transcrypto --out-bin -p sss-key sss recover
1277
1526
  Loaded SSS share: 'sss-key.share.3'
1278
1527
  Loaded SSS share: 'sss-key.share.5'
1279
1528
  Loaded SSS share: 'sss-key.share.1' # using only 3 shares: number 2/4 are missing
1280
1529
  Secret:
1281
- 999
1530
+ abcde
1282
1531
  ```
1283
1532
 
1284
- #### `sss verify`
1533
+ #### `sss rawverify`
1285
1534
 
1286
- Verify shares against a secret (private params).
1535
+ Raw verify shares against a secret (private params; BEWARE: no modern message wrapping, padding or validation).
1287
1536
 
1288
1537
  ```bash
1289
- poetry run transcrypto sss verify [-h] secret
1538
+ poetry run transcrypto sss rawverify [-h] secret
1290
1539
  ```
1291
1540
 
1292
1541
  | Option/Arg | Description |
1293
1542
  |---|---|
1294
- | `secret` | Integer secret used to generate the shares, 1≤`secret`<*modulus* [type: str] |
1543
+ | `secret` | Integer secret used to generate the shares [type: str] |
1295
1544
 
1296
1545
  **Example:**
1297
1546
 
1298
1547
  ```bash
1299
- $ poetry run transcrypto -p sss-key sss verify 999
1548
+ $ poetry run transcrypto -p sss-key sss rawverify 999
1300
1549
  SSS share 'sss-key.share.3' verification: OK
1301
1550
  SSS share 'sss-key.share.5' verification: OK
1302
1551
  SSS share 'sss-key.share.1' verification: OK
1303
- $ poetry run transcrypto -p sss-key sss verify 998
1552
+ $ poetry run transcrypto -p sss-key sss rawverify 998
1304
1553
  SSS share 'sss-key.share.3' verification: INVALID
1305
1554
  SSS share 'sss-key.share.5' verification: INVALID
1306
1555
  SSS share 'sss-key.share.1' verification: INVALID
@@ -1396,169 +1645,39 @@ Chooses an appropriate time unit based on magnitude and formats with fixed preci
1396
1645
  - special case: `0 → '0.00 s'`
1397
1646
  - errors: negative or non-finite inputs raise `InputError`
1398
1647
 
1399
- #### Cryptographically Secure Randomness
1648
+ #### Execution Timing
1400
1649
 
1401
- These helpers live in `base` and wrap Python’s `secrets` with additional checks and guarantees for crypto use-cases.
1650
+ A flexible timing utility that works as a **context manager**, **decorator**, or **manual timer object**.
1402
1651
 
1403
1652
  ```py
1404
1653
  from transcrypto import base
1654
+ import time
1405
1655
  ```
1406
1656
 
1407
- ##### Fixed-size random integers
1657
+ ##### Context manager
1408
1658
 
1409
1659
  ```py
1410
- # Generate a 256-bit integer (first bit always set)
1411
- r = base.RandBits(256)
1412
- assert r.bit_length() == 256
1660
+ with base.Timer('Block timing'):
1661
+ time.sleep(1.2)
1662
+ # → logs: "Block timing: 1.20 s" (default via logging.info)
1413
1663
  ```
1414
1664
 
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`
1665
+ Starts timing on entry, stops on exit, and reports elapsed time automatically.
1418
1666
 
1419
- ##### Uniform random integers in a range
1667
+ ##### Decorator
1420
1668
 
1421
1669
  ```py
1422
- # Uniform between [10, 20] inclusive
1423
- n = base.RandInt(10, 20)
1424
- assert 10 <= n <= 20
1425
- ```
1670
+ @base.Timer('Function timing')
1671
+ def slow_function():
1672
+ time.sleep(0.8)
1426
1673
 
1427
- Returns a crypto-secure integer uniformly distributed over the closed interval `[min_int, max_int]`.
1674
+ slow_function()
1675
+ # → logs: "Function timing: 0.80 s"
1676
+ ```
1428
1677
 
1429
- - constraints: `min_int 0` and `< max_int`
1430
- - errors: invalid bounds → `InputError`
1678
+ Wraps a function so that each call is automatically timed.
1431
1679
 
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
1680
+ ##### Manual use
1562
1681
 
1563
1682
  ```py
1564
1683
  tm = base.Timer('Inline timing', emit_print=True)
@@ -1582,21 +1701,6 @@ Manual control over `Start()` and `Stop()` for precise measurement of custom int
1582
1701
  - Cannot stop an unstarted or already stopped timer
1583
1702
  (raises `Error`)
1584
1703
 
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
1704
  #### Serialization Pipeline
1601
1705
 
1602
1706
  These helpers turn arbitrary Python objects into compressed and/or encrypted binary blobs, and back again — with detailed timing and size logging.
@@ -1679,114 +1783,89 @@ data/file → (decrypt) → (decompress if Zstd) → unpickle
1679
1783
  - `file_path` must exist; `data` must be at least 4 bytes.
1680
1784
  - Wrong key or corrupted data can raise `CryptoError`.
1681
1785
 
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.
1786
+ #### Cryptographically Secure Randomness
1691
1787
 
1692
- Example:
1788
+ These helpers live in `base` and wrap Python’s `secrets` with additional checks and guarantees for crypto use-cases.
1693
1789
 
1694
- <!-- cspell:disable -->
1695
1790
  ```py
1696
- from transcrypto import base, rsa, aes
1791
+ from transcrypto import base
1792
+ ```
1697
1793
 
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…)
1794
+ ##### Fixed-size random integers
1701
1795
 
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)
1796
+ ```py
1797
+ # Generate a 256-bit integer (first bit always set)
1798
+ r = base.RandBits(256)
1799
+ assert r.bit_length() == 256
1800
+ ```
1704
1801
 
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."
1802
+ 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.
1707
1803
 
1708
- print(priv.encoded)
1709
- # ▶ KLUv_WBwAIELAIAElWUBAAAAAAAAjA90cmFuc2NyeXB0by5yc2GUjA1SU0FQcml2YXRlS2V5lJOUKYGUXZQoikHf1EvsmZedAZve7TrLmobLAwuRIr_77TLG6G_0fsLGThERVJu075be8PLjUQYnLXcacZFQ5Fb1Iy1WtiE985euAEoBAAEAiiFR9ngiXMzkf41o5CRBY3h0D4DJVisDDhLmAWsiaHggzQCKIS_cmQ6MKXCtROtC7c_Mrsi9A-9NM8DksaHaRwvy6uTZAIpB4TVbsLxc41TEc19wIzpxbi9y5dW5gdfTkRQSSiz0ijmb8Xk3pyBfKAv8JbHp8Yv48gNZUfX67qq0J7yhJqeUoACKIbFb2kTNRzSqm3JRtjc2BPS-FnLFdadlFcV4-6IW7eqLAIogFZfzDN39gZLR9uTz4KHSTaqxWrJgP8-YYssjss6FlFKKIIItgCDv7ompNpY8gBs5bibN8XTsr-JOYSntDVT5Fe5vZWIu
1804
+ - errors: `n_bits < 8` → `InputError`
1710
1805
 
1711
- key = aes.AESKey(key256=b'x' * 32)
1712
- print(key)
1713
- # ▶ AESKey(key256=86a86df7…)
1806
+ ##### Uniform random integers in a range
1714
1807
 
1715
- encrypted = priv.Blob(key=key)
1716
- print(priv == rsa.RSAPrivateKey.Load(encrypted, key=key))
1717
- # True
1808
+ ```py
1809
+ # Uniform between [10, 20] inclusive
1810
+ n = base.RandInt(10, 20)
1811
+ assert 10 <= n <= 20
1718
1812
  ```
1719
- <!-- cspell:enable -->
1720
1813
 
1721
- #### AES-256 Symmetric Encryption
1814
+ Returns a crypto-secure integer uniformly distributed over the closed interval `[min_int, max_int]`.
1722
1815
 
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.
1816
+ - constraints: `min_int 0` and `< max_int`
1817
+ - errors: invalid bounds `InputError`
1725
1818
 
1726
- ##### Key creation
1819
+ ##### In-place secure shuffle
1727
1820
 
1728
1821
  ```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
1822
+ deck = list(range(10))
1823
+ base.RandShuffle(deck)
1824
+ print(deck) # securely shuffled order
1737
1825
  ```
1738
1826
 
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)
1827
+ Performs an in-place Fisher–Yates shuffle using `secrets.randbelow`. Suitable for sensitive data ordering.
1745
1828
 
1746
- ```py
1747
- data = b'secret message'
1748
- aad = b'metadata'
1829
+ - constraints: sequence length ≥ 2
1830
+ - errors: shorter sequences → `InputError`
1749
1831
 
1750
- # Encrypt (returns IV + ciphertext + tag)
1751
- ct = key.Encrypt(data, associated_data=aad)
1832
+ ##### Random byte strings
1752
1833
 
1753
- # Decrypt
1754
- pt = key.Decrypt(ct, associated_data=aad)
1755
- assert pt == data
1834
+ ```py
1835
+ # 32 random bytes
1836
+ b = base.RandBytes(32)
1837
+ assert len(b) == 32
1756
1838
  ```
1757
1839
 
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`
1840
+ Generates `n_bytes` of high-quality crypto-secure random data.
1764
1841
 
1765
- ##### AES-256 + ECB (unsafe, fixed block only)
1842
+ - constraints: `n_bytes 1`
1843
+ - errors: smaller values → `InputError`
1844
+
1845
+ #### Computing the Greatest Common Divisor
1766
1846
 
1767
1847
  ```py
1768
- # ECB mode is for 16-byte block encoding ONLY
1769
- ecb = key.ECBEncoder()
1848
+ >>> from transcrypto import base
1849
+ >>> base.GCD(462, 1071)
1850
+ 21
1851
+ >>> base.GCD(0, 17)
1852
+ 17
1853
+ ```
1770
1854
 
1771
- block = b'16-byte string!!'
1772
- ct_block = ecb.Encrypt(block)
1773
- pt_block = ecb.Decrypt(ct_block)
1774
- assert pt_block == block
1855
+ 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:
1775
1856
 
1776
- # Hex helpers
1777
- hex_ct = ecb.EncryptHex('00112233445566778899aabbccddeeff')
1857
+ ```py
1858
+ >>> base.ExtendedGCD(462, 1071)
1859
+ (21, -2, 1)
1860
+ >>> 462 * -2 + 1071 * 1
1861
+ 21
1778
1862
  ```
1779
1863
 
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:
1864
+ Use-cases:
1786
1865
 
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
1866
+ - modular inverses: `inv = x % m` when `gcd(a, m) == 1`
1867
+ - solving linear Diophantine equations
1868
+ - RSA / ECC key generation internals
1790
1869
 
1791
1870
  #### Fast Modular Arithmetic
1792
1871
 
@@ -1809,7 +1888,7 @@ assert (z * y) % m == x % m
1809
1888
  exp = modmath.ModExp(3, 10**20, m) # ≈ log₂(y) time, handles huge exponents
1810
1889
  ```
1811
1890
 
1812
- #### Chinese Remainder Theorem (CRT) – Pair
1891
+ ##### Chinese Remainder Theorem (CRT) – Pair
1813
1892
 
1814
1893
  ```py
1815
1894
  from transcrypto import modmath
@@ -1840,7 +1919,7 @@ x ≡ a2 (mod m2)
1840
1919
 
1841
1920
  This function is a 2-modulus variant; for multiple moduli, apply it iteratively or use a general CRT solver.
1842
1921
 
1843
- #### Modular Polynomials & Lagrange Interpolation
1922
+ ##### Modular Polynomials & Lagrange Interpolation
1844
1923
 
1845
1924
  ```py
1846
1925
  # f(t) = 7t³ − 3t² + 2t + 5 (coefficients constant-term first)
@@ -1876,6 +1955,176 @@ for k, m_p, perfect in modmath.MersennePrimesGenerator(0):
1876
1955
  break
1877
1956
  ```
1878
1957
 
1958
+ #### Cryptographic Hashing
1959
+
1960
+ Simple, fixed-output-size wrappers over Python’s `hashlib` for common digest operations, plus file hashing.
1961
+
1962
+ ```py
1963
+ from transcrypto import base
1964
+ ```
1965
+
1966
+ ##### SHA-256 hashing
1967
+
1968
+ ```py
1969
+ h = base.Hash256(b'hello world')
1970
+ assert len(h) == 32 # bytes
1971
+ print(h.hex()) # 64 hex chars
1972
+ ```
1973
+
1974
+ Computes the SHA-256 digest of a byte string, returning exactly 32 bytes (256 bits). Suitable for fingerprints, commitments, or internal crypto primitives.
1975
+
1976
+ ##### SHA-512 hashing
1977
+
1978
+ ```py
1979
+ h = base.Hash512(b'hello world')
1980
+ assert len(h) == 64 # bytes
1981
+ print(h.hex()) # 128 hex chars
1982
+ ```
1983
+
1984
+ 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.
1985
+
1986
+ ##### File hashing
1987
+
1988
+ ```py
1989
+ # Default SHA-256
1990
+ fh = base.FileHash('/path/to/file')
1991
+ print(fh.hex())
1992
+
1993
+ # SHA-512
1994
+ fh2 = base.FileHash('/path/to/file', digest='sha512')
1995
+ ```
1996
+
1997
+ Hashes a file from disk in streaming mode. By default uses SHA-256; `digest='sha512'` switches to SHA-512.
1998
+
1999
+ - constraints:
2000
+ - `digest` must be `'sha256'` or `'sha512'`
2001
+ - `full_path` must exist
2002
+ - errors: invalid digest or missing file → `InputError`
2003
+
2004
+ #### Symmetric Encryption Interface
2005
+
2006
+ `SymmetricCrypto` is an abstract base class that defines the **byte-in / byte-out** contract for symmetric ciphers.
2007
+
2008
+ - **Metadata handling** — if the algorithm uses a `nonce` or `tag`, the implementation must handle it internally (e.g., append it to ciphertext).
2009
+ - **AEAD modes** — if supported, `associated_data` must be authenticated; otherwise, a non-`None` value should raise `InputError`.
2010
+
2011
+ ```py
2012
+ class MyAES(base.SymmetricCrypto):
2013
+ def Encrypt(self, plaintext: bytes, *, associated_data=None) -> bytes:
2014
+ ...
2015
+ def Decrypt(self, ciphertext: bytes, *, associated_data=None) -> bytes:
2016
+ ...
2017
+ ```
2018
+
2019
+ #### Crypto Objects General Properties (`CryptoKey`)
2020
+
2021
+ Cryptographic objects all derive from the `CryptoKey` class and will all have some important characteristics:
2022
+
2023
+ - 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…`).
2024
+ - It will have a `_DebugDump()` method that **does print secrets** and can be used for **debugging only**.
2025
+ - Can be easily serialized to `bytes` by the `blob` property and to base-64 encoded `str` by the `encoded` property.
2026
+ - 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.
2027
+ - 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.
2028
+
2029
+ Example:
2030
+
2031
+ <!-- cspell:disable -->
2032
+ ```py
2033
+ from transcrypto import base, rsa, aes
2034
+
2035
+ priv = rsa.RSAPrivateKey.New(512) # small key, but good for this example
2036
+ print(str(priv)) # safe, no secrets
2037
+ # ▶ RSAPrivateKey(RSAPublicKey(public_modulus=pQaoxy-QeXSds1k9WsGjJw==, encrypt_exp=AQAB), modulus_p=f18141aa…, modulus_q=67494eb9…, decrypt_exp=c96db24a…)
2038
+
2039
+ print(priv._DebugDump()) # UNSAFE: prints secrets
2040
+ # ▶ 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)
2041
+
2042
+ print(priv.blob)
2043
+ # ▶ 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."
2044
+
2045
+ print(priv.encoded)
2046
+ # ▶ KLUv_WBwAIELAIAElWUBAAAAAAAAjA90cmFuc2NyeXB0by5yc2GUjA1SU0FQcml2YXRlS2V5lJOUKYGUXZQoikHf1EvsmZedAZve7TrLmobLAwuRIr_77TLG6G_0fsLGThERVJu075be8PLjUQYnLXcacZFQ5Fb1Iy1WtiE985euAEoBAAEAiiFR9ngiXMzkf41o5CRBY3h0D4DJVisDDhLmAWsiaHggzQCKIS_cmQ6MKXCtROtC7c_Mrsi9A-9NM8DksaHaRwvy6uTZAIpB4TVbsLxc41TEc19wIzpxbi9y5dW5gdfTkRQSSiz0ijmb8Xk3pyBfKAv8JbHp8Yv48gNZUfX67qq0J7yhJqeUoACKIbFb2kTNRzSqm3JRtjc2BPS-FnLFdadlFcV4-6IW7eqLAIogFZfzDN39gZLR9uTz4KHSTaqxWrJgP8-YYssjss6FlFKKIIItgCDv7ompNpY8gBs5bibN8XTsr-JOYSntDVT5Fe5vZWIu
2047
+
2048
+ key = aes.AESKey(key256=b'x' * 32)
2049
+ print(key)
2050
+ # ▶ AESKey(key256=86a86df7…)
2051
+
2052
+ encrypted = priv.Blob(key=key)
2053
+ print(priv == rsa.RSAPrivateKey.Load(encrypted, key=key))
2054
+ # ▶ True
2055
+ ```
2056
+ <!-- cspell:enable -->
2057
+
2058
+ #### AES-256 Symmetric Encryption
2059
+
2060
+ Implements AES-256 in **GCM mode** for authenticated encryption and decryption, plus an **ECB mode** helper for fixed-size block encoding.
2061
+ Also includes a high-iteration PBKDF2-based key derivation from static passwords.
2062
+
2063
+ ##### Key creation
2064
+
2065
+ ```py
2066
+ from transcrypto import aes
2067
+
2068
+ # From raw bytes (must be exactly 32 bytes)
2069
+ key = aes.AESKey(key256=b'\x00' * 32)
2070
+
2071
+ # From a static password (slow, high-iteration PBKDF2-SHA256)
2072
+ key = aes.AESKey.FromStaticPassword('correct horse battery staple')
2073
+ print(key.encoded) # URL-safe Base64
2074
+ ```
2075
+
2076
+ - **Length**: `key256` must be exactly 32 bytes
2077
+ - `FromStaticPassword()`:
2078
+ - Uses PBKDF2-HMAC-SHA256 with **fixed** salt and \~2 million iterations
2079
+ - Designed for **interactive** password entry, **not** for password databases
2080
+
2081
+ ##### AES-256 + GCM (default)
2082
+
2083
+ ```py
2084
+ data = b'secret message'
2085
+ aad = b'metadata'
2086
+
2087
+ # Encrypt (returns IV + ciphertext + tag)
2088
+ ct = key.Encrypt(data, associated_data=aad)
2089
+
2090
+ # Decrypt
2091
+ pt = key.Decrypt(ct, associated_data=aad)
2092
+ assert pt == data
2093
+ ```
2094
+
2095
+ - **Security**:
2096
+ - Random 128-bit IV (`iv`) per encryption
2097
+ - Authenticated tag (128-bit) ensures integrity
2098
+ - Optional `associated_data` is authenticated but not encrypted
2099
+ - **Errors**:
2100
+ - Tag mismatch or wrong key → `CryptoError`
2101
+
2102
+ ##### AES-256 + ECB (unsafe, fixed block only)
2103
+
2104
+ ```py
2105
+ # ECB mode is for 16-byte block encoding ONLY
2106
+ ecb = key.ECBEncoder()
2107
+
2108
+ block = b'16-byte string!!'
2109
+ ct_block = ecb.Encrypt(block)
2110
+ pt_block = ecb.Decrypt(ct_block)
2111
+ assert pt_block == block
2112
+
2113
+ # Hex helpers
2114
+ hex_ct = ecb.EncryptHex('00112233445566778899aabbccddeeff')
2115
+ ```
2116
+
2117
+ - **ECB mode**:
2118
+ - 16-byte plaintext ↔ 16-byte ciphertext
2119
+ - No padding, no IV, no integrity — **do not use for general encryption**
2120
+ - `associated_data` not supported
2121
+
2122
+ Key points:
2123
+
2124
+ - **GCM mode** is secure for general use; ECB mode is for special low-level operations
2125
+ - **Static password derivation** is intentionally slow to resist brute force
2126
+ - All sizes and parameters are validated with `InputError` on misuse
2127
+
1879
2128
  #### RSA (Rivest-Shamir-Adleman) Public Cryptography
1880
2129
 
1881
2130
  <https://en.wikipedia.org/wiki/RSA_cryptosystem>
@@ -1888,28 +2137,38 @@ By default and deliberate choice the *encryption exponent* will be either 7 or 6
1888
2137
  from transcrypto import rsa
1889
2138
 
1890
2139
  # Generate a key pair
1891
- priv = rsa.RSAPrivateKey.New(2048) # 2048-bit modulus
1892
- pub = rsa.RSAPublicKey.Copy(priv) # public half
2140
+ priv = rsa.RSAPrivateKey.New(2048) # 2048-bit modulus
2141
+ pub = rsa.RSAPublicKey.Copy(priv) # public half
1893
2142
  print(priv.public_modulus.bit_length()) # 2048
1894
2143
 
1895
- #Encrypt & decrypt
2144
+ # Safe Encrypt & decrypt
2145
+ msg = b'xyz'
2146
+ cipher = pub.Encrypt(msg, associated_data=b'aad')
2147
+ plain = priv.Decrypt(cipher, associated_data=b'aad')
2148
+ assert plain == msg
2149
+
2150
+ # Safe Sign & verify
2151
+ signature = priv.Sign(msg) # can also have associated_data, optionally
2152
+ assert pub.Verify(msg, signature)
2153
+
2154
+ # Raw Encrypt & decrypt
1896
2155
  msg = 123456789 # (Zero is forbidden by design; smallest valid message is 1.)
1897
- cipher = pub.Encrypt(msg)
1898
- plain = priv.Decrypt(cipher)
2156
+ cipher = pub.RawEncrypt(msg)
2157
+ plain = priv.RawDecrypt(cipher)
1899
2158
  assert plain == msg
1900
2159
 
1901
- # Sign & verify
1902
- signature = priv.Sign(msg)
1903
- assert pub.VerifySignature(msg, signature)
2160
+ # Raw Sign & verify
2161
+ signature = priv.RawSign(msg)
2162
+ assert pub.RawVerify(msg, signature)
1904
2163
 
1905
2164
  # Blind signatures (obfuscation pair) - only works on raw RSA
1906
2165
  pair = rsa.RSAObfuscationPair.New(pub)
1907
2166
 
1908
2167
  blind_msg = pair.ObfuscateMessage(msg) # what you send to signer
1909
- blind_sig = priv.Sign(blind_msg) # signer’s output
2168
+ blind_sig = priv.RawSign(blind_msg) # signer’s output
1910
2169
 
1911
2170
  sig = pair.RevealOriginalSignature(msg, blind_sig)
1912
- assert pub.VerifySignature(msg, sig)
2171
+ assert pub.RawVerify(msg, sig)
1913
2172
  ```
1914
2173
 
1915
2174
  #### El-Gamal Public-Key Cryptography
@@ -1919,61 +2178,39 @@ assert pub.VerifySignature(msg, sig)
1919
2178
  This is **raw El-Gamal** over a prime field — no padding, no hashing — and is **not** DSA.
1920
2179
  For real-world deployments, use a high-level library with authenticated encryption and proper encoding.
1921
2180
 
1922
- ##### Shared Public Key
1923
-
1924
2181
  ```py
1925
2182
  from transcrypto import elgamal
1926
2183
 
1927
- # Shared parameters (prime modulus, group base) for a group
2184
+ # Shared parameters (prime modulus, group base) for a group
1928
2185
  shared = elgamal.ElGamalSharedPublicKey.New(256)
1929
2186
  print(shared.prime_modulus)
1930
2187
  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
2188
 
1939
- ```py
1940
- # ➋ Public key from private
2189
+ # Public key from private
1941
2190
  priv = elgamal.ElGamalPrivateKey.New(shared)
1942
2191
  pub = elgamal.ElGamalPublicKey.Copy(priv)
1943
2192
 
1944
- # Encryption
1945
- msg = 42
1946
- cipher = pub.Encrypt(msg)
1947
- plain = priv.Decrypt(cipher)
2193
+ # Safe Encrypt & decrypt
2194
+ msg = b'xyz'
2195
+ cipher = pub.Encrypt(msg, associated_data=b'aad')
2196
+ plain = priv.Decrypt(cipher, associated_data=b'aad')
1948
2197
  assert plain == msg
1949
2198
 
1950
- # Signature verify
1951
- sig = priv.Sign(msg)
1952
- assert pub.VerifySignature(msg, sig)
1953
- ```
2199
+ # Safe Sign & verify
2200
+ signature = priv.Sign(msg) # can also have associated_data, optionally
2201
+ assert pub.Verify(msg, signature)
1954
2202
 
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)
2203
+ # Raw Encryption
2204
+ msg = 42
2205
+ cipher = pub.RawEncrypt(msg)
2206
+ plain = priv.RawDecrypt(cipher)
2207
+ assert plain == msg
1967
2208
 
1968
- # Signing
1969
- sig = priv.Sign(msg)
1970
- assert pub.VerifySignature(msg, sig)
2209
+ # Raw Signature verify
2210
+ sig = priv.RawSign(msg)
2211
+ assert pub.RawVerify(msg, sig)
1971
2212
  ```
1972
2213
 
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
2214
  Key points:
1978
2215
 
1979
2216
  - **Security parameters**:
@@ -1997,20 +2234,25 @@ This is **raw DSA** over a prime field — **no hashing or padding**. You sign/v
1997
2234
  ```py
1998
2235
  from transcrypto import dsa
1999
2236
 
2000
- # Shared parameters (p, q, g)
2237
+ # Shared parameters (p, q, g)
2001
2238
  shared = dsa.DSASharedPublicKey.New(p_bits=1024, q_bits=160)
2002
2239
  print(shared.prime_modulus) # p
2003
2240
  print(shared.prime_seed) # q (q | p-1)
2004
2241
  print(shared.group_base) # g
2005
2242
 
2006
- # Individual key pair
2243
+ # Individual key pair
2007
2244
  priv = dsa.DSAPrivateKey.New(shared)
2008
2245
  pub = dsa.DSAPublicKey.Copy(priv)
2009
2246
 
2010
- # Sign & verify (message must be 1 ≤ m < q)
2247
+ # Safe Sign & verify
2248
+ msg = b'xyz'
2249
+ signature = priv.Sign(msg) # can also have associated_data, optionally
2250
+ assert pub.Verify(msg, signature)
2251
+
2252
+ # Raw Sign & verify (message must be 1 ≤ m < q)
2011
2253
  msg = 123456789 % shared.prime_seed
2012
- sig = priv.Sign(msg)
2013
- assert pub.VerifySignature(msg, sig)
2254
+ sig = priv.RawSign(msg)
2255
+ assert pub.RawVerify(msg, sig)
2014
2256
  ```
2015
2257
 
2016
2258
  - ranges:
@@ -2044,13 +2286,13 @@ This is a way of bidding on some commitment (the `secret`) that can be cryptogra
2044
2286
  from transcrypto import base
2045
2287
 
2046
2288
  # 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
2289
+ bid_priv = base.PrivateBid512.New(secret) # this one you keep private
2290
+ bid_pub = base.PublicBid512.Copy(bid_priv) # this one you publish
2049
2291
 
2050
2292
  # Checking that a bid is genuine requires the public bid and knowing the nonce and the secret:
2051
2293
  print(bid_pub.VerifyBid(private_key, secret_bid)) # these come from a divulged private bid
2052
2294
  # 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)
2295
+ bid_pub_expected = base.PublicBid512.Copy(bid_priv)
2054
2296
  print(bid_pub == bid_pub_expected)
2055
2297
  ```
2056
2298
 
@@ -2063,8 +2305,8 @@ This is the information-theoretic SSS but with no authentication or binding betw
2063
2305
  ```py
2064
2306
  from transcrypto import sss
2065
2307
 
2066
- # Generate parameters: at least 3 of 5 shares needed,
2067
- # coefficients & modulus are 128-bit primes
2308
+ # Generate parameters: at least 3 of 5 shares needed,
2309
+ # coefficients & modulus are 128-bit primes
2068
2310
  priv = sss.ShamirSharedSecretPrivate.New(minimum_shares=3, bit_length=128)
2069
2311
  pub = sss.ShamirSharedSecretPublic.Copy(priv) # what you publish
2070
2312
 
@@ -2072,11 +2314,19 @@ print(f'threshold : {pub.minimum}')
2072
2314
  print(f'prime mod : {pub.modulus}')
2073
2315
  print(f'poly coefficients: {priv.polynomial}') # keep these private!
2074
2316
 
2075
- #Issuing shares
2317
+ # Safe Issuing shares
2318
+
2319
+ secret = b'xyz'
2320
+ # Generate 5 shares, each has a copy of the encrypted secret
2321
+ five_shares = priv.MakeDataShares(secret, 5)
2322
+ for sh in five_shares:
2323
+ print(sh)
2324
+
2325
+ # Raw Issuing shares
2076
2326
 
2077
2327
  secret = 0xC0FFEE
2078
2328
  # Generate an unlimited stream; here we take 5
2079
- five_shares = list(priv.Shares(secret, max_shares=5))
2329
+ five_shares = list(priv.RawShares(secret, max_shares=5))
2080
2330
  for sh in five_shares:
2081
2331
  print(f'share {sh.share_key} → {sh.share_value}')
2082
2332
  ```
@@ -2084,10 +2334,18 @@ for sh in five_shares:
2084
2334
  A single share object looks like `sss.ShamirSharePrivate(minimum=3, modulus=..., share_key=42, share_value=123456789)`.
2085
2335
 
2086
2336
  ```py
2087
- #Re-constructing the secret
2337
+ # Safe Re-constructing the secret
2338
+ secret = b'xyz'
2339
+ five_shares = priv.MakeDataShares(secret, 5)
2340
+ subset = five_shares[:3] # any 3 distinct shares
2341
+ recovered = subset[0].RecoverData(subset) # each share has the encrypted data, so you ask it to join with the others
2342
+ assert recovered == secret
2088
2343
 
2344
+ # Raw Re-constructing the secret
2345
+ secret = 0xC0FFEE
2346
+ five_shares = list(priv.RawShares(secret, max_shares=5))
2089
2347
  subset = five_shares[:3] # any 3 distinct shares
2090
- recovered = pub.RecoverSecret(subset)
2348
+ recovered = pub.RawRecoverSecret(subset)
2091
2349
  assert recovered == secret
2092
2350
  ```
2093
2351
 
@@ -2095,23 +2353,23 @@ If you supply fewer than minimum shares you get a `CryptoError`, unless you expl
2095
2353
 
2096
2354
  ```py
2097
2355
  try:
2098
- pub.RecoverSecret(five_shares[:2]) # raises
2356
+ pub.RawRecoverSecret(five_shares[:2]) # raises
2099
2357
  except Exception as e:
2100
2358
  print(e) # "unrecoverable secret …"
2101
2359
 
2102
2360
  # Force the interpolation even with 2 points (gives a wrong secret, of course)
2103
- print(pub.RecoverSecret(five_shares[:2], force_recover=True))
2361
+ print(pub.RawRecoverSecret(five_shares[:2], force_recover=True))
2104
2362
 
2105
2363
  # Checking that a share is genuine
2106
2364
 
2107
2365
  share = five_shares[0]
2108
- ok = priv.VerifyShare(secret, share) # ▶ True
2366
+ ok = priv.RawVerifyShare(secret, share) # ▶ True
2109
2367
  tampered = sss.ShamirSharePrivate(
2110
2368
  minimum=share.minimum,
2111
2369
  modulus=share.modulus,
2112
2370
  share_key=share.share_key,
2113
2371
  share_value=(share.share_value + 1) % share.modulus)
2114
- print(priv.VerifyShare(secret, tampered)) # ▶ False
2372
+ print(priv.RawVerifyShare(secret, tampered)) # ▶ False
2115
2373
  ```
2116
2374
 
2117
2375
  ## Appendix: Development Instructions