sig=""" -----BEGIN PGP SIGNED MESSAGE----- # # Pure Crypto Program based on modular exponentiation and RSA alone. # # Library # # 2003/8/23 (version 1.0) # # This is free software. # The code was written by Ralf Senderek . # Use this software on your own risk or not at all. # There is NO warranty; not even for MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. """ import sys, os Mode = "CONSERVATIVE" Check = "PURE" Modulus = ModulusLength = Encryption = Decryption = 0L Securityhash = Hash = 0L HashModulus = HashModulusLength = Generator = 1L Block = 0 protected = "None" UserID = ENTROPY = PASS = "" Minimal_key_length = Minimal_hashkey_length = 1000 # bits Pure_Comment = "*** based on modular exponentiation and RSA alone ***" SHA_Comment = " *** based on modular exponentiation, RSA and SHA1 ***" Comment = SHA_Comment Hashtype = "Hash: SHA1 " # OS specific settings OS = "unix" EOL = "\n" if os.name == "nt" or os.name == "dos" : Home = "c:\\pcp\\" Console = "CON" OS = "billware" else: Home = os.environ['HOME'] + "/.pcp/" Console = "/dev/tty" ################################################################ def Line (S) : # remove all end of line characters while S[-1:] == "\r" or S[-1:] == "\n" : S = S[:-1] return S ################################################################ # default values (can be overwritten by command line or config files) try: File = open(Home + "pcp-mode", "r") Content = [] Content = File.readlines() request1 = request2 = "" if len(Content) > 0 : request1 = Line(Content[0])[:4] if len(Content) > 1 : request2 = Line(Content[1])[:9] File.close() import string if string.upper(request1) == "PURE": Mode = "PURE" Hashtype = "Hash: SDLH " Comment = Pure_Comment else: Mode = "CONSERVATIVE" Hashtype = "Hash: SHA1 " if string.upper(request2) == "FASTCHECK": Check = "FAST" else: Check = "PURE" except: pass try: File = open(Home + "pcp-comment", "r") Comment = Line(File.readlines()[0]) File.close() except: pass ################################################################ def print_banner(): print EOL+EOL+"##### Pure Crypto Project Version 1.0 #####" print " based on Modular Exponentiation" print " and RSA alone"+EOL print "This software has been coded with utmost care but at the moment " print "it has not received much independent scrutiny and peer review." print "Use this software at your own risc and look at the code and documentation." + EOL ################################################################ def print_security_notice(): print "PCP is running in " + Mode + " mode." + EOL ################################################################ def countbits(Number): Bits = 0 while Number > 0 : Number = Number / 2 Bits = Bits + 1 return Bits ################################################################ def toLong (S) : # strips everything not decimal from String and converts to long # strip from the begining while ord(S[:1]) < 48 or ord (S[:1]) > 57 : S = S[1:] # strip from the end while ord(S[-1:]) < 48 or ord (S[-1:]) > 57 : S = S[:-1] return long(S) ################################################################ def toString (L) : # returns only decimal digits S = str(L) if S[-1:] == "L" : S = S[:-1] return S ################################################################ def read_cryptosystem(Keyfile) : import os, sys global Modulus, Encryption, Decryption, HashModulus, Generator global ModulusLength, HashModulusLength, Block, UserID, protected, Securityhash if Keyfile == "encryptionkey" : KeyfileName = "encryptionkey" else: KeyfileName = "signingkey" try: FILE = open(Home + KeyfileName, "r") Content = FILE.readlines() FILE.close() try: Modulus = toLong(Content[0]) Encryption = toLong(Content[1]) Decryption = toLong(Content[2]) HashModulus= toLong(Content[3]) Generator = toLong(Content[4]) UserID = Line(Content[5]) protected = Line(Content[6])[13:] if Content[7][:19] != "Securityhash = None" : Securityhash = toLong(Content[7]) except: print EOL + "Keyfile is corrupt ! " + EOL sys.exit(3) except IOError : print "Your keyfile is unavailable" sys.exit(1) # calculate length of moduli in bits ModulusLength = countbits(Modulus) HashModulusLength = countbits(HashModulus) # calculate blocks Block = ModulusLength / 8 # paranoia setting Block < Modulus Block = Block - 1 # in Bytes if ModulusLength < Minimal_key_length : print "Warning: Key is too short! (", toStrong(ModulusLength), " Bits)" sys.exit(4) if Mode == "PURE" : if HashModulusLength < Minimal_hashkey_length : print "Warning: Hash-Key is too short! (", toString(HashModulusLength), " Bits)" sys.exit(4) if (ModulusLength - 600 < HashModulusLength) and (KeyfileName == "signingkey") : print "Warning: The signing key\'s modulus (" ,toString(ModulusLength) , print ") must be at least 600 bits longer " print "than the hashmodulus (", toString(HashModulusLength), ") !" sys.exit(4) ################################################################ def burn_secretkey(): global Hash, PASS, Decryption print "burning ..." PASS = str(Modulus) Hash = Modulus Decryption = Modulus ################################################################ def print_securityhash(): global Securityhash # uses the present cryptosystem Message = toString(Modulus) + toString(Encryption) + toString(HashModulus) + toString(Generator) + UserID TestHash = 0L if Check == "FAST": print "FASTCHECK is requested." TestHash = conservativehash(Message) else: TestHash = hash256(Message) print "Securityhash: " print toString(TestHash) Securityhash = TestHash ################################################################ def key_valid(K): # returns K if valid, else None. if len(K) == 14 : TestMessage = K[1] + K[2] + K[3] + K[4] + K[5] + K[6] # modulus, encryption, HashModulus, Generator, UserID, Securityhash # nothing else is a valid key body TestHash = EncryptedSignature = SignatureNumber = PlainHash = 0L TestHash = hash(TestMessage) ClearSignature = "" EncryptedSignature = toLong(K[12]) SignatureNumber = ModExp(EncryptedSignature, Encryption, Modulus) if Mode == "PURE" : PlainHash = SignatureNumber % pow(2L,HashModulusLength) else : PlainHash = SignatureNumber % pow(2L,256L) if PlainHash == TestHash : return K else : print "Public key is not properly signed or is corrupted." return None ################################################################ def read_keylist(): KeyList = [] if Mode == "PURE" : KeyHome = Home + "/trusted-keys" else : KeyHome = Home + "/trusted-sha" try: KeyFiles = os.listdir(KeyHome) except: print EOL + "No trusted keys available" + EOL sys.exit(0) for File in KeyFiles : # read Key try : FILE = open (KeyHome + "/" + File, "r") KeyData = FILE.readlines() FILE.close() if len(KeyData) >= 10 : KeyList.append(KeyData) except IOError : print "cannot open " + File return KeyList ################################################################ def print_keylist(): KeyList = read_keylist() i = 0 for Key in KeyList : print str(i) +": ", Line(Key[5])[:75] i = i + 1 ################################################################ def select_cryptosystem(User): global Modulus, ModulusLength, Encryption, UserID, HashModulus global HashModulusLength, Generator, Block import os, regex, sys KeyList = read_keylist() try: X = int(User) User = Line(KeyList[X][5]) except: pass for PubKey in KeyList : if len(PubKey) >= 10 and regex.search(User, PubKey[5]) != -1 : # checking validity with user's pubkey read_cryptosystem("signingkey") if key_valid(PubKey) : Modulus = toLong(PubKey[1]) Encryption = toLong(PubKey[2]) HashModulus = toLong(PubKey[3]) Generator = toLong(PubKey[4]) UserID = Line(PubKey[5]) # calculate length of moduli in bits ModulusLength = countbits(Modulus) HashModulusLength = countbits(HashModulus) # calculate blocks Block = ModulusLength / 8 # paranoia setting Block < Modulus Block = Block - 1 # in Bytes if ModulusLength < Minimal_key_length : print "Warning: Key is too short! (", ModulusLength, " Bits)" sys.exit(4) print "Trusted public key found. Key is ", ModulusLength, " bits long." print_securityhash() # check the integrity of the stored securityhash try: StoredSecHash = toLong(PubKey[6]) if StoredSecHash != Securityhash : print EOL + "Warning: The signed key material", print " is inconsistent with the signed Security-Hash." print "Check with the key-issuer immediately."+ EOL else: print "The key\'s integrity is checked successfully." except: print "Cannot check the key\'s integrity." return PubKey else: print "Public key " + Line(PubKey[5]) + "is NOT a TRUSTED KEY.\n" return None return None ################################################################ 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 StringToLongList (S): List = [] index = 0 while index < len(S): PlainBlock = 0L count = 0 # PAD is 32 octetts long while count < (Block - 32) and index < len(S) : PlainBlock = PlainBlock * 256 + ord(S[index]) count = count + 1 index = index + 1 List.append(PlainBlock) return List ################################################################ def StringToLong (S): # returns Long < Modulus Number = 0L index = 0 while Number*256+255 < Modulus and index < len(S) : Number = Number * 256 + ord(S[index]) index = index + 1 return Number ################################################################ def HexToLong(Hexstring): # returns Long Number = 0L index = 0 while index < len(Hexstring) : Hex = ord(Hexstring[index]) if Hex > 47 and Hex < 58 : # 0-9 Number = Number * 16 + Hex - 48 if Hex > 96 and Hex < 103 : # a-f Number = Number * 16 + Hex - 87 index = index + 1 return Number ################################################################ def StringToLongList (S): List = [] index = 0 while index < len(S): PlainBlock = 0L count = 0 # PAD is 32 octetts long while count < (Block - 32) and index < len(S) : PlainBlock = PlainBlock * 256 + ord(S[index]) count = count + 1 index = index + 1 List.append(PlainBlock) return List ################################################################ def LongListToString (LL): PlainText = "" X = 0L Remainder = 0L for ClearBlock in LL : X = ClearBlock PlainBlock = "" while X > 0 : Remainder = X % 256 X = X / 256 PlainBlock = chr(Remainder) + PlainBlock PlainText = PlainText + PlainBlock return PlainText # String ################################################################ def getNonce(): import os # maybe there are better ways to get a nonce N = 0L if OS == "unix" : try : FILE = os.open("/dev/random", 0, 0) CHARS = os.read(FILE, 64) # 512 bit N = hash256(CHARS) # 256 bit except: print "Error: Cannot read random data from /dev/random" sys.exit(4) else: # Wish I had /dev/random ! import whrandom, time R = str(time.clock()) for X in range(8) : R = R + str(whrandom.random()) R = R + str(time.clock()) N = hash256(R) return N ################################################################ def create_OAEP(Message): # returns Long, with OAEP padded Nonce of 256 Bit Block = C1 = C2 = H = 0L Nonce2 = getNonce() H = Long_hash256(Nonce2) C1 = Message ^ H # XOR operation C2 = Nonce2 ^ Long_hash256(C1) Block = C1 * pow(2L,256L) + C2 return Block ################################################################ def extract_OAEP(Block): # returns 256 Bit Nonce from Long Message = Nonce2 = C1 = C2 = H = 0L C1 = Block / pow(2L,256L) C2 = Block % pow(2L,256L) H = Long_hash256(C1) Nonce2 = C2 ^ H H = Long_hash256(Nonce2) Message = C1 ^ H return Message ################################################################ def encrypt (P, E, M): # P : String, E, M : Long # returns List of Long Integer print "Encrypting ... " + EOL Plain = StringToLongList(P) Crypt = [] CryptBlock = 0L # The padding method currently implemented is hash-chain-padding # A nonce is transmitted in the first crypto-block with OAEP # # Block 2: PAD1 = nonce, PAD2 = hash(nonce), CHALLENGE = PAD1 XOR PAD2 # CHALLENGE has length 256 Bit # # Block i : Message XOR hash(PAD1) || PAD1 XOR hash(PAD1) # set PAD1 = hash(PAD1) and PAD2 = hash(PAD2) for i+1 Nonce = getNonce() PAD = 0L PAD1 = Nonce PAD2 = Long_hash256(PAD1) FirstBlock = create_OAEP(Nonce) CryptBlock = ModExp(FirstBlock, E, M) Crypt.append(CryptBlock) for PlainBlock in Plain : PAD = PAD1 ^ PAD2 # padding of PlainBlock with PAD1 extended to length of PlainBlock LONGPAD = pow(PAD1, (Block/32)+1) if Mode == "CONSERVATIVE" : # LONGPAD is too short! SHA has only 160 bits LONGPAD = pow(PAD1, (Block/20)+1) EXP = (Block - 32) * 8 # bits LONGPAD = LONGPAD % pow(2L, EXP) PlainBlock = PlainBlock ^ LONGPAD # XOR operation PlainBlock = PlainBlock * pow(2L,256L) + PAD CryptBlock = ModExp(PlainBlock, E, M) Crypt.append(CryptBlock) PAD1 = PAD2 PAD2 = Long_hash256(PAD1) return Crypt # LongList ################################################################ def decrypt (C, D, M): # C : LongList, D, M : Long # returns String Clear = [] # LongList ClearBlock = 0L count = len(C) # Padding inserted here. FirstBlock = ModExp(C[0] , D, M) Nonce = extract_OAEP(FirstBlock) C = C[1:] PAD = 0L PAD1 = Nonce PAD2 = Long_hash256(PAD1) ERRORS = "" for CryptBlock in C : PAD = PAD1 ^ PAD2 ClearBlock = ModExp(CryptBlock, D, M) # extract Challenge and Message CHALLENGE = ClearBlock % pow(2L, 256L) PaddedMessage = ClearBlock / pow(2L,256L) if PAD != CHALLENGE: ERRORS = "yes" # create Longpad and remove padding with PAD1 LONGPAD = pow(PAD1, (Block/32)+1) if Mode == "CONSERVATIVE" : # LONGPAD is too short! SHA has only 160 bits LONGPAD = pow(PAD1, (Block/20)+1) EXP = (Block - 32) * 8 # bits LONGPAD = LONGPAD % pow(2L, EXP) Message = PaddedMessage ^ LONGPAD # XOR operation Clear.append(Message) PAD1 = PAD2 PAD2 = Long_hash256(PAD1) count = count - 1 print count ClearText = LongListToString(Clear) if ERRORS : return "Cryptogram is corrupt. To prevent oracle attacks nothing is written." + EOL else: return ClearText ################################################################ def hash256(Message) : if Mode == "PURE" : return SDLH256(Message) if Mode == "CONSERVATIVE" : return conservativehash(Message) else: return None ################################################################ def Long_hash256(Number) : if Mode == "PURE" : return Long_SDLH256(Number) if Mode == "CONSERVATIVE" : return conservativehash(str(Number)) else: return None ################################################################ def hash(Message) : if Mode == "PURE" : return SDLH(Message) if Mode == "CONSERVATIVE" : return conservativehash(Message) else: return None ################################################################ def SDLH(Message) : print "..." # # Calculates hash(x) = Generator^x mod HashModulus # # Speeding up the computation by avoiding to convert the string into # a long integer # This is the code splitting each processed character into # two ModExp()-operations # Hash = 1 # for Character in Message: # (1) A = ModExp(Generator, ord(Character), HashModulus) # (2) B = ModExp(Hash, 256, HashModulus) # (3) Hash = (A * B) % HashModulus # return Hash # Impemantation with precomputed table # precomputing table for step (1) table = [] table.append(1L) index = 1 Number = 1L while index < 256 : Number = (Number * Generator) % HashModulus table.append(Number) index = index + 1 # process the message character by character Hash = 1L for Character in Message: A = table[ord(Character)] # step (1) B = Hash # step (2) index = 8 while index > 0 : B = (B * B) % HashModulus index = index - 1 Hash = (A * B) % HashModulus # step (3) return Hash ################################################################ def SDLH256(Message) : Hash256 = 0L X = SDLH(Message) Size = pow(2L,256L) while X > 0 : Section = X % Size X = X / Size Hash256 = Hash256 ^ Section return Hash256 ################################################################ def Long_SDLH256(Number) : print "..." # # Calculates hash(x) = Generator^x mod HashModulus # and XORs all 256 bit sections of the result # Hash256 = 0L X = ModExp(Generator, Number, HashModulus) Size = pow(2L,256L) while X > 0 : Section = X % Size X = X / Size Hash256 = Hash256 ^ Section return Hash256 ################################################################ def conservativehash(Message): # S : String returns Long try: import sha Hash = sha.new() Hash.update(Message) Hex = Hash.hexdigest() return HexToLong(Hex) except: print "No SHA-1 available. Use PURE mode instead." sys.exit(4) ################################################################ def load_secretkey(): import os, sys global Decryption # makes secretkey available in Decryption if protected == "otp" : print EOL + "Your secret key is protected." +EOL print "Please enter your passphrase to unlock it." Flags = Mode = 0 try: CONSOLE = os.open(Console, Flags, Mode) PASS = "" if OS == "unix" : os.system("stty -F " + Console + " -echo") CHAR = os.read(CONSOLE, 1) while CHAR != EOL : PASS = PASS + CHAR CHAR = os.read(CONSOLE, 1) if OS == "unix" : os.system("stty -F " + Console + " echo") except: print "Cannot open console for reading." print "Your secretkey is unavailable!" sys.exit(3) Hash = hash256(PASS) print EOL + "Unlocking your secret key." Decryption = unlock_secretkey(Hash) # Burn sensitive information PASS = str(Modulus) Hash = Modulus else: print EOL + "Your secret key is not protected !" + EOL # check if secretkey works well with a challenge Number = 1893947706487L EncryptedNumber = 0L EncryptedNumber = ModExp(Number, Encryption, Modulus) Challenge = 0L Challenge = ModExp(EncryptedNumber, Decryption, Modulus) if Challenge == Number : print "Secret key is good." else : print "Error : Your secret key is unavailable." sys.exit(4) ################################################################ def unlock_secretkey(H): import os, sys try : File = open(Home + "entropy", "r") Entropy = File.read()[:-1] File.close() # check if entropyfile is long enough if len(Entropy) < 1099999 : print "FATAL ERROR: There is not enough entropy", print " available!" print len(Entropy) sys.exit(4) except: print EOL + "Your entropy file is unavailable !" + EOL sys.exit(4) X = H IndexList = [] while X > 0 : Remainder = X % 1000000 X = X / 1000000 IndexList.append(Remainder) OTP = Modulus # Initial value of PAD for index in IndexList : PadString = Entropy[int(index) : int(index) + int(Block+1)] NewPad = StringToLong(PadString) print "*", OTP = OTP ^ NewPad print return Decryption ^ OTP # otp-method ############################################################ # Copyright (c) 2003 Ralf Senderek, Wassenberg, Germany # ############################################################ sig=""" -----BEGIN PGP SIGNATURE----- Version: 2.6.3in Charset: noconv iQEVAwUBP0fIp76wVDeIE49tAQE2iwgAvC0d7VDk75E8zEvf2h5aEGE5DWyXOusE Rku7j1KRw9KlZFOc0inZ9KRlhEpZ4VF0xSpWT0Z5xqjFLCVYZkW+cADdjMm43C2z 1Ipz3QQp9Dba34Mggyp57G+hUpw/8DIFqMJN37+b+DtCzMPcR+gWYyxuXhuJRaB6 suiTy7P7VKGN67gPaNI8oE396OxKe4qnLi3MbVV3TVqQq/JZ1gW0IAQ1fQICNZaB tqjy8rYRuUeBS0LzITM1ZJ+g2O11KfLR/gsm3MKLj8rc3p11jNFKr0EX8+LQYZbs aDRduKXs+LcYyDV5Kt7nxna9rCQKCcfxQJVaZ2e5E7TMWgQoXUjkyA== =W5VD -----END PGP SIGNATURE----- """