Fix sphinx/build_docs warnings for ciphers (#12485)

* Fix sphinx/build_docs warnings for ciphers

* Fix
This commit is contained in:
Maxim Smolskiy 2024-12-30 12:56:24 +03:00 committed by GitHub
parent 94b3777936
commit f45e392cf6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 170 additions and 125 deletions

View File

@ -1,5 +1,6 @@
""" """
https://en.wikipedia.org/wiki/Autokey_cipher https://en.wikipedia.org/wiki/Autokey_cipher
An autokey cipher (also known as the autoclave cipher) is a cipher that An autokey cipher (also known as the autoclave cipher) is a cipher that
incorporates the message (the plaintext) into the key. incorporates the message (the plaintext) into the key.
The key is generated from the message in some automated fashion, The key is generated from the message in some automated fashion,
@ -10,8 +11,9 @@ by adding a short primer key to the front of the message.
def encrypt(plaintext: str, key: str) -> str: def encrypt(plaintext: str, key: str) -> str:
""" """
Encrypt a given plaintext (string) and key (string), returning the Encrypt a given `plaintext` (string) and `key` (string), returning the
encrypted ciphertext. encrypted ciphertext.
>>> encrypt("hello world", "coffee") >>> encrypt("hello world", "coffee")
'jsqqs avvwo' 'jsqqs avvwo'
>>> encrypt("coffee is good as python", "TheAlgorithms") >>> encrypt("coffee is good as python", "TheAlgorithms")
@ -74,8 +76,9 @@ def encrypt(plaintext: str, key: str) -> str:
def decrypt(ciphertext: str, key: str) -> str: def decrypt(ciphertext: str, key: str) -> str:
""" """
Decrypt a given ciphertext (string) and key (string), returning the decrypted Decrypt a given `ciphertext` (string) and `key` (string), returning the decrypted
ciphertext. ciphertext.
>>> decrypt("jsqqs avvwo", "coffee") >>> decrypt("jsqqs avvwo", "coffee")
'hello world' 'hello world'
>>> decrypt("vvjfpk wj ohvp su ddylsv", "TheAlgorithms") >>> decrypt("vvjfpk wj ohvp su ddylsv", "TheAlgorithms")

View File

@ -7,24 +7,29 @@ def encrypt(input_string: str, key: int, alphabet: str | None = None) -> str:
""" """
encrypt encrypt
======= =======
Encodes a given string with the caesar cipher and returns the encoded Encodes a given string with the caesar cipher and returns the encoded
message message
Parameters: Parameters:
----------- -----------
* input_string: the plain-text that needs to be encoded
* key: the number of letters to shift the message by * `input_string`: the plain-text that needs to be encoded
* `key`: the number of letters to shift the message by
Optional: Optional:
* alphabet (None): the alphabet used to encode the cipher, if not
* `alphabet` (``None``): the alphabet used to encode the cipher, if not
specified, the standard english alphabet with upper and lowercase specified, the standard english alphabet with upper and lowercase
letters is used letters is used
Returns: Returns:
* A string containing the encoded cipher-text * A string containing the encoded cipher-text
More on the caesar cipher More on the caesar cipher
========================= =========================
The caesar cipher is named after Julius Caesar who used it when sending The caesar cipher is named after Julius Caesar who used it when sending
secret military messages to his troops. This is a simple substitution cipher secret military messages to his troops. This is a simple substitution cipher
where every character in the plain-text is shifted by a certain number known where every character in the plain-text is shifted by a certain number known
@ -32,26 +37,28 @@ def encrypt(input_string: str, key: int, alphabet: str | None = None) -> str:
Example: Example:
Say we have the following message: Say we have the following message:
"Hello, captain" ``Hello, captain``
And our alphabet is made up of lower and uppercase letters: And our alphabet is made up of lower and uppercase letters:
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" ``abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ``
And our shift is "2" And our shift is ``2``
We can then encode the message, one letter at a time. "H" would become "J", We can then encode the message, one letter at a time. ``H`` would become ``J``,
since "J" is two letters away, and so on. If the shift is ever two large, or since ``J`` is two letters away, and so on. If the shift is ever two large, or
our letter is at the end of the alphabet, we just start at the beginning our letter is at the end of the alphabet, we just start at the beginning
("Z" would shift to "a" then "b" and so on). (``Z`` would shift to ``a`` then ``b`` and so on).
Our final message would be "Jgnnq, ecrvckp" Our final message would be ``Jgnnq, ecrvckp``
Further reading Further reading
=============== ===============
* https://en.m.wikipedia.org/wiki/Caesar_cipher * https://en.m.wikipedia.org/wiki/Caesar_cipher
Doctests Doctests
======== ========
>>> encrypt('The quick brown fox jumps over the lazy dog', 8) >>> encrypt('The quick brown fox jumps over the lazy dog', 8)
'bpm yCqks jzwEv nwF rCuxA wDmz Bpm tiHG lwo' 'bpm yCqks jzwEv nwF rCuxA wDmz Bpm tiHG lwo'
@ -85,23 +92,28 @@ def decrypt(input_string: str, key: int, alphabet: str | None = None) -> str:
""" """
decrypt decrypt
======= =======
Decodes a given string of cipher-text and returns the decoded plain-text Decodes a given string of cipher-text and returns the decoded plain-text
Parameters: Parameters:
----------- -----------
* input_string: the cipher-text that needs to be decoded
* key: the number of letters to shift the message backwards by to decode * `input_string`: the cipher-text that needs to be decoded
* `key`: the number of letters to shift the message backwards by to decode
Optional: Optional:
* alphabet (None): the alphabet used to decode the cipher, if not
* `alphabet` (``None``): the alphabet used to decode the cipher, if not
specified, the standard english alphabet with upper and lowercase specified, the standard english alphabet with upper and lowercase
letters is used letters is used
Returns: Returns:
* A string containing the decoded plain-text * A string containing the decoded plain-text
More on the caesar cipher More on the caesar cipher
========================= =========================
The caesar cipher is named after Julius Caesar who used it when sending The caesar cipher is named after Julius Caesar who used it when sending
secret military messages to his troops. This is a simple substitution cipher secret military messages to his troops. This is a simple substitution cipher
where very character in the plain-text is shifted by a certain number known where very character in the plain-text is shifted by a certain number known
@ -110,27 +122,29 @@ def decrypt(input_string: str, key: int, alphabet: str | None = None) -> str:
Example: Example:
Say we have the following cipher-text: Say we have the following cipher-text:
"Jgnnq, ecrvckp" ``Jgnnq, ecrvckp``
And our alphabet is made up of lower and uppercase letters: And our alphabet is made up of lower and uppercase letters:
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" ``abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ``
And our shift is "2" And our shift is ``2``
To decode the message, we would do the same thing as encoding, but in To decode the message, we would do the same thing as encoding, but in
reverse. The first letter, "J" would become "H" (remember: we are decoding) reverse. The first letter, ``J`` would become ``H`` (remember: we are decoding)
because "H" is two letters in reverse (to the left) of "J". We would because ``H`` is two letters in reverse (to the left) of ``J``. We would
continue doing this. A letter like "a" would shift back to the end of continue doing this. A letter like ``a`` would shift back to the end of
the alphabet, and would become "Z" or "Y" and so on. the alphabet, and would become ``Z`` or ``Y`` and so on.
Our final message would be "Hello, captain" Our final message would be ``Hello, captain``
Further reading Further reading
=============== ===============
* https://en.m.wikipedia.org/wiki/Caesar_cipher * https://en.m.wikipedia.org/wiki/Caesar_cipher
Doctests Doctests
======== ========
>>> decrypt('bpm yCqks jzwEv nwF rCuxA wDmz Bpm tiHG lwo', 8) >>> decrypt('bpm yCqks jzwEv nwF rCuxA wDmz Bpm tiHG lwo', 8)
'The quick brown fox jumps over the lazy dog' 'The quick brown fox jumps over the lazy dog'
@ -150,41 +164,44 @@ def brute_force(input_string: str, alphabet: str | None = None) -> dict[int, str
""" """
brute_force brute_force
=========== ===========
Returns all the possible combinations of keys and the decoded strings in the Returns all the possible combinations of keys and the decoded strings in the
form of a dictionary form of a dictionary
Parameters: Parameters:
----------- -----------
* input_string: the cipher-text that needs to be used during brute-force
* `input_string`: the cipher-text that needs to be used during brute-force
Optional: Optional:
* alphabet: (None): the alphabet used to decode the cipher, if not
* `alphabet` (``None``): the alphabet used to decode the cipher, if not
specified, the standard english alphabet with upper and lowercase specified, the standard english alphabet with upper and lowercase
letters is used letters is used
More about brute force More about brute force
====================== ======================
Brute force is when a person intercepts a message or password, not knowing Brute force is when a person intercepts a message or password, not knowing
the key and tries every single combination. This is easy with the caesar the key and tries every single combination. This is easy with the caesar
cipher since there are only all the letters in the alphabet. The more cipher since there are only all the letters in the alphabet. The more
complex the cipher, the larger amount of time it will take to do brute force complex the cipher, the larger amount of time it will take to do brute force
Ex: Ex:
Say we have a 5 letter alphabet (abcde), for simplicity and we intercepted the Say we have a ``5`` letter alphabet (``abcde``), for simplicity and we intercepted
following message: the following message: ``dbc``,
"dbc"
we could then just write out every combination: we could then just write out every combination:
ecd... and so on, until we reach a combination that makes sense: ``ecd``... and so on, until we reach a combination that makes sense:
"cab" ``cab``
Further reading Further reading
=============== ===============
* https://en.wikipedia.org/wiki/Brute_force * https://en.wikipedia.org/wiki/Brute_force
Doctests Doctests
======== ========
>>> brute_force("jFyuMy xIH'N vLONy zILwy Gy!")[20] >>> brute_force("jFyuMy xIH'N vLONy zILwy Gy!")[20]
"Please don't brute force me!" "Please don't brute force me!"

View File

@ -11,32 +11,30 @@ def decrypt_caesar_with_chi_squared(
""" """
Basic Usage Basic Usage
=========== ===========
Arguments: Arguments:
* ciphertext (str): the text to decode (encoded with the caesar cipher) * `ciphertext` (str): the text to decode (encoded with the caesar cipher)
Optional Arguments: Optional Arguments:
* cipher_alphabet (list): the alphabet used for the cipher (each letter is * `cipher_alphabet` (list): the alphabet used for the cipher (each letter is
a string separated by commas) a string separated by commas)
* frequencies_dict (dict): a dictionary of word frequencies where keys are * `frequencies_dict` (dict): a dictionary of word frequencies where keys are
the letters and values are a percentage representation of the frequency as the letters and values are a percentage representation of the frequency as
a decimal/float a decimal/float
* case_sensitive (bool): a boolean value: True if the case matters during * `case_sensitive` (bool): a boolean value: ``True`` if the case matters during
decryption, False if it doesn't decryption, ``False`` if it doesn't
Returns: Returns:
* A tuple in the form of: * A tuple in the form of:
( (`most_likely_cipher`, `most_likely_cipher_chi_squared_value`,
most_likely_cipher, `decoded_most_likely_cipher`)
most_likely_cipher_chi_squared_value,
decoded_most_likely_cipher
)
where... where...
- most_likely_cipher is an integer representing the shift of the smallest - `most_likely_cipher` is an integer representing the shift of the smallest
chi-squared statistic (most likely key) chi-squared statistic (most likely key)
- most_likely_cipher_chi_squared_value is a float representing the - `most_likely_cipher_chi_squared_value` is a float representing the
chi-squared statistic of the most likely shift chi-squared statistic of the most likely shift
- decoded_most_likely_cipher is a string with the decoded cipher - `decoded_most_likely_cipher` is a string with the decoded cipher
(decoded by the most_likely_cipher key) (decoded by the most_likely_cipher key)
@ -45,37 +43,40 @@ def decrypt_caesar_with_chi_squared(
The caesar cipher The caesar cipher
----------------- -----------------
The caesar cipher is a very insecure encryption algorithm, however it has The caesar cipher is a very insecure encryption algorithm, however it has
been used since Julius Caesar. The cipher is a simple substitution cipher been used since Julius Caesar. The cipher is a simple substitution cipher
where each character in the plain text is replaced by a character in the where each character in the plain text is replaced by a character in the
alphabet a certain number of characters after the original character. The alphabet a certain number of characters after the original character. The
number of characters away is called the shift or key. For example: number of characters away is called the shift or key. For example:
Plain text: hello | Plain text: ``hello``
Key: 1 | Key: ``1``
Cipher text: ifmmp | Cipher text: ``ifmmp``
(each letter in hello has been shifted one to the right in the eng. alphabet) | (each letter in ``hello`` has been shifted one to the right in the eng. alphabet)
As you can imagine, this doesn't provide lots of security. In fact As you can imagine, this doesn't provide lots of security. In fact
decrypting ciphertext by brute-force is extremely easy even by hand. However decrypting ciphertext by brute-force is extremely easy even by hand. However
one way to do that is the chi-squared test. one way to do that is the chi-squared test.
The chi-squared test The chi-squared test
------------------- --------------------
Each letter in the english alphabet has a frequency, or the amount of times Each letter in the english alphabet has a frequency, or the amount of times
it shows up compared to other letters (usually expressed as a decimal it shows up compared to other letters (usually expressed as a decimal
representing the percentage likelihood). The most common letter in the representing the percentage likelihood). The most common letter in the
english language is "e" with a frequency of 0.11162 or 11.162%. The test is english language is ``e`` with a frequency of ``0.11162`` or ``11.162%``.
completed in the following fashion. The test is completed in the following fashion.
1. The ciphertext is decoded in a brute force way (every combination of the 1. The ciphertext is decoded in a brute force way (every combination of the
26 possible combinations) ``26`` possible combinations)
2. For every combination, for each letter in the combination, the average 2. For every combination, for each letter in the combination, the average
amount of times the letter should appear the message is calculated by amount of times the letter should appear the message is calculated by
multiplying the total number of characters by the frequency of the letter multiplying the total number of characters by the frequency of the letter.
For example: | For example:
In a message of 100 characters, e should appear around 11.162 times. | In a message of ``100`` characters, ``e`` should appear around ``11.162``
times.
3. Then, to calculate the margin of error (the amount of times the letter 3. Then, to calculate the margin of error (the amount of times the letter
SHOULD appear with the amount of times the letter DOES appear), we use SHOULD appear with the amount of times the letter DOES appear), we use
@ -84,10 +85,12 @@ def decrypt_caesar_with_chi_squared(
Let: Let:
- n be the number of times the letter actually appears - n be the number of times the letter actually appears
- p be the predicted value of the number of times the letter should - p be the predicted value of the number of times the letter should
appear (see #2) appear (see item ``2``)
- let v be the chi-squared test result (referred to here as chi-squared - let v be the chi-squared test result (referred to here as chi-squared
value/statistic) value/statistic)
::
(n - p)^2 (n - p)^2
--------- = v --------- = v
p p
@ -98,16 +101,16 @@ def decrypt_caesar_with_chi_squared(
to be the decoded answer. to be the decoded answer.
Further Reading Further Reading
================ ===============
* http://practicalcryptography.com/cryptanalysis/text-characterisation/chi-squared- * http://practicalcryptography.com/cryptanalysis/text-characterisation/chi-squared-statistic/
statistic/
* https://en.wikipedia.org/wiki/Letter_frequency * https://en.wikipedia.org/wiki/Letter_frequency
* https://en.wikipedia.org/wiki/Chi-squared_test * https://en.wikipedia.org/wiki/Chi-squared_test
* https://en.m.wikipedia.org/wiki/Caesar_cipher * https://en.m.wikipedia.org/wiki/Caesar_cipher
Doctests Doctests
======== ========
>>> decrypt_caesar_with_chi_squared( >>> decrypt_caesar_with_chi_squared(
... 'dof pz aol jhlzhy jpwoly zv wvwbshy? pa pz avv lhzf av jyhjr!' ... 'dof pz aol jhlzhy jpwoly zv wvwbshy? pa pz avv lhzf av jyhjr!'
... ) # doctest: +NORMALIZE_WHITESPACE ... ) # doctest: +NORMALIZE_WHITESPACE

View File

@ -1,14 +1,16 @@
""" """
Wikipedia: https://en.wikipedia.org/wiki/Enigma_machine | Wikipedia: https://en.wikipedia.org/wiki/Enigma_machine
Video explanation: https://youtu.be/QwQVMqfoB2E | Video explanation: https://youtu.be/QwQVMqfoB2E
Also check out Numberphile's and Computerphile's videos on this topic | Also check out Numberphile's and Computerphile's videos on this topic
This module contains function 'enigma' which emulates This module contains function ``enigma`` which emulates
the famous Enigma machine from WWII. the famous Enigma machine from WWII.
Module includes: Module includes:
- enigma function
- ``enigma`` function
- showcase of function usage - showcase of function usage
- 9 randomly generated rotors - ``9`` randomly generated rotors
- reflector (aka static rotor) - reflector (aka static rotor)
- original alphabet - original alphabet
@ -73,7 +75,7 @@ def _validator(
rotpos: RotorPositionT, rotsel: RotorSelectionT, pb: str rotpos: RotorPositionT, rotsel: RotorSelectionT, pb: str
) -> tuple[RotorPositionT, RotorSelectionT, dict[str, str]]: ) -> tuple[RotorPositionT, RotorSelectionT, dict[str, str]]:
""" """
Checks if the values can be used for the 'enigma' function Checks if the values can be used for the ``enigma`` function
>>> _validator((1,1,1), (rotor1, rotor2, rotor3), 'POLAND') >>> _validator((1,1,1), (rotor1, rotor2, rotor3), 'POLAND')
((1, 1, 1), ('EGZWVONAHDCLFQMSIPJBYUKXTR', 'FOBHMDKEXQNRAULPGSJVTYICZW', \ ((1, 1, 1), ('EGZWVONAHDCLFQMSIPJBYUKXTR', 'FOBHMDKEXQNRAULPGSJVTYICZW', \
@ -83,7 +85,7 @@ def _validator(
:param rotpos: rotor_positon :param rotpos: rotor_positon
:param rotsel: rotor_selection :param rotsel: rotor_selection
:param pb: plugb -> validated and transformed :param pb: plugb -> validated and transformed
:return: (rotpos, rotsel, pb) :return: (`rotpos`, `rotsel`, `pb`)
""" """
# Checks if there are 3 unique rotors # Checks if there are 3 unique rotors
@ -118,9 +120,10 @@ def _plugboard(pbstring: str) -> dict[str, str]:
>>> _plugboard('POLAND') >>> _plugboard('POLAND')
{'P': 'O', 'O': 'P', 'L': 'A', 'A': 'L', 'N': 'D', 'D': 'N'} {'P': 'O', 'O': 'P', 'L': 'A', 'A': 'L', 'N': 'D', 'D': 'N'}
In the code, 'pb' stands for 'plugboard' In the code, ``pb`` stands for ``plugboard``
Pairs can be separated by spaces Pairs can be separated by spaces
:param pbstring: string containing plugboard setting for the Enigma machine :param pbstring: string containing plugboard setting for the Enigma machine
:return: dictionary containing converted pairs :return: dictionary containing converted pairs
""" """
@ -168,29 +171,32 @@ def enigma(
plugb: str = "", plugb: str = "",
) -> str: ) -> str:
""" """
The only difference with real-world enigma is that I allowed string input. The only difference with real-world enigma is that ``I`` allowed string input.
All characters are converted to uppercase. (non-letter symbol are ignored) All characters are converted to uppercase. (non-letter symbol are ignored)
How it works:
(for every letter in the message) | How it works:
| (for every letter in the message)
- Input letter goes into the plugboard. - Input letter goes into the plugboard.
If it is connected to another one, switch it. If it is connected to another one, switch it.
- Letter goes through 3 rotors. - Letter goes through ``3`` rotors.
Each rotor can be represented as 2 sets of symbol, where one is shuffled. Each rotor can be represented as ``2`` sets of symbol, where one is shuffled.
Each symbol from the first set has corresponding symbol in Each symbol from the first set has corresponding symbol in
the second set and vice versa. the second set and vice versa.
example: example::
| ABCDEFGHIJKLMNOPQRSTUVWXYZ | e.g. F=D and D=F | ABCDEFGHIJKLMNOPQRSTUVWXYZ | e.g. F=D and D=F
| VKLEPDBGRNWTFCJOHQAMUZYIXS | | VKLEPDBGRNWTFCJOHQAMUZYIXS |
- Symbol then goes through reflector (static rotor). - Symbol then goes through reflector (static rotor).
There it is switched with paired symbol There it is switched with paired symbol.
The reflector can be represented as2 sets, each with half of the alphanet. The reflector can be represented as ``2`` sets, each with half of the alphanet.
There are usually 10 pairs of letters. There are usually ``10`` pairs of letters.
Example::
Example:
| ABCDEFGHIJKLM | e.g. E is paired to X | ABCDEFGHIJKLM | e.g. E is paired to X
| ZYXWVUTSRQPON | so when E goes in X goes out and vice versa | ZYXWVUTSRQPON | so when E goes in X goes out and vice versa
@ -211,9 +217,9 @@ def enigma(
:param text: input message :param text: input message
:param rotor_position: tuple with 3 values in range 1..26 :param rotor_position: tuple with ``3`` values in range ``1``.. ``26``
:param rotor_selection: tuple with 3 rotors () :param rotor_selection: tuple with ``3`` rotors
:param plugb: string containing plugboard configuration (default '') :param plugb: string containing plugboard configuration (default ``''``)
:return: en/decrypted string :return: en/decrypted string
""" """

View File

@ -3,8 +3,10 @@ An RSA prime factor algorithm.
The program can efficiently factor RSA prime number given the private key d and The program can efficiently factor RSA prime number given the private key d and
public key e. public key e.
Source: on page 3 of https://crypto.stanford.edu/~dabo/papers/RSA-survey.pdf
More readable source: https://www.di-mgt.com.au/rsa_factorize_n.html | Source: on page ``3`` of https://crypto.stanford.edu/~dabo/papers/RSA-survey.pdf
| More readable source: https://www.di-mgt.com.au/rsa_factorize_n.html
large number can take minutes to factor, therefore are not included in doctest. large number can take minutes to factor, therefore are not included in doctest.
""" """
@ -17,6 +19,7 @@ import random
def rsafactor(d: int, e: int, n: int) -> list[int]: def rsafactor(d: int, e: int, n: int) -> list[int]:
""" """
This function returns the factors of N, where p*q=N This function returns the factors of N, where p*q=N
Return: [p, q] Return: [p, q]
We call N the RSA modulus, e the encryption exponent, and d the decryption exponent. We call N the RSA modulus, e the encryption exponent, and d the decryption exponent.

View File

@ -2,8 +2,10 @@ def remove_duplicates(key: str) -> str:
""" """
Removes duplicate alphabetic characters in a keyword (letter is ignored after its Removes duplicate alphabetic characters in a keyword (letter is ignored after its
first appearance). first appearance).
:param key: Keyword to use :param key: Keyword to use
:return: String with duplicates removed :return: String with duplicates removed
>>> remove_duplicates('Hello World!!') >>> remove_duplicates('Hello World!!')
'Helo Wrd' 'Helo Wrd'
""" """
@ -18,6 +20,7 @@ def remove_duplicates(key: str) -> str:
def create_cipher_map(key: str) -> dict[str, str]: def create_cipher_map(key: str) -> dict[str, str]:
""" """
Returns a cipher map given a keyword. Returns a cipher map given a keyword.
:param key: keyword to use :param key: keyword to use
:return: dictionary cipher map :return: dictionary cipher map
""" """
@ -43,9 +46,11 @@ def create_cipher_map(key: str) -> dict[str, str]:
def encipher(message: str, cipher_map: dict[str, str]) -> str: def encipher(message: str, cipher_map: dict[str, str]) -> str:
""" """
Enciphers a message given a cipher map. Enciphers a message given a cipher map.
:param message: Message to encipher :param message: Message to encipher
:param cipher_map: Cipher map :param cipher_map: Cipher map
:return: enciphered string :return: enciphered string
>>> encipher('Hello World!!', create_cipher_map('Goodbye!!')) >>> encipher('Hello World!!', create_cipher_map('Goodbye!!'))
'CYJJM VMQJB!!' 'CYJJM VMQJB!!'
""" """
@ -55,9 +60,11 @@ def encipher(message: str, cipher_map: dict[str, str]) -> str:
def decipher(message: str, cipher_map: dict[str, str]) -> str: def decipher(message: str, cipher_map: dict[str, str]) -> str:
""" """
Deciphers a message given a cipher map Deciphers a message given a cipher map
:param message: Message to decipher :param message: Message to decipher
:param cipher_map: Dictionary mapping to use :param cipher_map: Dictionary mapping to use
:return: Deciphered string :return: Deciphered string
>>> cipher_map = create_cipher_map('Goodbye!!') >>> cipher_map = create_cipher_map('Goodbye!!')
>>> decipher(encipher('Hello World!!', cipher_map), cipher_map) >>> decipher(encipher('Hello World!!', cipher_map), cipher_map)
'HELLO WORLD!!' 'HELLO WORLD!!'
@ -70,6 +77,7 @@ def decipher(message: str, cipher_map: dict[str, str]) -> str:
def main() -> None: def main() -> None:
""" """
Handles I/O Handles I/O
:return: void :return: void
""" """
message = input("Enter message to encode or decode: ").strip() message = input("Enter message to encode or decode: ").strip()

View File

@ -22,7 +22,7 @@ TEST_NUMBER_TO_CHARACTER = {val: key for key, val in TEST_CHARACTER_TO_NUMBER.it
def __encrypt_part(message_part: str, character_to_number: dict[str, str]) -> str: def __encrypt_part(message_part: str, character_to_number: dict[str, str]) -> str:
""" """
Arrange the triagram value of each letter of 'message_part' vertically and join Arrange the triagram value of each letter of `message_part` vertically and join
them horizontally. them horizontally.
>>> __encrypt_part('ASK', TEST_CHARACTER_TO_NUMBER) >>> __encrypt_part('ASK', TEST_CHARACTER_TO_NUMBER)
@ -65,8 +65,8 @@ def __prepare(
""" """
A helper function that generates the triagrams and assigns each letter of the A helper function that generates the triagrams and assigns each letter of the
alphabet to its corresponding triagram and stores this in a dictionary alphabet to its corresponding triagram and stores this in a dictionary
("character_to_number" and "number_to_character") after confirming if the (`character_to_number` and `number_to_character`) after confirming if the
alphabet's length is 27. alphabet's length is ``27``.
>>> test = __prepare('I aM a BOy','abCdeFghijkLmnopqrStuVwxYZ+') >>> test = __prepare('I aM a BOy','abCdeFghijkLmnopqrStuVwxYZ+')
>>> expected = ('IAMABOY','ABCDEFGHIJKLMNOPQRSTUVWXYZ+', >>> expected = ('IAMABOY','ABCDEFGHIJKLMNOPQRSTUVWXYZ+',
@ -75,24 +75,28 @@ def __prepare(
True True
Testing with incomplete alphabet Testing with incomplete alphabet
>>> __prepare('I aM a BOy','abCdeFghijkLmnopqrStuVw') >>> __prepare('I aM a BOy','abCdeFghijkLmnopqrStuVw')
Traceback (most recent call last): Traceback (most recent call last):
... ...
KeyError: 'Length of alphabet has to be 27.' KeyError: 'Length of alphabet has to be 27.'
Testing with extra long alphabets Testing with extra long alphabets
>>> __prepare('I aM a BOy','abCdeFghijkLmnopqrStuVwxyzzwwtyyujjgfd') >>> __prepare('I aM a BOy','abCdeFghijkLmnopqrStuVwxyzzwwtyyujjgfd')
Traceback (most recent call last): Traceback (most recent call last):
... ...
KeyError: 'Length of alphabet has to be 27.' KeyError: 'Length of alphabet has to be 27.'
Testing with punctuations that are not in the given alphabet Testing with punctuations that are not in the given alphabet
>>> __prepare('am i a boy?','abCdeFghijkLmnopqrStuVwxYZ+') >>> __prepare('am i a boy?','abCdeFghijkLmnopqrStuVwxYZ+')
Traceback (most recent call last): Traceback (most recent call last):
... ...
ValueError: Each message character has to be included in alphabet! ValueError: Each message character has to be included in alphabet!
Testing with numbers Testing with numbers
>>> __prepare(500,'abCdeFghijkLmnopqrStuVwxYZ+') >>> __prepare(500,'abCdeFghijkLmnopqrStuVwxYZ+')
Traceback (most recent call last): Traceback (most recent call last):
... ...
@ -130,9 +134,9 @@ def encrypt_message(
PARAMETERS PARAMETERS
---------- ----------
* message: The message you want to encrypt. * `message`: The message you want to encrypt.
* alphabet (optional): The characters to be used for the cipher . * `alphabet` (optional): The characters to be used for the cipher .
* period (optional): The number of characters you want in a group whilst * `period` (optional): The number of characters you want in a group whilst
encrypting. encrypting.
>>> encrypt_message('I am a boy') >>> encrypt_message('I am a boy')
@ -169,20 +173,21 @@ def decrypt_message(
decrypt_message decrypt_message
=============== ===============
Decrypts a trifid_cipher encrypted message . Decrypts a trifid_cipher encrypted message.
PARAMETERS PARAMETERS
---------- ----------
* message: The message you want to decrypt . * `message`: The message you want to decrypt.
* alphabet (optional): The characters used for the cipher. * `alphabet` (optional): The characters used for the cipher.
* period (optional): The number of characters used in grouping when it * `period` (optional): The number of characters used in grouping when it
was encrypted. was encrypted.
>>> decrypt_message('BCDGBQY') >>> decrypt_message('BCDGBQY')
'IAMABOY' 'IAMABOY'
Decrypting with your own alphabet and period Decrypting with your own alphabet and period
>>> decrypt_message('FMJFVOISSUFTFPUFEQQC','FELIXMARDSTBCGHJKNOPQUVWYZ+',5) >>> decrypt_message('FMJFVOISSUFTFPUFEQQC','FELIXMARDSTBCGHJKNOPQUVWYZ+',5)
'AIDETOILECIELTAIDERA' 'AIDETOILECIELTAIDERA'
""" """