#!/usr/bin/env python3
"""Agent for implementing envelope encryption using AWS KMS."""

import json
import argparse
import os
import base64

try:
    import boto3
    from botocore.exceptions import ClientError
except ImportError:
    boto3 = None

try:
    from cryptography.hazmat.primitives.ciphers.aead import AESGCM
except ImportError:
    AESGCM = None

NONCE_SIZE = 12


def generate_data_key(kms_key_id, region="us-east-1"):
    """Generate a data encryption key using AWS KMS."""
    kms = boto3.client("kms", region_name=region)
    resp = kms.generate_data_key(KeyId=kms_key_id, KeySpec="AES_256")
    return {
        "plaintext_key": resp["Plaintext"],
        "encrypted_key": resp["CiphertextBlob"],
        "key_id": resp["KeyId"],
    }


def encrypt_data(plaintext_bytes, kms_key_id, region="us-east-1"):
    """Encrypt data using envelope encryption with AWS KMS."""
    key_data = generate_data_key(kms_key_id, region)
    nonce = os.urandom(NONCE_SIZE)
    aesgcm = AESGCM(key_data["plaintext_key"])
    ciphertext = aesgcm.encrypt(nonce, plaintext_bytes, None)

    # Zero out plaintext key from memory
    key_data["plaintext_key"] = b"\x00" * 32

    envelope = {
        "encrypted_data_key": base64.b64encode(key_data["encrypted_key"]).decode(),
        "nonce": base64.b64encode(nonce).decode(),
        "ciphertext": base64.b64encode(ciphertext).decode(),
        "kms_key_id": key_data["key_id"],
        "algorithm": "AES-256-GCM",
        "envelope_version": 1,
    }
    return envelope


def decrypt_data(envelope, region="us-east-1"):
    """Decrypt envelope-encrypted data using AWS KMS."""
    kms = boto3.client("kms", region_name=region)
    encrypted_key = base64.b64decode(envelope["encrypted_data_key"])
    resp = kms.decrypt(CiphertextBlob=encrypted_key)
    plaintext_key = resp["Plaintext"]

    nonce = base64.b64decode(envelope["nonce"])
    ciphertext = base64.b64decode(envelope["ciphertext"])
    aesgcm = AESGCM(plaintext_key)
    plaintext = aesgcm.decrypt(nonce, ciphertext, None)

    # Zero out plaintext key
    plaintext_key = b"\x00" * 32
    return plaintext


def encrypt_file(input_path, output_path, kms_key_id, region="us-east-1"):
    """Encrypt a file using envelope encryption."""
    with open(input_path, "rb") as f:
        plaintext = f.read()
    envelope = encrypt_data(plaintext, kms_key_id, region)
    with open(output_path, "w") as f:
        json.dump(envelope, f, indent=2)
    return {
        "input": str(input_path),
        "output": str(output_path),
        "original_size": len(plaintext),
        "kms_key_id": envelope["kms_key_id"],
    }


def decrypt_file(input_path, output_path, region="us-east-1"):
    """Decrypt an envelope-encrypted file."""
    with open(input_path, "r") as f:
        envelope = json.load(f)
    plaintext = decrypt_data(envelope, region)
    with open(output_path, "wb") as f:
        f.write(plaintext)
    return {"input": str(input_path), "output": str(output_path), "decrypted_size": len(plaintext)}


def list_kms_keys(region="us-east-1"):
    """List available KMS keys."""
    kms = boto3.client("kms", region_name=region)
    paginator = kms.get_paginator("list_keys")
    keys = []
    for page in paginator.paginate():
        for key in page["Keys"]:
            try:
                desc = kms.describe_key(KeyId=key["KeyId"])
                meta = desc["KeyMetadata"]
                keys.append({
                    "key_id": meta["KeyId"],
                    "arn": meta["Arn"],
                    "description": meta.get("Description", ""),
                    "state": meta["KeyState"],
                    "key_usage": meta["KeyUsage"],
                    "origin": meta["Origin"],
                })
            except ClientError:
                keys.append({"key_id": key["KeyId"], "error": "access denied"})
    return {"keys": keys, "total": len(keys)}


def audit_key_policy(key_id, region="us-east-1"):
    """Audit a KMS key's policy for overly permissive access."""
    kms = boto3.client("kms", region_name=region)
    policy = json.loads(kms.get_key_policy(KeyId=key_id, PolicyName="default")["Policy"])
    findings = []
    for stmt in policy.get("Statement", []):
        principal = stmt.get("Principal", {})
        if principal == "*" or principal.get("AWS") == "*":
            findings.append({
                "severity": "HIGH",
                "finding": "Key policy allows access to all AWS principals",
                "statement_id": stmt.get("Sid", "unknown"),
            })
    return {"key_id": key_id, "policy": policy, "findings": findings}


def main():
    if not boto3 or not AESGCM:
        print(json.dumps({"error": "boto3 and cryptography required"}))
        return
    parser = argparse.ArgumentParser(description="AWS KMS Envelope Encryption Agent")
    parser.add_argument("--region", default="us-east-1")
    sub = parser.add_subparsers(dest="command")
    e = sub.add_parser("encrypt", help="Encrypt file with envelope encryption")
    e.add_argument("--input", required=True)
    e.add_argument("--output", required=True)
    e.add_argument("--key-id", required=True, help="KMS key ID or ARN")
    d = sub.add_parser("decrypt", help="Decrypt envelope-encrypted file")
    d.add_argument("--input", required=True)
    d.add_argument("--output", required=True)
    sub.add_parser("list-keys", help="List KMS keys")
    a = sub.add_parser("audit", help="Audit KMS key policy")
    a.add_argument("--key-id", required=True)
    args = parser.parse_args()
    if args.command == "encrypt":
        result = encrypt_file(args.input, args.output, args.key_id, args.region)
    elif args.command == "decrypt":
        result = decrypt_file(args.input, args.output, args.region)
    elif args.command == "list-keys":
        result = list_kms_keys(args.region)
    elif args.command == "audit":
        result = audit_key_policy(args.key_id, args.region)
    else:
        parser.print_help()
        return
    print(json.dumps(result, indent=2, default=str))


if __name__ == "__main__":
    main()
