def test_parse_vaulttext_envelope_crlf(self): b_vaulttext = b"$ANSIBLE_VAULT;9.9;TEST\r\nansible" b_ciphertext, b_version, cipher_name, vault_id = vault.parse_vaulttext_envelope(b_vaulttext) b_lines = b_ciphertext.split(b'\n') self.assertEqual(b_lines[0], b"ansible", msg="Payload was not properly split from the header") self.assertEqual(cipher_name, u'TEST', msg="cipher name was not properly set") self.assertEqual(b_version, b"9.9", msg="version was not properly set")
def test_format_vaulttext_envelope(self): cipher_name = "TEST" b_ciphertext = b"ansible" b_vaulttext = vault.format_vaulttext_envelope(b_ciphertext, cipher_name, version=self.v.b_version, vault_id='default') b_lines = b_vaulttext.split(b'\n') self.assertGreater(len(b_lines), 1, msg="failed to properly add header") b_header = b_lines[0] # self.assertTrue(b_header.endswith(b';TEST'), msg="header does not end with cipher name") b_header_parts = b_header.split(b';') self.assertEqual(len(b_header_parts), 4, msg="header has the wrong number of parts") self.assertEqual(b_header_parts[0], b'$ANSIBLE_VAULT', msg="header does not start with $ANSIBLE_VAULT") self.assertEqual(b_header_parts[1], self.v.b_version, msg="header version is incorrect") self.assertEqual(b_header_parts[2], b'TEST', msg="header does not end with cipher name") # And just to verify, lets parse the results and compare b_ciphertext2, b_version2, cipher_name2, vault_id2 = \ vault.parse_vaulttext_envelope(b_vaulttext) self.assertEqual(b_ciphertext, b_ciphertext2) self.assertEqual(self.v.b_version, b_version2) self.assertEqual(cipher_name, cipher_name2) self.assertEqual('default', vault_id2)
def render_POST(self, request): request.setHeader("Content-Type", "application/json; charset=utf-8") version, cipher, vault_id = '1.1', 'AES256', '' is_source_encrypted = True try: body = json.loads(request.content.read()) except: request.setResponseCode(400) return json.dumps({"value": "bad input object"}).encode('utf-8') if body.get("password"): secret = VaultSecret( to_bytes(body["password"], "utf-8", errors='strict')) source = body.get("source", "") source = to_bytes(source, "utf-8", errors='strict') try: (payload, version, cipher, vault) = parse_vaulttext_envelope(source) except ansible.errors.AnsibleError: # maybe not encrypted is_source_encrypted = False payload = source try: this_cipher = CIPHER_MAPPING[cipher]() except Exception as e: request.setResponseCode(400) response_text = e.message return json.dumps({ "value": "error in %s" % response_text }).encode('utf-8') try: if is_source_encrypted: response_text = this_cipher.decrypt(payload, secret=secret) else: response_text = format_vaulttext_envelope( this_cipher.encrypt(payload, secret=secret), cipher, version, vault_id).strip() except ansible.errors.AnsibleError as e: request.setResponseCode(400) response_text = e.message return json.dumps({"value": response_text}).encode('utf-8') else: request.setResponseCode(400) response_text = b"password not specified" return json.dumps({ "value": response_text.decode('utf-8') }).encode('utf-8')
def _decrypt_if_vault_data(self, b_vault_data, b_file_name=None): '''Decrypt b_vault_data if encrypted and return b_data and the show_content flag''' if not is_encrypted(b_vault_data): show_content = True return b_vault_data, show_content b_ciphertext, b_version, cipher_name, vault_id = parse_vaulttext_envelope(b_vault_data) b_data = self._vault.decrypt(b_vault_data, filename=b_file_name) show_content = False return b_data, show_content
def _decrypt_if_vault_data(self, b_vault_data, b_file_name=None): '''Decrypt b_vault_data if encrypted and return b_data and the show_content flag''' if not is_encrypted(b_vault_data): show_content = True return b_vault_data, show_content b_ciphertext, b_version, cipher_name, vault_id = parse_vaulttext_envelope(b_vault_data) b_data = self._vault.decrypt(b_vault_data, filename=b_file_name) show_content = False return b_data, show_content
def test_non_hex(self): vaulttext_envelope = u'''$ANSIBLE_VAULT;1.1;AES256 3336396J326261303234626463623963633531343539616138316433353830356566396130353436 3562643163366231316662386565383735653432386435610a306664636137376132643732393835 63383038383730306639353234326630666539346233376330303938323639306661313032396437 6233623062366136310a633866373936313238333730653739323461656662303864663666653563 3138''' b_vaulttext_envelope = to_bytes(vaulttext_envelope, errors='strict', encoding='utf-8') b_vaulttext, b_version, cipher_name, vault_id = vault.parse_vaulttext_envelope(b_vaulttext_envelope) self.assertRaisesRegexp(vault.AnsibleVaultFormatError, '.*Vault format unhexlify error.*Non-hexadecimal digit found', vault.parse_vaulttext, b_vaulttext_envelope)
def test(self): vaulttext_envelope = u'''$ANSIBLE_VAULT;1.1;AES256 33363965326261303234626463623963633531343539616138316433353830356566396130353436 3562643163366231316662386565383735653432386435610a306664636137376132643732393835 63383038383730306639353234326630666539346233376330303938323639306661313032396437 6233623062366136310a633866373936313238333730653739323461656662303864663666653563 3138''' b_vaulttext_envelope = to_bytes(vaulttext_envelope, errors='strict', encoding='utf-8') b_vaulttext, b_version, cipher_name, vault_id = vault.parse_vaulttext_envelope(b_vaulttext_envelope) res = vault.parse_vaulttext(b_vaulttext) self.assertIsInstance(res[0], bytes) self.assertIsInstance(res[1], bytes) self.assertIsInstance(res[2], bytes)
def test_non_hex(self): vaulttext_envelope = u'''$ANSIBLE_VAULT;1.1;AES256 3336396J326261303234626463623963633531343539616138316433353830356566396130353436 3562643163366231316662386565383735653432386435610a306664636137376132643732393835 63383038383730306639353234326630666539346233376330303938323639306661313032396437 6233623062366136310a633866373936313238333730653739323461656662303864663666653563 3138''' b_vaulttext_envelope = to_bytes(vaulttext_envelope, errors='strict', encoding='utf-8') b_vaulttext, b_version, cipher_name, vault_id = vault.parse_vaulttext_envelope(b_vaulttext_envelope) self.assertRaisesRegexp(vault.AnsibleVaultFormatError, '.*Vault format unhexlify error.*Non-hexadecimal digit found', vault.parse_vaulttext, b_vaulttext_envelope)
def test(self): vaulttext_envelope = u'''$ANSIBLE_VAULT;1.1;AES256 33363965326261303234626463623963633531343539616138316433353830356566396130353436 3562643163366231316662386565383735653432386435610a306664636137376132643732393835 63383038383730306639353234326630666539346233376330303938323639306661313032396437 6233623062366136310a633866373936313238333730653739323461656662303864663666653563 3138''' b_vaulttext_envelope = to_bytes(vaulttext_envelope, errors='strict', encoding='utf-8') b_vaulttext, b_version, cipher_name, vault_id = vault.parse_vaulttext_envelope(b_vaulttext_envelope) res = vault.parse_vaulttext(b_vaulttext) self.assertIsInstance(res[0], bytes) self.assertIsInstance(res[1], bytes) self.assertIsInstance(res[2], bytes)
def _get_file_contents(self, file_name): ''' Reads the file contents from the given file name If the contents are vault-encrypted, it will decrypt them and return the decrypted data :arg file_name: The name of the file to read. If this is a relative path, it will be expanded relative to the basedir :raises AnsibleFileNotFOund: if the file_name does not refer to a file :raises AnsibleParserError: if we were unable to read the file :return: Returns a byte string of the file contents ''' if not file_name or not isinstance(file_name, (binary_type, text_type)): raise AnsibleParserError("Invalid filename: '%s'" % str(file_name)) b_file_name = to_bytes(self.path_dwim(file_name)) # This is what we really want but have to fix unittests to make it pass # if not os.path.exists(b_file_name) or not os.path.isfile(b_file_name): if not self.path_exists(b_file_name) or not self.is_file(b_file_name): raise AnsibleFileNotFound("Unable to retrieve file contents", file_name=file_name) show_content = True try: with open(b_file_name, 'rb') as f: data = f.read() if is_encrypted(data): # FIXME: plugin vault selector b_ciphertext, b_version, cipher_name, vault_id = parse_vaulttext_envelope( data) data = self._vault.decrypt(data, filename=b_file_name) show_content = False return (data, show_content) except (IOError, OSError) as e: raise AnsibleParserError( "an error occurred while trying to read the file '%s': %s" % (file_name, str(e)), orig_exc=e)
def test_decrypt_non_default_1_2(self): b_expected_plaintext = to_bytes('foo bar\n') vaulttext = '''$ANSIBLE_VAULT;1.2;AES256;ansible_devel 65616435333934613466373335363332373764363365633035303466643439313864663837393234 3330656363343637313962633731333237313636633534630a386264363438363362326132363239 39363166646664346264383934393935653933316263333838386362633534326664646166663736 6462303664383765650a356637643633366663643566353036303162386237336233393065393164 6264''' vault_secrets = self._vault_secrets_from_password('default', 'ansible') v = vault.VaultLib(vault_secrets) b_vaulttext = to_bytes(vaulttext) b_plaintext = v.decrypt(b_vaulttext) self.assertEqual(b_expected_plaintext, b_plaintext) b_ciphertext, b_version, cipher_name, vault_id = vault.parse_vaulttext_envelope(b_vaulttext) self.assertEqual('ansible_devel', vault_id) self.assertEqual(b'1.2', b_version)
def _get_file_contents(self, file_name): ''' Reads the file contents from the given file name If the contents are vault-encrypted, it will decrypt them and return the decrypted data :arg file_name: The name of the file to read. If this is a relative path, it will be expanded relative to the basedir :raises AnsibleFileNotFOund: if the file_name does not refer to a file :raises AnsibleParserError: if we were unable to read the file :return: Returns a byte string of the file contents ''' if not file_name or not isinstance(file_name, (binary_type, text_type)): raise AnsibleParserError("Invalid filename: '%s'" % str(file_name)) b_file_name = to_bytes(self.path_dwim(file_name)) # This is what we really want but have to fix unittests to make it pass # if not os.path.exists(b_file_name) or not os.path.isfile(b_file_name): if not self.path_exists(b_file_name) or not self.is_file(b_file_name): raise AnsibleFileNotFound("Unable to retrieve file contents", file_name=file_name) show_content = True try: with open(b_file_name, 'rb') as f: data = f.read() if is_encrypted(data): # FIXME: plugin vault selector b_ciphertext, b_version, cipher_name, vault_id = parse_vaulttext_envelope(data) data = self._vault.decrypt(data, filename=b_file_name) show_content = False return (data, show_content) except (IOError, OSError) as e: raise AnsibleParserError("an error occurred while trying to read the file '%s': %s" % (file_name, str(e)), orig_exc=e)
def run(self, tmp=None, task_vars=None): if task_vars is None: task_vars = dict() result = super(ActionModule, self).run(tmp, task_vars) del tmp # tmp is deprecated # If user supplies vault-id and vault-pass, use them. Otherwise use those that are automatically loaded with the playbook if 'vaultpass' in self._task.args: oVaultSecret = VaultSecret( self._task.args["vaultpass"].encode('utf-8')) if 'vaultid' in self._task.args: oVaultLib = VaultLib([(self._task.args["vaultid"], oVaultSecret)]) else: display.v(u'No vault-id supplied, using default identity.') oVaultLib = VaultLib([(C.DEFAULT_VAULT_IDENTITY, oVaultSecret) ]) else: display.v( u'No vault-id or vault-pass supplied, using playbook-sourced variables.' ) oVaultLib = self._loader._vault if len(self._loader._vault.secrets) == 0: display.warning( "No Vault secrets loaded by config and none supplied to plugin. Vault operations are not possible." ) if self._task.args["action"] == "encrypt": if "plaintext" not in self._task.args: return { "failed": True, "msg": "'plaintext' is required for encrypt." } b_vaulttext = oVaultLib.encrypt(self._task.args["plaintext"]) b_ciphertext, b_version, cipher_name, vault_id = parse_vaulttext_envelope( b_vaulttext) vaulttext_header = b_vaulttext.decode('utf-8').split('\n', 1)[0] ciphertext = b_ciphertext.decode('utf-8') if 'multiline_out' in self._task.args and self._task.args[ "multiline_out"] == True: multiline_length = 80 ciphertext = '\n'.join([ ciphertext[i:i + multiline_length] for i in range(0, len(ciphertext), multiline_length) ]) result['vaulttext'] = vaulttext_header + "\n" + ciphertext result['plaintext'] = self._task.args["plaintext"] else: if "vaulttext" not in self._task.args: return { "failed": True, "msg": "'vaulttext' is required for decrypt." } plaintext = oVaultLib.decrypt(self._task.args["vaulttext"]) result['vaulttext'] = self._task.args["vaulttext"] result['plaintext'] = plaintext result['failed'] = False return result
from cryptography.hazmat.primitives import hashes, padding from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.ciphers import (Cipher as C_Cipher, algorithms, modes) dir = os.path.dirname(__file__) filename = dir + '/files/OnePointOneStringNoTag.txt' vault_text = open(filename, 'r').read() lib = VaultLib(secrets=[['default', VaultSecret("daniel-ness/ansible-vault")]]) print lib.decrypt(vault_text) plaintext, vault_id, vault_secret = lib.decrypt_and_get_vault_id(vault_text) print plaintext print vault_text b_vaulttext, dummy, cipher_name, vault_id = parse_vaulttext_envelope( vault_text) print b_vaulttext cipher_text, salt, crypted_hmac = parse_vaulttext(b_vaulttext) print("Cipher Text: " + hexlify(cipher_text)) print("Salt: " + hexlify(salt)) print("HMAC: " + crypted_hmac) aes = VaultAES256() key1, key2, iv = aes._gen_key_initctr("daniel-ness/ansible-vault", salt) print("Key1: " + hexlify(key1)) print("Key2: " + hexlify(key2)) print("IV: " + hexlify(iv)) print("pre decrypt:")