UNPKG

tink-crypto

Version:

A multi-language, cross-platform library that provides cryptographic APIs that are secure, easy to use correctly, and hard(er) to misuse.

117 lines 13.7 kB
/** * @license * Copyright 2023 Google LLC * SPDX-License-Identifier: Apache-2.0 */ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import { SecurityException } from '../../../exception/security_exception'; import { PbHpkePublicKey, PbKeyData } from '../../../internal/proto'; import { HybridEncrypt } from '../hybrid_encrypt'; import { HpkeEncrypt } from './hpke_encrypt'; import * as hpkeValidators from './hpke_validators'; /** * This class corresponds to the 'HpkePrivateKeyFactory', but it does not * support key generation. The 'HpkePrivateKeyFactor'y' should be use to * generate new HPKE key pairs. * @final */ class HpkePublicKeyFactory { newKey(keyFormat) { throw new SecurityException('This operation is not supported for public keys. ' + 'Use HpkePrivateKeyManager to generate new keys.'); } newKeyData(serializedKeyFormat) { throw new SecurityException('This operation is not supported for public keys. ' + 'Use HpkePrivateKeyManager to generate new keys.'); } } /** * Key manager that produces new instances of {@link HpkeEncrypt} primitives. * @final */ export class HpkePublicKeyManager { constructor() { this.keyFactory = new HpkePublicKeyFactory(); } /** * Constructs an instance of the 'HybridEncrypt' primitive for a given HPKE * public key. */ getPrimitive(primitiveType, key) { return __awaiter(this, void 0, void 0, function* () { if (primitiveType !== this.getPrimitiveType()) { throw new SecurityException('Requested primitive type which is not ' + 'supported by this key manager.'); } const keyProto = HpkePublicKeyManager.getKeyProto(key); hpkeValidators.validatePublicKey(keyProto, this.getVersion()); return HpkeEncrypt.createHpkeEncrypt(keyProto); }); } /** Returns true if this KeyManager supports 'HpkePublicKey' key type. */ doesSupport(keyType) { return keyType === this.getKeyType(); } /** Returns the URL which identifies the keys managed by this KeyManager. */ getKeyType() { return HpkePublicKeyManager.KEY_TYPE; } /** * Returns the type of primitive which can be generated by this KeyManager, * i.e. 'HybridEncrypt' */ getPrimitiveType() { return HpkePublicKeyManager.SUPPORTED_PRIMITIVE; } getVersion() { return HpkePublicKeyManager.VERSION; } getKeyFactory() { return this.keyFactory; } static getKeyProto(keyMaterial) { if (keyMaterial instanceof PbKeyData) { return HpkePublicKeyManager.getKeyProtoFromKeyData(keyMaterial); } if (keyMaterial instanceof PbHpkePublicKey) { return keyMaterial; } throw new SecurityException('Key type is not supported. This key manager supports ' + HpkePublicKeyManager.KEY_TYPE + '.'); } static getKeyProtoFromKeyData(keyData) { if (keyData.getTypeUrl() !== HpkePublicKeyManager.KEY_TYPE) { throw new SecurityException('Key type ' + keyData.getTypeUrl() + ' is not supported. This key ' + 'manager supports ' + HpkePublicKeyManager.KEY_TYPE + '.'); } let key; try { key = PbHpkePublicKey.deserializeBinary(keyData.getValue()); } catch (e) { throw new SecurityException('Input cannot be parsed as ' + HpkePublicKeyManager.KEY_TYPE + ' key-proto.'); } /** * Throws the same generic error to avoid discrepancies between different * versions. */ if (!key.getParams() || !key.getPublicKey()) { throw new SecurityException('Input cannot be parsed as ' + HpkePublicKeyManager.KEY_TYPE + ' key-proto.'); } return key; } } HpkePublicKeyManager.KEY_TYPE = 'type.googleapis.com/google.crypto.tink.HpkePublicKey'; HpkePublicKeyManager.SUPPORTED_PRIMITIVE = HybridEncrypt; HpkePublicKeyManager.VERSION = 0; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaHBrZV9wdWJsaWNfa2V5X21hbmFnZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9oeWJyaWQvaW50ZXJuYWwvaHBrZS9ocGtlX3B1YmxpY19rZXlfbWFuYWdlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7OztHQUlHOzs7Ozs7Ozs7O0FBRUgsT0FBTyxFQUFDLGlCQUFpQixFQUFDLE1BQU0sdUNBQXVDLENBQUM7QUFFeEUsT0FBTyxFQUFDLGVBQWUsRUFBRSxTQUFTLEVBQVksTUFBTSx5QkFBeUIsQ0FBQztBQUU5RSxPQUFPLEVBQUMsYUFBYSxFQUFDLE1BQU0sbUJBQW1CLENBQUM7QUFFaEQsT0FBTyxFQUFDLFdBQVcsRUFBQyxNQUFNLGdCQUFnQixDQUFDO0FBQzNDLE9BQU8sS0FBSyxjQUFjLE1BQU0sbUJBQW1CLENBQUM7QUFFcEQ7Ozs7O0dBS0c7QUFDSCxNQUFNLG9CQUFvQjtJQUN4QixNQUFNLENBQUMsU0FBK0I7UUFDcEMsTUFBTSxJQUFJLGlCQUFpQixDQUN2QixtREFBbUQ7WUFDbkQsaURBQWlELENBQUMsQ0FBQztJQUN6RCxDQUFDO0lBRUQsVUFBVSxDQUFDLG1CQUErQjtRQUN4QyxNQUFNLElBQUksaUJBQWlCLENBQ3ZCLG1EQUFtRDtZQUNuRCxpREFBaUQsQ0FBQyxDQUFDO0lBQ3pELENBQUM7Q0FDRjtBQUVEOzs7R0FHRztBQUNILE1BQU0sT0FBTyxvQkFBb0I7SUFBakM7UUFLRSxlQUFVLEdBQUcsSUFBSSxvQkFBb0IsRUFBRSxDQUFDO0lBK0UxQyxDQUFDO0lBOUVDOzs7T0FHRztJQUNHLFlBQVksQ0FDZCxhQUE4QyxFQUM5QyxHQUF3Qjs7WUFDMUIsSUFBSSxhQUFhLEtBQUssSUFBSSxDQUFDLGdCQUFnQixFQUFFLEVBQUU7Z0JBQzdDLE1BQU0sSUFBSSxpQkFBaUIsQ0FDdkIsd0NBQXdDO29CQUN4QyxnQ0FBZ0MsQ0FBQyxDQUFDO2FBQ3ZDO1lBQ0QsTUFBTSxRQUFRLEdBQW9CLG9CQUFvQixDQUFDLFdBQVcsQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUN4RSxjQUFjLENBQUMsaUJBQWlCLENBQUMsUUFBUSxFQUFFLElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQyxDQUFDO1lBQzlELE9BQU8sV0FBVyxDQUFDLGlCQUFpQixDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQ2pELENBQUM7S0FBQTtJQUNELHlFQUF5RTtJQUN6RSxXQUFXLENBQUMsT0FBZTtRQUN6QixPQUFPLE9BQU8sS0FBSyxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7SUFDdkMsQ0FBQztJQUNELDRFQUE0RTtJQUM1RSxVQUFVO1FBQ1IsT0FBTyxvQkFBb0IsQ0FBQyxRQUFRLENBQUM7SUFDdkMsQ0FBQztJQUNEOzs7T0FHRztJQUNILGdCQUFnQjtRQUNkLE9BQU8sb0JBQW9CLENBQUMsbUJBQW1CLENBQUM7SUFDbEQsQ0FBQztJQUVELFVBQVU7UUFDUixPQUFPLG9CQUFvQixDQUFDLE9BQU8sQ0FBQztJQUN0QyxDQUFDO0lBRUQsYUFBYTtRQUNYLE9BQU8sSUFBSSxDQUFDLFVBQVUsQ0FBQztJQUN6QixDQUFDO0lBRU8sTUFBTSxDQUFDLFdBQVcsQ0FBQyxXQUNTO1FBQ2xDLElBQUksV0FBVyxZQUFZLFNBQVMsRUFBRTtZQUNwQyxPQUFPLG9CQUFvQixDQUFDLHNCQUFzQixDQUFDLFdBQVcsQ0FBQyxDQUFDO1NBQ2pFO1FBQ0QsSUFBSSxXQUFXLFlBQVksZUFBZSxFQUFFO1lBQzFDLE9BQU8sV0FBVyxDQUFDO1NBQ3BCO1FBQ0QsTUFBTSxJQUFJLGlCQUFpQixDQUN2Qix1REFBdUQ7WUFDdkQsb0JBQW9CLENBQUMsUUFBUSxHQUFHLEdBQUcsQ0FBQyxDQUFDO0lBQzNDLENBQUM7SUFFTyxNQUFNLENBQUMsc0JBQXNCLENBQUMsT0FBa0I7UUFDdEQsSUFBSSxPQUFPLENBQUMsVUFBVSxFQUFFLEtBQUssb0JBQW9CLENBQUMsUUFBUSxFQUFFO1lBQzFELE1BQU0sSUFBSSxpQkFBaUIsQ0FDdkIsV0FBVyxHQUFHLE9BQU8sQ0FBQyxVQUFVLEVBQUUsR0FBRyw4QkFBOEI7Z0JBQ25FLG1CQUFtQixHQUFHLG9CQUFvQixDQUFDLFFBQVEsR0FBRyxHQUFHLENBQUMsQ0FBQztTQUNoRTtRQUNELElBQUksR0FBb0IsQ0FBQztRQUN6QixJQUFJO1lBQ0YsR0FBRyxHQUFHLGVBQWUsQ0FBQyxpQkFBaUIsQ0FBQyxPQUFPLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FBQztTQUM3RDtRQUFDLE9BQU8sQ0FBQyxFQUFFO1lBQ1YsTUFBTSxJQUFJLGlCQUFpQixDQUN2Qiw0QkFBNEIsR0FBRyxvQkFBb0IsQ0FBQyxRQUFRO2dCQUM1RCxhQUFhLENBQUMsQ0FBQztTQUNwQjtRQUNEOzs7V0FHRztRQUNILElBQUksQ0FBQyxHQUFHLENBQUMsU0FBUyxFQUFFLElBQUksQ0FBQyxHQUFHLENBQUMsWUFBWSxFQUFFLEVBQUU7WUFDM0MsTUFBTSxJQUFJLGlCQUFpQixDQUN2Qiw0QkFBNEIsR0FBRyxvQkFBb0IsQ0FBQyxRQUFRO2dCQUM1RCxhQUFhLENBQUMsQ0FBQztTQUNwQjtRQUNELE9BQU8sR0FBRyxDQUFDO0lBQ2IsQ0FBQzs7QUFqRk0sNkJBQVEsR0FBRyxzREFBc0QsQ0FBQztBQUNqRCx3Q0FBbUIsR0FBRyxhQUFhLENBQUM7QUFDckQsNEJBQU8sR0FBRyxDQUFDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEBsaWNlbnNlXG4gKiBDb3B5cmlnaHQgMjAyMyBHb29nbGUgTExDXG4gKiBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTIuMFxuICovXG5cbmltcG9ydCB7U2VjdXJpdHlFeGNlcHRpb259IGZyb20gJy4uLy4uLy4uL2V4Y2VwdGlvbi9zZWN1cml0eV9leGNlcHRpb24nO1xuaW1wb3J0ICogYXMga2V5TWFuYWdlciBmcm9tICcuLi8uLi8uLi9pbnRlcm5hbC9rZXlfbWFuYWdlcic7XG5pbXBvcnQge1BiSHBrZVB1YmxpY0tleSwgUGJLZXlEYXRhLCBQYk1lc3NhZ2V9IGZyb20gJy4uLy4uLy4uL2ludGVybmFsL3Byb3RvJztcbmltcG9ydCAqIGFzIHV0aWwgZnJvbSAnLi4vLi4vLi4vaW50ZXJuYWwvdXRpbCc7XG5pbXBvcnQge0h5YnJpZEVuY3J5cHR9IGZyb20gJy4uL2h5YnJpZF9lbmNyeXB0JztcblxuaW1wb3J0IHtIcGtlRW5jcnlwdH0gZnJvbSAnLi9ocGtlX2VuY3J5cHQnO1xuaW1wb3J0ICogYXMgaHBrZVZhbGlkYXRvcnMgZnJvbSAnLi9ocGtlX3ZhbGlkYXRvcnMnO1xuXG4vKipcbiAqIFRoaXMgY2xhc3MgY29ycmVzcG9uZHMgdG8gdGhlICdIcGtlUHJpdmF0ZUtleUZhY3RvcnknLCBidXQgaXQgZG9lcyBub3RcbiAqIHN1cHBvcnQga2V5IGdlbmVyYXRpb24uIFRoZSAnSHBrZVByaXZhdGVLZXlGYWN0b3IneScgc2hvdWxkIGJlIHVzZSB0b1xuICogZ2VuZXJhdGUgbmV3IEhQS0Uga2V5IHBhaXJzLlxuICogQGZpbmFsXG4gKi9cbmNsYXNzIEhwa2VQdWJsaWNLZXlGYWN0b3J5IGltcGxlbWVudHMga2V5TWFuYWdlci5LZXlGYWN0b3J5IHtcbiAgbmV3S2V5KGtleUZvcm1hdDogUGJNZXNzYWdlfFVpbnQ4QXJyYXkpOiBuZXZlciB7XG4gICAgdGhyb3cgbmV3IFNlY3VyaXR5RXhjZXB0aW9uKFxuICAgICAgICAnVGhpcyBvcGVyYXRpb24gaXMgbm90IHN1cHBvcnRlZCBmb3IgcHVibGljIGtleXMuICcgK1xuICAgICAgICAnVXNlIEhwa2VQcml2YXRlS2V5TWFuYWdlciB0byBnZW5lcmF0ZSBuZXcga2V5cy4nKTtcbiAgfVxuXG4gIG5ld0tleURhdGEoc2VyaWFsaXplZEtleUZvcm1hdDogVWludDhBcnJheSk6IG5ldmVyIHtcbiAgICB0aHJvdyBuZXcgU2VjdXJpdHlFeGNlcHRpb24oXG4gICAgICAgICdUaGlzIG9wZXJhdGlvbiBpcyBub3Qgc3VwcG9ydGVkIGZvciBwdWJsaWMga2V5cy4gJyArXG4gICAgICAgICdVc2UgSHBrZVByaXZhdGVLZXlNYW5hZ2VyIHRvIGdlbmVyYXRlIG5ldyBrZXlzLicpO1xuICB9XG59XG5cbi8qKlxuICogS2V5IG1hbmFnZXIgdGhhdCBwcm9kdWNlcyBuZXcgaW5zdGFuY2VzIG9mIHtAbGluayBIcGtlRW5jcnlwdH0gcHJpbWl0aXZlcy5cbiAqIEBmaW5hbFxuICovXG5leHBvcnQgY2xhc3MgSHBrZVB1YmxpY0tleU1hbmFnZXIgaW1wbGVtZW50c1xuICAgIGtleU1hbmFnZXIuS2V5TWFuYWdlcjxIeWJyaWRFbmNyeXB0PiB7XG4gIHN0YXRpYyBLRVlfVFlQRSA9ICd0eXBlLmdvb2dsZWFwaXMuY29tL2dvb2dsZS5jcnlwdG8udGluay5IcGtlUHVibGljS2V5JztcbiAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgU1VQUE9SVEVEX1BSSU1JVElWRSA9IEh5YnJpZEVuY3J5cHQ7XG4gIHN0YXRpYyBWRVJTSU9OID0gMDtcbiAga2V5RmFjdG9yeSA9IG5ldyBIcGtlUHVibGljS2V5RmFjdG9yeSgpO1xuICAvKipcbiAgICogQ29uc3RydWN0cyBhbiBpbnN0YW5jZSBvZiB0aGUgJ0h5YnJpZEVuY3J5cHQnIHByaW1pdGl2ZSBmb3IgYSBnaXZlbiBIUEtFXG4gICAqIHB1YmxpYyBrZXkuXG4gICAqL1xuICBhc3luYyBnZXRQcmltaXRpdmUoXG4gICAgICBwcmltaXRpdmVUeXBlOiB1dGlsLkNvbnN0cnVjdG9yPEh5YnJpZEVuY3J5cHQ+LFxuICAgICAga2V5OiBQYktleURhdGF8UGJNZXNzYWdlKSB7XG4gICAgaWYgKHByaW1pdGl2ZVR5cGUgIT09IHRoaXMuZ2V0UHJpbWl0aXZlVHlwZSgpKSB7XG4gICAgICB0aHJvdyBuZXcgU2VjdXJpdHlFeGNlcHRpb24oXG4gICAgICAgICAgJ1JlcXVlc3RlZCBwcmltaXRpdmUgdHlwZSB3aGljaCBpcyBub3QgJyArXG4gICAgICAgICAgJ3N1cHBvcnRlZCBieSB0aGlzIGtleSBtYW5hZ2VyLicpO1xuICAgIH1cbiAgICBjb25zdCBrZXlQcm90bzogUGJIcGtlUHVibGljS2V5ID0gSHBrZVB1YmxpY0tleU1hbmFnZXIuZ2V0S2V5UHJvdG8oa2V5KTtcbiAgICBocGtlVmFsaWRhdG9ycy52YWxpZGF0ZVB1YmxpY0tleShrZXlQcm90bywgdGhpcy5nZXRWZXJzaW9uKCkpO1xuICAgIHJldHVybiBIcGtlRW5jcnlwdC5jcmVhdGVIcGtlRW5jcnlwdChrZXlQcm90byk7XG4gIH1cbiAgLyoqIFJldHVybnMgdHJ1ZSBpZiB0aGlzIEtleU1hbmFnZXIgc3VwcG9ydHMgJ0hwa2VQdWJsaWNLZXknIGtleSB0eXBlLiAqL1xuICBkb2VzU3VwcG9ydChrZXlUeXBlOiBzdHJpbmcpIHtcbiAgICByZXR1cm4ga2V5VHlwZSA9PT0gdGhpcy5nZXRLZXlUeXBlKCk7XG4gIH1cbiAgLyoqIFJldHVybnMgdGhlIFVSTCB3aGljaCBpZGVudGlmaWVzIHRoZSBrZXlzIG1hbmFnZWQgYnkgdGhpcyBLZXlNYW5hZ2VyLiAqL1xuICBnZXRLZXlUeXBlKCkge1xuICAgIHJldHVybiBIcGtlUHVibGljS2V5TWFuYWdlci5LRVlfVFlQRTtcbiAgfVxuICAvKipcbiAgICogUmV0dXJucyB0aGUgdHlwZSBvZiBwcmltaXRpdmUgd2hpY2ggY2FuIGJlIGdlbmVyYXRlZCBieSB0aGlzIEtleU1hbmFnZXIsXG4gICAqIGkuZS4gJ0h5YnJpZEVuY3J5cHQnXG4gICAqL1xuICBnZXRQcmltaXRpdmVUeXBlKCkge1xuICAgIHJldHVybiBIcGtlUHVibGljS2V5TWFuYWdlci5TVVBQT1JURURfUFJJTUlUSVZFO1xuICB9XG5cbiAgZ2V0VmVyc2lvbigpIHtcbiAgICByZXR1cm4gSHBrZVB1YmxpY0tleU1hbmFnZXIuVkVSU0lPTjtcbiAgfVxuXG4gIGdldEtleUZhY3RvcnkoKSB7XG4gICAgcmV0dXJuIHRoaXMua2V5RmFjdG9yeTtcbiAgfVxuXG4gIHByaXZhdGUgc3RhdGljIGdldEtleVByb3RvKGtleU1hdGVyaWFsOiBQYktleURhdGF8XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBiTWVzc2FnZSk6IFBiSHBrZVB1YmxpY0tleSB7XG4gICAgaWYgKGtleU1hdGVyaWFsIGluc3RhbmNlb2YgUGJLZXlEYXRhKSB7XG4gICAgICByZXR1cm4gSHBrZVB1YmxpY0tleU1hbmFnZXIuZ2V0S2V5UHJvdG9Gcm9tS2V5RGF0YShrZXlNYXRlcmlhbCk7XG4gICAgfVxuICAgIGlmIChrZXlNYXRlcmlhbCBpbnN0YW5jZW9mIFBiSHBrZVB1YmxpY0tleSkge1xuICAgICAgcmV0dXJuIGtleU1hdGVyaWFsO1xuICAgIH1cbiAgICB0aHJvdyBuZXcgU2VjdXJpdHlFeGNlcHRpb24oXG4gICAgICAgICdLZXkgdHlwZSBpcyBub3Qgc3VwcG9ydGVkLiBUaGlzIGtleSBtYW5hZ2VyIHN1cHBvcnRzICcgK1xuICAgICAgICBIcGtlUHVibGljS2V5TWFuYWdlci5LRVlfVFlQRSArICcuJyk7XG4gIH1cblxuICBwcml2YXRlIHN0YXRpYyBnZXRLZXlQcm90b0Zyb21LZXlEYXRhKGtleURhdGE6IFBiS2V5RGF0YSk6IFBiSHBrZVB1YmxpY0tleSB7XG4gICAgaWYgKGtleURhdGEuZ2V0VHlwZVVybCgpICE9PSBIcGtlUHVibGljS2V5TWFuYWdlci5LRVlfVFlQRSkge1xuICAgICAgdGhyb3cgbmV3IFNlY3VyaXR5RXhjZXB0aW9uKFxuICAgICAgICAgICdLZXkgdHlwZSAnICsga2V5RGF0YS5nZXRUeXBlVXJsKCkgKyAnIGlzIG5vdCBzdXBwb3J0ZWQuIFRoaXMga2V5ICcgK1xuICAgICAgICAgICdtYW5hZ2VyIHN1cHBvcnRzICcgKyBIcGtlUHVibGljS2V5TWFuYWdlci5LRVlfVFlQRSArICcuJyk7XG4gICAgfVxuICAgIGxldCBrZXk6IFBiSHBrZVB1YmxpY0tleTtcbiAgICB0cnkge1xuICAgICAga2V5ID0gUGJIcGtlUHVibGljS2V5LmRlc2VyaWFsaXplQmluYXJ5KGtleURhdGEuZ2V0VmFsdWUoKSk7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgdGhyb3cgbmV3IFNlY3VyaXR5RXhjZXB0aW9uKFxuICAgICAgICAgICdJbnB1dCBjYW5ub3QgYmUgcGFyc2VkIGFzICcgKyBIcGtlUHVibGljS2V5TWFuYWdlci5LRVlfVFlQRSArXG4gICAgICAgICAgJyBrZXktcHJvdG8uJyk7XG4gICAgfVxuICAgIC8qKlxuICAgICAqIFRocm93cyB0aGUgc2FtZSBnZW5lcmljIGVycm9yIHRvIGF2b2lkIGRpc2NyZXBhbmNpZXMgYmV0d2VlbiBkaWZmZXJlbnRcbiAgICAgKiB2ZXJzaW9ucy5cbiAgICAgKi9cbiAgICBpZiAoIWtleS5nZXRQYXJhbXMoKSB8fCAha2V5LmdldFB1YmxpY0tleSgpKSB7XG4gICAgICB0aHJvdyBuZXcgU2VjdXJpdHlFeGNlcHRpb24oXG4gICAgICAgICAgJ0lucHV0IGNhbm5vdCBiZSBwYXJzZWQgYXMgJyArIEhwa2VQdWJsaWNLZXlNYW5hZ2VyLktFWV9UWVBFICtcbiAgICAgICAgICAnIGtleS1wcm90by4nKTtcbiAgICB9XG4gICAgcmV0dXJuIGtleTtcbiAgfVxufVxuIl19