UNPKG

@dollhousemcp/mcp-server

Version:

DollhouseMCP - A Model Context Protocol (MCP) server that enables dynamic AI persona management from markdown files, allowing Claude and other compatible AI assistants to activate and switch between different behavioral personas.

320 lines 39.8 kB
/** * Pattern Encryption Service for Memory Security * * Part of Issue #1321 Phase 2: Memory Security Architecture * * PURPOSE: * Encrypts dangerous patterns extracted from FLAGGED memories using AES-256-GCM. * Ensures patterns are never stored in plain text while remaining accessible * for authorized security research outside LLM contexts. * * ARCHITECTURE: * - Uses AES-256-GCM for authenticated encryption * - Derives keys from DOLLHOUSE_ENCRYPTION_SECRET via PBKDF2 * - Generates unique IV for each encryption operation * - Provides GCM authentication tags for integrity verification * * SECURITY: * - Patterns encrypted at rest * - Decryption only outside LLM context * - All decryption attempts logged * - Key rotation supported * * REFACTOR NOTE: * Converted from static class to instance-based for DI architecture compatibility. * * @module PatternEncryptor */ import { randomBytes, createCipheriv, createDecipheriv, pbkdf2 } from 'node:crypto'; import { logger } from '../../utils/logger.js'; import { env } from '../../config/env.js'; /** * Default encryption configuration */ const DEFAULT_CONFIG = { // Honor DOLLHOUSE_DISABLE_ENCRYPTION environment variable // If explicitly disabled, respect that setting regardless of NODE_ENV enabled: env.DOLLHOUSE_DISABLE_ENCRYPTION ? false : env.NODE_ENV === 'production', secret: env.DOLLHOUSE_ENCRYPTION_SECRET, iterations: 100000, // SECURITY FIX: Configurable salt to prevent rainbow table attacks (#1733) // Falls back to default with startup warning if not configured salt: env.DOLLHOUSE_ENCRYPTION_SALT || 'dollhouse-pattern-encryption-v1', }; /** * PatternEncryptor service * * Handles encryption and decryption of dangerous patterns using AES-256-GCM. * Provides authenticated encryption with integrity protection via GCM mode. * * DI-COMPATIBLE: Instance-based service for dependency injection. */ export class PatternEncryptor { ALGORITHM = 'aes-256-gcm'; KEY_LENGTH = 32; // 256 bits IV_LENGTH = 16; // 128 bits AUTH_TAG_LENGTH = 16; // 128 bits config; encryptionKey; isInitialized = false; /** * Create a new PatternEncryptor instance * * @param config - Optional configuration overrides */ constructor(config) { this.config = { ...DEFAULT_CONFIG, ...config }; } /** * Initialize encryption with configuration * * @param config - Optional configuration overrides * @throws Error if encryption secret is not provided when enabled */ async initialize(config) { if (config) { this.config = { ...this.config, ...config }; } if (!this.config.enabled) { const reason = env.DOLLHOUSE_DISABLE_ENCRYPTION ? 'explicitly disabled via DOLLHOUSE_DISABLE_ENCRYPTION environment variable' : `disabled (NODE_ENV=${env.NODE_ENV})`; logger.info(`Pattern encryption ${reason}`); this.isInitialized = true; return; } if (!this.config.secret) { throw new Error('DOLLHOUSE_ENCRYPTION_SECRET environment variable is required when encryption is enabled'); } // SEC-05 (#1733): Warn if using the static default salt if (!env.DOLLHOUSE_ENCRYPTION_SALT) { logger.warn('⚠️ Pattern encryption using default salt. Set DOLLHOUSE_ENCRYPTION_SALT to a random 32+ character string ' + '(e.g. `openssl rand -hex 32`) for stronger protection against rainbow table attacks.'); } logger.info('Initializing pattern encryption', { algorithm: this.ALGORITHM, iterations: this.config.iterations, }); // Derive encryption key from secret this.encryptionKey = await this.deriveKey(this.config.secret, this.config.salt, this.config.iterations); this.isInitialized = true; logger.info('Pattern encryption initialized successfully'); } /** * Encrypt a pattern using AES-256-GCM * * @param pattern - Plain text pattern to encrypt * @returns Encrypted pattern with metadata * @throws Error if encryption not initialized or pattern is empty */ encrypt(pattern) { if (!this.isInitialized) { throw new Error('PatternEncryptor not initialized. Call initialize() first.'); } if (!this.config.enabled) { // Encryption disabled — return mock structure with MOCK: prefix (#1733) // so it cannot be confused with real ciphertext logger.debug('Encryption disabled, returning mock structure'); return { encryptedData: `MOCK:${Buffer.from(pattern).toString('base64')}`, algorithm: this.ALGORITHM, iv: '', authTag: '', }; } if (!pattern || pattern.length === 0) { throw new Error('Cannot encrypt empty pattern'); } if (!this.encryptionKey) { throw new Error('Encryption key not available'); } logger.debug('Encrypting pattern', { patternLength: pattern.length, }); try { // Generate random IV for this encryption const iv = randomBytes(this.IV_LENGTH); // Create cipher with AES-256-GCM const cipher = createCipheriv(this.ALGORITHM, this.encryptionKey, iv); // Encrypt the pattern let encrypted = cipher.update(pattern, 'utf8', 'base64'); encrypted += cipher.final('base64'); // Get authentication tag from GCM mode const authTag = cipher.getAuthTag(); logger.debug('Pattern encrypted successfully', { ivLength: iv.length, authTagLength: authTag.length, encryptedLength: encrypted.length, }); return { encryptedData: encrypted, algorithm: this.ALGORITHM, iv: iv.toString('base64'), authTag: authTag.toString('base64'), }; } catch (error) { logger.error('Failed to encrypt pattern', { error }); throw new Error(`Pattern encryption failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } /** * Decrypt an encrypted pattern * * @param encrypted - Encrypted pattern structure * @returns Decrypted plain text pattern * @throws Error if decryption fails or authentication fails */ decrypt(encrypted) { if (!this.isInitialized) { throw new Error('PatternEncryptor not initialized. Call initialize() first.'); } if (!this.config.enabled) { // Encryption disabled — decode mock data, stripping MOCK: prefix (#1733) logger.debug('Encryption disabled, decoding mock structure'); const data = encrypted.encryptedData.startsWith('MOCK:') ? encrypted.encryptedData.slice(5) : encrypted.encryptedData; return Buffer.from(data, 'base64').toString('utf8'); } if (!this.encryptionKey) { throw new Error('Encryption key not available'); } if (!encrypted.encryptedData || !encrypted.iv || !encrypted.authTag) { throw new Error('Invalid encrypted pattern: missing required fields'); } logger.debug('Decrypting pattern', { algorithm: encrypted.algorithm, }); try { // Decode base64 components const iv = Buffer.from(encrypted.iv, 'base64'); const authTag = Buffer.from(encrypted.authTag, 'base64'); // Create decipher with AES-256-GCM const decipher = createDecipheriv(this.ALGORITHM, this.encryptionKey, iv); // Set authentication tag for verification decipher.setAuthTag(authTag); // Decrypt the pattern let decrypted = decipher.update(encrypted.encryptedData, 'base64', 'utf8'); decrypted += decipher.final('utf8'); logger.debug('Pattern decrypted successfully', { decryptedLength: decrypted.length, }); return decrypted; } catch (error) { logger.error('Failed to decrypt pattern', { error }); // Authentication failure means data was tampered with // Check for various GCM authentication error messages if (error instanceof Error) { const errorMsg = error.message.toLowerCase(); if (errorMsg.includes('auth') || errorMsg.includes('decrypt') || errorMsg.includes('unsupported state') || errorMsg.includes('unable to authenticate')) { throw new Error('Pattern decryption failed: Authentication tag mismatch (data may be corrupted or tampered)'); } } throw new Error(`Pattern decryption failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } /** * Derive encryption key from secret using PBKDF2 * * @param secret - Master secret from environment * @param salt - Salt for key derivation * @param iterations - Number of PBKDF2 iterations * @returns Derived encryption key */ async deriveKey(secret, salt, iterations) { return new Promise((resolve, reject) => { pbkdf2(secret, salt, iterations, this.KEY_LENGTH, 'sha256', (err, derivedKey) => { if (err) { logger.error('Key derivation failed', { error: err }); reject(new Error(`Failed to derive encryption key: ${err.message}`)); } else { logger.debug('Encryption key derived successfully', { keyLength: derivedKey.length, iterations, }); resolve(derivedKey); } }); }); } /** * Check if encryption is enabled * * @returns true if encryption is enabled and initialized */ isEnabled() { return this.config.enabled && this.isInitialized; } /** * Get encryption configuration status * * @returns Configuration status (without exposing secrets) */ getStatus() { return { enabled: this.config.enabled, initialized: this.isInitialized, algorithm: this.ALGORITHM, keyLength: this.KEY_LENGTH, iterations: this.config.iterations, hasSecret: !!this.config.secret, }; } /** * Securely clear encryption key from memory * SECURITY FIX: Overwrites key buffer with zeros before releasing * * This prevents key recovery from memory dumps or process inspection. * Should be called when encryption is no longer needed. */ secureKeyClear() { if (this.encryptionKey) { // Overwrite key material with zeros this.encryptionKey.fill(0); this.encryptionKey = undefined; logger.debug('Encryption key securely cleared from memory'); } } /** * Reset the encryptor (useful for testing) * SECURITY FIX: Now performs secure key clearing * WARNING: This will securely clear the encryption key from memory */ reset() { this.secureKeyClear(); this.isInitialized = false; this.config = { ...DEFAULT_CONFIG }; logger.debug('PatternEncryptor reset'); } /** * Securely reset the encryptor and clear all sensitive data * SECURITY: Explicitly clears encryption keys from memory * * Use this when: * - Shutting down the application * - Rotating encryption keys * - Responding to security incidents */ secureReset() { this.secureKeyClear(); this.isInitialized = false; this.config = { ...DEFAULT_CONFIG }; logger.info('PatternEncryptor securely reset - all sensitive data cleared'); } /** * Dispose of the encryptor and securely clear all data * Implements cleanup for proper DI lifecycle management */ async dispose() { this.secureReset(); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUGF0dGVybkVuY3J5cHRvci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9zZWN1cml0eS9lbmNyeXB0aW9uL1BhdHRlcm5FbmNyeXB0b3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBMEJHO0FBRUgsT0FBTyxFQUFFLFdBQVcsRUFBRSxjQUFjLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQ3BGLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUMvQyxPQUFPLEVBQUUsR0FBRyxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFxQzFDOztHQUVHO0FBQ0gsTUFBTSxjQUFjLEdBQXFCO0lBQ3ZDLDBEQUEwRDtJQUMxRCxzRUFBc0U7SUFDdEUsT0FBTyxFQUFFLEdBQUcsQ0FBQyw0QkFBNEI7UUFDdkMsQ0FBQyxDQUFDLEtBQUs7UUFDUCxDQUFDLENBQUMsR0FBRyxDQUFDLFFBQVEsS0FBSyxZQUFZO0lBQ2pDLE1BQU0sRUFBRSxHQUFHLENBQUMsMkJBQTJCO0lBQ3ZDLFVBQVUsRUFBRSxNQUFNO0lBQ2xCLDJFQUEyRTtJQUMzRSwrREFBK0Q7SUFDL0QsSUFBSSxFQUFFLEdBQUcsQ0FBQyx5QkFBeUIsSUFBSSxpQ0FBaUM7Q0FDekUsQ0FBQztBQUVGOzs7Ozs7O0dBT0c7QUFDSCxNQUFNLE9BQU8sZ0JBQWdCO0lBQ1YsU0FBUyxHQUFHLGFBQWEsQ0FBQztJQUMxQixVQUFVLEdBQUcsRUFBRSxDQUFDLENBQUMsV0FBVztJQUM1QixTQUFTLEdBQUcsRUFBRSxDQUFDLENBQUMsV0FBVztJQUMzQixlQUFlLEdBQUcsRUFBRSxDQUFDLENBQUMsV0FBVztJQUUxQyxNQUFNLENBQW1CO0lBQ3pCLGFBQWEsQ0FBVTtJQUN2QixhQUFhLEdBQVksS0FBSyxDQUFDO0lBRXZDOzs7O09BSUc7SUFDSCxZQUFZLE1BQWtDO1FBQzVDLElB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TSxDQUFDLElBQUksQ0FBQyw4REFBOEQsQ0FBQyxDQUFDO0lBQzlFLENBQUM7SUFFRDs7O09BR0c7SUFDSCxLQUFLLENBQUMsT0FBTztRQUNYLElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQztJQUNyQixDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFBhdHRlcm4gRW5jcnlwdGlvbiBTZXJ2aWNlIGZvciBNZW1vcnkgU2VjdXJpdHlcbiAqXG4gKiBQYXJ0IG9mIElzc3VlICMxMzIxIFBoYXNlIDI6IE1lbW9yeSBTZWN1cml0eSBBcmNoaXRlY3R1cmVcbiAqXG4gKiBQVVJQT1NFOlxuICogRW5jcnlwdHMgZGFuZ2Vyb3VzIHBhdHRlcm5zIGV4dHJhY3RlZCBmcm9tIEZMQUdHRUQgbWVtb3JpZXMgdXNpbmcgQUVTLTI1Ni1HQ00uXG4gKiBFbnN1cmVzIHBhdHRlcm5zIGFyZSBuZXZlciBzdG9yZWQgaW4gcGxhaW4gdGV4dCB3aGlsZSByZW1haW5pbmcgYWNjZXNzaWJsZVxuICogZm9yIGF1dGhvcml6ZWQgc2VjdXJpdHkgcmVzZWFyY2ggb3V0c2lkZSBMTE0gY29udGV4dHMuXG4gKlxuICogQVJDSElURUNUVVJFOlxuICogLSBVc2VzIEFFUy0yNTYtR0NNIGZvciBhdXRoZW50aWNhdGVkIGVuY3J5cHRpb25cbiAqIC0gRGVyaXZlcyBrZXlzIGZyb20gRE9MTEhPVVNFX0VOQ1JZUFRJT05fU0VDUkVUIHZpYSBQQktERjJcbiAqIC0gR2VuZXJhdGVzIHVuaXF1ZSBJViBmb3IgZWFjaCBlbmNyeXB0aW9uIG9wZXJhdGlvblxuICogLSBQcm92aWRlcyBHQ00gYXV0aGVudGljYXRpb24gdGFncyBmb3IgaW50ZWdyaXR5IHZlcmlmaWNhdGlvblxuICpcbiAqIFNFQ1VSSVRZOlxuICogLSBQYXR0ZXJucyBlbmNyeXB0ZWQgYXQgcmVzdFxuICogLSBEZWNyeXB0aW9uIG9ubHkgb3V0c2lkZSBMTE0gY29udGV4dFxuICogLSBBbGwgZGVjcnlwdGlvbiBhdHRlbXB0cyBsb2dnZWRcbiAqIC0gS2V5IHJvdGF0aW9uIHN1cHBvcnRlZFxuICpcbiAqIFJFRkFDVE9SIE5PVEU6XG4gKiBDb252ZXJ0ZWQgZnJvbSBzdGF0aWMgY2xhc3MgdG8gaW5zdGFuY2UtYmFzZWQgZm9yIERJIGFyY2hpdGVjdHVyZSBjb21wYXRpYmlsaXR5LlxuICpcbiAqIEBtb2R1bGUgUGF0dGVybkVuY3J5cHRvclxuICovXG5cbmltcG9ydCB7IHJhbmRvbUJ5dGVzLCBjcmVhdGVDaXBoZXJpdiwgY3JlYXRlRGVjaXBoZXJpdiwgcGJrZGYyIH0gZnJvbSAnbm9kZTpjcnlwdG8nO1xuaW1wb3J0IHsgbG9nZ2VyIH0gZnJvbSAnLi4vLi4vdXRpbHMvbG9nZ2VyLmpzJztcbmltcG9ydCB7IGVudiB9IGZyb20gJy4uLy4uL2NvbmZpZy9lbnYuanMnO1xuXG4vKipcbiAqIEVuY3J5cHRlZCBwYXR0ZXJuIHN0cnVjdHVyZVxuICogQ29udGFpbnMgY2lwaGVydGV4dCwgYWxnb3JpdGhtIG1ldGFkYXRhLCBhbmQgYXV0aGVudGljYXRpb24gZGF0YVxuICovXG5leHBvcnQgaW50ZXJmYWNlIEVuY3J5cHRlZFBhdHRlcm4ge1xuICAvKiogQmFzZTY0LWVuY29kZWQgZW5jcnlwdGVkIGRhdGEgKi9cbiAgZW5jcnlwdGVkRGF0YTogc3RyaW5nO1xuXG4gIC8qKiBFbmNyeXB0aW9uIGFsZ29yaXRobSAoYWx3YXlzICdhZXMtMjU2LWdjbScpICovXG4gIGFsZ29yaXRobTogJ2Flcy0yNTYtZ2NtJztcblxuICAvKiogQmFzZTY0LWVuY29kZWQgaW5pdGlhbGl6YXRpb24gdmVjdG9yICovXG4gIGl2OiBzdHJpbmc7XG5cbiAgLyoqIEJhc2U2NC1lbmNvZGVkIEdDTSBhdXRoZW50aWNhdGlvbiB0YWcgZm9yIGludGVncml0eSAqL1xuICBhdXRoVGFnOiBzdHJpbmc7XG59XG5cbi8qKlxuICogQ29uZmlndXJhdGlvbiBmb3IgcGF0dGVybiBlbmNyeXB0aW9uXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgRW5jcnlwdGlvbkNvbmZpZyB7XG4gIC8qKiBFbmFibGUgZW5jcnlwdGlvbiAoZGVmYXVsdDogdHJ1ZSBpbiBwcm9kdWN0aW9uKSAqL1xuICBlbmFibGVkOiBib29sZWFuO1xuXG4gIC8qKiBFbmNyeXB0aW9uIHNlY3JldCAoZnJvbSBlbnZpcm9ubWVudCkgKi9cbiAgc2VjcmV0Pzogc3RyaW5nO1xuXG4gIC8qKiBQQktERjIgaXRlcmF0aW9ucyAoZGVmYXVsdDogMTAwMDAwKSAqL1xuICBpdGVyYXRpb25zOiBudW1iZXI7XG5cbiAgLyoqIFNhbHQgZm9yIGtleSBkZXJpdmF0aW9uIChkZWZhdWx0OiAnZG9sbGhvdXNlLXBhdHRlcm4tZW5jcnlwdGlvbi12MScpICovXG4gIHNhbHQ6IHN0cmluZztcbn1cblxuLyoqXG4gKiBEZWZhdWx0IGVuY3J5cHRpb24gY29uZmlndXJhdGlvblxuICovXG5jb25zdCBERUZBVUxUX0NPTkZJRzogRW5jcnlwdGlvbkNvbmZpZyA9IHtcbiAgLy8gSG9ub3IgRE9MTEhPVVNFX0RJU0FCTEVfRU5DUllQVElPTiBlbnZpcm9ubWVudCB2YXJpYWJsZVxuICAvLyBJZiBleHBsaWNpdGx5IGRpc2FibGVkLCByZXNwZWN0IHRoYXQgc2V0dGluZyByZWdhcmRsZXNzIG9mIE5PREVfRU5WXG4gIGVuYWJsZWQ6IGVudi5ET0xMSE9VU0VfRElTQUJMRV9FTkNSWVBUSU9OXG4gICAgPyBmYWxzZVxuICAgIDogZW52Lk5PREVfRU5WID09PSAncHJvZHVjdGlvbicsXG4gIHNlY3JldDogZW52LkRPTExIT1VTRV9FTkNSWVBUSU9OX1NFQ1JFVCxcbiAgaXRlcmF0aW9uczogMTAwMDAwLFxuICAvLyBTRUNVUklUWSBGSVg6IENvbmZpZ3VyYWJsZSBzYWx0IHRvIHByZXZlbnQgcmFpbmJvdyB0YWJsZSBhdHRhY2tzICgjMTczMylcbiAgLy8gRmFsbHMgYmFjayB0byBkZWZhdWx0IHdpdGggc3RhcnR1cCB3YXJuaW5nIGlmIG5vdCBjb25maWd1cmVkXG4gIHNhbHQ6IGVudi5ET0xMSE9VU0VfRU5DUllQVElPTl9TQUxUIHx8ICdkb2xsaG91c2UtcGF0dGVybi1lbmNyeXB0aW9uLXYxJyxcbn07XG5cbi8qKlxuICogUGF0dGVybkVuY3J5cHRvciBzZXJ2aWNlXG4gKlxuICogSGFuZGxlcyBlbmNyeXB0aW9uIGFuZCBkZWNyeXB0aW9uIG9mIGRhbmdlcm91cyBwYXR0ZXJucyB1c2luZyBBRVMtMjU2LUdDTS5cbiAqIFByb3ZpZGVzIGF1dGhlbnRpY2F0ZWQgZW5jcnlwdGlvbiB3aXRoIGludGVncml0eSBwcm90ZWN0aW9uIHZpYSBHQ00gbW9kZS5cbiAqXG4gKiBESS1DT01QQVRJQkxFOiBJbnN0YW5jZS1iYXNlZCBzZXJ2aWNlIGZvciBkZXBlbmRlbmN5IGluamVjdGlvbi5cbiAqL1xuZXhwb3J0IGNsYXNzIFBhdHRlcm5FbmNyeXB0b3Ige1xuICBwcml2YXRlIHJlYWRvbmx5IEFMR09SSVRITSA9ICdhZXMtMjU2LWdjbSc7XG4gIHByaXZhdGUgcmVhZG9ubHkgS0VZX0xFTkdUSCA9IDMyOyAvLyAyNTYgYml0c1xuICBwcml2YXRlIHJlYWRvbmx5IElWX0xFTkdUSCA9IDE2OyAvLyAxMjggYml0c1xuICBwcml2YXRlIHJlYWRvbmx5IEFVVEhfVEFHX0xFTkdUSCA9IDE2OyAvLyAxMjggYml0c1xuXG4gIHByaXZhdGUgY29uZmlnOiBFbmNyeXB0aW9uQ29uZmlnO1xuICBwcml2YXRlIGVuY3J5cHRpb25LZXk/OiBCdWZmZXI7XG4gIHByaXZhdGUgaXNJbml0aWFsaXplZDogYm9vbGVhbiA9IGZhbHNlO1xuXG4gIC8qKlxuICAgKiBDcmVhdGUgYSBuZXcgUGF0dGVybkVuY3J5cHRvciBpbnN0YW5jZVxuICAgKlxuICAgKiBAcGFyYW0gY29uZmlnIC0gT3B0aW9uYWwgY29uZmlndXJhdGlvbiBvdmVycmlkZXNcbiAgICovXG4gIGNvbnN0cnVjdG9yKGNvbmZpZz86IFBhcnRpYWw8RW5jcnlwdGlvbkNvbmZpZz4pIHtcbiAgICB0aGlzLmNvbmZpZyA9IHsgLi4uREVGQVVMVF9DT05GSUcsIC4uLmNvbmZpZyB9O1xuICB9XG5cbiAgLyoqXG4gICAqIEluaXRpYWxpemUgZW5jcnlwdGlvbiB3aXRoIGNvbmZpZ3VyYXRpb25cbiAgICpcbiAgICogQHBhcmFtIGNvbmZpZyAtIE9wdGlvbmFsIGNvbmZpZ3VyYXRpb24gb3ZlcnJpZGVzXG4gICAqIEB0aHJvd3MgRXJyb3IgaWYgZW5jcnlwdGlvbiBzZWNyZXQgaXMgbm90IHByb3ZpZGVkIHdoZW4gZW5hYmxlZFxuICAgKi9cbiAgYXN5bmMgaW5pdGlhbGl6ZShjb25maWc/OiBQYXJ0aWFsPEVuY3J5cHRpb25Db25maWc+KTogUHJvbWlzZTx2b2lkPiB7XG4gICAgaWYgKGNvbmZpZykge1xuICAgICAgdGhpcy5jb25maWcgPSB7IC4uLnRoaXMuY29uZmlnLCAuLi5jb25maWcgfTtcbiAgICB9XG5cbiAgICBpZiAoIXRoaXMuY29uZmlnLmVuYWJsZWQpIHtcbiAgICAgIGNvbnN0IHJlYXNvbiA9IGVudi5ET0xMSE9VU0VfRElTQUJMRV9FTkNSWVBUSU9OXG4gICAgICAgID8gJ2V4cGxpY2l0bHkgZGlzYWJsZWQgdmlhIERPTExIT1VTRV9ESVNBQkxFX0VOQ1JZUFRJT04gZW52aXJvbm1lbnQgdmFyaWFibGUnXG4gICAgICAgIDogYGRpc2FibGVkIChOT0RFX0VOVj0ke2Vudi5OT0RFX0VOVn0pYDtcbiAgICAgIGxvZ2dlci5pbmZvKGBQYXR0ZXJuIGVuY3J5cHRpb24gJHtyZWFzb259YCk7XG4gICAgICB0aGlzLmlzSW5pdGlhbGl6ZWQgPSB0cnVlO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGlmICghdGhpcy5jb25maWcuc2VjcmV0KSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICdET0xMSE9VU0VfRU5DUllQVElPTl9TRUNSRVQgZW52aXJvbm1lbnQgdmFyaWFibGUgaXMgcmVxdWlyZWQgd2hlbiBlbmNyeXB0aW9uIGlzIGVuYWJsZWQnXG4gICAgICApO1xuICAgIH1cblxuICAgIC8vIFNFQy0wNSAoIzE3MzMpOiBXYXJuIGlmIHVzaW5nIHRoZSBzdGF0aWMgZGVmYXVsdCBzYWx0XG4gICAgaWYgKCFlbnYuRE9MTEhPVVNFX0VOQ1JZUFRJT05fU0FMVCkge1xuICAgICAgbG9nZ2VyLndhcm4oXG4gICAgICAgICfimqDvuI8gIFBhdHRlcm4gZW5jcnlwdGlvbiB1c2luZyBkZWZhdWx0IHNhbHQuIFNldCBET0xMSE9VU0VfRU5DUllQVElPTl9TQUxUIHRvIGEgcmFuZG9tIDMyKyBjaGFyYWN0ZXIgc3RyaW5nICcgK1xuICAgICAgICAnKGUuZy4gYG9wZW5zc2wgcmFuZCAtaGV4IDMyYCkgZm9yIHN0cm9uZ2VyIHByb3RlY3Rpb24gYWdhaW5zdCByYWluYm93IHRhYmxlIGF0dGFja3MuJ1xuICAgICAgKTtcbiAgICB9XG5cbiAgICBsb2dnZXIuaW5mbygnSW5pdGlhbGl6aW5nIHBhdHRlcm4gZW5jcnlwdGlvbicsIHtcbiAgICAgIGFsZ29yaXRobTogdGhpcy5BTEdPUklUSE0sXG4gICAgICBpdGVyYXRpb25zOiB0aGlzLmNvbmZpZy5pdGVyYXRpb25zLFxuICAgIH0pO1xuXG4gICAgLy8gRGVyaXZlIGVuY3J5cHRpb24ga2V5IGZyb20gc2VjcmV0XG4gICAgdGhpcy5lbmNyeXB0aW9uS2V5ID0gYXdhaXQgdGhpcy5kZXJpdmVLZXkoXG4gICAgICB0aGlzLmNvbmZpZy5zZWNyZXQsXG4gICAgICB0aGlzLmNvbmZpZy5zYWx0LFxuICAgICAgdGhpcy5jb25maWcuaXRlcmF0aW9uc1xuICAgICk7XG5cbiAgICB0aGlzLmlzSW5pdGlhbGl6ZWQgPSB0cnVlO1xuICAgIGxvZ2dlci5pbmZvKCdQYXR0ZXJuIGVuY3J5cHRpb24gaW5pdGlhbGl6ZWQgc3VjY2Vzc2Z1bGx5Jyk7XG4gIH1cblxuICAvKipcbiAgICogRW5jcnlwdCBhIHBhdHRlcm4gdXNpbmcgQUVTLTI1Ni1HQ01cbiAgICpcbiAgICogQHBhcmFtIHBhdHRlcm4gLSBQbGFpbiB0ZXh0IHBhdHRlcm4gdG8gZW5jcnlwdFxuICAgKiBAcmV0dXJucyBFbmNyeXB0ZWQgcGF0dGVybiB3aXRoIG1ldGFkYXRhXG4gICAqIEB0aHJvd3MgRXJyb3IgaWYgZW5jcnlwdGlvbiBub3QgaW5pdGlhbGl6ZWQgb3IgcGF0dGVybiBpcyBlbXB0eVxuICAgKi9cbiAgZW5jcnlwdChwYXR0ZXJuOiBzdHJpbmcpOiBFbmNyeXB0ZWRQYXR0ZXJuIHtcbiAgICBpZiAoIXRoaXMuaXNJbml0aWFsaXplZCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdQYXR0ZXJuRW5jcnlwdG9yIG5vdCBpbml0aWFsaXplZC4gQ2FsbCBpbml0aWFsaXplKCkgZmlyc3QuJyk7XG4gICAgfVxuXG4gICAgaWYgKCF0aGlzLmNvbmZpZy5lbmFibGVkKSB7XG4gICAgICAvLyBFbmNyeXB0aW9uIGRpc2FibGVkIOKAlCByZXR1cm4gbW9jayBzdHJ1Y3R1cmUgd2l0aCBNT0NLOiBwcmVmaXggKCMxNzMzKVxuICAgICAgLy8gc28gaXQgY2Fubm90IGJlIGNvbmZ1c2VkIHdpdGggcmVhbCBjaXBoZXJ0ZXh0XG4gICAgICBsb2dnZXIuZGVidWcoJ0VuY3J5cHRpb24gZGlzYWJsZWQsIHJldHVybmluZyBtb2NrIHN0cnVjdHVyZScpO1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgZW5jcnlwdGVkRGF0YTogYE1PQ0s6JHtCdWZmZXIuZnJvbShwYXR0ZXJuKS50b1N0cmluZygnYmFzZTY0Jyl9YCxcbiAgICAgICAgYWxnb3JpdGhtOiB0aGlzLkFMR09SSVRITSxcbiAgICAgICAgaXY6ICcnLFxuICAgICAgICBhdXRoVGFnOiAnJyxcbiAgICAgIH07XG4gICAgfVxuXG4gICAgaWYgKCFwYXR0ZXJuIHx8IHBhdHRlcm4ubGVuZ3RoID09PSAwKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0Nhbm5vdCBlbmNyeXB0IGVtcHR5IHBhdHRlcm4nKTtcbiAgICB9XG5cbiAgICBpZiAoIXRoaXMuZW5jcnlwdGlvbktleSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdFbmNyeXB0aW9uIGtleSBub3QgYXZhaWxhYmxlJyk7XG4gICAgfVxuXG4gICAgbG9nZ2VyLmRlYnVnKCdFbmNyeXB0aW5nIHBhdHRlcm4nLCB7XG4gICAgICBwYXR0ZXJuTGVuZ3RoOiBwYXR0ZXJuLmxlbmd0aCxcbiAgICB9KTtcblxuICAgIHRyeSB7XG4gICAgICAvLyBHZW5lcmF0ZSByYW5kb20gSVYgZm9yIHRoaXMgZW5jcnlwdGlvblxuICAgICAgY29uc3QgaXYgPSByYW5kb21CeXRlcyh0aGlzLklWX0xFTkdUSCk7XG5cbiAgICAgIC8vIENyZWF0ZSBjaXBoZXIgd2l0aCBBRVMtMjU2LUdDTVxuICAgICAgY29uc3QgY2lwaGVyID0gY3JlYXRlQ2lwaGVyaXYodGhpcy5BTEdPUklUSE0sIHRoaXMuZW5jcnlwdGlvbktleSwgaXYpO1xuXG4gICAgICAvLyBFbmNyeXB0IHRoZSBwYXR0ZXJuXG4gICAgICBsZXQgZW5jcnlwdGVkID0gY2lwaGVyLnVwZGF0ZShwYXR0ZXJuLCAndXRmOCcsICdiYXNlNjQnKTtcbiAgICAgIGVuY3J5cHRlZCArPSBjaXBoZXIuZmluYWwoJ2Jhc2U2NCcpO1xuXG4gICAgICAvLyBHZXQgYXV0aGVudGljYXRpb24gdGFnIGZyb20gR0NNIG1vZGVcbiAgICAgIGNvbnN0IGF1dGhUYWcgPSBjaXBoZXIuZ2V0QXV0aFRhZygpO1xuXG4gICAgICBsb2dnZXIuZGVidWcoJ1BhdHRlcm4gZW5jcnlwdGVkIHN1Y2Nlc3NmdWxseScsIHtcbiAgICAgICAgaXZMZW5ndGg6IGl2Lmxlbmd0aCxcbiAgICAgICAgYXV0aFRhZ0xlbmd0aDogYXV0aFRhZy5sZW5ndGgsXG4gICAgICAgIGVuY3J5cHRlZExlbmd0aDogZW5jcnlwdGVkLmxlbmd0aCxcbiAgICAgIH0pO1xuXG4gICAgICByZXR1cm4ge1xuICAgICAgICBlbmNyeXB0ZWREYXRhOiBlbmNyeXB0ZWQsXG4gICAgICAgIGFsZ29yaXRobTogdGhpcy5BTEdPUklUSE0sXG4gICAgICAgIGl2OiBpdi50b1N0cmluZygnYmFzZTY0JyksXG4gICAgICAgIGF1dGhUYWc6IGF1dGhUYWcudG9TdHJpbmcoJ2Jhc2U2NCcpLFxuICAgICAgfTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgbG9nZ2VyLmVycm9yKCdGYWlsZWQgdG8gZW5jcnlwdCBwYXR0ZXJuJywgeyBlcnJvciB9KTtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgUGF0dGVybiBlbmNyeXB0aW9uIGZhaWxlZDogJHtlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3IubWVzc2FnZSA6ICdVbmtub3duIGVycm9yJ31gKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogRGVjcnlwdCBhbiBlbmNyeXB0ZWQgcGF0dGVyblxuICAgKlxuICAgKiBAcGFyYW0gZW5jcnlwdGVkIC0gRW5jcnlwdGVkIHBhdHRlcm4gc3RydWN0dXJlXG4gICAqIEByZXR1cm5zIERlY3J5cHRlZCBwbGFpbiB0ZXh0IHBhdHRlcm5cbiAgICogQHRocm93cyBFcnJvciBpZiBkZWNyeXB0aW9uIGZhaWxzIG9yIGF1dGhlbnRpY2F0aW9uIGZhaWxzXG4gICAqL1xuICBkZWNyeXB0KGVuY3J5cHRlZDogRW5jcnlwdGVkUGF0dGVybik6IHN0cmluZyB7XG4gICAgaWYgKCF0aGlzLmlzSW5pdGlhbGl6ZWQpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignUGF0dGVybkVuY3J5cHRvciBub3QgaW5pdGlhbGl6ZWQuIENhbGwgaW5pdGlhbGl6ZSgpIGZpcnN0LicpO1xuICAgIH1cblxuICAgIGlmICghdGhpcy5jb25maWcuZW5hYmxlZCkge1xuICAgICAgLy8gRW5jcnlwdGlvbiBkaXNhYmxlZCDigJQgZGVjb2RlIG1vY2sgZGF0YSwgc3RyaXBwaW5nIE1PQ0s6IHByZWZpeCAoIzE3MzMpXG4gICAgICBsb2dnZXIuZGVidWcoJ0VuY3J5cHRpb24gZGlzYWJsZWQsIGRlY29kaW5nIG1vY2sgc3RydWN0dXJlJyk7XG4gICAgICBjb25zdCBkYXRhID0gZW5jcnlwdGVkLmVuY3J5cHRlZERhdGEuc3RhcnRzV2l0aCgnTU9DSzonKVxuICAgICAgICA/IGVuY3J5cHRlZC5lbmNyeXB0ZWREYXRhLnNsaWNlKDUpXG4gICAgICAgIDogZW5jcnlwdGVkLmVuY3J5cHRlZERhdGE7XG4gICAgICByZXR1cm4gQnVmZmVyLmZyb20oZGF0YSwgJ2Jhc2U2NCcpLnRvU3RyaW5nKCd1dGY4Jyk7XG4gICAgfVxuXG4gICAgaWYgKCF0aGlzLmVuY3J5cHRpb25LZXkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignRW5jcnlwdGlvbiBrZXkgbm90IGF2YWlsYWJsZScpO1xuICAgIH1cblxuICAgIGlmICghZW5jcnlwdGVkLmVuY3J5cHRlZERhdGEgfHwgIWVuY3J5cHRlZC5pdiB8fCAhZW5jcnlwdGVkLmF1dGhUYWcpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignSW52YWxpZCBlbmNyeXB0ZWQgcGF0dGVybjogbWlzc2luZyByZXF1aXJlZCBmaWVsZHMnKTtcbiAgICB9XG5cbiAgICBsb2dnZXIuZGVidWcoJ0RlY3J5cHRpbmcgcGF0dGVybicsIHtcbiAgICAgIGFsZ29yaXRobTogZW5jcnlwdGVkLmFsZ29yaXRobSxcbiAgICB9KTtcblxuICAgIHRyeSB7XG4gICAgICAvLyBEZWNvZGUgYmFzZTY0IGNvbXBvbmVudHNcbiAgICAgIGNvbnN0IGl2ID0gQnVmZmVyLmZyb20oZW5jcnlwdGVkLml2LCAnYmFzZTY0Jyk7XG4gICAgICBjb25zdCBhdXRoVGFnID0gQnVmZmVyLmZyb20oZW5jcnlwdGVkLmF1dGhUYWcsICdiYXNlNjQnKTtcblxuICAgICAgLy8gQ3JlYXRlIGRlY2lwaGVyIHdpdGggQUVTLTI1Ni1HQ01cbiAgICAgIGNvbnN0IGRlY2lwaGVyID0gY3JlYXRlRGVjaXBoZXJpdih0aGlzLkFMR09SSVRITSwgdGhpcy5lbmNyeXB0aW9uS2V5LCBpdik7XG5cbiAgICAgIC8vIFNldCBhdXRoZW50aWNhdGlvbiB0YWcgZm9yIHZlcmlmaWNhdGlvblxuICAgICAgZGVjaXBoZXIuc2V0QXV0aFRhZyhhdXRoVGFnKTtcblxuICAgICAgLy8gRGVjcnlwdCB0aGUgcGF0dGVyblxuICAgICAgbGV0IGRlY3J5cHRlZCA9IGRlY2lwaGVyLnVwZGF0ZShlbmNyeXB0ZWQuZW5jcnlwdGVkRGF0YSwgJ2Jhc2U2NCcsICd1dGY4Jyk7XG4gICAgICBkZWNyeXB0ZWQgKz0gZGVjaXBoZXIuZmluYWwoJ3V0ZjgnKTtcblxuICAgICAgbG9nZ2VyLmRlYnVnKCdQYXR0ZXJuIGRlY3J5cHRlZCBzdWNjZXNzZnVsbHknLCB7XG4gICAgICAgIGRlY3J5cHRlZExlbmd0aDogZGVjcnlwdGVkLmxlbmd0aCxcbiAgICAgIH0pO1xuXG4gICAgICByZXR1cm4gZGVjcnlwdGVkO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBsb2dnZXIuZXJyb3IoJ0ZhaWxlZCB0byBkZWNyeXB0IHBhdHRlcm4nLCB7IGVycm9yIH0pO1xuXG4gICAgICAvLyBBdXRoZW50aWNhdGlvbiBmYWlsdXJlIG1lYW5zIGRhdGEgd2FzIHRhbXBlcmVkIHdpdGhcbiAgICAgIC8vIENoZWNrIGZvciB2YXJpb3VzIEdDTSBhdXRoZW50aWNhdGlvbiBlcnJvciBtZXNzYWdlc1xuICAgICAgaWYgKGVycm9yIGluc3RhbmNlb2YgRXJyb3IpIHtcbiAgICAgICAgY29uc3QgZXJyb3JNc2cgPSBlcnJvci5tZXNzYWdlLnRvTG93ZXJDYXNlKCk7XG4gICAgICAgIGlmIChcbiAgICAgICAgICBlcnJvck1zZy5pbmNsdWRlcygnYXV0aCcpIHx8XG4gICAgICAgICAgZXJyb3JNc2cuaW5jbHVkZXMoJ2RlY3J5cHQnKSB8fFxuICAgICAgICAgIGVycm9yTXNnLmluY2x1ZGVzKCd1bnN1cHBvcnRlZCBzdGF0ZScpIHx8XG4gICAgICAgICAgZXJyb3JNc2cuaW5jbHVkZXMoJ3VuYWJsZSB0byBhdXRoZW50aWNhdGUnKVxuICAgICAgICApIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ1BhdHRlcm4gZGVjcnlwdGlvbiBmYWlsZWQ6IEF1dGhlbnRpY2F0aW9uIHRhZyBtaXNtYXRjaCAoZGF0YSBtYXkgYmUgY29ycnVwdGVkIG9yIHRhbXBlcmVkKScpO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIHRocm93IG5ldyBFcnJvcihgUGF0dGVybiBkZWNyeXB0aW9uIGZhaWxlZDogJHtlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3IubWVzc2FnZSA6ICdVbmtub3duIGVycm9yJ31gKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogRGVyaXZlIGVuY3J5cHRpb24ga2V5IGZyb20gc2VjcmV0IHVzaW5nIFBCS0RGMlxuICAgKlxuICAgKiBAcGFyYW0gc2VjcmV0IC0gTWFzdGVyIHNlY3JldCBmcm9tIGVudmlyb25tZW50XG4gICAqIEBwYXJhbSBzYWx0IC0gU2FsdCBmb3Iga2V5IGRlcml2YXRpb25cbiAgICogQHBhcmFtIGl0ZXJhdGlvbnMgLSBOdW1iZXIgb2YgUEJLREYyIGl0ZXJhdGlvbnNcbiAgICogQHJldHVybnMgRGVyaXZlZCBlbmNyeXB0aW9uIGtleVxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBkZXJpdmVLZXkoXG4gICAgc2VjcmV0OiBzdHJpbmcsXG4gICAgc2FsdDogc3RyaW5nLFxuICAgIGl0ZXJhdGlvbnM6IG51bWJlclxuICApOiBQcm9taXNlPEJ1ZmZlcj4ge1xuICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICBwYmtkZjIoXG4gICAgICAgIHNlY3JldCxcbiAgICAgICAgc2FsdCxcbiAgICAgICAgaXRlcmF0aW9ucyxcbiAgICAgICAgdGhpcy5LRVlfTEVOR1RILFxuICAgICAgICAnc2hhMjU2JyxcbiAgICAgICAgKGVyciwgZGVyaXZlZEtleSkgPT4ge1xuICAgICAgICAgIGlmIChlcnIpIHtcbiAgICAgICAgICAgIGxvZ2dlci5lcnJvcignS2V5IGRlcml2YXRpb24gZmFpbGVkJywgeyBlcnJvcjogZXJyIH0pO1xuICAgICAgICAgICAgcmVqZWN0KG5ldyBFcnJvcihgRmFpbGVkIHRvIGRlcml2ZSBlbmNyeXB0aW9uIGtleTogJHtlcnIubWVzc2FnZX1gKSk7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGxvZ2dlci5kZWJ1ZygnRW5jcnlwdGlvbiBrZXkgZGVyaXZlZCBzdWNjZXNzZnVsbHknLCB7XG4gICAgICAgICAgICAgIGtleUxlbmd0aDogZGVyaXZlZEtleS5sZW5ndGgsXG4gICAgICAgICAgICAgIGl0ZXJhdGlvbnMsXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIHJlc29sdmUoZGVyaXZlZEtleSk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICApO1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIENoZWNrIGlmIGVuY3J5cHRpb24gaXMgZW5hYmxlZFxuICAgKlxuICAgKiBAcmV0dXJucyB0cnVlIGlmIGVuY3J5cHRpb24gaXMgZW5hYmxlZCBhbmQgaW5pdGlhbGl6ZWRcbiAgICovXG4gIGlzRW5hYmxlZCgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5jb25maWcuZW5hYmxlZCAmJiB0aGlzLmlzSW5pdGlhbGl6ZWQ7XG4gIH1cblxuICAvKipcbiAgICogR2V0IGVuY3J5cHRpb24gY29uZmlndXJhdGlvbiBzdGF0dXNcbiAgICpcbiAgICogQHJldHVybnMgQ29uZmlndXJhdGlvbiBzdGF0dXMgKHdpdGhvdXQgZXhwb3Npbmcgc2VjcmV0cylcbiAgICovXG4gIGdldFN0YXR1cygpIHtcbiAgICByZXR1cm4ge1xuICAgICAgZW5hYmxlZDogdGhpcy5jb25maWcuZW5hYmxlZCxcbiAgICAgIGluaXRpYWxpemVkOiB0aGlzLmlzSW5pdGlhbGl6ZWQsXG4gICAgICBhbGdvcml0aG06IHRoaXMuQUxHT1JJVEhNLFxuICAgICAga2V5TGVuZ3RoOiB0aGlzLktFWV9MRU5HVEgsXG4gICAgICBpdGVyYXRpb25zOiB0aGlzLmNvbmZpZy5pdGVyYXRpb25zLFxuICAgICAgaGFzU2VjcmV0OiAhIXRoaXMuY29uZmlnLnNlY3JldCxcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIFNlY3VyZWx5IGNsZWFyIGVuY3J5cHRpb24ga2V5IGZyb20gbWVtb3J5XG4gICAqIFNFQ1VSSVRZIEZJWDogT3ZlcndyaXRlcyBrZXkgYnVmZmVyIHdpdGggemVyb3MgYmVmb3JlIHJlbGVhc2luZ1xuICAgKlxuICAgKiBUaGlzIHByZXZlbnRzIGtleSByZWNvdmVyeSBmcm9tIG1lbW9yeSBkdW1wcyBvciBwcm9jZXNzIGluc3BlY3Rpb24uXG4gICAqIFNob3VsZCBiZSBjYWxsZWQgd2hlbiBlbmNyeXB0aW9uIGlzIG5vIGxvbmdlciBuZWVkZWQuXG4gICAqL1xuICBwcml2YXRlIHNlY3VyZUtleUNsZWFyKCk6IHZvaWQge1xuICAgIGlmICh0aGlzLmVuY3J5cHRpb25LZXkpIHtcbiAgICAgIC8vIE92ZXJ3cml0ZSBrZXkgbWF0ZXJpYWwgd2l0aCB6ZXJvc1xuICAgICAgdGhpcy5lbmNyeXB0aW9uS2V5LmZpbGwoMCk7XG4gICAgICB0aGlzLmVuY3J5cHRpb25LZXkgPSB1bmRlZmluZWQ7XG4gICAgICBsb2dnZXIuZGVidWcoJ0VuY3J5cHRpb24ga2V5IHNlY3VyZWx5IGNsZWFyZWQgZnJvbSBtZW1vcnknKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUmVzZXQgdGhlIGVuY3J5cHRvciAodXNlZnVsIGZvciB0ZXN0aW5nKVxuICAgKiBTRUNVUklUWSBGSVg6IE5vdyBwZXJmb3JtcyBzZWN1cmUga2V5IGNsZWFyaW5nXG4gICAqIFdBUk5JTkc6IFRoaXMgd2lsbCBzZWN1cmVseSBjbGVhciB0aGUgZW5jcnlwdGlvbiBrZXkgZnJvbSBtZW1vcnlcbiAgICovXG4gIHJlc2V0KCk6IHZvaWQge1xuICAgIHRoaXMuc2VjdXJlS2V5Q2xlYXIoKTtcbiAgICB0aGlzLmlzSW5pdGlhbGl6ZWQgPSBmYWxzZTtcbiAgICB0aGlzLmNvbmZpZyA9IHsgLi4uREVGQVVMVF9DT05GSUcgfTtcbiAgICBsb2dnZXIuZGVidWcoJ1BhdHRlcm5FbmNyeXB0b3IgcmVzZXQnKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTZWN1cmVseSByZXNldCB0aGUgZW5jcnlwdG9yIGFuZCBjbGVhciBhbGwgc2Vuc2l0aXZlIGRhdGFcbiAgICogU0VDVVJJVFk6IEV4cGxpY2l0bHkgY2xlYXJzIGVuY3J5cHRpb24ga2V5cyBmcm9tIG1lbW9yeVxuICAgKlxuICAgKiBVc2UgdGhpcyB3aGVuOlxuICAgKiAtIFNodXR0aW5nIGRvd24gdGhlIGFwcGxpY2F0aW9uXG4gICAqIC0gUm90YXRpbmcgZW5jcnlwdGlvbiBrZXlzXG4gICAqIC0gUmVzcG9uZGluZyB0byBzZWN1cml0eSBpbmNpZGVudHNcbiAgICovXG4gIHNlY3VyZVJlc2V0KCk6IHZvaWQge1xuICAgIHRoaXMuc2VjdXJlS2V5Q2xlYXIoKTtcbiAgICB0aGlzLmlzSW5pdGlhbGl6ZWQgPSBmYWxzZTtcbiAgICB0aGlzLmNvbmZpZyA9IHsgLi4uREVGQVVMVF9DT05GSUcgfTtcbiAgICBsb2dnZXIuaW5mbygnUGF0dGVybkVuY3J5cHRvciBzZWN1cmVseSByZXNldCAtIGFsbCBzZW5zaXRpdmUgZGF0YSBjbGVhcmVkJyk7XG4gIH1cblxuICAvKipcbiAgICogRGlzcG9zZSBvZiB0aGUgZW5jcnlwdG9yIGFuZCBzZWN1cmVseSBjbGVhciBhbGwgZGF0YVxuICAgKiBJbXBsZW1lbnRzIGNsZWFudXAgZm9yIHByb3BlciBESSBsaWZlY3ljbGUgbWFuYWdlbWVudFxuICAgKi9cbiAgYXN5bmMgZGlzcG9zZSgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0aGlzLnNlY3VyZVJlc2V0KCk7XG4gIH1cbn1cbiJdfQ==