#!/usr/bin/env python3
"""Fresh self-hosted issuance; no overwrite, retry, payload logging or shared key."""
import argparse
import base64
import fcntl
import json
import os
import re
import select
import stat
import subprocess
import sys
import time
from pathlib import Path


def pairs(items):
    result = {}
    for key, value in items:
        if key in result:
            raise ValueError("duplicate field")
        result[key] = value
    return result


def tenant_map(raw, expected):
    if len(raw) > 1024:
        raise ValueError("oversized record")
    record = json.loads(raw, object_pairs_hook=pairs)
    if (not isinstance(record, dict) or set(record) != {"tenant_id", "auth_key"}
            or record["tenant_id"] != expected or not isinstance(record["auth_key"], str)):
        raise ValueError("invalid record")
    key = base64.b64decode(record["auth_key"], validate=True)
    if len(key) != 32 or base64.b64encode(key).decode() != record["auth_key"]:
        raise ValueError("invalid key")
    return json.dumps({expected: record["auth_key"]}, separators=(",", ":")).encode() + b"\n"


def read_record(fd, deadline):
    raw = bytearray()
    while True:
        remaining = deadline - time.monotonic()
        if remaining <= 0 or not select.select([fd], [], [], remaining)[0]:
            raise ValueError("issuance timed out")
        part = os.read(fd, 1025 - len(raw))
        if not part:
            return bytes(raw)
        raw.extend(part)
        if len(raw) > 1024:
            raise ValueError("oversized record")


def enroll(args):
    if os.geteuid() != 0 or not re.fullmatch(r"[0-9a-f]+(?:-[0-9a-f]+)*", args.tenant_id) or len(args.tenant_id) > 64:
        raise ValueError("root and canonical tenant ID required")
    # This profile supports older kernels: tmpfs plus a host with swap disabled.
    # Host policy must prohibit enabling swap throughout issuance and service life.
    if len(Path("/proc/swaps").read_text().splitlines()) != 1:
        raise ValueError("swap must be disabled")
    destination = Path(args.key_map)
    directory = os.open(destination.parent, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW)
    processes, descriptors = [], [directory]
    try:
        info = os.fstat(directory)
        if info.st_uid != 0 or stat.S_IMODE(info.st_mode) != 0o700:
            raise ValueError("private root directory required")
        filesystem = subprocess.check_output(["findmnt", "-n", "-o", "FSTYPE", "-T", f"/proc/self/fd/{directory}"], pass_fds=(directory,), text=True).strip()
        if filesystem != "tmpfs" or os.path.lexists(destination):
            raise ValueError("fresh map on tmpfs required")
        info = os.fstat(args.admin_token_fd)
        access = fcntl.fcntl(args.admin_token_fd, fcntl.F_GETFL) & os.O_ACCMODE
        if args.admin_token_fd < 3 or not stat.S_ISFIFO(info.st_mode) or info.st_uid != 0 or info.st_mode & 0o077 or access != os.O_RDONLY:
            raise ValueError("independent private root-token input pipe required")
        relay_r, relay_w = os.pipe()
        source_r, source_w = os.pipe()
        descriptors.extend([relay_r, relay_w, source_r, source_w])
        deadline = time.monotonic() + 60
        source = subprocess.Popen([args.cli, "source", "configure", "--beacon-tenant-stdin", "--beacon-tenant-id", args.tenant_id,
                                   "--secretstore-address", args.source, "--secretstore-ca-file", args.source_ca,
                                   "--admin-token-fd", str(args.admin_token_fd)], stdin=source_r,
                                  pass_fds=(args.admin_token_fd,), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
        processes.append(source)
        issuer = subprocess.Popen([args.cli, "beacon", "tenant", "issue", "--tenant-id", args.tenant_id,
                                   "--relay-record-fd", str(relay_w), "--handoff-fd", str(source_w)],
                                  pass_fds=(relay_w, source_w), stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
        processes.append(issuer)
        for fd in [relay_w, source_w, source_r]:
            os.close(fd)
            descriptors.remove(fd)
        payload = tenant_map(read_record(relay_r, deadline), args.tenant_id)
        fd = os.open(destination.name, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o400, dir_fd=directory)
        with os.fdopen(fd, "wb") as output:
            output.write(payload)
        statuses = [process.wait(timeout=max(0.01, deadline - time.monotonic())) for process in processes]
        if statuses != [0, 0]:
            raise ValueError("consumer or issuer failed")
        print(f"tenant={args.tenant_id} relay=persisted source_kv_version=1 issuer=0")
    finally:
        for process in processes:
            if process.poll() is None:
                process.kill()
            process.wait()
        for fd in descriptors:
            os.close(fd)


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--tenant-id", required=True)
    parser.add_argument("--source", required=True)
    parser.add_argument("--source-ca", required=True)
    parser.add_argument("--key-map", required=True)
    parser.add_argument("--admin-token-fd", type=int, required=True)
    parser.add_argument("--cli", default="cloudtaser-cli")
    args = parser.parse_args()
    try:
        enroll(args)
    except Exception:
        # Input and remote replies can contain credentials. Never echo them.
        print("issuance failed or uncertain; stop activation, reconcile both consumers, do not reissue", file=sys.stderr)
        return 1
    return 0


if __name__ == "__main__":
    sys.exit(main())
