@nebulae/angular-ble
Version:
A Web Bluetooth (Bluetooth Low Energy) module for angular (v2+)
531 lines (530 loc) • 46.1 kB
JavaScript
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,uselessCode} checked by tsc
*/
import * as aes from 'aes-js';
import { Injectable } from '@angular/core';
import * as i0 from "@angular/core";
var CypherAesService = /** @class */ (function () {
function CypherAesService() {
this.masterKey = [];
this.initialVector = [];
this.encryptMethod = 'CBC';
this.isStaticInitialVector = true;
this.isConfigExecuted = false;
}
/**
* Initial config used to initalice all required params
* @param masterKey key used to encrypt and decrypt
* @param initialVector vector used to encrypt abd decrypt except when ECB encrypt method is used
* @param encryptMethod type of encrypt method is used, the possible options are: CBC, CTR, CFB, OFB, ECB
* @param additionalEncryptMethodParams configuration params used by the selected encrypt method.
* Note: if the method CTR or CFB is used this param is required otherwise is an optinal param.
* By CTR require the param counter and by CFB require the param segmentSize
* @param isStaticInitialVector defines if the initial vector is changed or not when the data are encrypted or not
*/
/**
* Initial config used to initalice all required params
* @param {?} masterKey key used to encrypt and decrypt
* @param {?=} initialVector vector used to encrypt abd decrypt except when ECB encrypt method is used
* @param {?=} encryptMethod type of encrypt method is used, the possible options are: CBC, CTR, CFB, OFB, ECB
* @param {?=} additionalEncryptMethodParams configuration params used by the selected encrypt method.
* Note: if the method CTR or CFB is used this param is required otherwise is an optinal param.
* By CTR require the param counter and by CFB require the param segmentSize
* @param {?=} isStaticInitialVector defines if the initial vector is changed or not when the data are encrypted or not
* @return {?}
*/
CypherAesService.prototype.config = /**
* Initial config used to initalice all required params
* @param {?} masterKey key used to encrypt and decrypt
* @param {?=} initialVector vector used to encrypt abd decrypt except when ECB encrypt method is used
* @param {?=} encryptMethod type of encrypt method is used, the possible options are: CBC, CTR, CFB, OFB, ECB
* @param {?=} additionalEncryptMethodParams configuration params used by the selected encrypt method.
* Note: if the method CTR or CFB is used this param is required otherwise is an optinal param.
* By CTR require the param counter and by CFB require the param segmentSize
* @param {?=} isStaticInitialVector defines if the initial vector is changed or not when the data are encrypted or not
* @return {?}
*/
function (masterKey, initialVector, encryptMethod, additionalEncryptMethodParams, isStaticInitialVector) {
if (initialVector === void 0) { initialVector = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; }
if (encryptMethod === void 0) { encryptMethod = 'CBC'; }
if (additionalEncryptMethodParams === void 0) { additionalEncryptMethodParams = {}; }
if (isStaticInitialVector === void 0) { isStaticInitialVector = true; }
this.isConfigExecuted = true;
this.masterKey = masterKey;
this.initialVector = initialVector;
this.encryptMethod = encryptMethod;
this.isStaticInitialVector = isStaticInitialVector;
this.additionalEncryptMethodParams = additionalEncryptMethodParams;
if (!isStaticInitialVector) {
this.enctrypMethodInstance = this.generateEncryptMethodInstance();
}
};
/**
* Encrypt the data using the encrypt method previously configured
* @param dataArrayBuffer data to encrypt
*/
/**
* Encrypt the data using the encrypt method previously configured
* @param {?} dataArrayBuffer data to encrypt
* @return {?}
*/
CypherAesService.prototype.encrypt = /**
* Encrypt the data using the encrypt method previously configured
* @param {?} dataArrayBuffer data to encrypt
* @return {?}
*/
function (dataArrayBuffer) {
if (!this.isConfigExecuted) {
throw new Error('Must configurate cypher-aes before call this method, use the method config()');
}
if (this.encryptMethod === 'CBC' || this.encryptMethod === 'ECB') {
dataArrayBuffer = this.addPadding(dataArrayBuffer);
}
return this.isStaticInitialVector
? this.generateEncryptMethodInstance().encrypt(dataArrayBuffer)
: this.enctrypMethodInstance.encrypt(dataArrayBuffer);
};
/**
* Decrypt the data using the encrypt method previously configured
* @param dataArrayBuffer data to decrypt
*/
/**
* Decrypt the data using the encrypt method previously configured
* @param {?} dataArrayBuffer data to decrypt
* @return {?}
*/
CypherAesService.prototype.decrypt = /**
* Decrypt the data using the encrypt method previously configured
* @param {?} dataArrayBuffer data to decrypt
* @return {?}
*/
function (dataArrayBuffer) {
if (!this.isConfigExecuted) {
throw new Error('Must configurate cypher-aes before call this method, use the method config()');
}
return this.isStaticInitialVector
? this.generateEncryptMethodInstance().decrypt(dataArrayBuffer)
: this.enctrypMethodInstance.decrypt(dataArrayBuffer);
};
/**
* Change the current initalVector
* @param initialVector new initalVector
*/
/**
* Change the current initalVector
* @param {?} initialVector new initalVector
* @return {?}
*/
CypherAesService.prototype.changeInitialVector = /**
* Change the current initalVector
* @param {?} initialVector new initalVector
* @return {?}
*/
function (initialVector) {
if (!this.isStaticInitialVector) {
this.enctrypMethodInstance = this.generateEncryptMethodInstance();
}
this.initialVector = initialVector;
};
/**
* Change the current encyptMethod
* @param encryptMethod new encryptMethod
*/
/**
* Change the current encyptMethod
* @param {?} encryptMethod new encryptMethod
* @return {?}
*/
CypherAesService.prototype.changeEncryptMethod = /**
* Change the current encyptMethod
* @param {?} encryptMethod new encryptMethod
* @return {?}
*/
function (encryptMethod) {
if (!this.isStaticInitialVector) {
this.enctrypMethodInstance = this.generateEncryptMethodInstance();
}
this.encryptMethod = encryptMethod;
};
/**
* Change the current isStaticInitialVector
* @param isStaticInitialVector new isStaticInitalVector
*/
/**
* Change the current isStaticInitialVector
* @param {?} isStaticInitialVector new isStaticInitalVector
* @return {?}
*/
CypherAesService.prototype.changeStaticInitialVector = /**
* Change the current isStaticInitialVector
* @param {?} isStaticInitialVector new isStaticInitalVector
* @return {?}
*/
function (isStaticInitialVector) {
if (!isStaticInitialVector) {
this.enctrypMethodInstance = this.generateEncryptMethodInstance();
}
this.isStaticInitialVector = isStaticInitialVector;
};
/**
* Change the current masterKey
* @param masterKey new masterKey
*/
/**
* Change the current masterKey
* @param {?} masterKey new masterKey
* @return {?}
*/
CypherAesService.prototype.changeMasterKey = /**
* Change the current masterKey
* @param {?} masterKey new masterKey
* @return {?}
*/
function (masterKey) {
if (!this.isStaticInitialVector) {
this.enctrypMethodInstance = this.generateEncryptMethodInstance();
}
this.masterKey = masterKey;
};
/**
* Add padding to the list
* @param {?} arrayBuffer
* @return {?}
*/
CypherAesService.prototype.addPadding = /**
* Add padding to the list
* @param {?} arrayBuffer
* @return {?}
*/
function (arrayBuffer) {
/** @type {?} */
var paddingLength = Math.ceil(Array.from(arrayBuffer).length / 16) * 16;
/** @type {?} */
var paddingList = new Array(paddingLength - Array.from(arrayBuffer).length).fill(0);
return new Uint8Array(Array.from(arrayBuffer).concat(paddingList));
};
/**
* generate a instance of the encrypt method using the current method configured
* @return {?}
*/
CypherAesService.prototype.generateEncryptMethodInstance = /**
* generate a instance of the encrypt method using the current method configured
* @return {?}
*/
function () {
/** @type {?} */
var enctrypMethodInstance;
switch (this.encryptMethod) {
case 'CBC':
enctrypMethodInstance = new aes.ModeOfOperation.cbc(this.masterKey, this.initialVector);
break;
case 'CTR':
if (!this.additionalEncryptMethodParams.counter) {
throw new Error('additionalEncryptMethodParams.counter is required to use encrypt method CTR');
}
enctrypMethodInstance = new aes.ModeOfOperation.ctr(this.masterKey, this.initialVector, new aes.Counter(this.additionalEncryptMethodParams.counter));
break;
case 'CFB':
if (!this.additionalEncryptMethodParams.segmentSize) {
throw new Error('additionalEncryptMethodParams.segmentSize is required to use encrypt method CFB');
}
enctrypMethodInstance = new aes.ModeOfOperation.cfb(this.masterKey, this.initialVector, this.additionalEncryptMethodParams.segmentSize);
break;
case 'OFB':
enctrypMethodInstance = new aes.ModeOfOperation.ofb(this.masterKey, this.initialVector);
break;
case 'ECB':
enctrypMethodInstance = new aes.ModeOfOperation.ecb(this.masterKey);
break;
}
return enctrypMethodInstance;
};
// #region UTILS
/**
* Convert the text to bytes
* @param text Text to convert
*/
/**
* Convert the text to bytes
* @param {?} text Text to convert
* @return {?}
*/
CypherAesService.prototype.textToBytes = /**
* Convert the text to bytes
* @param {?} text Text to convert
* @return {?}
*/
function (text) {
return aes.utils.utf8.toBytes(text);
};
/**
* Convert the bytes to text
* @param bytes Bytes to convert
*/
/**
* Convert the bytes to text
* @param {?} bytes Bytes to convert
* @return {?}
*/
CypherAesService.prototype.bytesToText = /**
* Convert the bytes to text
* @param {?} bytes Bytes to convert
* @return {?}
*/
function (bytes) {
return aes.utils.utf8.fromBytes(bytes);
};
/**
* Convert the bytes to hex
* @param bytes bytes to convert
*/
/**
* Convert the bytes to hex
* @param {?} bytes bytes to convert
* @return {?}
*/
CypherAesService.prototype.bytesTohex = /**
* Convert the bytes to hex
* @param {?} bytes bytes to convert
* @return {?}
*/
function (bytes) {
return aes.utils.hex.fromBytes(bytes);
};
/**
* Convert the hex to bytes
* @param hex Hex to convert
*/
/**
* Convert the hex to bytes
* @param {?} hex Hex to convert
* @return {?}
*/
CypherAesService.prototype.hexToBytes = /**
* Convert the hex to bytes
* @param {?} hex Hex to convert
* @return {?}
*/
function (hex) {
return aes.utils.hex.toBytes(hex);
};
// #endregion
/**
* @param {?} key
* @return {?}
*/
CypherAesService.prototype.generateSubkeys = /**
* @param {?} key
* @return {?}
*/
function (key) {
/** @type {?} */
var const_Zero = new Uint8Array(16);
/** @type {?} */
var const_Rb = new Buffer('00000000000000000000000000000087', 'hex');
/** @type {?} */
var enctrypMethodInstance = new aes.ModeOfOperation.cbc(key, new Uint8Array(16));
/** @type {?} */
var lEncrypted = enctrypMethodInstance.encrypt(const_Zero);
/** @type {?} */
var l = new Buffer(this.bytesTohex(lEncrypted), 'hex');
/** @type {?} */
var subkey1 = this.bitShiftLeft(l);
// tslint:disable-next-line:no-bitwise
if (l[0] & 0x80) {
subkey1 = this.xor(subkey1, const_Rb);
}
/** @type {?} */
var subkey2 = this.bitShiftLeft(subkey1);
// tslint:disable-next-line:no-bitwise
if (subkey1[0] & 0x80) {
subkey2 = this.xor(subkey2, const_Rb);
}
return { subkey1: subkey1, subkey2: subkey2 };
};
/**
* @param {?} key
* @param {?} message
* @return {?}
*/
CypherAesService.prototype.aesCmac = /**
* @param {?} key
* @param {?} message
* @return {?}
*/
function (key, message) {
console.log('INICIA CIFRADO!!!!!!!!!!!!!!!!');
/** @type {?} */
var subkeys = this.generateSubkeys(key);
/** @type {?} */
var blockCount = Math.ceil(message.length / 16);
/** @type {?} */
var lastBlockCompleteFlag;
/** @type {?} */
var lastBlock;
/** @type {?} */
var lastBlockIndex;
if (blockCount === 0) {
blockCount = 1;
lastBlockCompleteFlag = false;
}
else {
lastBlockCompleteFlag = message.length % 16 === 0;
}
lastBlockIndex = blockCount - 1;
if (lastBlockCompleteFlag) {
lastBlock = this.xor(this.getMessageBlock(message, lastBlockIndex), subkeys.subkey1);
}
else {
lastBlock = this.xor(this.getPaddedMessageBlock(message, lastBlockIndex), subkeys.subkey2);
}
/** @type {?} */
var x = new Buffer('00000000000000000000000000000000', 'hex');
/** @type {?} */
var y;
/** @type {?} */
var enctrypMethodInstance;
for (var index = 0; index < lastBlockIndex; index++) {
enctrypMethodInstance = new aes.ModeOfOperation.cbc(key, new Uint8Array(16));
y = this.xor(x, this.getMessageBlock(message, index));
/** @type {?} */
var xEncrypted = enctrypMethodInstance.encrypt(y);
console.log('X normal ===============> ', this.bytesTohex(y));
console.log('X encrypted ==============> ', this.bytesTohex(xEncrypted));
x = new Buffer(this.bytesTohex(xEncrypted), 'hex');
}
y = this.xor(lastBlock, x);
enctrypMethodInstance = new aes.ModeOfOperation.cbc(key, new Uint8Array(16));
/** @type {?} */
var yEncrypted = enctrypMethodInstance.encrypt(y);
console.log('Y normal ==============> ', this.bytesTohex(y));
console.log('Y encrypted ==============> ', this.bytesTohex(yEncrypted));
return yEncrypted;
};
/**
* @param {?} message
* @param {?} blockIndex
* @return {?}
*/
CypherAesService.prototype.getMessageBlock = /**
* @param {?} message
* @param {?} blockIndex
* @return {?}
*/
function (message, blockIndex) {
/** @type {?} */
var block = new Buffer(16);
/** @type {?} */
var start = blockIndex * 16;
/** @type {?} */
var end = start + 16;
/** @type {?} */
var blockI = 0;
for (var i = start; i < end; i++) {
block[blockI] = message[i];
blockI++;
}
return block;
};
/**
* @param {?} message
* @param {?} blockIndex
* @return {?}
*/
CypherAesService.prototype.getPaddedMessageBlock = /**
* @param {?} message
* @param {?} blockIndex
* @return {?}
*/
function (message, blockIndex) {
/** @type {?} */
var block = new Buffer(16);
/** @type {?} */
var start = blockIndex * 16;
/** @type {?} */
var end = message.length;
block.fill(0);
/** @type {?} */
var blockI = 0;
for (var i = start; i < end; i++) {
block[blockI] = message[i];
blockI++;
}
block[end - start] = 0x80;
return block;
};
/**
* @param {?} buffer
* @return {?}
*/
CypherAesService.prototype.bitShiftLeft = /**
* @param {?} buffer
* @return {?}
*/
function (buffer) {
/** @type {?} */
var shifted = new Buffer(buffer.length);
/** @type {?} */
var last = buffer.length - 1;
for (var index = 0; index < last; index++) {
// tslint:disable-next-line:no-bitwise
shifted[index] = buffer[index] << 1;
// tslint:disable-next-line:no-bitwise
if (buffer[index + 1] & 0x80) {
shifted[index] += 0x01;
}
}
// tslint:disable-next-line:no-bitwise
shifted[last] = buffer[last] << 1;
return shifted;
};
/**
* @param {?} bufferA
* @param {?} bufferB
* @return {?}
*/
CypherAesService.prototype.xor = /**
* @param {?} bufferA
* @param {?} bufferB
* @return {?}
*/
function (bufferA, bufferB) {
/** @type {?} */
var length = Math.min(bufferA.length, bufferB.length);
/** @type {?} */
var output = new Buffer(length);
for (var index = 0; index < length; index++) {
// tslint:disable-next-line:no-bitwise
output[index] = bufferA[index] ^ bufferB[index];
}
return output;
};
CypherAesService.decorators = [
{ type: Injectable, args: [{
providedIn: 'root'
},] },
];
/** @nocollapse */
CypherAesService.ctorParameters = function () { return []; };
/** @nocollapse */ CypherAesService.ngInjectableDef = i0.defineInjectable({ factory: function CypherAesService_Factory() { return new CypherAesService(); }, token: CypherAesService, providedIn: "root" });
return CypherAesService;
}());
export { CypherAesService };
if (false) {
/** @type {?} */
CypherAesService.prototype.masterKey;
/** @type {?} */
CypherAesService.prototype.initialVector;
/** @type {?} */
CypherAesService.prototype.encryptMethod;
/** @type {?} */
CypherAesService.prototype.isStaticInitialVector;
/** @type {?} */
CypherAesService.prototype.enctrypMethodInstance;
/** @type {?} */
CypherAesService.prototype.isConfigExecuted;
/** @type {?} */
CypherAesService.prototype.additionalEncryptMethodParams;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3lwaGVyLWFlcy5zZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6Im5nOi8vQG5lYnVsYWUvYW5ndWxhci1ibGUvIiwic291cmNlcyI6WyJsaWIvY3lwaGVyL2N5cGhlci1hZXMuc2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7O0FBQ0EsT0FBTyxLQUFLLEdBQUcsTUFBTSxRQUFRLENBQUM7QUFDOUIsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGVBQWUsQ0FBQzs7O0lBYXpDO3lCQVBvQixFQUFFOzZCQUNFLEVBQUU7NkJBQ0YsS0FBSztxQ0FDRyxJQUFJO2dDQUVULEtBQUs7S0FFaEI7SUFDaEI7Ozs7Ozs7OztPQVNHOzs7Ozs7Ozs7Ozs7SUFDSCxpQ0FBTTs7Ozs7Ozs7Ozs7SUFBTixVQUNFLFNBQVMsRUFDVCxhQUFnRSxFQUNoRSxhQUFxQixFQUNyQiw2QkFBa0MsRUFDbEMscUJBQTRCO1FBSDVCLDhCQUFBLEVBQUEsaUJBQWlCLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUNoRSw4QkFBQSxFQUFBLHFCQUFxQjtRQUNyQiw4Q0FBQSxFQUFBLGtDQUFrQztRQUNsQyxzQ0FBQSxFQUFBLDRCQUE0QjtRQUU1QixJQUFJLENBQUMsZ0JBQWdCLEdBQUcsSUFBSSxDQUFDO1FBQzdCLElBQUksQ0FBQyxTQUFTLEdBQUcsU0FBUyxDQUFDO1FBQzNCLElBQUksQ0FBQyxhQUFhLEdBQUcsYUFBYSxDQUFDO1FBQ25DLElBQUksQ0FBQyxhQUFhLEdBQUcsYUFBYSxDQUFDO1FBQ25DLElBQUksQ0FBQyxxQkFBcUIsR0FBRyxxQkFBcUIsQ0FBQztRQUNuRCxJQUFJLENBQUMsNkJBQTZCLEdBQUcsNkJBQTZCLENB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QUM7UUFDbEMsR0FBRyxDQUFDLENBQUMsSUFBSSxLQUFLLEdBQUcsQ0FBQyxFQUFFLEtBQUssR0FBRyxNQUFNLEVBQUUsS0FBSyxFQUFFLEVBQUUsQ0FBQzs7WUFFNUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxHQUFHLE9BQU8sQ0FBQyxLQUFLLENBQUMsR0FBRyxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUM7U0FDakQ7UUFDRCxNQUFNLENBQUMsTUFBTSxDQUFDO0tBQ2Y7O2dCQXBVRixVQUFVLFNBQUM7b0JBQ1YsVUFBVSxFQUFFLE1BQU07aUJBQ25COzs7OzsyQkFORDs7U0FPYSxnQkFBZ0IiLCJzb3VyY2VzQ29udGVudCI6WyJkZWNsYXJlIGNvbnN0IEJ1ZmZlcjtcbmltcG9ydCAqIGFzIGFlcyBmcm9tICdhZXMtanMnO1xuaW1wb3J0IHsgSW5qZWN0YWJsZSB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuXG5ASW5qZWN0YWJsZSh7XG4gIHByb3ZpZGVkSW46ICdyb290J1xufSlcbmV4cG9ydCBjbGFzcyBDeXBoZXJBZXNTZXJ2aWNlIHtcbiAgcHJpdmF0ZSBtYXN0ZXJLZXkgPSBbXTtcbiAgcHJpdmF0ZSBpbml0aWFsVmVjdG9yID0gW107XG4gIHByaXZhdGUgZW5jcnlwdE1ldGhvZCA9ICdDQkMnO1xuICBwcml2YXRlIGlzU3RhdGljSW5pdGlhbFZlY3RvciA9IHRydWU7XG4gIHByaXZhdGUgZW5jdHJ5cE1ldGhvZEluc3RhbmNlO1xuICBwcml2YXRlIGlzQ29uZmlnRXhlY3V0ZWQgPSBmYWxzZTtcbiAgcHJpdmF0ZSBhZGRpdGlvbmFsRW5jcnlwdE1ldGhvZFBhcmFtcztcbiAgY29uc3RydWN0b3IoKSB7fVxuICAvKipcbiAgICogSW5pdGlhbCBjb25maWcgdXNlZCB0byBpbml0YWxpY2UgYWxsIHJlcXVpcmVkIHBhcmFtc1xuICAgKiBAcGFyYW0gbWFzdGVyS2V5IGtleSB1c2VkIHRvIGVuY3J5cHQgYW5kIGRlY3J5cHRcbiAgICogQHBhcmFtIGluaXRpYWxWZWN0b3IgdmVjdG9yIHVzZWQgdG8gZW5jcnlwdCBhYmQgZGVjcnlwdCBleGNlcHQgd2hlbiBFQ0IgZW5jcnlwdCBtZXRob2QgaXMgdXNlZFxuICAgKiBAcGFyYW0gZW5jcnlwdE1ldGhvZCB0eXBlIG9mIGVuY3J5cHQgbWV0aG9kIGlzIHVzZWQsIHRoZSBwb3NzaWJsZSBvcHRpb25zIGFyZTogQ0JDLCBDVFIsIENGQiwgT0ZCLCBFQ0JcbiAgICogQHBhcmFtIGFkZGl0aW9uYWxFbmNyeXB0TWV0aG9kUGFyYW1zIGNvbmZpZ3VyYXRpb24gcGFyYW1zIHVzZWQgYnkgdGhlIHNlbGVjdGVkIGVuY3J5cHQgbWV0aG9kLlxuICAgKiBOb3RlOiBpZiB0aGUgbWV0aG9kIENUUiBvciBDRkIgaXMgdXNlZCB0aGlzIHBhcmFtIGlzIHJlcXVpcmVkIG90aGVyd2lzZSBpcyBhbiBvcHRpbmFsIHBhcmFtLlxuICAgKiBCeSBDVFIgcmVxdWlyZSB0aGUgcGFyYW0gY291bnRlciBhbmQgYnkgQ0ZCIHJlcXVpcmUgdGhlIHBhcmFtIHNlZ21lbnRTaXplXG4gICAqIEBwYXJhbSBpc1N0YXRpY0luaXRpYWxWZWN0b3IgZGVmaW5lcyBpZiB0aGUgaW5pdGlhbCB2ZWN0b3IgaXMgY2hhbmdlZCBvciBub3Qgd2hlbiB0aGUgZGF0YSBhcmUgZW5jcnlwdGVkIG9yIG5vdFxuICAgKi9cbiAgY29uZmlnKFxuICAgIG1hc3RlcktleSxcbiAgICBpbml0aWFsVmVjdG9yID0gWzAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDAsIDBdLFxuICAgIGVuY3J5cHRNZXRob2QgPSAnQ0JDJyxcbiAgICBhZGRpdGlvbmFsRW5jcnlwdE1ldGhvZFBhcmFtcyA9IHt9LFxuICAgIGlzU3RhdGljSW5pdGlhbFZlY3RvciA9IHRydWVcbiAgKSB7XG4gICAgdGhpcy5pc0NvbmZpZ0V4ZWN1dGVkID0gdHJ1ZTtcbiAgICB0aGlzLm1hc3RlcktleSA9IG1hc3RlcktleTtcbiAgICB0aGlzLmluaXRpYWxWZWN0b3IgPSBpbml0aWFsVmVjdG9yO1xuICAgIHRoaXMuZW5jcnlwdE1ldGhvZCA9IGVuY3J5cHRNZXRob2Q7XG4gICAgdGhpcy5pc1N0YXRpY0luaXRpYWxWZWN0b3IgPSBpc1N0YXRpY0luaXRpYWxWZWN0b3I7XG4gICAgdGhpcy5hZGRpdGlvbmFsRW5jcnlwdE1ldGhvZFBhcmFtcyA9IGFkZGl0aW9uYWxFbmNyeXB0TWV0aG9kUGFyYW1zO1xuICAgIGlmICghaXNTdGF0aWNJbml0aWFsVmVjdG9yKSB7XG4gICAgICB0aGlzLmVuY3RyeXBNZXRob2RJbnN0YW5jZSA9IHRoaXMuZ2VuZXJhdGVFbmNyeXB0TWV0aG9kSW5zdGFuY2UoKTtcbiAgICB9XG4gIH1cbiAgLyoqXG4gICAqIEVuY3J5cHQgdGhlIGRhdGEgdXNpbmcgdGhlIGVuY3J5cHQgbWV0aG9kIHByZXZpb3VzbHkgY29uZmlndXJlZFxuICAgKiBAcGFyYW0gZGF0YUFycmF5QnVmZmVyIGRhdGEgdG8gZW5jcnlwdFxuICAgKi9cbiAgZW5jcnlwdChkYXRhQXJyYXlCdWZmZXI6IFVpbnQ4QXJyYXkgfCBVaW50MTZBcnJheSB8IFVpbnQzMkFycmF5KSB7XG4gICAgaWYgKCF0aGlzLmlzQ29uZmlnRXhlY3V0ZWQpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgJ011c3QgY29uZmlndXJhdGUgY3lwaGVyLWFlcyBiZWZvcmUgY2FsbCB0aGlzIG1ldGhvZCwgdXNlIHRoZSBtZXRob2QgY29uZmlnKCknXG4gICAgICApO1xuICAgIH1cbiAgICBpZiAodGhpcy5lbmNyeXB0TWV0aG9kID09PSAnQ0JDJyB8fCB0aGlzLmVuY3J5cHRNZXRob2QgPT09ICdFQ0InKSB7XG4gICAgICBkYXRhQXJyYXlCdWZmZXIgPSB0aGlzLmFkZFBhZGRpbmcoZGF0YUFycmF5QnVmZmVyKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuaXNTdGF0aWNJbml0aWFsVmVjdG9yXG4gICAgICA/IHRoaXMuZ2VuZXJhdGVFbmNyeXB0TWV0aG9kSW5zdGFuY2UoKS5lbmNyeXB0KGRhdGFBcnJheUJ1ZmZlcilcbiAgICAgIDogdGhpcy5lbmN0cnlwTWV0aG9kSW5zdGFuY2UuZW5jcnlwdChkYXRhQXJyYXlCdWZmZXIpO1xuICB9XG4gIC8qKlxuICAgKiBEZWNyeXB0IHRoZSBkYXRhIHVzaW5nIHRoZSBlbmNyeXB0IG1ldGhvZCBwcmV2aW91c2x5IGNvbmZpZ3VyZWRcbiAgICogQHBhcmFtIGRhdGFBcnJheUJ1ZmZlciBkYXRhIHRvIGRlY3J5cHRcbiAgICovXG4gIGRlY3J5cHQoZGF0YUFycmF5QnVmZmVyOiBVaW50OEFycmF5IHwgVWludDE2QXJyYXkgfCBVaW50MzJBcnJheSkge1xuICAgIGlmICghdGhpcy5pc0NvbmZpZ0V4ZWN1dGVkKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICdNdXN0IGNvbmZpZ3VyYXRlIGN5cGhlci1hZXMgYmVmb3JlIGNhbGwgdGhpcyBtZXRob2QsIHVzZSB0aGUgbWV0aG9kIGNvbmZpZygpJ1xuICAgICAgKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuaXNTdGF0aWNJbml0aWFsVmVjdG9yXG4gICAgICA/IHRoaXMuZ2VuZXJhdGVFbmNyeXB0TWV0aG9kSW5zdGFuY2UoKS5kZWNyeXB0KGRhdGFBcnJheUJ1ZmZlcilcbiAgICAgIDogdGhpcy5lbmN0cnlwTWV0aG9kSW5zdGFuY2UuZGVjcnlwdChkYXRhQXJyYXlCdWZmZXIpO1xuICB9XG4gIC8qKlxuICAgKiBDaGFuZ2UgdGhlIGN1cnJlbnQgaW5pdGFsVmVjdG9yXG4gICAqIEBwYXJhbSBpbml0aWFsVmVjdG9yIG5ldyBpbml0YWxWZWN0b3JcbiAgICovXG4gIGNoYW5nZUluaXRpYWxWZWN0b3IoaW5pdGlhbFZlY3Rvcikge1xuICAgIGlmICghdGhpcy5pc1N0YXRpY0luaXRpYWxWZWN0b3IpIHtcbiAgICAgIHRoaXMuZW5jdHJ5cE1ldGhvZEluc3RhbmNlID0gdGhpcy5nZW5lcmF0ZUVuY3J5cHRNZXRob2RJbnN0YW5jZSgpO1xuICAgIH1cbiAgICB0aGlzLmluaXRpYWxWZWN0b3IgPSBpbml0aWFsVmVjdG9yO1xuICB9XG5cbiAgLyoqXG4gICAqIENoYW5nZSB0aGUgY3VycmVudCBlbmN5cHRNZXRob2RcbiAgICogQHBhcmFtIGVuY3J5cHRNZXRob2QgbmV3IGVuY3J5cHRNZXRob2RcbiAgICovXG4gIGNoYW5nZUVuY3J5cHRNZXRob2QoZW5jcnlwdE1ldGhvZCkge1xuICAgIGlmICghdGhpcy5pc1N0YXRpY0luaXRpYWxWZWN0b3IpIHtcbiAgICAgIHRoaXMuZW5jdHJ5cE1ldGhvZEluc3RhbmNlID0gdGhpcy5nZW5lcmF0ZUVuY3J5cHRNZXRob2RJbnN0YW5jZSgpO1xuICAgIH1cbiAgICB0aGlzLmVuY3J5cHRNZXRob2QgPSBlbmNyeXB0TWV0aG9kO1xuICB9XG5cbiAgLyoqXG4gICAqIENoYW5nZSB0aGUgY3VycmVudCBpc1N0YXRpY0luaXRpYWxWZWN0b3JcbiAgICogQHBhcmFtIGlzU3RhdGljSW5pdGlhbFZlY3RvciBuZXcgaXNTdGF0aWNJbml0YWxWZWN0b3JcbiAgICovXG4gIGNoYW5nZVN0YXRpY0luaXRpYWxWZWN0b3IoaXNTdGF0aWNJbml0aWFsVmVjdG9yKSB7XG4gICAgaWYgKCFpc1N0YXRpY0luaXRpYWxWZWN0b3IpIHtcbiAgICAgIHRoaXMuZW5jdHJ5cE1ldGhvZEluc3RhbmNlID0gdGhpcy5nZW5lcmF0ZUVuY3J5cHRNZXRob2RJbnN0YW5jZSgpO1xuICAgIH1cbiAgICB0aGlzLmlzU3RhdGljSW5pdGlhbFZlY3RvciA9IGlzU3RhdGljSW5pdGlhbFZlY3RvcjtcbiAgfVxuICAvKipcbiAgICogQ2hhbmdlIHRoZSBjdXJyZW50IG1hc3RlcktleVxuICAgKiBAcGFyYW0gbWFzdGVyS2V5IG5ldyBtYXN0ZXJLZXlcbiAgICovXG4gIGNoYW5nZU1hc3RlcktleShtYXN0ZXJLZXkpIHtcbiAgICBpZiAoIXRoaXMuaXNTdGF0aWNJbml0aWFsVmVjdG9yKSB7XG4gICAgICB0aGlzLmVuY3RyeXBNZXRob2RJbnN0YW5jZSA9IHRoaXMuZ2VuZXJhdGVFbmNyeXB0TWV0aG9kSW5zdGFuY2UoKTtcbiAgICB9XG4gICAgdGhpcy5tYXN0ZXJLZXkgPSBtYXN0ZXJLZXk7XG4gIH1cblxuICAvKipcbiAgICogQWRkIHBhZGRpbmcgdG8gdGhlIGxpc3RcbiAgICovXG4gIHByaXZhdGUgYWRkUGFkZGluZyhhcnJheUJ1ZmZlcjogVWludDhBcnJheSB8IFVpbnQxNkFycmF5IHwgVWludDMyQXJyYXkpIHtcbiAgICBjb25zdCBwYWRkaW5nTGVuZ3RoID0gTWF0aC5jZWlsKEFycmF5LmZyb20oYXJyYXlCdWZmZXIpLmxlbmd0aCAvIDE2KSAqIDE2O1xuICAgIGNvbnN0IHBhZGRpbmdMaXN0ID0gbmV3IEFycmF5KFxuICAgICAgcGFkZGluZ0xlbmd0aCAtIEFycmF5LmZyb20oYXJyYXlCdWZmZXIpLmxlbmd0aFxuICAgICkuZmlsbCgwKTtcbiAgICByZXR1cm4gbmV3IFVpbnQ4QXJyYXkoQXJyYXkuZnJvbShhcnJheUJ1ZmZlcikuY29uY2F0KHBhZGRpbmdMaXN0KSk7XG4gIH1cbiAgLyoqXG4gICAqIGdlbmVyYXRlIGEgaW5zdGFuY2Ugb2YgdGhlIGVuY3J5cHQgbWV0aG9kIHVzaW5nIHRoZSBjdXJyZW50IG1ldGhvZCBjb25maWd1cmVkXG4gICAqL1xuICBwcml2YXRlIGdlbmVyYXRlRW5jcnlwdE1ldGhvZEluc3RhbmNlKCkge1xuICAgIGxldCBlbmN0cnlwTWV0aG9kSW5zdGFuY2U7XG4gICAgc3dpdGNoICh0aGlzLmVuY3J5cHRNZXRob2QpIHtcbiAgICAgIGNhc2UgJ0NCQyc6XG4gICAgICAgIGVuY3RyeXBNZXRob2RJbnN0YW5jZSA9IG5ldyBhZXMuTW9kZU9mT3BlcmF0aW9uLmNiYyhcbiAgICAgICAgICB0aGlzLm1hc3RlcktleSxcbiAgICAgICAgICB0aGlzLmluaXRpYWxWZWN0b3JcbiAgICAgICAgKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICdDVFInOlxuICAgICAgICBpZiAoIXRoaXMuYWRkaXRpb25hbEVuY3J5cHRNZXRob2RQYXJhbXMuY291bnRlcikge1xuICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAgICdhZGRpdGlvbmFsRW5jcnlwdE1ldGhvZFBhcmFtcy5jb3VudGVyIGlzIHJlcXVpcmVkIHRvIHVzZSBlbmNyeXB0IG1ldGhvZCBDVFInXG4gICAgICAgICAgKTtcbiAgICAgICAgfVxuICAgICAgICBlbmN0cnlwTWV0aG9kSW5zdGFuY2UgPSBuZXcgYWVzLk1vZGVPZk9wZXJhdGlvbi5jdHIoXG4gICAgICAgICAgdGhpcy5tYXN0ZXJLZXksXG4gICAgICAgICAgdGhpcy5pbml0aWFsVmVjdG9yLFxuICAgICAgICAgIG5ldyBhZXMuQ291bnRlcih0aGlzLmFkZGl0aW9uYWxFbmNyeXB0TWV0aG9kUGFyYW1zLmNvdW50ZXIpXG4gICAgICAgICk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAnQ0ZCJzpcbiAgICAgICAgaWYgKCF0aGlzLmFkZGl0aW9uYWxFbmNyeXB0TWV0aG9kUGFyYW1zLnNlZ21lbnRTaXplKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICAgJ2FkZGl0aW9uYWxFbmNyeXB0TWV0aG9kUGFyYW1zLnNlZ21lbnRTaXplIGlzIHJlcXVpcmVkIHRvIHVzZSBlbmNyeXB0IG1ldGhvZCBDRkInXG4gICAgICAgICAgKTtcbiAgICAgICAgfVxuICAgICAgICBlbmN0cnlwTWV0aG9kSW5zdGFuY2UgPSBuZXcgYWVzLk1vZGVPZk9wZXJhdGlvbi5jZmIoXG4gICAgICAgICAgdGhpcy5tYXN0ZXJLZXksXG4gICAgICAgICAgdGhpcy5pbml0aWFsVmVjdG9yLFxuICAgICAgICAgIHRoaXMuYWRkaXRpb25hbEVuY3J5cHRNZXRob2RQYXJhbXMuc2VnbWVudFNpemVcbiAgICAgICAgKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICdPRkInOlxuICAgICAgICBlbmN0cnlwTWV0aG9kSW5zdGFuY2UgPSBuZXcgYWVzLk1vZGVPZk9wZXJhdGlvbi5vZmIoXG4gICAgICAgICAgdGhpcy5tYXN0ZXJLZXksXG4gICAgICAgICAgdGhpcy5pbml0aWFsVmVjdG9yXG4gICAgICAgICk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAnRUNCJzpcbiAgICAgICAgZW5jdHJ5cE1ldGhvZEluc3RhbmNlID0gbmV3IGFlcy5Nb2RlT2ZPcGVyYXRpb24uZWNiKHRoaXMubWFzdGVyS2V5KTtcbiAgICAgICAgYnJlYWs7XG4gICAgfVxuICAgIHJldHVybiBlbmN0cnlwTWV0aG9kSW5zdGFuY2U7XG4gIH1cbiAgLy8gI3JlZ2lvbiBVVElMU1xuICAvKipcbiAgICogQ29udmVydCB0aGUgdGV4dCB0byBieXRlc1xuICAgKiBAcGFyYW0gdGV4dCBUZXh0IHRvIGNvbnZlcnRcbiAgICovXG4gIHRleHRUb0J5dGVzKHRleHQpIHtcbiAgICByZXR1cm4gYWVzLnV0aWxzLnV0ZjgudG9CeXRlcyh0ZXh0KTtcbiAgfVxuICAvKipcbiAgICogQ29udmVydCB0aGUgYnl0ZXMgdG8gdGV4dFxuICAgKiBAcGFyYW0gYnl0ZXMgQnl0ZXMgdG8gY29udmVydFxuICAgKi9cbiAgYnl0ZXNUb1RleHQoYnl0ZXM6IFVpbnQ4QXJyYXkgfCBVaW50MTZBcnJheSB8IFVpbnQzMkFycmF5KSB7XG4gICAgcmV0dXJuIGFlcy51dGlscy51dGY4LmZyb21CeXRlcyhieXRlcyk7XG4gIH1cblxuICAvKipcbiAgICogQ29udmVydCB0aGUgYnl0ZXMgdG8gaGV4XG4gICAqIEBwYXJhbSBieXRlcyBieXRlcyB0byBjb252ZXJ0XG4gICAqL1xuICBieXRlc1RvaGV4KGJ5dGVzKSB7XG4gICAgcmV0dXJuIGFlcy51dGlscy5oZXguZnJvbUJ5dGVzKGJ5dGVzKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb252ZXJ0IHRoZSBoZXggdG8gYnl0ZXNcbiAgICogQHBhcmFtIGhleCBIZXggdG8gY29udmVydFxuICAgKi9cbiAgaGV4VG9CeXRlcyhoZXgpIHtcbiAgICByZXR1cm4gYWVzLnV0aWxzLmhleC50b0J5dGVzKGhleCk7XG4gIH1cbiAgLy8gI2VuZHJlZ2lvblxuXG4gIGdlbmVyYXRlU3Via2V5cyhrZXkpIHtcbiAgICBjb25zdCBjb25zdF9aZXJvID0gbmV3IFVpbnQ4QXJyYXkoMTYpO1xuICAgIGNvbnN0IGNvbnN0X1JiID0gbmV3IEJ1ZmZlcignMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwODcnLCAnaGV4Jyk7XG4gICAgY29uc3QgZW5jdHJ5cE1ldGhvZEluc3RhbmNlID0gbmV3IGFlcy5Nb2RlT2ZPcGVyYXRpb24uY2JjKGtleSwgbmV3IFVpbnQ4QXJyYXkoMTYpKTtcblxuICAgIGNvbnN0IGxFbmNyeXB0ZWQgPSBlbmN0cnlwTWV0aG9kSW5zdGFuY2UuZW5jcnlwdChjb25zdF9aZXJvKTtcbiAgICBjb25zdCBsID0gbmV3IEJ1ZmZlcih0aGlzLmJ5dGVzVG9oZXgobEVuY3J5cHRlZCksICdoZXgnKTtcbiAgICBsZXQgc3Via2V5MSA9IHRoaXMuYml0U2hpZnRMZWZ0KGwpO1xuICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1iaXR3aXNlXG4gICAgaWYgKGxbMF0gJiAweDgwKSB7XG4gICAgICBzdWJrZXkxID0gdGhpcy54b3Ioc3Via2V5MSwgY29uc3RfUmIpO1xuICAgIH1cblxuICAgIGxldCBzdWJrZXkyID0gdGhpcy5iaXRTaGlmdExlZnQoc3Via2V5MSk7XG4gICAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOm5vLWJpdHdpc2VcbiAgICBpZiAoc3Via2V5MVswXSAmIDB4ODApIHtcbiAgICAgIHN1YmtleTIgPSB0aGlzLnhvcihzdWJrZXkyLCBjb25zdF9SYik7XG4gICAgfVxuXG4gICAgcmV0dXJuIHsgc3Via2V5MTogc3Via2V5MSwgc3Via2V5Mjogc3Via2V5MiB9O1xuICB9XG5cbiAgYWVzQ21hYyhrZXksIG1lc3NhZ2UpIHtcbiAgICBjb25zb2xlLmxvZygnSU5JQ0lBIENJRlJBRE8hISEhISEhISEhISEhISEhJyk7XG4gICAgY29uc3Qgc3Via2V5cyA9IHRoaXMuZ2VuZXJhdGVTdWJrZXlzKGtleSk7XG4gICAgbGV0IGJsb2NrQ291bnQgPSBNYXRoLmNlaWwobWVzc2FnZS5sZW5ndGggLyAxNik7XG4gICAgbGV0IGxhc3RCbG9ja0NvbXBsZXRlRmxhZywgbGFzdEJsb2NrLCBsYXN0QmxvY2tJbmRleDtcblxuICAgIGlmIChibG9ja0NvdW50ID09PSAwKSB7XG4gICAgICBibG9ja0NvdW50ID0gMTtcbiAgICAgIGxhc3RCbG9ja0NvbXBsZXRlRmxhZyA9IGZhbHNlO1xuICAgIH0gZWxzZSB7XG4gICAgICBsYXN0QmxvY2tDb21wbGV0ZUZsYWcgPSBtZXNzYWdlLmxlbmd0aCAlIDE2ID09PSAwO1xuICAgIH1cbiAgICBsYXN0QmxvY2tJbmRleCA9IGJsb2NrQ291bnQgLSAxO1xuXG4gICAgaWYgKGxhc3RCbG9ja0NvbXBsZXRlRmxhZykge1xuICAgICAgbGFzdEJsb2NrID0gdGhpcy54b3IoXG4gICAgICAgIHRoaXMuZ2V0TWVzc2FnZUJsb2NrKG1lc3NhZ2UsIGxhc3RCbG9ja0luZGV4KSxcbiAgICAgICAgc3Via2V5cy5zdWJrZXkxXG4gICAgICApO1xuICAgIH0gZWxzZSB7XG4gICAgICBsYXN0QmxvY2sgPSB0aGlzLnhvcihcbiAgICAgICAgdGhpcy5nZXRQYWRkZWRNZXNzYWdlQmxvY2sobWVzc2FnZSwgbGFzdEJsb2NrSW5kZXgpLFxuICAgICAgICBzdWJrZXlzLnN1YmtleTJcbiAgICAgICk7XG4gICAgfVxuXG4gICAgbGV0IHggPSBuZXcgQnVmZmVyKCcwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMCcsICdoZXgnKTtcbiAgICBsZXQgeTtcblxuICAgIGxldCBlbmN0cnlwTWV0aG9kSW5zdGFuY2U7XG4gICAgZm9yIChsZXQgaW5kZXggPSAwOyBpbmRleCA8IGxhc3RCbG9ja0luZGV4OyBpbmRleCsrKSB7XG4gICAgICBlbmN0cnlwTWV0aG9kSW5zdGFuY2UgPSBuZXcgYWVzLk1vZGVPZk9wZXJhdGlvbi5jYmMoa2V5LCBuZXcgVWludDhBcnJheSgxNikpO1xuICAgICAgeSA9IHRoaXMueG9yKHgsIHRoaXMuZ2V0TWVzc2FnZUJsb2NrKG1lc3NhZ2UsIGluZGV4KSk7XG4gICAgICBjb25zdCB4RW5jcnlwdGVkID0gZW5jdHJ5cE1ldGhvZEluc3RhbmNlLmVuY3J5cHQoeSk7XG4gICAgICBjb25zb2xlLmxvZygnWCBub3JtYWwgPT09PT09PT09PT09PT09PiAnLCB0aGlzLmJ5dGVzVG9oZXgoeSkpO1xuICAgICAgY29uc29sZS5sb2coJ1ggZW5jcnlwdGVkID09PT09PT09PT09PT09PiAnLCB0aGlzLmJ5dGVzVG9oZXgoeEVuY3J5cHRlZCkpO1xuICAgICAgeCA9IG5ldyBCdWZmZXIodGhpcy5ieXRlc1RvaGV4KHhFbmNyeXB0ZWQpLCAnaGV4Jyk7XG4gICAgfVxuICAgIHkgPSB0aGlzLnhvcihsYXN0QmxvY2ssIHgpO1xuICAgIGVuY3RyeXBNZXRob2RJbnN0YW5jZSA9IG5ldyBhZXMuTW9kZU9mT3BlcmF0aW9uLmNiYyhrZXksIG5ldyBVaW50OEFycmF5KDE2KSk7XG4gICAgY29uc3QgeUVuY3J5cHRlZCA9IGVuY3RyeXBNZXRob2RJbnN0YW5jZS5lbmNyeXB0KHkpO1xuICAgIGNvbnNvbGUubG9nKCdZIG5vcm1hbCA9PT09PT09PT09PT09PT4gJywgdGhpcy5ieXRlc1RvaGV4KHkpKTtcbiAgICBjb25zb2xlLmxvZygnWSBlbmNyeXB0ZWQgPT09PT09PT09PT09PT0+ICcsIHRoaXMuYnl0ZXNUb2hleCh5RW5jcnlwdGVkKSk7XG4gICAgcmV0dXJuIHlFbmNyeXB0ZWQ7XG4gIH1cblxuICBnZXRNZXNzYWdlQmxvY2sobWVzc2FnZSwgYmxvY2tJbmRleCkge1xuICAgIGNvbnN0IGJsb2NrID0gbmV3IEJ1ZmZlcigxNik7XG4gICAgY29uc3Qgc3RhcnQgPSBibG9ja0luZGV4ICogMTY7XG4gICAgY29uc3QgZW5kID0gc3RhcnQgKyAxNjtcbiAgICBsZXQgYmxvY2tJID0gMDtcbiAgICBmb3IgKGxldCBpID0gc3RhcnQ7IGkgPCBlbmQ7IGkrKykge1xuICAgICAgYmxvY2tbYmxvY2tJXSA9IG1lc3NhZ2VbaV07XG4gICAgICBibG9ja0krKztcbiAgICB9XG4gICAgcmV0dXJuIGJsb2NrO1xuICB9XG5cbiAgZ2V0UGFkZGVkTWVzc2FnZUJsb2NrKG1lc3NhZ2UsIGJsb2NrSW5kZXgpIHtcbiAgICBjb25zdCBibG9jayA9IG5ldyBCdWZmZXIoMTYpO1xuICAgIGNvbnN0IHN0YXJ0ID0gYmxvY2tJbmRleCAqIDE2O1xuICAgIGNvbnN0IGVuZCA9IG1lc3NhZ2UubGVuZ3RoO1xuXG4gICAgYmxvY2suZmlsbCgwKTtcbiAgICBsZXQgYmxvY2tJID0gMDtcbiAgICBmb3IgKGxldCBpID0gc3RhcnQ7IGkgPCBlbmQ7IGkrKykge1xuICAgICAgYmxvY2tbYmxvY2tJXSA9IG1lc3NhZ2VbaV07XG4gICAgICBibG9ja0krKztcbiAgICB9XG4gICAgYmxvY2tbZW5kIC0gc3RhcnRdID0gMHg4MDtcblxuICAgIHJldHVybiBibG9jaztcbiAgfVxuXG4gIGJpdFNoaWZ0TGVmdChidWZmZXIpIHtcbiAgICBjb25zdCBzaGlmdGVkID0gbmV3IEJ1ZmZlcihidWZmZXIubGVuZ3RoKTtcbiAgICBjb25zdCBsYXN0ID0gYnVmZmVyLmxlbmd0aCAtIDE7XG4gICAgZm9yIChsZXQgaW5kZXggPSAwOyBpbmRleCA8IGxhc3Q7IGluZGV4KyspIHtcbiAgICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1iaXR3aXNlXG4gICAgICBzaGlmdGVkW2luZGV4XSA9IGJ1ZmZlcltpbmRleF0gPDwgMTtcbiAgICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1iaXR3aXNlXG4gICAgICBpZiAoYnVmZmVyW2luZGV4ICsgMV0gJiAweDgwKSB7XG4gICAgICAgIHNoaWZ0ZWRbaW5kZXhdICs9IDB4MDE7XG4gICAgICB9XG4gICAgfVxuICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTpuby1iaXR3aXNlXG4gICAgc2hpZnRlZFtsYXN0XSA9IGJ1ZmZlcltsYXN0XSA8PCAxO1xuICAgIHJldHVybiBzaGlmdGVkO1xuICB9XG5cbiAgeG9yKGJ1ZmZlckEsIGJ1ZmZlckIpIHtcbiAgICBjb25zdCBsZW5ndGggPSBNYXRoLm1pbihidWZmZXJBLmxlbmd0aCwgYnVmZmVyQi5sZW5ndGgpO1xuICAgIGNvbnN0IG91dHB1dCA9IG5ldyBCdWZmZXIobGVuZ3RoKTtcbiAgICBmb3IgKGxldCBpbmRleCA9IDA7IGluZGV4IDwgbGVuZ3RoOyBpbmRleCsrKSB7XG4gICAgICAvLyB0c2xpbnQ6ZGlzYWJsZS1uZXh0LWxpbmU6bm8tYml0d2lzZVxuICAgICAgb3V0cHV0W2luZGV4XSA9IGJ1ZmZlckFbaW5kZXhdIF4gYnVmZmVyQltpbmRleF07XG4gICAgfVxuICAgIHJldHVybiBvdXRwdXQ7XG4gIH1cbn1cbiJdfQ==