def test_pseudo_bytes(self): with self.assertRaises(MemoryError): Rand.rand_pseudo_bytes(-1) self.assertEqual(Rand.rand_pseudo_bytes(0), ('', 1)) a, b = Rand.rand_pseudo_bytes(1) self.assertEqual(len(a), 1) self.assertEqual(b, 1)
def test_pseudo_bytes(self): with self.assertRaises(MemoryError): Rand.rand_pseudo_bytes(-1) self.assertEqual(Rand.rand_pseudo_bytes(0), (b'', 1)) a, b = Rand.rand_pseudo_bytes(1) self.assertEqual(len(a), 1) self.assertEqual(b, 1)
def test_pseudo_bytes(self): with warnings.catch_warnings(): warnings.simplefilter('ignore', DeprecationWarning) with self.assertRaises(MemoryError): Rand.rand_pseudo_bytes(-1) self.assertEqual(Rand.rand_pseudo_bytes(0), (b'', 1)) a, b = Rand.rand_pseudo_bytes(1) self.assertEqual(len(a), 1) self.assertEqual(b, 1)
def __init__(self, source, destination, private_key, pub_key_cache): self.private_key = private_key self.cipher = rdfvalue.CipherProperties( name=self.cipher_name, key=Rand.rand_pseudo_bytes(self.key_size / 8)[0], iv=Rand.rand_pseudo_bytes(self.iv_size / 8)[0], hmac_key=Rand.rand_pseudo_bytes(self.key_size / 8)[0], ) self.pub_key_cache = pub_key_cache serialized_cipher = self.cipher.SerializeToString() self.cipher_metadata = rdfvalue.CipherMetadata() # Old clients interpret this as a string so we have to omit the "aff4:/" # prefix on the wire. Can be removed after all clients have been updated. self.cipher_metadata.SetWireFormat("source", utils.SmartStr(source.Basename())) # Sign this cipher. digest = self.hash_function(serialized_cipher).digest() # We never want to have a password dialog private_key = self.private_key.GetPrivateKey() self.cipher_metadata.signature = private_key.sign( digest, self.hash_function_name) # Now encrypt the cipher with our key rsa_key = pub_key_cache.GetRSAPublicKey(destination) stats.STATS.IncrementCounter("grr_rsa_operations") # M2Crypto verifies the key on each public_encrypt call which is horribly # slow therefore we just call the swig wrapped method directly. self.encrypted_cipher = m2.rsa_public_encrypt(rsa_key.rsa, serialized_cipher, self.e_padding) # Encrypt the metadata block symmetrically. _, self.encrypted_cipher_metadata = self.Encrypt( self.cipher_metadata.SerializeToString(), self.cipher.iv) self.signature_verified = True
def __init__(self, source, destination, private_key, pub_key_cache): self.private_key = private_key self.cipher = rdfvalue.CipherProperties( name=self.cipher_name, key=Rand.rand_pseudo_bytes(self.key_size / 8)[0], iv=Rand.rand_pseudo_bytes(self.iv_size / 8)[0], hmac_key=Rand.rand_pseudo_bytes(self.key_size / 8)[0], ) self.pub_key_cache = pub_key_cache serialized_cipher = self.cipher.SerializeToString() self.cipher_metadata = rdfvalue.CipherMetadata() # Old clients interpret this as a string so we have to omit the "aff4:/" # prefix on the wire. Can be removed after all clients have been updated. self.cipher_metadata.SetWireFormat("source", utils.SmartStr(source.Basename())) # Sign this cipher. digest = self.hash_function(serialized_cipher).digest() # We never want to have a password dialog private_key = self.private_key.GetPrivateKey() self.cipher_metadata.signature = private_key.sign( digest, self.hash_function_name) # Now encrypt the cipher with our key rsa_key = pub_key_cache.GetRSAPublicKey(destination) stats.STATS.IncrementCounter("grr_rsa_operations") # M2Crypto verifies the key on each public_encrypt call which is horribly # slow therefore we just call the swig wrapped method directly. self.encrypted_cipher = m2.rsa_public_encrypt( rsa_key.rsa, serialized_cipher, self.e_padding) # Encrypt the metadata block symmetrically. _, self.encrypted_cipher_metadata = self.Encrypt( self.cipher_metadata.SerializeToString(), self.cipher.iv) self.signature_verified = True
def Encrypt(self, data, iv=None): """Symmetrically encrypt the data using the optional iv.""" if iv is None: iv = Rand.rand_pseudo_bytes(self.iv_size / 8)[0] evp_cipher = EVP.Cipher(alg=self.cipher_name, key=self.cipher.key, iv=iv, op=ENCRYPT) ctext = evp_cipher.update(data) ctext += evp_cipher.final() return iv, ctext
def test_pseudo_bytes(self): self.assertRaises(MemoryError, Rand.rand_pseudo_bytes, -1) assert Rand.rand_pseudo_bytes(0) == ("", 1) a, b = Rand.rand_pseudo_bytes(1) assert len(a) == 1 assert b == 1
def test_pseudo_bytes(self): self.assertRaises(MemoryError, Rand.rand_pseudo_bytes, -1) assert Rand.rand_pseudo_bytes(0) == ('', 1) a, b = Rand.rand_pseudo_bytes(1) assert len(a) == 1 assert b == 1
def main(): q = LockboxOrderedDict() observer = Observer() observer.schedule(CleartextEventHandler(q, cleartext_dir, ciphertext_dir), path=cleartext_dir, recursive=True) observer.schedule(CiphertextEventHandler(q, cleartext_dir, ciphertext_dir), path=ciphertext_dir, recursive=True) observer.start() try: while True: time.sleep(1) try: while True: (stem, deets) = q.first() #just read, don't pop here because we need it to stay in the OrderedDict in case another event comes in on this file before we're done logger.debug('%s %s', stem, deets) if not deets['collided']: check_for_collided_after_operation = False if deets['action'] == 'encrypt': logger.debug('encrypting from %s to %s', deets['cleartext_full_path'], deets['ciphertext_full_path']) check_for_collided_after_operation = True target_path = deets['ciphertext_full_path'] (tmp_fd, tmp_path) = tempfile.mkstemp() os.close(tmp_fd) rio = BIO.openfile(deets['cleartext_full_path'], 'rb') rio.write_close() wrio = BIO.openfile(tmp_path, 'wb') cf = BIO.CipherStream(wrio) salt = Rand.rand_pseudo_bytes(8)[0] logger.debug("salt=%s",ByteToHex(salt)) passphrase_f = open(os.path.join(deets['config_dir_path'], 'key.txt'), 'r') ## TODO cache this passphrase = passphrase_f.readline().rstrip() passphrase_f.close() (key, iv) = m2.bytes_to_key(m2.aes_256_cbc(), m2.md5(), passphrase, salt, 1) logger.debug("key=%s", ByteToHex(key)) logger.debug("iv=%s", ByteToHex(iv)) cf.set_cipher('aes_256_cbc', key, iv, 1) wrio.write('Salted__') # magic - see openssl's enc.c wrio.write(salt) while True: out = rio.read(4096) if not out: break cf.write(out) cf.flush() cf.write_close() cf.close() wrio.flush() wrio.write_close() wrio.close() elif deets['action'] == 'decrypt': logger.debug('decrypting from %s to %s', deets['ciphertext_full_path'], deets['cleartext_full_path']) check_for_collided_after_operation = True target_path = deets['cleartext_full_path'] (tmp_fd, tmp_path) = tempfile.mkstemp() os.close(tmp_fd) ## TODO: handle failure of decryption rio = BIO.openfile(deets['ciphertext_full_path'], 'rb') rio.write_close() wrio = BIO.openfile(deets['cleartext_full_path'], 'wb') cf = BIO.CipherStream(wrio) header = rio.read(size=len('Salted__')) if header != 'Salted__': print "uh, this doesn't look like an encrypted file" exit() salt = rio.read(size=8) logger.debug("salt=%s",ByteToHex(salt)) passphrase_f = open(os.path.join(deets['config_dir_path'], 'key.txt'), 'r') ## TODO cache this passphrase = passphrase_f.readline().rstrip() passphrase_f.close() (key, iv) = m2.bytes_to_key(m2.aes_256_cbc(), m2.md5(), passphrase, salt, 1) logger.debug("key=%s", ByteToHex(key)) logger.debug("iv=%s", ByteToHex(iv)) cf.set_cipher('aes_256_cbc', key, iv, 0) while True: out = rio.read(4096) if not out: break cf.write(out) cf.flush() cf.write_close() cf.close() wrio.flush() wrio.write_close() wrio.close() elif deets['action'] == 'delete-cipher': logger.debug('deleting %s', deets['ciphertext_full_path']) if os.path.exists(deets['ciphertext_full_path']): os.unlink(deets['ciphertext_full_path']) elif deets['action'] == 'delete-clear': logger.debug('deleting %s', deets['cleartext_full_path']) if os.path.exists(deets['cleartext_full_path']): os.unlink(deets['cleartext_full_path']) else: logger.error('Unknown action %s on %s', deets['action'], deets) time.sleep(0) ## http://stackoverflow.com/questions/787803/how-does-a-threading-thread-yield-the-rest-of-its-quantum-in-python (stem, deets) = q.popitem(False) if check_for_collided_after_operation: ## now, if it got marked as collided in the interim while we were encrypting/decrypting then we don't want to overwrite if not deets['collided']: ## move from temp location to real location if platform.system() == 'Windows': ## TODO: this can throw an exception on windows if the target_place is locked. need to think about how to deal with this if os.path.exists(target_path): logger.debug('windows only: unlink %s', target_path) os.unlink(target_path) logger.debug('rename %s to %s', tmp_path, target_path) mkdir_p(os.path.dirname(target_path)) os.rename(tmp_path, target_path) else: ## TODO: if it was a decrypt, move the newly decrypted file to stem-from-dropbox-collision ## if it was an encrypt, immediately decrypt ciphertext version to stem-from-dropbox-collision ## TODO: delete tmp_path in the case that we're no longer using it (it was an encrypt that we no longer can copy to the ciphertext location) pass else: (stem, deets) = q.popitem(False) if deets['action'] in ('delete-clear','delete-cipher'): logger.error('should not get a collided delete because they should be removed from the queue in our OrderedDict subclass') else: ## TODO: immediately decrypt and put it into stem-from-dropbox-collision pass except StopIteration: #print "nada" pass except KeyboardInterrupt: pass observer.stop() observer.join()
hexStr = ''.join( hexStr.split(" ") ) for i in range(0, len(hexStr), 2): bytes.append( chr( int (hexStr[i:i+2], 16 ) ) ) return ''.join( bytes ) rio = BIO.openfile(os.path.expanduser('~/Lockbox/foobar/firsttest.txt'), 'rb') rio.write_close() wrio = BIO.openfile(os.path.expanduser('~/Dropbox/LOCKBOX-foobar/firsttest.txt'), 'wb') cf = BIO.CipherStream(wrio) salt = Rand.rand_pseudo_bytes(8)[0] #salt = '\x00\x00\x00\x00\x00\x00\x00\x00' print "salt=%s" % ByteToHex(salt) passphrase = 'foo bar' (key, iv) = m2.bytes_to_key(m2.aes_256_cbc(), m2.md5(), passphrase, salt, 1) print "key=%s" % ByteToHex(key) print "iv=%s" % ByteToHex(iv) cf.set_cipher('aes_256_cbc', key, iv, 1) wrio.write('Salted__') # magic - see openssl's enc.c wrio.write(salt)