#!/usr/bin/env python3 from Cryptotools.Numbers.coprime import phi from Cryptotools.Numbers.carmi import carmichael_lambda from Cryptotools.Utils.utils import gcd from Cryptotools.Numbers.primeNumber import getPrimeNumber from sys import getsizeof class RSAKey: """ This class store the RSA key with the modulus associated The key is a tuple of the key and the modulus n Attributes: key: It's the exponent key, can be public or private modulus: It's the public modulus length: It's the key length """ def __init__(self, key, modulus, length): """ Contain the RSA Key. An object of RSAKey can be a public key or a private key Args: key (Integer): it's the exponent of the key modulus (Integer): it's the public modulus of the key length (Integer): length of the key """ self._key = key self._modulus = modulus self._length = length @property def key(self): return self._key @property def modulus(self): return self._modulus @property def length(self): return self.length class RSA: """ This class generate public key based on RSA algorithm Attributes: p: it's the prime number for generating the modulus q: it's the second prime number for the modulus public: Object of the RSAKey which is the public key private: Object of the RSAKey for the private key """ def __init__(self): """ Build a RSA Key """ self._p = None self._q = None self._public = None self._private = None def generateKeys(self, size=512): """ This function generate both public and private keys Args: size: It's the size of the key and must be multiple of 64 """ # p and q must be coprime self._p = getPrimeNumber(size) self._q = getPrimeNumber(size) # compute n = pq n = self._p * self._q phin = (self._p - 1) * (self._q - 1) # e must be coprime with phi(n) # According to the FIPS 186-5, the public key exponent must be odd # and the minimum size is 65536 (Cf. Section 5.4 PKCS #1) # https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-5.pdf e = 65535 # Works for i in range(2, phin - 1): if gcd(phin, e) == 1: break e += 1 # print(gcd(phin, e)) self._public = RSAKey(e, n, getsizeof(e)) # d is the reverse modulo of phi(n) # d = self._inverseModular(e, phin) d = pow(e, -1, phin) # Works in python 3.8 self._private = RSAKey(d, n, getsizeof(d)) @property def e(self): return self._public.key @property def d(self): return self._private.key @property def n(self): return self._public.modulus @property def p(self): return self._p @property def q(self): return self._q def encrypt(self, data) -> list: """ This function return a list of data encrypted with the public key Args: data (str): it's the plaintext which need to be encrypted Returns: return a list of the data encrypted, each entries contains the value encoded """ dataEncoded = self._str2bin(data) return list( pow(int(x), self._public.key, self._public.modulus) for x in dataEncoded ) def decrypt(self, data) -> list: """ This function return a list decrypted with the private key Args: data (str): It's the encrypted data which need to be decrypted Returns: Return the list of data uncrypted into plaintext """ decrypted = list() for x in data: d = pow(x, self._private.key, self._private.modulus) decrypted.append(chr(d)) return ''.join(decrypted) def _str2bin(self, data) -> list: """ This function convert a string into the unicode value Args: data: the string which need to be converted Returns: Return a list of unicode values of data """ return list(ord(x) for x in data) def _inverseModular(self, a, n): """ This function compute the modular inverse for finding d, the decryption key Args: a (Integer): the base of the exponent n (Integer): the modulus """ for b in range(1, n): #if pow(a, b, n) == 1: if (a * b) % n == 1: inv = b break return inv