def test_open_with_wrong_associated_data(self): """Ensure 'open' raises IntegrityError if wrong associated data is given""" bad_ad = [b"INVALID"] for ex in SIVExample.load(): siv = SIV(ex.key) with self.assertRaises(IntegrityError): siv.open(ex.ciphertext, bad_ad)
def wrap_key_owner(passphrase, data_key): """ data_key must be encoded Using argon2.low_level function, which is technically a hazmat function, but it doesn't appear to have any dangerous problems. We can't use the argon2.PasswordHasher() functions because they compress the hash and metadata using Blake2b and you can't extract the raw bytes of the hash or salt, which we need for the SIV. """ #derive key using passphrase and salt salt = os.urandom(16) derived_key = argon2.low_level.hash_secret_raw( passphrase.encode(), salt, time_cost=1, memory_cost=8, parallelism=1, hash_len=32, type=argon2.low_level.Type.I) # wrap the data key using the derived key siv = SIV(derived_key) ciphertext = siv.seal(data_key) # add salt to the start of ciphertext ciphertext = salt + ciphertext return ciphertext
def decrypt(self: object, passphrase: bytes) -> None: with open(self.in_path, 'rb') as in_file: salt = in_file.read(self.SALT_SIZE) key = self._derive_key(passphrase, salt) siv = SIV(key) nonce = in_file.read(self.NONCE_SIZE) plaintext = siv.open(in_file.read(), [nonce]) self._extract(plaintext)
def test_open_with_wrong_key(self): """Ensure 'open' raises IntegrityError if wrong key is given""" bad_key = b"\x01" * 32 siv = SIV(bad_key) for ex in SIVExample.load(): with self.assertRaises(IntegrityError): siv.open(ex.ciphertext, ex.ad)
def encrypt(self: object) -> [bytes, str]: passphrase = self._gen_xkcd_phrase() salt = os.urandom(self.SALT_SIZE) key = self._derive_key(passphrase.encode('utf-8'), salt) siv = SIV(key) nonce = os.urandom(self.NONCE_SIZE) ciphertext = salt + nonce + siv.seal(self._archive(), [nonce]) return ciphertext, passphrase
def dec_file(filepath, data_key): siv = SIV(data_key) with open(filepath, 'rb') as myfile: ciphertext = myfile.read() # first 16 bytes are the nonce nonce = ciphertext[:16] plaintext = siv.open(ciphertext[16:], [nonce]) return plaintext.decode() return None
def enc_file(filepath): key = SIV.generate_key() siv = SIV(key) nonce = os.urandom(16) with open(filepath, 'r') as myfile: data = myfile.read().encode() ciphertext = siv.seal(data, [nonce]) return (key, nonce, nonce + ciphertext) #returning ciphertext with the nonce (first 16 bytes) return None
def setup(self): password = self.ask_password() # Generate Salt if not self.config["salt"]: salt = urandom(SALT_SIZE) self.config["salt"] = binascii.hexlify(salt) # Derive key from password and initialize SIV key = self.derive_key(password) self.engine = SIV(key)
def dec_file(self): """ Decrypt file and return plaintext """ with open(self.input_file, 'rb') as f: data = f.read() siv = SIV(self.k) nonce = data[:16] pt = siv.open(data[16:], [nonce]) return pt.decode()
def encryption_machine(msg): encrypt = [] key = SIV.generate_key() siv = SIV(key) nonce = os.urandom(16) # create a random nonce ciphertext = siv.seal(msg, [nonce]) # msg is in byte encrypt.append(ciphertext) encrypt.append(nonce) encrypt.append(key) return encrypt # we create a list with the nonce, the key and the ciphertext to be able to decrypt it later
class AES(Encryption): default_options = {"salt": None} pubkey = None def setup(self): password = self.ask_password() # Generate Salt if not self.config["salt"]: salt = urandom(SALT_SIZE) self.config["salt"] = binascii.hexlify(salt) # Derive key from password and initialize SIV key = self.derive_key(password) self.engine = SIV(key) def derive_key(self, password): salt = binascii.unhexlify(self.config["salt"]) pw_bytes = bytes(password, "utf-8") dk = hash_secret_raw( pw_bytes, salt, time_cost=ARGON2_TIME_COST, memory_cost=ARGON2_MEMORY_COST, parallelism=ARGON2_PARALLELISM, hash_len=ARGON2_HASH_LEN, type=ARGON2_TYPE, ) return dk def encrypt(self, data): nonce = urandom(NONCE_SIZE) if not isinstance(data, bytes): data = bytes(data, "utf-8") sealed_data = self.engine.seal(data, [nonce]) return (sealed_data, nonce) def decrypt(self, payload): data = self.engine.open(payload[0], [payload[1]]) return data
def _gen_key(): """ Generate key, initialize SIV object, and return relevant data """ k = SIV.generate_key() siv = SIV(k) ret = { "k": k, "siv": siv, "nonce": os.urandom(16) } return ret
def unwrap_key_owner(passphrase, wrapped_key_ciphertext): #derive key using passphrase and salt salt = wrapped_key_ciphertext[:16] derived_key = argon2.low_level.hash_secret_raw( passphrase.encode(), salt, time_cost=1, memory_cost=8, parallelism=1, hash_len=32, type=argon2.low_level.Type.I) # unwrap the data key using the derived key siv = SIV(derived_key) data_key = siv.open(wrapped_key_ciphertext[16:]) return data_key
def main(): allocate_kms_key() allocate_ddb_table() #Allocate encryption keys, store encrypted rows plaintext_dek = allocate_dek() siv = SIV(plaintext_dek) for item_key in valid_partition_keys: p_key = encrypt_pk(siv, item_key) response = dynamodb.put_item( TableName=ddb_table_name, Item={ 'PK': { 'B': p_key, }, 'PK_plain': { "S": str(item_key) } } ) if response: print("Wrote item with encrypted PK '{}', with plaintext {}".format(p_key, str(item_key))) #Re-build the SIV module from a fresk DEK to prove the key is deterministic. plaintext_dek = allocate_dek() siv = SIV(plaintext_dek) for item_key in valid_partition_keys: p_key = encrypt_pk(siv, item_key) response = dynamodb.get_item( TableName=ddb_table_name, Key={ 'PK': { 'B': p_key, } } ) if response: print("Read item with plaintext PK {}, with decrypted plaintext PK {}. Both should match.".format(response['Item']['PK_plain']['S'], decrypt_pk(siv, response['Item']['PK']['B'])))
def test_open(self): """Ensure the 'open' passes all AES-SIV test vectors""" for ex in PMACSIVExample.load(): siv = SIV(ex.key, PMAC) plaintext = siv.open(ex.ciphertext, ex.ad) self.assertEqual(plaintext, ex.plaintext)
def test_seal(self): """Ensure the 'seal' method passes all AES-SIV test vectors""" for ex in PMACSIVExample.load(): siv = SIV(ex.key, PMAC) ciphertext = siv.seal(ex.plaintext, ex.ad) self.assertEqual(ciphertext, ex.ciphertext)
def test_generate_key(self): """Ensure we can generate random keys with the right default size""" key = SIV.generate_key() self.assertEqual(len(key), 32)
def dec_str(ciphertext, data_key): siv = SIV(data_key) nonce = ciphertext[:16] plaintext = siv.open(ciphertext[16:], [nonce]) return plaintext.decode()
def decryption_machine(encrypt): siv = SIV(encrypt[2]) plaintext = siv.open(encrypt[0], [encrypt[1]]) return plaintext