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 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,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.8.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-win_amd64
5
+
@@ -0,0 +1 @@
1
+ mincrypt