import base64 import secrets import math import Crypto.Util # This commutative cipher is based on the SRA cryptographical system, which is just a modification of RSA where the # modulus n is known, but both the encryption and decryption exponents are kept secret. As long as both keys use the # same modulus, this cryptography system is commutative, i.e. Ea(Eb(x)) = Eb(Ea(x)) if encryption with key a is denoted # as Ea() and encryption with key b is denoted as Eb. class CommutativeCipher: def __init__(self, p, q): self.n = p*q carmichael_function = (p-1) * (q-1) # Make the exponent have almost as many bits as the modulus number_of_bits = int(math.ceil(math.log(self.n) / math.log(2))) self.e = Crypto.Util.number.getPrime(number_of_bits-10, randfunc=secrets.token_bytes) self.d = pow(self.e, -1, carmichael_function) def encode(self, message): message_was_base64 = False message_was_bytes = False if isinstance(message, str): message_bytes = base64.b64decode(message) message = message_bytes message_was_base64 = True try: message_int = int.from_bytes(message) message = message_int message_was_bytes = True except TypeError: # Assume message is already an integer pass if not isinstance(message, int): raise Exception( 'The message to encrypt was not of the correct type (base64 string, bytes-like object, or integer' ) if message >= self.n: raise Exception( 'The message is equal to or larger than the modulus' ) encrypted = pow(message, self.e, self.n) if message_was_bytes: # Find number of bits number_of_bits = int(math.ceil(math.log(encrypted) / math.log(2))) number_of_bytes = int(math.ceil(number_of_bits / 8)) encrypted = encrypted.to_bytes(number_of_bytes) if message_was_base64: encrypted = base64.b64encode(encrypted) return encrypted def decode(self, cipher): cipher_was_base64 = False cipher_was_bytes = False if isinstance(cipher, str): cipher_was_base64 = True cipher = base64.b64decode(cipher) try: cipher_int = int.from_bytes(cipher) cipher = cipher_int cipher_was_bytes = True except TypeError: pass if not isinstance(cipher, int): raise Exception('The passed cipher was not a valid type (base64 string, bytes object or integer)') if cipher >= self.n: raise Exception('The passed cipher is equal to or larger than the modulus') decrypted = pow(cipher, self.d, self.n) if cipher_was_bytes: number_of_bits = int(math.ceil(math.log(decrypted)/math.log(2))) number_of_bytes = int(math.ceil(number_of_bits / 8)) decrypted = decrypted.to_bytes(number_of_bytes) if cipher_was_base64: decrypted = base64.b64encode(decrypted) return decrypted