// SPDX-License-Identifier: Apache-2.0
// Copyright 2024-2026 Vorion LLC
/**
 * TEE (Trusted Execution Environment) Binding Service
 *
 * Implements hardware-bound key attestation for CAR ID security hardening (SH-3).
 * TEE binding ensures that:
 * 1. Agent keys are generated inside a secure enclave
 * 2. The DID is bound to the enclave's measurement
 * 3. At runtime, the expected code is provably executing
 *
 * Supported platforms:
 * - Intel SGX (DCAP/EPID attestation)
 * - AWS Nitro Enclaves
 * - AMD SEV-SNP
 * - ARM TrustZone
 * - Apple Secure Enclave
 *
 * @status INTEGRATION_REQUIRED
 *   Platform-specific verifiers define the interface and format validation
 *   but do not perform actual attestation verification. Each platform requires
 *   its own native SDK or remote attestation service:
 *   - Intel SGX: Intel DCAP Quote Verification Library or IAS API
 *   - AWS Nitro: AWS Nitro Enclaves SDK (attestation document COSE verification)
 *   - AMD SEV-SNP: AMD SEV Tool or sev-snp-measure
 *   - ARM TrustZone: Platform-specific OP-TEE client
 *   - Apple Secure Enclave: DeviceCheck / App Attest API
 *   The TEEService orchestration, metrics, and binding proof logic are real.
 *   Only the attestation verification callbacks are stubs awaiting SDK integration.
 *
 * @packageDocumentation
 */
import { createLogger } from '../common/logger.js';
import { VorionError } from '../common/errors.js';
import { Counter, Histogram, Gauge } from 'prom-client';
import { vorionRegistry } from '../common/metrics-registry.js';
import { TEEPlatform as TEEPlatformEnum, teeConfigSchema, teeAttestationSchema, teeKeyBindingSchema, } from './types.js';
const logger = createLogger({ component: 'security-tee' });
// =============================================================================
// Metrics
// =============================================================================
const teeAttestationsVerified = new Counter({
    name: 'vorion_security_tee_attestations_verified_total',
    help: 'Total TEE attestations verified',
    labelNames: ['platform', 'result'],
    registers: [vorionRegistry],
});
const teeVerificationDuration = new Histogram({
    name: 'vorion_security_tee_verification_duration_seconds',
    help: 'Duration of TEE attestation verification',
    labelNames: ['platform'],
    buckets: [0.01, 0.05, 0.1, 0.25, 0.5, 1, 2.5],
    registers: [vorionRegistry],
});
const teeKeyBindings = new Counter({
    name: 'vorion_security_tee_key_bindings_total',
    help: 'Total TEE key bindings created',
    labelNames: ['platform'],
    registers: [vorionRegistry],
});
const teeActiveBindings = new Gauge({
    name: 'vorion_security_tee_active_bindings',
    help: 'Number of active TEE key bindings',
    labelNames: ['platform'],
    registers: [vorionRegistry],
});
// =============================================================================
// Errors
// =============================================================================
/**
 * TEE-specific error
 */
export class TEEError extends VorionError {
    code = 'TEE_ERROR';
    statusCode = 403;
    constructor(message, details) {
        super(message, details);
        this.name = 'TEEError';
    }
}
/**
 * TEE attestation verification failed
 */
export class TEEAttestationError extends TEEError {
    code = 'TEE_ATTESTATION_ERROR';
    constructor(message, details) {
        super(message, details);
        this.name = 'TEEAttestationError';
    }
}
/**
 * TEE key binding error
 */
export class TEEKeyBindingError extends TEEError {
    code = 'TEE_KEY_BINDING_ERROR';
    constructor(message, details) {
        super(message, details);
        this.name = 'TEEKeyBindingError';
    }
}
/**
 * Intel SGX attestation verifier
 * Uses DCAP (Data Center Attestation Primitives) or EPID
 */
class SGXVerifier {
    async verify(attestation) {
        const startTime = Date.now();
        try {
            // In a real implementation, this would:
            // 1. Parse the SGX quote from attestation.signature
            // 2. Verify the quote signature using Intel's attestation service
            // 3. Validate PCR values against expected measurements
            // 4. Check certificate chain
            // Validate required fields
            if (!attestation.signature) {
                return {
                    valid: false,
                    reason: 'Missing SGX quote signature',
                    verifiedAt: new Date().toISOString(),
                };
            }
            if (!attestation.pcrs) {
                return {
                    valid: false,
                    reason: 'Missing PCR values',
                    verifiedAt: new Date().toISOString(),
                };
            }
            // Verify certificate chain if provided
            if (attestation.certificateChain && attestation.certificateChain.length > 0) {
                // In production, verify the chain against Intel's root CA
                logger.debug({ chainLength: attestation.certificateChain.length }, 'Verifying SGX certificate chain');
            }
            // Check attestation freshness
            const now = new Date();
            if (attestation.validUntil && attestation.validUntil < now) {
                return {
                    valid: false,
                    reason: 'Attestation has expired',
                    verifiedAt: now.toISOString(),
                };
            }
            logger.debug({ enclaveId: attestation.enclaveId, measurementHash: attestation.measurementHash }, 'SGX attestation verification not available (no verification backend configured)');
            return {
                valid: false,
                reason: 'No TEE verification backend configured. This platform verifier is a placeholder.',
                platform: TEEPlatformEnum.SGX,
                verifiedAt: new Date().toISOString(),
            };
        }
        finally {
            const duration = (Date.now() - startTime) / 1000;
            teeVerificationDuration.observe({ platform: 'sgx' }, duration);
        }
    }
    validateMeasurement(expectedHash, attestation) {
        return attestation.measurementHash === expectedHash;
    }
}
/**
 * AWS Nitro Enclaves attestation verifier
 */
class NitroVerifier {
    async verify(attestation) {
        const startTime = Date.now();
        try {
            // In a real implementation, this would:
            // 1. Parse the Nitro attestation document
            // 2. Verify the COSE signature
            // 3. Validate PCRs against expected values
            // 4. Verify AWS root certificate chain
            // Validate required fields
            if (!attestation.signature) {
                return {
                    valid: false,
                    reason: 'Missing Nitro attestation document',
                    verifiedAt: new Date().toISOString(),
                };
            }
            // Nitro uses specific PCRs
            const requiredPCRs = ['PCR0', 'PCR1', 'PCR2'];
            if (attestation.pcrs) {
                for (const pcr of requiredPCRs) {
                    if (!attestation.pcrs[pcr]) {
                        return {
                            valid: false,
                            reason: `Missing required PCR: ${pcr}`,
                            verifiedAt: new Date().toISOString(),
                        };
                    }
                }
            }
            // Check attestation freshness
            const now = new Date();
            if (attestation.validUntil && attestation.validUntil < now) {
                return {
                    valid: false,
                    reason: 'Attestation has expired',
                    verifiedAt: now.toISOString(),
                };
            }
            logger.debug({ enclaveId: attestation.enclaveId, measurementHash: attestation.measurementHash }, 'Nitro attestation verification not available (no verification backend configured)');
            return {
                valid: false,
                reason: 'No TEE verification backend configured. This platform verifier is a placeholder.',
                platform: TEEPlatformEnum.NITRO,
                verifiedAt: new Date().toISOString(),
            };
        }
        finally {
            const duration = (Date.now() - startTime) / 1000;
            teeVerificationDuration.observe({ platform: 'nitro' }, duration);
        }
    }
    validateMeasurement(expectedHash, attestation) {
        // For Nitro, PCR0 contains the enclave image measurement
        if (attestation.pcrs?.['PCR0']) {
            return attestation.pcrs['PCR0'] === expectedHash;
        }
        return attestation.measurementHash === expectedHash;
    }
}
/**
 * AMD SEV-SNP attestation verifier
 */
class SEVVerifier {
    async verify(attestation) {
        const startTime = Date.now();
        try {
            // Validate required fields
            if (!attestation.signature) {
                return {
                    valid: false,
                    reason: 'Missing SEV attestation report',
                    verifiedAt: new Date().toISOString(),
                };
            }
            // Check attestation freshness
            const now = new Date();
            if (attestation.validUntil && attestation.validUntil < now) {
                return {
                    valid: false,
                    reason: 'Attestation has expired',
                    verifiedAt: now.toISOString(),
                };
            }
            logger.debug({ enclaveId: attestation.enclaveId, measurementHash: attestation.measurementHash }, 'SEV attestation verification not available (no verification backend configured)');
            return {
                valid: false,
                reason: 'No TEE verification backend configured. This platform verifier is a placeholder.',
                platform: TEEPlatformEnum.SEV,
                verifiedAt: new Date().toISOString(),
            };
        }
        finally {
            const duration = (Date.now() - startTime) / 1000;
            teeVerificationDuration.observe({ platform: 'sev' }, duration);
        }
    }
    validateMeasurement(expectedHash, attestation) {
        return attestation.measurementHash === expectedHash;
    }
}
/**
 * ARM TrustZone attestation verifier
 */
class TrustZoneVerifier {
    async verify(attestation) {
        const startTime = Date.now();
        try {
            // TrustZone attestation is platform-specific
            if (!attestation.signature) {
                return {
                    valid: false,
                    reason: 'Missing TrustZone attestation',
                    verifiedAt: new Date().toISOString(),
                };
            }
            // Check attestation freshness
            const now = new Date();
            if (attestation.validUntil && attestation.validUntil < now) {
                return {
                    valid: false,
                    reason: 'Attestation has expired',
                    verifiedAt: now.toISOString(),
                };
            }
            logger.debug({ enclaveId: attestation.enclaveId }, 'TrustZone attestation verification not available (no verification backend configured)');
            return {
                valid: false,
                reason: 'No TEE verification backend configured. This platform verifier is a placeholder.',
                platform: TEEPlatformEnum.TRUSTZONE,
                verifiedAt: new Date().toISOString(),
            };
        }
        finally {
            const duration = (Date.now() - startTime) / 1000;
            teeVerificationDuration.observe({ platform: 'trustzone' }, duration);
        }
    }
    validateMeasurement(expectedHash, attestation) {
        return attestation.measurementHash === expectedHash;
    }
}
/**
 * Apple Secure Enclave verifier
 */
class SecureEnclaveVerifier {
    async verify(attestation) {
        const startTime = Date.now();
        try {
            // Secure Enclave uses DeviceCheck/App Attest
            if (!attestation.signature) {
                return {
                    valid: false,
                    reason: 'Missing Secure Enclave attestation',
                    verifiedAt: new Date().toISOString(),
                };
            }
            // Check attestation freshness
            const now = new Date();
            if (attestation.validUntil && attestation.validUntil < now) {
                return {
                    valid: false,
                    reason: 'Attestation has expired',
                    verifiedAt: now.toISOString(),
                };
            }
            logger.debug({ enclaveId: attestation.enclaveId }, 'Secure Enclave attestation verification not available (no verification backend configured)');
            return {
                valid: false,
                reason: 'No TEE verification backend configured. This platform verifier is a placeholder.',
                platform: TEEPlatformEnum.SECURE_ENCLAVE,
                verifiedAt: new Date().toISOString(),
            };
        }
        finally {
            const duration = (Date.now() - startTime) / 1000;
            teeVerificationDuration.observe({ platform: 'secure-enclave' }, duration);
        }
    }
    validateMeasurement(expectedHash, attestation) {
        return attestation.measurementHash === expectedHash;
    }
}
// =============================================================================
// TEE Binding Service
// =============================================================================
/**
 * TEE Binding Service for hardware attestation and key binding
 *
 * @example
 * ```typescript
 * const tee = new TEEBindingService({
 *   requiredForTiers: [TrustTier.T4, TrustTier.T5],
 *   allowedPlatforms: ['sgx', 'nitro'],
 *   maxAttestationAge: 86400, // 24 hours
 * });
 *
 * // Verify attestation
 * const result = await tee.verifyAttestation(attestation);
 *
 * // Bind DID key to enclave
 * const binding = await tee.bindKeyToEnclave(didKeyId, attestation);
 * ```
 */
export class TEEBindingService {
    config;
    verifiers;
    bindings; // didKeyId -> binding
    /**
     * Create a new TEE binding service
     *
     * @param config - TEE configuration
     */
    constructor(config) {
        const defaultConfig = {
            requiredForTiers: [4, 5],
            allowedPlatforms: [TEEPlatformEnum.SGX, TEEPlatformEnum.NITRO],
            maxAttestationAge: 86400,
        };
        this.config = { ...defaultConfig, ...teeConfigSchema.parse(config) };
        this.bindings = new Map();
        // Initialize platform-specific verifiers
        this.verifiers = new Map([
            [TEEPlatformEnum.SGX, new SGXVerifier()],
            [TEEPlatformEnum.NITRO, new NitroVerifier()],
            [TEEPlatformEnum.SEV, new SEVVerifier()],
            [TEEPlatformEnum.TRUSTZONE, new TrustZoneVerifier()],
            [TEEPlatformEnum.SECURE_ENCLAVE, new SecureEnclaveVerifier()],
        ]);
        logger.info({
            requiredForTiers: this.config.requiredForTiers,
            allowedPlatforms: this.config.allowedPlatforms,
            maxAttestationAge: this.config.maxAttestationAge,
        }, 'TEE binding service initialized');
    }
    /**
     * Verify a TEE attestation
     *
     * @param attestation - TEE attestation to verify
     * @returns Verification result
     */
    async verifyAttestation(attestation) {
        // Validate attestation format
        try {
            teeAttestationSchema.parse(attestation);
        }
        catch (error) {
            teeAttestationsVerified.inc({ platform: attestation.platform, result: 'invalid' });
            return {
                valid: false,
                reason: 'Invalid attestation format',
                verifiedAt: new Date().toISOString(),
            };
        }
        // Check if platform is allowed
        if (!this.config.allowedPlatforms.includes(attestation.platform)) {
            teeAttestationsVerified.inc({ platform: attestation.platform, result: 'invalid' });
            return {
                valid: false,
                reason: `Platform not allowed: ${attestation.platform}`,
                verifiedAt: new Date().toISOString(),
            };
        }
        // Check attestation age
        const age = (Date.now() - attestation.timestamp.getTime()) / 1000;
        if (age > this.config.maxAttestationAge) {
            teeAttestationsVerified.inc({ platform: attestation.platform, result: 'expired' });
            return {
                valid: false,
                reason: `Attestation too old (age: ${Math.floor(age)}s, max: ${this.config.maxAttestationAge}s)`,
                verifiedAt: new Date().toISOString(),
            };
        }
        // Get platform-specific verifier
        const verifier = this.verifiers.get(attestation.platform);
        if (!verifier) {
            teeAttestationsVerified.inc({ platform: attestation.platform, result: 'invalid' });
            return {
                valid: false,
                reason: `No verifier available for platform: ${attestation.platform}`,
                verifiedAt: new Date().toISOString(),
            };
        }
        // Verify attestation
        const result = await verifier.verify(attestation);
        // Check expected measurements if configured
        if (result.valid && this.config.expectedMeasurements) {
            const expectedHash = this.config.expectedMeasurements[attestation.enclaveId];
            if (expectedHash) {
                const measurementValid = verifier.validateMeasurement(expectedHash, attestation);
                if (!measurementValid) {
                    teeAttestationsVerified.inc({ platform: attestation.platform, result: 'measurement_mismatch' });
                    return {
                        valid: false,
                        reason: 'Code measurement does not match expected value',
                        platform: attestation.platform,
                        verifiedAt: new Date().toISOString(),
                    };
                }
            }
        }
        teeAttestationsVerified.inc({
            platform: attestation.platform,
            result: result.valid ? 'success' : 'invalid',
        });
        return result;
    }
    /**
     * Bind a DID key to an enclave
     *
     * @param didKeyId - DID verification method ID
     * @param enclaveAttestation - TEE attestation proving enclave validity
     * @returns Key binding record
     */
    async bindKeyToEnclave(didKeyId, enclaveAttestation) {
        // First verify the attestation
        const verificationResult = await this.verifyAttestation(enclaveAttestation);
        if (!verificationResult.valid) {
            throw new TEEAttestationError(`Cannot bind key: ${verificationResult.reason}`, { didKeyId, enclaveId: enclaveAttestation.enclaveId });
        }
        // Create binding proof
        // In a real implementation, this would be a cryptographic proof
        // that the key was generated inside the enclave
        const bindingProof = await this.createBindingProof(didKeyId, enclaveAttestation);
        // Calculate validity period
        const validUntil = enclaveAttestation.validUntil
            ? new Date(Math.min(enclaveAttestation.validUntil.getTime(), Date.now() + this.config.maxAttestationAge * 1000))
            : new Date(Date.now() + this.config.maxAttestationAge * 1000);
        const binding = {
            didKeyId,
            enclaveKeyId: enclaveAttestation.enclaveId,
            bindingProof,
            boundAt: new Date(),
            validUntil,
        };
        // Validate binding
        teeKeyBindingSchema.parse(binding);
        // Store binding
        this.bindings.set(didKeyId, binding);
        teeKeyBindings.inc({ platform: enclaveAttestation.platform });
        teeActiveBindings.set({ platform: enclaveAttestation.platform }, this.getBindingCount(enclaveAttestation.platform));
        logger.info({ didKeyId, enclaveId: enclaveAttestation.enclaveId, platform: enclaveAttestation.platform }, 'Key bound to enclave');
        return binding;
    }
    /**
     * Verify a key binding
     *
     * @param binding - Key binding to verify
     * @returns Whether binding is valid
     */
    async verifyKeyBinding(binding) {
        try {
            teeKeyBindingSchema.parse(binding);
        }
        catch {
            logger.warn({ didKeyId: binding.didKeyId }, 'Invalid binding format');
            return false;
        }
        // Check if binding has expired
        if (binding.validUntil && binding.validUntil < new Date()) {
            logger.debug({ didKeyId: binding.didKeyId }, 'Binding has expired');
            return false;
        }
        // Verify binding proof
        // In a real implementation, this would verify the cryptographic proof
        const proofValid = this.verifyBindingProof(binding);
        if (!proofValid) {
            logger.warn({ didKeyId: binding.didKeyId }, 'Invalid binding proof');
            return false;
        }
        return true;
    }
    /**
     * Check if TEE binding is required for a trust tier
     *
     * @param trustTier - Trust tier to check
     * @returns Whether TEE binding is required
     */
    isRequired(trustTier) {
        return this.config.requiredForTiers.includes(trustTier);
    }
    /**
     * Validate that running code matches attestation measurement
     *
     * @param expectedHash - Expected code measurement hash
     * @param attestation - TEE attestation to validate
     * @returns Whether measurement matches
     */
    validateCodeMeasurement(expectedHash, attestation) {
        const verifier = this.verifiers.get(attestation.platform);
        if (!verifier) {
            return false;
        }
        return verifier.validateMeasurement(expectedHash, attestation);
    }
    /**
     * Get a stored binding
     *
     * @param didKeyId - DID verification method ID
     * @returns Binding if exists
     */
    getBinding(didKeyId) {
        return this.bindings.get(didKeyId);
    }
    /**
     * Remove a binding
     *
     * @param didKeyId - DID verification method ID
     */
    removeBinding(didKeyId) {
        const binding = this.bindings.get(didKeyId);
        if (binding) {
            this.bindings.delete(didKeyId);
            logger.info({ didKeyId }, 'Key binding removed');
        }
    }
    /**
     * Get current configuration
     */
    getConfig() {
        return { ...this.config };
    }
    /**
     * Create a binding proof
     * In a real implementation, this would be a cryptographic proof
     */
    async createBindingProof(didKeyId, attestation) {
        // Create a simple hash-based proof for development
        // In production, this would be a proper cryptographic binding
        const data = JSON.stringify({
            didKeyId,
            enclaveId: attestation.enclaveId,
            measurementHash: attestation.measurementHash,
            timestamp: attestation.timestamp.toISOString(),
        });
        const encoder = new TextEncoder();
        const hashBuffer = await crypto.subtle.digest('SHA-256', encoder.encode(data));
        const hashArray = new Uint8Array(hashBuffer);
        let binary = '';
        for (let i = 0; i < hashArray.length; i++) {
            binary += String.fromCharCode(hashArray[i]);
        }
        return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
    }
    /**
     * Verify a binding proof
     */
    verifyBindingProof(binding) {
        // In a real implementation, this would verify the cryptographic proof
        // For now, just check that the proof exists and has valid format
        return binding.bindingProof.length > 0;
    }
    /**
     * Get count of bindings for a platform
     */
    getBindingCount(_platform) {
        return this.bindings.size;
    }
}
/**
 * Create a TEE binding service with default configuration for CAR ID
 */
export function createTEEBindingService(config) {
    const defaultConfig = {
        requiredForTiers: [4, 5], // T4+
        allowedPlatforms: [TEEPlatformEnum.SGX, TEEPlatformEnum.NITRO],
        maxAttestationAge: 86400, // 24 hours
    };
    return new TEEBindingService({ ...defaultConfig, ...config });
}
//# sourceMappingURL=tee.js.map