def _generate_seed(self, size): rp = RandomPool() for i in range(7): m = SHA.new() tempseed = rp.get_bytes(size) m.update(tempseed) rp.add_event(m.hexdigest()) return rp.get_bytes(size)
class FallbackRandomPool (BaseOSRandomPool): def __init__(self): self._wr = RandomPool() self.randomize() def get_bytes(self, n): return self._wr.get_bytes(n)
def AFSplit(data, stripes, digesttype='sha1'): """AF-Split data using digesttype. Returned data size will be len(data) * stripes""" blockSize = len(data) rand = RandomPool() bufblock = "\x00" * blockSize ret = "" for i in range(0, stripes-1): # Get some random data rand.randomize() rand.stir() r = rand.get_bytes(blockSize) if rand.entropy < 0: print "Warning: RandomPool entropy dropped below 0" ret += r bufblock = _xor(r, bufblock) bufblock = _diffuse(bufblock, blockSize, digesttype) rand.add_event(bufblock) ret += _xor(bufblock, data) return ret
def AFSplit(data, stripes, digesttype='sha1'): """AF-Split data using digesttype. Returned data size will be len(data) * stripes""" blockSize = len(data) rand = RandomPool() bufblock = "\x00" * blockSize ret = "" for i in range(0, stripes - 1): # Get some random data rand.randomize() rand.stir() r = rand.get_bytes(blockSize) if rand.entropy < 0: print "Warning: RandomPool entropy dropped below 0" ret += r bufblock = _xor(r, bufblock) bufblock = _diffuse(bufblock, blockSize, digesttype) rand.add_event(bufblock) ret += _xor(bufblock, data) return ret
class FallbackRandomPool(BaseOSRandomPool): def __init__(self): self._wr = RandomPool() self.randomize() def get_bytes(self, n): return self._wr.get_bytes(n)
def changepassword(self): """ Creates a new key. The key itself is actually stored in the database in crypted form. This key is encrypted using the password that the user provides. This makes it easy to change the password for the database. If oldKeyCrypted is none, then a new password is generated.""" if self._callback == None: raise CryptoNoCallbackException("No call back class has been specified") if self._keycrypted == None: # Generate a new key, 32 bits in length, if that's # too long for the Cipher, _getCipherReal will sort it out random = RandomPool() key = random.get_bytes(32).encode("base64") else: password = self._callback.execute("Please enter your current password") cipher = self._getcipher_real(password, self._algo) plainkey = cipher.decrypt(self._keycrypted.decode("base64")) key = self._retrievedata(plainkey) newpassword1 = self._callback.execute("Please enter your new password") newpassword2 = self._callback.execute("Please enter your new password again") if newpassword1 != newpassword2: raise CryptoPasswordMismatchException("Passwords do not match") newcipher = self._getcipher_real(newpassword1, self._algo) self._keycrypted = newcipher.encrypt(self._preparedata(key, newcipher.block_size)).encode("base64") # we also want to create the cipher if there isn't one already # so this CryptoEngine can be used from now on if self._cipher == None: self._cipher = self._getcipher_real(key.decode("base64"), self._algo) CryptoEngine._timeoutcount = time.time() return self._keycrypted
def encrypt(plain_text, passwd): '''Encrypts C{plain_text} using a key derived from C{passwd}. A key is derived from C{passwd} using L{_derive_key} with a random 8 byte salt. This key is then used to encrypt the following string: <len(plain)> + plain + "keysafe" + <random bytes> Then the salt and the ciphertext is concatenated and the result is base64 encoded before being returned. @type plain_text: string @param plain_text: password @type passwd: string @param passwd: key to use for encryption @rtype: string @return: base64 encoded encryption of C{plain_text} using C{passwd} ''' # TODO: Change the string to be encrypted to # <len(plain)> + <plain> + <sha256(plain)> + <random bytes> # This should be better than using "keysafe" in the string # create the salt, and derive the key rp = RandomPool() salt = rp.get_bytes(8) key = _derive_key(passwd, salt) # create the full plain text string, make sure it matches the block size # for AES pt = plain_text + _KNOWN_STR rnd_pad_len = struct.unpack('B', rp.get_bytes(1))[0] rnd_pad_len += AES.block_size - \ (1 + len(pt) + rnd_pad_len) % AES.block_size rnd_pad = rp.get_bytes(rnd_pad_len) full_plain_text = struct.pack('B', len(plain_text)) + pt + rnd_pad # encrypt, it seems I need to do it twice to get a predictable result cipher_block = AES.new(key, AES.MODE_PGP) cipher_text = cipher_block.encrypt(full_plain_text) cipher_text = cipher_block.encrypt(full_plain_text) return b64encode(salt + cipher_text)
def runTest(self): """Crypto.Util.randpool.RandomPool""" # Import the winrandom module and try to use it from Crypto.Util.randpool import RandomPool sys.stderr.write("SelfTest: You can ignore the RandomPool_DeprecationWarning that follows.\n") rpool = RandomPool() x = rpool.get_bytes(16) y = rpool.get_bytes(16) self.assertNotEqual(x, y) self.assertNotEqual(rpool.entropy, 0) rpool.randomize() rpool.stir('foo') rpool.add_event('foo')
def changepassword(self): """ Creates a new key. The key itself is actually stored in the database in crypted form. This key is encrypted using the password that the user provides. This makes it easy to change the password for the database. If oldKeyCrypted is none, then a new password is generated.""" if (self._callback == None): raise CryptoNoCallbackException( "No call back class has been specified") if (self._keycrypted == None): # Generate a new key, 32 bits in length, if that's # too long for the Cipher, _getCipherReal will sort it out random = RandomPool() key = str(random.get_bytes(32)).encode('base64') else: password = self._callback.getsecret( "Please enter your current password") cipher = self._getcipher_real(password, self._algo) plainkey = cipher.decrypt(str(self._keycrypted).decode('base64')) key = self._retrievedata(plainkey) newpassword1 = self._callback.getsecret( "Please enter your new password") newpassword2 = self._callback.getsecret( "Please enter your new password again") if (newpassword1 != newpassword2): raise CryptoPasswordMismatchException("Passwords do not match") newcipher = self._getcipher_real(newpassword1, self._algo) self._keycrypted = str( newcipher.encrypt(self._preparedata( key, newcipher.block_size))).encode('base64') # we also want to create the cipher if there isn't one already # so this CryptoEngine can be used from now on if (self._cipher == None): self._cipher = self._getcipher_real( str(key).decode('base64'), self._algo) CryptoEngine._timeoutcount = time.time() return self._keycrypted
def set_key(self, keyIndex, password, iterations, checkMinStripes=0): """Sets the key block at keyIndex using password Sets the key block at keyIndex using password, hashed iterations times using PBKDFv2 (RFC2104). This LuksFile must be unlocked by calling open_any_key() before calling this function. checkMinStripes is used to detect basic header manipulation, since the number of stripes for the key is set by create(), before we write a password to disk we make sure the key is not weak because of a small number of stripes. This function will raise an error if the key is already enabled. """ # Some checks if self.masterKey == None: raise LuksError( "A key has not been unlocked. Call open_any_key() first.") if keyIndex < 0 or keyIndex > 7: raise LuksError("keyIndex out of range") key = self.keys[keyIndex] if key.active != self.LUKS_KEY_DISABLED: raise LuksError("Key is active. Delete the key and try again") if checkMinStripes == 0: checkMinStripes = self.KEY_STRIPES if key.stripes < checkMinStripes: raise LuksError( "Key section %i contains too few stripes. Header manipulation?" % keyIndex) key.passwordIterations = iterations # Generate a random salt for this key rand = RandomPool(self.SALT_SIZE) key.passwordSalt = rand.get_bytes(self.SALT_SIZE) # Hash the key using PBKDFv2 pbkdf = PBKDFv2.PBKDFv2() derived_key = pbkdf.makeKey(password, key.passwordSalt, key.passwordIterations, self.keyBytes, self.hashSpec) # Split the key into key.stripes AfKey = AfSplitter.AFSplit(self.masterKey, key.stripes, self.hashSpec) AfKeySize = len(AfKey) if AfKeySize != key.stripes * self.keyBytes: raise LuksError( "ERROR: AFSplit did not return the correct length of key") # Set the key for IV generation self.ivGen.set_key(derived_key) # Encrypt the splitted key using the hashed password AfSectors = int(math.ceil(float(AfKeySize) / self.SECTOR_SIZE)) for sector in range(0, AfSectors): self._encrypt_sector(derived_key, key.keyMaterialOffset + sector, sector, \ AfKey[int(sector * self.SECTOR_SIZE):int((sector + 1) * self.SECTOR_SIZE)]) key.active = self.LUKS_KEY_ENABLED # Reset the key used for to IV generation in data mode self.ivGen.set_key(self.masterKey) self._save_header()
class Random: def __init__(self): from Crypto.Util.randpool import RandomPool self.RandomPool = RandomPool() def getRandomString(self, N): """Returns a N-bit length random string.""" r = self.getRandomNumber(N) return number.long_to_bytes(r) def getRandomNumber(self, N): """Returns an N-bit length random number.""" if self.RandomPool.entropy < 2 * N: self.RandomPool.randomize(4 * N) self.RandomPool.add_event('') self.RandomPool.stir() random = number.getRandomNumber(N, self.RandomPool.get_bytes) self.RandomPool.stir() return random def getPrime(self, N): """Returns a N-bit length prime.""" if self.RandomPool.entropy < 2 * N: self.RandomPool.randomize(4 * N) self.RandomPool.add_event('') self.RandomPool.stir() prime = number.getPrime(N, self.RandomPool.get_bytes) self.RandomPool.stir() return prime def addEvent(self, text): """Adds a bit of random text to the pool as additional entropy. Use caution. The curreny implementation of this function just XORs the text over the entropy, probably giving it bias if we just roll through our messages. I'm not sure. """ self.RandomPool.add_event(text) self.RandomPool.stir() def verifyEntropy(self, N): """Verifies enough entropy is in the RandomPool. If we are close to no entropy, attempt to add some.""" if self.RandomPool.entropy < 2 * N: self.RandomPool.randomize(4 * N) self.RandomPool.add_event('') self.RandomPool.stir() if self.RandomPool.entropy < N: # if the stirring got rid of entropy, seed with more entropy self.verifyEntropy(2 * N) def get_bytes(self, num): """Get num bytes of randomness from the RandomPool.""" return self.RandomPool.get_bytes(num)
def generateRandom(n): atfork() rand = RandomPool() # using seperate instance of RandomPool purposely return rand.get_bytes(n)
def set_key(self, keyIndex, password, iterations, checkMinStripes=0): """Sets the key block at keyIndex using password Sets the key block at keyIndex using password, hashed iterations times using PBKDFv2 (RFC2104). This LuksFile must be unlocked by calling open_any_key() before calling this function. checkMinStripes is used to detect basic header manipulation, since the number of stripes for the key is set by create(), before we write a password to disk we make sure the key is not weak because of a small number of stripes. This function will raise an error if the key is already enabled. """ # Some checks if self.masterKey == None: raise "A key has not been unlocked. Call open_any_key() first." if keyIndex < 0 or keyIndex > 7: raise "keyIndex out of range" key = self.keys[keyIndex] if key.active != self.LUKS_KEY_DISABLED: raise "Key is active. Delete the key and try again" if checkMinStripes == 0: checkMinStripes = self.KEY_STRIPES if key.stripes < checkMinStripes: raise "Key section %i contains too few stripes. Header manipulation?" % keyIndex key.passwordIterations = iterations # Generate a random salt for this key rand = RandomPool(self.SALT_SIZE) key.passwordSalt = rand.get_bytes(self.SALT_SIZE) # Hash the key using PBKDFv2 pbkdf = PBKDFv2.PBKDFv2() derived_key = pbkdf.makeKey(password, key.passwordSalt, key.passwordIterations, self.keyBytes, self.hashSpec) # Split the key into key.stripes AfKey = AfSplitter.AFSplit(self.masterKey, key.stripes, self.hashSpec) AfKeySize = len(AfKey) if AfKeySize != key.stripes * self.keyBytes: raise "ERROR: AFSplit did not return the correct length of key" # Set the key for IV generation self.ivGen.set_key(derived_key) # Encrypt the splitted key using the hashed password AfSectors = int(math.ceil(float(AfKeySize) / self.SECTOR_SIZE)) for sector in range(0, AfSectors): self._encrypt_sector(derived_key, key.keyMaterialOffset + sector, sector, \ AfKey[int(sector*self.SECTOR_SIZE):int((sector+1)*self.SECTOR_SIZE)]) key.active = self.LUKS_KEY_ENABLED # Reset the key used for to IV generation in data mode self.ivGen.set_key(self.masterKey) self._save_header()
def create(self, file, cipherName, cipherMode, hashSpec, masterSize, stripes): """Initializes the file class passed in with the LUKS header Parameters cipherName: aes, cast5, blowfish cipherMode: cbc-plain, cbc-essiv:<hash> hashSpec: sha1, sha256, md5, ripemd160 masterSize: length of the master key in bytes (must match cipher) stripes: number of stripes when Af Splitting keys For compatibility with the Linux kernel dm-crypt, hashSpec must equal "sha1" cbc-plain uses the sector number as the IV. This has a weakness: an attacker may be able to detect the existance of watermarked files. cbc-essiv:<hash> protects against the weakness in cbc-plain, but is slightly slower. The digest size of the hash function passed to cbc-essiv must match the key size of the cipher. aes-cbc-essiv:sha256 works, while aes-cbc-essiv:sha1 does not For more information about the details of the attacks and risk assesment, see http://clemens.endorphin.org/LinuxHDEncSettings """ if self.file != None: raise "This LuksFile has already been initialized" self._check_cipher(cipherName, cipherMode) self.magic = self.LUKS_MAGIC self.version = 1 self.mkDigestIterations = 10 self.keyBytes = masterSize self.hashSpec = hashSpec rand = RandomPool(self.SALT_SIZE + 16 + masterSize) # Generate the salt self.mkDigestSalt = rand.get_bytes(self.SALT_SIZE) # Generate a random master key self.masterKey = rand.get_bytes(self.keyBytes) self.ivGen.set_key(self.masterKey) # generate the master key digest pbkdf = PBKDFv2.PBKDFv2() self.mkDigest = pbkdf.makeKey(self.masterKey, self.mkDigestSalt, self.mkDigestIterations, hashlib.new(self.hashSpec).digest_size, self.hashSpec) # init the key information currentSector = math.ceil(592.0 / self.SECTOR_SIZE) alignSectors = 4096 / self.SECTOR_SIZE blocksPerStripe = math.ceil(float(self.keyBytes * stripes) / self.SECTOR_SIZE) self.keys = [None] * 8 for i in range(0, 8): if currentSector % alignSectors > 0: currentSector += alignSectors - currentSector % alignSectors self.keys[i] = self._key_block() self.keys[i].create(currentSector, stripes, self.LUKS_KEY_DISABLED) currentSector += blocksPerStripe # Set the data offset if currentSector % alignSectors > 0: currentSector += alignSectors - currentSector % alignSectors self.payloadOffset = currentSector # Generate a UUID for this file self._uuidgen(rand) # Create a new file, and save the header into it self.file = file self._save_header() # Write FF into all the key slots FFData = "\xFF" * int(self.SECTOR_SIZE) for i in range(0, 8): self.file.seek(int(self.keys[i].keyMaterialOffset)) for sector in range(0, int(blocksPerStripe)): self.file.write(FFData) # Flush the file to disk try: self.file.flush() os.fsync(self.file.fileno()) except: # We might get an error because self.file.fileno() does not exist on StringIO pass
def create(self, file, cipherName, cipherMode, hashSpec, masterSize, stripes): """Initializes the file class passed in with the LUKS header Parameters cipherName: aes, cast5, blowfish cipherMode: cbc-plain, cbc-essiv:<hash> hashSpec: sha1, sha256, md5, ripemd160 masterSize: length of the master key in bytes (must match cipher) stripes: number of stripes when Af Splitting keys For compatibility with the Linux kernel dm-crypt, hashSpec must equal "sha1" cbc-plain uses the sector number as the IV. This has a weakness: an attacker may be able to detect the existance of watermarked files. cbc-essiv:<hash> protects against the weakness in cbc-plain, but is slightly slower. The digest size of the hash function passed to cbc-essiv must match the key size of the cipher. aes-cbc-essiv:sha256 works, while aes-cbc-essiv:sha1 does not For more information about the details of the attacks and risk assesment, see http://clemens.endorphin.org/LinuxHDEncSettings """ if self.file != None: raise LuksError("This LuksFile has already been initialized") self._check_cipher(cipherName, cipherMode) self.magic = self.LUKS_MAGIC self.version = 1 self.mkDigestIterations = 10 self.keyBytes = masterSize self.hashSpec = hashSpec rand = RandomPool(self.SALT_SIZE + 16 + masterSize) # Generate the salt self.mkDigestSalt = rand.get_bytes(self.SALT_SIZE) # Generate a random master key self.masterKey = rand.get_bytes(self.keyBytes) self.ivGen.set_key(self.masterKey) # generate the master key digest pbkdf = PBKDFv2.PBKDFv2() self.mkDigest = pbkdf.makeKey(self.masterKey, self.mkDigestSalt, self.mkDigestIterations, hashlib.new(self.hashSpec).digest_size, self.hashSpec) # init the key information currentSector = math.ceil(592.0 / self.SECTOR_SIZE) alignSectors = 4096 / self.SECTOR_SIZE blocksPerStripe = math.ceil( float(self.keyBytes * stripes) / self.SECTOR_SIZE) self.keys = [None] * 8 for i in range(0, 8): if currentSector % alignSectors > 0: currentSector += alignSectors - currentSector % alignSectors self.keys[i] = self._key_block() self.keys[i].create(currentSector, stripes, self.LUKS_KEY_DISABLED) currentSector += blocksPerStripe # Set the data offset if currentSector % alignSectors > 0: currentSector += alignSectors - currentSector % alignSectors self.payloadOffset = currentSector # Generate a UUID for this file self._uuidgen(rand) # Create a new file, and save the header into it self.file = file self._save_header() # Write FF into all the key slots FFData = "\xFF" * int(self.SECTOR_SIZE) for i in range(0, 8): self.file.seek(int(self.keys[i].keyMaterialOffset)) for sector in range(0, int(blocksPerStripe)): self.file.write(FFData) # Flush the file to disk try: self.file.flush() os.fsync(self.file.fileno()) except: # We might get an error because self.file.fileno() does not exist on StringIO pass