#!/usr/bin/python3 """ * File : sdlh-generate-hashkey * * [ Key generation for Shamir's Discrete Logarithm Hash Function https://senderek.ie/sdlh ] * [ SDLH is part of the Pure Crypto Project https://senderek.ie/pcp2 ] * * Version : 2.0 * Date : 1 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, is_strong_bpsw_prp except: print ("Please install the python3-gmpy2 package") sys.exit(2) # Global variables P = Q = R = S = 0 G = 0 G_P = G_Q = 0 Order = 0 # Constants maxMillerRabin = 64 DefaultSize = 90 MinSize = 85 MaxSize = 128 #-----------------------------------------------------------# def ModExp (Base, Exp, Mod): Hash = 1 X = Exp Factor = Base while X > 0 : Remainder = X % 2 X = X // 2 if Remainder == 1: Hash = Hash * Factor % Mod Factor = Factor * Factor % Mod return Hash #-----------------------------------------------------------# def bits(N) : I = 0 while N > 0 : N = N // 2 I = I + 1 return I #-----------------------------------------------------------# 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] #-----------------------------------------------------------# # number of random bytes requested from /dev/random size = DefaultSize if (len(sys.argv) == 3) and sys.argv[1] == "-size": # user selects size try: size = int(sys.argv[2]) except: print("Using the default size", DefaultSize, "random bytes.") size = DefaultSize if (size < MinSize) or (size > MaxSize) : print("Your size is not in the range", MinSize, "to" , MaxSize, "random bytes.") sys.exit(2) else: if not (len(sys.argv) == 1) : print("usage: sdlh-generate-hashkey [-size numrandombytes]") sys.exit(1) print() print("Generating strong primes P and Q ...") print("Using ", size, " bytes from /dev/random for each guess") print() [P, R] = generate_strong_prime(size) [Q, S] = generate_strong_prime(size) if (P == 0) or (Q == 0) : print("One of the two generated numbers are not prime.") sys.exit(2) Phi = (P-1) * (Q-1) End = False Test = 1 while not End: TestG = read_random( 2*size - 1 ) G_P = TestG % P G_Q = TestG % Q if (G_P <= 1) or (G_P >= P) or (G_Q <= 1) or (G_Q >= Q) : print("G_P or G_Q is foul.") sys.exit(2) A = ModExp(G_P, R, P) B = ModExp(G_Q, S, Q) if ( A > 1 ) and ( B > 1 ) : End = True G = TestG Order = Phi // 2 print() print("Found a valid generator for the group mod(P*Q) of size ", bits(P*Q), "bits") else: print(" + ", end="") Test += 1 print() print ("P = " , P, "\n[", str(bits(P)), " bits ]") print() print ("Q = ", Q, "\n[", str(bits(Q)), " bits ]") print() D = abs(Q - P) print ("Distance: abs(Q-P) = ", D, "\n[", str(bits(D)), " bits ]") print() print ("***********************************") print ("Hashmodulus = P*Q = ", str(P*Q), "\n[", str(bits(P*Q)), " bits ]") print() print ("Generator = ", str(G), "\n[", str(bits(G)), " bits ]") print ("***********************************") print() RET=input("Do you want to use this hashmodulus and generator values? [yes/no] : ") if RET == "yes" or RET == "y" : ID = sanitize(input("Please provide the identifikation string for this key : ")) print() print("Writing a hashkey file ./hashkey") try: F = open("./hashkey", "w") F.write("Hashmodulus = " + str(P*Q) + "\n") F.write("Generator = " + str(G) + "\n") F.write(str(ID) + "\n") F.close() print("RESULT:") os.system("ls -la ./hashkey") print() os.system("cat ./hashkey") except: print("The file ./hashkey cannot be written to the file system.") sys.exit(2) else: print("Forget everything") sys.exit(0) #-----------------------------------------------------------# # Copyright 2003 - 2025, Ralf Senderek, Ireland # #-----------------------------------------------------------#