codext 1.16.2__tar.gz → 1.16.3__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.
Files changed (139) hide show
  1. {codext-1.16.2 → codext-1.16.3}/PKG-INFO +2 -1
  2. {codext-1.16.2 → codext-1.16.3}/README.md +1 -0
  3. {codext-1.16.2 → codext-1.16.3}/docs/pages/enc/crypto.md +24 -0
  4. codext-1.16.3/src/codext/VERSION.txt +1 -0
  5. {codext-1.16.2 → codext-1.16.3}/src/codext/crypto/__init__.py +1 -0
  6. codext-1.16.3/src/codext/crypto/phillips.py +130 -0
  7. codext-1.16.3/src/codext/crypto/vic.py +142 -0
  8. {codext-1.16.2 → codext-1.16.3}/src/codext.egg-info/PKG-INFO +2 -1
  9. {codext-1.16.2 → codext-1.16.3}/src/codext.egg-info/SOURCES.txt +1 -0
  10. codext-1.16.2/src/codext/VERSION.txt +0 -1
  11. codext-1.16.2/src/codext/crypto/phillips.py +0 -128
  12. {codext-1.16.2 → codext-1.16.3}/.coveragerc +0 -0
  13. {codext-1.16.2 → codext-1.16.3}/.github/ISSUE_TEMPLATE/add-encoding.yml +0 -0
  14. {codext-1.16.2 → codext-1.16.3}/.github/copilot-instructions.md +0 -0
  15. {codext-1.16.2 → codext-1.16.3}/.github/prompts/add_codec.prompt.md +0 -0
  16. {codext-1.16.2 → codext-1.16.3}/.github/pull_request_template.md +0 -0
  17. {codext-1.16.2 → codext-1.16.3}/.github/workflows/python-package.yml +0 -0
  18. {codext-1.16.2 → codext-1.16.3}/.gitignore +0 -0
  19. {codext-1.16.2 → codext-1.16.3}/.readthedocs.yml +0 -0
  20. {codext-1.16.2 → codext-1.16.3}/LICENSE +0 -0
  21. {codext-1.16.2 → codext-1.16.3}/docs/coverage.svg +0 -0
  22. {codext-1.16.2 → codext-1.16.3}/docs/mkdocs.yml +0 -0
  23. {codext-1.16.2 → codext-1.16.3}/docs/pages/cli.md +0 -0
  24. {codext-1.16.2 → codext-1.16.3}/docs/pages/css/extra.css +0 -0
  25. {codext-1.16.2 → codext-1.16.3}/docs/pages/demos/using-bases.gif +0 -0
  26. {codext-1.16.2 → codext-1.16.3}/docs/pages/demos/using-codext.gif +0 -0
  27. {codext-1.16.2 → codext-1.16.3}/docs/pages/demos/using-debase.gif +0 -0
  28. {codext-1.16.2 → codext-1.16.3}/docs/pages/enc/base.md +0 -0
  29. {codext-1.16.2 → codext-1.16.3}/docs/pages/enc/binary.md +0 -0
  30. {codext-1.16.2 → codext-1.16.3}/docs/pages/enc/common.md +0 -0
  31. {codext-1.16.2 → codext-1.16.3}/docs/pages/enc/compressions.md +0 -0
  32. {codext-1.16.2 → codext-1.16.3}/docs/pages/enc/hashing.md +0 -0
  33. {codext-1.16.2 → codext-1.16.3}/docs/pages/enc/languages.md +0 -0
  34. {codext-1.16.2 → codext-1.16.3}/docs/pages/enc/others.md +0 -0
  35. {codext-1.16.2 → codext-1.16.3}/docs/pages/enc/stegano.md +0 -0
  36. {codext-1.16.2 → codext-1.16.3}/docs/pages/enc/web.md +0 -0
  37. {codext-1.16.2 → codext-1.16.3}/docs/pages/features.md +0 -0
  38. {codext-1.16.2 → codext-1.16.3}/docs/pages/guessing.md +0 -0
  39. {codext-1.16.2 → codext-1.16.3}/docs/pages/howto.md +0 -0
  40. {codext-1.16.2 → codext-1.16.3}/docs/pages/img/banner.png +0 -0
  41. {codext-1.16.2 → codext-1.16.3}/docs/pages/img/icon.png +0 -0
  42. {codext-1.16.2 → codext-1.16.3}/docs/pages/img/logo.png +0 -0
  43. {codext-1.16.2 → codext-1.16.3}/docs/pages/index.md +0 -0
  44. {codext-1.16.2 → codext-1.16.3}/docs/pages/manipulations.md +0 -0
  45. {codext-1.16.2 → codext-1.16.3}/docs/requirements.txt +0 -0
  46. {codext-1.16.2 → codext-1.16.3}/pyproject.toml +0 -0
  47. {codext-1.16.2 → codext-1.16.3}/pytest.ini +0 -0
  48. {codext-1.16.2 → codext-1.16.3}/requirements.txt +0 -0
  49. {codext-1.16.2 → codext-1.16.3}/setup.cfg +0 -0
  50. {codext-1.16.2 → codext-1.16.3}/src/codext/__common__.py +0 -0
  51. {codext-1.16.2 → codext-1.16.3}/src/codext/__info__.py +0 -0
  52. {codext-1.16.2 → codext-1.16.3}/src/codext/__init__.py +0 -0
  53. {codext-1.16.2 → codext-1.16.3}/src/codext/base/__init__.py +0 -0
  54. {codext-1.16.2 → codext-1.16.3}/src/codext/base/_base.py +0 -0
  55. {codext-1.16.2 → codext-1.16.3}/src/codext/base/_base2n.py +0 -0
  56. {codext-1.16.2 → codext-1.16.3}/src/codext/base/base100.py +0 -0
  57. {codext-1.16.2 → codext-1.16.3}/src/codext/base/base122.py +0 -0
  58. {codext-1.16.2 → codext-1.16.3}/src/codext/base/base45.py +0 -0
  59. {codext-1.16.2 → codext-1.16.3}/src/codext/base/base85.py +0 -0
  60. {codext-1.16.2 → codext-1.16.3}/src/codext/base/base91.py +0 -0
  61. {codext-1.16.2 → codext-1.16.3}/src/codext/base/baseN.py +0 -0
  62. {codext-1.16.2 → codext-1.16.3}/src/codext/binary/__init__.py +0 -0
  63. {codext-1.16.2 → codext-1.16.3}/src/codext/binary/baudot.py +0 -0
  64. {codext-1.16.2 → codext-1.16.3}/src/codext/binary/bcd.py +0 -0
  65. {codext-1.16.2 → codext-1.16.3}/src/codext/binary/excess3.py +0 -0
  66. {codext-1.16.2 → codext-1.16.3}/src/codext/binary/gray.py +0 -0
  67. {codext-1.16.2 → codext-1.16.3}/src/codext/binary/manchester.py +0 -0
  68. {codext-1.16.2 → codext-1.16.3}/src/codext/binary/rotate.py +0 -0
  69. {codext-1.16.2 → codext-1.16.3}/src/codext/checksums/__init__.py +0 -0
  70. {codext-1.16.2 → codext-1.16.3}/src/codext/checksums/adler.py +0 -0
  71. {codext-1.16.2 → codext-1.16.3}/src/codext/checksums/crc.py +0 -0
  72. {codext-1.16.2 → codext-1.16.3}/src/codext/checksums/luhn.py +0 -0
  73. {codext-1.16.2 → codext-1.16.3}/src/codext/common/__init__.py +0 -0
  74. {codext-1.16.2 → codext-1.16.3}/src/codext/common/a1z26.py +0 -0
  75. {codext-1.16.2 → codext-1.16.3}/src/codext/common/cases.py +0 -0
  76. {codext-1.16.2 → codext-1.16.3}/src/codext/common/dummy.py +0 -0
  77. {codext-1.16.2 → codext-1.16.3}/src/codext/common/octal.py +0 -0
  78. {codext-1.16.2 → codext-1.16.3}/src/codext/common/ordinal.py +0 -0
  79. {codext-1.16.2 → codext-1.16.3}/src/codext/compressions/__init__.py +0 -0
  80. {codext-1.16.2 → codext-1.16.3}/src/codext/compressions/gzipp.py +0 -0
  81. {codext-1.16.2 → codext-1.16.3}/src/codext/compressions/lz77.py +0 -0
  82. {codext-1.16.2 → codext-1.16.3}/src/codext/compressions/lz78.py +0 -0
  83. {codext-1.16.2 → codext-1.16.3}/src/codext/compressions/pkzip.py +0 -0
  84. {codext-1.16.2 → codext-1.16.3}/src/codext/crypto/affine.py +0 -0
  85. {codext-1.16.2 → codext-1.16.3}/src/codext/crypto/atbash.py +0 -0
  86. {codext-1.16.2 → codext-1.16.3}/src/codext/crypto/bacon.py +0 -0
  87. {codext-1.16.2 → codext-1.16.3}/src/codext/crypto/barbie.py +0 -0
  88. {codext-1.16.2 → codext-1.16.3}/src/codext/crypto/citrix.py +0 -0
  89. {codext-1.16.2 → codext-1.16.3}/src/codext/crypto/polybius.py +0 -0
  90. {codext-1.16.2 → codext-1.16.3}/src/codext/crypto/railfence.py +0 -0
  91. {codext-1.16.2 → codext-1.16.3}/src/codext/crypto/rot.py +0 -0
  92. {codext-1.16.2 → codext-1.16.3}/src/codext/crypto/scytale.py +0 -0
  93. {codext-1.16.2 → codext-1.16.3}/src/codext/crypto/shift.py +0 -0
  94. {codext-1.16.2 → codext-1.16.3}/src/codext/crypto/vigenere.py +0 -0
  95. {codext-1.16.2 → codext-1.16.3}/src/codext/crypto/xor.py +0 -0
  96. {codext-1.16.2 → codext-1.16.3}/src/codext/hashing/__init__.py +0 -0
  97. {codext-1.16.2 → codext-1.16.3}/src/codext/hashing/blake.py +0 -0
  98. {codext-1.16.2 → codext-1.16.3}/src/codext/hashing/crypt.py +0 -0
  99. {codext-1.16.2 → codext-1.16.3}/src/codext/hashing/md.py +0 -0
  100. {codext-1.16.2 → codext-1.16.3}/src/codext/hashing/mmh3.py +0 -0
  101. {codext-1.16.2 → codext-1.16.3}/src/codext/hashing/sha.py +0 -0
  102. {codext-1.16.2 → codext-1.16.3}/src/codext/hashing/shake.py +0 -0
  103. {codext-1.16.2 → codext-1.16.3}/src/codext/languages/__init__.py +0 -0
  104. {codext-1.16.2 → codext-1.16.3}/src/codext/languages/braille.py +0 -0
  105. {codext-1.16.2 → codext-1.16.3}/src/codext/languages/galactic.py +0 -0
  106. {codext-1.16.2 → codext-1.16.3}/src/codext/languages/ipsum.py +0 -0
  107. {codext-1.16.2 → codext-1.16.3}/src/codext/languages/leetspeak.py +0 -0
  108. {codext-1.16.2 → codext-1.16.3}/src/codext/languages/morse.py +0 -0
  109. {codext-1.16.2 → codext-1.16.3}/src/codext/languages/navajo.py +0 -0
  110. {codext-1.16.2 → codext-1.16.3}/src/codext/languages/radio.py +0 -0
  111. {codext-1.16.2 → codext-1.16.3}/src/codext/languages/southpark.py +0 -0
  112. {codext-1.16.2 → codext-1.16.3}/src/codext/languages/tap.py +0 -0
  113. {codext-1.16.2 → codext-1.16.3}/src/codext/languages/tomtom.py +0 -0
  114. {codext-1.16.2 → codext-1.16.3}/src/codext/macros.json +0 -0
  115. {codext-1.16.2 → codext-1.16.3}/src/codext/others/__init__.py +0 -0
  116. {codext-1.16.2 → codext-1.16.3}/src/codext/others/dna.py +0 -0
  117. {codext-1.16.2 → codext-1.16.3}/src/codext/others/kbshift.py +0 -0
  118. {codext-1.16.2 → codext-1.16.3}/src/codext/others/letters.py +0 -0
  119. {codext-1.16.2 → codext-1.16.3}/src/codext/others/markdown.py +0 -0
  120. {codext-1.16.2 → codext-1.16.3}/src/codext/others/uuencode.py +0 -0
  121. {codext-1.16.2 → codext-1.16.3}/src/codext/stegano/__init__.py +0 -0
  122. {codext-1.16.2 → codext-1.16.3}/src/codext/stegano/hexagram.py +0 -0
  123. {codext-1.16.2 → codext-1.16.3}/src/codext/stegano/klopf.py +0 -0
  124. {codext-1.16.2 → codext-1.16.3}/src/codext/stegano/resistor.py +0 -0
  125. {codext-1.16.2 → codext-1.16.3}/src/codext/stegano/rick.py +0 -0
  126. {codext-1.16.2 → codext-1.16.3}/src/codext/stegano/sms.py +0 -0
  127. {codext-1.16.2 → codext-1.16.3}/src/codext/stegano/whitespace.py +0 -0
  128. {codext-1.16.2 → codext-1.16.3}/src/codext/web/__init__.py +0 -0
  129. {codext-1.16.2 → codext-1.16.3}/src/codext/web/html.py +0 -0
  130. {codext-1.16.2 → codext-1.16.3}/src/codext/web/url.py +0 -0
  131. {codext-1.16.2 → codext-1.16.3}/src/codext.egg-info/dependency_links.txt +0 -0
  132. {codext-1.16.2 → codext-1.16.3}/src/codext.egg-info/entry_points.txt +0 -0
  133. {codext-1.16.2 → codext-1.16.3}/src/codext.egg-info/requires.txt +0 -0
  134. {codext-1.16.2 → codext-1.16.3}/src/codext.egg-info/top_level.txt +0 -0
  135. {codext-1.16.2 → codext-1.16.3}/tests/__init__.py +0 -0
  136. {codext-1.16.2 → codext-1.16.3}/tests/test_base.py +0 -0
  137. {codext-1.16.2 → codext-1.16.3}/tests/test_common.py +0 -0
  138. {codext-1.16.2 → codext-1.16.3}/tests/test_generated.py +0 -0
  139. {codext-1.16.2 → codext-1.16.3}/tests/test_manual.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codext
3
- Version: 1.16.2
3
+ Version: 1.16.3
4
4
  Summary: Native codecs extension
5
5
  Author-email: Alexandre D'Hondt <alexandre.dhondt@gmail.com>
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -1041,6 +1041,7 @@ This category also contains `ascii85`, `adobe`, `[x]btoa`, `zeromq` with the `ba
1041
1041
  - [X] `scytaleN`: encrypts using the number of letters on the rod (*N* belongs to [1,[)
1042
1042
  - [X] `shiftN`: shift ordinals (*N* belongs to [1,255])
1043
1043
  - [X] `trithemius`: aka Trithemius Cipher (variant of Vigenere Cipher)
1044
+ - [X] `vic`: aka VIC Cipher
1044
1045
  - [X] `vigenere`: aka Vigenere Cipher
1045
1046
  - [X] `xorN`: XOR with a single byte (*N* belongs to [1,255])
1046
1047
 
@@ -343,6 +343,7 @@ This category also contains `ascii85`, `adobe`, `[x]btoa`, `zeromq` with the `ba
343
343
  - [X] `scytaleN`: encrypts using the number of letters on the rod (*N* belongs to [1,[)
344
344
  - [X] `shiftN`: shift ordinals (*N* belongs to [1,255])
345
345
  - [X] `trithemius`: aka Trithemius Cipher (variant of Vigenere Cipher)
346
+ - [X] `vic`: aka VIC Cipher
346
347
  - [X] `vigenere`: aka Vigenere Cipher
347
348
  - [X] `xorN`: XOR with a single byte (*N* belongs to [1,255])
348
349
 
@@ -274,6 +274,30 @@ This is a variant of the [Vigenere Cipher](#vigenere-cipher) with key `"ABCDEFGH
274
274
 
275
275
  -----
276
276
 
277
+ ### VIC Cipher
278
+
279
+ The VIC cipher combines a straddling checkerboard substitution (converting letters to a stream of digits) and an over-encryption based on a modulo-10 sum with eventually a conversion from digits to text.
280
+
281
+ **Codec** | **Conversions** | **Aliases** | **Comment**
282
+ :---: | :---: | --- | ---
283
+ `vic` | text <-> VIC digit/text ciphertext | `vic-keyword`, `vic-key-12`, `vic-^-26-0248`, `vic-*-73-534T` | optional checkerboard keyword or alphabet marker, blank positions (exactly 2 distinct digits), over-encryption key (digits), "`T`" marker for text conversion
284
+
285
+ Alphabet markers:
286
+ - `*`: uses "`ABC[...]XYZ./`" (uppercase letters + "`./`")
287
+ - `^`: uses "`./ZYX[...]CBA`" ("`./`" + reversed uppercase letters)
288
+
289
+ ```python
290
+ >>> import codext
291
+ >>> codext.encode("VICTOR", "vic-^-26-0248T")
292
+ 'VVXYWYY.XYJ'
293
+ >>> codext.decode("VVXYWYY.XYJ", "vic-^-26-0248T")
294
+ 'VICTOR2' # trailing superfluous letter because of text conversion
295
+ >>> codext.encode("LONGTESTSTRING", "vic-^-85-72564")
296
+ '5031237844273727064454072378'
297
+ ```
298
+
299
+ -----
300
+
277
301
  ### Vigenere Cipher
278
302
 
279
303
  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_.
@@ -0,0 +1 @@
1
+ 1.16.3
@@ -10,6 +10,7 @@ from .railfence import *
10
10
  from .rot import *
11
11
  from .scytale import *
12
12
  from .shift import *
13
+ from .vic import *
13
14
  from .vigenere import *
14
15
  from .xor import *
15
16
 
@@ -0,0 +1,130 @@
1
+ # -*- coding: UTF-8 -*-
2
+ """Phillips Cipher Codec - phillips content encoding.
3
+
4
+ The Phillips cipher is a polyalphabetic substitution cipher using 8 key squares. The first square is a 5×5 grid built
5
+ from a keyword (I and J share one cell). Seven additional squares are derived by sequentially swapping adjacent rows
6
+ in a descending bubble pattern. Plaintext is divided into blocks of T letters (default 5); each letter is individually
7
+ enciphered by shifting its grid position right by DH columns and down by DV rows (both default to 1), wrapping
8
+ around with toroidal topology. J is treated as I.
9
+
10
+ Parameters:
11
+ key -- keyword used to seed the initial 5×5 grid (required)
12
+ block_size -- number of letters per grid-cycle block, 1–25 (default 5)
13
+ dh -- horizontal (column) shift for encryption, 1–4 (default 1)
14
+ dv -- vertical (row) shift for encryption, 1–4 (default 1)
15
+
16
+ This codec:
17
+ - en/decodes strings from str to str
18
+ - en/decodes strings from bytes to bytes
19
+ - decodes file content to str (read)
20
+ - encodes file content from str to bytes (write)
21
+
22
+ Reference: https://www.dcode.fr/phillips-cipher
23
+ """
24
+ from ..__common__ import *
25
+
26
+
27
+ __examples__ = {
28
+ 'enc(phillips)': None,
29
+ 'enc(phillips-key)': {'ATTACK': "HUUHLL", 'TESTME': "UFZUSM", 'ABCDEF': "HCLMFA"},
30
+ 'enc(phillips-key-5-1-2)': {'This is a Test String': "KPVBVHTCRHCHCGQBT"},
31
+ 'dec(phillips-key-5-1-2)': {'KPVBVHTCRHCHCGQBT': "THISISATESTSTRING"},
32
+ 'enc-dec(phillips-key)': ["ATTACK", "TESTME", "ABCDEF"],
33
+ 'enc-dec(phillips-secret)': ["HELLOWORLD", "ATTACKATDAWN"],
34
+ 'enc-dec(phillips-key-2)': ["ATTACK", "TESTME"],
35
+ 'enc-dec(phillips-key-5-2)': ["ATTACK"],
36
+ 'enc-dec(phillips-key-5-1-2)': ["ATTACK"],
37
+ }
38
+ __guess__ = ["phillips-key", "phillips-secret", "phillips-password"]
39
+
40
+
41
+ _ALPHABET = "ABCDEFGHIKLMNOPQRSTUVWXYZ"
42
+
43
+
44
+ def __make_grids(key):
45
+ """Return all 8 grids built by a descending bubble-swap row permutation."""
46
+ seen, letters = set(), []
47
+ for c in key.upper().replace("J", "I") + _ALPHABET:
48
+ if c in set(_ALPHABET) and c not in seen:
49
+ letters.append(c)
50
+ seen.add(c)
51
+ grid = [letters[i * 5:(i + 1) * 5] for i in range(5)]
52
+ grids = [grid]
53
+ for k in range(7):
54
+ r = k % 4
55
+ grid = [row[:] for row in grid]
56
+ grid[r], grid[r + 1] = grid[r + 1], grid[r]
57
+ grids.append(grid)
58
+ return grids
59
+
60
+
61
+ def _shift_text(text, grids, block_size, dh, dv, errors, decode=False):
62
+ t = ensure_str(text).upper().replace("J", "I")
63
+ pos_maps = [
64
+ {ch: (r, c) for r, row in enumerate(grid) for c, ch in enumerate(row)}
65
+ for grid in grids
66
+ ]
67
+ _h = handle_error("phillips", errors, decode=decode)
68
+ r, i = "", 0
69
+ for pos, c in enumerate(t):
70
+ if c == " ":
71
+ continue
72
+ if c not in set(_ALPHABET):
73
+ r += _h(c, pos, r)
74
+ continue
75
+ grid_idx = (i // block_size) % 8
76
+ grid = grids[grid_idx]
77
+ s, col = pos_maps[grid_idx][c]
78
+ r += grid[(s + dv) % 5][(col + dh) % 5]
79
+ i += 1
80
+ return r, len(text)
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
105
+
106
+
107
+ def phillips_encode(key, block_size=None, dh=None, dv=None):
108
+ _key, block_size, dh, dv, grids = _make_cipher(key, block_size, dh, dv)
109
+ def encode(text, errors="strict"):
110
+ if grids is None:
111
+ raise LookupError("Bad parameter for encoding 'phillips': "
112
+ "key must be a non-empty alphabetic string")
113
+ return _shift_text(text, grids, block_size, dh, dv, errors)
114
+ return encode
115
+
116
+
117
+ def phillips_decode(key, block_size=None, dh=None, dv=None):
118
+ _key, block_size, dh, dv, grids = _make_cipher(key, block_size, dh, dv)
119
+ def decode(text, errors="strict"):
120
+ if grids is None:
121
+ raise LookupError("Bad parameter for decoding 'phillips': "
122
+ "key must be a non-empty alphabetic string")
123
+ return _shift_text(text, grids, block_size, -dh, -dv, errors, True)
124
+ return decode
125
+
126
+
127
+ add("phillips", phillips_encode, phillips_decode,
128
+ r"^phillips(?:[-_]cipher)?(?:[-_]([a-zA-Z]+))?(?:[-_]([1-9]|1[0-9]|2[0-5]))?(?:[-_]([1-4]))?(?:[-_]([1-4]))?$",
129
+ printables_rate=1., penalty=.1)
130
+
@@ -0,0 +1,142 @@
1
+ # -*- coding: UTF-8 -*-
2
+ """Vic Cipher Codec - vic 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 itertools import cycle
11
+ from string import ascii_uppercase as UC
12
+
13
+ from ..__common__ import *
14
+
15
+
16
+ __examples__ = {
17
+ 'dec(vic-test-33)': None,
18
+ 'dec(vic-*-12-1234T': {'BBXMBDMBCBBHEMC': "THISISATEST"},
19
+ 'dec(vic-^-26-0248T)': {'VVXYWYY.XYJ': "VICTOR2"}, # '2' because of superfluous extra letter from substitution
20
+ 'enc(vic-test-12-ABC)': None,
21
+ 'enc(vic-*-12-1234T': {'This is a Test': "BBXMBDMBCBBHEMC"},
22
+ 'enc(vic-^-26-0248)': {'VICTOR': "88547440546"},
23
+ 'enc(vic-^-26-0248T)': {'VICTOR': "VVXYWYY.XYJ"},
24
+ 'enc-dec(vic-^-26)': ["TEST", "LONGTESTSTRING", "VICTOR"],
25
+ 'enc-dec(vic-^-85-72564)': ["TEST", "LONGTESTSTRING", "VICTOR"],
26
+ 'enc-dec(vic-^-85-54321T)': ["TEST", "LONGTESTSTRING", "VICTOR"],
27
+ }
28
+ __guess__ = ["vic"]
29
+
30
+
31
+ def __build_alphabet(key, alphabet=UC, reverse=False):
32
+ """Return the 28-letter alphabet derived from the input key."""
33
+ seen, result = set(), ""
34
+ for c in (key or "").upper():
35
+ if c.isalpha() and c not in seen:
36
+ result += c
37
+ seen.add(c)
38
+ for c in (alphabet[::-1] if reverse else alphabet):
39
+ if c not in seen:
40
+ result += c
41
+ return "./" + result if reverse else result + "./"
42
+
43
+
44
+ def __build_checkerboard(alphabet, blank1=1, blank2=2):
45
+ """Build encode/decode lookup tables for the straddling checkerboard.
46
+
47
+ Layout with digit1=2 and digit2=6:
48
+ 0 1 [2] 3 4 5 [6] 7 8 9
49
+ 0: * * * * * * * *
50
+ 2: * * * * * * * * * *
51
+ 6: * * * * * * * * * *
52
+ """
53
+ if blank1 == blank2:
54
+ raise LookupError(f"Bad parameter for encoding 'vic': blank1 and blank2 cannot be identical")
55
+ enc, dec, i = {}, {}, 0
56
+ # top row
57
+ for col in range(10):
58
+ if col not in (blank1, blank2):
59
+ enc[alphabet[i]] = str(col)
60
+ i += 1
61
+ # second row ; header digit is 'blank1'
62
+ for col in range(10):
63
+ enc[alphabet[i]] = str(blank1) + str(col)
64
+ i += 1
65
+ # third row ; header digit is 'blank2'
66
+ for col in range(10):
67
+ enc[alphabet[i]] = str(blank2) + str(col)
68
+ i += 1
69
+ return enc, {v: k for k, v in enc.items()}
70
+
71
+
72
+ def __set_params(key, blanks, numeric_key):
73
+ return (UC if key == "*" else UC[::-1] if key == "^" else key).upper(), \
74
+ tuple(map(int, str(blanks) or "12")), \
75
+ str(numeric_key or "").rstrip("T"), \
76
+ key == "^", \
77
+ str(numeric_key or " ")[-1] == "T"
78
+
79
+
80
+ def vic_encode(key=None, blanks=None, numeric_key=None):
81
+ key, blanks, numeric_key, rev, txt = __set_params(key, blanks, numeric_key)
82
+ enc_map, dec_map = __build_checkerboard(__build_alphabet(key, reverse=rev), blanks[0], blanks[1])
83
+ def _encode(text, errors="strict"):
84
+ _h = handle_error("vic", errors)
85
+ digits, nk_i, nk_l = [], 0, len(numeric_key or "")
86
+ for pos, c in enumerate(ensure_str(text).upper().replace(" ", "")):
87
+ # 1) encode with the straddling checkerboard
88
+ c = enc_map[c] if c in enc_map else _h(c, pos, "".join(digits))
89
+ # 2) if numeric_key is defined, over-encrypt digits
90
+ if numeric_key and c.isdigit():
91
+ for ci in c:
92
+ digits.append(str((int(ci) + int(numeric_key[nk_i % nk_l])) % 10))
93
+ nk_i += 1
94
+ else:
95
+ digits.append(c)
96
+ r = "".join(d for d in digits if d)
97
+ # 3) if text mode, convert digits to text
98
+ if txt:
99
+ i, r0, l, r = 0, r, len(r), ""
100
+ while i < len(r0):
101
+ if int(r0[i]) in blanks:
102
+ r += dec_map[r0[i] + ("0" if i == l - 1 else r0[i+1])]
103
+ i += 1
104
+ else:
105
+ r += dec_map[r0[i]]
106
+ i += 1
107
+ return r, len(r)
108
+ return _encode
109
+
110
+
111
+ def vic_decode(key=None, blanks=None, numeric_key=None):
112
+ key, blanks, numeric_key, rev, txt = __set_params(key, blanks, numeric_key)
113
+ enc_map, dec_map = __build_checkerboard(__build_alphabet(key, reverse=rev), blanks[0], blanks[1])
114
+ def _decode(text, errors="strict"):
115
+ _h = handle_error("vic", errors, decode=True)
116
+ # 1) if text mode, convert text to digits
117
+ text = "".join(enc_map[c] for c in ensure_str(text)) if txt else ensure_str(text)
118
+ # 2) if numeric_key is defined, over-decrypt
119
+ digits, nk_i, nk_l = [], 0, len(numeric_key or "")
120
+ for pos, c in enumerate(text):
121
+ if numeric_key and c.isdigit():
122
+ for ci in c:
123
+ digits.append(str((int(c) - int(numeric_key[nk_i % nk_l])) % 10))
124
+ nk_i += 1
125
+ else:
126
+ digits.append(c if c.isdigit() else _h(c, pos, "".join(digits)))
127
+ # 3) decode with the straddling checkerboard
128
+ i, r, r0 = 0, "", "".join(d for d in digits if d)
129
+ while i < (l := len(r0)):
130
+ if int(r0[i]) in blanks:
131
+ r += r0[i] if i == l - 1 else dec_map[r0[i] + r0[i+1]]
132
+ i += 1
133
+ else:
134
+ r += dec_map[r0[i]] if r0[i] in dec_map else _h(r0[i], i, r)
135
+ i += 1
136
+ return r, len(r)
137
+ return _decode
138
+
139
+
140
+ add("vic", vic_encode, vic_decode, r"vic(?:[-_]([\^*./a-zA-Z]+))?(?:[-_]([0-9]{2}))?(?:[-_]([0-9]+T?))?$",
141
+ printables_rate=1., penalty=.1)
142
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codext
3
- Version: 1.16.2
3
+ Version: 1.16.3
4
4
  Summary: Native codecs extension
5
5
  Author-email: Alexandre D'Hondt <alexandre.dhondt@gmail.com>
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -1041,6 +1041,7 @@ This category also contains `ascii85`, `adobe`, `[x]btoa`, `zeromq` with the `ba
1041
1041
  - [X] `scytaleN`: encrypts using the number of letters on the rod (*N* belongs to [1,[)
1042
1042
  - [X] `shiftN`: shift ordinals (*N* belongs to [1,255])
1043
1043
  - [X] `trithemius`: aka Trithemius Cipher (variant of Vigenere Cipher)
1044
+ - [X] `vic`: aka VIC Cipher
1044
1045
  - [X] `vigenere`: aka Vigenere Cipher
1045
1046
  - [X] `xorN`: XOR with a single byte (*N* belongs to [1,255])
1046
1047
 
@@ -91,6 +91,7 @@ src/codext/crypto/railfence.py
91
91
  src/codext/crypto/rot.py
92
92
  src/codext/crypto/scytale.py
93
93
  src/codext/crypto/shift.py
94
+ src/codext/crypto/vic.py
94
95
  src/codext/crypto/vigenere.py
95
96
  src/codext/crypto/xor.py
96
97
  src/codext/hashing/__init__.py
@@ -1 +0,0 @@
1
- 1.16.2
@@ -1,128 +0,0 @@
1
- # -*- coding: UTF-8 -*-
2
- """Phillips Cipher Codec - phillips content encoding.
3
-
4
- The Phillips cipher is a polyalphabetic substitution cipher using 8 key
5
- squares. The first square is a 5×5 grid built from a keyword (I and J share
6
- one cell). Seven additional squares are derived by rotating every row of the
7
- previous square one step to the left. Plaintext is enciphered in bigrams,
8
- each pair using the next square in a cycle of 8. Non-alphabetic characters
9
- are passed through unchanged; J is treated as I.
10
-
11
- This codec:
12
- - en/decodes strings from str to str
13
- - en/decodes strings from bytes to bytes
14
- - decodes file content to str (read)
15
- - encodes file content from str to bytes (write)
16
-
17
- Reference: https://www.dcode.fr/phillips-cipher
18
- """
19
- from ..__common__ import *
20
-
21
-
22
- __examples__ = {
23
- 'enc(phillips)': None,
24
- 'enc(phillips-key)': {'ATTACK': 'BSSBIC', 'TESTME': 'QBTPLY', 'ABCDEF': 'BKDFYD'},
25
- 'enc-dec(phillips-key)': ['ATTACK', 'TESTME', 'ABCDEF'],
26
- 'enc-dec(phillips-secret)': ['HELLOWORLD', 'ATTACKATDAWN'],
27
- }
28
- __guess__ = ["phillips-key", "phillips-secret", "phillips-password"]
29
-
30
-
31
- _ALPHABET = "ABCDEFGHIKLMNOPQRSTUVWXYZ"
32
-
33
-
34
- def __make_grids(key):
35
- """Return all 8 grids: the initial grid plus 7 row-rotated variants."""
36
- # build the initial 5×5 grid from a keyword (J treated as I)
37
- seen, letters = set(), []
38
- for c in key.upper().replace("J", "I") + _ALPHABET:
39
- if c in set(_ALPHABET) and c not in seen:
40
- letters.append(c)
41
- seen.add(c)
42
- grid = [letters[i * 5:(i + 1) * 5] for i in range(5)]
43
- # now build the other 7 row-rotated variant grids
44
- grids = [grid]
45
- for _ in range(7):
46
- grid = [row[1:] + [row[0]] for row in grid]
47
- grids.append(grid)
48
- return grids
49
-
50
-
51
- def __process_pair(a, b, grid, decode=False):
52
- """Encode or decode a letter pair using Playfair substitution rules.
53
-
54
- Same row → each letter shifts one step right (encode) / left (decode).
55
- Same col → each letter shifts one step down (encode) / up (decode).
56
- Rectangle → each letter moves to the other's column (self-inverse).
57
- """
58
- pos = {ch: (r, c) for r, row in enumerate(grid) for c, ch in enumerate(row)}
59
- r1, c1 = pos[a]
60
- r2, c2 = pos[b]
61
- d = -1 if decode else 1
62
- if r1 == r2:
63
- return grid[r1][(c1 + d) % 5], grid[r2][(c2 + d) % 5]
64
- if c1 == c2:
65
- return grid[(r1 + d) % 5][c1], grid[(r2 + d) % 5][c2]
66
- return grid[r1][c2], grid[r2][c1] # rectangle rule is its own inverse
67
-
68
-
69
- def phillips_encode(key):
70
- _key = (key or "").strip()
71
- # Compute grids eagerly if key is valid; otherwise defer error to call time
72
- _grids = __make_grids(_key) if _key and _key.isalpha() else None
73
- def encode(text, errors="strict"):
74
- if _grids is None:
75
- raise LookupError("Bad parameter for encoding 'phillips': "
76
- "key must be a non-empty alphabetic string")
77
- t = ensure_str(text).upper().replace("J", "I")
78
- alpha = [(i, c) for i, c in enumerate(t) if c in set(_ALPHABET)]
79
- # Pad to an even count with a trailing X
80
- padding_char = None
81
- if len(alpha) % 2 == 1:
82
- alpha.append((-1, "X"))
83
- enc_map = {}
84
- for pair_num, k in enumerate(range(0, len(alpha), 2)):
85
- pos1, a = alpha[k]
86
- pos2, b = alpha[k + 1]
87
- e1, e2 = __process_pair(a, b, _grids[pair_num % 8])
88
- enc_map[pos1] = e1
89
- if pos2 >= 0:
90
- enc_map[pos2] = e2
91
- else:
92
- padding_char = e2
93
- result = [enc_map.get(i, c) for i, c in enumerate(t)]
94
- if padding_char is not None:
95
- result.append(padding_char)
96
- return "".join(result), len(text)
97
- return encode
98
-
99
-
100
- def phillips_decode(key):
101
- _key = (key or "").strip()
102
- # Compute grids eagerly if key is valid; otherwise defer error to call time
103
- _grids = __make_grids(_key) if _key and _key.isalpha() else None
104
- def decode(text, errors="strict"):
105
- if _grids is None:
106
- raise LookupError("Bad parameter for decoding 'phillips': "
107
- "key must be a non-empty alphabetic string")
108
- t = ensure_str(text).upper().replace("J", "I")
109
- alpha = [(i, c) for i, c in enumerate(t) if c in set(_ALPHABET)]
110
- if len(alpha) % 2 == 1:
111
- if errors == "strict":
112
- raise ValueError("phillips: encoded text must contain an even "
113
- "number of alphabetic characters")
114
- alpha = alpha[:-1]
115
- dec_map = {}
116
- for pair_num, k in enumerate(range(0, len(alpha), 2)):
117
- pos1, a = alpha[k]
118
- pos2, b = alpha[k + 1]
119
- d1, d2 = __process_pair(a, b, _grids[pair_num % 8], decode=True)
120
- dec_map[pos1] = d1
121
- dec_map[pos2] = d2
122
- return "".join(dec_map.get(i, c) for i, c in enumerate(t)), len(text)
123
- return decode
124
-
125
-
126
- add("phillips", phillips_encode, phillips_decode, r"^phillips(?:[-_]cipher)?(?:[-_]([a-zA-Z]+))?$", printables_rate=1.,
127
- penalty=.1)
128
-
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