#!/usr/bin/python3 """ * File : pcp2-generate-rsakeys * [Part of the Pure Crypto Project https://senderek.ie/pcp2] * * Version : 2.0 * Date : 4 Jan 2025 * License : BSD * * Copyright (c) 2003 - 2025 * Ralf Senderek, Ireland. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Ralf Senderek. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * """ import sys, os try: from gmpy2 import is_prime, gcd ,is_strong_bpsw_prp except: print ("Please install the python3-gmpy2 package") sys.exit(2) # default intallation path for pcp2 modules sys.path.append("/usr/share/pcp2") import pure maxMillerRabin = 64 # defaults for encryption keys DefaultEncSize = 90 # 1440 bits MinEncSize = 85 # 1360 bits MaxEncSize = 128 # 2040 bits # a signing key modulus must be at least 520 bits larger than the hash modulus ! # defaults for signing keys DefaultSigSize = 135 # 2150 bits MinSigSize = 135 # 2150 bits MaxSigSize = 200 # 3200 bits # set a fixed encryption exponent (F4) Encryption = 65537 #-----------------------------------------------------------# def bits(N) : I = 0 while N > 0 : N = N // 2 I = I + 1 return I #-----------------------------------------------------------# def egcd(a, b): if a == 0: return (b, 0, 1) g, y, x = egcd(b%a,a) return (g, x - (b//a) * y, y) #-----------------------------------------------------------# def modinv(a, m): g, x, y = egcd(a, m) if g != 1: raise Exception('No modular inverse') return x%m #-----------------------------------------------------------# def read_random(noBytes): Rand = 0 try: F = open("/dev/random", "rb") Rand = int.from_bytes(F.read(noBytes), "big") F.close() except: print("no random bytes available") if (Rand % 2) == 0 : # make Rand odd Rand += 1 return Rand #-----------------------------------------------------------# def generate_strong_prime( randombytes ): # is_prime() performs a number of Miller-Rabin tests # is_strong_bpsw_prp performs a strong Lucas probable prime test End = False cnt = 0 while not End: P = read_random(randombytes) cnt += 1 if is_prime(P , maxMillerRabin): if is_prime((P-1)//2 , maxMillerRabin): End = True print ("Found a strong prime with", cnt, "tries") if is_strong_bpsw_prp(P): return [ P, (P-1)//2 ] return [0,0] #-----------------------------------------------------------# def sanitize(data): maxchars = 100 forbidden = "\n\r\t\"$%=?~;\\" good = "" for i in range(len(data)) : if (data[i] not in forbidden) : good = good + data[i] return good[:maxchars] #-----------------------------------------------------------# keytype = "none" size = 0 if (len(sys.argv) >= 3) and sys.argv[1] == "-size": # user selects size try: size = int(sys.argv[2]) del(sys.argv[1:3]) except: size = 0 if len(sys.argv) == 2: # a signingkey must be longer than an encryption key print() if "enc" in sys.argv[1] : keytype = "encryptionkey" print("Generating a ",keytype) if size : if ((size < MinEncSize) or (size > MaxEncSize)) : print("Your size value is not in the range", MinEncSize, "to" , MaxEncSize, "random bytes.") sys.exit(2) else: # use the default size = DefaultEncSize # number of random bytes requested from /dev/random else: keytype = "signingkey" print("Generating a ",keytype) if size : if ((size < MinSigSize) or (size > MaxSigSize)) : print("Your size value is not in the range", MinSigSize, "to" , MaxSigSize, "random bytes.") sys.exit(2) else: # use the default size = DefaultSigSize # number of random bytes requested from /dev/random else: print("usage: pcp2-generate-rsakeys [-size numrandombytes] enc|sig") sys.exit(1) HashModulus = Generator = 1 try: FILE = open("hashkey", "rb") Content = FILE.readlines() FILE.close() try: HashModulus = pure.toLong(Content[0]) Generator = pure.toLong(Content[1]) except: print ("\nNo hashkey information available in file \"hashkey\"") print ("Please run sdlh-generate-hashkey first to overwrite it.\n") sys.exit(3) except IOError : print ("\nThe hashkey file is unavailable.") print ("Please run sdlh-generate-hashkey first.\n") sys.exit(1) if (HashModulus != 1) and (Generator != 1) : print("Hashkey information is available") print("Hashmodulus : ", HashModulus, "[ ", str(bits(HashModulus)), "bits ]") print("Hashgenerator : ", Generator) print() print("Generating strong primes P and Q ...") print("using ", size, " bytes from /dev/random for each guess") print() P = Q = Modulus = phi = 0 P = generate_strong_prime(size)[0] Q = generate_strong_prime(size)[0] print ("P = " , P, "\n[", str(bits(P)), " bits ]") print ("Q = ", Q, "\n[", str(bits(Q)), " bits ]") D = abs(Q - P) print ("Distance: abs(Q-P) = ", D, "\n[", str(bits(D)), " bits ]") print() print ("***********************************") print ("Modulus = P*Q = ", str(P*Q), "\n[", str(bits(P*Q)), " bits ]") if P and Q : Modulus = P*Q if (keytype == "signingkey") and ( (bits(HashModulus) + pure.ModulusMargin) > bits(Modulus) ) : print("The RSA modulus is too short.") print("There is only a", bits(Modulus) - bits(HashModulus) , "bit difference to the hash modulus." ) print("This difference must be at least", pure.ModulusMargin, "bits.") print("You need to increase the size of your signing key.") sys.exit(4) phi = (P-1)*(Q-1) # check if gcd( (p - 1)(q - 1), Encryption ) = 1, which is required for RSA # abort with an error message, if the primes do not fit. RSAcheck = gcd( (P-1)*(Q-1), Encryption ) if RSAcheck != 1: print("ERROR: Please repeat the generation, as p and q don\'t work with the chosen encryption exponent", Encryption) sys.exit(4) # determine the decryption exponent Decryption = modinv(Encryption, phi) print() print('Encryption exponent =', Encryption, "\n[", str(bits(Encryption)), " bits ]") print() print('Decryption exponent =', Decryption, "\n[", str(bits(Decryption)), " bits ]") print ("***********************************") print() RET=input("Do you want to use these values? [yes/no] : ") if RET == "yes" or RET == "y" : UserID = sanitize(input("Please provide the identifikation string for this key : ")) try: Message = b'' Message += pure.toBytes(Modulus) + pure.toBytes(Encryption) + pure.toBytes(HashModulus) + pure.toBytes(Generator) + UserID.encode() SecHash = 0 pure.HashModulus = HashModulus pure.Generator = Generator SecHash = pure.hash256(Message) print ("Securityhash : ",str(SecHash)) except: print ("\nCannot create a security hash value\n") sys.exit(3) print() print("Writing a key file ...") try: F = open("./" + keytype, "w") F.write("Modulus = " + str(P*Q) + "\n") F.write("Encryption = " + str(Encryption) + "\n") F.write("Decryption = " + str(Decryption) + "\n") F.write("Hashmodulus = " + str(HashModulus) + "\n") F.write("Generator = " + str(Generator) + "\n") F.write(str(UserID) + "\n") F.write("Securityhash = " + str(SecHash) + "\n") F.write("Protection = None\n") F.close() os.system("chmod 600 " + "./" + keytype) F = open("./" + keytype +".pub", "w") F.write("Modulus = " + str(P*Q) + "\n") F.write("Encryption = " + str(Encryption) + "\n") F.write("Hashmodulus = " + str(HashModulus) + "\n") F.write("Generator = " + str(Generator) + "\n") F.write(str(UserID) + "\n") F.write("Securityhash = " + str(SecHash) + "\n") F.close() print("RESULT: ./" + keytype) os.system("ls -la ./" + keytype) print() os.system("cat ./"+ keytype) except: print("The file cannot be written to the file system.") else: print("Forget everything") sys.exit(0) #-----------------------------------------------------------# # Copyright 2003 - 2025, Ralf Senderek, Ireland # #-----------------------------------------------------------#