fastcrypter 2.3.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.
- fastcrypter/__init__.py +285 -0
- fastcrypter/advanced_encryptor.py +368 -0
- fastcrypter/algorithms/__init__.py +119 -0
- fastcrypter/algorithms/compression/__init__.py +17 -0
- fastcrypter/algorithms/encryption/__init__.py +17 -0
- fastcrypter/core/__init__.py +16 -0
- fastcrypter/core/compressor.py +409 -0
- fastcrypter/core/custom_encoder.py +309 -0
- fastcrypter/core/encryptor.py +703 -0
- fastcrypter/core/enhanced_compressor.py +542 -0
- fastcrypter/core/key_manager.py +315 -0
- fastcrypter/exceptions.py +219 -0
- fastcrypter/file_encryptor.py +135 -0
- fastcrypter/secure_compressor.py +568 -0
- fastcrypter/utils/__init__.py +17 -0
- fastcrypter-2.3.0.dist-info/METADATA +381 -0
- fastcrypter-2.3.0.dist-info/RECORD +21 -0
- fastcrypter-2.3.0.dist-info/WHEEL +5 -0
- fastcrypter-2.3.0.dist-info/entry_points.txt +2 -0
- fastcrypter-2.3.0.dist-info/licenses/LICENSE +21 -0
- fastcrypter-2.3.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Custom Encoder for Encrypter package.
|
|
3
|
+
|
|
4
|
+
This module provides custom encoding functionality that converts binary data
|
|
5
|
+
to text using only specified characters for steganography and obfuscation.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import secrets
|
|
9
|
+
from typing import Union, Optional, Tuple
|
|
10
|
+
from ..exceptions import ValidationError, ErrorCodes
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class CustomEncoder:
|
|
14
|
+
"""
|
|
15
|
+
Custom encoder that converts binary data to text using only specified characters.
|
|
16
|
+
|
|
17
|
+
This encoder is useful for creating encrypted output that looks like normal text
|
|
18
|
+
or follows specific character constraints.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
# Default character set (can be customized)
|
|
22
|
+
DEFAULT_CHARSET = "abcdef98Xvbvii"
|
|
23
|
+
|
|
24
|
+
def __init__(self, charset: str = DEFAULT_CHARSET, padding_char: Optional[str] = None):
|
|
25
|
+
"""
|
|
26
|
+
Initialize the CustomEncoder.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
charset (str): Characters to use for encoding.
|
|
30
|
+
padding_char (str, optional): Character for padding. Uses last char if None.
|
|
31
|
+
|
|
32
|
+
Raises:
|
|
33
|
+
ValidationError: If charset is invalid.
|
|
34
|
+
"""
|
|
35
|
+
if not charset or len(charset) < 2:
|
|
36
|
+
raise ValidationError(
|
|
37
|
+
"Charset must contain at least 2 characters",
|
|
38
|
+
ErrorCodes.INVALID_CONFIGURATION
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
# Remove duplicates while preserving order
|
|
42
|
+
seen = set()
|
|
43
|
+
self.charset = ''.join(char for char in charset if not (char in seen or seen.add(char)))
|
|
44
|
+
|
|
45
|
+
if len(self.charset) < 2:
|
|
46
|
+
raise ValidationError(
|
|
47
|
+
"Charset must contain at least 2 unique characters",
|
|
48
|
+
ErrorCodes.INVALID_CONFIGURATION
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
self.base = len(self.charset)
|
|
52
|
+
self.padding_char = padding_char or self.charset[-1]
|
|
53
|
+
|
|
54
|
+
# Create lookup tables for fast encoding/decoding
|
|
55
|
+
self.char_to_value = {char: i for i, char in enumerate(self.charset)}
|
|
56
|
+
self.value_to_char = {i: char for i, char in enumerate(self.charset)}
|
|
57
|
+
|
|
58
|
+
def encode(self, data: Union[bytes, bytearray]) -> str:
|
|
59
|
+
"""
|
|
60
|
+
Encode binary data to custom character set.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
data: Binary data to encode.
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
str: Encoded string using custom charset.
|
|
67
|
+
|
|
68
|
+
Raises:
|
|
69
|
+
ValidationError: If data is invalid.
|
|
70
|
+
"""
|
|
71
|
+
if not isinstance(data, (bytes, bytearray)):
|
|
72
|
+
raise ValidationError(
|
|
73
|
+
"Data must be bytes or bytearray",
|
|
74
|
+
ErrorCodes.INVALID_INPUT_FORMAT
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
if len(data) == 0:
|
|
78
|
+
return ""
|
|
79
|
+
|
|
80
|
+
# Convert bytes to big integer
|
|
81
|
+
number = int.from_bytes(data, byteorder='big')
|
|
82
|
+
|
|
83
|
+
if number == 0:
|
|
84
|
+
return self.charset[0]
|
|
85
|
+
|
|
86
|
+
# Convert to custom base
|
|
87
|
+
result = []
|
|
88
|
+
while number > 0:
|
|
89
|
+
result.append(self.value_to_char[number % self.base])
|
|
90
|
+
number //= self.base
|
|
91
|
+
|
|
92
|
+
# Reverse to get correct order
|
|
93
|
+
encoded = ''.join(reversed(result))
|
|
94
|
+
|
|
95
|
+
# Add length prefix to handle leading zeros
|
|
96
|
+
length_prefix = self._encode_length(len(data))
|
|
97
|
+
|
|
98
|
+
return length_prefix + encoded
|
|
99
|
+
|
|
100
|
+
def decode(self, encoded: str) -> bytes:
|
|
101
|
+
"""
|
|
102
|
+
Decode custom encoded string back to binary data.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
encoded: Encoded string to decode.
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
bytes: Original binary data.
|
|
109
|
+
|
|
110
|
+
Raises:
|
|
111
|
+
ValidationError: If encoded string is invalid.
|
|
112
|
+
"""
|
|
113
|
+
if not isinstance(encoded, str):
|
|
114
|
+
raise ValidationError(
|
|
115
|
+
"Encoded data must be string",
|
|
116
|
+
ErrorCodes.INVALID_INPUT_FORMAT
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
if len(encoded) == 0:
|
|
120
|
+
return b""
|
|
121
|
+
|
|
122
|
+
# Validate characters
|
|
123
|
+
for char in encoded:
|
|
124
|
+
if char not in self.char_to_value:
|
|
125
|
+
raise ValidationError(
|
|
126
|
+
f"Invalid character '{char}' in encoded data",
|
|
127
|
+
ErrorCodes.INVALID_INPUT_FORMAT
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# Extract length prefix
|
|
131
|
+
original_length, data_start = self._decode_length(encoded)
|
|
132
|
+
encoded_data = encoded[data_start:]
|
|
133
|
+
|
|
134
|
+
if len(encoded_data) == 0:
|
|
135
|
+
return b'\x00' * original_length
|
|
136
|
+
|
|
137
|
+
# Convert from custom base to integer
|
|
138
|
+
number = 0
|
|
139
|
+
for char in encoded_data:
|
|
140
|
+
number = number * self.base + self.char_to_value[char]
|
|
141
|
+
|
|
142
|
+
# Convert to bytes with correct length
|
|
143
|
+
if number == 0:
|
|
144
|
+
return b'\x00' * original_length
|
|
145
|
+
|
|
146
|
+
# Calculate required bytes
|
|
147
|
+
byte_length = (number.bit_length() + 7) // 8
|
|
148
|
+
result = number.to_bytes(byte_length, byteorder='big')
|
|
149
|
+
|
|
150
|
+
# Pad with leading zeros if necessary
|
|
151
|
+
if len(result) < original_length:
|
|
152
|
+
result = b'\x00' * (original_length - len(result)) + result
|
|
153
|
+
|
|
154
|
+
return result
|
|
155
|
+
|
|
156
|
+
def _encode_length(self, length: int) -> str:
|
|
157
|
+
"""Encode length as prefix using custom charset."""
|
|
158
|
+
if length == 0:
|
|
159
|
+
return self.charset[0] + self.padding_char
|
|
160
|
+
|
|
161
|
+
result = []
|
|
162
|
+
while length > 0:
|
|
163
|
+
result.append(self.value_to_char[length % self.base])
|
|
164
|
+
length //= self.base
|
|
165
|
+
|
|
166
|
+
# Add separator
|
|
167
|
+
return ''.join(reversed(result)) + self.padding_char
|
|
168
|
+
|
|
169
|
+
def _decode_length(self, encoded: str) -> Tuple[int, int]:
|
|
170
|
+
"""Decode length prefix and return (length, data_start_index)."""
|
|
171
|
+
separator_pos = encoded.find(self.padding_char)
|
|
172
|
+
if separator_pos == -1:
|
|
173
|
+
raise ValidationError(
|
|
174
|
+
"Invalid encoded format: missing length separator",
|
|
175
|
+
ErrorCodes.INVALID_INPUT_FORMAT
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
length_part = encoded[:separator_pos]
|
|
179
|
+
if len(length_part) == 0:
|
|
180
|
+
return 0, separator_pos + 1
|
|
181
|
+
|
|
182
|
+
# Decode length
|
|
183
|
+
length = 0
|
|
184
|
+
for char in length_part:
|
|
185
|
+
length = length * self.base + self.char_to_value[char]
|
|
186
|
+
|
|
187
|
+
return length, separator_pos + 1
|
|
188
|
+
|
|
189
|
+
def encode_with_noise(self, data: Union[bytes, bytearray], noise_ratio: float = 0.1) -> str:
|
|
190
|
+
"""
|
|
191
|
+
Encode data with random noise characters for obfuscation.
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
data: Binary data to encode.
|
|
195
|
+
noise_ratio: Ratio of noise characters to add (0.0 to 1.0).
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
str: Encoded string with noise.
|
|
199
|
+
"""
|
|
200
|
+
if not 0.0 <= noise_ratio <= 1.0:
|
|
201
|
+
raise ValidationError(
|
|
202
|
+
"Noise ratio must be between 0.0 and 1.0",
|
|
203
|
+
ErrorCodes.INVALID_CONFIGURATION
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
# Encode normally
|
|
207
|
+
encoded = self.encode(data)
|
|
208
|
+
|
|
209
|
+
if noise_ratio == 0.0:
|
|
210
|
+
return encoded
|
|
211
|
+
|
|
212
|
+
# Add noise characters
|
|
213
|
+
noise_count = int(len(encoded) * noise_ratio)
|
|
214
|
+
result = list(encoded)
|
|
215
|
+
|
|
216
|
+
for _ in range(noise_count):
|
|
217
|
+
# Insert random character at random position
|
|
218
|
+
pos = secrets.randbelow(len(result) + 1)
|
|
219
|
+
noise_char = secrets.choice(self.charset)
|
|
220
|
+
result.insert(pos, noise_char)
|
|
221
|
+
|
|
222
|
+
# Mark noise positions (simple approach - use pattern)
|
|
223
|
+
# In real implementation, you might want more sophisticated noise marking
|
|
224
|
+
return ''.join(result)
|
|
225
|
+
|
|
226
|
+
def create_steganographic_text(self, data: Union[bytes, bytearray],
|
|
227
|
+
template: str = "The quick brown fox jumps over the lazy dog") -> str:
|
|
228
|
+
"""
|
|
229
|
+
Create steganographic text that hides data in character substitutions.
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
data: Binary data to hide.
|
|
233
|
+
template: Template text to use as base.
|
|
234
|
+
|
|
235
|
+
Returns:
|
|
236
|
+
str: Text with hidden data.
|
|
237
|
+
"""
|
|
238
|
+
encoded = self.encode(data)
|
|
239
|
+
|
|
240
|
+
# Simple steganography: replace characters in template
|
|
241
|
+
result = list(template.lower())
|
|
242
|
+
encoded_pos = 0
|
|
243
|
+
|
|
244
|
+
for i, char in enumerate(result):
|
|
245
|
+
if char.isalpha() and encoded_pos < len(encoded):
|
|
246
|
+
# Map alphabet to our charset
|
|
247
|
+
if char in 'abcdef':
|
|
248
|
+
result[i] = encoded[encoded_pos]
|
|
249
|
+
encoded_pos += 1
|
|
250
|
+
elif char in 'ghijklmnop':
|
|
251
|
+
# Map to numbers in our charset
|
|
252
|
+
if '9' in self.charset and '8' in self.charset:
|
|
253
|
+
result[i] = '9' if (ord(char) % 2) else '8'
|
|
254
|
+
if encoded_pos < len(encoded):
|
|
255
|
+
encoded_pos += 1
|
|
256
|
+
|
|
257
|
+
return ''.join(result)
|
|
258
|
+
|
|
259
|
+
def get_charset_info(self) -> dict:
|
|
260
|
+
"""
|
|
261
|
+
Get information about the current charset.
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
dict: Charset information.
|
|
265
|
+
"""
|
|
266
|
+
return {
|
|
267
|
+
'charset': self.charset,
|
|
268
|
+
'base': self.base,
|
|
269
|
+
'padding_char': self.padding_char,
|
|
270
|
+
'efficiency': f"{(8 * 8) / (len(self.charset).bit_length() * 8):.2%}",
|
|
271
|
+
'characters': list(self.charset),
|
|
272
|
+
'unique_count': len(self.charset)
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
def benchmark_encoding(self, data_size: int = 1024) -> dict:
|
|
276
|
+
"""
|
|
277
|
+
Benchmark encoding performance.
|
|
278
|
+
|
|
279
|
+
Args:
|
|
280
|
+
data_size: Size of test data in bytes.
|
|
281
|
+
|
|
282
|
+
Returns:
|
|
283
|
+
dict: Benchmark results.
|
|
284
|
+
"""
|
|
285
|
+
import time
|
|
286
|
+
|
|
287
|
+
# Generate test data
|
|
288
|
+
test_data = secrets.token_bytes(data_size)
|
|
289
|
+
|
|
290
|
+
# Benchmark encoding
|
|
291
|
+
start_time = time.time()
|
|
292
|
+
encoded = self.encode(test_data)
|
|
293
|
+
encode_time = time.time() - start_time
|
|
294
|
+
|
|
295
|
+
# Benchmark decoding
|
|
296
|
+
start_time = time.time()
|
|
297
|
+
decoded = self.decode(encoded)
|
|
298
|
+
decode_time = time.time() - start_time
|
|
299
|
+
|
|
300
|
+
return {
|
|
301
|
+
'data_size': data_size,
|
|
302
|
+
'encoded_size': len(encoded),
|
|
303
|
+
'expansion_ratio': len(encoded) / data_size,
|
|
304
|
+
'encode_time': encode_time,
|
|
305
|
+
'decode_time': decode_time,
|
|
306
|
+
'encode_speed_mbps': (data_size / (1024 * 1024)) / encode_time,
|
|
307
|
+
'decode_speed_mbps': (data_size / (1024 * 1024)) / decode_time,
|
|
308
|
+
'correctness': test_data == decoded
|
|
309
|
+
}
|