codext 1.16.3__tar.gz → 1.16.5__tar.gz
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.
- {codext-1.16.3 → codext-1.16.5}/PKG-INFO +2 -1
- {codext-1.16.3 → codext-1.16.5}/README.md +1 -0
- codext-1.16.5/docs/hakin9-codext.pdf +0 -0
- {codext-1.16.3 → codext-1.16.5}/docs/pages/enc/crypto.md +19 -1
- codext-1.16.5/src/codext/VERSION.txt +1 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/base/_base.py +6 -4
- {codext-1.16.3 → codext-1.16.5}/src/codext/checksums/crc.py +2 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/crypto/__init__.py +1 -1
- {codext-1.16.3 → codext-1.16.5}/src/codext/crypto/phillips.py +28 -50
- codext-1.16.5/src/codext/crypto/playfair.py +143 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/crypto/polybius.py +6 -7
- {codext-1.16.3 → codext-1.16.5}/src/codext/crypto/vic.py +1 -1
- {codext-1.16.3 → codext-1.16.5}/src/codext/hashing/crypt.py +2 -2
- {codext-1.16.3 → codext-1.16.5}/src/codext/languages/ipsum.py +8 -8
- {codext-1.16.3 → codext-1.16.5}/src/codext/others/uuencode.py +7 -7
- {codext-1.16.3 → codext-1.16.5}/src/codext/stegano/hexagram.py +4 -3
- {codext-1.16.3 → codext-1.16.5}/src/codext/stegano/whitespace.py +22 -21
- {codext-1.16.3 → codext-1.16.5}/src/codext.egg-info/PKG-INFO +2 -1
- {codext-1.16.3 → codext-1.16.5}/src/codext.egg-info/SOURCES.txt +2 -0
- {codext-1.16.3 → codext-1.16.5}/tests/test_base.py +24 -1
- {codext-1.16.3 → codext-1.16.5}/tests/test_manual.py +20 -2
- codext-1.16.3/src/codext/VERSION.txt +0 -1
- {codext-1.16.3 → codext-1.16.5}/.coveragerc +0 -0
- {codext-1.16.3 → codext-1.16.5}/.github/ISSUE_TEMPLATE/add-encoding.yml +0 -0
- {codext-1.16.3 → codext-1.16.5}/.github/copilot-instructions.md +0 -0
- {codext-1.16.3 → codext-1.16.5}/.github/prompts/add_codec.prompt.md +0 -0
- {codext-1.16.3 → codext-1.16.5}/.github/pull_request_template.md +0 -0
- {codext-1.16.3 → codext-1.16.5}/.github/workflows/python-package.yml +0 -0
- {codext-1.16.3 → codext-1.16.5}/.gitignore +0 -0
- {codext-1.16.3 → codext-1.16.5}/.readthedocs.yml +0 -0
- {codext-1.16.3 → codext-1.16.5}/LICENSE +0 -0
- {codext-1.16.3 → codext-1.16.5}/docs/coverage.svg +0 -0
- {codext-1.16.3 → codext-1.16.5}/docs/mkdocs.yml +0 -0
- {codext-1.16.3 → codext-1.16.5}/docs/pages/cli.md +0 -0
- {codext-1.16.3 → codext-1.16.5}/docs/pages/css/extra.css +0 -0
- {codext-1.16.3 → codext-1.16.5}/docs/pages/demos/using-bases.gif +0 -0
- {codext-1.16.3 → codext-1.16.5}/docs/pages/demos/using-codext.gif +0 -0
- {codext-1.16.3 → codext-1.16.5}/docs/pages/demos/using-debase.gif +0 -0
- {codext-1.16.3 → codext-1.16.5}/docs/pages/enc/base.md +0 -0
- {codext-1.16.3 → codext-1.16.5}/docs/pages/enc/binary.md +0 -0
- {codext-1.16.3 → codext-1.16.5}/docs/pages/enc/common.md +0 -0
- {codext-1.16.3 → codext-1.16.5}/docs/pages/enc/compressions.md +0 -0
- {codext-1.16.3 → codext-1.16.5}/docs/pages/enc/hashing.md +0 -0
- {codext-1.16.3 → codext-1.16.5}/docs/pages/enc/languages.md +0 -0
- {codext-1.16.3 → codext-1.16.5}/docs/pages/enc/others.md +0 -0
- {codext-1.16.3 → codext-1.16.5}/docs/pages/enc/stegano.md +0 -0
- {codext-1.16.3 → codext-1.16.5}/docs/pages/enc/web.md +0 -0
- {codext-1.16.3 → codext-1.16.5}/docs/pages/features.md +0 -0
- {codext-1.16.3 → codext-1.16.5}/docs/pages/guessing.md +0 -0
- {codext-1.16.3 → codext-1.16.5}/docs/pages/howto.md +0 -0
- {codext-1.16.3 → codext-1.16.5}/docs/pages/img/banner.png +0 -0
- {codext-1.16.3 → codext-1.16.5}/docs/pages/img/icon.png +0 -0
- {codext-1.16.3 → codext-1.16.5}/docs/pages/img/logo.png +0 -0
- {codext-1.16.3 → codext-1.16.5}/docs/pages/index.md +0 -0
- {codext-1.16.3 → codext-1.16.5}/docs/pages/manipulations.md +0 -0
- {codext-1.16.3 → codext-1.16.5}/docs/requirements.txt +0 -0
- {codext-1.16.3 → codext-1.16.5}/pyproject.toml +0 -0
- {codext-1.16.3 → codext-1.16.5}/pytest.ini +0 -0
- {codext-1.16.3 → codext-1.16.5}/requirements.txt +0 -0
- {codext-1.16.3 → codext-1.16.5}/setup.cfg +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/__common__.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/__info__.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/__init__.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/base/__init__.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/base/_base2n.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/base/base100.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/base/base122.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/base/base45.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/base/base85.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/base/base91.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/base/baseN.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/binary/__init__.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/binary/baudot.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/binary/bcd.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/binary/excess3.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/binary/gray.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/binary/manchester.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/binary/rotate.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/checksums/__init__.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/checksums/adler.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/checksums/luhn.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/common/__init__.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/common/a1z26.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/common/cases.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/common/dummy.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/common/octal.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/common/ordinal.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/compressions/__init__.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/compressions/gzipp.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/compressions/lz77.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/compressions/lz78.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/compressions/pkzip.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/crypto/affine.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/crypto/atbash.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/crypto/bacon.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/crypto/barbie.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/crypto/citrix.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/crypto/railfence.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/crypto/rot.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/crypto/scytale.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/crypto/shift.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/crypto/vigenere.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/crypto/xor.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/hashing/__init__.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/hashing/blake.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/hashing/md.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/hashing/mmh3.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/hashing/sha.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/hashing/shake.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/languages/__init__.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/languages/braille.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/languages/galactic.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/languages/leetspeak.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/languages/morse.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/languages/navajo.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/languages/radio.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/languages/southpark.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/languages/tap.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/languages/tomtom.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/macros.json +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/others/__init__.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/others/dna.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/others/kbshift.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/others/letters.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/others/markdown.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/stegano/__init__.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/stegano/klopf.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/stegano/resistor.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/stegano/rick.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/stegano/sms.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/web/__init__.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/web/html.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext/web/url.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext.egg-info/dependency_links.txt +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext.egg-info/entry_points.txt +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext.egg-info/requires.txt +0 -0
- {codext-1.16.3 → codext-1.16.5}/src/codext.egg-info/top_level.txt +0 -0
- {codext-1.16.3 → codext-1.16.5}/tests/__init__.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/tests/test_common.py +0 -0
- {codext-1.16.3 → codext-1.16.5}/tests/test_generated.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codext
|
|
3
|
-
Version: 1.16.
|
|
3
|
+
Version: 1.16.5
|
|
4
4
|
Summary: Native codecs extension
|
|
5
5
|
Author-email: Alexandre D'Hondt <alexandre.dhondt@gmail.com>
|
|
6
6
|
License: GNU GENERAL PUBLIC LICENSE
|
|
@@ -1034,6 +1034,7 @@ This category also contains `ascii85`, `adobe`, `[x]btoa`, `zeromq` with the `ba
|
|
|
1034
1034
|
- [X] `barbie-N`: aka Barbie Typewriter (*N* belongs to [1, 4])
|
|
1035
1035
|
- [X] `beaufort`: aka Beaufort Cipher (variant of Vigenere Cipher)
|
|
1036
1036
|
- [X] `citrix`: aka Citrix CTX1 password encoding
|
|
1037
|
+
- [X] `playfair`: aka Playfair Cipher
|
|
1037
1038
|
- [X] `phillips`: aka Phillips Cipher (polyalphabetic block cipher with 8 key squares)
|
|
1038
1039
|
- [X] `polybius`: aka Polybius Square Cipher
|
|
1039
1040
|
- [X] `railfence`: aka Rail Fence Cipher
|
|
@@ -336,6 +336,7 @@ This category also contains `ascii85`, `adobe`, `[x]btoa`, `zeromq` with the `ba
|
|
|
336
336
|
- [X] `barbie-N`: aka Barbie Typewriter (*N* belongs to [1, 4])
|
|
337
337
|
- [X] `beaufort`: aka Beaufort Cipher (variant of Vigenere Cipher)
|
|
338
338
|
- [X] `citrix`: aka Citrix CTX1 password encoding
|
|
339
|
+
- [X] `playfair`: aka Playfair Cipher
|
|
339
340
|
- [X] `phillips`: aka Phillips Cipher (polyalphabetic block cipher with 8 key squares)
|
|
340
341
|
- [X] `polybius`: aka Polybius Square Cipher
|
|
341
342
|
- [X] `railfence`: aka Rail Fence Cipher
|
|
Binary file
|
|
@@ -257,6 +257,25 @@ This is a dynamic encoding, that is, it can be called with an integer to define
|
|
|
257
257
|
|
|
258
258
|
-----
|
|
259
259
|
|
|
260
|
+
### Playfair Cipher
|
|
261
|
+
|
|
262
|
+
The Playfair cipher is a symmetric encryption method using polygram substitution with bigrams (pairs of letters), invented in 1854 by Charles Wheatstone, but popularized by Lord Playfair.
|
|
263
|
+
|
|
264
|
+
**Codec** | **Conversions** | **Aliases** | **Comment**
|
|
265
|
+
:---: | :---: | --- | ---
|
|
266
|
+
`playfair` | text <-> Playfair ciphertext | `playfair`, `playfair-playfairexample`, `playfair-keyword` | Dynamic key parameter ; letters only ; `J` is normalized to `I`
|
|
267
|
+
|
|
268
|
+
```python
|
|
269
|
+
>>> codext.encode("HIDETHEGOLDINTHETREESTUMP", "playfair-playfairexample")
|
|
270
|
+
'BMODZBXDNABEKUDMUIXMMOUVIF'
|
|
271
|
+
>>> codext.decode("BMODZBXDNABEKUDMUIXMMOUVIF", "playfair-playfairexample")
|
|
272
|
+
'HIDETHEGOLDINTHETREXESTUMP'
|
|
273
|
+
>>> codext.decode(codext.encode("INSTRUMENT", "playfair-keyword"), "playfair-keyword")
|
|
274
|
+
'INSTRUMENT'
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
-----
|
|
278
|
+
|
|
260
279
|
### Trithemius Cipher
|
|
261
280
|
|
|
262
281
|
This is a variant of the [Vigenere Cipher](#vigenere-cipher) with key `"ABCDEFGHIJKLMNOPQRSTUVWXYZ"`.
|
|
@@ -335,4 +354,3 @@ This is a dynamic encoding, that is, it can be called with an integer to define
|
|
|
335
354
|
>>> codext.encode("~bcy*cy*k*~oy~", "xor-10")
|
|
336
355
|
'this is a test'
|
|
337
356
|
```
|
|
338
|
-
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
1.16.5
|
|
@@ -125,8 +125,10 @@ def base_encode(input, charset, errors="strict", exc=BaseEncodeError):
|
|
|
125
125
|
if i > SIZE_LIMIT:
|
|
126
126
|
raise InputSizeLimitError("Input exceeded size limit")
|
|
127
127
|
return i * charset[0]
|
|
128
|
-
|
|
129
|
-
|
|
128
|
+
# keep this fast-path only for a non-standard 10-character charset ; the digits
|
|
129
|
+
# charset uses the generic loop below (leading zeros, bignums, no str/int round-trip)
|
|
130
|
+
if n == 10 and charset != digits:
|
|
131
|
+
return "".join(charset[int(x)] for x in str(i))
|
|
130
132
|
while i > 0:
|
|
131
133
|
i, c = divmod(i, n)
|
|
132
134
|
r = charset[c] + r
|
|
@@ -148,8 +150,8 @@ def base_decode(input, charset, errors="strict", exc=BaseDecodeError):
|
|
|
148
150
|
i, n, dec = 0, len(charset), lambda n: base_encode(n, [chr(x) for x in range(256)], errors, exc)
|
|
149
151
|
if n == 1:
|
|
150
152
|
return i2s(len(input))
|
|
151
|
-
if n == 10:
|
|
152
|
-
return
|
|
153
|
+
if n == 10 and charset != digits:
|
|
154
|
+
return "".join(str(charset.index(c)) for c in input)
|
|
153
155
|
for k, c in enumerate(input):
|
|
154
156
|
try:
|
|
155
157
|
i = i * n + charset.index(c)
|
|
@@ -13,6 +13,7 @@ from ..__common__ import add
|
|
|
13
13
|
|
|
14
14
|
CRC = {
|
|
15
15
|
'': {
|
|
16
|
+
'': (0x1021, 0xc6c6, True, True, 0, 0xbf05),
|
|
16
17
|
'a': (0x1021, 0xc6c6, True, True, 0, 0xbf05),
|
|
17
18
|
},
|
|
18
19
|
8: {
|
|
@@ -78,6 +79,7 @@ CRC = {
|
|
|
78
79
|
'mpt1327': (0x6815, 0, False, False, 1, 0x2566),
|
|
79
80
|
},
|
|
80
81
|
16: {
|
|
82
|
+
'': (0x8005, 0, True, True, 0, 0xbb3d),
|
|
81
83
|
'acorn': (0x1021, 0, False, False, 0, 0x31c3),
|
|
82
84
|
'arc': (0x8005, 0, True, True, 0, 0xbb3d),
|
|
83
85
|
'atom': (0x002d, 0, True, True, 0, 0x4287),
|
|
@@ -4,6 +4,7 @@ from .atbash import *
|
|
|
4
4
|
from .bacon import *
|
|
5
5
|
from .barbie import *
|
|
6
6
|
from .citrix import *
|
|
7
|
+
from .playfair import *
|
|
7
8
|
from .phillips import *
|
|
8
9
|
from .polybius import *
|
|
9
10
|
from .railfence import *
|
|
@@ -13,4 +14,3 @@ from .shift import *
|
|
|
13
14
|
from .vic import *
|
|
14
15
|
from .vigenere import *
|
|
15
16
|
from .xor import *
|
|
16
|
-
|
|
@@ -25,7 +25,9 @@ from ..__common__ import *
|
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
__examples__ = {
|
|
28
|
+
'dec(phillips)': None,
|
|
28
29
|
'enc(phillips)': None,
|
|
30
|
+
'enc(phillips-key)': {'ATTACK1': None},
|
|
29
31
|
'enc(phillips-key)': {'ATTACK': "HUUHLL", 'TESTME': "UFZUSM", 'ABCDEF': "HCLMFA"},
|
|
30
32
|
'enc(phillips-key-5-1-2)': {'This is a Test String': "KPVBVHTCRHCHCGQBT"},
|
|
31
33
|
'dec(phillips-key-5-1-2)': {'KPVBVHTCRHCHCGQBT': "THISISATESTSTRING"},
|
|
@@ -58,69 +60,45 @@ def __make_grids(key):
|
|
|
58
60
|
return grids
|
|
59
61
|
|
|
60
62
|
|
|
61
|
-
def
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
63
|
+
def __set_params(key, block_size, dh, dv):
|
|
64
|
+
return (key or "").strip(), \
|
|
65
|
+
int(block_size) if block_size else 5, \
|
|
66
|
+
int(dh) if dh else 1, \
|
|
67
|
+
int(dv) if dv else 1, \
|
|
68
|
+
__make_grids(key) if key and key.isalpha() else None
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def __shift_text(text, grids, block_size, dh, dv, errors, decode=False):
|
|
72
|
+
pos_maps = [{ch: (r, c) for r, row in enumerate(grid) for c, ch in enumerate(row)} for grid in grids]
|
|
73
|
+
r, i, t = "", 0, ensure_str(text).upper().replace("J", "I").replace(" ", "")
|
|
67
74
|
_h = handle_error("phillips", errors, decode=decode)
|
|
68
|
-
r, i = "", 0
|
|
69
75
|
for pos, c in enumerate(t):
|
|
70
|
-
if c == " ":
|
|
71
|
-
continue
|
|
72
76
|
if c not in set(_ALPHABET):
|
|
73
77
|
r += _h(c, pos, r)
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
return r, len(
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
def _make_cipher(key, block_size, dh, dv):
|
|
84
|
-
_key = (key or "").strip()
|
|
85
|
-
try:
|
|
86
|
-
block_size = int(block_size) if block_size else 5
|
|
87
|
-
except (ValueError, TypeError):
|
|
88
|
-
raise LookupError(f"Bad parameter for encoding 'phillips': block_size must be an integer, got {block_size}")
|
|
89
|
-
if not (1 <= block_size <= 25):
|
|
90
|
-
raise LookupError("Bad parameter for encoding 'phillips': block_size must be between 1 and 25, got "
|
|
91
|
-
f"{block_size}")
|
|
92
|
-
try:
|
|
93
|
-
dh = int(dh) if dh else 1
|
|
94
|
-
except (ValueError, TypeError):
|
|
95
|
-
raise LookupError(f"Bad parameter for encoding 'phillips': dh must be an integer, got {dh}")
|
|
96
|
-
if not (1 <= dh <= 4):
|
|
97
|
-
raise LookupError(f"Bad parameter for encoding 'phillips': dh must be between 1 and 4, got {dh}")
|
|
98
|
-
try:
|
|
99
|
-
dv = int(dv) if dv else 1
|
|
100
|
-
except (ValueError, TypeError):
|
|
101
|
-
raise LookupError(f"Bad parameter for encoding 'phillips': dv must be an integer, got {dv}")
|
|
102
|
-
if not (1 <= dv <= 4):
|
|
103
|
-
raise LookupError(f"Bad parameter for encoding 'phillips': dv must be between 1 and 4, got {dv}")
|
|
104
|
-
return _key, block_size, dh, dv, __make_grids(_key) if _key and _key.isalpha() else None
|
|
78
|
+
else:
|
|
79
|
+
grid_idx = (i // block_size) % 8
|
|
80
|
+
grid = grids[grid_idx]
|
|
81
|
+
s, col = pos_maps[grid_idx][c]
|
|
82
|
+
r += grid[(s + dv) % 5][(col + dh) % 5]
|
|
83
|
+
i += 1
|
|
84
|
+
return r, len(r)
|
|
105
85
|
|
|
106
86
|
|
|
107
87
|
def phillips_encode(key, block_size=None, dh=None, dv=None):
|
|
108
|
-
_key, block_size, dh, dv, grids =
|
|
88
|
+
_key, block_size, dh, dv, grids = __set_params(key, block_size, dh, dv)
|
|
109
89
|
def encode(text, errors="strict"):
|
|
110
|
-
if
|
|
111
|
-
raise LookupError("Bad parameter for encoding 'phillips': "
|
|
112
|
-
|
|
113
|
-
return _shift_text(text, grids, block_size, dh, dv, errors)
|
|
90
|
+
if _key == "":
|
|
91
|
+
raise LookupError("Bad parameter for encoding 'phillips': key cannot be empty")
|
|
92
|
+
return __shift_text(text, grids, block_size, dh, dv, errors)
|
|
114
93
|
return encode
|
|
115
94
|
|
|
116
95
|
|
|
117
96
|
def phillips_decode(key, block_size=None, dh=None, dv=None):
|
|
118
|
-
_key, block_size, dh, dv, grids =
|
|
97
|
+
_key, block_size, dh, dv, grids = __set_params(key, block_size, dh, dv)
|
|
119
98
|
def decode(text, errors="strict"):
|
|
120
|
-
if
|
|
121
|
-
raise LookupError("Bad parameter for
|
|
122
|
-
|
|
123
|
-
return _shift_text(text, grids, block_size, -dh, -dv, errors, True)
|
|
99
|
+
if _key == "":
|
|
100
|
+
raise LookupError("Bad parameter for encoding 'phillips': key cannot be empty")
|
|
101
|
+
return __shift_text(text, grids, block_size, -dh, -dv, errors, True)
|
|
124
102
|
return decode
|
|
125
103
|
|
|
126
104
|
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# -*- coding: UTF-8 -*-
|
|
2
|
+
"""Playfair Cipher Codec - playfair content encoding.
|
|
3
|
+
|
|
4
|
+
The Playfair cipher is a symmetric encryption method using polygram substitution
|
|
5
|
+
with bigrams (pairs of letters), invented in 1854 by Charles Wheatstone, but
|
|
6
|
+
popularized by his friend Lord Playfair.
|
|
7
|
+
|
|
8
|
+
This codec:
|
|
9
|
+
- en/decodes strings from str to str
|
|
10
|
+
- en/decodes strings from bytes to bytes
|
|
11
|
+
- decodes file content to str (read)
|
|
12
|
+
- encodes file content from str to bytes (write)
|
|
13
|
+
|
|
14
|
+
Reference: https://www.dcode.fr/playfair-cipher
|
|
15
|
+
"""
|
|
16
|
+
from string import ascii_uppercase as UC, digits as DG
|
|
17
|
+
|
|
18
|
+
from ..__common__ import *
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
__examples__ = {
|
|
22
|
+
'dec(playfair_cipher-testkey-4)': None,
|
|
23
|
+
'dec(playfair)': {'SIHTHTDQCUUTUSHOHW': "THISISATESTSTRINGX", '1a': None, 'a1b': None},
|
|
24
|
+
'dec(playfair-playfairexample)': {'BMODZBXDNABEKUDMUIXMMOUVIF': 'HIDETHEGOLDINTHETREXESTUMP'},
|
|
25
|
+
'enc(playfair-*-5-XQ-IJ)': {'UDTUTUTGMH': "TESTSTRJNG"},
|
|
26
|
+
'enc(playfair_cipher-*-5-XX)': None,
|
|
27
|
+
'enc(playfair_cipher-*-5-X-JJ)': None,
|
|
28
|
+
'enc(playfair)': {'String!': None, 'This is a test String': "SIHTHTDQCUUTUSHOHW"},
|
|
29
|
+
'enc(playfair-*-5-XQ-IJ)': {'TESTSTRING': "UDTUTUTGMH"},
|
|
30
|
+
'enc(playfair-playfairexample)': {'HIDETHEGOLDINTHETREESTUMP': 'BMODZBXDNABEKUDMUIXMMOUVIF'},
|
|
31
|
+
'enc-dec(playfair-keyword)': ["TEST"],
|
|
32
|
+
'enc-dec(playfair-*-5-X-IJ)': ["TEST", "TESTSENTENCE"],
|
|
33
|
+
}
|
|
34
|
+
__guess__ = ["playfair"]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# Standard 5×5 Playfair alphabet (I and J share the same cell)
|
|
38
|
+
_DEFAULT_ALPHABET = {
|
|
39
|
+
5: UC.replace("J", ""),
|
|
40
|
+
6: UC + DG,
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def __build_grid(key=None, grid_size=5, replace_char=('J', 'I')):
|
|
45
|
+
"""Build the Playfair grid from an optional keyword. """
|
|
46
|
+
seen, grid = set(), []
|
|
47
|
+
for c in key.upper():
|
|
48
|
+
if c == replace_char[0]:
|
|
49
|
+
c = replace_char[1]
|
|
50
|
+
if c.isalpha() and c not in seen:
|
|
51
|
+
seen.add(c)
|
|
52
|
+
grid.append(c)
|
|
53
|
+
for c in _DEFAULT_ALPHABET[grid_size]:
|
|
54
|
+
if c not in seen:
|
|
55
|
+
seen.add(c)
|
|
56
|
+
grid.append(c)
|
|
57
|
+
return grid, {grid[i]: (i // grid_size, i % grid_size) for i in range(grid_size ** 2)}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def __set_params(key, grid_size, fill_chars, replace_char):
|
|
61
|
+
if len(fc := str(fill_chars or "XQ").upper()) == 2 and fc[0] == fc[1]:
|
|
62
|
+
raise LookupError("Bad parameter for encoding 'playfair': filling char must be a single letter or a pair of "
|
|
63
|
+
"distinct letters")
|
|
64
|
+
if len(rc := str(replace_char or "JI").upper()) == 2 and rc[0] == rc[1]:
|
|
65
|
+
raise LookupError("Bad parameter for encoding 'playfair': replaced char must be a pair of distinct letters")
|
|
66
|
+
gs = int(grid_size or 5)
|
|
67
|
+
a = _DEFAULT_ALPHABET[gs]
|
|
68
|
+
if len(fc) == 1:
|
|
69
|
+
fc += a[(a.index(fc[0])+1) % len(a)]
|
|
70
|
+
return (a if key == "*" else a[::-1] if key == "^" else key).upper(), gs, tuple(fc), tuple(rc)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def playfair_encode(key=None, grid_size=None, fill_chars=None, replace_char=None):
|
|
74
|
+
key, gsize, fchars, rchar = __set_params(key, grid_size, fill_chars, replace_char)
|
|
75
|
+
grid, pos = __build_grid(key, gsize, rchar)
|
|
76
|
+
def encode(text, errors="strict"):
|
|
77
|
+
_filler = lambda c: fchars[1] if c == fchars[0] else fchars[0]
|
|
78
|
+
chars = [rchar[1] if c == rchar[0] else c for c in ensure_str(text).upper().replace(" ", "")]
|
|
79
|
+
result, i, _h = [], 0, handle_error("playfair", errors)
|
|
80
|
+
while i < len(chars):
|
|
81
|
+
a = chars[i]
|
|
82
|
+
if a not in grid:
|
|
83
|
+
result.append(_h(a, i))
|
|
84
|
+
i += 1
|
|
85
|
+
continue
|
|
86
|
+
if i + 1 < len(chars):
|
|
87
|
+
b = chars[i+1]
|
|
88
|
+
if a == b:
|
|
89
|
+
b = _filler(a)
|
|
90
|
+
else:
|
|
91
|
+
i += 1
|
|
92
|
+
else:
|
|
93
|
+
b = _filler(a)
|
|
94
|
+
r_a, c_a = pos[a]
|
|
95
|
+
r_b, c_b = pos[b]
|
|
96
|
+
if r_a == r_b:
|
|
97
|
+
ea, eb = grid[r_a * 5 + (c_a + 1) % 5], grid[r_b * 5 + (c_b + 1) % 5]
|
|
98
|
+
elif c_a == c_b:
|
|
99
|
+
ea, eb = grid[((r_a + 1) % 5) * 5 + c_a], grid[((r_b + 1) % 5) * 5 + c_b]
|
|
100
|
+
else:
|
|
101
|
+
ea, eb = grid[r_a * 5 + c_b], grid[r_b * 5 + c_a]
|
|
102
|
+
result.extend([ea, eb])
|
|
103
|
+
i += 1
|
|
104
|
+
return (r := "".join(result)), len(r)
|
|
105
|
+
return encode
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def playfair_decode(key=None, grid_size=None, fill_chars=None, replace_char=None):
|
|
109
|
+
key, gsize, _, rchar = __set_params(key, grid_size, fill_chars, replace_char)
|
|
110
|
+
grid, pos = __build_grid(key, gsize, rchar)
|
|
111
|
+
def decode(text, errors="strict"):
|
|
112
|
+
chars = [rchar[1] if c == rchar[0] else c for c in ensure_str(text)]
|
|
113
|
+
result, i, _h = [], 0, handle_error("playfair", errors, decode=True)
|
|
114
|
+
while i < len(chars):
|
|
115
|
+
try:
|
|
116
|
+
r_a, c_a = pos[chars[i]]
|
|
117
|
+
except KeyError:
|
|
118
|
+
result.append(_h(chars[i], i, "".join(result)))
|
|
119
|
+
i += 1
|
|
120
|
+
continue
|
|
121
|
+
try:
|
|
122
|
+
r_b, c_b = pos[chars[i+1]]
|
|
123
|
+
except KeyError:
|
|
124
|
+
result.append(chars[i])
|
|
125
|
+
result.append(_h(chars[i+1], i+1, "".join(result)))
|
|
126
|
+
i += 2
|
|
127
|
+
continue
|
|
128
|
+
if r_a == r_b:
|
|
129
|
+
da, db = grid[r_a * 5 + (c_a - 1) % 5], grid[r_b * 5 + (c_b - 1) % 5]
|
|
130
|
+
elif c_a == c_b:
|
|
131
|
+
da, db = grid[((r_a - 1) % 5) * 5 + c_a], grid[((r_b - 1) % 5) * 5 + c_b]
|
|
132
|
+
else:
|
|
133
|
+
da, db = grid[r_a * 5 + c_b], grid[r_b * 5 + c_a]
|
|
134
|
+
result.extend([da, db])
|
|
135
|
+
i += 2
|
|
136
|
+
return (r := "".join(result)), len(r)
|
|
137
|
+
return decode
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
add("playfair", playfair_encode, playfair_decode,
|
|
141
|
+
r"^playfair(?:[-_]cipher)?(?:[-_]([\^*]|[a-zA-Z]+))?(?:[-_]([56]))?(?:[-_]([A-Z]{1,2}))?(?:[-_]([A-Z]{2}))?$",
|
|
142
|
+
printables_rate=1., penalty=.1)
|
|
143
|
+
|
|
@@ -11,6 +11,8 @@ from ..__common__ import *
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
__examples__ = {
|
|
14
|
+
'enc(polybius-ABCDEFGHIKLMNOPQRSTUVZZZZ)': None,
|
|
15
|
+
'dec(polybius)': {'4423244 24': None},
|
|
14
16
|
'enc(polybius|polybius-square|polybius_square)': {'this is a test': "44232443 2443 11 44154344"},
|
|
15
17
|
'enc(polybius-ABCDEFGHIKLMNOPQRSTUVWXYZ)': {'this is a test': "44232443 2443 11 44154344"},
|
|
16
18
|
'dec(polybius)': {'44232443 2443 11 44154344': "THIS IS A TEST"},
|
|
@@ -59,15 +61,12 @@ def polybius_decode(alphabet=_DEFAULT_ALPHABET):
|
|
|
59
61
|
_h = handle_error("polybius", errors, decode=True)
|
|
60
62
|
r, t, i = "", ensure_str(text), 0
|
|
61
63
|
while i < len(t):
|
|
62
|
-
if t[i]
|
|
63
|
-
r += " "
|
|
64
|
-
i += 1
|
|
65
|
-
elif i + 1 < len(t):
|
|
64
|
+
if t[i:i+2].isdigit():
|
|
66
65
|
r += decmap.get(t[i:i+2]) or _h(t[i:i+2], i, r)
|
|
67
|
-
i += 2
|
|
68
|
-
else:
|
|
69
|
-
r += _h(t[i], i, r)
|
|
70
66
|
i += 1
|
|
67
|
+
else:
|
|
68
|
+
r += " " if t[i] == " " else _h(t[i], i, r)
|
|
69
|
+
i += 1
|
|
71
70
|
return r, len(t)
|
|
72
71
|
return decode
|
|
73
72
|
|
|
@@ -137,6 +137,6 @@ def vic_decode(key=None, blanks=None, numeric_key=None):
|
|
|
137
137
|
return _decode
|
|
138
138
|
|
|
139
139
|
|
|
140
|
-
add("vic", vic_encode, vic_decode, r"vic(?:[-_]([
|
|
140
|
+
add("vic", vic_encode, vic_decode, r"vic(?:[-_]([\^*]|[./a-zA-Z]+))?(?:[-_]([0-9]{2}))?(?:[-_]([0-9]+T?))?$",
|
|
141
141
|
printables_rate=1., penalty=.1)
|
|
142
142
|
|
|
@@ -13,8 +13,9 @@ from ..__common__ import *
|
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
__examples__ = {
|
|
16
|
-
'
|
|
16
|
+
'dec(lorem_ipsum)': {'orci #': None, 'Auctor babel commodo#': None},
|
|
17
17
|
'enc(ipsum)': {'Bad test#': None},
|
|
18
|
+
'enc-dec(ipsum|lorem-ipsum)': ["This is a test !"],
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
|
|
@@ -59,7 +60,7 @@ SCHARS = "0123456789.,:;!?+=-*/\\"
|
|
|
59
60
|
|
|
60
61
|
|
|
61
62
|
def ipsum_encode(text, errors="strict"):
|
|
62
|
-
s, strip = "", False
|
|
63
|
+
s, strip, _h = "", False, handle_error("ipsum", errors, " ")
|
|
63
64
|
for i, c in enumerate(text):
|
|
64
65
|
try:
|
|
65
66
|
if c == " " or c in SCHARS:
|
|
@@ -70,13 +71,12 @@ def ipsum_encode(text, errors="strict"):
|
|
|
70
71
|
s += (w.capitalize() if c.isupper() else w) + " "
|
|
71
72
|
strip = True
|
|
72
73
|
except KeyError:
|
|
73
|
-
s +=
|
|
74
|
-
return s[:-1] if strip else s, len(
|
|
74
|
+
s += _h(c, i, s)
|
|
75
|
+
return (s := s[:-1] if strip else s), len(s)
|
|
75
76
|
|
|
76
77
|
|
|
77
78
|
def ipsum_decode(text, errors="strict"):
|
|
78
|
-
s = ""
|
|
79
|
-
words = text.split(" ")
|
|
79
|
+
s, words, _h = "", text.split(" "), handle_error("ipsum", errors, decode=True, item="word")
|
|
80
80
|
for i, w in enumerate(words[:-1] if words[-1] == "" else words):
|
|
81
81
|
if w.strip() == "":
|
|
82
82
|
s += " "
|
|
@@ -88,8 +88,8 @@ def ipsum_decode(text, errors="strict"):
|
|
|
88
88
|
raise KeyError
|
|
89
89
|
s += w[:len(w)-len(w.lstrip(SCHARS))] + w.strip(SCHARS)[0] + w[len(w.rstrip(SCHARS)):len(w)]
|
|
90
90
|
except KeyError:
|
|
91
|
-
s +=
|
|
92
|
-
return s, len(
|
|
91
|
+
s += _h(w, i, s)
|
|
92
|
+
return s, len(s)
|
|
93
93
|
|
|
94
94
|
|
|
95
95
|
add("ipsum", ipsum_encode, ipsum_decode, pattern=r"^(?:lorem[-_]?)?ipsum$", printables_rate=1.,
|
|
@@ -13,10 +13,10 @@ from ..__common__ import *
|
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
__examples__ = {
|
|
16
|
-
'enc(uu|uu_codec)': {'this is a test': "begin 666 -\n.=&AI<R!I<R!A('1E<W0 \n \nend\n"},
|
|
17
|
-
'dec(uu-encode)': {'.=&AI<R!I<R!A(\'1E<W0 ': "this is a test", '.=&AI<R!I<R!A(\'1E<W0`': "this is a test"},
|
|
18
16
|
'dec(uu-codec)': {'begin 666 -\n.=&AI<R!I<R!A(\'1E<W0`': None, '.=&AI<R!I<R!A(\'1E<W0`\n\n\n`\nend': None},
|
|
19
17
|
'dec(uu_codec)': {'begin 777 test.txt\n.=&AI<R!I<R!A(\'1E<W0`\n\n\n`\nend': "this is a test"},
|
|
18
|
+
'dec(uu-encode)': {'.=&AI<R!I<R!A(\'1E<W0 ': "this is a test", '.=&AI<R!I<R!A(\'1E<W0`': "this is a test"},
|
|
19
|
+
'enc(uu|uu_codec)': {'this is a test': "begin 666 -\n.=&AI<R!I<R!A('1E<W0 \n \nend\n"},
|
|
20
20
|
'enc-dec(uu)': ["begin 666 -\n.=&AI<R!I<R!A(\'1E<W0`", "@random{512,1024,2048}"],
|
|
21
21
|
}
|
|
22
22
|
|
|
@@ -25,11 +25,11 @@ def uu_encode(text, errors="strict"):
|
|
|
25
25
|
r, t = b"begin 666 -\n", b(text)
|
|
26
26
|
for i in range(0, len(t), 45):
|
|
27
27
|
r += _enc(t[i:i+45])
|
|
28
|
-
return r + b" \nend\n", len(
|
|
28
|
+
return (r := r + b" \nend\n"), len(r)
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
def uu_decode(text, errors="strict"):
|
|
32
|
-
|
|
32
|
+
_h = handle_error("uu", "strict", decode=True, kind="token", item="line")
|
|
33
33
|
lines = b(text).strip(b" \t\r\n\f").split(b"\n")
|
|
34
34
|
start, end = re.match(b"^begin [1-7]{3} .*$", lines[0]), re.match(b"^end$", lines[-1])
|
|
35
35
|
if start and end:
|
|
@@ -40,15 +40,15 @@ def uu_decode(text, errors="strict"):
|
|
|
40
40
|
if errors == "ignore":
|
|
41
41
|
lines = lines[1:] if start else lines[:-1]
|
|
42
42
|
elif end:
|
|
43
|
-
|
|
43
|
+
_h(lines[0], 0)
|
|
44
44
|
elif start:
|
|
45
|
-
|
|
45
|
+
_h(lines[-1], len(lines)-1)
|
|
46
46
|
while len(lines) > 0 and lines[-1].strip(b" \t\r\n\f") in [b"", b"`"]:
|
|
47
47
|
lines = lines[:-1]
|
|
48
48
|
r = b""
|
|
49
49
|
for l in lines:
|
|
50
50
|
r += _dec(l.strip(b" \t\r\n\f"))
|
|
51
|
-
return r, len(
|
|
51
|
+
return r, len(r)
|
|
52
52
|
|
|
53
53
|
|
|
54
54
|
add("uu", uu_encode, uu_decode, pattern=r"^uu(?:[-_]?encode|[-_]codec)?$",
|
|
@@ -11,6 +11,7 @@ from ..__common__ import *
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
__examples__ = {
|
|
14
|
+
'dec(hexagram)': {'䷕䷞䷈䷇t': None},
|
|
14
15
|
'enc(hexagram|iching|i-ching-hexagrams)': {'this is a test': "䷰䷭䷚䷔䷞䷺䷗䷔䷞䷺䷗䷚䷏䷊䷂䷕䷞䷈䷇☯"},
|
|
15
16
|
}
|
|
16
17
|
|
|
@@ -25,13 +26,13 @@ def hexagram_encode(input, errors="strict"):
|
|
|
25
26
|
|
|
26
27
|
|
|
27
28
|
def hexagram_decode(input, errors="strict"):
|
|
28
|
-
r,
|
|
29
|
+
r, _h = "", handle_error("hexagram", errors, decode=True)
|
|
29
30
|
for i, c in enumerate(input):
|
|
30
31
|
try:
|
|
31
32
|
r += DECMAP[c]
|
|
32
33
|
except KeyError:
|
|
33
|
-
r +=
|
|
34
|
-
return codecs.decode(r, "base64"), len(
|
|
34
|
+
r += _h(c, i, r)
|
|
35
|
+
return (r := codecs.decode(r, "base64")), len(r)
|
|
35
36
|
|
|
36
37
|
|
|
37
38
|
add("hexagram", hexagram_encode, hexagram_decode, printables_rate=0.,
|
|
@@ -18,6 +18,11 @@ __examples1__ = {
|
|
|
18
18
|
'enc(whitespace|whitespaces)': {'test': "\t \t \t\t\t \t\t \t \t \t\t \t \t \t\t"},
|
|
19
19
|
'enc(whitespace-inv|whitespace_inverted)': {'test': " \t\t\t \t \t\t \t \t \t\t\t \t\t \t\t\t \t "},
|
|
20
20
|
}
|
|
21
|
+
__examples2__ = {
|
|
22
|
+
'dec(whitespace+after-before)': {'} \n a \n . v ': None},
|
|
23
|
+
'enc(whitespace+after-before)': {'Test\r': None},
|
|
24
|
+
'enc-dec(whitespace+after-before)': ["Test", "TESTSTRING!"],
|
|
25
|
+
}
|
|
21
26
|
__guess1__ = ["whitespace", "whitespace-inv"]
|
|
22
27
|
__guess2__ = ["whitespace+after-before", "whitespace-after+before"]
|
|
23
28
|
|
|
@@ -30,42 +35,38 @@ add_map("whitespace", ENCMAP, intype="bin", pattern=r"^whitespaces?([-_]inv(?:er
|
|
|
30
35
|
def wsba_encode(p):
|
|
31
36
|
eq = "ord(c)" + p
|
|
32
37
|
def encode(text, errors="strict"):
|
|
33
|
-
r = []
|
|
38
|
+
r, _h = [], handle_error("whitespace" + p, errors, repl_char="\x00")
|
|
34
39
|
for i, c in enumerate(text):
|
|
35
40
|
if ord(c) < min(ord(c) for c in printable[:-6]):
|
|
36
|
-
r.append(
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
before
|
|
43
|
-
|
|
44
|
-
r.append(" " * before + enc + " " * after)
|
|
45
|
-
s = "\n".join(r)
|
|
46
|
-
return s, len(s)
|
|
41
|
+
r.append(_h(c, i))
|
|
42
|
+
else:
|
|
43
|
+
enc, offset = "\x00", random.randint(-10,10)
|
|
44
|
+
while enc not in printable[:-6]:
|
|
45
|
+
after, before = random.randint(0, 20), random.randint(0, 20)
|
|
46
|
+
enc = chr(eval(eq) % 256)
|
|
47
|
+
r.append(" " * before + enc + " " * after)
|
|
48
|
+
return (s := "\n".join(r)), len(s)
|
|
47
49
|
return encode
|
|
48
50
|
|
|
49
51
|
|
|
50
52
|
def wsba_decode(p):
|
|
51
53
|
eq = "ord(c)" + "".join({'-':"+",'+':"-"}.get(c, c) for c in p)
|
|
52
54
|
def decode(text, errors="strict"):
|
|
53
|
-
s = ""
|
|
55
|
+
s, _h = "", handle_error("whitespace_after_before", errors, decode=True, item="line")
|
|
54
56
|
for i, l in enumerate(text.split("\n")):
|
|
55
|
-
ll
|
|
56
|
-
if ll == 0:
|
|
57
|
+
if (ll := len(l.strip())) == 0:
|
|
57
58
|
continue
|
|
58
59
|
if ll > 1:
|
|
59
|
-
s +=
|
|
60
|
-
after = len(l) - len(l.rstrip(" "))
|
|
61
|
-
before = len(l) - len(l.lstrip(" "))
|
|
60
|
+
s += _h(l, i)
|
|
61
|
+
after, before = len(l) - len(l.rstrip(" ")), len(l) - len(l.lstrip(" "))
|
|
62
62
|
c = l[before]
|
|
63
63
|
s += chr(eval(eq))
|
|
64
|
-
return s, len(
|
|
64
|
+
return s, len(s)
|
|
65
65
|
return decode
|
|
66
66
|
|
|
67
67
|
|
|
68
68
|
op = r"[+-](?:\d+(?:\.\d+)?[*/])?"
|
|
69
|
-
add("whitespace_after_before", wsba_encode, wsba_decode,
|
|
70
|
-
expansion_factor=(22., 3.),
|
|
69
|
+
add("whitespace_after_before", wsba_encode, wsba_decode, examples=__examples2__, guess=__guess2__,
|
|
70
|
+
printables_rate=1., penalty=.1, expansion_factor=(22., 3.), entropy=1.,
|
|
71
|
+
pattern=r"whitespace("+op+r"before"+op+r"after|"+op+r"after"+op+r"before)$")
|
|
71
72
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codext
|
|
3
|
-
Version: 1.16.
|
|
3
|
+
Version: 1.16.5
|
|
4
4
|
Summary: Native codecs extension
|
|
5
5
|
Author-email: Alexandre D'Hondt <alexandre.dhondt@gmail.com>
|
|
6
6
|
License: GNU GENERAL PUBLIC LICENSE
|
|
@@ -1034,6 +1034,7 @@ This category also contains `ascii85`, `adobe`, `[x]btoa`, `zeromq` with the `ba
|
|
|
1034
1034
|
- [X] `barbie-N`: aka Barbie Typewriter (*N* belongs to [1, 4])
|
|
1035
1035
|
- [X] `beaufort`: aka Beaufort Cipher (variant of Vigenere Cipher)
|
|
1036
1036
|
- [X] `citrix`: aka Citrix CTX1 password encoding
|
|
1037
|
+
- [X] `playfair`: aka Playfair Cipher
|
|
1037
1038
|
- [X] `phillips`: aka Phillips Cipher (polyalphabetic block cipher with 8 key squares)
|
|
1038
1039
|
- [X] `polybius`: aka Polybius Square Cipher
|
|
1039
1040
|
- [X] `railfence`: aka Rail Fence Cipher
|
|
@@ -12,6 +12,7 @@ requirements.txt
|
|
|
12
12
|
.github/prompts/add_codec.prompt.md
|
|
13
13
|
.github/workflows/python-package.yml
|
|
14
14
|
docs/coverage.svg
|
|
15
|
+
docs/hakin9-codext.pdf
|
|
15
16
|
docs/mkdocs.yml
|
|
16
17
|
docs/requirements.txt
|
|
17
18
|
docs/pages/cli.md
|
|
@@ -86,6 +87,7 @@ src/codext/crypto/bacon.py
|
|
|
86
87
|
src/codext/crypto/barbie.py
|
|
87
88
|
src/codext/crypto/citrix.py
|
|
88
89
|
src/codext/crypto/phillips.py
|
|
90
|
+
src/codext/crypto/playfair.py
|
|
89
91
|
src/codext/crypto/polybius.py
|
|
90
92
|
src/codext/crypto/railfence.py
|
|
91
93
|
src/codext/crypto/rot.py
|
|
@@ -112,7 +112,30 @@ class TestCodecsBase(TestCase):
|
|
|
112
112
|
self.assertEqual(codecs.decode(B8, "base8-01234567"), STR)
|
|
113
113
|
self.assertRaises(LookupError, codecs.encode, "test", "base8-0123456")
|
|
114
114
|
self.assertRaises(LookupError, codecs.encode, "test", "base8-012345678")
|
|
115
|
-
|
|
115
|
+
|
|
116
|
+
def test_codec_base10(self):
|
|
117
|
+
B10 = "2361031878030638688519054699098996"
|
|
118
|
+
self.assertEqual(codecs.encode(STR, "base10"), B10)
|
|
119
|
+
self.assertEqual(codecs.encode(b(STR), "base10"), b(B10))
|
|
120
|
+
self.assertEqual(codecs.decode(B10, "base10"), STR)
|
|
121
|
+
self.assertEqual(codecs.decode(b(B10), "base10"), b(STR))
|
|
122
|
+
for alias in ["int", "integer", "dec", "decimal"]:
|
|
123
|
+
self.assertEqual(codecs.encode(STR, alias), B10)
|
|
124
|
+
self.assertEqual(codecs.decode(B10, alias), STR)
|
|
125
|
+
# leading null bytes must be preserved as leading '0' characters
|
|
126
|
+
self.assertEqual(codecs.encode("\x00abc", "base10"), "06382179")
|
|
127
|
+
self.assertEqual(codecs.encode("\x00", "base10"), "0")
|
|
128
|
+
self.assertEqual(codecs.encode("\x00\x00abc", "base10"), "006382179")
|
|
129
|
+
self.assertEqual(codecs.decode("06382179", "base10"), "\x00abc")
|
|
130
|
+
self.assertEqual(codecs.decode("00", "base10"), "\x00\x00")
|
|
131
|
+
self.assertEqual(codecs.encode(b("\x00abc"), "base10"), b("06382179"))
|
|
132
|
+
self.assertEqual(codecs.decode(b("06382179"), "base10"), b("\x00abc"))
|
|
133
|
+
# a value whose big integer ends in a 0xe nibble must survive decoding
|
|
134
|
+
self.assertEqual(codecs.decode(codecs.encode(b"d\xc6\xfe", "base10"), "base10"), b"d\xc6\xfe")
|
|
135
|
+
# large inputs must not hit the int<->str conversion digit limit
|
|
136
|
+
for data in [b"\x00\xff\xfe", b"\x00\x00", b"\x2a" * 2048]:
|
|
137
|
+
self.assertEqual(codecs.decode(codecs.encode(data, "base10"), "base10"), data)
|
|
138
|
+
|
|
116
139
|
def test_codec_base16(self):
|
|
117
140
|
B16 = "7468697320697320612074657374"
|
|
118
141
|
self.assertEqual(codecs.encode(STR, "base16"), B16)
|
|
@@ -106,6 +106,25 @@ class ManualTestCase(TestCase):
|
|
|
106
106
|
for s, r in [("", ""), ("0", "0"), ("1", "8"), ("7992739871", "3")]:
|
|
107
107
|
self.assertEqual(codecs.encode(s, "luhn"), r)
|
|
108
108
|
self.assertEqual(codecs.encode("-", "luhn", errors="ignore"), "")
|
|
109
|
+
|
|
110
|
+
def test_codec_crc_bare_defaults(self):
|
|
111
|
+
# a crc<width> used without a variant suffix must resolve to that family's default ;
|
|
112
|
+
# width 16 and the crc-a family had no default and crashed with KeyError instead
|
|
113
|
+
from codext.checksums.crc import CRC
|
|
114
|
+
for n in CRC.keys():
|
|
115
|
+
name = "crc%d" % n if isinstance(n, int) else "crc"
|
|
116
|
+
expected = "%0{}x".format(round((n or 16)/4+.5)) % CRC[n][""][5]
|
|
117
|
+
self.assertEqual(codecs.encode("123456789", name), expected)
|
|
118
|
+
self.assertEqual(codecs.encode(b"123456789", name), b(expected))
|
|
119
|
+
# bare crc-16 is CRC-16/ARC (poly 0x8005), the same as its arc/ibm/lha aliases
|
|
120
|
+
for name in ["crc16", "crc-16", "crc_16", "crc16-arc", "crc16-ibm", "crc16-lha"]:
|
|
121
|
+
self.assertEqual(codecs.encode("123456789", name), "bb3d")
|
|
122
|
+
# bare crc is CRC-A, the same as the crca alias
|
|
123
|
+
self.assertEqual(codecs.encode("123456789", "crc"), "bf05")
|
|
124
|
+
self.assertEqual(codecs.encode("123456789", "crca"), "bf05")
|
|
125
|
+
# adding a default must not make an unknown variant resolve
|
|
126
|
+
self.assertRaises(LookupError, codecs.encode, "123456789", "crc16-bad")
|
|
127
|
+
self.assertRaises(LookupError, codecs.encode, "123456789", "crc-bad")
|
|
109
128
|
|
|
110
129
|
def test_codec_dummy_str_manips(self):
|
|
111
130
|
STR = "this is a test"
|
|
@@ -152,8 +171,7 @@ class ManualTestCase(TestCase):
|
|
|
152
171
|
METHODS = [x[7:].lower() for x in crypt.__dict__ if x.startswith("METHOD_")] \
|
|
153
172
|
if crypt is not None else []
|
|
154
173
|
for m in METHODS:
|
|
155
|
-
h
|
|
156
|
-
self.assertIsNotNone(codecs.encode(STR, h))
|
|
174
|
+
self.assertIsNotNone(codecs.encode(STR, h := f"crypt-{m}"))
|
|
157
175
|
self.assertRaises(NotImplementedError, codecs.decode, STR, h)
|
|
158
176
|
|
|
159
177
|
def test_codec_markdown(self):
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
1.16.3
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|