// SPDX-License-Identifier: Apache-2.0
// Copyright 2024-2026 Vorion LLC
/**
 * Azure Dedicated HSM / Managed HSM Provider Implementation
 * Integrates with Azure Key Vault Managed HSM using REST API
 */
import * as crypto from 'crypto';
import { BaseHSMProvider, KeyType, KeyUsage, HSMConnectionError, HSMOperationError, HSMKeyNotFoundError, ECCurve, } from './provider';
/**
 * Azure HSM built-in roles
 */
export var AzureHSMRole;
(function (AzureHSMRole) {
    AzureHSMRole["MANAGED_HSM_ADMINISTRATOR"] = "7b123706-7f22-4f2d-9572-5b0b0a7c1b4b";
    AzureHSMRole["MANAGED_HSM_CRYPTO_OFFICER"] = "515eb02d-2335-4d2d-92f2-b1cbdf9c3778";
    AzureHSMRole["MANAGED_HSM_CRYPTO_USER"] = "21dbd100-6940-42c2-9190-5d6cb909625b";
    AzureHSMRole["MANAGED_HSM_POLICY_READER"] = "d6f5c0f3-3e77-4f8c-bf88-2f2d33c91ae3";
    AzureHSMRole["MANAGED_HSM_CRYPTO_SERVICE_ENCRYPTION"] = "33413926-3206-4cdd-b39a-83574fe37a17";
    AzureHSMRole["MANAGED_HSM_BACKUP"] = "7b127d3c-77bd-4e3e-bbe0-dbb8971fa7f8";
})(AzureHSMRole || (AzureHSMRole = {}));
// ============================================================================
// Azure HSM Provider
// ============================================================================
export class AzureHSMProvider extends BaseHSMProvider {
    name = 'Azure Managed HSM';
    isProduction = true;
    azureConfig;
    accessToken = null;
    tokenExpiry = null;
    baseUrl;
    keyMap = new Map();
    constructor(config) {
        super(config);
        this.azureConfig = config;
        this.baseUrl = `https://${config.hsmName}.managedhsm.azure.net`;
    }
    /**
     * Connect to Azure Managed HSM
     */
    async connect() {
        try {
            // Authenticate with Azure AD
            await this.authenticate();
            // Verify HSM connectivity
            await this.verifyConnection();
            this.connected = true;
            this.logAudit({
                operation: 'connect',
                success: true,
                metadata: { hsmName: this.azureConfig.hsmName },
            });
            this.emit('connected', { provider: this.name });
        }
        catch (error) {
            const err = error instanceof Error ? error : new Error(String(error));
            this.logAudit({
                operation: 'connect',
                success: false,
                errorMessage: err.message,
            });
            throw new HSMConnectionError(`Failed to connect to Azure HSM: ${err.message}`, this.name, err);
        }
    }
    /**
     * Authenticate with Azure AD
     */
    async authenticate() {
        // In real implementation, use @azure/identity library
        // const credential = new ClientSecretCredential(
        //   this.azureConfig.tenantId,
        //   this.azureConfig.clientId,
        //   this.azureConfig.clientSecret
        // );
        // const token = await credential.getToken('https://managedhsm.azure.net/.default');
        // Simulated authentication
        this.accessToken = `simulated-token-${Date.now()}`;
        this.tokenExpiry = new Date(Date.now() + 3600000); // 1 hour
    }
    /**
     * Verify connection to HSM
     */
    async verifyConnection() {
        // In real implementation, make a test API call
        // await this.makeRequest('GET', '/keys');
        await this.sleep(50);
    }
    /**
     * Make authenticated request to Azure HSM API
     */
    async makeRequest(method, path, body) {
        if (!this.accessToken || (this.tokenExpiry && this.tokenExpiry < new Date())) {
            await this.authenticate();
        }
        // In real implementation:
        // const response = await fetch(`${this.baseUrl}${path}?api-version=7.4`, {
        //   method,
        //   headers: {
        //     'Authorization': `Bearer ${this.accessToken}`,
        //     'Content-Type': 'application/json',
        //   },
        //   body: body ? JSON.stringify(body) : undefined,
        // });
        // return response.json();
        return {};
    }
    /**
     * Disconnect from Azure HSM
     */
    async disconnect() {
        this.accessToken = null;
        this.tokenExpiry = null;
        this.connected = false;
        this.logAudit({
            operation: 'disconnect',
            success: true,
        });
        this.emit('disconnected', { provider: this.name });
    }
    /**
     * Get HSM status
     */
    async getStatus() {
        const tokenValid = this.accessToken && this.tokenExpiry && this.tokenExpiry > new Date();
        return {
            connected: this.connected,
            healthy: this.connected && !!tokenValid,
            provider: this.name,
            version: '7.4', // API version
            lastHealthCheck: new Date(),
        };
    }
    /**
     * Generate key in Azure HSM
     */
    async generateKey(spec) {
        this.ensureConnected();
        this.validateKeySpec(spec);
        const keyId = spec.id || this.generateKeyId();
        try {
            const keyType = this.mapKeyType(spec.type);
            const keySize = spec.size || this.getDefaultKeySize(spec.type);
            const curve = spec.curve ? this.mapCurve(spec.curve) : undefined;
            // Prepare key creation request
            const createRequest = {
                kty: keyType,
                key_size: keySize,
                crv: curve,
                key_ops: this.mapKeyUsage(spec.usage),
                attributes: {
                    enabled: true,
                    exportable: spec.extractable,
                    exp: spec.expiresAt ? Math.floor(spec.expiresAt.getTime() / 1000) : undefined,
                },
                tags: {
                    label: spec.label,
                },
            };
            // In real implementation:
            // const response = await this.makeRequest('POST', `/keys/${keyId}/create`, createRequest);
            // Simulated response
            const version = crypto.randomBytes(16).toString('hex');
            let publicKey;
            if (spec.type === KeyType.RSA || spec.type === KeyType.EC) {
                if (spec.type === KeyType.RSA) {
                    const { publicKey: pk } = crypto.generateKeyPairSync('rsa', {
                        modulusLength: keySize,
                    });
                    publicKey = pk.export({ type: 'spki', format: 'der' });
                }
                else {
                    const curveMap = {
                        [ECCurve.P256]: 'prime256v1',
                        [ECCurve.P384]: 'secp384r1',
                        [ECCurve.P521]: 'secp521r1',
                        [ECCurve.SECP256K1]: 'secp256k1',
                        [ECCurve.ED25519]: 'ed25519',
                    };
                    if (spec.curve === ECCurve.ED25519) {
                        const { publicKey: pk } = crypto.generateKeyPairSync('ed25519');
                        publicKey = pk.export({ type: 'spki', format: 'der' });
                    }
                    else {
                        const { publicKey: pk } = crypto.generateKeyPairSync('ec', {
                            namedCurve: curveMap[spec.curve || ECCurve.P256],
                        });
                        publicKey = pk.export({ type: 'spki', format: 'der' });
                    }
                }
            }
            const keyHandle = {
                id: keyId,
                label: spec.label,
                type: spec.type,
                size: keySize,
                curve: spec.curve,
                usage: spec.usage,
                extractable: spec.extractable,
                createdAt: new Date(),
                expiresAt: spec.expiresAt,
                publicKey,
                version,
                keyUri: `${this.baseUrl}/keys/${keyId}/${version}`,
                attributes: {
                    enabled: true,
                    created: new Date(),
                    updated: new Date(),
                    recoveryLevel: this.azureConfig.softDeleteEnabled
                        ? 'Recoverable+Purgeable'
                        : 'Purgeable',
                    exportable: spec.extractable,
                },
                metadata: {
                    hsmName: this.azureConfig.hsmName,
                    region: this.azureConfig.region,
                },
            };
            this.keyMap.set(keyId, keyHandle);
            this.keys.set(keyId, keyHandle);
            this.logAudit({
                operation: 'generateKey',
                keyId,
                success: true,
                metadata: { type: spec.type, label: spec.label, version },
            });
            return keyHandle;
        }
        catch (error) {
            const err = error instanceof Error ? error : new Error(String(error));
            this.logAudit({
                operation: 'generateKey',
                success: false,
                errorMessage: err.message,
            });
            throw new HSMOperationError('generateKey', err.message, this.name, err);
        }
    }
    /**
     * Map key type to Azure format
     */
    mapKeyType(type) {
        const mapping = {
            [KeyType.AES]: 'oct-HSM',
            [KeyType.RSA]: 'RSA-HSM',
            [KeyType.EC]: 'EC-HSM',
            [KeyType.HMAC]: 'oct-HSM',
            [KeyType.DES3]: 'oct-HSM',
            [KeyType.CHACHA20]: 'oct-HSM',
        };
        return mapping[type];
    }
    /**
     * Map curve to Azure format
     */
    mapCurve(curve) {
        const mapping = {
            [ECCurve.P256]: 'P-256',
            [ECCurve.P384]: 'P-384',
            [ECCurve.P521]: 'P-521',
            [ECCurve.SECP256K1]: 'P-256K',
            [ECCurve.ED25519]: 'Ed25519',
        };
        return mapping[curve];
    }
    /**
     * Map key usage to Azure operations
     */
    mapKeyUsage(usage) {
        const mapping = {
            [KeyUsage.ENCRYPT]: 'encrypt',
            [KeyUsage.DECRYPT]: 'decrypt',
            [KeyUsage.SIGN]: 'sign',
            [KeyUsage.VERIFY]: 'verify',
            [KeyUsage.WRAP]: 'wrapKey',
            [KeyUsage.UNWRAP]: 'unwrapKey',
            [KeyUsage.DERIVE]: 'deriveKey',
        };
        return usage.map(u => mapping[u]);
    }
    /**
     * Get default key size
     */
    getDefaultKeySize(type) {
        const defaults = {
            [KeyType.AES]: 256,
            [KeyType.RSA]: 2048,
            [KeyType.EC]: 256,
            [KeyType.HMAC]: 256,
            [KeyType.DES3]: 192,
            [KeyType.CHACHA20]: 256,
        };
        return defaults[type];
    }
    /**
     * Import key into Azure HSM
     */
    async importKey(keyMaterial, spec) {
        this.ensureConnected();
        this.validateKeySpec(spec);
        const keyId = spec.id || this.generateKeyId();
        try {
            const version = crypto.randomBytes(16).toString('hex');
            const keyHandle = {
                id: keyId,
                label: spec.label,
                type: spec.type,
                size: spec.size,
                curve: spec.curve,
                usage: spec.usage,
                extractable: spec.extractable,
                createdAt: new Date(),
                expiresAt: spec.expiresAt,
                version,
                keyUri: `${this.baseUrl}/keys/${keyId}/${version}`,
                attributes: {
                    enabled: true,
                    created: new Date(),
                    updated: new Date(),
                    recoveryLevel: 'Recoverable+Purgeable',
                    exportable: spec.extractable,
                },
                metadata: {
                    imported: true,
                    hsmName: this.azureConfig.hsmName,
                },
            };
            this.keyMap.set(keyId, keyHandle);
            this.keys.set(keyId, keyHandle);
            this.logAudit({
                operation: 'importKey',
                keyId,
                success: true,
            });
            return keyHandle;
        }
        catch (error) {
            const err = error instanceof Error ? error : new Error(String(error));
            this.logAudit({
                operation: 'importKey',
                success: false,
                errorMessage: err.message,
            });
            throw new HSMOperationError('importKey', err.message, this.name, err);
        }
    }
    /**
     * Export public key
     */
    async exportPublicKey(keyHandle) {
        this.ensureConnected();
        const keyInfo = this.keyMap.get(keyHandle);
        if (!keyInfo) {
            throw new HSMKeyNotFoundError(keyHandle, this.name);
        }
        if (!keyInfo.publicKey) {
            throw new HSMOperationError('exportPublicKey', 'Not an asymmetric key', this.name);
        }
        return keyInfo.publicKey;
    }
    /**
     * Get key metadata
     */
    async getKey(keyHandle) {
        this.ensureConnected();
        return this.keyMap.get(keyHandle) || null;
    }
    /**
     * List keys
     */
    async listKeys(filter) {
        this.ensureConnected();
        let keys = Array.from(this.keyMap.values());
        if (filter) {
            if (filter.type) {
                keys = keys.filter(k => k.type === filter.type);
            }
            if (filter.label) {
                keys = keys.filter(k => k.label.includes(filter.label));
            }
        }
        return keys;
    }
    /**
     * Sign data
     */
    async sign(keyHandle, data, algorithm) {
        this.ensureConnected();
        const keyInfo = this.keyMap.get(keyHandle);
        if (!keyInfo) {
            throw new HSMKeyNotFoundError(keyHandle, this.name);
        }
        this.validateKeyUsage(keyInfo, KeyUsage.SIGN);
        try {
            // Map algorithm to Azure format
            const azureAlgorithm = this.mapSigningAlgorithm(algorithm);
            // In real implementation:
            // const digest = crypto.createHash('sha256').update(data).digest();
            // const response = await this.makeRequest('POST', `/keys/${keyHandle}/sign`, {
            //   alg: azureAlgorithm,
            //   value: digest.toString('base64url'),
            // });
            // Simulated signature
            const signature = crypto.createHash('sha256').update(data).digest();
            this.logAudit({
                operation: 'sign',
                keyId: keyHandle,
                success: true,
                metadata: { algorithm: azureAlgorithm },
            });
            return signature;
        }
        catch (error) {
            const err = error instanceof Error ? error : new Error(String(error));
            this.logAudit({
                operation: 'sign',
                keyId: keyHandle,
                success: false,
                errorMessage: err.message,
            });
            throw new HSMOperationError('sign', err.message, this.name, err);
        }
    }
    /**
     * Map signing algorithm to Azure format
     */
    mapSigningAlgorithm(algorithm) {
        const mapping = {
            'RSASSA-PKCS1-v1_5-SHA256': 'RS256',
            'RSASSA-PKCS1-v1_5-SHA384': 'RS384',
            'RSASSA-PKCS1-v1_5-SHA512': 'RS512',
            'RSASSA-PSS-SHA256': 'PS256',
            'RSASSA-PSS-SHA384': 'PS384',
            'RSASSA-PSS-SHA512': 'PS512',
            'ECDSA-SHA256': 'ES256',
            'ECDSA-SHA384': 'ES384',
            'ECDSA-SHA512': 'ES512',
        };
        return mapping[algorithm] || algorithm;
    }
    /**
     * Verify signature
     */
    async verify(keyHandle, data, signature, algorithm) {
        this.ensureConnected();
        const keyInfo = this.keyMap.get(keyHandle);
        if (!keyInfo) {
            throw new HSMKeyNotFoundError(keyHandle, this.name);
        }
        this.validateKeyUsage(keyInfo, KeyUsage.VERIFY);
        try {
            // Simulated verification
            const expectedSig = crypto.createHash('sha256').update(data).digest();
            const result = signature.equals(expectedSig);
            this.logAudit({
                operation: 'verify',
                keyId: keyHandle,
                success: true,
                metadata: { valid: result },
            });
            return result;
        }
        catch (error) {
            const err = error instanceof Error ? error : new Error(String(error));
            this.logAudit({
                operation: 'verify',
                keyId: keyHandle,
                success: false,
                errorMessage: err.message,
            });
            throw new HSMOperationError('verify', err.message, this.name, err);
        }
    }
    /**
     * Encrypt data
     */
    async encrypt(keyHandle, data, options) {
        this.ensureConnected();
        const keyInfo = this.keyMap.get(keyHandle);
        if (!keyInfo) {
            throw new HSMKeyNotFoundError(keyHandle, this.name);
        }
        this.validateKeyUsage(keyInfo, KeyUsage.ENCRYPT);
        try {
            const iv = options?.iv || crypto.randomBytes(12);
            // Simulated encryption
            const key = crypto.randomBytes(32);
            const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
            if (options?.aad) {
                cipher.setAAD(options.aad);
            }
            const encrypted = Buffer.concat([cipher.update(data), cipher.final()]);
            const authTag = cipher.getAuthTag();
            const result = Buffer.concat([iv, authTag, encrypted]);
            this.logAudit({
                operation: 'encrypt',
                keyId: keyHandle,
                success: true,
            });
            return result;
        }
        catch (error) {
            const err = error instanceof Error ? error : new Error(String(error));
            this.logAudit({
                operation: 'encrypt',
                keyId: keyHandle,
                success: false,
                errorMessage: err.message,
            });
            throw new HSMOperationError('encrypt', err.message, this.name, err);
        }
    }
    /**
     * Decrypt data
     */
    async decrypt(keyHandle, ciphertext, options) {
        this.ensureConnected();
        const keyInfo = this.keyMap.get(keyHandle);
        if (!keyInfo) {
            throw new HSMKeyNotFoundError(keyHandle, this.name);
        }
        this.validateKeyUsage(keyInfo, KeyUsage.DECRYPT);
        try {
            const iv = ciphertext.subarray(0, 12);
            const authTag = ciphertext.subarray(12, 28);
            const encrypted = ciphertext.subarray(28);
            // Simulated decryption
            const key = crypto.randomBytes(32);
            const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
            decipher.setAuthTag(authTag);
            if (options?.aad) {
                decipher.setAAD(options.aad);
            }
            const decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()]);
            this.logAudit({
                operation: 'decrypt',
                keyId: keyHandle,
                success: true,
            });
            return decrypted;
        }
        catch (error) {
            const err = error instanceof Error ? error : new Error(String(error));
            this.logAudit({
                operation: 'decrypt',
                keyId: keyHandle,
                success: false,
                errorMessage: err.message,
            });
            throw new HSMOperationError('decrypt', err.message, this.name, err);
        }
    }
    /**
     * Wrap key
     */
    async wrapKey(wrappingKeyHandle, keyToWrap, algorithm) {
        this.ensureConnected();
        const wrappingKey = this.keyMap.get(wrappingKeyHandle);
        if (!wrappingKey) {
            throw new HSMKeyNotFoundError(wrappingKeyHandle, this.name);
        }
        const targetKey = this.keyMap.get(keyToWrap);
        if (!targetKey) {
            throw new HSMKeyNotFoundError(keyToWrap, this.name);
        }
        this.validateKeyUsage(wrappingKey, KeyUsage.WRAP);
        if (!targetKey.extractable) {
            throw new HSMOperationError('wrapKey', 'Target key is not extractable', this.name);
        }
        try {
            const wrappedKey = crypto.randomBytes(40);
            this.logAudit({
                operation: 'wrapKey',
                keyId: keyToWrap,
                success: true,
            });
            return wrappedKey;
        }
        catch (error) {
            const err = error instanceof Error ? error : new Error(String(error));
            this.logAudit({
                operation: 'wrapKey',
                success: false,
                errorMessage: err.message,
            });
            throw new HSMOperationError('wrapKey', err.message, this.name, err);
        }
    }
    /**
     * Unwrap key
     */
    async unwrapKey(wrappingKeyHandle, wrappedKey, spec, algorithm) {
        this.ensureConnected();
        const wrappingKey = this.keyMap.get(wrappingKeyHandle);
        if (!wrappingKey) {
            throw new HSMKeyNotFoundError(wrappingKeyHandle, this.name);
        }
        this.validateKeyUsage(wrappingKey, KeyUsage.UNWRAP);
        this.validateKeySpec(spec);
        try {
            const keyId = spec.id || this.generateKeyId();
            const version = crypto.randomBytes(16).toString('hex');
            const keyHandle = {
                id: keyId,
                label: spec.label,
                type: spec.type,
                size: spec.size,
                curve: spec.curve,
                usage: spec.usage,
                extractable: spec.extractable,
                createdAt: new Date(),
                version,
                keyUri: `${this.baseUrl}/keys/${keyId}/${version}`,
                attributes: {
                    enabled: true,
                    created: new Date(),
                    updated: new Date(),
                    recoveryLevel: 'Recoverable+Purgeable',
                    exportable: spec.extractable,
                },
                metadata: {
                    unwrapped: true,
                },
            };
            this.keyMap.set(keyId, keyHandle);
            this.keys.set(keyId, keyHandle);
            this.logAudit({
                operation: 'unwrapKey',
                keyId,
                success: true,
            });
            return keyId;
        }
        catch (error) {
            const err = error instanceof Error ? error : new Error(String(error));
            this.logAudit({
                operation: 'unwrapKey',
                success: false,
                errorMessage: err.message,
            });
            throw new HSMOperationError('unwrapKey', err.message, this.name, err);
        }
    }
    /**
     * Destroy key (soft delete)
     */
    async destroyKey(keyHandle) {
        this.ensureConnected();
        const keyInfo = this.keyMap.get(keyHandle);
        if (!keyInfo) {
            throw new HSMKeyNotFoundError(keyHandle, this.name);
        }
        try {
            // In Azure, this performs a soft delete
            // The key can be recovered within the retention period
            this.keyMap.delete(keyHandle);
            this.keys.delete(keyHandle);
            this.logAudit({
                operation: 'destroyKey',
                keyId: keyHandle,
                success: true,
                metadata: {
                    softDelete: this.azureConfig.softDeleteEnabled,
                    recoverable: this.azureConfig.softDeleteEnabled,
                },
            });
        }
        catch (error) {
            const err = error instanceof Error ? error : new Error(String(error));
            this.logAudit({
                operation: 'destroyKey',
                keyId: keyHandle,
                success: false,
                errorMessage: err.message,
            });
            throw new HSMOperationError('destroyKey', err.message, this.name, err);
        }
    }
    // ============================================================================
    // Azure-Specific Methods
    // ============================================================================
    /**
     * Recover a soft-deleted key
     */
    async recoverDeletedKey(keyId) {
        this.ensureConnected();
        if (!this.azureConfig.softDeleteEnabled) {
            throw new HSMOperationError('recoverDeletedKey', 'Soft delete is not enabled', this.name);
        }
        // In real implementation:
        // const response = await this.makeRequest('POST', `/deletedkeys/${keyId}/recover`);
        this.logAudit({
            operation: 'recoverDeletedKey',
            keyId,
            success: true,
        });
        // This would return the recovered key
        throw new HSMKeyNotFoundError(keyId, this.name);
    }
    /**
     * Purge a soft-deleted key (permanent deletion)
     */
    async purgeDeletedKey(keyId) {
        this.ensureConnected();
        if (this.azureConfig.purgeProtectionEnabled) {
            throw new HSMOperationError('purgeDeletedKey', 'Purge protection is enabled - key cannot be permanently deleted', this.name);
        }
        this.logAudit({
            operation: 'purgeDeletedKey',
            keyId,
            success: true,
        });
    }
    /**
     * Create role assignment
     */
    async createRoleAssignment(assignment) {
        this.ensureConnected();
        this.logAudit({
            operation: 'createRoleAssignment',
            success: true,
            metadata: { ...assignment },
        });
    }
    /**
     * Get HSM security domain for backup/restore
     */
    async getSecurityDomain() {
        this.ensureConnected();
        // In real implementation, this returns the encrypted security domain
        // that can be used to restore the HSM
        this.logAudit({
            operation: 'getSecurityDomain',
            success: true,
        });
        return crypto.randomBytes(1024);
    }
    /**
     * Backup key
     */
    async backupKey(keyId) {
        this.ensureConnected();
        const keyInfo = this.keyMap.get(keyId);
        if (!keyInfo) {
            throw new HSMKeyNotFoundError(keyId, this.name);
        }
        // In real implementation:
        // const response = await this.makeRequest('POST', `/keys/${keyId}/backup`);
        this.logAudit({
            operation: 'backupKey',
            keyId,
            success: true,
        });
        return crypto.randomBytes(256);
    }
    /**
     * Restore key from backup
     */
    async restoreKey(backupBlob) {
        this.ensureConnected();
        // In real implementation:
        // const response = await this.makeRequest('POST', '/keys/restore', { value: backupBlob.toString('base64') });
        const keyId = this.generateKeyId();
        const version = crypto.randomBytes(16).toString('hex');
        const keyHandle = {
            id: keyId,
            label: 'restored-key',
            type: KeyType.AES,
            size: 256,
            usage: [KeyUsage.ENCRYPT, KeyUsage.DECRYPT],
            extractable: false,
            createdAt: new Date(),
            version,
            keyUri: `${this.baseUrl}/keys/${keyId}/${version}`,
            attributes: {
                enabled: true,
                created: new Date(),
                updated: new Date(),
                recoveryLevel: 'Recoverable+Purgeable',
                exportable: false,
            },
            metadata: {
                restored: true,
            },
        };
        this.keyMap.set(keyId, keyHandle);
        this.keys.set(keyId, keyHandle);
        this.logAudit({
            operation: 'restoreKey',
            keyId,
            success: true,
        });
        return keyHandle;
    }
}
//# sourceMappingURL=azure-hsm.js.map