aegisvault 0.3.80

Convert otpauth URI file to Encrypted Aegis vault JSON on stdout
Documentation
#!/usr/bin/env python3

# this depends on the 'cryptography' package
# pip install cryptography

# example usage: ./scripts/decrypt.py --input ./app/src/test/resources/com/beemdevelopment/aegis/importers/aegis_encrypted.json
# password: test

import argparse
import base64
import getpass
import io
import json
import sys

from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.hazmat.primitives.kdf.scrypt import Scrypt
from cryptography.hazmat.backends import default_backend
import cryptography
backend = default_backend()

def die(msg, code=1):
    print(msg, file=sys.stderr)
    exit(code)

def main():
    parser = argparse.ArgumentParser(description="Decrypt an Aegis vault")
    parser.add_argument("--input", dest="input", required=True, help="encrypted Aegis vault file")
    parser.add_argument("--output", dest="output", default="-", help="output file ('-' for stdout)")
    args = parser.parse_args()

    # parse the Aegis vault file
    with io.open(args.input, "r") as f:
        data = json.load(f)

    # ask the user for a password
    password = getpass.getpass().encode("utf-8")

    # extract all password slots from the header
    header = data["header"]
    slots = [slot for slot in header["slots"] if slot["type"] == 1]

    # try the given password on every slot until one succeeds
    master_key = None
    for slot in slots:
        # derive a key from the given password
        kdf = Scrypt(
            salt=bytes.fromhex(slot["salt"]),
            length=32,
            n=slot["n"],
            r=slot["r"],
            p=slot["p"],
            backend=backend
        )
        key = kdf.derive(password)

        # try to use the derived key to decrypt the master key
        cipher = AESGCM(key)
        params = slot["key_params"]
        try:
            master_key = cipher.decrypt(
                nonce=bytes.fromhex(params["nonce"]),
                data=bytes.fromhex(slot["key"]) + bytes.fromhex(params["tag"]),
                associated_data=None
            )
            break
        except cryptography.exceptions.InvalidTag:
            pass

    if master_key is None:
        die("error: unable to decrypt the master key with the given password")

    # decode the base64 vault contents
    content = base64.b64decode(data["db"])

    # decrypt the vault contents using the master key
    params = header["params"]
    cipher = AESGCM(master_key)
    db = cipher.decrypt(
        nonce=bytes.fromhex(params["nonce"]),
        data=content + bytes.fromhex(params["tag"]),
        associated_data=None
    )

    db = db.decode("utf-8")
    if args.output != "-":
        with io.open(args.output, "w") as f:
            f.write(db)
    else:
        print(db)

if __name__ == "__main__":
    main()