cryptotools/Cryptotools/Encryptions/RSA.py
2026-01-11 09:19:22 +01:00

175 lines
4.8 KiB
Python

#!/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