56 lines
2.1 KiB
Python
56 lines
2.1 KiB
Python
from easybits import Bits
|
|
|
|
class CaesarCipher:
|
|
"""Implements the caeser cipher.
|
|
In the caeser cipher, each character is converted into a number,
|
|
then a secret number is added to each (if the result is too large, we
|
|
loop it back around), and the result is converted back into another
|
|
character.
|
|
|
|
int_min and int_max represent the lowest and highest allowed int values
|
|
of characters. They are set to include all the ASCII printable
|
|
characters (https://en.wikipedia.org/wiki/ASCII#Printable_character_table)
|
|
|
|
ASCII values outside this range (for example, '\n', the newline character),
|
|
just get passed through unencrypted.
|
|
"""
|
|
|
|
int_min = 32
|
|
int_max = 127
|
|
|
|
def __init__(self, secret):
|
|
self.secret = secret
|
|
|
|
def encrypt(self, plaintext):
|
|
"Converts a plaintext message into an encrypted ciphertext"
|
|
ciphertext = []
|
|
for char in plaintext:
|
|
plain_int = Bits(char, encoding='ascii').int
|
|
if self.int_min <= plain_int and plain_int < self.int_max:
|
|
cipher_int = self.rotate(self.secret, plain_int)
|
|
ciphertext.append(Bits(cipher_int, length=8).ascii)
|
|
else:
|
|
ciphertext.append(char)
|
|
return ''.join(ciphertext)
|
|
|
|
def decrypt(self, ciphertext):
|
|
"Converts an encrypted ciphertext into a plaintext message"
|
|
plaintext = []
|
|
for char in ciphertext:
|
|
cipher_int = Bits(char, encoding='ascii').int
|
|
if self.int_min <= cipher_int and cipher_int < self.int_max:
|
|
plain_int = self.rotate(-self.secret, cipher_int)
|
|
plaintext.append(Bits(plain_int, length=8).ascii)
|
|
else:
|
|
plaintext.append(char)
|
|
return ''.join(plaintext)
|
|
|
|
def rotate(self, secret, x):
|
|
"""Adds a secret number to x.
|
|
The modulo operator (%) is used to ensure that the result
|
|
is greater than equal to int_min and less than int_max.
|
|
"""
|
|
range_size = self.int_max - self.int_min
|
|
return (x + secret - self.int_min) % range_size + self.int_min
|
|
|