// Ed25519 keypair + signing helpers.
// Per Shield-R2 #1 (CRITICAL): the platform issues exactly ONE asymmetric
// primitive (Ed25519) for both passport signatures and API JWTs. No RSA, no
// HS256, no padding-oracle surface.
//
// Per Shield-R1 #1: keypair generation must use a vetted library API
// (Ed25519PrivateKey.generate() in pyca; @noble/curves' ed25519.utils.randomPrivateKey()
// in TypeScript). Raw token_bytes(32) seed derivation is rejected — fails any
// FIPS 140-3 audit and is operationally fragile.
//
// In production T1, private keys live in HashiCorp Vault. This module exposes
// the in-memory primitives; the Vault wiring lands in the next slice.
import { ed25519 } from "@noble/curves/ed25519.js";
import { createHash, randomUUID } from "node:crypto";
/**
 * Generate a fresh Ed25519 keypair using the library's vetted CSPRNG path.
 * Equivalent to pyca/cryptography Ed25519PrivateKey.generate().
 */
export function generateKeypair() {
    // @noble/curves 2.x: randomSecretKey() (was randomPrivateKey in 1.x).
    const privateKey = ed25519.utils.randomSecretKey();
    const publicKey = ed25519.getPublicKey(privateKey);
    const publicKeyFingerprint = createHash("sha256").update(publicKey).digest("hex");
    return { privateKey, publicKey, publicKeyFingerprint };
}
/** Sign arbitrary bytes with an Ed25519 private key. Returns base64url unpadded. */
export function sign(message, privateKey) {
    const sig = ed25519.sign(message, privateKey);
    return Buffer.from(sig).toString("base64url");
}
/** Verify a base64url Ed25519 signature against bytes + public key. */
export function verify(signatureBase64Url, message, publicKey) {
    // Constant-time-ish; @noble/curves verify is the canonical path.
    const sig = Buffer.from(signatureBase64Url, "base64url");
    if (sig.length !== 64)
        return false;
    try {
        return ed25519.verify(sig, message, publicKey);
    }
    catch {
        return false;
    }
}
/** UUID4 helper (re-exported so call sites import one place). */
export function newUuid() {
    return randomUUID();
}
/**
 * Canonical JSON serialization for signing. Sorts object keys at EVERY depth
 * (not just top level) and preserves array element order.
 *
 * SECURITY: a previous version used `JSON.stringify(obj, Object.keys(obj).sort())`
 * which filters keys at every depth via the replacer-array. That stripped nested
 * object content from the signed payload — mutating `authorized_scope.write_data`
 * would not change the signed bytes, so tampered passports verified successfully.
 * This implementation walks the tree and serializes deterministically.
 */
export function canonicalJson(value) {
    return stringifyCanonical(value);
}
function stringifyCanonical(value) {
    if (value === null)
        return "null";
    if (typeof value === "number") {
        if (!Number.isFinite(value))
            throw new Error("canonicalJson: non-finite number");
        return String(value);
    }
    if (typeof value === "boolean")
        return value ? "true" : "false";
    if (typeof value === "string")
        return JSON.stringify(value);
    if (Array.isArray(value)) {
        return "[" + value.map(stringifyCanonical).join(",") + "]";
    }
    if (typeof value === "object") {
        const obj = value;
        const keys = Object.keys(obj).sort();
        const parts = [];
        for (const k of keys) {
            const v = obj[k];
            if (v === undefined)
                continue; // standard JSON.stringify behavior
            parts.push(JSON.stringify(k) + ":" + stringifyCanonical(v));
        }
        return "{" + parts.join(",") + "}";
    }
    throw new Error(`canonicalJson: unsupported type ${typeof value}`);
}
//# sourceMappingURL=ed25519.js.map