#!/usr/bin/env python3
"""
Two-endpoint HTTPS test server for the Android theme-color / DANGEROUS-tint POC.

  port 18443 — "attacker.test" — serves poc.html with a *trusted* cert
  port 19443 — "badcert.test"  — serves bad.js   with an *untrusted* cert

The "good" cert is made trustworthy to Chrome via
--ignore-certificate-errors-spki-list=<printed-hash>; the "bad" cert is not in
that list, so loading from it still produces a certificate error (which the
user allow-lists once by tapping "Proceed").
"""

import base64
import hashlib
import http.server
import os
import ssl
import subprocess
import sys
import threading

ROOT = os.path.dirname(os.path.abspath(__file__))
GOOD_KEY,  GOOD_CRT  = os.path.join(ROOT, "good.key"),  os.path.join(ROOT, "good.crt")
BAD_KEY,   BAD_CRT   = os.path.join(ROOT, "bad.key"),   os.path.join(ROOT, "bad.crt")


def gen_cert(key, crt, cn):
    if os.path.exists(key) and os.path.exists(crt):
        return
    subprocess.check_call([
        "openssl", "req", "-x509", "-newkey", "rsa:2048", "-nodes",
        "-keyout", key, "-out", crt, "-days", "3650",
        "-subj", f"/CN={cn}",
        "-addext", f"subjectAltName=DNS:{cn}",
    ])


def spki_b64(crt):
    der = subprocess.check_output(
        ["openssl", "x509", "-in", crt, "-pubkey", "-noout"])
    der = subprocess.check_output(
        ["openssl", "pkey", "-pubin", "-outform", "der"], input=der)
    return base64.b64encode(hashlib.sha256(der).digest()).decode()


class Handler(http.server.SimpleHTTPRequestHandler):
    def __init__(self, *a, **kw):
        super().__init__(*a, directory=ROOT, **kw)

    def end_headers(self):
        # Ensure bad.js is treated as active script content.
        if self.path.endswith(".js"):
            self.send_header("Content-Type", "application/javascript")
        self.send_header("Cache-Control", "no-store")
        super().end_headers()


def serve(port, certfile, keyfile, label):
    httpd = http.server.ThreadingHTTPServer(("0.0.0.0", port), Handler)
    ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
    ctx.load_cert_chain(certfile, keyfile)
    httpd.socket = ctx.wrap_socket(httpd.socket, server_side=True)
    print(f"[+] {label:13s} https://0.0.0.0:{port}/")
    httpd.serve_forever()


def main():
    gen_cert(GOOD_KEY, GOOD_CRT, "attacker.test")
    gen_cert(BAD_KEY,  BAD_CRT,  "badcert.test")

    spki = spki_b64(GOOD_CRT)
    print("=" * 72)
    print("GOOD-cert SPKI (pass to Chrome so attacker.test is treated as SECURE):")
    print(f"  --ignore-certificate-errors-spki-list={spki}")
    print("BAD cert is NOT in that list → still errors → user clicks 'Proceed'.")
    print("=" * 72)

    threading.Thread(
        target=serve, args=(19443, BAD_CRT, BAD_KEY, "badcert.test"),
        daemon=True).start()
    try:
        serve(18443, GOOD_CRT, GOOD_KEY, "attacker.test")
    except KeyboardInterrupt:
        sys.exit(0)


if __name__ == "__main__":
    main()
