Skip to content

Commit 2586215

Browse files
committed
Added new codec: vigenere
1 parent 1240004 commit 2586215

File tree

4 files changed

+85
-0
lines changed

4 files changed

+85
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ This category also contains `ascii85`, `adobe`, `[x]btoa`, `zeromq` with the `ba
290290
- [X] `rotN`: aka Caesar cipher (*N* belongs to [1,25])
291291
- [X] `scytaleN`: encrypts using the number of letters on the rod (*N* belongs to [1,[)
292292
- [X] `shiftN`: shift ordinals (*N* belongs to [1,255])
293+
- [X] `vigenere`: aka Vigenere Cipher
293294
- [X] `xorN`: XOR with a single byte (*N* belongs to [1,255])
294295

295296
> :warning: Crypto functions are of course definitely **NOT** encoding functions ; they are implemented for leveraging the `.encode(...)` API from `codecs`.

docs/pages/enc/crypto.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,24 @@ This is a dynamic encoding, that is, it can be called with an integer to define
202202

203203
-----
204204

205+
### Vigenere Cipher
206+
207+
This is a dynamic encoding, that is, it holds the key. There is no default key, meaning that `vigenere` as the encoding scheme throws a `LookupError` indicating that the _key must be a non-empty alphabetic string_.
208+
209+
**Codec** | **Conversions** | **Aliases** | **Comment**
210+
:---: | :---: | --- | ---
211+
`vigenere` | text <-> Vigenere ciphertext | `vigenere-abcdef`, `vigenere_MySuperSecret` | key only consists of characters, not digits
212+
213+
```python
214+
>>> codext.encode("This is a test !", "vigenere-abababa")
215+
'Tiit it a tfsu !'
216+
>>> codext.encode("This is a test !", "vigenere_MySuperSecret")
217+
'Ffam xw r liuk !'
218+
>>> codext.decode("Tiit it a tfsu !", "vigenere-abababa")
219+
```
220+
221+
-----
222+
205223
### XOR with 1 byte
206224

207225
This is a dynamic encoding, that is, it can be called with an integer to define the ordinal of the byte to XOR with the input text.

src/codext/crypto/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@
99
from .rot import *
1010
from .scytale import *
1111
from .shift import *
12+
from .vigenere import *
1213
from .xor import *
1314

src/codext/crypto/vigenere.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# -*- coding: UTF-8 -*-
2+
"""Vigenere Cipher Codec - vigenere content encoding.
3+
4+
This codec:
5+
- en/decodes strings from str to str
6+
- en/decodes strings from bytes to bytes
7+
- decodes file content to str (read)
8+
- encodes file content from str to bytes (write)
9+
"""
10+
from string import ascii_lowercase as LC, ascii_uppercase as UC
11+
12+
from ..__common__ import *
13+
14+
15+
__examples__ = {
16+
'enc(vigenere)': None,
17+
'enc(vigenere-lemon)': {'ATTACKATDAWN': 'LXFOPVEFRNHR'},
18+
'enc(vigenere-key)': {'hello': 'rijvs'},
19+
'enc(vigenère_key)': {'Hello World': 'Rijvs Uyvjn'},
20+
'enc-dec(vigenere-secret)': ['hello world', 'ATTACK AT DAWN', 'Test 1234!'],
21+
}
22+
__guess__ = ["vigenere-key", "vigenere-secret", "vigenere-password"]
23+
24+
25+
__char = lambda c, k, i, d=False: (LC if (b := c in LC) else UC)[(ord(c) - ord("Aa"[b]) + \
26+
[1, -1][d] * (ord(k[i % len(k)]) - ord('a'))) % 26]
27+
28+
29+
def __check(key):
30+
key = key.lower()
31+
if not key or not key.isalpha():
32+
raise LookupError("Bad parameter for encoding 'vigenere': key must be a non-empty alphabetic string")
33+
return key
34+
35+
36+
def vigenere_encode(key):
37+
def encode(text, errors="strict"):
38+
result, i, k = [], 0, __check(key)
39+
for c in ensure_str(text):
40+
if c in LC or c in UC:
41+
result.append(__char(c, k, i))
42+
i += 1
43+
else:
44+
result.append(c)
45+
r = "".join(result)
46+
return r, len(r)
47+
return encode
48+
49+
50+
def vigenere_decode(key):
51+
def decode(text, errors="strict"):
52+
result, i, k = [], 0, __check(key)
53+
for c in ensure_str(text):
54+
if c in LC or c in UC:
55+
result.append(__char(c, k, i, True))
56+
i += 1
57+
else:
58+
result.append(c)
59+
r = "".join(result)
60+
return r, len(r)
61+
return decode
62+
63+
64+
add("vigenere", vigenere_encode, vigenere_decode, r"vigen[eè]re(?:[-_]cipher)?(?:[-_]([a-zA-Z]+))?$", penalty=.1)
65+

0 commit comments

Comments
 (0)