Skip to content

Instantly share code, notes, and snippets.

@kgaughan
Created May 3, 2022 19:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kgaughan/9b7b318600f0c860771a4de55d1286d3 to your computer and use it in GitHub Desktop.
Save kgaughan/9b7b318600f0c860771a4de55d1286d3 to your computer and use it in GitHub Desktop.
Decrypting ansible-vault files without ansible-vault
import binascii
import sys
import typing
from cryptography.hazmat.primitives import ciphers, hashes, hmac, padding
from cryptography.hazmat.primitives.ciphers import algorithms
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
def vault_parse(lines: typing.List[str]) -> typing.Tuple[bytes, bytes, bytes]:
parts = binascii.unhexlify("".join(lines[1:])).decode("ascii").split("\n")
return (
binascii.unhexlify(parts[0]), # salt
binascii.unhexlify(parts[1]), # encrypted HMAC
binascii.unhexlify(parts[2]), # ciphertext
)
def vault_create_derived_key(
passwd: bytes,
salt: bytes,
key_length: int,
iv_length: int,
) -> bytes:
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=2 * key_length + iv_length,
salt=salt,
iterations=10000,
)
return kdf.derive(passwd)
def vault_initialise_key_init_iv(
passwd: bytes,
salt: bytes,
) -> typing.Tuple[bytes, bytes, bytes]:
key_length = 32 # AES256
iv_length = algorithms.AES.block_size // 8
derived_key = vault_create_derived_key(passwd, salt, key_length, iv_length)
iv = derived_key[(key_length * 2) : (key_length * 2) + iv_length]
key1 = derived_key[:key_length]
key2 = derived_key[key_length : (key_length * 2)]
return key1, key2, iv
def vault_decrypt(contents: typing.List[str], passwd: bytes) -> bytes:
"""
Decrypts your vault token found at the parameter passed in.
The token is decrypted with ansible-vault.
Returns the unencrypted github token.
"""
# This is very brute force
if len(contents) == 0 or contents[0] != "$ANSIBLE_VAULT;1.1;AES256":
sys.exit("Cannot parse vault")
# This is the reverse of the algorithm given at
# https://docs.ansible.com/ansible/latest/user_guide/vault.html
salt, encrypted_hmac, ciphertext = vault_parse(contents)
key1, key2, iv = vault_initialise_key_init_iv(passwd, salt)
# Validate ciphertext against the HMAC
hmac_o = hmac.HMAC(key2, hashes.SHA256())
hmac_o.update(ciphertext)
hmac_o.verify(encrypted_hmac)
# Decrypt the ciphertext using AES256+CTR
cipher = ciphers.Cipher(
ciphers.algorithms.AES(key1),
ciphers.modes.CTR(iv),
)
decryptor = cipher.decryptor()
unpadder = padding.PKCS7(128).unpadder()
return (
unpadder.update(decryptor.update(ciphertext) + decryptor.finalize())
+ unpadder.finalize()
)
@kgaughan
Copy link
Author

kgaughan commented May 3, 2022

Note that this isn't entirely my work, and it's a bit rough and ready. It's derived from the source of Ansible itself, so should be considered subject to the same license.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment