#!/usr/bin/env python3
"""TLS 1.3 configuration audit agent using ssl and cryptography libraries."""

import json
import sys
import argparse
import ssl
import socket
from datetime import datetime

try:
    from cryptography import x509
except ImportError:
    print("Install: pip install cryptography")
    sys.exit(1)


def check_tls_versions(host, port=443):
    """Check supported TLS versions on a server."""
    results = {}
    protocols = {
        "TLSv1.0": ssl.TLSVersion.TLSv1 if hasattr(ssl.TLSVersion, 'TLSv1') else None,
        "TLSv1.1": ssl.TLSVersion.TLSv1_1 if hasattr(ssl.TLSVersion, 'TLSv1_1') else None,
        "TLSv1.2": ssl.TLSVersion.TLSv1_2,
        "TLSv1.3": ssl.TLSVersion.TLSv1_3,
    }
    for version_name, version in protocols.items():
        if version is None:
            results[version_name] = {"supported": False, "note": "Not available in this Python build"}
            continue
        try:
            ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
            ctx.check_hostname = False
            ctx.verify_mode = ssl.CERT_NONE
            ctx.minimum_version = version
            ctx.maximum_version = version
            with socket.create_connection((host, port), timeout=5) as sock:
                with ctx.wrap_socket(sock, server_hostname=host) as ssock:
                    results[version_name] = {
                        "supported": True,
                        "cipher": ssock.cipher()[0],
                        "severity": "CRITICAL" if "1.0" in version_name or "1.1" in version_name else "INFO",
                    }
        except (ssl.SSLError, ConnectionRefusedError, socket.timeout, OSError):
            results[version_name] = {"supported": False}
    return results


def get_certificate_info(host, port=443):
    """Retrieve and analyze server certificate."""
    ctx = ssl.create_default_context()
    ctx.check_hostname = False
    ctx.verify_mode = ssl.CERT_NONE
    try:
        with socket.create_connection((host, port), timeout=5) as sock:
            with ctx.wrap_socket(sock, server_hostname=host) as ssock:
                der_cert = ssock.getpeercert(binary_form=True)
                cert = x509.load_der_x509_certificate(der_cert)
                days_remaining = (cert.not_valid_after_utc.replace(tzinfo=None) - datetime.utcnow()).days
                return {
                    "subject": cert.subject.rfc4514_string(),
                    "issuer": cert.issuer.rfc4514_string(),
                    "not_after": str(cert.not_valid_after_utc),
                    "days_remaining": days_remaining,
                    "key_size": cert.public_key().key_size,
                    "signature_algorithm": cert.signature_hash_algorithm.name if cert.signature_hash_algorithm else "unknown",
                    "issues": [],
                }
    except Exception as e:
        return {"error": str(e)}


def check_cipher_suites(host, port=443):
    """Check negotiated cipher suites."""
    ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
    ctx.check_hostname = False
    ctx.verify_mode = ssl.CERT_NONE
    try:
        with socket.create_connection((host, port), timeout=5) as sock:
            with ctx.wrap_socket(sock, server_hostname=host) as ssock:
                cipher = ssock.cipher()
                return {
                    "negotiated_cipher": cipher[0],
                    "protocol": cipher[1],
                    "bits": cipher[2],
                    "tls13_ciphers": ["TLS_AES_256_GCM_SHA384", "TLS_AES_128_GCM_SHA256",
                                       "TLS_CHACHA20_POLY1305_SHA256"],
                }
    except Exception as e:
        return {"error": str(e)}


def run_audit(host, port=443):
    """Execute TLS 1.3 configuration audit."""
    print(f"\n{'='*60}")
    print(f"  TLS 1.3 CONFIGURATION AUDIT")
    print(f"  Target: {host}:{port}")
    print(f"  Generated: {datetime.utcnow().isoformat()} UTC")
    print(f"{'='*60}\n")

    versions = check_tls_versions(host, port)
    print(f"--- TLS VERSION SUPPORT ---")
    for ver, info in versions.items():
        status = "SUPPORTED" if info.get("supported") else "NOT SUPPORTED"
        sev = info.get("severity", "")
        print(f"  {ver}: {status} {f'[{sev}]' if sev else ''}")

    cert = get_certificate_info(host, port)
    print(f"\n--- CERTIFICATE ---")
    if "error" not in cert:
        print(f"  Subject: {cert['subject']}")
        print(f"  Issuer: {cert['issuer']}")
        print(f"  Expires: {cert['not_after']} ({cert['days_remaining']} days)")
        print(f"  Key size: {cert['key_size']}")

    cipher = check_cipher_suites(host, port)
    print(f"\n--- CIPHER SUITE ---")
    if "error" not in cipher:
        print(f"  Negotiated: {cipher['negotiated_cipher']}")
        print(f"  Protocol: {cipher['protocol']}")

    return {"versions": versions, "certificate": cert, "cipher": cipher}


def main():
    parser = argparse.ArgumentParser(description="TLS 1.3 Audit Agent")
    parser.add_argument("--host", required=True, help="Target hostname")
    parser.add_argument("--port", type=int, default=443, help="Target port")
    parser.add_argument("--output", help="Save report to JSON file")
    args = parser.parse_args()

    report = run_audit(args.host, args.port)
    if args.output:
        with open(args.output, "w") as f:
            json.dump(report, f, indent=2, default=str)
        print(f"\n[+] Report saved to {args.output}")


if __name__ == "__main__":
    main()
