def signing(M: bytes, privateK: tuple = None, saving: bool = False, Verbose: bool = False): """ Signing a message M (bytes). """ from ..hashbased import hashFunctions as hashF # y choosed randomly between 1 and p-2 with condition than y coprime to p-1 if not privateK: privateK = it.extractKeyFromFile("private_key") p, g, x = privateK size = it.getKeySize(privateK) # M = bm.fileToBytes(M) # M = "Blablabla".encode() if Verbose: print("Hashing in progress...") hm = hashF.sponge(M, size) # #base64 to int hm = bm.bytes_to_int(bm.mult_to_bytes(hm)) if Verbose: print("Hashing done.\n") p1 = p - 1 k = rd.randrange(2, p - 2) while not ut.coprime(k, p1): k = rd.randrange(2, p - 2) if Verbose: print(f"Your secret integer is: {k}") s1 = ut.square_and_multiply(g, k, p) s2 = (multGroup.inv(k, p1) * (hm - x * s1)) % p1 # In the unlikely event that s2 = 0 start again with a different random k. if s2 == 0: if Verbose: print("Unlikely, s2 is equal to 0. Restart signing...") signing(M, privateK, saving, Verbose) else: sign = (s1, s2) if saving: sign = it.writeKeytoFile(sign, "elG_signature") return sign
def inv(a: int, m: int, Verbose: bool = False): """ Returns inverse of a mod m. If a and m are prime to each other, then there is an a^(-1) such that a^(-1) * a is congruent to 1 mod m. """ if ut.euclid(a, m) != 1: if Verbose: print( f"gcd({a}, {m}) = {ut.euclid(a, m)} != 1 thus you cannot get an invert of {a}." ) raise ValueError( f"gcd({a}, {m}) != 1 thus you cannot get an invert of {a}.") # a modular multiplicative inverse can be found directly elif a == 0: if Verbose: print( f"a = 0 and 0 cannot have multiplicative inverse ( 0 * nothing = 1 ) ." ) raise ValueError("0 cannot have multiplicative inverse.") elif ut.millerRabin(m) and m % a != 0: # A simple consequence of Fermat's little theorem is that if p is prime and does not divide a # then a^−1 ≡ a^(p − 2) (mod p) is the multiplicative if Verbose: print( f"From Fermat's little theorem, because {m} is prime and does not divide {a} so: {a}^-1 = {a}^({m}-2) mod {m}" ) u = ut.square_and_multiply(a, m - 2, m) elif ut.coprime(a, m) and m < (1 << 20): #From Euler's theorem, if a and n are coprime, then a^−1 ≡ a^(φ(n) − 1) (mod n). if Verbose: print( f"From Euler's theorem, because {a} and {m} are coprime -> {a}^-1 = {a}^(phi({m})-1) mod {m}" ) u = ut.square_and_multiply(a, phi(m, 1, 1, Verbose) - 1, m) else: if Verbose: print( f"Modular inverse u solves the given equation: a.u+m.v=1.\n Let's use the euclid extended algorithm tho." ) # Modular inverse u solves the given equation: a.u+m.v=1 # n number of iterations _, u, _, _, _ = ut.euclid_ext(a, m, Verbose) if u < 0: u += m if Verbose: return u, f"u = {u} + {m}k, k in Z" else: return u
def carmichaelFunction(n): """ The Carmichael function associates to every positive integer n a positive integer λ(n), defined as the smallest positive integer m such that a^m ≡ 1 (mod n) for every integer a between 1 and n that is coprime to n. In algebraic terms, λ(n) is the exponent of the multiplicative group of integers modulo n. https://mathworld.wolfram.com/CarmichaelFunction.html """ return max(multiplicativeOrder(a, n) for a in range(n + 1) if ut.coprime(a, n))
def key_gen(size: int = 2048, randomFunction=None, saving=False, Verbose=False): """ RSA key generation. n: number of bits for safe prime generation.\n randomFunction: prng choosen for random prime number generation (default = randbits from secrets module). As a transitional measure, the use of RSA-based signature and confidentiality mechanisms with a key size of at least 2000 bits remain conform for the year 2023. saving: True if you want to save the private key to a file. """ sizeB = size // 2 # 1- Choose two distinct prime numbers p and q if Verbose: print(f"Let's try to generate two distinct prime numbers p and q of {size} bits.") p, q = prng.randomPrime(sizeB, randomFunction, Verbose=Verbose), prng.randomPrime(sizeB, randomFunction, Verbose=Verbose) # 2- Compute n = pq. n = p * q # new modulus # 3- Compute λ(n), where λ is Carmichael's totient function. # since p and q are prime, λ(p) = φ(p) = p − 1 and likewise λ(q) = q − 1. Hence λ(n) = lcm(p − 1, q − 1). carmichaelTotient = ut.lcm(p - 1, q - 1) # 4- Choosing e, part of the public key e = rd.randrange(1, carmichaelTotient) while not ut.coprime(e, carmichaelTotient) or bm.hammingWeight(e) > (0.995 * sizeB): e = rd.randrange(1, carmichaelTotient) # e having a short bit-length and small Hamming weight results in more efficient encryption # https://en.wikipedia.org/wiki/Hamming_weight # 5- Chossing d, modular multiplicative inverse of e modulo carmichaelTotient(n) d = multGroup.inv(e, carmichaelTotient) # Private Key exponent # p, q, and λ(n) must also be kept secret because they can be used to calculate d. In fact, they can all be discarded after d has been computed. del p, q, carmichaelTotient public_key, private_key = (n, e), (n, d) if saving: public_key = it.writeKeytoFile(public_key, "public_key", config.DIRECTORY_PROCESSING, ".kpk") it.writeKeytoFile(private_key, "private_key", config.DIRECTORY_PROCESSING, ".kpk") if Verbose: print("\nYour private key has been generated Bob, keep it safe and never distibute them !") print("\nThe public key has been generated, send this to your Alice: ", end="") it.prGreen(public_key) if not saving: return (public_key, private_key)
def congruenceClasses(e: int): """ Returns coprimes elements of e. If n is a positive integer, the integers between 0 and n − 1 that are coprime to n (or equivalently, the congruence classes coprime to n) form a group. """ elements = [] for i in range(e): if ut.coprime(i, e) and i not in elements: elements.append(i) return elements