# Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
# SPDX-License-Identifier: AGPL-3.0-or-later
#
# Compile the vendored external crypto KAT vectors (test/vectors/*.json, produced
# by tools/curate_crypto_vectors.py) into a C table the native test loops over:
# test/test_crypto_kat/kat_data.inc. Deterministic and offline - it only reads the
# committed JSON, so CI can re-run it and diff to catch a stale .inc.
#
# Each vector is emitted as a struct of hex C-strings (decoded at runtime by the
# host test); the struct layouts live in test/test_crypto_kat/test_crypto_kat.cpp.

import json
import os

HERE = os.path.dirname(os.path.abspath(__file__))
VEC_DIR = os.path.join(HERE, "..", "test", "vectors")
OUT = os.path.join(HERE, "..", "test", "test_crypto_kat", "kat_data.inc")

# json file -> (C array name, C struct type, ordered emit fields). Each field is
# (json_key, kind); kind "hex"/"int" pull that key, "valid" derives 1/0 from the
# result. tcId always leads. The struct types are defined in the test .cpp.
SPECS = [
    (
        "wycheproof_hmac_sha256.json",
        "KAT_HMAC_SHA256",
        "KatMac",
        [("key", "hex"), ("msg", "hex"), ("tag", "hex"), ("tagSize", "int"), (None, "valid")],
    ),
    (
        "wycheproof_hmac_sha512.json",
        "KAT_HMAC_SHA512",
        "KatMac",
        [("key", "hex"), ("msg", "hex"), ("tag", "hex"), ("tagSize", "int"), (None, "valid")],
    ),
    (
        "wycheproof_aes_128_gcm.json",
        "KAT_AES128GCM",
        "KatAead",
        [("key", "hex"), ("iv", "hex"), ("aad", "hex"), ("msg", "hex"), ("ct", "hex"), ("tag", "hex"), (None, "valid")],
    ),
    ("wycheproof_x25519.json", "KAT_X25519", "KatX25519", [("public", "hex"), ("private", "hex"), ("shared", "hex")]),
    (
        "wycheproof_ed25519.json",
        "KAT_ED25519",
        "KatEd25519",
        [("public", "hex"), ("msg", "hex"), ("sig", "hex"), (None, "valid")],
    ),
    ("rfc5869_hkdf_sha256.json", "KAT_HKDF_EXTRACT", "KatHkdf", [("salt", "hex"), ("ikm", "hex"), ("prk", "hex")]),
    (
        "rfc8439_chacha20.json",
        "KAT_CHACHA20",
        "KatChacha",
        [("key", "hex"), ("counter", "int"), ("nonce", "hex"), ("keystream", "hex")],
    ),
    ("rfc8439_poly1305.json", "KAT_POLY1305", "KatPoly", [("key", "hex"), ("msg", "hex"), ("tag", "hex")]),
]


def emit_field(v, key, kind):
    if kind == "valid":
        return "1" if v["result"] == "valid" else "0"
    if kind == "int":
        return str(int(v.get(key, 0) or 0))
    return '"%s"' % (v.get(key, "") or "")


def main():
    lines = [
        "// Generated by tools/gen_crypto_vectors.py from test/vectors/*.json - do not edit.",
        "// External crypto known-answer vectors (Project Wycheproof + RFC appendices).",
        "// clang-format off",
        "",
    ]
    for fname, arr, struct, fields in SPECS:
        with open(os.path.join(VEC_DIR, fname)) as f:
            doc = json.load(f)
        src = doc.get("source", "")
        commit = doc.get("commit", "")
        prov = "%s%s" % (src, (" @ " + commit) if commit else "")
        lines.append("// %s -- %s (%d vectors)" % (arr, prov, len(doc["vectors"])))
        lines.append("static const %s %s[] = {" % (struct, arr))
        for v in doc["vectors"]:
            cells = [str(v["tcId"])]
            cells += [emit_field(v, k, kind) for (k, kind) in fields]
            lines.append("    {%s}," % ", ".join(cells))
        lines.append("};")
        lines.append("")
    lines.append("// clang-format on")
    with open(OUT, "w") as f:
        f.write("\n".join(lines) + "\n")
    total = 0
    for fname, arr, struct, _ in SPECS:
        with open(os.path.join(VEC_DIR, fname)) as f:
            total += len(json.load(f)["vectors"])
    print(
        "wrote %s (%d vectors across %d primitives)"
        % (os.path.relpath(OUT, os.path.join(HERE, "..")), total, len(SPECS))
    )


if __name__ == "__main__":
    main()
