mincrypt 1.1.7__py3-none-win_amd64.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.
- mincrypt/__init__.py +44 -0
- mincrypt/caesar.py +13 -0
- mincrypt/exceptions.py +10 -0
- mincrypt/fastcalc.py +184 -0
- mincrypt/fastcalc.pyd +0 -0
- mincrypt/rail_fence.py +50 -0
- mincrypt/vigenere.py +37 -0
- mincrypt/xor.py +38 -0
- mincrypt-1.1.7.dist-info/METADATA +243 -0
- mincrypt-1.1.7.dist-info/RECORD +12 -0
- mincrypt-1.1.7.dist-info/WHEEL +5 -0
- mincrypt-1.1.7.dist-info/top_level.txt +1 -0
mincrypt/__init__.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from . import caesar, fastcalc, rail_fence, vigenere, xor
|
|
3
|
+
from .exceptions import InvalidAlgorithmError, InvalidKeyError
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"caesar",
|
|
7
|
+
"vigenere",
|
|
8
|
+
"xor",
|
|
9
|
+
"rail_fence",
|
|
10
|
+
"fastcalc",
|
|
11
|
+
"encrypt",
|
|
12
|
+
"decrypt",
|
|
13
|
+
"InvalidAlgorithmError",
|
|
14
|
+
"InvalidKeyError",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
_ALGORITHMS = {
|
|
18
|
+
"caesar": caesar,
|
|
19
|
+
"vigenere": vigenere,
|
|
20
|
+
"xor": xor,
|
|
21
|
+
"rail_fence": rail_fence,
|
|
22
|
+
"rail-fence": rail_fence,
|
|
23
|
+
"railfence": rail_fence,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _get_algorithm(name: str):
|
|
28
|
+
try:
|
|
29
|
+
return _ALGORITHMS[name.lower()]
|
|
30
|
+
except KeyError as exc:
|
|
31
|
+
available = ", ".join(sorted(_ALGORITHMS))
|
|
32
|
+
raise InvalidAlgorithmError(
|
|
33
|
+
f"Unknown algorithm {name!r}. Available: {available}."
|
|
34
|
+
) from exc
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def encrypt(text: str, algorithm: str, **options) -> str:
|
|
38
|
+
module = _get_algorithm(algorithm)
|
|
39
|
+
return module.encrypt(text, **options)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def decrypt(text: str, algorithm: str, **options) -> str:
|
|
43
|
+
module = _get_algorithm(algorithm)
|
|
44
|
+
return module.decrypt(text, **options)
|
mincrypt/caesar.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from . import fastcalc
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def encrypt(text: str, shift: int = 3) -> str:
|
|
7
|
+
"""Encrypt text with a Caesar shift."""
|
|
8
|
+
return fastcalc.shift_text(text, shift)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def decrypt(text: str, shift: int = 3) -> str:
|
|
12
|
+
"""Decrypt text encrypted with the same Caesar shift."""
|
|
13
|
+
return fastcalc.shift_text(text, -shift)
|
mincrypt/exceptions.py
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
class CryptoError(Exception):
|
|
2
|
+
"""Base error for mincrypt."""
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class InvalidKeyError(CryptoError, ValueError):
|
|
6
|
+
"""Raised when a key is empty or invalid."""
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class InvalidAlgorithmError(CryptoError, ValueError):
|
|
10
|
+
"""Raised when an unknown algorithm name is requested."""
|
mincrypt/fastcalc.py
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def mod(value: int, base: int) -> int:
|
|
5
|
+
"""Return value modulo base."""
|
|
6
|
+
if base == 0:
|
|
7
|
+
raise ValueError("base must not be 0")
|
|
8
|
+
return value % base
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def shift_char(char: str, shift: int) -> str:
|
|
12
|
+
"""
|
|
13
|
+
Shift one alphabet character.
|
|
14
|
+
|
|
15
|
+
Non alphabet characters are returned unchanged.
|
|
16
|
+
Keeps original uppercase/lowercase.
|
|
17
|
+
"""
|
|
18
|
+
if len(char) != 1:
|
|
19
|
+
raise ValueError("char must be a single character")
|
|
20
|
+
|
|
21
|
+
code = ord(char)
|
|
22
|
+
|
|
23
|
+
if 65 <= code <= 90:
|
|
24
|
+
return chr(((code - 65 + shift) % 26) + 65)
|
|
25
|
+
|
|
26
|
+
if 97 <= code <= 122:
|
|
27
|
+
return chr(((code - 97 + shift) % 26) + 97)
|
|
28
|
+
|
|
29
|
+
return char
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def shift_text(text: str, shift: int) -> str:
|
|
33
|
+
"""
|
|
34
|
+
Shift all alphabet characters in text.
|
|
35
|
+
|
|
36
|
+
Non alphabet characters are kept unchanged.
|
|
37
|
+
"""
|
|
38
|
+
result = []
|
|
39
|
+
|
|
40
|
+
for char in text:
|
|
41
|
+
code = ord(char)
|
|
42
|
+
|
|
43
|
+
if 65 <= code <= 90:
|
|
44
|
+
result.append(chr(((code - 65 + shift) % 26) + 65))
|
|
45
|
+
elif 97 <= code <= 122:
|
|
46
|
+
result.append(chr(((code - 97 + shift) % 26) + 97))
|
|
47
|
+
else:
|
|
48
|
+
result.append(char)
|
|
49
|
+
|
|
50
|
+
return "".join(result)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def alpha_shift_value(char: str) -> int:
|
|
54
|
+
"""
|
|
55
|
+
Convert one alphabet character to a shift value.
|
|
56
|
+
|
|
57
|
+
A/a -> 0
|
|
58
|
+
B/b -> 1
|
|
59
|
+
...
|
|
60
|
+
Z/z -> 25
|
|
61
|
+
"""
|
|
62
|
+
if len(char) != 1:
|
|
63
|
+
raise ValueError("char must be a single character")
|
|
64
|
+
|
|
65
|
+
code = ord(char)
|
|
66
|
+
|
|
67
|
+
if 65 <= code <= 90:
|
|
68
|
+
return code - 65
|
|
69
|
+
|
|
70
|
+
if 97 <= code <= 122:
|
|
71
|
+
return code - 97
|
|
72
|
+
|
|
73
|
+
raise ValueError("char must be an alphabet character")
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def alpha_shift_values(text: str) -> list[int]:
|
|
77
|
+
"""
|
|
78
|
+
Convert alphabet characters in text to shift values.
|
|
79
|
+
|
|
80
|
+
Non alphabet characters are ignored.
|
|
81
|
+
"""
|
|
82
|
+
values = []
|
|
83
|
+
|
|
84
|
+
for char in text:
|
|
85
|
+
code = ord(char)
|
|
86
|
+
|
|
87
|
+
if 65 <= code <= 90:
|
|
88
|
+
values.append(code - 65)
|
|
89
|
+
elif 97 <= code <= 122:
|
|
90
|
+
values.append(code - 97)
|
|
91
|
+
|
|
92
|
+
return values
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def text_to_bytes(value: str, encoding: str = "utf-8") -> bytes:
|
|
96
|
+
"""Convert text to bytes."""
|
|
97
|
+
return value.encode(encoding)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def bytes_to_text(value: bytes, encoding: str = "utf-8") -> str:
|
|
101
|
+
"""Convert bytes to text."""
|
|
102
|
+
return value.decode(encoding)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def repeat_key_bytes(key: bytes, length: int) -> bytes:
|
|
106
|
+
"""
|
|
107
|
+
Repeat key bytes until reaching target length.
|
|
108
|
+
|
|
109
|
+
Example:
|
|
110
|
+
key=b"ab", length=5 -> b"ababa"
|
|
111
|
+
"""
|
|
112
|
+
if length < 0:
|
|
113
|
+
raise ValueError("length must be >= 0")
|
|
114
|
+
|
|
115
|
+
if not key:
|
|
116
|
+
raise ValueError("key must not be empty")
|
|
117
|
+
|
|
118
|
+
repeated = bytearray()
|
|
119
|
+
|
|
120
|
+
while len(repeated) < length:
|
|
121
|
+
repeated.extend(key)
|
|
122
|
+
|
|
123
|
+
return bytes(repeated[:length])
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def xor_bytes(data: bytes, key: bytes) -> bytes:
|
|
127
|
+
"""
|
|
128
|
+
XOR data bytes with repeating key bytes.
|
|
129
|
+
|
|
130
|
+
Returns raw bytes.
|
|
131
|
+
"""
|
|
132
|
+
if not key:
|
|
133
|
+
raise ValueError("key must not be empty")
|
|
134
|
+
|
|
135
|
+
result = bytearray()
|
|
136
|
+
key_length = len(key)
|
|
137
|
+
|
|
138
|
+
for index, byte in enumerate(data):
|
|
139
|
+
result.append(byte ^ key[index % key_length])
|
|
140
|
+
|
|
141
|
+
return bytes(result)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def rail_index_pattern(length: int, rails: int) -> list[int]:
|
|
145
|
+
"""
|
|
146
|
+
Return rail index pattern for Rail Fence calculation.
|
|
147
|
+
|
|
148
|
+
Example:
|
|
149
|
+
length=8, rails=3 -> [0, 1, 2, 1, 0, 1, 2, 1]
|
|
150
|
+
"""
|
|
151
|
+
if length < 0:
|
|
152
|
+
raise ValueError("length must be >= 0")
|
|
153
|
+
|
|
154
|
+
if rails < 2:
|
|
155
|
+
raise ValueError("rails must be >= 2")
|
|
156
|
+
|
|
157
|
+
pattern = []
|
|
158
|
+
rail = 0
|
|
159
|
+
direction = 1
|
|
160
|
+
|
|
161
|
+
for _ in range(length):
|
|
162
|
+
pattern.append(rail)
|
|
163
|
+
|
|
164
|
+
rail += direction
|
|
165
|
+
|
|
166
|
+
if rail == 0 or rail == rails - 1:
|
|
167
|
+
direction *= -1
|
|
168
|
+
|
|
169
|
+
return pattern
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def split_by_indexes(text: str, indexes: list[int], target: int) -> str:
|
|
173
|
+
"""
|
|
174
|
+
Return characters from text where indexes match target.
|
|
175
|
+
|
|
176
|
+
Used by algorithms that group characters by calculated indexes.
|
|
177
|
+
"""
|
|
178
|
+
result = []
|
|
179
|
+
|
|
180
|
+
for position, char in enumerate(text):
|
|
181
|
+
if indexes[position] == target:
|
|
182
|
+
result.append(char)
|
|
183
|
+
|
|
184
|
+
return "".join(result)
|
mincrypt/fastcalc.pyd
ADDED
|
Binary file
|
mincrypt/rail_fence.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from . import fastcalc
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def _validate_rails(rails: int) -> None:
|
|
7
|
+
if not isinstance(rails, int):
|
|
8
|
+
raise TypeError("rails must be an int")
|
|
9
|
+
if rails < 2:
|
|
10
|
+
raise ValueError("rails must be at least 2")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def encrypt(text: str, rails: int = 3) -> str:
|
|
14
|
+
"""Encrypt text with a Rail Fence transposition."""
|
|
15
|
+
_validate_rails(rails)
|
|
16
|
+
|
|
17
|
+
pattern = fastcalc.rail_index_pattern(len(text), rails)
|
|
18
|
+
rows = []
|
|
19
|
+
|
|
20
|
+
for rail in range(rails):
|
|
21
|
+
rows.append(fastcalc.split_by_indexes(text, pattern, rail))
|
|
22
|
+
|
|
23
|
+
return "".join(rows)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def decrypt(text: str, rails: int = 3) -> str:
|
|
27
|
+
"""Decrypt text encrypted with the same rail count."""
|
|
28
|
+
_validate_rails(rails)
|
|
29
|
+
|
|
30
|
+
pattern = fastcalc.rail_index_pattern(len(text), rails)
|
|
31
|
+
counts = [0] * rails
|
|
32
|
+
|
|
33
|
+
for rail in pattern:
|
|
34
|
+
counts[rail] += 1
|
|
35
|
+
|
|
36
|
+
rows = []
|
|
37
|
+
start = 0
|
|
38
|
+
|
|
39
|
+
for count in counts:
|
|
40
|
+
rows.append(list(text[start : start + count]))
|
|
41
|
+
start += count
|
|
42
|
+
|
|
43
|
+
indexes = [0] * rails
|
|
44
|
+
output = []
|
|
45
|
+
|
|
46
|
+
for rail in pattern:
|
|
47
|
+
output.append(rows[rail][indexes[rail]])
|
|
48
|
+
indexes[rail] += 1
|
|
49
|
+
|
|
50
|
+
return "".join(output)
|
mincrypt/vigenere.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from . import fastcalc
|
|
4
|
+
from .exceptions import InvalidKeyError
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _crypt(text: str, key: str, direction: int) -> str:
|
|
8
|
+
shifts = fastcalc.alpha_shift_values(key)
|
|
9
|
+
|
|
10
|
+
if not shifts:
|
|
11
|
+
raise InvalidKeyError("Vigenere key must contain at least one letter.")
|
|
12
|
+
|
|
13
|
+
result = []
|
|
14
|
+
key_index = 0
|
|
15
|
+
key_length = len(shifts)
|
|
16
|
+
|
|
17
|
+
for char in text:
|
|
18
|
+
code = ord(char)
|
|
19
|
+
|
|
20
|
+
if 65 <= code <= 90 or 97 <= code <= 122:
|
|
21
|
+
shift = shifts[key_index % key_length] * direction
|
|
22
|
+
result.append(fastcalc.shift_char(char, shift))
|
|
23
|
+
key_index += 1
|
|
24
|
+
else:
|
|
25
|
+
result.append(char)
|
|
26
|
+
|
|
27
|
+
return "".join(result)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def encrypt(text: str, key: str) -> str:
|
|
31
|
+
"""Encrypt text with a repeating Vigenere key."""
|
|
32
|
+
return _crypt(text, key, direction=1)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def decrypt(text: str, key: str) -> str:
|
|
36
|
+
"""Decrypt text encrypted with the same Vigenere key."""
|
|
37
|
+
return _crypt(text, key, direction=-1)
|
mincrypt/xor.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import base64
|
|
4
|
+
|
|
5
|
+
from . import fastcalc
|
|
6
|
+
from .exceptions import InvalidKeyError
|
|
7
|
+
|
|
8
|
+
BytesLike = bytes | bytearray | memoryview | str
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _to_bytes(value: BytesLike) -> bytes:
|
|
12
|
+
if isinstance(value, str):
|
|
13
|
+
return fastcalc.text_to_bytes(value)
|
|
14
|
+
return bytes(value)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def xor_bytes(data: BytesLike, key: BytesLike) -> bytes:
|
|
18
|
+
"""XOR bytes with a repeating key and return raw bytes."""
|
|
19
|
+
data_bytes = _to_bytes(data)
|
|
20
|
+
key_bytes = _to_bytes(key)
|
|
21
|
+
|
|
22
|
+
if not key_bytes:
|
|
23
|
+
raise InvalidKeyError("XOR key must not be empty.")
|
|
24
|
+
|
|
25
|
+
return fastcalc.xor_bytes(data_bytes, key_bytes)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def encrypt(text: str, key: BytesLike) -> str:
|
|
29
|
+
"""Encrypt text and return a URL-safe Base64 token."""
|
|
30
|
+
encrypted = xor_bytes(text, key)
|
|
31
|
+
return fastcalc.bytes_to_text(base64.urlsafe_b64encode(encrypted), encoding="ascii")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def decrypt(token: str, key: BytesLike) -> str:
|
|
35
|
+
"""Decrypt a Base64 token produced by encrypt()."""
|
|
36
|
+
encrypted = base64.urlsafe_b64decode(fastcalc.text_to_bytes(token, encoding="ascii"))
|
|
37
|
+
decrypted = xor_bytes(encrypted, key)
|
|
38
|
+
return fastcalc.bytes_to_text(decrypted)
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mincrypt
|
|
3
|
+
Version: 1.1.7
|
|
4
|
+
Summary: Simple classical cryptography utilities.
|
|
5
|
+
Author: jishino
|
|
6
|
+
Author-email: jishino@gmail.com
|
|
7
|
+
License: MIT
|
|
8
|
+
Keywords: crypto,cryptography
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
16
|
+
Classifier: Topic :: Terminals
|
|
17
|
+
Classifier: Topic :: Utilities
|
|
18
|
+
Requires-Python: >=3.8
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
Dynamic: author
|
|
21
|
+
Dynamic: author-email
|
|
22
|
+
Dynamic: classifier
|
|
23
|
+
Dynamic: description
|
|
24
|
+
Dynamic: description-content-type
|
|
25
|
+
Dynamic: keywords
|
|
26
|
+
Dynamic: license
|
|
27
|
+
Dynamic: requires-python
|
|
28
|
+
Dynamic: summary
|
|
29
|
+
|
|
30
|
+
`mincrypt` includes Caesar, Vigenere, XOR, and Rail Fence helpers, plus generic `encrypt()` and `decrypt()` functions for selecting an algorithm by name.
|
|
31
|
+
|
|
32
|
+
## Features
|
|
33
|
+
|
|
34
|
+
- Caesar shift encryption and decryption
|
|
35
|
+
- Vigenere encryption and decryption with repeating alphabetic keys
|
|
36
|
+
- XOR encryption that returns URL-safe Base64 tokens
|
|
37
|
+
- Rail Fence transposition encryption and decryption
|
|
38
|
+
- Generic algorithm dispatch with `encrypt()` and `decrypt()`
|
|
39
|
+
- Small helper functions for shifts, bytes, XOR, modulo, and rail patterns
|
|
40
|
+
- Custom exceptions for invalid keys and unknown algorithms
|
|
41
|
+
|
|
42
|
+
## Requirements
|
|
43
|
+
|
|
44
|
+
- Python 3.8 or newer
|
|
45
|
+
|
|
46
|
+
## Installation
|
|
47
|
+
|
|
48
|
+
From the project root, install the package in editable mode:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
python -m pip install -e .
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Or, during local development, keep the package directory on your `PYTHONPATH` and import it directly:
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
import mincrypt
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Quick start
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
from mincrypt import caesar, decrypt, encrypt, rail_fence, vigenere, xor
|
|
64
|
+
|
|
65
|
+
message = "Hello UIPackage!"
|
|
66
|
+
|
|
67
|
+
caesar_cipher = caesar.encrypt(message, shift=4)
|
|
68
|
+
print(caesar_cipher)
|
|
69
|
+
print(caesar.decrypt(caesar_cipher, shift=4))
|
|
70
|
+
|
|
71
|
+
vigenere_cipher = vigenere.encrypt(message, key="secret")
|
|
72
|
+
print(vigenere_cipher)
|
|
73
|
+
print(vigenere.decrypt(vigenere_cipher, key="secret"))
|
|
74
|
+
|
|
75
|
+
xor_cipher = xor.encrypt(message, key="secret")
|
|
76
|
+
print(xor_cipher)
|
|
77
|
+
print(xor.decrypt(xor_cipher, key="secret"))
|
|
78
|
+
|
|
79
|
+
rail_cipher = rail_fence.encrypt(message, rails=3)
|
|
80
|
+
print(rail_cipher)
|
|
81
|
+
print(rail_fence.decrypt(rail_cipher, rails=3))
|
|
82
|
+
|
|
83
|
+
# Generic helper
|
|
84
|
+
cipher = encrypt(message, algorithm="caesar", shift=2)
|
|
85
|
+
print(cipher)
|
|
86
|
+
print(decrypt(cipher, algorithm="caesar", shift=2))
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Example output:
|
|
90
|
+
|
|
91
|
+
```text
|
|
92
|
+
Lipps YMTegoeki!
|
|
93
|
+
Hello UIPackage!
|
|
94
|
+
Zincs NATctotyi!
|
|
95
|
+
Hello UIPackage!
|
|
96
|
+
OwAPHgpUJiwzEwYfEgIGUw==
|
|
97
|
+
Hello UIPackage!
|
|
98
|
+
HoPael Iakg!lUce
|
|
99
|
+
Hello UIPackage!
|
|
100
|
+
Jgnnq WKRcemcig!
|
|
101
|
+
Hello UIPackage!
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Algorithms
|
|
105
|
+
|
|
106
|
+
### Caesar
|
|
107
|
+
|
|
108
|
+
Shift alphabetic characters by a fixed number. Uppercase and lowercase letters are preserved, and non-alphabetic characters are left unchanged.
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
from mincrypt import caesar
|
|
112
|
+
|
|
113
|
+
cipher = caesar.encrypt("Attack at dawn!", shift=3)
|
|
114
|
+
plain = caesar.decrypt(cipher, shift=3)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Vigenere
|
|
118
|
+
|
|
119
|
+
Encrypt text using a repeating alphabetic key. Non-alphabetic characters are preserved and do not consume key characters.
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
from mincrypt import vigenere
|
|
123
|
+
|
|
124
|
+
cipher = vigenere.encrypt("Attack at dawn!", key="lemon")
|
|
125
|
+
plain = vigenere.decrypt(cipher, key="lemon")
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
A Vigenere key must contain at least one alphabetic character.
|
|
129
|
+
|
|
130
|
+
### XOR
|
|
131
|
+
|
|
132
|
+
XOR text with a repeating key. `encrypt()` returns a URL-safe Base64 token, and `decrypt()` converts that token back to text.
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
from mincrypt import xor
|
|
136
|
+
|
|
137
|
+
cipher = xor.encrypt("Attack at dawn!", key="secret")
|
|
138
|
+
plain = xor.decrypt(cipher, key="secret")
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
For raw bytes, use `xor.xor_bytes()`:
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
from mincrypt import xor
|
|
145
|
+
|
|
146
|
+
raw = xor.xor_bytes(b"data", b"key")
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Rail Fence
|
|
150
|
+
|
|
151
|
+
Encrypt text with a Rail Fence transposition using two or more rails.
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
from mincrypt import rail_fence
|
|
155
|
+
|
|
156
|
+
cipher = rail_fence.encrypt("Attack at dawn!", rails=3)
|
|
157
|
+
plain = rail_fence.decrypt(cipher, rails=3)
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Generic API
|
|
161
|
+
|
|
162
|
+
Use `encrypt()` and `decrypt()` when you want to choose an algorithm dynamically:
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
from mincrypt import encrypt, decrypt
|
|
166
|
+
|
|
167
|
+
cipher = encrypt("Hello", algorithm="caesar", shift=3)
|
|
168
|
+
plain = decrypt(cipher, algorithm="caesar", shift=3)
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Supported algorithm names:
|
|
172
|
+
|
|
173
|
+
- `caesar`
|
|
174
|
+
- `vigenere`
|
|
175
|
+
- `xor`
|
|
176
|
+
- `rail_fence`
|
|
177
|
+
- `rail-fence`
|
|
178
|
+
- `railfence`
|
|
179
|
+
|
|
180
|
+
Algorithm-specific options are passed as keyword arguments:
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
encrypt("Hello", algorithm="caesar", shift=3)
|
|
184
|
+
encrypt("Hello", algorithm="vigenere", key="secret")
|
|
185
|
+
encrypt("Hello", algorithm="xor", key="secret")
|
|
186
|
+
encrypt("Hello", algorithm="rail_fence", rails=3)
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Exceptions
|
|
190
|
+
|
|
191
|
+
```python
|
|
192
|
+
from mincrypt import InvalidAlgorithmError, InvalidKeyError
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
- `InvalidKeyError` is raised when a required key is empty or invalid.
|
|
196
|
+
- `InvalidAlgorithmError` is raised when the generic API receives an unknown algorithm name.
|
|
197
|
+
|
|
198
|
+
Example:
|
|
199
|
+
|
|
200
|
+
```python
|
|
201
|
+
from mincrypt import InvalidAlgorithmError, encrypt
|
|
202
|
+
|
|
203
|
+
try:
|
|
204
|
+
encrypt("Hello", algorithm="unknown")
|
|
205
|
+
except InvalidAlgorithmError as exc:
|
|
206
|
+
print(exc)
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## Helper functions
|
|
210
|
+
|
|
211
|
+
The `fastcalc` module contains lower-level helpers used by the algorithms:
|
|
212
|
+
|
|
213
|
+
- `mod(value, base)`
|
|
214
|
+
- `shift_char(char, shift)`
|
|
215
|
+
- `shift_text(text, shift)`
|
|
216
|
+
- `alpha_shift_value(char)`
|
|
217
|
+
- `alpha_shift_values(text)`
|
|
218
|
+
- `text_to_bytes(value, encoding="utf-8")`
|
|
219
|
+
- `bytes_to_text(value, encoding="utf-8")`
|
|
220
|
+
- `repeat_key_bytes(key, length)`
|
|
221
|
+
- `xor_bytes(data, key)`
|
|
222
|
+
- `rail_index_pattern(length, rails)`
|
|
223
|
+
- `split_by_indexes(text, indexes, target)`
|
|
224
|
+
|
|
225
|
+
Most users should prefer the higher-level algorithm modules unless they need these building blocks directly.
|
|
226
|
+
|
|
227
|
+
## Project layout
|
|
228
|
+
|
|
229
|
+
```text
|
|
230
|
+
mincrypt/
|
|
231
|
+
├── __init__.py
|
|
232
|
+
├── caesar.py
|
|
233
|
+
├── exceptions.py
|
|
234
|
+
├── fastcalc.py
|
|
235
|
+
├── rail_fence.py
|
|
236
|
+
├── vigenere.py
|
|
237
|
+
└── xor.py
|
|
238
|
+
README.md
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## License
|
|
242
|
+
|
|
243
|
+
MIT
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
mincrypt/__init__.py,sha256=r5p8ZNA0eswX233DXBeydKQ_J_1e8oVE5B2ntK6L4Ew,1069
|
|
2
|
+
mincrypt/caesar.py,sha256=EpCC9d3GJPOFjitVZAhifbdoRLdQp2Wl3oY6L7sRlxQ,351
|
|
3
|
+
mincrypt/exceptions.py,sha256=Hug61Tg39gB98cjRn9I1VtxdiUw5fI6DVp0pBBOTE6s,282
|
|
4
|
+
mincrypt/fastcalc.py,sha256=u-lO_JRgtlnp-llK7EVV4GFgcKIH3AUBliL0X6d1B-c,3998
|
|
5
|
+
mincrypt/fastcalc.pyd,sha256=NatTylblbQ4YHsjh7gYM5ta4y5oKXMSzZdGeCJ_YiKQ,233984
|
|
6
|
+
mincrypt/rail_fence.py,sha256=VUgqzHfnGqM5_wHcGfhAfpfOVmencdXeEzRkNdyZtbE,1167
|
|
7
|
+
mincrypt/vigenere.py,sha256=QGBpistNJFMtqyY6YP8rOzRXRbrUGu1QEUOp7GRXSeo,976
|
|
8
|
+
mincrypt/xor.py,sha256=q8Q2S73JGLY79DtTW6qDdF-C-0ExuTV2b2G3kHjSQd8,1148
|
|
9
|
+
mincrypt-1.1.7.dist-info/METADATA,sha256=MYhbaj5t6PQioGdylVsNAFXFcVH7I94E3rn0IRJw35Q,5834
|
|
10
|
+
mincrypt-1.1.7.dist-info/WHEEL,sha256=s_n0jCiMo-4SyzSB02xUGmNO5EUQXxQMERsYM7CCHiM,97
|
|
11
|
+
mincrypt-1.1.7.dist-info/top_level.txt,sha256=vue3km76FXK2IFlYN48MgnrWZwOQWgUBFh7SqqGdmxY,9
|
|
12
|
+
mincrypt-1.1.7.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
mincrypt
|