# TrustedPickle - http://trustedpickle.sourceforge.net/ # Copyright (c) 2003, Gre7g Luterman # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # # * 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. # # * Neither the name of the Gre7g Luterman nor the names of its contributors may # be used to endorse or promote products derived from this software without # specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. """TrustedPickle.py contains a variety of classes and functions used to sign pickle files with private keys. These pickle files contain not only UNENCRYPTED data, they also contain signatures and trust relationships which allow a program to decide whether the data should be trusted BEFORE unpickling. Although a two-part keying system is used to sign the data, the data itself is intentionally unencrypted. This is done for three important reasons: 1. Certain governments have created laws which control the use of strong encryption. Although this program uses a similar technology to verify who authored a file, the data is not encrypted, so that this technology may be exported freely. 2. This project is not "about" data encryption, it is for creating freely- transferrable files whose authorship can be validated before use. 3. By leaving the data unencrypted, the end user may retain control over whether the data is used or not. The user can be informed that the file was not created by a trusted party (such as the original program's author) and given the chance to accept or reject the file before it is unpickled. Using TrustedPickle.py has one other big advantage over blindly using regular pickle files. The Python maintainers stress that it may be possible to maliciously construct a pickle file so that it executes arbitrary code. By only allowing your program to use data signed by yourself or one of your trusted agents, you can reduce the risk of a third party circumventing your program's security measures by creating such data files. Constants available in this module: TRUSTED: indicates data signed with a trusted key TRANSFERRED: indicates data with a trust relationship to a trusted key UNKNOWN: indicates no trust relationship links data to a trusted key REVOKED: indicates data signed with a revoked key Custom exceptions thrown by this module: FileFormatError: unexpected file end or invalid file signature encountered MismatchedKeys: thrown when mismatched public and private keys are used Common classes and functions in this module: PrivateKey: required to sign a file or establish a trust relationship PublicKey: includes a signed name and e-mail address, identifying the author Signature: shows a programmers faith in a document TrustRelationship: includes a truster, a trustee, and a transferrable flag PublicKeyFile: file holding public keys and trust relationships TPickle: signed data file NewKeys(): interactively create a public and private key pair ModuleObject: module wrapper Low-level functions in this module you should not need to call: Hash(): used in signing a string """ import cPickle import getpass import glob import md5 import os import random import re import sets from types import * import zlib # Constant TRUSTED = 0 TRANSFERRED = 1 UNKNOWN = 2 REVOKED = 3 MAGIC_ZIP = "TrPZ" MAGIC_UNZIP = "TrPU" MAGIC_PRIV = "TrPv" MAGIC_PUB = "TrPb" EXT_SEARCH = re.compile(r"(\.[^\.]+)$") # Global # GenSeed5x52 must be set externally if a key pair before key pair generation! GenSeed5x52 = [] # Should be set to a list of five random longs of 52 bits. CompressLevel = 9 class FileFormatError(Exception): pass class MismatchedKeys(Exception): pass def GCD(a, b): "Returns the greatest common denomenator of a & b" if a < b: a, b = b, a while b: a, b = b, a % b return a def Solve(m, Key, n): "Returns (m ** Key) mod n" i, j = m, 1 while Key: if Key & 1: j = (j * i) % n i = (i * i) % n Key >>= 1 return j def ExtEuclid(e, phi): "Returns Key, such that ((Key * e) mod phi) = 1. NOTE: Key may be negative!" x1, Key, y1, y2 = 0L, 1L, 1L, 0L while phi: q = e / phi e, phi = phi, e - (q * phi) x1, Key = Key - (q * x1), x1 y1, y2 = y2 - (q * y1), y1 return Key def TestPrime(n): "Uses Fermat's little theorem to test if n is PROBABLY a prime." a = random.randint(2, n - 1) return Solve(a, n - 1, n) == 1 def Reseed(): "Pick five new random numbers using the old ones as seeds." global GenSeed5x52 for a in range(5): random.seed(GenSeed5x52[a]) GenSeed5x52[a] = long(random.randint(0, 1L << 52)) def PickNums(): "Returns three random numbers for p, q, and Key from GenSeed5x52 bits." # 5 4 3 2 1 # 1098765432109876543210987654321098765432109876543210 # GenSeed5x52[0]: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa # GenSeed5x52[1]: aaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb # GenSeed5x52[2]: bbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccccccccccccc # GenSeed5x52[3]: cccccccccccccccccccccccccccccccccccccccccccccccccccc # GenSeed5x52[4]: ccccccccccccccccccccccccccccccccccccccccccccccccxxxx # p = 1 + 64 a's # q = 1 + 64 b's # Key = 128 c's p = (1L << 64) | (GenSeed5x52[0] << 12) | (GenSeed5x52[1] >> 40) q = (1L << 64) | ((GenSeed5x52[1] & ((1L << 40) - 1)) << 24) | \ (GenSeed5x52[2] >> 28) Key = ((GenSeed5x52[2] & ((1L << 28) - 1)) << 100) | \ (GenSeed5x52[3] << 48) | (GenSeed5x52[4] >> 4) return p, q, Key def WriteN(File, N, Bytes): S = "" for i in range(Bytes): S += chr(N & 0xFF) N >>= 8 File.write(S) def ReadN(File, Bytes): S = File.read(Bytes) if len(S) != Bytes: raise FileFormatError RetVal = 0L for i in range(Bytes): RetVal |= long(ord(S[i])) << (i << 3) return RetVal def WriteS(File, Str): WriteN(File, len(Str), 6) File.write(Str) def ReadS(File): L = ReadN(File, 6) RetVal = File.read(L) if len(RetVal) != L: raise FileFormatError return RetVal def Hash(String): "Returns a 128 bit digest of the string as a long." return long(md5.md5(String).hexdigest(), 16) class PrivateKey: """Holds a private key, used in signing documents and trust relationships. To create a private key, instantiate the object with a filename (and optionally a password to keep the key secure) and then pass it into the GenKeys() function, below. GenKeys() takes care of writing the key to disk. To read in an existing key, instantiate with filename (and password, if one was used in its creation), and call the Read() function. Call the Test() function to verify that the password used was correct. If incorrect, use SetPassword(), Read(), and Test() to try again. The password on an existing private key may be changed by calling SetPassword() and Write(). Make sure the key was decoded correctly by calling Test() BEFORE changing the password!""" TestHash = Hash("Test is a test string used to validate private keys.") def __init__(self, Filename, Password="", Key=None): "Constructor." self.Key, self.Filename, self.Password = Key, Filename, Hash(Password) def SetPassword(self, Password=""): "Set/change a password on the private key file to make it more private." self.Password = Hash(Password) def Write(self): "Write key to file." F = open(self.Filename, "wb") try: F.write(MAGIC_PRIV) WriteN(F, self.Key ^ self.Password, 17) finally: F.close() def Read(self): "Read and decode the key from a file." F = open(self.Filename, "rb") try: if F.read(4) != MAGIC_PRIV: raise FileFormatError self.Key = ReadN(F, 17) ^ self.Password finally: F.close() def Test(self, Public): """Test the private key against the public key to make sure it was decoded correctly.""" Encode = Solve(self.TestHash, self.Key, Public.n) return self.TestHash == Solve(Encode, Public.Key, Public.n) class PublicKey: """PublicKey is a user's public key, name, and address, signed with their private key. Before trusting the name or address portion of this class, be sure to test the signature with the Test() function.""" def __init__(self, n, Key, Name, Address, PrivVal): "Constructor." # DO NOT SAVE A REFERENCE TO THE USER'S PRIVATE KEY!!! self.n, self.Key, self.Name, self.Address = n, Key, Name, Address if isinstance(PrivVal, PrivateKey): H = Hash(self.Name + self.Address) self.Validator = Solve(H, PrivVal.Key, self.n) else: self.Validator = PrivVal def Write(self, File): "Writes PublicKey to the file." WriteN(File, self.n, 17) WriteN(File, self.Key, 16) WriteS(File, self.Name) WriteS(File, self.Address) WriteN(File, self.Validator, 17) def Read(File): "Static method: Returns a PublicKey read from the file." return PublicKey(ReadN(File, 17), ReadN(File, 16), ReadS(File), ReadS(File), ReadN(File, 17)) Read = staticmethod(Read) def Test(self): """Test the Name and Address against the public key to make sure it has not been altered.""" H = Hash(self.Name + self.Address) return H == Solve(self.Validator, self.Key, self.n) class Signature: """Signature is a user's vouchure for the data's integrity. Before trusting this signature, be sure to test it with the Test() function.""" def __init__(self, *Args): "Constructors." if len(Args) == 2: self._Loaded_(*Args) else: self._New_(*Args) def _Loaded_(self, Signer, Validator): self.Signer, self.Validator = Signer, Validator def _New_(self, Data, PublicFile, Private): # DO NOT SAVE A REFERENCE TO THE USER'S PRIVATE KEY!!! if not Private.Test(PublicFile.MyPublicKey()): raise MismatchedKeys self.Signer = PublicFile.Owner self.Validator = Solve(Hash(Data), Private.Key, PublicFile.MyPublicKey().n) def Write(self, File): "Writes PublicKey to the file." WriteN(File, self.Signer, 17) WriteN(File, self.Validator, 17) def Read(File): "Static method: Returns a Signature read from the file." return Signature(ReadN(File, 17), ReadN(File, 17)) Read = staticmethod(Read) def Test(self, Data, Public): "Test the signature." return Hash(Data) == Solve(self.Validator, Public.Key, Public.n) class TrustRelationship: """TrustRelationship tracks a truster who trusts a trustee. The Transferrable flag (Y/N) indicates whether the truster trusts the trustee enough to chain on their own trust relationships. Before trusting the listed relationship, be sure to test the signature with the Test() function.""" def __init__(self, *Args): "Constructors." if len(Args) == 5: self._Loaded_(*Args) else: self._New_(*Args) def _Loaded_(self, Truster, Trustee, n, Transferrable, Validator): self.Truster, self.Trustee, self.n = Truster, Trustee, n self.Transferrable, self.Validator = Transferrable, Validator def _New_(self, Truster, Trustee, PrivVal, Transferrable="N"): # DO NOT SAVE A REFERENCE TO THE USER'S PRIVATE KEY!!! self.Truster, self.Trustee, self.n = Truster.Key, Trustee.Key, Truster.n self.Transferrable = Transferrable H = Hash(hex(Trustee.Key) + Transferrable) if isinstance(PrivVal, PrivateKey): self.Validator = Solve(H, PrivVal.Key, self.n) else: self.Validator = PrivVal if H != Solve(self.Validator, self.Truster, self.n): raise MismatchedKeys def Test(self): """Test the trustee and transferred flag against the truster's public key to make sure it has not been altered.""" H = Hash(hex(self.Trustee) + self.Transferrable) return H == Solve(self.Validator, self.Truster, self.n) def Write(self, File): "Writes TrustRelationship to the file." WriteN(File, self.Truster, 16) WriteN(File, self.Trustee, 16) WriteN(File, self.n, 17) File.write(self.Transferrable) WriteN(File, self.Validator, 17) def Read(File): "Static method: Returns a TrustRelationship read from the file." Truster, Trustee, n = ReadN(File, 16), ReadN(File, 16), ReadN(File, 17) Transferrable, Validator = File.read(1), ReadN(File, 17) return TrustRelationship(Truster, Trustee, n, Transferrable, Validator) Read = staticmethod(Read) def GenKeys(PrivateFilename, Name, Address, PrivatePassword=""): "Generates a private and public key. Saves private. Set GenSeed5x52 first!" while True: # Pick valid-looking values for p, q, and PubKey p, q, PubKey = PickNums() while True: # Reject non-prime p if TestPrime(p): break Reseed() p, x, x = PickNums() while True: # Reject non-prime q if TestPrime(q): break Reseed() x, q, x = PickNums() # Calculate n and phi n = p * q phi = (p - 1) * (q - 1) while True: # Make sure PubKey and phi are mostly prime if (PubKey > 1) and (PubKey < phi) and (GCD(PubKey, phi) == 1): break Reseed() x, x, PubKey = PickNums() # Calculate PrivKey PrivKey = ExtEuclid(PubKey, phi) if PrivKey < 0: PrivKey += phi # Test with a random message M = long(random.randint(0, n)) if Solve(Solve(M, PubKey, n), PrivKey, n) == M: break Private = PrivateKey(PrivateFilename, PrivatePassword, PrivKey) Private.Write() Public = PublicKey(n, PubKey, Name, Address, Private) return Private, Public class PublicKeyFile: """The PublicKeyFile class encapsulates the disk file which maintains a user's public key and all their pertinent trust relationships.""" def __init__(self, Filename, Public=None): "Constructor." self.Filename, self.Owner = Filename, None self.Trusted, self.Revoked = {}, {} if Public: self.Owner, self.Keys = Public.Key, {Public.Key: Public} def MyPublicKey(self): return self.Keys[self.Owner] def Write(self): "Write file to disk." F = open(self.Filename, "wb") try: F.write(MAGIC_PUB) WriteN(F, self.Owner, 16) WriteN(F, len(self.Keys), 2) for i in self.Keys: self.Keys[i].Write(F) WriteN(F, sum(map(lambda x: len(self.Trusted[x]), self.Trusted)), 2) for i in self.Trusted: for j in self.Trusted[i]: self.Trusted[i][j].Write(F) WriteN(F, sum(map(lambda x: len(self.Revoked[x]), self.Revoked)), 2) for i in self.Revoked: for j in self.Revoked[i]: self.Revoked[i][j].Write(F) finally: F.close() def Read(self): "Read file from disk." F = open(self.Filename, "rb") try: if F.read(4) != MAGIC_PUB: raise FileFormatError self.Owner = ReadN(F, 16) self.Keys = {} for i in range(ReadN(F, 2)): P = PublicKey.Read(F) self.Keys[P.Key] = P self.Trusted = {} for i in range(ReadN(F, 2)): T = TrustRelationship.Read(F) self.Trusted[T.Trustee] = self.Trusted.get(T.Trustee, {}) self.Trusted[T.Trustee][T.Truster] = T self.Revoked = {} for i in range(ReadN(F, 2)): T = TrustRelationship.Read(F) self.Revoked[T.Trustee] = self.Revoked.get(T.Trustee, {}) self.Revoked[T.Trustee][T.Truster] = T finally: F.close() def Merge(self, PubFile, Overwrite=False): "Merge a user's public keys and relationships into our key file." # If they didn't give us an object, assume it is a filename. if not isinstance(PubFile, PublicKeyFile): PubFile = PublicKeyFile(PubFile) PubFile.Read() # Copy all of the user's public keys into our file. for i in PubFile.Keys: if Overwrite or not self.Keys.has_key(i): self.Keys[i] = PubFile.Keys[i] # Copy all of the user's trust relationships into our file. for i in PubFile.Trusted: self.Trusted[i] = self.Trusted.get(i, {}) for j in PubFile.Trusted[i]: if Overwrite or not self.Trusted[i].has_key(j): self.Trusted[i][j] = PubFile.Trusted[i][j] # Copy all of the user's trust revokations into our file. for i in PubFile.Revoked: self.Revoked[i] = self.Revoked.get(i, {}) for j in PubFile.Revoked[i]: if Overwrite or not self.Revoked[i].has_key(j): self.Revoked[i][j] = PubFile.Revoked[i][j] def AddTrust(self, PubFilename, Private, Transferrable="N"): "Add a trust relationship with the user in the given public key file." # Read in trustee's public keyfile. P = PublicKeyFile(PubFilename) P.Read() # Put the user in our key list. self.Keys[P.Owner] = P.MyPublicKey() # Create a trust relationship where we trust the user we just loaded. T = TrustRelationship(self.MyPublicKey(), P.MyPublicKey(), Private, Transferrable) # Add the trust relationship to our list. self.Trusted[T.Trustee] = self.Trusted.get(T.Trustee, {}) self.Trusted[T.Trustee][T.Truster] = T # Copy the user's public keys into ours. # Note: This does not make us trust who they trust. It just helps us track # who trusts whom. self.Merge(P) def RevokeTrust(self, Relationship): # Add a trust relationship to our Revoked list and remove it from Trusted." self.Revoked[Relationship.Trustee] = \ self.Revoked.get(Relationship.Trustee, {}) self.Revoked[Relationship.Trustee][Relationship.Truster] = Relationship try: del self.Trusted[Relationship.Trustee][Relationship.Truster] if not len(self.Trusted[Relationship.Trustee]): del self.Trusted[Relationship.Trustee] except KeyError: pass class ModuleObject: "Used to wrap a module so it can be pickled." def __init__(self, Module): "Constructor." self.Name, self.File = Module.__name__, Module.__file__ F = open(self.File, "rb") self.Data = F.read() F.close() def Import(self): "Import the file back in and return it." Ext = EXT_SEARCH.search(self.File).group(1) while True: Filename = "x%d" % random.randint(0,1000000000) L = glob.glob(Filename + Ext) if not L: break F = open(Filename + Ext, "wb") F.write(self.Data) F.close() Module = __import__(Filename) Module.__name__, Module.__file__ = self.Name, self.File os.unlink(Filename + Ext) if Ext == ".py": os.unlink(Filename + Ext + "c") return Module class TPickle: """TPickle is the trusted pickle object. It contains data, signatures, and trust relationships.""" def __init__(self, Filename): "Constructor." self.Filename = Filename self.Compress, self.Data = True, None self.Keys, self.Signatures, self.Trusted = {}, {}, {} def Read(self): "Read the trusted pickle from the file." F = open(self.Filename, "rb") try: Sig = F.read(4) if Sig == MAGIC_ZIP: self.Compress = True self.Data = zlib.decompress(ReadS(F)) elif Sig == MAGIC_UNZIP: self.Compress = False self.Data = ReadS(F) else: raise FileFormatError self.Keys = {} for i in range(ReadN(F, 2)): K = PublicKey.Read(F) self.Keys[K.Key] = K self.Signatures = {} for i in range(ReadN(F, 2)): S = Signature.Read(F) self.Signatures[S.Signer] = S self.Trusted = {} for i in range(ReadN(F, 2)): T = TrustRelationship.Read(F) self.Trusted[T.Trustee] = self.Trusted.get(T.Trustee, {}) self.Trusted[T.Trustee][T.Truster] = T finally: F.close() def Write(self): "Write the trusted pickle out to file." F = open(self.Filename, "wb") try: if self.Compress: F.write(MAGIC_ZIP) WriteS(F, zlib.compress(self.Data)) else: F.write(MAGIC_UNZIP) WriteS(F, self.Data) WriteN(F, len(self.Keys), 2) for i in self.Keys: self.Keys[i].Write(F) WriteN(F, len(self.Signatures), 2) for i in self.Signatures: self.Signatures[i].Write(F) WriteN(F, sum(map(lambda x: len(self.Trusted[x]), self.Trusted)), 2) for i in self.Trusted: for j in self.Trusted[i]: self.Trusted[i][j].Write(F) finally: F.close() def SetCompression(self, Compress=True): self.Compress = Compress def TestSignatures(self, Trusted=[], Revoked=[]): "Test to see if the document has been signed by someone we trust." # Use set objects Trusted, Revoked = sets.Set(Trusted), sets.Set(Revoked) # Was it signed by anyone we trust? AllSigs = sets.Set(filter(lambda x: self.Signatures[x].Test(self.Data, self.Keys[self.Signatures[x].Signer]), self.Signatures)) Sigs = AllSigs - Revoked RetVal = Sigs & Trusted if RetVal: return TRUSTED, map(lambda x: self.Keys[x], RetVal) # Do we have any transferrable trust? Found, Trusters = sets.Set(), Sigs.copy() for i in Trusters: if self.Trusted.has_key(i): for j in self.Trusted[i]: T = self.Trusted[i][j] if T.Test(): Found.add(T.Truster) Found = Trusters = Trusters | Found while Found: Found = sets.Set() for i in Trusters: if self.Trusted.has_key(i): for j in self.Trusted[i]: T = self.Trusted[i][j] if (T.Transferrable == "Y") and (T.Truster not in Trusters): if not T.Test(): continue Found.add(T.Truster) Trusters |= Found RetVal = list((Trusters & Trusted) - Revoked) if RetVal: return TRANSFERRED, map(lambda x: self.Keys[x], RetVal) # Are any of the signers revoked? RetVal = list(AllSigs & Revoked) if RetVal: return REVOKED, map(lambda x: self.Keys[x], RetVal) # Return the signers. return UNKNOWN, map(lambda x: self.Keys[x], Sigs) def Pickle(self, Data): "Construct the pickle from a data object." if type(Data) == ModuleType: Data = ModuleObject(Data) self.Data = cPickle.dumps(Data, -1) def Unpickle(self): "Deconstruct the pickle data into a data object." Data = cPickle.loads(self.Data) if isinstance(Data, ModuleObject): Data = Data.Import() return Data def Sign(self, PublicFile, Private, IncludeTrusts=False): "Sign the document." # Add our signature & key self.Signatures[PublicFile.Owner] = \ Signature(self.Data, PublicFile, Private) self.Keys[PublicFile.Owner] = PublicFile.MyPublicKey() # Include those who trust us? if IncludeTrusts == True: Found, Trusters = sets.Set(), sets.Set([PublicFile.Owner]) for i in Trusters: if PublicFile.Trusted.has_key(i): for j in PublicFile.Trusted[i]: T = PublicFile.Trusted[i][j] Found.add(T.Truster) self.Trusted[T.Trustee] = self.Trusted.get(T.Trustee, {}) self.Trusted[T.Trustee][T.Truster] = T self.Keys[T.Truster] = PublicFile.Keys[T.Truster] Found = Trusters = sets.Set([PublicFile.Owner]) | Found while Found: Found = sets.Set() for i in Trusters: if PublicFile.Trusted.has_key(i): for j in PublicFile.Trusted[i]: T = PublicFile.Trusted[i][j] if (T.Transferrable == "Y") and (T.Truster not in Trusters): Found.add(T.Truster) self.Trusted[T.Trustee] = self.Trusted.get(T.Trustee, {}) self.Trusted[T.Trustee][T.Truster] = T self.Keys[T.Truster] = PublicFile.Keys[T.Truster] Trusters |= Found def NewKeys(): "Interactively generate a new key pair. DO NOT AUTOMATE!" # This routine should not be automated because it relies on the unpredictable # amount of time between inputs to help seed the random number generator. # If you re-seed the random number generator immediately after picking a # random number, then you will end up with 52 bits worth of randomness. # Good key generation requires 256 bits worth of randomness. global GenSeed5x52 random.seed() GenSeed5x52 = [long(random.randint(0, (1L << 52) - 1))] Name = raw_input("Your name: ") random.seed() GenSeed5x52.append(long(random.randint(0, (1L << 52) - 1))) Address = raw_input("Your e-mail address: ") random.seed() GenSeed5x52.append(long(random.randint(0, (1L << 52) - 1))) Base = raw_input("Key pair filename base (___.pub & ___.prv): ") random.seed() GenSeed5x52.append(long(random.randint(0, (1L << 52) - 1))) while True: Pass1 = getpass.getpass("Private key password (blank OK): ") if Pass1 == "": break Pass2 = getpass.getpass("Repeat password (for verification): ") if Pass1 == Pass2: break random.seed() GenSeed5x52.append(long(random.randint(0, (1L << 52) - 1))) Private, Public = GenKeys("%s.prv" % Base, Name, Address, Pass1) KeyFile = PublicKeyFile("%s.pub" % Base, Public) KeyFile.Write()