UNPKG

@bitgo/utxo-lib

Version:

Client-side Bitcoin JavaScript library

510 lines 71.7 kB
"use strict"; /** * This module provides taproot utilities including the legacy MuSig2 key aggregation * algorithm used by BitGo's deprecated `p2tr` script type (chains 30, 31). * * ## Legacy p2tr vs Standard p2trMusig2 * * BitGo supports two taproot address types: * * 1. **Legacy `p2tr` (chains 30, 31) - DEPRECATED** * - Uses the `aggregateMuSigPubkeys()` function in this module * - Based on an older MuSig2 variant that predates BIP327 * - Expects 32-byte x-only pubkeys with even Y coordinates * - Sorts keys AFTER x-only conversion * - Corresponds to MuSig2 before the 32-byte to 33-byte key change * - See: https://github.com/jonasnick/bips/pull/37 * * 2. **Standard `p2trMusig2` (chains 40, 41) - RECOMMENDED** * - Uses the `@brandonblack/musig` library (standard BIP327 implementation) * - Uses full 33-byte compressed pubkeys throughout aggregation * - Key order affects the resulting aggregate key * - Fully compatible with the BIP327 specification * * ## Key Difference * * The critical difference is **when x-only conversion happens**: * - Legacy: Converts to x-only BEFORE sorting (produces order-independent keys) * - Standard: Uses 33-byte keys throughout (key order matters) * * For the same two pubkeys, these methods produce DIFFERENT aggregate keys because * the sort order differs between 33-byte and 32-byte representations. * * See `modules/utxo-lib/bip-0327/README.md` for detailed comparison and test cases. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.INITIAL_TAPSCRIPT_VERSION = exports.EVEN_Y_COORD_PREFIX = void 0; exports.aggregateMuSigPubkeys = aggregateMuSigPubkeys; exports.serializeScriptSize = serializeScriptSize; exports.hashTapLeaf = hashTapLeaf; exports.hashTapBranch = hashTapBranch; exports.calculateTapTweak = calculateTapTweak; exports.tapTweakPrivkey = tapTweakPrivkey; exports.tapTweakPubkey = tapTweakPubkey; exports.getDepthFirstTaptree = getDepthFirstTaptree; exports.getHuffmanTaptree = getHuffmanTaptree; exports.getControlBlock = getControlBlock; exports.parseTaprootWitness = parseTaprootWitness; exports.parseControlBlock = parseControlBlock; exports.getTapleafHash = getTapleafHash; exports.getTaptreeRoot = getTaptreeRoot; exports.getTweakedOutputKey = getTweakedOutputKey; exports.createTaprootOutputScript = createTaprootOutputScript; exports.getTaprootOutputKey = getTaprootOutputKey; const assert = require("assert"); const FastPriorityQueue = require("fastpriorityqueue"); const bitcoinjs_lib_1 = require("bitcoinjs-lib"); const secp256k1_1 = require("@bitgo/secp256k1"); const varuint = require('varuint-bitcoin'); /** * The 0x02 prefix indicating an even Y coordinate which is implicitly assumed * on all 32 byte x-only pub keys as defined in BIP340. */ exports.EVEN_Y_COORD_PREFIX = Buffer.of(0x02); exports.INITIAL_TAPSCRIPT_VERSION = 0xc0; /** * Aggregates a list of public keys into a single public key using the legacy MuSig2 algorithm. * * This implements the deprecated key aggregation method used by BitGo's `p2tr` script type * (chains 30, 31). It corresponds to an older variant of MuSig2 that predates the change * from 32-byte to 33-byte keys in the BIP327 specification. * * ## Algorithm * * The implementation follows the MuSig2 key aggregation scheme: * * ``` * P = sum_i (μ_i * P_i) * ``` * * where: * - `P_i` is the public key of the i-th signer * - `μ_i` is the MuSig coefficient computed as: * - `L = TaggedHash("KeyAgg list", P_1 || P_2 || ... || P_n)` * - `μ_i = TaggedHash("KeyAgg coefficient", L || P_i)` for most keys * - `μ_i = 1` for the second unique key (optimization to save an exponentiation) * * ## Key Characteristics (Legacy Variant) * * 1. **X-only pubkeys**: Expects 32-byte x-only pubkeys (assumes even Y coordinates) * 2. **Pre-aggregation sorting**: Sorts keys in ascending order BEFORE aggregation * 3. **Order-independent**: Due to sorting, key order doesn't affect the result * 4. **33-byte internal representation**: Internally prepends 0x02 prefix for elliptic curve operations * * ## Differences from Standard MuSig2 (BIP327) * * The standard MuSig2 implementation (`@brandonblack/musig` library): * - Uses 33-byte compressed pubkeys throughout * - Does NOT sort keys before aggregation (key order matters) * - Produces different aggregate keys even for the same set of pubkeys * * ## Reference Implementation * * This corresponds to `key_agg_bitgo_p2tr_legacy()` in the Python reference implementation * at `modules/utxo-lib/bip-0327/reference.py`. * * @param ecc Elliptic curve implementation * @param pubkeys List of 32-byte x-only public keys to aggregate (must have even Y coordinates) * @returns 32-byte Buffer representing the x-only aggregate public key * @throws {Error} if fewer than 2 pubkeys provided or elliptic curve operations fail * * @see modules/utxo-lib/bip-0327/README.md for detailed comparison with standard MuSig2 * @see https://github.com/jonasnick/bips/pull/37 for the MuSig2 specification change */ function aggregateMuSigPubkeys(ecc, pubkeys) { // TODO: Consider enforcing key uniqueness. assert(pubkeys.length > 1, 'at least two pubkeys are required for musig key aggregation'); // LEGACY BEHAVIOR: Sort the keys in ascending order BEFORE aggregation. // This makes the aggregate key independent of input order. // Standard MuSig2 (BIP327) does NOT sort, making key order significant. pubkeys.sort(Buffer.compare); // Compute the "KeyAgg list" hash L = TaggedHash("KeyAgg list", P_1 || P_2 || ... || P_n) // This hash is used to derive the MuSig coefficients for each pubkey. // In the reference implementation (reference.py), this is done in hash_keys(). const L = bitcoinjs_lib_1.crypto.taggedHash('KeyAgg list', Buffer.concat(pubkeys)); // Find the second unique pubkey in the sorted list. // This key (and any keys identical to it) will use coefficient μ = 1 as an optimization. // This saves an expensive point multiplication (see MuSig2* appendix in the MuSig2 paper). // In the reference implementation, this is get_second_key(). const secondUniquePubkey = pubkeys.find((pubkey) => !pubkeys[0].equals(pubkey)); // For each pubkey P_i, compute the tweaked pubkey μ_i * P_i // where μ_i is the MuSig coefficient for that key. const tweakedPubkeys = pubkeys.map((pubkey) => { // Convert 32-byte x-only pubkey to 33-byte compressed format by prepending 0x02. // This assumes an even Y coordinate (standard for x-only pubkeys in taproot). // In the reference implementation, this is done implicitly in key_agg() via lift_x(). const xyPubkey = Buffer.concat([exports.EVEN_Y_COORD_PREFIX, pubkey]); if (secondUniquePubkey !== undefined && secondUniquePubkey.equals(pubkey)) { // Optimization: The second unique key gets coefficient μ = 1. // This means μ_i * P_i = 1 * P_i = P_i (no multiplication needed). // This saves an expensive elliptic curve point multiplication operation. // See key_agg_coeff_internal() in reference.py where it returns 1 for pk_ == pk2. return xyPubkey; } // Compute the MuSig coefficient: μ_i = TaggedHash("KeyAgg coefficient", L || P_i) // In the reference implementation, this is key_agg_coeff_internal(). const c = bitcoinjs_lib_1.crypto.taggedHash('KeyAgg coefficient', Buffer.concat([L, pubkey])); // Compute the tweaked pubkey: μ_i * P_i (elliptic curve point multiplication) const tweakedPubkey = ecc.pointMultiply(xyPubkey, c); if (!tweakedPubkey) { throw new Error('Failed to multiply pubkey by coefficient'); } return tweakedPubkey; }); // Sum all the tweaked pubkeys to get the aggregate pubkey: // P = sum_i (μ_i * P_i) = μ_1*P_1 + μ_2*P_2 + ... + μ_n*P_n // This is elliptic curve point addition. const aggregatePubkey = tweakedPubkeys.reduce((prev, curr) => { const next = ecc.pointAdd(prev, curr); if (!next) throw new Error('Failed to sum pubkeys'); return next; }); // Convert the aggregate pubkey from 33-byte compressed format back to 32-byte x-only format // by removing the first byte (the Y coordinate prefix). // In the reference implementation, this is done by get_xonly_pk() which calls xbytes(). return aggregatePubkey.slice(1); } /** * Encodes the length of a script as a bitcoin variable length integer. * @param script * @returns */ function serializeScriptSize(script) { return varuint.encode(script.length); } /** * Gets a tapleaf tagged hash from a script. * @param script * @returns */ function hashTapLeaf(script, leafVersion = exports.INITIAL_TAPSCRIPT_VERSION) { const size = serializeScriptSize(script); return bitcoinjs_lib_1.crypto.taggedHash('TapLeaf', Buffer.concat([Buffer.of(leafVersion), size, script])); } /** * Creates a lexicographically sorted tapbranch from two child taptree nodes * and returns its tagged hash. * @param child1 * @param child2 * @returns the tagged tapbranch hash */ function hashTapBranch(child1, child2) { // sort the children lexicographically const sortedChildren = [child1, child2].sort(Buffer.compare); return bitcoinjs_lib_1.crypto.taggedHash('TapBranch', Buffer.concat(sortedChildren)); } function calculateTapTweak(pubkey, taptreeRoot) { if (pubkey.length !== 32) { throw new Error(`Invalid pubkey size ${pubkey.length}.`); } if (taptreeRoot) { if (taptreeRoot.length !== 32) { throw new Error(`Invalid taptreeRoot size ${taptreeRoot.length}.`); } return bitcoinjs_lib_1.crypto.taggedHash('TapTweak', Buffer.concat([pubkey, taptreeRoot])); } // If the spending conditions do not require a script path, the output key should commit to an // unspendable script path instead of having no script path. // https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#cite_note-22 return bitcoinjs_lib_1.crypto.taggedHash('TapTweak', Buffer.from(pubkey)); } /** * Tweaks a privkey, using the tagged hash of its pubkey, and (optionally) a taptree root * @param ecc Elliptic curve implementation * @param pubkey public key, used to calculate the tweak * @param privkey the privkey to tweak * @param taptreeRoot the taptree root tagged hash * @returns {Buffer} the tweaked privkey */ function tapTweakPrivkey(ecc, pubkey, privkey, taptreeRoot) { const tapTweak = calculateTapTweak(pubkey, taptreeRoot); const point = ecc.pointFromScalar(privkey); if (!point) throw new Error('Invalid private key'); if (point[0] % 2 === 1) privkey = ecc.privateNegate(privkey); const result = ecc.privateAdd(privkey, tapTweak); if (!result) throw new Error('Invalid private key'); return result; } /** * Tweaks an internal pubkey, using the tagged hash of itself, and (optionally) a taptree root * @param ecc Elliptic curve implementation * @param pubkey the internal pubkey to tweak * @param taptreeRoot the taptree root tagged hash * @returns {TweakedPubkey} the tweaked pubkey */ function tapTweakPubkey(ecc, pubkey, taptreeRoot) { const tapTweak = calculateTapTweak(pubkey, taptreeRoot); const result = ecc.xOnlyPointAddTweak(pubkey, tapTweak); if (!result) throw new Error('Invalid pubkey'); return result; } function recurseTaptree(leaves, targetDepth = 0) { const { value, done } = leaves.next(); assert(!done, 'insufficient leaves to reconstruct tap tree'); const [index, leaf] = value; const tree = { root: hashTapLeaf(leaf.script, leaf.leafVersion), paths: [], }; tree.paths[index] = []; for (let depth = leaf.depth; depth > targetDepth; depth--) { const sibling = recurseTaptree(leaves, depth); tree.paths.forEach((path) => path.push(sibling.root)); sibling.paths.forEach((path) => path.push(tree.root)); tree.root = hashTapBranch(tree.root, sibling.root); // Merge disjoint sparse arrays of paths into tree.paths Object.assign(tree.paths, sibling.paths); } return tree; } /** * Gets the root hash and hash-paths of a taptree from the depth-first * construction used in BIP-0371 PSBTs * @param tree * @returns {Taptree} the tree, represented by its root hash, and the paths to * that root from each of the input scripts */ function getDepthFirstTaptree(tree) { const iter = tree.leaves.entries(); const ret = recurseTaptree(iter); assert(iter.next().done, 'invalid tap tree, no path to some leaves'); return ret; } /** * Gets the root hash of a taptree using a weighted Huffman construction from a * list of scripts and corresponding weights. * @param scripts * @param weights * @returns {Taptree} the tree, represented by its root hash, and the paths to that root from each of the input scripts */ function getHuffmanTaptree(scripts, weights) { assert(scripts.length > 0, 'at least one script is required to construct a tap tree'); // Create a queue/heap of the provided scripts prioritized according to their // corresponding weights. const queue = new FastPriorityQueue((a, b) => { return a.weight < b.weight; }); scripts.forEach((script, index) => { const weight = weights[index] || 1; assert(weight > 0, 'script weight must be a positive value'); queue.add({ weight, taggedHash: hashTapLeaf(script), paths: { [index]: [] }, }); }); // Now that we have a queue of weighted scripts, we begin a loop whereby we // remove the two lowest weighted items from the queue. We create a tap branch // node from the two items, and add the branch back to the queue with the // combined weight of both its children. Each loop reduces the number of items // in the queue by one, and we repeat until we are left with only one item - // this becomes the tap tree root. // // For example, if we begin with scripts A, B, C, D with weights 6, 3, 1, 1 // After first loop: A(6), B(3), CD(1 + 1) // After second loop: A(6), B[CD](3 + 2) // Final loop: A[B[CD]](6+5) // The final tree will look like: // // A[B[CD]] // / \ // A B[CD] // / \ // B [CD] // / \ // C D // // This ensures that the spending conditions we believe to have the highest // probability of being used are further up the tree than less likely scripts, // thereby reducing the size of the merkle proofs for the more likely scripts. while (queue.size > 1) { // We can safely expect two polls to return non-null elements since we've // checked that the queue has at least two elements before looping. const child1 = queue.poll(); const child2 = queue.poll(); Object.values(child1.paths).forEach((path) => path.push(child2.taggedHash)); Object.values(child2.paths).forEach((path) => path.push(child1.taggedHash)); queue.add({ taggedHash: hashTapBranch(child1.taggedHash, child2.taggedHash), weight: child1.weight + child2.weight, paths: { ...child1.paths, ...child2.paths }, }); } // After the while loop above completes we should have exactly one element // remaining in the queue, which we can safely extract below. const rootNode = queue.poll(); const paths = Object.entries(rootNode.paths).reduce((acc, [index, path]) => { acc[Number(index)] = path; // TODO: Why doesn't TS know it's a number? return acc; }, Array(scripts.length)); return { root: rootNode.taggedHash, paths }; } function getControlBlock(parity, pubkey, path, leafVersion = exports.INITIAL_TAPSCRIPT_VERSION) { const parityVersion = leafVersion + parity; return Buffer.concat([Buffer.of(parityVersion), pubkey, ...path]); } /** * Parses a taproot witness stack and extracts key data elements. * @param witnessStack * @returns {ScriptPathWitness|KeyPathWitness} an object representing the * parsed witness for a script path or key path spend. * @throws {Error} if the witness stack does not conform to the BIP 341 script validation rules */ function parseTaprootWitness(witnessStack) { let annex; if (witnessStack.length >= 2 && witnessStack[witnessStack.length - 1][0] === 0x50) { // If there are at least two witness elements, and the first byte of the last element is // 0x50, this last element is called annex a and is removed from the witness stack annex = witnessStack[witnessStack.length - 1]; witnessStack = witnessStack.slice(0, -1); } if (witnessStack.length < 1) { throw new Error('witness stack must have at least one element'); } else if (witnessStack.length === 1) { // key path spend const signature = witnessStack[0]; if (!bitcoinjs_lib_1.script.isCanonicalSchnorrSignature(signature)) { throw new Error('invalid signature'); } return { spendType: 'Key', signature, annex }; } // script path spend // second to last element is the tapscript const tapscript = witnessStack[witnessStack.length - 2]; const tapscriptChunks = bitcoinjs_lib_1.script.decompile(tapscript); if (!tapscriptChunks || tapscriptChunks.length === 0) { throw new Error('tapscript is not a valid script'); } // The last stack element is called the control block c, and must have length 33 + 32m, // for a value of m that is an integer between 0 and 128, inclusive const controlBlock = witnessStack[witnessStack.length - 1]; if (controlBlock.length < 33 || controlBlock.length > 33 + 32 * 128 || controlBlock.length % 32 !== 1) { throw new Error('invalid control block length'); } return { spendType: 'Script', scriptSig: witnessStack.slice(0, -2), tapscript, controlBlock, annex, }; } /** * Parses a taproot control block. * @param ecc Elliptic curve implementation * @param controlBlock the control block to parse * @returns {ControlBlock} the parsed control block * @throws {Error} if the witness stack does not conform to the BIP 341 script validation rules */ function parseControlBlock(ecc, controlBlock) { if ((controlBlock.length - 1) % 32 !== 0) { throw new TypeError('Invalid control block length'); } const parity = controlBlock[0] & 0x01; // Let p = c[1:33] and let P = lift_x(int(p)) where lift_x and [:] are defined as in BIP340. // Fail if this point is not on the curve const internalPubkey = controlBlock.slice(1, 33); if (!ecc.isXOnlyPoint(internalPubkey)) { throw new Error('internal pubkey is not an EC point'); } // The leaf version cannot be 0x50 as that would result in ambiguity with the annex. const leafVersion = controlBlock[0] & 0xfe; if (leafVersion === 0x50) { throw new Error('invalid leaf version'); } const path = []; for (let j = 33; j < controlBlock.length; j += 32) { path.push(controlBlock.slice(j, j + 32)); } return { parity, internalPubkey, leafVersion, path, }; } /** * Calculates the tapleaf hash from a control block and script. * @param ecc Elliptic curve implementation * @param controlBlock the control block, either raw or parsed * @param tapscript the leaf script corresdponding to the control block * @returns {Buffer} the tapleaf hash */ function getTapleafHash(ecc, controlBlock, tapscript) { if (Buffer.isBuffer(controlBlock)) { controlBlock = parseControlBlock(ecc, controlBlock); } const { leafVersion } = controlBlock; return bitcoinjs_lib_1.crypto.taggedHash('TapLeaf', Buffer.concat([Buffer.of(leafVersion), serializeScriptSize(tapscript), tapscript])); } /** * Calculates the taptree root hash from a control block and script. * @param ecc Elliptic curve implementation * @param controlBlock the control block, either raw or parsed * @param tapscript the leaf script corresdponding to the control block * @param tapleafHash the leaf hash if already calculated * @returns {Buffer} the taptree root hash */ function getTaptreeRoot(ecc, controlBlock, tapscript, tapleafHash) { if (Buffer.isBuffer(controlBlock)) { controlBlock = parseControlBlock(ecc, controlBlock); } const { path } = controlBlock; tapleafHash = tapleafHash || getTapleafHash(ecc, controlBlock, tapscript); // `taptreeMerkleHash` begins as our tapscript tapleaf hash and its value iterates // through its parent tapbranch hashes until it ends up as the taptree root hash let taptreeMerkleHash = tapleafHash; for (const taptreeSiblingHash of path) { taptreeMerkleHash = Buffer.compare(taptreeMerkleHash, taptreeSiblingHash) === -1 ? bitcoinjs_lib_1.crypto.taggedHash('TapBranch', Buffer.concat([taptreeMerkleHash, taptreeSiblingHash])) : bitcoinjs_lib_1.crypto.taggedHash('TapBranch', Buffer.concat([taptreeSiblingHash, taptreeMerkleHash])); } return taptreeMerkleHash; } function getTweakedOutputKey(payment) { assert(payment.output); if (payment.output.length === 34) { return payment.output?.subarray(2); } throw new Error(`invalid p2tr tweaked output key size ${payment.output.length}`); } /** * @returns output script for either script path input controlBlock * & leafScript OR key path input internalPubKey & taptreeRoot */ function createTaprootOutputScript(p2trArgs) { let internalPubKey; let taptreeRoot; if ('internalPubKey' in p2trArgs) { internalPubKey = p2trArgs.internalPubKey; taptreeRoot = p2trArgs.taptreeRoot; } else { internalPubKey = parseControlBlock(secp256k1_1.ecc, p2trArgs.controlBlock).internalPubkey; taptreeRoot = getTaptreeRoot(secp256k1_1.ecc, p2trArgs.controlBlock, p2trArgs.leafScript); } const outputKey = tapTweakPubkey(secp256k1_1.ecc, internalPubKey, taptreeRoot).xOnlyPubkey; return bitcoinjs_lib_1.script.compile([bitcoinjs_lib_1.script.OPS.OP_1, Buffer.from(outputKey)]); } /** * @returns x-only taproot output key (tapOutputKey) */ function getTaprootOutputKey(outputScript) { const outputDecompiled = bitcoinjs_lib_1.script.decompile(outputScript); if (outputDecompiled?.length !== 2) { throw new Error('invalid taproot output script'); } const [op1, outputKey] = outputDecompiled; if (op1 !== bitcoinjs_lib_1.script.OPS.OP_1 || !Buffer.isBuffer(outputKey) || outputKey.length !== 32) { throw new Error('invalid taproot output script'); } return outputKey; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGFwcm9vdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy90YXByb290LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FnQ0c7OztBQTJFSCxzREE2REM7QUFPRCxrREFFQztBQU9ELGtDQUdDO0FBU0Qsc0NBS0M7QUFFRCw4Q0FjQztBQVVELDBDQWNDO0FBY0Qsd0NBU0M7QUEyQ0Qsb0RBS0M7QUFTRCw4Q0FvRUM7QUFFRCwwQ0FTQztBQThCRCxrREEyQ0M7QUFTRCw4Q0ErQkM7QUFTRCx3Q0FjQztBQVVELHdDQXdCQztBQUVELGtEQU1DO0FBTUQsOERBY0M7QUFLRCxrREFVQztBQWxrQkQsaUNBQWtDO0FBQ2xDLHVEQUF3RDtBQUN4RCxpREFBNEY7QUFDNUYsZ0RBQWlEO0FBQ2pELE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO0FBRTNDOzs7R0FHRztBQUNVLFFBQUEsbUJBQW1CLEdBQUcsTUFBTSxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsQ0FBQztBQUN0QyxRQUFBLHlCQUF5QixHQUFHLElBQUksQ0FBQztBQVk5Qzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBZ0RHO0FBQ0gsU0FBZ0IscUJBQXFCLENBQUMsR0FBMkIsRUFBRSxPQUFpQjtJQUNsRiwyQ0FBMkM7SUFDM0MsTUFBTSxDQUFDLE9BQU8sQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFLDZEQUE2RCxDQUFDLENBQUM7SUFFMUYsd0VBQXdFO0lBQ3hFLDJEQUEyRDtJQUMzRCx3RUFBd0U7SUFDeEUsT0FBTyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUM7SUFFN0IseUZBQXlGO0lBQ3pGLHNFQUFzRTtJQUN0RSwrRUFBK0U7SUFDL0UsTUFBTSxDQUFDLEdBQUcsc0JBQU8sQ0FBQyxVQUFVLENBQUMsYUFB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TUFBTSxTQUFTLEdBQUcsY0FBYyxDQUFDLGVBQU0sRUFBRSxjQUFjLEVBQUUsV0FBVyxDQUFDLENBQUMsV0FBVyxDQUFDO0lBQ2xGLE9BQU8sc0JBQU8sQ0FBQyxPQUFPLENBQUMsQ0FBQyxzQkFBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUM7QUFDckUsQ0FBQztBQUVEOztHQUVHO0FBQ0gsU0FBZ0IsbUJBQW1CLENBQUMsWUFBMEM7SUFDNUUsTUFBTSxnQkFBZ0IsR0FBRyxzQkFBTyxDQUFDLFNBQVMsQ0FBQyxZQUFZLENBQUMsQ0FBQztJQUN6RCxJQUFJLGdCQUFnQixFQUFFLE1BQU0sS0FBSyxDQUFDLEVBQUUsQ0FBQztRQUNuQyxNQUFNLElBQUksS0FBSyxDQUFDLCtCQUErQixDQUFDLENBQUM7SUFDbkQsQ0FBQztJQUNELE1BQU0sQ0FBQyxHQUFHLEVBQUUsU0FBUyxDQUFDLEdBQUcsZ0JBQWdCLENBQUM7SUFDMUMsSUFBSSxHQUFHLEtBQUssc0JBQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxJQUFJLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxTQUFTLENBQUMsSUFBSSxTQUFTLENBQUMsTUFBTSxLQUFLLEVBQUUsRUFBRSxDQUFDO1FBQ3ZGLE1BQU0sSUFBSSxLQUFLLENBQUMsK0JBQStCLENBQUMsQ0FBQztJQUNuRCxDQUFDO0lBQ0QsT0FBTyxTQUFTLENBQUM7QUFDbkIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogVGhpcyBtb2R1bGUgcHJvdmlkZXMgdGFwcm9vdCB1dGlsaXRpZXMgaW5jbHVkaW5nIHRoZSBsZWdhY3kgTXVTaWcyIGtleSBhZ2dyZWdhdGlvblxuICogYWxnb3JpdGhtIHVzZWQgYnkgQml0R28ncyBkZXByZWNhdGVkIGBwMnRyYCBzY3JpcHQgdHlwZSAoY2hhaW5zIDMwLCAzMSkuXG4gKlxuICogIyMgTGVnYWN5IHAydHIgdnMgU3RhbmRhcmQgcDJ0ck11c2lnMlxuICpcbiAqIEJpdEdvIHN1cHBvcnRzIHR3byB0YXByb290IGFkZHJlc3MgdHlwZXM6XG4gKlxuICogMS4gKipMZWdhY3kgYHAydHJgIChjaGFpbnMgMzAsIDMxKSAtIERFUFJFQ0FURUQqKlxuICogICAgLSBVc2VzIHRoZSBgYWdncmVnYXRlTXVTaWdQdWJrZXlzKClgIGZ1bmN0aW9uIGluIHRoaXMgbW9kdWxlXG4gKiAgICAtIEJhc2VkIG9uIGFuIG9sZGVyIE11U2lnMiB2YXJpYW50IHRoYXQgcHJlZGF0ZXMgQklQMzI3XG4gKiAgICAtIEV4cGVjdHMgMzItYnl0ZSB4LW9ubHkgcHVia2V5cyB3aXRoIGV2ZW4gWSBjb29yZGluYXRlc1xuICogICAgLSBTb3J0cyBrZXlzIEFGVEVSIHgtb25seSBjb252ZXJzaW9uXG4gKiAgICAtIENvcnJlc3BvbmRzIHRvIE11U2lnMiBiZWZvcmUgdGhlIDMyLWJ5dGUgdG8gMzMtYnl0ZSBrZXkgY2hhbmdlXG4gKiAgICAtIFNlZTogaHR0cHM6Ly9naXRodWIuY29tL2pvbmFzbmljay9iaXBzL3B1bGwvMzdcbiAqXG4gKiAyLiAqKlN0YW5kYXJkIGBwMnRyTXVzaWcyYCAoY2hhaW5zIDQwLCA0MSkgLSBSRUNPTU1FTkRFRCoqXG4gKiAgICAtIFVzZXMgdGhlIGBAYnJhbmRvbmJsYWNrL211c2lnYCBsaWJyYXJ5IChzdGFuZGFyZCBCSVAzMjcgaW1wbGVtZW50YXRpb24pXG4gKiAgICAtIFVzZXMgZnVsbCAzMy1ieXRlIGNvbXByZXNzZWQgcHVia2V5cyB0aHJvdWdob3V0IGFnZ3JlZ2F0aW9uXG4gKiAgICAtIEtleSBvcmRlciBhZmZlY3RzIHRoZSByZXN1bHRpbmcgYWdncmVnYXRlIGtleVxuICogICAgLSBGdWxseSBjb21wYXRpYmxlIHdpdGggdGhlIEJJUDMyNyBzcGVjaWZpY2F0aW9uXG4gKlxuICogIyMgS2V5IERpZmZlcmVuY2VcbiAqXG4gKiBUaGUgY3JpdGljYWwgZGlmZmVyZW5jZSBpcyAqKndoZW4geC1vbmx5IGNvbnZlcnNpb24gaGFwcGVucyoqOlxuICogLSBMZWdhY3k6IENvbnZlcnRzIHRvIHgtb25seSBCRUZPUkUgc29ydGluZyAocHJvZHVjZXMgb3JkZXItaW5kZXBlbmRlbnQga2V5cylcbiAqIC0gU3RhbmRhcmQ6IFVzZXMgMzMtYnl0ZSBrZXlzIHRocm91Z2hvdXQgKGtleSBvcmRlciBtYXR0ZXJzKVxuICpcbiAqIEZvciB0aGUgc2FtZSB0d28gcHVia2V5cywgdGhlc2UgbWV0aG9kcyBwcm9kdWNlIERJRkZFUkVOVCBhZ2dyZWdhdGUga2V5cyBiZWNhdXNlXG4gKiB0aGUgc29ydCBvcmRlciBkaWZmZXJzIGJldHdlZW4gMzMtYnl0ZSBhbmQgMzItYnl0ZSByZXByZXNlbnRhdGlvbnMuXG4gKlxuICogU2VlIGBtb2R1bGVzL3V0eG8tbGliL2JpcC0wMzI3L1JFQURNRS5tZGAgZm9yIGRldGFpbGVkIGNvbXBhcmlzb24gYW5kIHRlc3QgY2FzZXMuXG4gKi9cblxuaW1wb3J0IHsgVGFwVHJlZSBhcyBQc2J0VGFwVHJlZSwgVGFwTGVhZiBhcyBQc2J0VGFwTGVhZiB9IGZyb20gJ2JpcDE3NC9zcmMvbGliL2ludGVyZmFjZXMnO1xuaW1wb3J0IGFzc2VydCA9IHJlcXVpcmUoJ2Fzc2VydCcpO1xuaW1wb3J0IEZhc3RQcmlvcml0eVF1ZXVlID0gcmVxdWlyZSgnZmFzdHByaW9yaXR5cXVldWUnKTtcbmltcG9ydCB7IHNjcmlwdCBhcyBic2NyaXB0LCBjcnlwdG8gYXMgYmNyeXB0bywgcGF5bWVudHMgYXMgYnBheW1lbnRzIH0gZnJvbSAnYml0Y29pbmpzLWxpYic7XG5pbXBvcnQgeyBlY2MgYXMgZWNjTGliIH0gZnJvbSAnQGJpdGdvL3NlY3AyNTZrMSc7XG5jb25zdCB2YXJ1aW50ID0gcmVxdWlyZSgndmFydWludC1iaXRjb2luJyk7XG5cbi8qKlxuICogVGhlIDB4MDIgcHJlZml4IGluZGljYXRpbmcgYW4gZXZlbiBZIGNvb3JkaW5hdGUgd2hpY2ggaXMgaW1wbGljaXRseSBhc3N1bWVkXG4gKiBvbiBhbGwgMzIgYnl0ZSB4LW9ubHkgcHViIGtleXMgYXMgZGVmaW5lZCBpbiBCSVAzNDAuXG4gKi9cbmV4cG9ydCBjb25zdCBFVkVOX1lfQ09PUkRfUFJFRklYID0gQnVmZmVyLm9mKDB4MDIpO1xuZXhwb3J0IGNvbnN0IElOSVRJQUxfVEFQU0NSSVBUX1ZFUlNJT04gPSAweGMwO1xuXG5leHBvcnQgaW50ZXJmYWNlIFRpbnlTZWNwMjU2azFJbnRlcmZhY2Uge1xuICBpc1hPbmx5UG9pbnQocDogVWludDhBcnJheSk6IGJvb2xlYW47XG4gIHhPbmx5UG9pbnRBZGRUd2VhayhwOiBVaW50OEFycmF5LCB0d2VhazogVWludDhBcnJheSk6IFhPbmx5UG9pbnRBZGRUd2Vha1Jlc3VsdCB8IG51bGw7XG4gIHBvaW50RnJvbVNjYWxhcihzazogVWludDhBcnJheSwgY29tcHJlc3NlZD86IGJvb2xlYW4pOiBVaW50OEFycmF5IHwgbnVsbDtcbiAgcG9pbnRNdWx0aXBseShhOiBVaW50OEFycmF5LCBiOiBVaW50OEFycmF5KTogVWludDhBcnJheSB8IG51bGw7XG4gIHBvaW50QWRkKGE6IFVpbnQ4QXJyYXksIGI6IFVpbnQ4QXJyYXkpOiBVaW50OEFycmF5IHwgbnVsbDtcbiAgcHJpdmF0ZUFkZChkOiBVaW50OEFycmF5LCB0d2VhazogVWludDhBcnJheSk6IFVpbnQ4QXJyYXkgfCBudWxsO1xuICBwcml2YXRlTmVnYXRlKGQ6IFVpbnQ4QXJyYXkpOiBVaW50OEFycmF5O1xufVxuXG4vKipcbiAqIEFnZ3JlZ2F0ZXMgYSBsaXN0IG9mIHB1YmxpYyBrZXlzIGludG8gYSBzaW5nbGUgcHVibGljIGtleSB1c2luZyB0aGUgbGVnYWN5IE11U2lnMiBhbGdvcml0aG0uXG4gKlxuICogVGhpcyBpbXBsZW1lbnRzIHRoZSBkZXByZWNhdGVkIGtleSBhZ2dyZWdhdGlvbiBtZXRob2QgdXNlZCBieSBCaXRHbydzIGBwMnRyYCBzY3JpcHQgdHlwZVxuICogKGNoYWlucyAzMCwgMzEpLiBJdCBjb3JyZXNwb25kcyB0byBhbiBvbGRlciB2YXJpYW50IG9mIE11U2lnMiB0aGF0IHByZWRhdGVzIHRoZSBjaGFuZ2VcbiAqIGZyb20gMzItYnl0ZSB0byAzMy1ieXRlIGtleXMgaW4gdGhlIEJJUDMyNyBzcGVjaWZpY2F0aW9uLlxuICpcbiAqICMjIEFsZ29yaXRobVxuICpcbiAqIFRoZSBpbXBsZW1lbnRhdGlvbiBmb2xsb3dzIHRoZSBNdVNpZzIga2V5IGFnZ3JlZ2F0aW9uIHNjaGVtZTpcbiAqXG4gKiBgYGBcbiAqIFAgPSBzdW1faSAozrxfaSAqIFBfaSlcbiAqIGBgYFxuICpcbiAqIHdoZXJlOlxuICogLSBgUF9pYCBpcyB0aGUgcHVibGljIGtleSBvZiB0aGUgaS10aCBzaWduZXJcbiAqIC0gYM68X2lgIGlzIHRoZSBNdVNpZyBjb2VmZmljaWVudCBjb21wdXRlZCBhczpcbiAqICAgLSBgTCA9IFRhZ2dlZEhhc2goXCJLZXlBZ2cgbGlzdFwiLCBQXzEgfHwgUF8yIHx8IC4uLiB8fCBQX24pYFxuICogICAtIGDOvF9pID0gVGFnZ2VkSGFzaChcIktleUFnZyBjb2VmZmljaWVudFwiLCBMIHx8IFBfaSlgIGZvciBtb3N0IGtleXNcbiAqICAgLSBgzrxfaSA9IDFgIGZvciB0aGUgc2Vjb25kIHVuaXF1ZSBrZXkgKG9wdGltaXphdGlvbiB0byBzYXZlIGFuIGV4cG9uZW50aWF0aW9uKVxuICpcbiAqICMjIEtleSBDaGFyYWN0ZXJpc3RpY3MgKExlZ2FjeSBWYXJpYW50KVxuICpcbiAqIDEuICoqWC1vbmx5IHB1YmtleXMqKjogRXhwZWN0cyAzMi1ieXRlIHgtb25seSBwdWJrZXlzIChhc3N1bWVzIGV2ZW4gWSBjb29yZGluYXRlcylcbiAqIDIuICoqUHJlLWFnZ3JlZ2F0aW9uIHNvcnRpbmcqKjogU29ydHMga2V5cyBpbiBhc2NlbmRpbmcgb3JkZXIgQkVGT1JFIGFnZ3JlZ2F0aW9uXG4gKiAzLiAqKk9yZGVyLWluZGVwZW5kZW50Kio6IER1ZSB0byBzb3J0aW5nLCBrZXkgb3JkZXIgZG9lc24ndCBhZmZlY3QgdGhlIHJlc3VsdFxuICogNC4gKiozMy1ieXRlIGludGVybmFsIHJlcHJlc2VudGF0aW9uKio6IEludGVybmFsbHkgcHJlcGVuZHMgMHgwMiBwcmVmaXggZm9yIGVsbGlwdGljIGN1cnZlIG9wZXJhdGlvbnNcbiAqXG4gKiAjIyBEaWZmZXJlbmNlcyBmcm9tIFN0YW5kYXJkIE11U2lnMiAoQklQMzI3KVxuICpcbiAqIFRoZSBzdGFuZGFyZCBNdVNpZzIgaW1wbGVtZW50YXRpb24gKGBAYnJhbmRvbmJsYWNrL211c2lnYCBsaWJyYXJ5KTpcbiAqIC0gVXNlcyAzMy1ieXRlIGNvbXByZXNzZWQgcHVia2V5cyB0aHJvdWdob3V0XG4gKiAtIERvZXMgTk9UIHNvcnQga2V5cyBiZWZvcmUgYWdncmVnYXRpb24gKGtleSBvcmRlciBtYXR0ZXJzKVxuICogLSBQcm9kdWNlcyBkaWZmZXJlbnQgYWdncmVnYXRlIGtleXMgZXZlbiBmb3IgdGhlIHNhbWUgc2V0IG9mIHB1YmtleXNcbiAqXG4gKiAjIyBSZWZlcmVuY2UgSW1wbGVtZW50YXRpb25cbiAqXG4gKiBUaGlzIGNvcnJlc3BvbmRzIHRvIGBrZXlfYWdnX2JpdGdvX3AydHJfbGVnYWN5KClgIGluIHRoZSBQeXRob24gcmVmZXJlbmNlIGltcGxlbWVudGF0aW9uXG4gKiBhdCBgbW9kdWxlcy91dHhvLWxpYi9iaXAtMDMyNy9yZWZlcmVuY2UucHlgLlxuICpcbiAqIEBwYXJhbSBlY2MgRWxsaXB0aWMgY3VydmUgaW1wbGVtZW50YXRpb25cbiAqIEBwYXJhbSBwdWJrZXlzIExpc3Qgb2YgMzItYnl0ZSB4LW9ubHkgcHVibGljIGtleXMgdG8gYWdncmVnYXRlIChtdXN0IGhhdmUgZXZlbiBZIGNvb3JkaW5hdGVzKVxuICogQHJldHVybnMgMzItYnl0ZSBCdWZmZXIgcmVwcmVzZW50aW5nIHRoZSB4LW9ubHkgYWdncmVnYXRlIHB1YmxpYyBrZXlcbiAqIEB0aHJvd3Mge0Vycm9yfSBpZiBmZXdlciB0aGFuIDIgcHVia2V5cyBwcm92aWRlZCBvciBlbGxpcHRpYyBjdXJ2ZSBvcGVyYXRpb25zIGZhaWxcbiAqXG4gKiBAc2VlIG1vZHVsZXMvdXR4by1saWIvYmlwLTAzMjcvUkVBRE1FLm1kIGZvciBkZXRhaWxlZCBjb21wYXJpc29uIHdpdGggc3RhbmRhcmQgTXVTaWcyXG4gKiBAc2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9qb25hc25pY2svYmlwcy9wdWxsLzM3IGZvciB0aGUgTXVTaWcyIHNwZWNpZmljYXRpb24gY2hhbmdlXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBhZ2dyZWdhdGVNdVNpZ1B1YmtleXMoZWNjOiBUaW55U2VjcDI1NmsxSW50ZXJmYWNlLCBwdWJrZXlzOiBCdWZmZXJbXSk6IFVpbnQ4QXJyYXkge1xuICAvLyBUT0RPOiBDb25zaWRlciBlbmZvcmNpbmcga2V5IHVuaXF1ZW5lc3MuXG4gIGFzc2VydChwdWJrZXlzLmxlbmd0aCA+IDEsICdhdCBsZWFzdCB0d28gcHVia2V5cyBhcmUgcmVxdWlyZWQgZm9yIG11c2lnIGtleSBhZ2dyZWdhdGlvbicpO1xuXG4gIC8vIExFR0FDWSBCRUhBVklPUjogU29ydCB0aGUga2V5cyBpbiBhc2NlbmRpbmcgb3JkZXIgQkVGT1JFIGFnZ3JlZ2F0aW9uLlxuICAvLyBUaGlzIG1ha2VzIHRoZSBhZ2dyZWdhdGUga2V5IGluZGVwZW5kZW50IG9mIGlucHV0IG9yZGVyLlxuICAvLyBTdGFuZGFyZCBNdVNpZzIgKEJJUDMyNykgZG9lcyBOT1Qgc29ydCwgbWFraW5nIGtleSBvcmRlciBzaWduaWZpY2FudC5cbiAgcHVia2V5cy5zb3J0KEJ1ZmZlci5jb21wYXJlKTtcblxuICAvLyBDb21wdXRlIHRoZSBcIktleUFnZyBsaXN0XCIgaGFzaCBMID0gVGFnZ2VkSGFzaChcIktleUFnZyBsaXN0XCIsIFBfMSB8fCBQXzIgfHwgLi4uIHx8IFBfbilcbiAgLy8gVGhpcyBoYXNoIGlzIHVzZWQgdG8gZGVyaXZlIHRoZSBNdVNpZyBjb2VmZmljaWVudHMgZm9yIGVhY2ggcHVia2V5LlxuICAvLyBJbiB0aGUgcmVmZXJlbmNlIGltcGxlbWVudGF0aW9uIChyZWZlcmVuY2UucHkpLCB0aGlzIGlzIGRvbmUgaW4gaGFzaF9rZXlzKCkuXG4gIGNvbnN0IEwgPSBiY3J5cHRvLnRhZ2dlZEhhc2goJ0tleUFnZyBsaXN0JywgQnVmZmVyLmNvbmNhdChwdWJrZXlzKSk7XG5cbiAgLy8gRmluZCB0aGUgc2Vjb25kIHVuaXF1ZSBwdWJrZXkgaW4gdGhlIHNvcnRlZCBsaXN0LlxuICAvLyBUaGlzIGtleSAoYW5kIGFueSBrZXlzIGlkZW50aWNhbCB0byBpdCkgd2lsbCB1c2UgY29lZmZpY2llbnQgzrwgPSAxIGFzIGFuIG9wdGltaXphdGlvbi5cbiAgLy8gVGhpcyBzYXZlcyBhbiBleHBlbnNpdmUgcG9pbnQgbXVsdGlwbGljYXRpb24gKHNlZSBNdVNpZzIqIGFwcGVuZGl4IGluIHRoZSBNdVNpZzIgcGFwZXIpLlxuICAvLyBJbiB0aGUgcmVmZXJlbmNlIGltcGxlbWVudGF0aW9uLCB0aGlzIGlzIGdldF9zZWNvbmRfa2V5KCkuXG4gIGNvbnN0IHNlY29uZFVuaXF1ZVB1YmtleSA9IHB1YmtleXMuZmluZCgocHVia2V5KSA9PiAhcHVia2V5c1swXS5lcXVhbHMocHVia2V5KSk7XG5cbiAgLy8gRm9yIGVhY2ggcHVia2V5IFBfaSwgY29tcHV0ZSB0aGUgdHdlYWtlZCBwdWJrZXkgzrxfaSAqIFBfaVxuICAvLyB3aGVyZSDOvF9pIGlzIHRoZSBNdVNpZyBjb2VmZmljaWVudCBmb3IgdGhhdCBrZXkuXG4gIGNvbnN0IHR3ZWFrZWRQdWJrZXlzOiBVaW50OEFycmF5W10gPSBwdWJrZXlzLm1hcCgocHVia2V5KSA9PiB7XG4gICAgLy8gQ29udmVydCAzMi1ieXRlIHgtb25seSBwdWJrZXkgdG8gMzMtYnl0ZSBjb21wcmVzc2VkIGZvcm1hdCBieSBwcmVwZW5kaW5nIDB4MDIuXG4gICAgLy8gVGhpcyBhc3N1bWVzIGFuIGV2ZW4gWSBjb29yZGluYXRlIChzdGFuZGFyZCBmb3IgeC1vbmx5IHB1YmtleXMgaW4gdGFwcm9vdCkuXG4gICAgLy8gSW4gdGhlIHJlZmVyZW5jZSBpbXBsZW1lbnRhdGlvbiwgdGhpcyBpcyBkb25lIGltcGxpY2l0bHkgaW4ga2V5X2FnZygpIHZpYSBsaWZ0X3goKS5cbiAgICBjb25zdCB4eVB1YmtleSA9IEJ1ZmZlci5jb25jYXQoW0VWRU5fWV9DT09SRF9QUkVGSVgsIHB1YmtleV0pO1xuXG4gICAgaWYgKHNlY29uZFVuaXF1ZVB1YmtleSAhPT0gdW5kZWZpbmVkICYmIHNlY29uZFVuaXF1ZVB1YmtleS5lcXVhbHMocHVia2V5KSkge1xuICAgICAgLy8gT3B0aW1pemF0aW9uOiBUaGUgc2Vjb25kIHVuaXF1ZSBrZXkgZ2V0cyBjb2VmZmljaWVudCDOvCA9IDEuXG4gICAgICAvLyBUaGlzIG1lYW5zIM68X2kgKiBQX2kgPSAxICogUF9pID0gUF9pIChubyBtdWx0aXBsaWNhdGlvbiBuZWVkZWQpLlxuICAgICAgLy8gVGhpcyBzYXZlcyBhbiBleHBlbnNpdmUgZWxsaXB0aWMgY3VydmUgcG9pbnQgbXVsdGlwbGljYXRpb24gb3BlcmF0aW9uLlxuICAgICAgLy8gU2VlIGtleV9hZ2dfY29lZmZfaW50ZXJuYWwoKSBpbiByZWZlcmVuY2UucHkgd2hlcmUgaXQgcmV0dXJucyAxIGZvciBwa18gPT0gcGsyLlxuICAgICAgcmV0dXJuIHh5UHVia2V5O1xuICAgIH1cblxuICAgIC8vIENvbXB1dGUgdGhlIE11U2lnIGNvZWZmaWNpZW50OiDOvF9pID0gVGFnZ2VkSGFzaChcIktleUFnZyBjb2VmZmljaWVudFwiLCBMIHx8IFBfaSlcbiAgICAvLyBJbiB0aGUgcmVmZXJlbmNlIGltcGxlbWVudGF0aW9uLCB0aGlzIGlzIGtleV9hZ2dfY29lZmZfaW50ZXJuYWwoKS5cbiAgICBjb25zdCBjID0gYmNyeXB0by50YWdnZWRIYXNoKCdLZXlBZ2cgY29lZmZpY2llbnQnLCBCdWZmZXIuY29uY2F0KFtMLCBwdWJrZXldKSk7XG5cbiAgICAvLyBDb21wdXRlIHRoZSB0d2Vha2VkIHB1YmtleTogzrxfaSAqIFBfaSAoZWxsaXB0aWMgY3VydmUgcG9pbnQgbXVsdGlwbGljYXRpb24pXG4gICAgY29uc3QgdHdlYWtlZFB1YmtleSA9IGVjYy5wb2ludE11bHRpcGx5KHh5UHVia2V5LCBjKTtcbiAgICBpZiAoIXR3ZWFrZWRQdWJrZXkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignRmFpbGVkIHRvIG11bHRpcGx5IHB1YmtleSBieSBjb2VmZmljaWVudCcpO1xuICAgIH1cbiAgICByZXR1cm4gdHdlYWtlZFB1YmtleTtcbiAgfSk7XG5cbiAgLy8gU3VtIGFsbCB0aGUgdHdlYWtlZCBwdWJrZXlzIHRvIGdldCB0aGUgYWdncmVnYXRlIHB1YmtleTpcbiAgLy8gUCA9IHN1bV9pICjOvF9pICogUF9pKSA9IM68XzEqUF8xICsgzrxfMipQXzIgKyAuLi4gKyDOvF9uKlBfblxuICAvLyBUaGlzIGlzIGVsbGlwdGljIGN1cnZlIHBvaW50IGFkZGl0aW9uLlxuICBjb25zdCBhZ2dyZWdhdGVQdWJrZXkgPSB0d2Vha2VkUHVia2V5cy5yZWR1Y2UoKHByZXYsIGN1cnIpID0+IHtcbiAgICBjb25zdCBuZXh0ID0g