UNPKG

@praecise/tere

Version:

Trusted Execution Runtime Environment SDK

411 lines (410 loc) 16.4 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.TereClient = void 0; // packages/sdk/src/client.ts const axios_1 = __importDefault(require("axios")); const types_1 = require("./types"); /** * Main client for interacting with the TERE service */ class TereClient { /** * Create a new TERE client * @param options Client configuration options */ constructor(options = {}) { this.endpoint = options.endpoint || 'https://api.tere.praecise.com'; // Initialize HTTP client this.httpClient = axios_1.default.create({ baseURL: this.endpoint, headers: { 'Content-Type': 'application/json', ...(options.apiKey ? { 'Authorization': `ApiKey ${options.apiKey}` } : {}) }, timeout: options.timeout || 30000, }); } /** * Deploy a TERE script to a Trusted Execution Environment * @param options Deployment options * @returns Deployment result */ async deploy(options) { try { // Prepare binary data let binaryData; if (Buffer.isBuffer(options.tereBinary)) { binaryData = options.tereBinary.toString('base64'); } else if (typeof options.tereBinary === 'string') { // Check if it's already base64 if (/^[A-Za-z0-9+/=]+$/.test(options.tereBinary)) { binaryData = options.tereBinary; } else { // Convert to base64 binaryData = Buffer.from(options.tereBinary).toString('base64'); } } else { throw new types_1.TereError('Invalid TERE binary format', 'invalid_binary', 400); } // Convert config to API format const apiConfig = options.config ? { tee_type: options.config.teeType, provider: options.config.provider, location: options.config.location, resource_limits: options.config.resourceLimits ? { cpu_cores: options.config.resourceLimits.cpuCores, memory_mb: options.config.resourceLimits.memoryMb, storage_gb: options.config.resourceLimits.storageGb } : undefined, security_settings: options.config.securitySettings ? { secure_boot: options.config.securitySettings.secureBoot, integrity_monitoring: options.config.securitySettings.integrityMonitoring, vtpm: options.config.securitySettings.vtpm, confidential_compute_type: options.config.securitySettings.confidentialComputeType, min_firmware_version: options.config.securitySettings.minFirmwareVersion, enable_hsm: options.config.securitySettings.enableHsm, hsm_key_ring: options.config.securitySettings.hsmKeyRing } : undefined, network_config: options.config.networkConfig ? { network: options.config.networkConfig.network, subnet: options.config.networkConfig.subnet, use_public_ip: options.config.networkConfig.usePublicIp, network_tags: options.config.networkConfig.networkTags } : undefined } : undefined; // Prepare payload const payload = { name: options.name, tere_binary: binaryData, description: options.description, config: apiConfig }; // Make API request const response = await this.httpClient.post('/api/deploy', payload); // Parse and return response return { scriptId: response.data.script_id, attestation: response.data.attestation, instanceInfo: { teeType: response.data.instance_info.tee_type, location: response.data.instance_info.location, attestationVerificationUrl: response.data.instance_info.attestation_verification_url, details: response.data.instance_info.details || {} } }; } catch (error) { throw this.handleError(error, 'Deployment failed'); } } /** * Execute a function in a deployed script * @param options Execute options * @returns Execution result */ async execute(options) { try { // Serialize arguments if they're an array let serializedArgs; if (Array.isArray(options.arguments)) { serializedArgs = Buffer.from(JSON.stringify(options.arguments)).toString('base64'); } else if (typeof options.arguments === 'string') { // Check if it's already base64 if (/^[A-Za-z0-9+/=]+$/.test(options.arguments)) { serializedArgs = options.arguments; } else { // Assume it's JSON and convert to base64 serializedArgs = Buffer.from(options.arguments).toString('base64'); } } else { // Convert object or any other type to JSON and then to base64 serializedArgs = Buffer.from(JSON.stringify(options.arguments)).toString('base64'); } // Create payload const payload = { script_id: options.scriptId, function: options.function, arguments: serializedArgs, wait_for_result: options.waitForResult !== undefined ? options.waitForResult : true, caller_id: options.callerId, nonce: options.nonce, hsm_key_id: options.hsmKeyId // Add HSM key ID if provided }; // Make API request const response = await this.httpClient.post('/api/execute', payload); // Parse resource usage if available let resourceUsage = undefined; if (response.data.resource_usage) { resourceUsage = { gasUsed: response.data.resource_usage.gas_used, executionTimeMs: response.data.resource_usage.execution_time_ms, memoryBytes: response.data.resource_usage.memory_bytes }; } // Parse and return response return { result: response.data.result, attestation: response.data.attestation, resourceUsage, jobId: response.data.job_id, hsmAttestation: response.data.hsm_attestation // Include HSM attestation if available }; } catch (error) { throw this.handleError(error, 'Execution failed'); } } /** * Get the status of an asynchronous job * @param jobId ID of the job to check * @returns Current job status and result if completed */ async getJobStatus(jobId) { try { // Make API request const response = await this.httpClient.get(`/api/jobs/${jobId}`); // Parse resource usage if available let resourceUsage = undefined; if (response.data.resource_usage) { resourceUsage = { gasUsed: response.data.resource_usage.gas_used, executionTimeMs: response.data.resource_usage.execution_time_ms, memoryBytes: response.data.resource_usage.memory_bytes }; } // Parse and return response return { result: response.data.result, attestation: response.data.attestation, resourceUsage, jobId: response.data.job_id, hsmAttestation: response.data.hsm_attestation // Include HSM attestation if available }; } catch (error) { throw this.handleError(error, 'Failed to get job status'); } } /** * List all deployed scripts * @returns Array of script information */ async listScripts() { try { // Make API request const response = await this.httpClient.get('/api/scripts'); // Parse and return response return response.data.map((script) => ({ id: script.id, name: script.name, description: script.description, provider: script.provider, teeType: script.tee_type, location: script.location, creationTime: new Date(script.creation_time), status: script.status, hsmEnabled: script.hsm_enabled // Include HSM status })); } catch (error) { throw this.handleError(error, 'Failed to list scripts'); } } /** * Get information about a specific script * @param scriptId ID of the script * @returns Script information */ async getScript(scriptId) { try { // Make API request const response = await this.httpClient.get(`/api/scripts/${scriptId}`); // Parse and return response return { id: response.data.id, name: response.data.name, description: response.data.description, provider: response.data.provider, teeType: response.data.tee_type, location: response.data.location, creationTime: new Date(response.data.creation_time), status: response.data.status, hsmEnabled: response.data.hsm_enabled // Include HSM status }; } catch (error) { throw this.handleError(error, 'Failed to get script'); } } /** * Start a stopped instance * @param scriptId ID of the script */ async startInstance(scriptId) { try { await this.httpClient.post(`/api/instances/${scriptId}/start`); } catch (error) { throw this.handleError(error, 'Failed to start instance'); } } /** * Stop a running instance * @param scriptId ID of the script */ async stopInstance(scriptId) { try { await this.httpClient.post(`/api/instances/${scriptId}/stop`); } catch (error) { throw this.handleError(error, 'Failed to stop instance'); } } /** * Delete a deployed script * @param scriptId ID of the script * @param options Options for deletion */ async deleteScript(scriptId, options) { try { const queryParams = (options === null || options === void 0 ? void 0 : options.preserveState) ? '?preserveState=true' : ''; await this.httpClient.delete(`/api/scripts/${scriptId}${queryParams}`); } catch (error) { throw this.handleError(error, 'Failed to delete script'); } } /** * Verify an attestation * @param options Verification options * @returns Verification result */ async verifyAttestation(options) { try { // Create payload const payload = { attestation: options.attestation, expected_nonce: options.expectedNonce }; // Make API request const response = await this.httpClient.post('/api/verify-attestation', payload); // Map API response to SDK type let details = undefined; if (response.data.details) { details = { teeType: response.data.details.tee_type, provider: response.data.details.provider, instanceId: response.data.details.instance_id, timestamp: new Date(response.data.details.timestamp), secureBoot: response.data.details.secure_boot, integrityVerified: response.data.details.integrity_verified, firmwareVersion: response.data.details.firmware_version, // Add HSM info if available hsmInfo: response.data.details.hsm_info ? { enabled: response.data.details.hsm_info.enabled, protectionLevel: response.data.details.hsm_info.protection_level, keyDetails: response.data.details.hsm_info.key_details } : undefined }; } return { valid: response.data.valid, details }; } catch (error) { throw this.handleError(error, 'Attestation verification failed'); } } /** * List HSM keys available for a script * @param scriptId ID of the script * @returns List of HSM keys and their details */ async listHsmKeys(scriptId) { try { // Make API request const response = await this.httpClient.get(`/api/scripts/${scriptId}/hsm-keys`); // Parse and return response return response.data.map((key) => ({ id: key.id, name: key.name, purpose: key.purpose, algorithm: key.algorithm, protectionLevel: key.protection_level, createTime: new Date(key.create_time), status: key.status })); } catch (error) { throw this.handleError(error, 'Failed to list HSM keys'); } } /** * Create a new HSM key for a script * @param scriptId ID of the script * @param keyId ID for the new key * @param purpose Purpose of the key ('encrypt', 'sign', 'decrypt') * @param algorithm Optional algorithm specification * @returns Details of the created key */ async createHsmKey(scriptId, keyId, purpose, algorithm) { try { // Create payload const payload = { key_id: keyId, purpose, algorithm }; // Make API request const response = await this.httpClient.post(`/api/scripts/${scriptId}/hsm-keys`, payload); return { id: response.data.id, name: response.data.name, purpose: response.data.purpose, algorithm: response.data.algorithm, protectionLevel: response.data.protection_level, createTime: new Date(response.data.create_time), status: response.data.status }; } catch (error) { throw this.handleError(error, 'Failed to create HSM key'); } } /** * Handle errors from API calls * @param error Original error * @param defaultMessage Default error message * @returns Properly formatted TereError */ handleError(error, defaultMessage) { if (axios_1.default.isAxiosError(error)) { const axiosError = error; if (axiosError.response) { const data = axiosError.response.data; const errorCode = data.error || 'unknown_error'; const message = data.message || defaultMessage; const requestId = data.request_id; return new types_1.TereError(message, errorCode, axiosError.response.status, requestId); } else if (axiosError.request) { return new types_1.TereError('No response received from server', 'network_error', 0); } } if (error instanceof Error) { return new types_1.TereError(error.message, 'unknown_error', 0); } return new types_1.TereError(String(error) || defaultMessage, 'unknown_error', 0); } } exports.TereClient = TereClient;