bitgo
Version:
BitGo JavaScript SDK
266 lines • 51.5 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const openpgp = require("openpgp");
const should = require("should");
const crypto = require("crypto");
const assert = require("assert");
const sdk_core_1 = require("@bitgo/sdk-core");
const utxo_lib_1 = require("@bitgo/utxo-lib");
const sinon = require("sinon");
const sodium = require('libsodium-wrappers-sumo');
describe('OpenGPG Utils Tests', function () {
let senderKey;
let recipientKey;
let otherKey;
before(async function () {
openpgp.config.rejectCurves = new Set();
senderKey = await openpgp.generateKey({
userIDs: [
{
name: 'sender',
email: 'sender@username.com',
},
],
curve: 'secp256k1',
});
recipientKey = await openpgp.generateKey({
userIDs: [
{
name: 'recipient',
email: 'recipient@username.com',
},
],
curve: 'secp256k1',
});
otherKey = await openpgp.generateKey({
userIDs: [
{
name: 'other',
email: 'other@username.com',
},
],
curve: 'secp256k1',
});
});
describe('createShareProof', function () {
it('should create an Ed share proof', async function () {
const uValue = crypto.randomBytes(32).toString('hex');
const proof = await sdk_core_1.openpgpUtils.createShareProof(senderKey.privateKey, uValue, 'eddsa');
// verify proof
const decodedProof = await openpgp.readKey({ armoredKey: proof }).should.be.fulfilled();
const decodedPubKey = await openpgp.readKey({ armoredKey: senderKey.publicKey }).should.be.fulfilled();
const isValid = (await decodedProof.verifyPrimaryUser([decodedPubKey]))[0].valid;
isValid.should.be.true();
const proofSubkeys = decodedProof.getSubkeys()[1];
const decodedUValueProof = Buffer.from(proofSubkeys.keyPacket.publicParams.Q.slice(1)).toString('hex');
const rawUValueProof = Buffer.from(sodium.crypto_scalarmult_ed25519_base_noclamp(Buffer.from(uValue, 'hex'))).toString('hex');
decodedUValueProof.should.equal(rawUValueProof);
});
it('should create an Ec share proof', async function () {
const uValue = crypto.randomBytes(32).toString('hex');
const proof = await sdk_core_1.openpgpUtils.createShareProof(senderKey.privateKey, uValue, 'ecdsa');
// verify proof
const decodedProof = await openpgp.readKey({ armoredKey: proof }).should.be.fulfilled();
const decodedPubKey = await openpgp.readKey({ armoredKey: senderKey.publicKey }).should.be.fulfilled();
const isValid = (await decodedProof.verifyPrimaryUser([decodedPubKey]))[0].valid;
isValid.should.be.true();
const proofSubkeys = decodedProof.getSubkeys()[1];
const decodedUValueProof = proofSubkeys.keyPacket.publicParams.Q;
const rawUValueProof = utxo_lib_1.ecc.pointFromScalar(Buffer.from(uValue, 'hex'), false);
equal(decodedUValueProof, rawUValueProof).should.be.true();
});
});
describe('verifyPrimaryUserWrapper', function () {
it('should verify primary user with a date check', async function () {
const uValue = crypto.randomBytes(32).toString('hex');
const proof = await sdk_core_1.openpgpUtils.createShareProof(senderKey.privateKey, uValue, 'eddsa');
// verify proof
const decodedProof = await openpgp.readKey({ armoredKey: proof }).should.be.fulfilled();
const decodedPubKey = await openpgp.readKey({ armoredKey: senderKey.publicKey }).should.be.fulfilled();
const isValid = (await sdk_core_1.openpgpUtils.verifyPrimaryUserWrapper(decodedProof, decodedPubKey, true))[0].valid;
should.exist(isValid);
if (isValid !== null) {
isValid.should.be.true();
}
});
it('should verify primary user without a date check', async function () {
const uValue = crypto.randomBytes(32).toString('hex');
const proof = await sdk_core_1.openpgpUtils.createShareProof(senderKey.privateKey, uValue, 'eddsa');
// verify proof
const decodedProof = await openpgp.readKey({ armoredKey: proof }).should.be.fulfilled();
const decodedPubKey = await openpgp.readKey({ armoredKey: senderKey.publicKey }).should.be.fulfilled();
const isValid = (await sdk_core_1.openpgpUtils.verifyPrimaryUserWrapper(decodedProof, decodedPubKey, false))[0].valid;
should.exist(isValid);
if (isValid !== null) {
isValid.should.be.true();
}
});
});
describe('verifyShareProof EdDSA', function () {
it('should be able to verify a valid proof', async function () {
const uValue = crypto.randomBytes(32).toString('hex');
const proof = await sdk_core_1.openpgpUtils.createShareProof(senderKey.privateKey, uValue, 'eddsa');
const isValid = await sdk_core_1.openpgpUtils.verifyShareProof(senderKey.publicKey, proof, uValue, 'eddsa');
isValid.should.be.true();
});
it('should be able to detect sender is an attacker', async function () {
const uValue = crypto.randomBytes(32).toString('hex');
const proof = await sdk_core_1.openpgpUtils.createShareProof(otherKey.privateKey, uValue, 'eddsa');
const isValid = await sdk_core_1.openpgpUtils.verifyShareProof(senderKey.publicKey, proof, uValue, 'eddsa');
isValid.should.be.false();
});
it('should be able to detect u value is corrupted', async function () {
const uValue = crypto.randomBytes(32).toString('hex');
const proof = await sdk_core_1.openpgpUtils.createShareProof(senderKey.privateKey, uValue, 'eddsa');
const uValueCorrupted = crypto.randomBytes(32).toString('hex');
const isValid = await sdk_core_1.openpgpUtils.verifyShareProof(senderKey.publicKey, proof, uValueCorrupted, 'eddsa');
isValid.should.be.false();
});
});
describe('verifyShareProof ECDSA', function () {
it('should be able to verify a valid proof', async function () {
const uValue = crypto.randomBytes(32).toString('hex');
const proof = await sdk_core_1.openpgpUtils.createShareProof(senderKey.privateKey, uValue, 'ecdsa');
const isValid = await sdk_core_1.openpgpUtils.verifyShareProof(senderKey.publicKey, proof, uValue, 'ecdsa');
isValid.should.be.true();
});
it('should be able to detect sender is an attacker', async function () {
const uValue = crypto.randomBytes(32).toString('hex');
const proof = await sdk_core_1.openpgpUtils.createShareProof(otherKey.privateKey, uValue, 'ecdsa');
const isValid = await sdk_core_1.openpgpUtils.verifyShareProof(senderKey.publicKey, proof, uValue, 'ecdsa');
isValid.should.be.false();
});
it('should be able to detect u value is corrupted', async function () {
const uValue = crypto.randomBytes(32).toString('hex');
const proof = await sdk_core_1.openpgpUtils.createShareProof(senderKey.privateKey, uValue, 'ecdsa');
const uValueCorrupted = crypto.randomBytes(32).toString('hex');
const isValid = await sdk_core_1.openpgpUtils.verifyShareProof(senderKey.publicKey, proof, uValueCorrupted, 'ecdsa');
isValid.should.be.false();
});
});
describe('verifySharedDataProof and createSharedDataProof test', function () {
it('should be able to detect if proof value is corrupted or not', async function () {
const sharedData1 = crypto.randomBytes(32).toString('hex');
const sharedData2 = crypto.randomBytes(32).toString('hex');
const dataToProofArray = [
{ name: 's1', value: sharedData1 },
{ name: 's2', value: sharedData2 },
];
const proof = await sdk_core_1.openpgpUtils.createSharedDataProof(senderKey.privateKey, otherKey.publicKey, dataToProofArray);
let isValid = await sdk_core_1.openpgpUtils.verifySharedDataProof(senderKey.publicKey, proof, dataToProofArray);
isValid.should.be.true();
// tamper with the data
dataToProofArray[0].value = 'tampered data';
isValid = await sdk_core_1.openpgpUtils.verifySharedDataProof(senderKey.publicKey, proof, dataToProofArray);
isValid.should.be.false();
});
it('should be able verify data proof if created in the future', async function () {
const sharedData1 = crypto.randomBytes(32).toString('hex');
const sharedData2 = crypto.randomBytes(32).toString('hex');
const dataToProofArray = [
{ name: 's1', value: sharedData1 },
{ name: 's2', value: sharedData2 },
];
const proof = await sdk_core_1.openpgpUtils.createSharedDataProof(senderKey.privateKey, otherKey.publicKey, dataToProofArray);
const clock = sinon.useFakeTimers(new Date('2001-02-14T12:00:00Z').getTime());
const isValid = await sdk_core_1.openpgpUtils.verifySharedDataProof(senderKey.publicKey, proof, dataToProofArray);
isValid.should.be.true();
clock.restore();
});
});
describe('encrypt and decrypt with signing', function () {
it('should successfully encrypt, sign, and decrypt', async function () {
const text = 'original message';
const signedMessage = await sdk_core_1.openpgpUtils.encryptAndSignText(text, recipientKey.publicKey, senderKey.privateKey);
const decryptedMessage = await sdk_core_1.openpgpUtils.readSignedMessage(signedMessage, senderKey.publicKey, recipientKey.privateKey);
decryptedMessage.should.equal(text);
});
it('should fail on verification with wrong public key', async function () {
const text = 'original message';
const signedMessage = await sdk_core_1.openpgpUtils.encryptAndSignText(text, recipientKey.publicKey, senderKey.privateKey);
await sdk_core_1.openpgpUtils
.readSignedMessage(signedMessage, otherKey.publicKey, recipientKey.privateKey)
.should.be.rejected();
});
it('should fail on decryption with wrong private key', async function () {
const text = 'original message';
const signedMessage = await sdk_core_1.openpgpUtils.encryptAndSignText(text, recipientKey.publicKey, senderKey.privateKey);
await sdk_core_1.openpgpUtils
.readSignedMessage(signedMessage, senderKey.publicKey, otherKey.privateKey)
.should.be.rejectedWith('Error decrypting message: Session key decryption failed.');
});
it('should encrypt, sign, and decrypt without previously clearing rejectedCurves', async function () {
openpgp.config.rejectCurves = new Set([openpgp.enums.curve.secp256k1]);
const text = 'original message';
const signedMessage = await sdk_core_1.openpgpUtils.encryptAndSignText(text, recipientKey.publicKey, senderKey.privateKey);
const decryptedMessage = await sdk_core_1.openpgpUtils.readSignedMessage(signedMessage, senderKey.publicKey, recipientKey.privateKey);
decryptedMessage.should.equal(text);
openpgp.config.rejectCurves = new Set();
});
});
describe('signatures and verification', function () {
it('should verify signature', async function () {
const text = 'some payload';
const signature = await sdk_core_1.openpgpUtils.signText(text, senderKey.privateKey);
const isValidSignature = await sdk_core_1.openpgpUtils.verifySignature(text, signature, senderKey.publicKey);
isValidSignature.should.be.true();
});
it('should fail verification if public key is incorrect', async function () {
const text = 'some payload';
const signature = await sdk_core_1.openpgpUtils.signText(text, senderKey.privateKey);
const isValidSignature = await sdk_core_1.openpgpUtils.verifySignature(text, signature, recipientKey.publicKey);
isValidSignature.should.be.false();
});
it('should fail verification if message is incorrect', async function () {
const text = 'some payload';
const signature = await sdk_core_1.openpgpUtils.signText(text, senderKey.privateKey);
const isValidSignature = await sdk_core_1.openpgpUtils.verifySignature('something else', signature, senderKey.publicKey);
isValidSignature.should.be.false();
});
});
describe('GPG key generation', function () {
it('should generate a a GPG key for secp256k1 with random name and email', async function () {
const gpgKey = await sdk_core_1.openpgpUtils.generateGPGKeyPair('secp256k1');
should.exist(gpgKey);
should.exist(gpgKey.privateKey);
should.exist(gpgKey.publicKey);
});
it('should generate a a GPG key for with random name and email', async function () {
const gpgKey = await sdk_core_1.openpgpUtils.generateGPGKeyPair('ed25519');
should.exist(gpgKey);
should.exist(gpgKey.privateKey);
should.exist(gpgKey.publicKey);
});
it('should generate a a GPG key with provided name and email', async function () {
const userName = 'John Doe';
const userEmail = 'john.doe@example.com';
const gpgKey = await sdk_core_1.openpgpUtils.generateGPGKeyPair('secp256k1', userName, userEmail);
should.exist(gpgKey);
should.exist(gpgKey.privateKey);
should.exist(gpgKey.publicKey);
const parsedKey = await openpgp.readKey({ armoredKey: gpgKey.publicKey });
should.exist(parsedKey);
assert.ok(parsedKey);
const primaryUser = await parsedKey.getPrimaryUser();
primaryUser.user.userID?.name?.should.equal(userName);
primaryUser.user.userID?.email?.should.equal(userEmail);
});
it('should fail to generate a a GPG key for unknown curve', async function () {
await sdk_core_1.openpgpUtils
.generateGPGKeyPair('unknownCurve')
.should.be.rejectedWith('Error generating keypair: Unknown curve');
});
});
function equal(buf1, buf2) {
if (buf1.byteLength != buf2.byteLength)
return false;
const dv1 = new Int8Array(buf1);
const dv2 = new Int8Array(buf2);
for (let i = 0; i != buf1.byteLength; i++) {
if (dv1[i] != dv2[i])
return false;
}
return true;
}
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3BlbmdwZ1V0aWxzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vdGVzdC92Mi91bml0L2ludGVybmFsL29wZW5ncGdVdGlscy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLG1DQUFtQztBQUNuQyxpQ0FBa0M7QUFDbEMsaUNBQWlDO0FBQ2pDLGlDQUFpQztBQUVqQyw4Q0FBK0M7QUFDL0MsOENBQW1EO0FBQ25ELCtCQUErQjtBQUUvQixNQUFNLE1BQU0sR0FBRyxPQUFPLENBQUMseUJBQXlCLENBQUMsQ0FBQztBQUVsRCxRQUFRLENBQUMscUJBQXFCLEVBQUU7SUFDOUIsSUFBSSxTQUFvRCxDQUFDO0lBQ3pELElBQUksWUFBdUQsQ0FBQztJQUM1RCxJQUFJLFFBQW1ELENBQUM7SUFFeEQsTUFBTSxDQUFDLEtBQUs7UUFDVixPQUFPLENBQUMsTUFBTSxDQUFDLFlBQVksR0FBRyxJQUFJLEdBQUcsRUFBRSxDQUFDO1FBQ3hDLFNBQVMsR0FBRyxNQUFNLE9BQU8sQ0FBQyxXQUFXLENBQUM7WUFDcEMsT0FBTyxFQUFFO2dCQUNQO29CQUNFLElBQUksRUFBRSxRQUFRO29CQUNkLEtBQUssRUFBRSxxQkFBcUI7aUJBQzdCO2FBQ0Y7WUFDRCxLQUFLLEVBQUUsV0FBVztTQUNuQixDQUFDLENBQUM7UUFDSCxZQUFZLEdBQUcsTUFBTSxPQUFPLENBQUMsV0FBVyxDQUFDO1lBQ3ZDLE9BQU8sRUFBRTtnQkFDUDtvQkFDRSxJQUFJLEVBQUUsV0FBVztvQkFDakIsS0FBSyxFQUFFLHdCQUF3QjtpQkFDaEM7YUFDRjtZQUNELEtBQUssRUFBRSxXQUFXO1NBQ25CLENBQUMsQ0FBQztRQUNILFFBQVEsR0FBRyxNQUFNLE9BQU8sQ0FBQyxXQUFXLENBQUM7WUFDbkMsT0FBTyxFQUFFO2dCQUNQO29CQUNFLElBQUksRUFBRSxPQUFPO29CQUNiLEtBQUssRUFBRSxvQkFBb0I7aUJBQzVCO2FBQ0Y7WUFDRCxLQUFLLEVBQUUsV0FB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dkUsQ0FBQyxDQUFDLENBQUM7SUFDTCxDQUFDLENBQUMsQ0FBQztJQUVILFNBQVMsS0FBSyxDQUFDLElBQUksRUFBRSxJQUFJO1FBQ3ZCLElBQUksSUFBSSxDQUFDLFVBQVUsSUFBSSxJQUFJLENBQUMsVUFBVTtZQUFFLE9BQU8sS0FBSyxDQUFDO1FBQ3JELE1BQU0sR0FBRyxHQUFHLElBQUksU0FBUyxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ2hDLE1BQU0sR0FBRyxHQUFHLElBQUksU0FBUyxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ2hDLEtBQUssSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsSUFBSSxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUM7WUFDMUMsSUFBSSxHQUFHLENBQUMsQ0FBQyxDQUFDLElBQUksR0FBRyxDQUFDLENBQUMsQ0FBQztnQkFBRSxPQUFPLEtBQUssQ0FBQztRQUNyQyxDQUFDO1FBQ0QsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0FBQ0gsQ0FBQyxDQUFDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBvcGVucGdwIGZyb20gJ29wZW5wZ3AnO1xuaW1wb3J0IHNob3VsZCA9IHJlcXVpcmUoJ3Nob3VsZCcpO1xuaW1wb3J0ICogYXMgY3J5cHRvIGZyb20gJ2NyeXB0byc7XG5pbXBvcnQgKiBhcyBhc3NlcnQgZnJvbSAnYXNzZXJ0JztcblxuaW1wb3J0IHsgb3BlbnBncFV0aWxzIH0gZnJvbSAnQGJpdGdvL3Nkay1jb3JlJztcbmltcG9ydCB7IGVjYyBhcyBzZWNwMjU2azEgfSBmcm9tICdAYml0Z28vdXR4by1saWInO1xuaW1wb3J0ICogYXMgc2lub24gZnJvbSAnc2lub24nO1xuXG5jb25zdCBzb2RpdW0gPSByZXF1aXJlKCdsaWJzb2RpdW0td3JhcHBlcnMtc3VtbycpO1xuXG5kZXNjcmliZSgnT3BlbkdQRyBVdGlscyBUZXN0cycsIGZ1bmN0aW9uICgpIHtcbiAgbGV0IHNlbmRlcktleTogeyBwdWJsaWNLZXk6IHN0cmluZzsgcHJpdmF0ZUtleTogc3RyaW5nIH07XG4gIGxldCByZWNpcGllbnRLZXk6IHsgcHVibGljS2V5OiBzdHJpbmc7IHByaXZhdGVLZXk6IHN0cmluZyB9O1xuICBsZXQgb3RoZXJLZXk6IHsgcHVibGljS2V5OiBzdHJpbmc7IHByaXZhdGVLZXk6IHN0cmluZyB9O1xuXG4gIGJlZm9yZShhc3luYyBmdW5jdGlvbiAoKSB7XG4gICAgb3BlbnBncC5jb25maWcucmVqZWN0Q3VydmVzID0gbmV3IFNldCgpO1xuICAgIHNlbmRlcktleSA9IGF3YWl0IG9wZW5wZ3AuZ2VuZXJhdGVLZXkoe1xuICAgICAgdXNlcklEczogW1xuICAgICAgICB7XG4gICAgICAgICAgbmFtZTogJ3NlbmRlcicsXG4gICAgICAgICAgZW1haWw6ICdzZW5kZXJAdXNlcm5hbWUuY29tJyxcbiAgICAgICAgfSxcbiAgICAgIF0sXG4gICAgICBjdXJ2ZTogJ3NlY3AyNTZrMScsXG4gICAgfSk7XG4gICAgcmVjaXBpZW50S2V5ID0gYXdhaXQgb3BlbnBncC5nZW5lcmF0ZUtleSh7XG4gICAgICB1c2VySURzOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBuYW1lOiAncmVjaXBpZW50JyxcbiAgICAgICAgICBlbWFpbDogJ3JlY2lwaWVudEB1c2VybmFtZS5jb20nLFxuICAgICAgICB9LFxuICAgICAgXSxcbiAgICAgIGN1cnZlOiAnc2VjcDI1NmsxJyxcbiAgICB9KTtcbiAgICBvdGhlcktleSA9IGF3YWl0IG9wZW5wZ3AuZ2VuZXJhdGVLZXkoe1xuICAgICAgdXNlcklEczogW1xuICAgICAgICB7XG4gICAgICAgICAgbmFtZTogJ290aGVyJyxcbiAgICAgICAgICBlbWFpbDogJ290aGVyQHVzZXJuYW1lLmNvbScsXG4gICAgICAgIH0sXG4gICAgICBdLFxuICAgICAgY3VydmU6ICdzZWNwMjU2azEnLFxuICAgIH0pO1xuICB9KTtcblxuICBkZXNjcmliZSgnY3JlYXRlU2hhcmVQcm9vZicsIGZ1bmN0aW9uICgpIHtcbiAgICBpdCgnc2hvdWxkIGNyZWF0ZSBhbiBFZCBzaGFyZSBwcm9vZicsIGFzeW5jIGZ1bmN0aW9uICgpIHtcbiAgICAgIGNvbnN0IHVWYWx1ZSA9IGNyeXB0by5yYW5kb21CeXRlcygzMikudG9TdHJpbmcoJ2hleCcpO1xuICAgICAgY29uc3QgcHJvb2YgPSBhd2FpdCBvcGVucGdwVXRpbHMuY3JlYXRlU2hhcmVQcm9vZihzZW5kZXJLZXkucHJpdmF0ZUtleSwgdVZhbHVlLCAnZWRkc2EnKTtcblxuICAgICAgLy8gdmVyaWZ5IHByb29mXG4gICAgICBjb25zdCBkZWNvZGVkUHJvb2YgPSBhd2FpdCBvcGVucGdwLnJlYWRLZXkoeyBhcm1vcmVkS2V5OiBwcm9vZiB9KS5zaG91bGQuYmUuZnVsZmlsbGVkKCk7XG4gICAgICBjb25zdCBkZWNvZGVkUHViS2V5ID0gYXdhaXQgb3BlbnBncC5yZWFkS2V5KHsgYXJtb3JlZEtleTogc2VuZGVyS2V5LnB1YmxpY0tleSB9KS5zaG91bGQuYmUuZnVsZmlsbGVkKCk7XG4gICAgICBjb25zdCBpc1ZhbGlkID0gKGF3YWl0IGRlY29kZWRQcm9vZi52ZXJpZnlQcmltYXJ5VXNlcihbZGVjb2RlZFB1YktleV0pKVswXS52YWxpZDtcbiAgICAgIGlzVmFsaWQuc2hvdWxkLmJlLnRydWUoKTtcblxuICAgICAgY29uc3QgcHJvb2ZTdWJrZXlzID0gZGVjb2RlZFByb29mLmdldFN1YmtleXMoKVsxXTtcblxuICAgICAgY29uc3QgZGVjb2RlZFVWYWx1ZVByb29mID0gQnVmZmVyLmZyb20ocHJvb2ZTdWJrZXlzLmtleVBhY2tldC5wdWJsaWNQYXJhbXMuUS5zbGljZSgxKSkudG9TdHJpbmcoJ2hleCcpO1xuICAgICAgY29uc3QgcmF3VVZhbHVlUHJvb2YgPSBCdWZmZXIuZnJvbShcbiAgICAgICAgc29kaXVtLmNyeXB0b19zY2FsYXJtdWx0X2VkMjU1MTlfYmFzZV9ub2NsYW1wKEJ1ZmZlci5mcm9tKHVWYWx1ZSwgJ2hleCcpKVxuICAgICAgKS50b1N0cmluZygnaGV4Jyk7XG5cbiAgICAgIGRlY29kZWRVVmFsdWVQcm9vZi5zaG91bGQuZXF1YWwocmF3VVZhbHVlUHJvb2YpO1xuICAgIH0pO1xuXG4gICAgaXQoJ3Nob3VsZCBjcmVhdGUgYW4gRWMgc2hhcmUgcHJvb2YnLCBhc3luYyBmdW5jdGlvbiAoKSB7XG4gICAgICBjb25zdCB1VmFsdWUgPSBjcnlwdG8ucmFuZG9tQnl0ZXMoMzIpLnRvU3RyaW5nKCdoZXgnKTtcbiAgICAgIGNvbnN0IHByb29mID0gYXdhaXQgb3BlbnBncFV0aWxzLmNyZWF0ZVNoYXJlUHJvb2Yoc2VuZGVyS2V5LnByaXZhdGVLZXksIHVWYWx1ZSwgJ2VjZHNhJyk7XG5cbiAgICAgIC8vIHZlcmlmeSBwcm9vZlxuICAgICAgY29uc3QgZGVjb2RlZFByb29mID0gYXdhaXQgb3BlbnBncC5yZWFkS2V5KHsgYXJtb3JlZEtleTogcHJvb2YgfSkuc2hvdWxkLmJlLmZ1bGZpbGxlZCgpO1xuICAgICAgY29uc3QgZGVjb2RlZFB1YktleSA9IGF3YWl0IG9wZW5wZ3AucmVhZEtleSh7IGFybW9yZWRLZXk6IHNlbmRlcktleS5wdWJsaWNLZXkgfSkuc2hvdWxkLmJlLmZ1bGZpbGxlZCgpO1xuICAgICAgY29uc3QgaXNWYWxpZCA9IChhd2FpdCBkZWNvZGVkUHJvb2YudmVyaWZ5UHJpbWFyeVVzZXIoW2RlY29kZWRQdWJLZXldKSlbMF0udmFsaWQ7XG4gICAgICBpc1ZhbGlkLnNob3VsZC5iZS50cnVlKCk7XG5cbiAgICAgIGNvbnN0IHByb29mU3Via2V5cyA9IGRlY29kZWRQcm9vZi5nZXRTdWJrZXlzKClbMV07XG5cbiAgICAgIGNvbnN0IGRlY29kZWRVVmFsdWVQcm9vZiA9IHByb29mU3Via2V5cy5rZXlQYWNrZXQucHVibGljUGFyYW1zLlE7XG4gICAgICBjb25zdCByYXdVVmFsdWVQcm9vZiA9IHNlY3AyNTZrMS5wb2ludEZyb21TY2FsYXIoQnVmZmVyLmZyb20odVZhbHVlLCAnaGV4JyksIGZhbHNlKTtcbiAgICAgIGVxdWFsKGRlY29kZWRVVmFsdWVQcm9vZiwgcmF3VVZhbHVlUHJvb2YpLnNob3VsZC5iZS50cnVlKCk7XG4gICAgfSk7XG4gIH0pO1xuXG4gIGRlc2NyaWJlKCd2ZXJpZnlQcmltYXJ5VXNlcldyYXBwZXInLCBmdW5jdGlvbiAoKSB7XG4gICAgaXQoJ3Nob3VsZCB2ZXJpZnkgcHJpbWFyeSB1c2VyIHdpdGggYSBkYXRlIGNoZWNrJywgYXN5bmMgZnVuY3Rpb24gKCkge1xuICAgICAgY29uc3QgdVZhbHVlID0gY3J5cHRvLnJhbmRvbUJ5dGVzKDMyKS50b1N0cmluZygnaGV4Jyk7XG4gICAgICBjb25zdCBwcm9vZiA9IGF3YWl0IG9wZW5wZ3BVdGlscy5jcmVhdGVTaGFyZVByb29mKHNlbmRlcktleS5wcml2YXRlS2V5LCB1VmFsdWUsICdlZGRzYScpO1xuXG4gICAgICAvLyB2ZXJpZnkgcHJvb2ZcbiAgICAgIGNvbnN0IGRlY29kZWRQcm9vZiA9IGF3YWl0IG9wZW5wZ3AucmVhZEtleSh7IGFybW9yZWRLZXk6IHByb29mIH0pLnNob3VsZC5iZS5mdWxmaWxsZWQoKTtcbiAgICAgIGNvbnN0IGRlY29kZWRQdWJLZXkgPSBhd2FpdCBvcGVucGdwLnJlYWRLZXkoeyBhcm1vcmVkS2V5OiBzZW5kZXJLZXkucHVibGljS2V5IH0pLnNob3VsZC5iZS5mdWxmaWxsZWQoKTtcbiAgICAgIGNvbnN0IGlzVmFsaWQgPSAoYXdhaXQgb3BlbnBncFV0aWxzLnZlcmlmeVByaW1hcnlVc2VyV3JhcHBlcihkZWNvZGVkUHJvb2YsIGRlY29kZWRQdWJLZXksIHRydWUpKVswXS52YWxpZDtcbiAgICAgIHNob3VsZC5leGlzdChpc1ZhbGlkKTtcbiAgICAgIGlmIChpc1ZhbGlkICE9PSBudWxsKSB7XG4gICAgICAgIGlzVmFsaWQuc2hvdWxkLmJlLnRydWUoKTtcbiAgICAgIH1cbiAgICB9KTtcblxuICAgIGl0KCdzaG91bGQgdmVyaWZ5IHByaW1hcnkgdXNlciB3aXRob3V0IGEgZGF0ZSBjaGVjaycsIGFzeW5jIGZ1bmN0aW9uICgpIHtcbiAgICAgIGNvbnN0IHVWYWx1ZSA9IGNyeXB0by5yYW5kb21CeXRlcygzMikudG9TdHJpbmcoJ2hleCcpO1xuICAgICAgY29uc3QgcHJvb2YgPSBhd2FpdCBvcGVucGdwVXRpbHMuY3JlYXRlU2hhcmVQcm9vZihzZW5kZXJLZXkucHJpdmF0ZUtleSwgdVZhbHVlLCAnZWRkc2EnKTtcblxuICAgICAgLy8gdmVyaWZ5IHByb29mXG4gICAgICBjb25zdCBkZWNvZGVkUHJvb2YgPSBhd2FpdCBvcGVucGdwLnJlYWRLZXkoeyBhcm1vcmVkS2V5OiBwcm9vZiB9KS5zaG91bGQuYmUuZnVsZmlsbGVkKCk7XG4gICAgICBjb25zdCBkZWNvZGVkUHViS2V5ID0gYXdhaXQgb3BlbnBncC5yZWFkS2V5KHsgYXJtb3JlZEtleTogc2VuZGVyS2V5LnB1YmxpY0tleSB9KS5zaG91bGQuYmUuZnVsZmlsbGVkKCk7XG4gICAgICBjb25zdCBpc1ZhbGlkID0gKGF3YWl0IG9wZW5wZ3BVdGlscy52ZXJpZnlQcmltYXJ5VXNlcldyYXBwZXIoZGVjb2RlZFByb29mLCBkZWNvZGVkUHViS2V5LCBmYWxzZSkpWzBdLnZhbGlkO1xuICAgICAgc2hvdWxkLmV4aXN0KGlzVmFsaWQpO1xuICAgICAgaWYgKGlzVmFsaWQgIT09IG51bGwpIHtcbiAgICAgICAgaXNWYWxpZC5zaG91bGQuYmUudHJ1ZSgpO1xuICAgICAgfVxuICAgIH0pO1xuICB9KTtcblxuICBkZXNjcmliZSgndmVyaWZ5U2hhcmVQcm9vZiBFZERTQScsIGZ1bmN0aW9uICgpIHtcbiAgICBpdCgnc2hvdWxkIGJlIGFibGUgdG8gdmVyaWZ5IGEgdmFsaWQgcHJvb2YnLCBhc3luYyBmdW5jdGlvbiAoKSB7XG4gICAgICBjb25zdCB1VmFsdWUgPSBjcnlwdG8ucmFuZG9tQnl0ZXMoMzIpLnRvU3RyaW5nKCdoZXgnKTtcbiAgICAgIGNvbnN0IHByb29mID0gYXdhaXQgb3BlbnBncFV0aWxzLmNyZWF0ZVNoYXJlUHJvb2Yoc2VuZGVyS2V5LnByaXZhdGVLZXksIHVWYWx1ZSwgJ2VkZHNhJyk7XG4gICAgICBjb25zdCBpc1ZhbGlkID0gYXdhaXQgb3BlbnBncFV0aWxzLnZlcmlmeVNoYXJlUHJvb2Yoc2VuZGVyS2V5LnB1YmxpY0tleSwgcHJvb2YsIHVWYWx1ZSwgJ2VkZHNhJyk7XG4gICAgICBpc1ZhbGlkLnNob3VsZC5iZS50cnVlKCk7XG4gICAgfSk7XG5cbiAgICBpdCgnc2hvdWxkIGJlIGFibGUgdG8gZGV0ZWN0IHNlbmRlciBpcyBhbiBhdHRhY2tlcicsIGFzeW5jIGZ1bmN0aW9uICgpIHtcbiAgICAgIGNvbnN0IHVWYWx1ZSA9IGNyeXB0by5yYW5kb21CeXRlcygzMikudG9TdHJpbmcoJ2hleCcpO1xuICAgICAgY29uc3QgcHJvb2YgPSBhd2FpdCBvcGVucGdwVXRpbHMuY3JlYXRlU2hhcmVQcm9vZihvdGhlcktleS5wcml2YXRlS2V5LCB1VmFsdWUsICdlZGRzYScpO1xuICAgICAgY29uc3QgaXNWYWxpZCA9IGF3YWl0IG9wZW5wZ3BVdGlscy52ZXJpZnlTaGFyZVByb29mKHNlbmRlcktleS5wdWJsaWNLZXksIHByb29mLCB1VmFsdWUsICdlZGRzYScpO1xuICAgICAgaXNWYWxpZC5zaG91bGQuYmUuZmFsc2UoKTtcbiAgICB9KTtcblxuICAgIGl0KCdzaG91bGQgYmUgYWJsZSB0byBkZXRlY3QgdSB2YWx1ZSBpcyBjb3JydXB0ZWQnLCBhc3luYyBmdW5jdGlvbiAoKSB7XG4gICAgICBjb25zdCB1VmFsdWUgPSBjcnlwdG8ucmFuZG9tQnl0ZXMoMzIpLnRvU3RyaW5nKCdoZXgnKTtcbiAgICAgIGNvbnN0IHByb29mID0gYXdhaXQgb3BlbnBncFV0aWxzLmNyZWF0ZVNoYXJlUHJvb2Yoc2VuZGVyS2V5LnByaXZhdGVLZXksIHVWYWx1ZSwgJ2VkZHNhJyk7XG4gICAgICBjb25zdCB1VmFsdWVDb3JydXB0ZWQgPSBjcnlwdG8ucmFuZG9tQnl0ZXMoMzIpLnRvU3RyaW5nKCdoZXgnKTtcbiAgICAgIGNvbnN0IGlzVmFsaWQgPSBhd2FpdCBvcGVucGdwVXRpbHMudmVyaWZ5U2hhcmVQcm9vZihzZW5kZXJLZXkucHVibGljS2V5LCBwcm9vZiwgdVZhbHVlQ29ycnVwdGVkLCAnZWRkc2EnKTtcbiAgICAgIGlzVmFsaWQuc2hvdWxkLmJlLmZhbHNlKCk7XG4gICAgfSk7XG4gIH0pO1xuXG4gIGRlc2NyaWJlKCd2ZXJpZnlTaGFyZVByb29mIEVDRFNBJywgZnVuY3Rpb24gKCkge1xuICAgIGl0KCdzaG91bGQgYmUgYWJsZSB0byB2ZXJpZnkgYSB2YWxpZCBwcm9vZicsIGFzeW5jIGZ1bmN0aW9uICgpIHtcbiAgICAgIGNvbnN0IHVWYWx1ZSA9IGNyeXB0by5yYW5kb21CeXRlcygzMikudG9TdHJpbmcoJ2hleCcpO1xuICAgICAgY29uc3QgcHJvb2YgPSBhd2FpdCBvcGVucGdwVXRpbHMuY3JlYXRlU2hhcmVQcm9vZihzZW5kZXJLZXkucHJpdmF0ZUtleSwgdVZhbHVlLCAnZWNkc2EnKTtcbiAgICAgIGNvbnN0IGlzVmFsaWQgPSBhd2FpdCBvcGVucGdwVXRpbHMudmVyaWZ5U2hhcmVQcm9vZihzZW5kZXJLZXkucHVibGljS2V5LCBwcm9vZiwgdVZhbHVlLCAnZWNkc2EnKTtcbiAgICAgIGlzVmFsaWQuc2hvdWxkLmJlLnRydWUoKTtcbiAgICB9KTtcblxuICAgIGl0KCdzaG91bGQgYmUgYWJsZSB0byBkZXRlY3Qgc2VuZGVyIGlzIGFuIGF0dGFja2VyJywgYXN5bmMgZnVuY3Rpb24gKCkge1xuICAgICAgY29uc3QgdVZhbHVlID0gY3J5cHRvLnJhbmRvbUJ5dGVzKDMyKS50b1N0cmluZygnaGV4Jyk7XG4gICAgICBjb25zdCBwcm9vZiA9IGF3YWl0IG9wZW5wZ3BVdGlscy5jcmVhdGVTaGFyZVByb29mKG90aGVyS2V5LnByaXZhdGVLZXksIHVWYWx1ZSwgJ2VjZHNhJyk7XG4gICAgICBjb25zdCBpc1ZhbGlkID0gYXdhaXQgb3BlbnBncFV0aWxzLnZlcmlmeVNoYXJlUHJvb2Yoc2VuZGVyS2V5LnB1YmxpY0tleSwgcHJvb2YsIHVWYWx1ZSwgJ2VjZHNhJyk7XG4gICAgICBpc1ZhbGlkLnNob3VsZC5iZS5mYWxzZSgpO1xuICAgIH0pO1xuXG4gICAgaXQoJ3Nob3VsZCBiZSBhYmxlIHRvIGRldGVjdCB1IHZhbHVlIGlzIGNvcnJ1cHRlZCcsIGFzeW5jIGZ1bmN0aW9uICgpIHtcbiAgICAgIGNvbnN0IHVWYWx1ZSA9IGNyeXB0by5yYW5kb21CeXRlcygzMikudG9TdHJpbmcoJ2hleCcpO1xuICAgICAgY29uc3QgcHJvb2YgPSBhd2FpdCBvcGVucGdwVXRpbHMuY3JlYXRlU2hhcmVQcm9vZihzZW5kZXJLZXkucHJpdmF0ZUtleSwgdVZhbHVlLCAnZWNkc2EnKTtcbiAgICAgIGNvbnN0IHVWYWx1ZUNvcnJ1cHRlZCA9IGNyeXB0by5yYW5kb21CeXRlcygzMikudG9TdHJpbmcoJ2hleCcpO1xuICAgICAgY29uc3QgaXNWYWxpZCA9IGF3YWl0IG9wZW5wZ3BVdGlscy52ZXJpZnlTaGFyZVByb29mKHNlbmRlcktleS5wdWJsaWNLZXksIHByb29mLCB1VmFsdWVDb3JydXB0ZWQsICdlY2RzYScpO1xuICAgICAgaXNWYWxpZC5zaG91bGQuYmUuZmFsc2UoKTtcbiAgICB9KTtcbiAgfSk7XG5cbiAgZGVzY3JpYmUoJ3ZlcmlmeVNoYXJlZERhdGFQcm9vZiBhbmQgY3JlYXRlU2hhcmVkRGF0YVByb29mIHRlc3QnLCBmdW5jdGlvbiAoKSB7XG4gICAgaXQoJ3Nob3VsZCBiZSBhYmxlIHRvIGRldGVjdCBpZiBwcm9vZiB2YWx1ZSBpcyBjb3JydXB0ZWQgb3Igbm90JywgYXN5bmMgZnVuY3Rpb24gKCkge1xuICAgICAgY29uc3Qgc2hhcmVkRGF0YTEgPSBjcnlwdG8ucmFuZG9tQnl0ZXMoMzIpLnRvU3RyaW5nKCdoZXgnKTtcbiAgICAgIGNvbnN0IHNoYXJlZERhdGEyID0gY3J5cHRvLnJhbmRvbUJ5dGVzKDMyKS50b1N0cmluZygnaGV4Jyk7XG4gICAgICBjb25zdCBkYXRhVG9Qcm9vZkFycmF5ID0gW1xuICAgICAgICB7IG5hbWU6ICdzMScsIHZhbHVlOiBzaGFyZWREYXRhMSB9LFxuICAgICAgICB7IG5hbWU6ICdzMicsIHZhbHVlOiBzaGFyZWREYXRhMiB9LFxuICAgICAgXTtcbiAgICAgIGNvbnN0IHByb29mID0gYXdhaXQgb3BlbnBncFV0aWxzLmNyZWF0ZVNoYXJlZERhdGFQcm9vZihcbiAgICAgICAgc2VuZGVyS2V5LnByaXZhdGVLZXksXG4gICAgICAgIG90aGVyS2V5LnB1YmxpY0tleSxcbiAgICAgICAgZGF0YVRvUHJvb2ZBcnJheVxuICAgICAgKTtcbiAgICAgIGxldCBpc1ZhbGlkID0gYXdhaXQgb3BlbnBncFV0aWxzLnZlcmlmeVNoYXJlZERhdGFQcm9vZihzZW5kZXJLZXkucHVibGljS2V5LCBwcm9vZiwgZGF0YVRvUHJvb2ZBcnJheSk7XG4gICAgICBpc1ZhbGlkLnNob3VsZC5iZS50cnVlKCk7XG4gICAgICAvLyB0YW1wZXIgd2l0aCB0aGUgZGF0YVxuICAgICAgZGF0YVRvUHJvb2ZBcnJheVswXS52YWx1ZSA9ICd0YW1wZXJlZCBkYXRhJztcbiAgICAgIGlzVmFsaWQgPSBhd2FpdCBvcGVucGdwVXRpbHMudmVyaWZ5U2hhcmVkRGF0YVByb29mKHNlbmRlcktleS5wdWJsaWNLZXksIHByb29mLCBkYXRhVG9Qcm9vZkFycmF5KTtcbiAgICAgIGlzVmFsaWQuc2hvdWxkLmJlLmZhbHNlKCk7XG4gICAgfSk7XG5cbiAgICBpdCgnc2hvdWxkIGJlIGFibGUgdmVyaWZ5IGRhdGEgcHJvb2YgaWYgY3JlYXRlZCBpbiB0aGUgZnV0dXJlJywgYXN5bmMgZnVuY3Rpb24gKCkge1xuICAgICAgY29uc3Qgc2hhcmVkRGF0YTEgPSBjcnlwdG8ucmFuZG9tQnl0ZXMoMzIpLnRvU3RyaW5nKCdoZXgnKTtcbiAgICAgIGNvbnN0IHNoYXJlZERhdGEyID0gY3J5cHRvLnJhbmRvbUJ5dGVzKDMyKS50b1N0cmluZygnaGV4Jyk7XG4gICAgICBjb25zdCBkYXRhVG9Qcm9vZkFycmF5ID0gW1xuICAgICAgICB7IG5hbWU6ICdzMScsIHZhbHVlOiBzaGFyZWREYXRhMSB9LFxuICAgICAgICB7IG5hbWU6ICdzMicsIHZhbHVlOiBzaGFyZWREYXRhMiB9LFxuICAgICAgXTtcbiAgICAgIGNvbnN0IHByb29mID0gYXdhaXQgb3BlbnBncFV0aWxzLmNyZWF0ZVNoYXJlZERhdGFQcm9vZihcbiAgICAgICAgc2VuZGVyS2V5LnByaXZhdGVLZXksXG4gICAgICAgIG90aGVyS2V5LnB1YmxpY0tleSxcbiAgICAgICAgZGF0YVRvUHJvb2ZBcnJheVxuICAgICAgKTtcbiAgICAgIGNvbnN0IGNsb2NrID0gc2lub24udXNlRmFrZVRpbWVycyhuZXcgRGF0ZSgnMjAwMS0wMi0xNFQxMjowMDowMFonKS5nZXRUaW1lKCkpO1xuICAgICAgY29uc3QgaXNWYWxpZCA9IGF3YWl0IG9wZW5wZ3BVdGlscy52ZXJpZnlTaGFyZWREYXRhUHJvb2Yoc2VuZGVyS2V5LnB1YmxpY0tleSwgcHJvb2YsIGRhdGFUb1Byb29mQXJyYXkpO1xuICAgICAgaXNWYWxpZC5zaG91bGQuYmUudHJ1ZSgpO1xuICAgICAgY2xvY2sucmVzdG9yZSgpO1xuICAgIH0pO1xuICB9KTtcblxuICBkZXNjcmliZSgnZW5jcnlwdCBhbmQgZGVjcnlwdCB3aXRoIHNpZ25pbmcnLCBmdW5jdGlvbiAoKSB7XG4gICAgaXQoJ3Nob3VsZCBzdWNjZXNzZnVsbHkgZW5jcnlwdCwgc2lnbiwgYW5kIGRlY3J5cHQnLCBhc3luYyBmdW5jdGlvbiAoKSB7XG4gICAgICBjb25zdCB0ZXh0ID0gJ29yaWdpbmFsIG1lc3NhZ2UnO1xuXG4gICAgICBjb25zdCBzaWduZWRNZXNzYWdlID0gYXdhaXQgb3BlbnBncFV0aWxzLmVuY3J5cHRBbmRTaWduVGV4dCh0ZXh0LCByZWNpcGllbnRLZXkucHVibGljS2V5LCBzZW5kZXJLZXkucHJpdmF0ZUtleSk7XG4gICAgICBjb25zdCBkZWNyeXB0ZWRNZXNzYWdlID0gYXdhaXQgb3BlbnBncFV0aWxzLnJlYWRTaWduZWRNZXNzYWdlKFxuICAgICAgICBzaWduZWRNZXNzYWdlLFxuICAgICAgICBzZW5kZXJLZXkucHVibGljS2V5LFxuICAgICAgICByZWNpcGllbnRLZXkucHJpdmF0ZUtleVxuICAgICAgKTtcblxuICAgICAgZGVjcnlwdGVkTWVzc2FnZS5zaG91bGQuZXF1YWwodGV4dCk7XG4gICAgfSk7XG5cbiAgICBpdCgnc2hvdWxkIGZhaWwgb24gdmVyaWZpY2F0aW9uIHdpdGggd3JvbmcgcHVibGljIGtleScsIGFzeW5jIGZ1bmN0aW9uICgpIHtcbiAgICAgIGNvbnN0IHRleHQgPSAnb3JpZ2luYWwgbWVzc2FnZSc7XG5cbiAgICAgIGNvbnN0IHNpZ25lZE1lc3NhZ2UgPSBhd2FpdCBvcGVucGdwVXRpbHMuZW5jcnlwdEFuZFNpZ25UZXh0KHRleHQsIHJlY2lwaWVudEtleS5wdWJsaWNLZXksIHNlbmRlcktleS5wcml2YXRlS2V5KTtcbiAgICAgIGF3YWl0IG9wZW5wZ3BVdGlsc1xuICAgICAgICAucmVhZFNpZ25lZE1lc3NhZ2Uoc2lnbmVkTWVzc2FnZSwgb3RoZXJLZXkucHVibGljS2V5LCByZWNpcGllbnRLZXkucHJpdmF0ZUtleSlcbiAgICAgICAgLnNob3VsZC5iZS5yZWplY3RlZCgpO1xuICAgIH0pO1xuXG4gICAgaXQoJ3Nob3VsZCBmYWlsIG9uIGRlY3J5cHRpb24gd2l0aCB3cm9uZyBwcml2YXRlIGtleScsIGFzeW5jIGZ1bmN0aW9uICgpIHtcbiAgICAgIGNvbnN0IHRleHQgPSAnb3JpZ2luYWwgbWVzc2FnZSc7XG5cbiAgICAgIGNvbnN0IHNpZ25lZE1lc3NhZ2UgPSBhd2FpdCBvcGVucGdwVXRpbHMuZW5jcnlwdEFuZFNpZ25UZXh0KHRleHQsIHJlY2lwaWVudEtleS5wdWJsaWNLZXksIHNlbmRlcktleS5wcml2YXRlS2V5KTtcbiAgICAgIGF3YWl0IG9wZW5wZ3BVdGlsc1xuICAgICAgICAucmVhZFNpZ25lZE1lc3NhZ2Uoc2lnbmVkTWVzc2FnZSwgc2VuZGVyS2V5LnB1YmxpY0tleSwgb3RoZXJLZXkucHJpdmF0ZUtleSlcbiAgICAgICAgLnNob3VsZC5iZS5yZWplY3RlZFdpdGgoJ0Vycm9yIGRlY3J5cHRpbmcgbWVzc2FnZTogU2Vzc2lvbiBrZXkgZGVjcnlwdGlvbiBmYWlsZWQuJyk7XG4gICAgfSk7XG5cbiAgICBpdCgnc2hvdWxkIGVuY3J5cHQsIHNpZ24sIGFuZCBkZWNyeXB0IHdpdGhvdXQgcHJldmlvdXNseSBjbGVhcmluZyByZWplY3RlZEN1cnZlcycsIGFzeW5jIGZ1bmN0aW9uICgpIHtcbiAgICAgIG9wZW5wZ3AuY29uZmlnLnJlamVjdEN1cnZlcyA9IG5ldyBTZXQoW29wZW5wZ3AuZW51bXMuY3VydmUuc2VjcDI1NmsxXSk7XG5cbiAgICAgIGNvbnN0IHRleHQgPSAnb3JpZ2luYWwgbWVzc2FnZSc7XG4gICAgICBjb25zdCBzaWduZWRNZXNzYWdlID0gYXdhaXQgb3BlbnBncFV0aWxzLmVuY3J5cHRBbmRTaWduVGV4dCh0ZXh0LCByZWNpcGllbnRLZXkucHVibGljS2V5LCBzZW5kZXJLZXkucHJpdmF0ZUtleSk7XG4gICAgICBjb25zdCBkZWNyeXB0ZWRNZXNzYWdlID0gYXdhaXQgb3BlbnBncFV0aWxzLnJlYWRTaWduZWRNZXNzYWdlKFxuICAgICAgICBzaWduZWRNZXNzYWdlLFxuICAgICAgICBzZW5kZXJLZXkucHVibGljS2V5LFxuICAgICAgICByZWNpcGllbnRLZXkucHJpdmF0ZUtleVxuICAgICAgKTtcbiAgICAgIGRlY3J5cHRlZE1lc3NhZ2Uuc2hvdWxkLmVxdWFsKHRleHQpO1xuXG4gICAgICBvcGVucGdwLmNvbmZpZy5yZWplY3RDdXJ2ZXMgPSBuZXcgU2V0KCk7XG4gICAgfSk7XG4gIH0pO1xuXG4gIGRlc2NyaWJlKCdzaWduYXR1cmVzIGFuZCB2ZXJpZmljYXRpb24nLCBmdW5jdGlvbiAoKSB7XG4gICAgaXQoJ3Nob3VsZCB2ZXJpZnkgc2lnbmF0dXJlJywgYXN5bmMgZnVuY3Rpb24gKCkge1xuICAgICAgY29uc3QgdGV4dCA9ICdzb21lIHBheWxvYWQnO1xuICAgICAgY29uc3Qgc2lnbmF0dXJlID0gYXdhaXQgb3BlbnBncFV0aWxzLnNpZ25UZXh0KHRleHQsIHNlbmRlcktleS5wcml2YXRlS2V5KTtcbiAgICAgIGNvbnN0IGlzVmFsaWRTaWduYXR1cmUgPSBhd2FpdCBvcGVucGdwVXRpbHMudmVyaWZ5U2lnbmF0dXJlKHRleHQsIHNpZ25hdHVyZSwgc2VuZGVyS2V5LnB1YmxpY0tleSk7XG5cbiAgICAgIGlzVmFsaWRTaWduYXR1cmUuc2hvdWxkLmJlLnRydWUoKTtcbiAgICB9KTtcblxuICAgIGl0KCdzaG91bGQgZmFpbCB2ZXJpZmljYXRpb24gaWYgcHVibGljIGtleSBpcyBpbmNvcnJlY3QnLCBhc3luYyBmdW5jdGlvbiAoKSB7XG4gICAgICBjb25zdCB0ZXh0ID0gJ3NvbWUgcGF5bG9hZCc7XG4gICAgICBjb25zdCBzaWduYXR1cmUgPSBhd2FpdCBvcGVucGdwVXRpbHMuc2lnblRleHQodGV4dCwgc2VuZGVyS2V5LnByaXZhdGVLZXkpO1xuICAgICAgY29uc3QgaXNWYWxpZFNpZ25hdHVyZSA9IGF3YWl0IG9wZW5wZ3BVdGlscy52ZXJpZnlTaWduYXR1cmUodGV4dCwgc2lnbmF0dXJlLCByZWNpcGllbnRLZXkucHVibGljS2V5KTtcblxuICAgICAgaXNWYWxpZFNpZ25hdHVyZS5zaG91bGQuYmUuZmFsc2UoKTtcbiAgICB9KTtcblxuICAgIGl0KCdzaG91bGQgZmFpbCB2ZXJpZmljYXRpb24gaWYgbWVzc2FnZSBpcyBpbmNvcnJlY3QnLCBhc3luYyBmdW5jdGlvbiAoKSB7XG4gICAgICBjb25zdCB0ZXh0ID0gJ3NvbWUgcGF5bG9hZCc7XG4gICAgICBjb25zdCBzaWduYXR1cmUgPSBhd2FpdCBvcGVucGdwVXRpbHMuc2lnblRleHQodGV4dCwgc2VuZGVyS2V5LnByaXZhdGVLZXkpO1xuICAgICAgY29uc3QgaXNWYWxpZFNpZ25hdHVyZSA9IGF3YWl0IG9wZW5wZ3BVdGlscy52ZXJpZnlTaWduYXR1cmUoJ3NvbWV0aGluZyBlbHNlJywgc2lnbmF0dXJlLCBzZW5kZXJLZXkucHVibGljS2V5KTtcblxuICAgICAgaXNWYWxpZFNpZ25hdHVyZS5zaG91bGQuYmUuZmFsc2UoKTtcbiAgICB9KTtcbiAgfSk7XG5cbiAgZGVzY3JpYmUoJ0dQRyBrZXkgZ2VuZXJhdGlvbicsIGZ1bmN0aW9uICgpIHtcbiAgICBpdCgnc2hvdWxkIGdlbmVyYXRlIGEgYSBHUEcga2V5IGZvciBzZWNwMjU2azEgd2l0aCByYW5kb20gbmFtZSBhbmQgZW1haWwnLCBhc3luYyBmdW5jdGlvbiAoKSB7XG4gICAgICBjb25zdCBncGdLZXkgPSBhd2FpdCBvcGVucGdwVXRpbHMuZ2VuZXJhdGVHUEdLZXlQYWlyKCdzZWNwMjU2azEnKTtcblxuICAgICAgc2hvdWxkLmV4aXN0KGdwZ0tleSk7XG4gICAgICBzaG91bGQuZXhpc3QoZ3BnS2V5LnByaXZhdGVLZXkpO1xuICAgICAgc2hvdWxkLmV4aXN0KGdwZ0tleS5wdWJsaWNLZXkpO1xuICAgIH0pO1xuXG4gICAgaXQoJ3Nob3VsZCBnZW5lcmF0ZSBhIGEgR1BHIGtleSBmb3IgIHdpdGggcmFuZG9tIG5hbWUgYW5kIGVtYWlsJywgYXN5bmMgZnVuY3Rpb24gKCkge1xuICAgICAgY29uc3QgZ3BnS2V5ID0gYXdhaXQgb3BlbnBncFV0aWxzLmdlbmVyYXRlR1BHS2V5UGFpcignZWQyNTUxOScpO1xuXG4gICAgICBzaG91bGQuZXhpc3QoZ3BnS2V5KTtcbiAgICAgIHNob3VsZC5leGlzdChncGdLZXkucHJpdmF0ZUtleSk7XG4gICAgICBzaG91bGQuZXhpc3QoZ3BnS2V5LnB1YmxpY0tleSk7XG4gICAgfSk7XG5cbiAgICBpdCgnc2hvdWxkIGdlbmVyYXRlIGEgYSBHUEcga2V5IHdpdGggcHJvdmlkZWQgbmFtZSBhbmQgZW1haWwnLCBhc3luYyBmdW5jdGlvbiAoKSB7XG4gICAgICBjb25zdCB1c2VyTmFtZSA9ICdKb2huIERvZSc7XG4gICAgICBjb25zdCB1c2VyRW1haWwgPSAnam9obi5kb2VAZXhhbXBsZS5jb20nO1xuICAgICAgY29uc3QgZ3BnS