codebook.transposition
This module defines a number of classical transposition ciphers.
It features simple ciphers like the rail_fence
and the scytale
,
as well as the fractionating transposition cipher adfgvx
.
1""" 2This module defines a number of classical transposition ciphers. 3 4It features simple ciphers like the `rail_fence` and the `scytale`, 5as well as the fractionating transposition cipher `adfgvx`. 6""" 7import collections 8import itertools 9 10from codebook.utils import codegroup, validate_key, validate_plaintext 11 12 13@codegroup 14def rail_fence(plaintext: str, *, n: int) -> str: 15 """Rail Fence cipher (page 8) 16 17 - `plaintext` is the message to be encrypted 18 - `n` is the number of rails to use 19 """ 20 plaintext = validate_plaintext(plaintext) 21 22 seq = [""] * n 23 zigzag = itertools.cycle(itertools.chain(range(n - 1), range(n - 1, 0, -1))) 24 for i, c in zip(zigzag, plaintext): 25 seq[i] += c 26 27 return "".join(seq).upper() 28 29 30@codegroup 31def scytale(plaintext: str, *, diameter: int) -> str: 32 """Scytale cipher (page 8) 33 34 - `plaintext` is the message to be encrypted 35 - `diameter` is the number of letters that *can fit around the rod's circumference* 36 """ 37 plaintext = validate_plaintext(plaintext) 38 39 d, m = divmod(len(plaintext), diameter) 40 rows = [] 41 for i in range(diameter): 42 offset = i if m > i else m 43 start = i * d + offset 44 end = start + d + m - offset 45 rows.append(plaintext[start:end]) 46 47 seq = [] 48 for t in itertools.zip_longest(*rows): 49 seq.append("".join(c or "" for c in t)) 50 51 return "".join(seq).upper() 52 53 54@codegroup 55def adfgvx(plaintext: str, *, key: str) -> str: 56 """ADFGVX cipher (Appendix F, page 374) 57 58 - `plaintext` is the message to be encrypted 59 - `key` is the keyword or keyphrase used for the transposition stage of the cipher 60 61 The ADFGVX cipher uses two different keys: a 36 symbol alphabet to encode 62 the 6x6 grid, and a keyword/keyphrase for the transposition stage. 63 For simplicity, the grid's values are hardcoded with the book's example: 64 65 | | **A** | **D** | **F** | **G** | **V** | **X** | 66 | ----- | ----- | ----- | ----- | ----- | ----- | ----- | 67 | **A** | 8 | p | 3 | d | 1 | n | 68 | **D** | l | t | 4 | o | a | h | 69 | **F** | 7 | k | b | c | 5 | z | 70 | **G** | j | u | 6 | w | g | m | 71 | **V** | x | s | v | i | r | 2 | 72 | **X** | 9 | e | y | 0 | f | q | 73 """ 74 plaintext = validate_plaintext(plaintext) 75 key = validate_key(key) 76 77 lookup = {} 78 for i, c in enumerate("8p3d1nlt4oah7kbc5zju6wgmxsvir29ey0fq"): 79 y, x = divmod(i, 6) 80 lookup[ord(c)] = "ADFGVX"[y] + "ADFGVX"[x] 81 82 stage_one = plaintext.translate(lookup) 83 84 columns = collections.defaultdict(list) 85 for k, c in zip(itertools.cycle(key), stage_one): 86 columns[k].append(c) 87 88 seq = itertools.chain(*[columns[k] for k in sorted(key)]) 89 return "".join(seq)
@codegroup
def
rail_fence(plaintext: str, *, n: int) -> str:
14@codegroup 15def rail_fence(plaintext: str, *, n: int) -> str: 16 """Rail Fence cipher (page 8) 17 18 - `plaintext` is the message to be encrypted 19 - `n` is the number of rails to use 20 """ 21 plaintext = validate_plaintext(plaintext) 22 23 seq = [""] * n 24 zigzag = itertools.cycle(itertools.chain(range(n - 1), range(n - 1, 0, -1))) 25 for i, c in zip(zigzag, plaintext): 26 seq[i] += c 27 28 return "".join(seq).upper()
Rail Fence cipher (page 8)
plaintext
is the message to be encryptedn
is the number of rails to use
@codegroup
def
scytale(plaintext: str, *, diameter: int) -> str:
31@codegroup 32def scytale(plaintext: str, *, diameter: int) -> str: 33 """Scytale cipher (page 8) 34 35 - `plaintext` is the message to be encrypted 36 - `diameter` is the number of letters that *can fit around the rod's circumference* 37 """ 38 plaintext = validate_plaintext(plaintext) 39 40 d, m = divmod(len(plaintext), diameter) 41 rows = [] 42 for i in range(diameter): 43 offset = i if m > i else m 44 start = i * d + offset 45 end = start + d + m - offset 46 rows.append(plaintext[start:end]) 47 48 seq = [] 49 for t in itertools.zip_longest(*rows): 50 seq.append("".join(c or "" for c in t)) 51 52 return "".join(seq).upper()
Scytale cipher (page 8)
plaintext
is the message to be encrypteddiameter
is the number of letters that can fit around the rod's circumference
@codegroup
def
adfgvx(plaintext: str, *, key: str) -> str:
55@codegroup 56def adfgvx(plaintext: str, *, key: str) -> str: 57 """ADFGVX cipher (Appendix F, page 374) 58 59 - `plaintext` is the message to be encrypted 60 - `key` is the keyword or keyphrase used for the transposition stage of the cipher 61 62 The ADFGVX cipher uses two different keys: a 36 symbol alphabet to encode 63 the 6x6 grid, and a keyword/keyphrase for the transposition stage. 64 For simplicity, the grid's values are hardcoded with the book's example: 65 66 | | **A** | **D** | **F** | **G** | **V** | **X** | 67 | ----- | ----- | ----- | ----- | ----- | ----- | ----- | 68 | **A** | 8 | p | 3 | d | 1 | n | 69 | **D** | l | t | 4 | o | a | h | 70 | **F** | 7 | k | b | c | 5 | z | 71 | **G** | j | u | 6 | w | g | m | 72 | **V** | x | s | v | i | r | 2 | 73 | **X** | 9 | e | y | 0 | f | q | 74 """ 75 plaintext = validate_plaintext(plaintext) 76 key = validate_key(key) 77 78 lookup = {} 79 for i, c in enumerate("8p3d1nlt4oah7kbc5zju6wgmxsvir29ey0fq"): 80 y, x = divmod(i, 6) 81 lookup[ord(c)] = "ADFGVX"[y] + "ADFGVX"[x] 82 83 stage_one = plaintext.translate(lookup) 84 85 columns = collections.defaultdict(list) 86 for k, c in zip(itertools.cycle(key), stage_one): 87 columns[k].append(c) 88 89 seq = itertools.chain(*[columns[k] for k in sorted(key)]) 90 return "".join(seq)
ADFGVX cipher (Appendix F, page 374)
plaintext
is the message to be encryptedkey
is the keyword or keyphrase used for the transposition stage of the cipher
The ADFGVX cipher uses two different keys: a 36 symbol alphabet to encode the 6x6 grid, and a keyword/keyphrase for the transposition stage. For simplicity, the grid's values are hardcoded with the book's example:
A | D | F | G | V | X | |
---|---|---|---|---|---|---|
A | 8 | p | 3 | d | 1 | n |
D | l | t | 4 | o | a | h |
F | 7 | k | b | c | 5 | z |
G | j | u | 6 | w | g | m |
V | x | s | v | i | r | 2 |
X | 9 | e | y | 0 | f | q |