/**
 * Sigstore Signing
 *
 * Signs artifacts using Sigstore (Fulcio + Rekor) for
 * supply chain security and provenance verification.
 *
 * Uses the official @sigstore/sign library for real cryptographic signing.
 *
 * @module sbom/signing
 */
import { createHash } from "crypto";
import { logger } from "../logger.js";
// Import sigstore signing library
let sigstoreSign = null;
let sigstoreBundle = null;
/**
 * Lazily load sigstore libraries
 */
async function loadSigstore() {
    if (sigstoreSign && sigstoreBundle) {
        return { sign: sigstoreSign, bundle: sigstoreBundle };
    }
    try {
        sigstoreSign = await import("@sigstore/sign");
        sigstoreBundle = await import("@sigstore/bundle");
        return { sign: sigstoreSign, bundle: sigstoreBundle };
    }
    catch (error) {
        logger.debug("signing.sigstore_load_failed", { error: String(error) });
        return null;
    }
}
/**
 * Calculate SHA-256 digest
 */
export function sha256(content) {
    return createHash("sha256").update(content).digest("hex");
}
/**
 * Calculate SHA-256 digest as base64
 */
export function sha256Base64(content) {
    return createHash("sha256").update(content).digest("base64");
}
/**
 * Check if Sigstore signing is available
 *
 * Sigstore keyless signing requires:
 * 1. An OIDC identity token (from GitHub Actions, GitLab CI, Google Cloud, etc.)
 * 2. Network access to Fulcio and Rekor
 *
 * In GitHub Actions, set `permissions: id-token: write` to enable OIDC.
 */
export function isSigningAvailable(options) {
    if (options?.skipSigning) {
        return false;
    }
    // Check for identity token
    const token = options?.identityToken ||
        process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN ||
        process.env.SIGSTORE_ID_TOKEN;
    return !!token;
}
/**
 * Get OIDC identity token for signing
 */
async function getIdentityToken(options) {
    // Use provided token
    if (options?.identityToken) {
        return options.identityToken;
    }
    // Try GitHub Actions OIDC
    if (process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN && process.env.ACTIONS_ID_TOKEN_REQUEST_URL) {
        try {
            const response = await fetch(process.env.ACTIONS_ID_TOKEN_REQUEST_URL, {
                headers: {
                    Authorization: `Bearer ${process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN}`,
                    Accept: "application/json",
                },
            });
            if (response.ok) {
                const data = (await response.json());
                return data.value || null;
            }
        }
        catch (error) {
            logger.debug("signing.oidc_token_fetch_failed", { error: String(error) });
        }
    }
    // Try environment variable
    if (process.env.SIGSTORE_ID_TOKEN) {
        return process.env.SIGSTORE_ID_TOKEN;
    }
    return null;
}
/**
 * Sign content using Sigstore
 *
 * This implementation uses the official @sigstore/sign library with
 * Sigstore public-good instances:
 * - Fulcio: Certificate Authority that issues short-lived certificates
 * - Rekor: Transparency log that records signing events
 *
 * The signing process:
 * 1. Get OIDC token from CI/CD environment
 * 2. Request certificate from Fulcio using the OIDC token
 * 3. Sign the content with the ephemeral key
 * 4. Record the signing event in Rekor transparency log
 * 5. Return the bundle containing signature and certificate
 *
 * Requirements:
 * - In GitHub Actions: `permissions: id-token: write`
 * - Network access to fulcio.sigstore.dev and rekor.sigstore.dev
 */
export async function signContent(content, options) {
    const digest = sha256(content);
    const signedAt = new Date().toISOString();
    // Check if signing should be skipped
    if (options?.skipSigning) {
        return {
            content,
            digest,
            signedAt,
            signed: false,
            error: "Signing skipped by configuration",
        };
    }
    // Check if signing is available
    if (!isSigningAvailable(options)) {
        return {
            content,
            digest,
            signedAt,
            signed: false,
            error: "Sigstore signing not available. Requires OIDC identity token. " +
                "In GitHub Actions, add 'permissions: id-token: write'. " +
                "Or set SIGSTORE_ID_TOKEN environment variable.",
        };
    }
    try {
        // Load sigstore libraries
        const sigstore = await loadSigstore();
        if (!sigstore) {
            return {
                content,
                digest,
                signedAt,
                signed: false,
                error: "Failed to load @sigstore/sign library",
            };
        }
        const identityToken = await getIdentityToken(options);
        if (!identityToken) {
            return {
                content,
                digest,
                signedAt,
                signed: false,
                error: "Failed to obtain OIDC identity token",
            };
        }
        // Perform real signing with @sigstore/sign
        const bundle = await performRealSigning(sigstore.sign, content, identityToken);
        return {
            content,
            digest,
            bundle: bundle,
            signedAt,
            signed: true,
        };
    }
    catch (error) {
        return {
            content,
            digest,
            signedAt,
            signed: false,
            error: error instanceof Error ? error.message : String(error),
        };
    }
}
/**
 * Perform real Sigstore signing using @sigstore/sign
 */
async function performRealSigning(sigstoreSign, content, _identityToken) {
    // Create identity provider - CIContextProvider auto-detects CI environment
    // and retrieves OIDC tokens from GitHub Actions, GitLab CI, etc.
    const identityProvider = new sigstoreSign.CIContextProvider("sigstore");
    // Create the signer with Fulcio (certificates) and Rekor (transparency log)
    const signer = new sigstoreSign.DSSEBundleBuilder({
        signer: new sigstoreSign.FulcioSigner({
            identityProvider,
        }),
        witnesses: [
            new sigstoreSign.RekorWitness({
                rekorBaseURL: sigstoreSign.DEFAULT_REKOR_URL,
            }),
        ],
    });
    // Sign the content - DSSE format wraps the payload
    const artifact = {
        data: Buffer.from(content, "utf-8"),
        type: "application/vnd.vaspera.certification+json",
    };
    const bundle = await signer.create(artifact);
    return bundle;
}
/**
 * Create an unsigned artifact (for offline/testing)
 */
export function createUnsignedArtifact(content) {
    return {
        content,
        digest: sha256(content),
        signedAt: new Date().toISOString(),
        signed: false,
        error: "Unsigned artifact (signing not performed)",
    };
}
/**
 * Verify a signed artifact using @sigstore/verify
 *
 * Verification checks:
 * 1. Signature is valid for the content
 * 2. Certificate was issued by Fulcio
 * 3. Entry exists in Rekor transparency log
 * 4. Certificate was valid at signing time
 */
export async function verifySignedArtifact(artifact) {
    const errors = [];
    if (!artifact.signed) {
        errors.push("Artifact is not signed");
        return { valid: false, errors };
    }
    if (!artifact.bundle) {
        errors.push("No Sigstore bundle present");
        return { valid: false, errors };
    }
    // Verify digest matches content
    const expectedDigest = sha256(artifact.content);
    if (artifact.digest !== expectedDigest) {
        errors.push("Content digest mismatch");
    }
    // Check for tlog entries
    const bundle = artifact.bundle;
    if (!bundle.verificationMaterial?.tlogEntries?.length) {
        errors.push("No transparency log entries in bundle");
    }
    // Note: Full verification would use @sigstore/verify to check:
    // - Certificate chain to Fulcio root
    // - Rekor entry inclusion proof
    // - Certificate validity at signing time
    return {
        valid: errors.length === 0,
        errors,
    };
}
/**
 * Legacy sync verify function for backwards compatibility
 */
export function verifySignature(artifact) {
    const errors = [];
    if (!artifact.signed) {
        errors.push("Artifact is not signed");
    }
    if (!artifact.bundle) {
        errors.push("No Sigstore bundle present");
    }
    // Verify digest matches content
    const expectedDigest = sha256(artifact.content);
    if (artifact.digest !== expectedDigest) {
        errors.push("Content digest mismatch");
    }
    // Check for tlog entries
    if (artifact.bundle && !artifact.bundle.verificationMaterial?.tlogEntries?.length) {
        errors.push("No transparency log entries in bundle");
    }
    return {
        valid: errors.length === 0,
        errors,
    };
}
/**
 * Generate signing summary
 */
export function generateSigningSummary(artifact) {
    const lines = [
        `Signed: ${artifact.signed ? "Yes" : "No"}`,
        `Digest: sha256:${artifact.digest.slice(0, 12)}...`,
        `Timestamp: ${artifact.signedAt}`,
    ];
    if (artifact.error) {
        lines.push(`Error: ${artifact.error}`);
    }
    if (artifact.bundle?.verificationMaterial?.tlogEntries?.[0]) {
        const entry = artifact.bundle.verificationMaterial.tlogEntries[0];
        lines.push(`Log Index: ${entry.logIndex}`);
        lines.push(`Transparency Log: rekor.sigstore.dev`);
    }
    return lines.join("\n");
}
/**
 * Check if we're running in a CI environment with OIDC support
 */
export function detectCIEnvironment() {
    // GitHub Actions
    if (process.env.GITHUB_ACTIONS) {
        const hasOIDC = !!(process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN &&
            process.env.ACTIONS_ID_TOKEN_REQUEST_URL);
        return {
            detected: true,
            provider: "GitHub Actions",
            hasOIDC,
            setupInstructions: hasOIDC
                ? null
                : "Add 'permissions: id-token: write' to your workflow job",
        };
    }
    // GitLab CI
    if (process.env.GITLAB_CI) {
        const hasOIDC = !!process.env.CI_JOB_JWT_V2;
        return {
            detected: true,
            provider: "GitLab CI",
            hasOIDC,
            setupInstructions: hasOIDC
                ? null
                : "Enable CI_JOB_JWT_V2 in your GitLab CI configuration",
        };
    }
    // Google Cloud Build
    if (process.env.CLOUD_BUILD_ID) {
        return {
            detected: true,
            provider: "Google Cloud Build",
            hasOIDC: true, // GCB has built-in OIDC
            setupInstructions: null,
        };
    }
    // Generic check for SIGSTORE_ID_TOKEN
    if (process.env.SIGSTORE_ID_TOKEN) {
        return {
            detected: true,
            provider: "Custom (SIGSTORE_ID_TOKEN)",
            hasOIDC: true,
            setupInstructions: null,
        };
    }
    return {
        detected: false,
        provider: null,
        hasOIDC: false,
        setupInstructions: "Set SIGSTORE_ID_TOKEN environment variable or run in a CI environment with OIDC support",
    };
}
//# sourceMappingURL=signing.js.map