ghostbytes 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ghostbytes/__init__.py +3 -0
- ghostbytes/__main__.py +9 -0
- ghostbytes/crypto/config.py +155 -0
- ghostbytes/crypto/crypto.py +162 -0
- ghostbytes/crypto/crypto.pyi +149 -0
- ghostbytes/crypto/kyber.py +257 -0
- ghostbytes/crypto/oaep_extension.py +125 -0
- ghostbytes/crypto/primitives.py +360 -0
- ghostbytes/error.py +177 -0
- ghostbytes/gui/gui.py +2809 -0
- ghostbytes/gui/theme.py +95 -0
- ghostbytes/gui/wrappers.py +566 -0
- ghostbytes/img/icon.ico +0 -0
- ghostbytes/img/icon.png +0 -0
- ghostbytes/tools/benchmark.py +218 -0
- ghostbytes/tools/rand.py +118 -0
- ghostbytes/tools/shred.py +384 -0
- ghostbytes/tools/tools.pyi +67 -0
- ghostbytes-1.0.0.dist-info/METADATA +182 -0
- ghostbytes-1.0.0.dist-info/RECORD +22 -0
- ghostbytes-1.0.0.dist-info/WHEEL +4 -0
- ghostbytes-1.0.0.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
"""
|
|
2
|
+
primitives.py
|
|
3
|
+
|
|
4
|
+
This modules provides helpers for:
|
|
5
|
+
|
|
6
|
+
* AES-GCM encryption / decryption
|
|
7
|
+
* RSA-OAEP encryption / decryption
|
|
8
|
+
* Argon2id KDF
|
|
9
|
+
* RSA key generation / verification
|
|
10
|
+
* Key-format, key-type detection (for non-pycryptodome)
|
|
11
|
+
|
|
12
|
+
The ``ML-KEM`` implementations / functions are located in kyber.py
|
|
13
|
+
The ``extended_oaep`` implementations / functions are located in oaep_extension.py
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from Crypto.Cipher import AES as _aes
|
|
17
|
+
from Crypto.Cipher import PKCS1_OAEP as _oaep
|
|
18
|
+
from Crypto.PublicKey import RSA as _rsa
|
|
19
|
+
|
|
20
|
+
from argon2.low_level import hash_secret_raw, Type
|
|
21
|
+
|
|
22
|
+
from cryptography.hazmat.primitives import serialization
|
|
23
|
+
from cryptography.hazmat.primitives.asymmetric import mlkem, rsa
|
|
24
|
+
|
|
25
|
+
from ghostbytes.crypto.config import RSA_KEY_OUT_FORMAT, CryptoConfig
|
|
26
|
+
from ghostbytes.error import aes_envelope_extraction_failed, data_integrity_check_failed, \
|
|
27
|
+
decryption_key_incorrect, envelope_too_small, invalid_argument, invalid_configuration_type, \
|
|
28
|
+
invalid_keyfile, keyfile_cannot_crypt, keyfile_passphrase_incorrect, rsa_message_too_big, \
|
|
29
|
+
rsa_public_exponent_not_prime, store_iv_choice_mismatch, unsupported_keyfile
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def aes_encrypt(config, key, plaintext):
|
|
33
|
+
"""
|
|
34
|
+
Encrypts plaintext using AES-GCM
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
config: encryption configuration in ``CryptoConfig``
|
|
38
|
+
key: AES key as bytes, must be 16, 24, or 32 bytes
|
|
39
|
+
plaintext: data to encrypt as bytes
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
AES envelop of ciphertext and authentication tag as bytes.
|
|
43
|
+
|
|
44
|
+
Raises:
|
|
45
|
+
invalid_configuration_type: when config is not a ``CryptoConfig`` instance
|
|
46
|
+
invalid_argument: if ``plaintext`` or ``key`` is not bytes, or if key has an
|
|
47
|
+
invalid length
|
|
48
|
+
store_iv_choice_mismatch: if ``config.store_iv`` is invalid.
|
|
49
|
+
"""
|
|
50
|
+
_require_config(config)
|
|
51
|
+
if not isinstance(plaintext, bytes) or not isinstance(key, bytes):
|
|
52
|
+
raise invalid_argument("AES plaintext and key", "must be bytes")
|
|
53
|
+
if len(key) not in (16, 24, 32):
|
|
54
|
+
raise invalid_argument("AES key", "must be 16, 24, or 32 bytes")
|
|
55
|
+
encryptor = _aes.new(
|
|
56
|
+
key=key,
|
|
57
|
+
mode=_aes.MODE_GCM,
|
|
58
|
+
mac_len=config.mac_len,
|
|
59
|
+
use_aesni=True,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
ciphertext, digest = encryptor.encrypt_and_digest(plaintext)
|
|
63
|
+
|
|
64
|
+
if config.store_iv == 'append':
|
|
65
|
+
ciphertext += encryptor.nonce + digest
|
|
66
|
+
elif config.store_iv == 'prepend':
|
|
67
|
+
ciphertext = encryptor.nonce + digest + ciphertext
|
|
68
|
+
else:
|
|
69
|
+
raise store_iv_choice_mismatch()
|
|
70
|
+
|
|
71
|
+
return ciphertext
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def aes_decrypt(config, key, ciphertext):
|
|
75
|
+
"""
|
|
76
|
+
Decrypt and authenticate an AES-GCM envelop
|
|
77
|
+
|
|
78
|
+
The function extracts the AES-GCM nonce and authenticate tag from the
|
|
79
|
+
ciphertext and verifies it with the decrypted plaintext.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
config: encryption configuration in ``CryptoConfig``
|
|
83
|
+
key: AES key as bytes, must be 16, 24, or 32 bytes
|
|
84
|
+
ciphertext: data to decrypt as bytes
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
The decrypted text as plaintext
|
|
88
|
+
|
|
89
|
+
Raises:
|
|
90
|
+
invalid_configuration_type: when config is not a ``CryptoConfig`` instance
|
|
91
|
+
invalid_argument: if ``plaintext`` or ``key`` is not bytes, or if key has an
|
|
92
|
+
invalid length
|
|
93
|
+
envelop_too_small: if the size of the envelop is too small to contain a
|
|
94
|
+
complete ML-KEM ciphertext
|
|
95
|
+
aes_envelope_extraction_failed: if the authentication tag cannot be
|
|
96
|
+
extracted correctly.
|
|
97
|
+
decrypt_key_incorrect: if decryption fails
|
|
98
|
+
data_integrity_check_failed: if authentication fails
|
|
99
|
+
store_iv_choice_mismatch: if ``config.store_iv`` is invalid.
|
|
100
|
+
"""
|
|
101
|
+
_require_config(config)
|
|
102
|
+
envelop_len = 16 + config.mac_len # 16 bytes for nonce length
|
|
103
|
+
if not isinstance(ciphertext, bytes) or not isinstance(key, bytes):
|
|
104
|
+
raise invalid_argument("AES ciphertext and key", "must be bytes")
|
|
105
|
+
if len(key) not in (16, 24, 32):
|
|
106
|
+
raise invalid_argument("AES key", "must be 16, 24, or 32 bytes")
|
|
107
|
+
if len(ciphertext) < envelop_len:
|
|
108
|
+
raise envelope_too_small()
|
|
109
|
+
|
|
110
|
+
if config.store_iv == 'append':
|
|
111
|
+
nonce = ciphertext[len(ciphertext) -
|
|
112
|
+
envelop_len:len(ciphertext) -
|
|
113
|
+
envelop_len +
|
|
114
|
+
16]
|
|
115
|
+
mac = ciphertext[len(ciphertext) - envelop_len + 16:]
|
|
116
|
+
ciphertext = ciphertext[:len(ciphertext) - envelop_len]
|
|
117
|
+
elif config.store_iv == 'prepend':
|
|
118
|
+
nonce = ciphertext[:16]
|
|
119
|
+
mac = ciphertext[16:16 + config.mac_len]
|
|
120
|
+
ciphertext = ciphertext[16 + config.mac_len:]
|
|
121
|
+
else:
|
|
122
|
+
raise store_iv_choice_mismatch()
|
|
123
|
+
|
|
124
|
+
if len(mac) != config.mac_len:
|
|
125
|
+
raise aes_envelope_extraction_failed()
|
|
126
|
+
|
|
127
|
+
decryptor = _aes.new(
|
|
128
|
+
key=key,
|
|
129
|
+
mode=_aes.MODE_GCM,
|
|
130
|
+
mac_len=config.mac_len,
|
|
131
|
+
use_aesni=True,
|
|
132
|
+
nonce=nonce
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
try:
|
|
136
|
+
plaintext = decryptor.decrypt(ciphertext)
|
|
137
|
+
except ValueError as e:
|
|
138
|
+
raise decryption_key_incorrect() from e
|
|
139
|
+
|
|
140
|
+
try:
|
|
141
|
+
decryptor.verify(mac)
|
|
142
|
+
except ValueError as e:
|
|
143
|
+
raise data_integrity_check_failed() from e
|
|
144
|
+
|
|
145
|
+
return plaintext
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def rsa_oaep_encrypt(config, key, plaintext):
|
|
149
|
+
"""
|
|
150
|
+
Encrypt a message using RSA-OAEP.
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
config: encryption configuration in ``CryptoConfig``
|
|
154
|
+
key: RSA public key.
|
|
155
|
+
plaintext: data to encrypt as bytes
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
The RSA-OAEP ciphertext as bytes
|
|
159
|
+
|
|
160
|
+
Raises:
|
|
161
|
+
invalid_configuration_types: if ``config`` is invalid
|
|
162
|
+
keyfile_cannot_crypt: if ``key`` cannot encrypt
|
|
163
|
+
rsa_message_too_big: if the plaintext is too large for RSA-OAEP
|
|
164
|
+
"""
|
|
165
|
+
_require_config(config)
|
|
166
|
+
if not key.can_encrypt():
|
|
167
|
+
raise keyfile_cannot_crypt("encrypt")
|
|
168
|
+
|
|
169
|
+
oaep = _oaep.new(
|
|
170
|
+
key,
|
|
171
|
+
config.hash_func
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
try:
|
|
175
|
+
return oaep.encrypt(plaintext)
|
|
176
|
+
except ValueError as e:
|
|
177
|
+
raise rsa_message_too_big() from e
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def rsa_oaep_decrypt(config, key, ciphertext):
|
|
181
|
+
"""
|
|
182
|
+
Decrypt a message using RSA-OAEP
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
config: encryption configuration in ``CryptoConfig``
|
|
186
|
+
ciphertext: data to decrypt as bytes
|
|
187
|
+
key: RSA private key.
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
The decrypted plaintext as bytes
|
|
191
|
+
|
|
192
|
+
Raises:
|
|
193
|
+
invalid_configuration_types: if ``config`` is invalid
|
|
194
|
+
keyfile_cannot_crypt: if ``key`` cannot decrypt (``key`` is not private key)
|
|
195
|
+
decryption_key_incorrect: if RSA-OAEP decryption fails
|
|
196
|
+
"""
|
|
197
|
+
_require_config(config)
|
|
198
|
+
if not key.has_private():
|
|
199
|
+
raise keyfile_cannot_crypt("decrypt")
|
|
200
|
+
|
|
201
|
+
oaep = _oaep.new(
|
|
202
|
+
key,
|
|
203
|
+
config.hash_func
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
try:
|
|
207
|
+
return oaep.decrypt(ciphertext)
|
|
208
|
+
except ValueError as e:
|
|
209
|
+
raise decryption_key_incorrect() from e
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def derive_key(config, secret):
|
|
213
|
+
"""
|
|
214
|
+
Derive a 256-bit key from a secret using Argon2id memory-hard
|
|
215
|
+
key derivation function (KDF)
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
config: key derivation configuration in ``CryptoConfig``
|
|
219
|
+
secret: Non-empty secret material as bytes
|
|
220
|
+
|
|
221
|
+
Returns:
|
|
222
|
+
A 32-bytes derived key.
|
|
223
|
+
|
|
224
|
+
Raises:
|
|
225
|
+
invalid_configuration_type: if ``config`` is invalid
|
|
226
|
+
invalid_argument: if ``key`` is not non-empty bytes
|
|
227
|
+
"""
|
|
228
|
+
_require_config(config)
|
|
229
|
+
if not isinstance(secret, bytes) or not secret:
|
|
230
|
+
raise invalid_argument("KDF secret", "must be non-empty bytes")
|
|
231
|
+
return hash_secret_raw(
|
|
232
|
+
secret=secret,
|
|
233
|
+
salt=config.kdf_salt,
|
|
234
|
+
time_cost=config.kdf_time_cost,
|
|
235
|
+
memory_cost=config.kdf_memory_cost,
|
|
236
|
+
parallelism=config.kdf_parallelism,
|
|
237
|
+
hash_len=32,
|
|
238
|
+
type=Type.ID
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def genrsa(length, exp, passphrase, out_format=RSA_KEY_OUT_FORMAT[0]):
|
|
243
|
+
"""
|
|
244
|
+
Generate and serialize an RSA key pair.
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
key_size: RSA modulus size in bits. Must be at least 1024
|
|
248
|
+
exponent: RSA public exponent. Must be an odd prime greater than 2
|
|
249
|
+
passphrase: Passphrase used to encrypt the private key
|
|
250
|
+
out_format: Output format supported by PyCryptodome, such as ``"PEM"``
|
|
251
|
+
|
|
252
|
+
Returns:
|
|
253
|
+
A tuple ``(public_key, private_key)``.
|
|
254
|
+
Each key is encoded into bytes.
|
|
255
|
+
|
|
256
|
+
Raises:
|
|
257
|
+
invalid_argument: if a parameter is invalid (rsa key size < 1024 bits? RSA exponent
|
|
258
|
+
too small? RSA key encoding not supported?)
|
|
259
|
+
rsa_public_exponent_not_prime: if ``exponent`` is not prime.
|
|
260
|
+
"""
|
|
261
|
+
if not isinstance(length, int) or length < 1024:
|
|
262
|
+
raise invalid_argument("RSA key size",
|
|
263
|
+
"must be an integer of at least 1024 bits")
|
|
264
|
+
if not isinstance(exp, int) or exp < 3:
|
|
265
|
+
raise invalid_argument(
|
|
266
|
+
"RSA public exponent",
|
|
267
|
+
"must be an integer greater than 2")
|
|
268
|
+
if out_format not in RSA_KEY_OUT_FORMAT:
|
|
269
|
+
raise invalid_argument("RSA key encoding", "is not supported")
|
|
270
|
+
for i in range(2, int(exp**0.5) + 1):
|
|
271
|
+
if exp % i == 0:
|
|
272
|
+
raise rsa_public_exponent_not_prime()
|
|
273
|
+
|
|
274
|
+
key = _rsa.generate(length, None, exp)
|
|
275
|
+
public = key.public_key()
|
|
276
|
+
|
|
277
|
+
public_key = public.export_key(out_format)
|
|
278
|
+
private_key = key.export_key(out_format, passphrase)
|
|
279
|
+
|
|
280
|
+
return public_key, private_key
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def verify_rsa(public_key, private_key, passphrase):
|
|
284
|
+
"""
|
|
285
|
+
Verifies that an RSA public key matches a private key
|
|
286
|
+
|
|
287
|
+
Args:
|
|
288
|
+
public_key: Serialised RSA public key
|
|
289
|
+
private_key: Serialised RSA private key
|
|
290
|
+
passphrase: passphrase for the private key
|
|
291
|
+
|
|
292
|
+
Returns:
|
|
293
|
+
``True`` if the keys contain the same RSA modulus; otherwise ``False``
|
|
294
|
+
|
|
295
|
+
Raises:
|
|
296
|
+
keyfile_passphrase_incorrect: if the passphrase is invalid
|
|
297
|
+
invalid_keyfile: if either key cannot be imported.
|
|
298
|
+
"""
|
|
299
|
+
try:
|
|
300
|
+
priv = _rsa.import_key(private_key, passphrase)
|
|
301
|
+
except (ValueError, TypeError, IndexError) as e:
|
|
302
|
+
if passphrase is not None:
|
|
303
|
+
raise keyfile_passphrase_incorrect() from e
|
|
304
|
+
raise invalid_keyfile() from e
|
|
305
|
+
if not priv.has_private():
|
|
306
|
+
return False
|
|
307
|
+
try:
|
|
308
|
+
pub = _rsa.import_key(public_key, passphrase)
|
|
309
|
+
except (ValueError, TypeError, IndexError) as e:
|
|
310
|
+
if passphrase is not None:
|
|
311
|
+
raise keyfile_passphrase_incorrect() from e
|
|
312
|
+
raise invalid_keyfile() from e
|
|
313
|
+
return priv.n == pub.n
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
def detect_key_format(key_bytes: bytes) -> str:
|
|
317
|
+
"""Detect if the serialised key data is PEM or DER encoded through inspecting the headers"""
|
|
318
|
+
if key_bytes.startswith(b"-----BEGIN"):
|
|
319
|
+
return "PEM"
|
|
320
|
+
|
|
321
|
+
if key_bytes[0] == 0x30:
|
|
322
|
+
if len(key_bytes) > 1 and (key_bytes[1] & 0x80 or key_bytes[1] < 128):
|
|
323
|
+
return "DER"
|
|
324
|
+
|
|
325
|
+
raise invalid_keyfile()
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def keytype(key_bytes, passphrase=None):
|
|
329
|
+
"""Determine whether serialised key data contains an RSA or ML-KEM key"""
|
|
330
|
+
try:
|
|
331
|
+
if detect_key_format(key_bytes) == "PEM":
|
|
332
|
+
if b"PRIVATE KEY" in key_bytes:
|
|
333
|
+
key_obj = serialization.load_pem_private_key(
|
|
334
|
+
key_bytes, password=passphrase)
|
|
335
|
+
else:
|
|
336
|
+
key_obj = serialization.load_pem_public_key(key_bytes)
|
|
337
|
+
else:
|
|
338
|
+
try:
|
|
339
|
+
key_obj = serialization.load_der_private_key(
|
|
340
|
+
key_bytes, password=passphrase)
|
|
341
|
+
except Exception:
|
|
342
|
+
key_obj = serialization.load_der_public_key(key_bytes)
|
|
343
|
+
except Exception as e:
|
|
344
|
+
raise invalid_keyfile() from e
|
|
345
|
+
|
|
346
|
+
if isinstance(key_obj, (rsa.RSAPrivateKey, rsa.RSAPublicKey)):
|
|
347
|
+
return "RSA"
|
|
348
|
+
if isinstance(
|
|
349
|
+
key_obj,
|
|
350
|
+
(mlkem.MLKEM768PrivateKey,
|
|
351
|
+
mlkem.MLKEM768PublicKey,
|
|
352
|
+
mlkem.MLKEM1024PrivateKey,
|
|
353
|
+
mlkem.MLKEM1024PublicKey)):
|
|
354
|
+
return "ML-KEM"
|
|
355
|
+
raise unsupported_keyfile()
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
def _require_config(config):
|
|
359
|
+
if not isinstance(config, CryptoConfig):
|
|
360
|
+
raise invalid_configuration_type()
|
ghostbytes/error.py
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"""Exception types and error factories used throughout Ghostbytes."""
|
|
2
|
+
|
|
3
|
+
# Error factories intentionally use compact one-line definitions and retain
|
|
4
|
+
# the public lambda-based API for compatibility.
|
|
5
|
+
# pylint: disable=missing-function-docstring,multiple-statements,unnecessary-lambda-assignment
|
|
6
|
+
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
|
|
9
|
+
INTERNAL_ERROR = "INTERNAL_ERROR"
|
|
10
|
+
USAGE_ERROR = "USAGE_ERROR"
|
|
11
|
+
INCORRECT_KEY_ERROR = "INCORRECT_KEY"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class GhostBytesError(Exception):
|
|
15
|
+
"""Base exception carrying a user-facing message and optional heading."""
|
|
16
|
+
|
|
17
|
+
def __init__(self, message, heading=None):
|
|
18
|
+
super().__init__(message)
|
|
19
|
+
self.heading = heading
|
|
20
|
+
self.message = message
|
|
21
|
+
|
|
22
|
+
def __str__(self):
|
|
23
|
+
now = datetime.now().replace(microsecond=0).isoformat()
|
|
24
|
+
heading = self.__class__.__name__.removesuffix("Error") + " Error"
|
|
25
|
+
if self.heading is not None:
|
|
26
|
+
heading += f" ({self.heading})"
|
|
27
|
+
return f"[{now}] {heading}: {self.message}"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class CryptoError(GhostBytesError):
|
|
31
|
+
"""Error raised for cryptographic operation failures."""
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class GeneralError(GhostBytesError):
|
|
35
|
+
"""Error raised for invalid usage or general operation failures."""
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# Cryptographic operation errors.
|
|
39
|
+
def decryption_key_incorrect(): return CryptoError("Decryption key incorrect!")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def data_integrity_check_failed(): return CryptoError(
|
|
43
|
+
"Data integrity check failed. The data may have been modified or corrupted.")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def keyfile_passphrase_incorrect(): return CryptoError(
|
|
47
|
+
"Passphrase for RSA private key incorrect!")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def keyfile_cannot_crypt(operation): return CryptoError(
|
|
51
|
+
f"Provided RSA key cannot {operation}")
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def invalid_keyfile(): return CryptoError(
|
|
55
|
+
"Invalid keyfile. Use a public/private key pair generated by Ghostbytes or an equivalent tool.")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def unsupported_keyfile(): return CryptoError(
|
|
59
|
+
"Unsupported keyfile. Use a keyfile generated by a supported algorithm.")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def envelope_too_small(): return CryptoError(
|
|
63
|
+
"Failed to extract encryption envelope. The file is too small to contain encrypted data.",
|
|
64
|
+
USAGE_ERROR)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def rsa_message_too_big(): return CryptoError(
|
|
68
|
+
"Failed to encrypt RSA message. The message exceeds the maximum size supported by the key.")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def algorithm_not_supported(): return CryptoError(
|
|
72
|
+
"Algorithm not supported", INTERNAL_ERROR)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def store_iv_choice_mismatch(): return CryptoError(
|
|
76
|
+
"STORE_IV must be either `append` or `prepend`", USAGE_ERROR)
|
|
77
|
+
def aes_envelope_extraction_failed(): return CryptoError(
|
|
78
|
+
"AES envelope extraction failed", INTERNAL_ERROR)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def hash_not_supported(): return CryptoError("Hash not supported", USAGE_ERROR)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def rsa_public_exponent_not_prime(): return CryptoError(
|
|
85
|
+
"Public exponent is not a prime number", USAGE_ERROR)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def unsupported_mlkem_key(): return CryptoError(
|
|
89
|
+
"The selected key is not a supported ML-KEM-768 or ML-KEM-1024 key.",
|
|
90
|
+
USAGE_ERROR)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def text_not_in_bytes(): return TypeError(
|
|
94
|
+
"Ciphertext / plaintext must be in Bytes form")
|
|
95
|
+
|
|
96
|
+
# Input and configuration errors.
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def parameter_not_exist(name): return GeneralError(
|
|
100
|
+
f"Parameter name `{name}` must exist.", USAGE_ERROR)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
invalid_argument = lambda name, detail="is invalid": GeneralError(
|
|
104
|
+
f"Parameter `{name}` {detail}.", USAGE_ERROR)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def invalid_configuration(detail): return GeneralError(
|
|
108
|
+
f"Invalid Ghostbytes configuration: {detail}", USAGE_ERROR)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def invalid_configuration_type(): return GeneralError(
|
|
112
|
+
"Configuration must be an instance of `CryptoConfig`.", USAGE_ERROR)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def invalid_mode(mode): return GeneralError(
|
|
116
|
+
f"Unsupported batch mode `{mode}`. Use `encrypt` or `decrypt`.", USAGE_ERROR)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def invalid_random_length(): return GeneralError(
|
|
120
|
+
"Random byte length must be a non-negative integer.", USAGE_ERROR)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def invalid_repeat(): return GeneralError(
|
|
124
|
+
"Shred repeat count must be at least 1.", USAGE_ERROR)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def invalid_pattern(): return GeneralError(
|
|
128
|
+
"Overwrite pattern cannot be empty.", USAGE_ERROR)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def key_encoding_not_found(): return GeneralError(
|
|
132
|
+
"Key encoding not found", USAGE_ERROR)
|
|
133
|
+
|
|
134
|
+
# File and system errors.
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def io_error(operation): return GeneralError(
|
|
138
|
+
f"Unable to {operation}.", INTERNAL_ERROR)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def temporary_write_failed(): return GeneralError(
|
|
142
|
+
"Unable to write temporary wipe data.", INTERNAL_ERROR)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def file_not_found(): return GeneralError(
|
|
146
|
+
"Filename specified cannot be found", USAGE_ERROR)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def not_a_file(): return GeneralError(
|
|
150
|
+
"The selected filename is not a file.", USAGE_ERROR)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def shred_option_not_supported(): return GeneralError(
|
|
154
|
+
"Shred option not supported", USAGE_ERROR)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def chunk_size_invalid(): return GeneralError(
|
|
158
|
+
"Chunk size in shred functions must be greater than zero", USAGE_ERROR)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def partition_not_found(): return GeneralError(
|
|
162
|
+
"Disk partition not found!", INTERNAL_ERROR)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def temporary_filename_unavailable(): return GeneralError(
|
|
166
|
+
"Failed to generate a random temporary file for shred operations", INTERNAL_ERROR)
|
|
167
|
+
|
|
168
|
+
# Randomness and key-management errors.
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def random_function_not_supported(): return CryptoError(
|
|
172
|
+
"Random function not supported", USAGE_ERROR)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def unix_only_function(function): return GeneralError(
|
|
176
|
+
f"{function} is only available on Unix-like systems.", USAGE_ERROR
|
|
177
|
+
)
|