sliftutils 1.0.2 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursorrules +3 -1
- package/index.d.ts +144 -1
- package/misc/https/certs.d.ts +71 -0
- package/misc/https/certs.ts +568 -0
- package/misc/https/dist/certs.ts.cache +530 -0
- package/misc/https/dist/dns.ts.cache +166 -0
- package/misc/https/dist/httpsCerts.ts.cache +181 -0
- package/misc/https/dist/persistentLocalStorage.ts.cache +45 -0
- package/misc/https/dns.d.ts +13 -0
- package/misc/https/dns.ts +163 -0
- package/misc/https/httpsCerts.d.ts +18 -0
- package/misc/https/httpsCerts.ts +184 -0
- package/misc/https/node-forge-ed25519.d.ts +17 -0
- package/misc/https/persistentLocalStorage.d.ts +5 -0
- package/misc/https/persistentLocalStorage.ts +36 -0
- package/misc/openrouter.d.ts +1 -1
- package/misc/openrouter.ts +1 -1
- package/package.json +3 -1
- package/render-utils/Anchor.tsx +2 -1
- package/web/Page.tsx +1 -1
- package/web/browser.tsx +1 -1
- package/web/index.html +3 -0
- package/yarn.lock +338 -3
|
@@ -0,0 +1,530 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/// <reference path="./node-forge-ed25519.d.ts" />
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
20
|
+
if (mod && mod.__esModule) return mod;
|
|
21
|
+
var result = {};
|
|
22
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
23
|
+
__setModuleDefault(result, mod);
|
|
24
|
+
return result;
|
|
25
|
+
};
|
|
26
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
27
|
+
return (mod && mod.__esModule && mod.default) ? mod : { "default": mod };
|
|
28
|
+
};
|
|
29
|
+
Object.defineProperty(exports, "__esModule", { value: true , configurable: true});
|
|
30
|
+
exports.getOwnNodeIdAllowUndefined = exports.getOwnNodeId = exports.createTestBrowserKeyCert = exports.getThreadKeyCert = exports.verifyMachineIdForPublicKey = exports.getOwnMachineId = exports.getIdentityCAPromise = exports.getIdentityCA = exports.loadIdentityCA = exports.setIdentityCARaw = exports.encodeNodeId = exports.decodeNodeIdAssert = exports.decodeNodeId = exports.getMachineId = exports.createCertFromCA = exports.generateTestCA = exports.generateRSAKeyPair = exports.generateKeyPair = exports.validateCertificate = exports.validateCACert = exports.verify = exports.sign = exports.getPublicIdentifier = exports.parseCert = exports.privateKeyToPem = exports.createX509 = exports.getCommonName = exports.identityStorageKey = exports.CA_NOT_FOUND_ERROR = void 0;
|
|
31
|
+
module.allowclient = true;
|
|
32
|
+
// https://www.rfc-editor.org/rfc/rfc5280#page-42
|
|
33
|
+
const compileFlags_1 = require("socket-function/require/compileFlags");
|
|
34
|
+
const forge = __importStar(require("node-forge"));
|
|
35
|
+
const caching_1 = require("socket-function/src/caching");
|
|
36
|
+
const misc_1 = require("socket-function/src/misc");
|
|
37
|
+
const js_sha256_1 = __importDefault(require("js-sha256"));
|
|
38
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
39
|
+
const certStore_1 = require("socket-function/src/certStore");
|
|
40
|
+
const measure_1 = require("socket-function/src/profiling/measure");
|
|
41
|
+
const nodeCache_1 = require("socket-function/src/nodeCache");
|
|
42
|
+
const SocketFunction_1 = require("socket-function/SocketFunction");
|
|
43
|
+
const nodeCache_2 = require("socket-function/src/nodeCache");
|
|
44
|
+
const persistentLocalStorage_1 = require("./persistentLocalStorage");
|
|
45
|
+
(0, compileFlags_1.setFlag)(require, "node-forge", "allowclient", true);
|
|
46
|
+
(0, compileFlags_1.setFlag)(require, "js-sha256", "allowclient", true);
|
|
47
|
+
const timeInDay = 1000 * 60 * 60 * 24;
|
|
48
|
+
exports.CA_NOT_FOUND_ERROR = "18aa7318-f88f-4d2d-b41f-3daf4a433827";
|
|
49
|
+
exports.identityStorageKey = "machineCA_10";
|
|
50
|
+
function getIdentityStore(domain) {
|
|
51
|
+
return (0, persistentLocalStorage_1.getKeyStore)(domain, exports.identityStorageKey);
|
|
52
|
+
}
|
|
53
|
+
function getCommonName(cert) {
|
|
54
|
+
let subject = new crypto_1.default.X509Certificate(cert).subject;
|
|
55
|
+
let subjectKVPs = new Map(subject.split(",").map(x => x.trim().split("=")).map(x => [x[0], x.slice(1).join("=")]));
|
|
56
|
+
let commonName = subjectKVPs.get("CN");
|
|
57
|
+
if (!commonName)
|
|
58
|
+
throw new Error(`No common name in subject: ${subject}`);
|
|
59
|
+
return commonName;
|
|
60
|
+
}
|
|
61
|
+
exports.getCommonName = getCommonName;
|
|
62
|
+
function createX509(config) {
|
|
63
|
+
return (0, measure_1.measureBlock)(function createX509() {
|
|
64
|
+
let { domain, issuer, lifeSpan, keyPair } = config;
|
|
65
|
+
let certObj = forge.pki.createCertificate();
|
|
66
|
+
certObj.publicKey = keyPair.publicKey;
|
|
67
|
+
certObj.serialNumber = "01";
|
|
68
|
+
// Give it 5 minutes before now. If we give it too much time, it can look like the cert is really
|
|
69
|
+
// old, which will trigger various processes to try to get a fresher one (as if it lasts for
|
|
70
|
+
// 1 hour, but we set notBefore to 1 month ago, it looks 1 month old, and so almost expired,
|
|
71
|
+
// when it isn't...)
|
|
72
|
+
certObj.validity.notBefore = new Date(Date.now() - 1000 * 60 * 5);
|
|
73
|
+
certObj.validity.notAfter = new Date(Date.now() + lifeSpan);
|
|
74
|
+
const commonNameAttrs = [{ name: "commonName", value: domain }];
|
|
75
|
+
certObj.setSubject(commonNameAttrs);
|
|
76
|
+
if (issuer === "self") {
|
|
77
|
+
certObj.setIssuer(commonNameAttrs);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
certObj.setIssuer(forge.pki.certificateFromPem(issuer.cert.toString()).subject.attributes);
|
|
81
|
+
}
|
|
82
|
+
let extensions = [];
|
|
83
|
+
const isCA = issuer === "self";
|
|
84
|
+
if (isCA) {
|
|
85
|
+
extensions.push({ name: "basicConstraints", cA: true });
|
|
86
|
+
}
|
|
87
|
+
let localHostDomain = "127-0-0-1." + domain.split(".").slice(-2).join(".");
|
|
88
|
+
extensions.push(...[
|
|
89
|
+
{ name: "keyUsage", keyCertSign: isCA, digitalSignature: true, nonRepudiation: true, keyEncipherment: true, dataEncipherment: true },
|
|
90
|
+
{ name: "subjectKeyIdentifier" },
|
|
91
|
+
{
|
|
92
|
+
name: "subjectAltName",
|
|
93
|
+
altNames: [
|
|
94
|
+
{ type: 2, value: domain },
|
|
95
|
+
{ type: 2, value: "*." + domain },
|
|
96
|
+
{ type: 2, value: localHostDomain },
|
|
97
|
+
// NOTE: No longer allow 127.0.0.1, to make this more secure. We might enable this
|
|
98
|
+
// behavior behind a flag, for development.
|
|
99
|
+
//{ type: 7, ip: "127.0.0.1" }
|
|
100
|
+
]
|
|
101
|
+
},
|
|
102
|
+
// NOTE: nameConstraints are supported with our branch. But... chrome doesn't support them, so there's no point in using them.
|
|
103
|
+
// "node-forge": "https://github.com/sliftist/forge#e618181b469b07bdc70b968b0391beb8ef5fecd6",
|
|
104
|
+
// {
|
|
105
|
+
// name: "nameConstraints",
|
|
106
|
+
// permittedSubtrees: [
|
|
107
|
+
// // Chrome doesn't respect nameConstraints per https://bugs.chromium.org/p/chromium/issues/detail?id=1072083,
|
|
108
|
+
// // as the spec decided that "free to process or ignore such information" (when present in self
|
|
109
|
+
// // signed certificates), and therefore the chrome implementation decided "The first order is to behave predictably",
|
|
110
|
+
// // so... they're not going to support it, because why have a feature in one place, if it isn't
|
|
111
|
+
// // on android as well... ugh...
|
|
112
|
+
// // Works fine on Edge though
|
|
113
|
+
// { type: 2, value: forge.util.encodeUtf8(domain) },
|
|
114
|
+
// { type: 2, value: forge.util.encodeUtf8(localHostDomain) },
|
|
115
|
+
// ]
|
|
116
|
+
// },
|
|
117
|
+
]);
|
|
118
|
+
certObj.setExtensions(extensions);
|
|
119
|
+
(0, measure_1.measureBlock)(function sign() {
|
|
120
|
+
if (issuer === "self") {
|
|
121
|
+
certObj.sign(keyPair.privateKey, forge.md.sha256.create());
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
certObj.sign(privateKeyFromPem(issuer.key.toString()), forge.md.sha256.create());
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
return (0, measure_1.measureBlock)(function toPems() {
|
|
128
|
+
return {
|
|
129
|
+
domain,
|
|
130
|
+
cert: Buffer.from(forge.pki.certificateToPem(certObj)),
|
|
131
|
+
key: Buffer.from(privateKeyToPem(keyPair.privateKey)),
|
|
132
|
+
};
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
exports.createX509 = createX509;
|
|
137
|
+
function privateKeyToPem(buffer) {
|
|
138
|
+
if ("privateKeyBytes" in buffer) {
|
|
139
|
+
return forge.ed25519.privateKeyToPem(buffer);
|
|
140
|
+
}
|
|
141
|
+
return forge.pki.privateKeyToPem(buffer);
|
|
142
|
+
}
|
|
143
|
+
exports.privateKeyToPem = privateKeyToPem;
|
|
144
|
+
function privateKeyFromPem(pem) {
|
|
145
|
+
// We want to guess the type correctly, as caught exceptions make debugging annoying
|
|
146
|
+
if (pem.length < 200) {
|
|
147
|
+
try {
|
|
148
|
+
return forge.ed25519.privateKeyFromPem(pem);
|
|
149
|
+
}
|
|
150
|
+
catch (_a) { }
|
|
151
|
+
}
|
|
152
|
+
try {
|
|
153
|
+
return forge.pki.privateKeyFromPem(pem);
|
|
154
|
+
}
|
|
155
|
+
catch (_b) {
|
|
156
|
+
return forge.ed25519.privateKeyFromPem(pem);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
function publicKeyFromCert(cert) {
|
|
160
|
+
return parseCert(cert).publicKey;
|
|
161
|
+
}
|
|
162
|
+
function parseCert(PEMorDER) {
|
|
163
|
+
return forge.pki.certificateFromPem(normalizeCertToPEM(PEMorDER));
|
|
164
|
+
}
|
|
165
|
+
exports.parseCert = parseCert;
|
|
166
|
+
// Gets a unique value to represent the public key
|
|
167
|
+
function getPublicIdentifier(PEMorDER) {
|
|
168
|
+
let obj = parseCert(PEMorDER);
|
|
169
|
+
let publicKey = obj.publicKey;
|
|
170
|
+
if ("publicKeyBytes" in publicKey) {
|
|
171
|
+
return Buffer.from(publicKey.publicKeyBytes);
|
|
172
|
+
}
|
|
173
|
+
return Buffer.from(new Uint32Array(publicKey.n.data).buffer);
|
|
174
|
+
}
|
|
175
|
+
exports.getPublicIdentifier = getPublicIdentifier;
|
|
176
|
+
function isED25519(key) {
|
|
177
|
+
return key.length < 256;
|
|
178
|
+
}
|
|
179
|
+
// EQUIVALENT TO: `crypto.createSign("SHA256").update(JSON.stringify(payload)).sign(keyCert.key, "binary")`
|
|
180
|
+
exports.sign = (0, measure_1.measureWrap)(function sign(keyPair, data) {
|
|
181
|
+
let dataStr = JSON.stringify(data);
|
|
182
|
+
if (isED25519(keyPair.key)) {
|
|
183
|
+
let privateKey = forge.pki.ed25519.privateKeyFromPem(keyPair.key.toString());
|
|
184
|
+
return privateKey.sign(dataStr);
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
let privateKey = forge.pki.privateKeyFromPem(keyPair.key.toString());
|
|
188
|
+
const md = forge.md.sha256.create();
|
|
189
|
+
md.update(dataStr);
|
|
190
|
+
return privateKey.sign(md);
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
function verify(cert, signature, data) {
|
|
194
|
+
let certObj = parseCert(cert);
|
|
195
|
+
return certObj.publicKey.verify(JSON.stringify(data), signature);
|
|
196
|
+
}
|
|
197
|
+
exports.verify = verify;
|
|
198
|
+
function normalizeCertToPEM(PEMorDER) {
|
|
199
|
+
if (PEMorDER.toString().startsWith("-----BEGIN CERTIFICATE-----")) {
|
|
200
|
+
return PEMorDER.toString();
|
|
201
|
+
}
|
|
202
|
+
PEMorDER = PEMorDER.toString("base64");
|
|
203
|
+
return "-----BEGIN CERTIFICATE-----\n" + PEMorDER + "\n-----END CERTIFICATE-----";
|
|
204
|
+
}
|
|
205
|
+
function getDomainPartFromPublicKey(publicKey) {
|
|
206
|
+
let bytes;
|
|
207
|
+
if ("publicKeyBytes" in publicKey) {
|
|
208
|
+
bytes = publicKey.publicKeyBytes;
|
|
209
|
+
}
|
|
210
|
+
else if (publicKey instanceof Buffer) {
|
|
211
|
+
bytes = publicKey;
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
bytes = Buffer.from(new Uint32Array(publicKey.n.data).buffer);
|
|
215
|
+
}
|
|
216
|
+
return "b" + js_sha256_1.default.sha256(Buffer.from(bytes)).slice(0, 16).replaceAll("+", "-").replaceAll("/", "_");
|
|
217
|
+
}
|
|
218
|
+
function validateCACert(domain, cert) {
|
|
219
|
+
let certParsed = parseCert(cert);
|
|
220
|
+
let subject = certParsed.subject.getField("CN").value;
|
|
221
|
+
let localhostDomain = "127-0-0-1." + subject.split(".").slice(-2).join(".");
|
|
222
|
+
let domainParts = subject.split(".").reverse();
|
|
223
|
+
let rootDomainParsed = [domainParts.shift(), domainParts.shift()].reverse().join(".");
|
|
224
|
+
if (rootDomainParsed !== domain) {
|
|
225
|
+
// This is important, as our trust store contains more then just OUR certificates,
|
|
226
|
+
// so if we allow any domains then real domains can impersonate anyone! It has to
|
|
227
|
+
// be one OUR domain to be trusted!
|
|
228
|
+
throw new Error(`Certificate root domain should be ${domain}, but is ${rootDomainParsed}`);
|
|
229
|
+
}
|
|
230
|
+
// TODO: Maybe just skip if it isn't a hash string?
|
|
231
|
+
if (domainParts[0] === "noproxy") {
|
|
232
|
+
domainParts.shift();
|
|
233
|
+
}
|
|
234
|
+
let certExpectedPublicKeyPart = (domainParts.shift() || "").split("-").slice(-1)[0];
|
|
235
|
+
let certActualPublicKeyPart = getDomainPartFromPublicKey(certParsed.publicKey);
|
|
236
|
+
if (certExpectedPublicKeyPart !== certActualPublicKeyPart) {
|
|
237
|
+
throw new Error(`Certificate public key in the url is ${certExpectedPublicKeyPart}, but in the cert is ${certActualPublicKeyPart}`);
|
|
238
|
+
}
|
|
239
|
+
// ALSO, require name constraints to be present, and to restrict to the "CN"
|
|
240
|
+
let nameConstraints = certParsed.getExtension("nameConstraints");
|
|
241
|
+
if (!nameConstraints) {
|
|
242
|
+
throw new Error(`Certificate must have nameConstraints`);
|
|
243
|
+
}
|
|
244
|
+
let subtrees = nameConstraints.permittedSubtrees;
|
|
245
|
+
if (!subtrees) {
|
|
246
|
+
throw new Error(`Certificate must have nameConstraints.permittedSubtrees`);
|
|
247
|
+
}
|
|
248
|
+
let subtreeValues = subtrees.map((x) => x.value);
|
|
249
|
+
// Ignore localhostDomain, as it can always safely be allowed (the same machine
|
|
250
|
+
// is always allowed).
|
|
251
|
+
subtreeValues = subtreeValues.filter((x) => x !== localhostDomain);
|
|
252
|
+
if (subtreeValues.length !== 1 || subtreeValues[0] !== subject) {
|
|
253
|
+
throw new Error(`Certificate must have a single constrained domain (had ${JSON.stringify(subtreeValues)})`);
|
|
254
|
+
}
|
|
255
|
+
validateAltNames(certParsed, subject);
|
|
256
|
+
}
|
|
257
|
+
exports.validateCACert = validateCACert;
|
|
258
|
+
function validateCertificate(domain, cert, issuerCert) {
|
|
259
|
+
validateCACert(domain, issuerCert);
|
|
260
|
+
let certParsed = parseCert(cert);
|
|
261
|
+
let subject = certParsed.subject.getField("CN").value;
|
|
262
|
+
let localhostDomain = "127-0-0-1." + subject.split(".").slice(-2).join(".");
|
|
263
|
+
let domainParts = subject.split(".").reverse();
|
|
264
|
+
let rootDomainParsed = [domainParts.shift(), domainParts.shift()].reverse().join(".");
|
|
265
|
+
if (rootDomainParsed !== domain) {
|
|
266
|
+
throw new Error(`Certificate root domain should be ${domain}, but is ${rootDomainParsed}`);
|
|
267
|
+
}
|
|
268
|
+
// TODO: Maybe just skip if it isn't a hash string?
|
|
269
|
+
if (domainParts[0] === "noproxy") {
|
|
270
|
+
domainParts.shift();
|
|
271
|
+
}
|
|
272
|
+
let issuerCertParsed = parseCert(issuerCert);
|
|
273
|
+
let issuerExpectedPublicKeyPart = domainParts.shift() || "";
|
|
274
|
+
let issuerActualPublicKeyPart = getDomainPartFromPublicKey(issuerCertParsed.publicKey);
|
|
275
|
+
if (issuerExpectedPublicKeyPart !== issuerActualPublicKeyPart) {
|
|
276
|
+
throw new Error(`Issuer public key in the url is ${issuerExpectedPublicKeyPart}, but in the cert is ${issuerActualPublicKeyPart}`);
|
|
277
|
+
}
|
|
278
|
+
// Take the last part
|
|
279
|
+
let certExpectedPublicKeyPart = domainParts.shift() || "";
|
|
280
|
+
let certActualPublicKeyPart = getDomainPartFromPublicKey(certParsed.publicKey);
|
|
281
|
+
if (certExpectedPublicKeyPart !== certActualPublicKeyPart) {
|
|
282
|
+
throw new Error(`Certificate public key in the url is ${certExpectedPublicKeyPart}, but in the cert is ${certActualPublicKeyPart}`);
|
|
283
|
+
}
|
|
284
|
+
let nameConstraints = issuerCertParsed.getExtension("nameConstraints");
|
|
285
|
+
if (!nameConstraints) {
|
|
286
|
+
throw new Error(`CA must have nameConstraints`);
|
|
287
|
+
}
|
|
288
|
+
let subtrees = nameConstraints.permittedSubtrees;
|
|
289
|
+
if (!subtrees) {
|
|
290
|
+
throw new Error(`CA must have nameConstraints.permittedSubtrees`);
|
|
291
|
+
}
|
|
292
|
+
let subtreeValues = subtrees.map((x) => x.value);
|
|
293
|
+
// Ignore localhostDomain, as it can always safely be allowed (the same machine
|
|
294
|
+
// is always allowed).
|
|
295
|
+
subtreeValues = subtreeValues.filter((x) => x !== localhostDomain);
|
|
296
|
+
if (subtreeValues.length !== 1) {
|
|
297
|
+
throw new Error(`CA must have a single constrained domain (had ${JSON.stringify(subtreeValues)})`);
|
|
298
|
+
}
|
|
299
|
+
let subtree = subtreeValues[0];
|
|
300
|
+
if (subtree !== subject && !subject.endsWith("." + subtree)) {
|
|
301
|
+
throw new Error(`Certificate must be a subtree of the CA (CA: ${subtree}, cert: ${subject})`);
|
|
302
|
+
}
|
|
303
|
+
validateAltNames(certParsed, subject);
|
|
304
|
+
// Verify issuer ACTUALLY signed certParsed
|
|
305
|
+
if (!issuerCertParsed.verify(certParsed)) {
|
|
306
|
+
throw new Error(`Issuer did not sign certificate`);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
exports.validateCertificate = validateCertificate;
|
|
310
|
+
// Require alt names to be either equal to "CN", or a subtree of "CN"
|
|
311
|
+
function validateAltNames(certParsed, subject) {
|
|
312
|
+
let localhostDomain = "127-0-0-1." + subject.split(".").slice(-2).join(".");
|
|
313
|
+
let altNamesObj = certParsed.getExtension("subjectAltName");
|
|
314
|
+
let altNames = altNamesObj === null || altNamesObj === void 0 ? void 0 : altNamesObj.altNames.map((x) => x.value);
|
|
315
|
+
// Allow localhostDomain, as it can always safely be allowed
|
|
316
|
+
altNames = altNames.filter((x) => x !== localhostDomain);
|
|
317
|
+
if (altNames.some((x) => !(x === subject
|
|
318
|
+
|| x.endsWith("." + subject)
|
|
319
|
+
// Commented out, because... it is so easy to publish a 127.0.0.1 A record,
|
|
320
|
+
// and even to generate a real cert, so, we should jsut do that, and keep it secure.
|
|
321
|
+
// If we need this for development we can put it behind a flag, so non-development
|
|
322
|
+
// instances are still secure.
|
|
323
|
+
// // Also allow 127.0.0.1, for local testing, for now?
|
|
324
|
+
// // - This might be insecure if these are trusted by the browser,
|
|
325
|
+
// // as then anyone that stores any cookies in this ip can have
|
|
326
|
+
// // the cookies stolen. But... if this is just cross-server...
|
|
327
|
+
// // I don't see how this could cause a security vulnerability.
|
|
328
|
+
// || x === Buffer.from([127, 0, 0, 1]).toString()
|
|
329
|
+
))) {
|
|
330
|
+
throw new Error(`Invalid alt names. Must be subtrees of the subject (CN) ${JSON.stringify(subject)}, was ${JSON.stringify(altNames)}`);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
function generateKeyPair() {
|
|
334
|
+
return (0, measure_1.measureBlock)(function generateKeyPair() {
|
|
335
|
+
// NOTE: We use ED25519 because it can generated keys about 10X faster, WHICH, is still slow
|
|
336
|
+
// (~6ms on my machine). So we DEFINITELY don't want it to be 10X slower!
|
|
337
|
+
// NOTE: ED25519 doens't have great support in browsers, but we shouldn't need self signed certificates
|
|
338
|
+
// in the browser anyway.
|
|
339
|
+
// - https://security.stackexchange.com/a/236943/282367
|
|
340
|
+
//let keyPair = forge.ed25519.generateKeyPair();
|
|
341
|
+
let keyPair = forge.pki.rsa.generateKeyPair();
|
|
342
|
+
return keyPair;
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
exports.generateKeyPair = generateKeyPair;
|
|
346
|
+
function generateRSAKeyPair() {
|
|
347
|
+
return (0, measure_1.measureBlock)(function generateKeyPair() {
|
|
348
|
+
return forge.pki.rsa.generateKeyPair();
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
exports.generateRSAKeyPair = generateRSAKeyPair;
|
|
352
|
+
function generateTestCA(domain) {
|
|
353
|
+
const keyPair = generateKeyPair();
|
|
354
|
+
let caPublicKeyPart = getDomainPartFromPublicKey(keyPair.publicKey);
|
|
355
|
+
let fullDomain = `${caPublicKeyPart}.${domain}`;
|
|
356
|
+
if (!(0, misc_1.isNode)()) {
|
|
357
|
+
fullDomain = `${caPublicKeyPart}.${domain}`;
|
|
358
|
+
}
|
|
359
|
+
return createX509({ domain: fullDomain, issuer: "self", keyPair, lifeSpan: timeInDay * 365 * 20 });
|
|
360
|
+
}
|
|
361
|
+
exports.generateTestCA = generateTestCA;
|
|
362
|
+
let identityCA = (0, caching_1.cache)((domain) => {
|
|
363
|
+
let identityCA = (0, caching_1.lazy)((async () => {
|
|
364
|
+
let identityCACached = getIdentityStore(domain);
|
|
365
|
+
let caCached = await identityCACached.get();
|
|
366
|
+
if (!caCached) {
|
|
367
|
+
console.log(`Generating new identity CA`);
|
|
368
|
+
const keyPair = generateKeyPair();
|
|
369
|
+
let caPublicKeyPart = getDomainPartFromPublicKey(keyPair.publicKey);
|
|
370
|
+
let fullDomain = `${caPublicKeyPart}.${domain}`;
|
|
371
|
+
if (!(0, misc_1.isNode)()) {
|
|
372
|
+
fullDomain = `${caPublicKeyPart}.${domain}`;
|
|
373
|
+
}
|
|
374
|
+
let value = createX509({ domain: fullDomain, issuer: "self", keyPair, lifeSpan: timeInDay * 365 * 20 });
|
|
375
|
+
caCached = {
|
|
376
|
+
domain: value.domain,
|
|
377
|
+
certB64: value.cert.toString("base64"),
|
|
378
|
+
keyB64: value.key.toString("base64"),
|
|
379
|
+
};
|
|
380
|
+
await identityCACached.set(caCached);
|
|
381
|
+
}
|
|
382
|
+
let result = {
|
|
383
|
+
domain: caCached.domain,
|
|
384
|
+
cert: Buffer.from(caCached.certB64, "base64"),
|
|
385
|
+
key: Buffer.from(caCached.keyB64, "base64"),
|
|
386
|
+
};
|
|
387
|
+
(0, certStore_1.trustCertificate)(result.cert.toString());
|
|
388
|
+
identityCA.set(result);
|
|
389
|
+
return result;
|
|
390
|
+
}));
|
|
391
|
+
return identityCA;
|
|
392
|
+
});
|
|
393
|
+
// IMPORTANT! We do not embed any debug info in this domain. If we did, it would be useful,
|
|
394
|
+
// but... potentally a security vulnerability, as if the debug info (such as a prefix)
|
|
395
|
+
// is used to identify what a certificate is for, it would be easy for an attack to
|
|
396
|
+
// forge this (as the debug info won't be secured). So it is much better to keep
|
|
397
|
+
// the certificate opaque, and then require any metadata to be actually vetted
|
|
398
|
+
// (and hopefully stored in a UI, showing IP, time, etc).
|
|
399
|
+
function createCertFromCA(config) {
|
|
400
|
+
return (0, measure_1.measureBlock)(function createCertFromCA() {
|
|
401
|
+
let { CAKeyPair } = config;
|
|
402
|
+
const keyPair = generateKeyPair();
|
|
403
|
+
let domainKeyPart = getDomainPartFromPublicKey(keyPair.publicKey);
|
|
404
|
+
let fullDomain = `${domainKeyPart}.${config.CAKeyPair.domain}`;
|
|
405
|
+
return createX509({
|
|
406
|
+
domain: fullDomain,
|
|
407
|
+
issuer: CAKeyPair,
|
|
408
|
+
keyPair,
|
|
409
|
+
lifeSpan: timeInDay * 365 * 10,
|
|
410
|
+
});
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
exports.createCertFromCA = createCertFromCA;
|
|
414
|
+
function getMachineId(domainName) {
|
|
415
|
+
return domainName.split(".").slice(-3).join(".");
|
|
416
|
+
}
|
|
417
|
+
exports.getMachineId = getMachineId;
|
|
418
|
+
function decodeNodeId(nodeId) {
|
|
419
|
+
let locationObj = (0, nodeCache_1.getNodeIdLocation)(nodeId);
|
|
420
|
+
if (!locationObj) {
|
|
421
|
+
return undefined;
|
|
422
|
+
}
|
|
423
|
+
let parts = locationObj.address.split(".");
|
|
424
|
+
if (nodeId.startsWith("127-0-0-1.") && parts.length === 3) {
|
|
425
|
+
return {
|
|
426
|
+
threadId: "",
|
|
427
|
+
machineId: parts.at(-3) || "",
|
|
428
|
+
domain: parts.slice(-2).join("."),
|
|
429
|
+
port: locationObj.port,
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
if (parts.length < 4) {
|
|
433
|
+
return undefined;
|
|
434
|
+
}
|
|
435
|
+
return {
|
|
436
|
+
threadId: parts.at(-4) || "",
|
|
437
|
+
machineId: parts.at(-3) || "",
|
|
438
|
+
domain: parts.slice(-2).join(".") || "",
|
|
439
|
+
port: locationObj.port,
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
exports.decodeNodeId = decodeNodeId;
|
|
443
|
+
function decodeNodeIdAssert(nodeId) {
|
|
444
|
+
let result = decodeNodeId(nodeId);
|
|
445
|
+
if (!result) {
|
|
446
|
+
throw new Error(`Invalid nodeId: ${nodeId}`);
|
|
447
|
+
}
|
|
448
|
+
return result;
|
|
449
|
+
}
|
|
450
|
+
exports.decodeNodeIdAssert = decodeNodeIdAssert;
|
|
451
|
+
function encodeNodeId(parts) {
|
|
452
|
+
return `${parts.threadId}.${parts.machineId}.${parts.domain}:${parts.port}`;
|
|
453
|
+
}
|
|
454
|
+
exports.encodeNodeId = encodeNodeId;
|
|
455
|
+
async function setIdentityCARaw(domain, json) {
|
|
456
|
+
let identityCACached = getIdentityStore(domain);
|
|
457
|
+
let obj = JSON.parse(json);
|
|
458
|
+
let ca = {
|
|
459
|
+
domain: obj.domain,
|
|
460
|
+
cert: Buffer.from(obj.certB64, "base64"),
|
|
461
|
+
key: Buffer.from(obj.keyB64, "base64"),
|
|
462
|
+
};
|
|
463
|
+
(0, certStore_1.trustCertificate)(ca.cert.toString());
|
|
464
|
+
identityCA(domain).set(ca);
|
|
465
|
+
getThreadKeyCertBase(domain).reset();
|
|
466
|
+
await identityCACached.set(obj);
|
|
467
|
+
(0, nodeCache_2.resetAllNodeCallFactories)();
|
|
468
|
+
}
|
|
469
|
+
exports.setIdentityCARaw = setIdentityCARaw;
|
|
470
|
+
async function loadIdentityCA(domain) {
|
|
471
|
+
await identityCA(domain)();
|
|
472
|
+
}
|
|
473
|
+
exports.loadIdentityCA = loadIdentityCA;
|
|
474
|
+
function getIdentityCA(domain) {
|
|
475
|
+
let value = identityCA(domain)();
|
|
476
|
+
if (value instanceof Promise) {
|
|
477
|
+
throw new Error("Identity CA is not yet loaded. Call and wait for loadIdentityCA() in your startup before accessing the identity (or call getIdentityCAPromise())");
|
|
478
|
+
}
|
|
479
|
+
return value;
|
|
480
|
+
}
|
|
481
|
+
exports.getIdentityCA = getIdentityCA;
|
|
482
|
+
// TODO: Replace this with a database, so it is easy for us to trust CAs
|
|
483
|
+
// cross machine, and even have multiple users, etc, etc.
|
|
484
|
+
function getIdentityCAPromise(domain) {
|
|
485
|
+
return identityCA(domain)();
|
|
486
|
+
}
|
|
487
|
+
exports.getIdentityCAPromise = getIdentityCAPromise;
|
|
488
|
+
function getOwnMachineId(domain) {
|
|
489
|
+
return getMachineId(getIdentityCA(domain).domain);
|
|
490
|
+
}
|
|
491
|
+
exports.getOwnMachineId = getOwnMachineId;
|
|
492
|
+
/** Part of the machineId comes from the publicKey, so we can use it to verify */
|
|
493
|
+
function verifyMachineIdForPublicKey(config) {
|
|
494
|
+
let { machineId, publicKey } = config;
|
|
495
|
+
let domainPart = getDomainPartFromPublicKey(publicKey);
|
|
496
|
+
return machineId.split(".").at(-3) === domainPart;
|
|
497
|
+
}
|
|
498
|
+
exports.verifyMachineIdForPublicKey = verifyMachineIdForPublicKey;
|
|
499
|
+
// NOTE: We don't have a cache per CA, as... the CA should be set first
|
|
500
|
+
// TODO: Maybe throw if they try to change the CA after they generate any certificates?
|
|
501
|
+
// TODO: Regenerate certificates after enough time (as thread certs should be relatively short lived,
|
|
502
|
+
// so it is plausible for them to expire)
|
|
503
|
+
// - We will also need to provide a callback so that users of the cert can update the cert they
|
|
504
|
+
// are using as well.
|
|
505
|
+
function getThreadKeyCert(domain) {
|
|
506
|
+
return getThreadKeyCertBase(domain)();
|
|
507
|
+
}
|
|
508
|
+
exports.getThreadKeyCert = getThreadKeyCert;
|
|
509
|
+
const getThreadKeyCertBase = (0, caching_1.cache)((domain) => (0, caching_1.lazy)(() => {
|
|
510
|
+
let ca = getIdentityCA(domain);
|
|
511
|
+
return createCertFromCA({ CAKeyPair: ca });
|
|
512
|
+
}));
|
|
513
|
+
exports.createTestBrowserKeyCert = (0, caching_1.lazy)(async () => {
|
|
514
|
+
let keyPair = generateRSAKeyPair();
|
|
515
|
+
return await createX509({ domain: "test", issuer: "self", keyPair, lifeSpan: timeInDay * 365 * 20 });
|
|
516
|
+
});
|
|
517
|
+
function getOwnNodeId() {
|
|
518
|
+
let nodeId = SocketFunction_1.SocketFunction.mountedNodeId;
|
|
519
|
+
if (!nodeId) {
|
|
520
|
+
throw new Error(`Node must be mounted before nodeId is accessed`);
|
|
521
|
+
}
|
|
522
|
+
return nodeId;
|
|
523
|
+
}
|
|
524
|
+
exports.getOwnNodeId = getOwnNodeId;
|
|
525
|
+
function getOwnNodeIdAllowUndefined() {
|
|
526
|
+
return SocketFunction_1.SocketFunction.mountedNodeId;
|
|
527
|
+
}
|
|
528
|
+
exports.getOwnNodeIdAllowUndefined = getOwnNodeIdAllowUndefined;
|
|
529
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2VydHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJjZXJ0cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUEsa0RBQWtEOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUVsRCxNQUFNLENBQUMsV0FBVyxHQUFHLElBQUksQ0FBQztBQUUxQixpREFBaUQ7QUFFakQsdUVBQStEO0FBQy9ELGtEQUFvQztBQUdwQyx5REFBMEQ7QUFDMUQsbURBQWtEO0FBQ2xELDBEQUErQjtBQUMvQixvREFBNEI7QUFDNUIsNkRBQWlFO0FBQ2pFLG1FQUE4RjtBQUM5Riw2REFBa0g7QUFFbEgsbUVBQWdFO0FBQ2hFLDZEQUEwRTtBQUMxRSxxRUFBdUQ7QUFFdkQsSUFBQSxzQkFBTyxFQUFDLE9BQU8sRUFBRSxZQUFZLEVBQUUsYUFBYSxFQUFFLElBQUksQ0FBQyxDQUFDO0FBQ3BELElBQUEsc0JBQU8sRUFBQyxPQUFPLEVBQUUsV0FBVyxFQUFFLGFBQWEsRUFBRSxJQUFJLENBQUMsQ0FBQztBQUVuRCxNQUFNLFNBQVMsR0FBRyxJQUFJLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLENBQUM7QUFFekIsUUFBQSxrQkFBa0IsR0FBRyxzQ0FBc0MsQ0FBQztBQUU1RCxRQUFBLGtCQUFrQixHQUFHLGNBQWMsQ0FBQztBQUdqRCxTQUFTLGdCQUFnQixDQUFDLE1BQWM7SUFDcEMsT0FBTyxJQUFBLG9DQUFXLEVBQXNCLE1BQU0sRUFBRSwwQkFBa0IsQ0FBQyxDQUFDO0FBQ3hFLENBQUM7QUFJRCxTQUFnQixhQUFhLENBQUMsSUFBcUI7SUFDL0MsSUFBSSxPQUFPLEdBQUcsSUFBSSxnQkFBTSxDQUFDLGVBQWUsQ0FBQyxJQUFJLENBQUMsQ0FBQyxPQUFPLENBQUM7SUFDdkQsSUFBSSxXQUFXLEdBQUcsSUFBSSxHQUFHLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFD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LDRDQUVDO0FBQ0QsTUFBTSxvQkFBb0IsR0FBRyxJQUFBLGVBQUssRUFBQyxDQUFDLE1BQWMsRUFBRSxFQUFFLENBQUMsSUFBQSxjQUFJLEVBQUMsR0FBRyxFQUFFO0lBQzdELElBQUksRUFBRSxHQUFHLGFBQWEsQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUMvQixPQUFPLGdCQUFnQixDQUFDLEVBQUUsU0FBUyxFQUFFLEVBQUUsRUFBRSxDQUFDLENBQUM7QUFDL0MsQ0FBQyxDQUFDLENBQUMsQ0FBQztBQUVTLFFBQUEsd0JBQXdCLEdBQUcsSUFBQSxjQUFJLEVBQUMsS0FBSyxJQUFJLEVBQUU7SUFDcEQsSUFBSSxPQUFPLEdBQUcsa0JBQWtCLEVBQUUsQ0FBQztJQUNuQyxPQUFPLE1BQU0sVUFBVSxDQUFDLEVBQUUsTUFBTSxFQUFFLE1BQU0sRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLE9BQU8sRUFBRSxRQUFRLEVBQUUsU0FBUyxHQUFHLEdBQUcsR0FBRyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0FBQ3pHLENBQUMsQ0FBQyxDQUFDO0FBRUgsU0FBZ0IsWUFBWTtJQUN4QixJQUFJLE1BQU0sR0FBRywrQkFBYyxDQUFDLGFBQWEsQ0FBQztJQUMxQyxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUM7UUFDVixNQUFNLElBQUksS0FBSyxDQUFDLGdEQUFnRCxDQUFDLENBQUM7SUFDdEUsQ0FBQztJQUNELE9BQU8sTUFBTSxDQUFDO0FBQ2xCLENBQUM7QUFORCxvQ0FNQztBQUVELFNBQWdCLDBCQUEwQjtJQUN0QyxPQUFPLCtCQUFjLENBQUMsYUFBYSxDQUFDO0FBQ3hDLENBQUM7QUFGRCxnRUFFQyIsInNvdXJjZXNDb250ZW50IjpbIi8vLyA8cmVmZXJlbmNlIHBhdGg9XCIuL25vZGUtZm9yZ2UtZWQyNTUxOS5kLnRzXCIgLz5cblxubW9kdWxlLmFsbG93Y2xpZW50ID0gdHJ1ZTtcblxuLy8gaHR0cHM6Ly93d3cucmZjLWVkaXRvci5vcmcvcmZjL3JmYzUyODAjcGFnZS00MlxuXG5pbXBvcnQgeyBzZXRGbGFnIH0gZnJvbSBcInNvY2tldC1mdW5jdGlvbi9yZXF1aXJlL2NvbXBpbGVGbGFnc1wiO1xuaW1wb3J0ICogYXMgZm9yZ2UgZnJvbSBcIm5vZGUtZm9yZ2VcIjtcbmltcG9ydCBvcyBmcm9tIFwib3NcIjtcbmltcG9ydCBmc1N5bmMgZnJvbSBcImZzXCI7XG5pbXBvcnQgeyBjYWNoZSwgbGF6eSB9IGZyb20gXCJzb2NrZXQtZnVuY3Rpb24vc3JjL2NhY2hpbmdcIjtcbmltcG9ydCB7IGlzTm9kZSB9IGZyb20gXCJzb2NrZXQtZnVuY3Rpb24vc3JjL21pc2NcIjtcbmltcG9ydCBzaGEyNjUgZnJvbSBcImpzLXNoYTI1NlwiO1xuaW1wb3J0IGNyeXB0byBmcm9tIFwiY3J5cHRvXCI7XG5pbXBvcnQgeyB0cnVzdENlcnRpZmljYXRlIH0gZnJvbSBcInNvY2tldC1mdW5jdGlvbi9zcmMvY2VydFN0b3JlXCI7XG5pbXBvcnQgeyBtZWFzdXJlQmxvY2ssIG1lYXN1cmVGbmMsIG1lYXN1cmVXcmFwIH0gZnJvbSBcInNvY2tldC1mdW5jdGlvbi9zcmMvcHJvZmlsaW5nL21lYXN1cmVcIjtcbmltcG9ydCB7IGdldE5vZGVJZERvbWFpbiwgZ2V0Tm9kZUlkRG9tYWluTWF5YmVVbmRlZmluZWQsIGdldE5vZGVJZExvY2F0aW9uIH0gZnJvbSBcInNvY2tldC1mdW5jdGlvbi9zcmMvbm9kZUNhY2hlXCI7XG5pbXBvcnQgeyBNYXliZVByb21pc2UgfSBmcm9tIFwic29ja2V0LWZ1bmN0aW9uL3NyYy90eXBlc1wiO1xuaW1wb3J0IHsgU29ja2V0RnVuY3Rpb24gfSBmcm9tIFwic29ja2V0LWZ1bmN0aW9uL1NvY2tldEZ1bmN0aW9uXCI7XG5pbXBvcnQgeyByZXNldEFsbE5vZGVDYWxsRmFjdG9yaWVzIH0gZnJvbSBcInNvY2tldC1mdW5jdGlvbi9zcmMvbm9kZUNhY2hlXCI7XG5pbXBvcnQgeyBnZXRLZXlTdG9yZSB9IGZyb20gXCIuL3BlcnNpc3RlbnRMb2NhbFN0b3JhZ2VcIjtcblxuc2V0RmxhZyhyZXF1aXJlLCBcIm5vZGUtZm9yZ2VcIiwgXCJhbGxvd2NsaWVudFwiLCB0cnVlKTtcbnNldEZsYWcocmVxdWlyZSwgXCJqcy1zaGEyNTZcIiwgXCJhbGxvd2NsaWVudFwiLCB0cnVlKTtcblxuY29uc3QgdGltZUluRGF5ID0gMTAwMCAqIDYwICogNjAgKiAyNDtcblxuZXhwb3J0IGNvbnN0IENBX05PVF9GT1VORF9FUlJPUiA9IFwiMThhYTczMTgtZjg4Zi00ZDJkLWI0MWYtM2RhZjRhNDMzODI3XCI7XG5cbmV4cG9ydCBjb25zdCBpZGVudGl0eVN0b3JhZ2VLZXkgPSBcIm1hY2hpbmVDQV8xMFwiO1xuZXhwb3J0IHR5cGUgSWRlbnRpdHlTdG9yYWdlVHlwZSA9IHsgZG9tYWluOiBzdHJpbmc7IGNlcnRCNjQ6IHN0cmluZzsga2V5QjY0OiBzdHJpbmcgfTtcblxuZnVuY3Rpb24gZ2V0SWRlbnRpdHlTdG9yZShkb21haW46IHN0cmluZykge1xuICAgIHJldHVybiBnZXRLZXlTdG9yZTxJZGVudGl0eVN0b3JhZ2VUeXBlPihkb21haW4sIGlkZW50aXR5U3RvcmFnZUtleSk7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgWDUwOUtleVBhaXIgeyBkb21haW46IHN0cmluZzsgY2VydDogQnVmZmVyOyBrZXk6IEJ1ZmZlcjsgfVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0Q29tbW9uTmFtZShjZXJ0OiBCdWZmZXIgfCBzdHJpbmcpIHtcbiAgICBsZXQgc3ViamVjdCA9IG5ldyBjcnlwdG8uWDUwOUNlcnRpZmljYXRlKGNlcnQpLnN1YmplY3Q7XG4gICAgbGV0IHN1YmplY3RLVlBzID0gbmV3IE1hcChzdWJqZWN0LnNwbGl0KFwiLFwiKS5tYXAoeCA9PiB4LnRyaW0oKS5zcGxpdChcIj1cIikpLm1hcCh4ID0+IFt4WzBdLCB4LnNsaWNlKDEpLmpvaW4oXCI9XCIpXSkpO1xuICAgIGxldCBjb21tb25OYW1lID0gc3ViamVjdEtWUHMuZ2V0KFwiQ05cIik7XG4gICAgaWYgKCFjb21tb25OYW1lKSB0aHJvdyBuZXcgRXJyb3IoYE5vIGNvbW1vbiBuYW1lIGluIHN1YmplY3Q6ICR7c3ViamVjdH1gKTtcbiAgICByZXR1cm4gY29tbW9uTmFtZTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZVg1MDkoXG4gICAgY29uZmlnOiB7XG4gICAgICAgIGRvbWFpbjogc3RyaW5nO1xuICAgICAgICBpc3N1ZXI6IFg1MDlLZXlQYWlyIHwgXCJzZWxmXCI7XG4gICAgICAgIGxpZmVTcGFuOiBudW1iZXI7XG4gICAgICAgIGtleVBhaXI6IHtcbiAgICAgICAgICAgIHB1YmxpY0tleTogZm9yZ2UuRWQyNTUxOVB1YmxpY0tleTtcbiAgICAgICAgICAgIHByaXZhdGVLZXk6IGZvcmdlLkVkMjU1MTlQcml2YXRlS2V5O1xuICAgICAgICB9IHwgZm9yZ2UucGtpLktleVBhaXI7XG4gICAgfVxuKTogWDUwOUtleVBhaXIge1xuICAgIHJldHVybiBtZWFzdXJlQmxvY2soZnVuY3Rpb24gY3JlYXRlWDUwOSgpIHtcbiAgICAgICAgbGV0IHsgZG9tYWluLCBpc3N1ZXIsIGxpZmVTcGFuLCBrZXlQYWlyIH0gPSBjb25maWc7XG5cbiAgICAgICAgbGV0IGNlcnRPYmogPSBmb3JnZS5wa2kuY3JlYXRlQ2VydGlmaWNhdGUoKTtcbiAgICAgICAgY2VydE9iai5wdWJsaWNLZXkgPSBrZXlQYWlyLnB1YmxpY0tleTtcbiAgICAgICAgY2VydE9iai5zZXJpYWxOdW1iZXIgPSBcIjAxXCI7XG4gICAgICAgIC8vIEdpdmUgaXQgNSBtaW51dGVzIGJlZm9yZSBub3cuIElmIHdlIGdpdmUgaXQgdG9vIG11Y2ggdGltZSwgaXQgY2FuIGxvb2sgbGlrZSB0aGUgY2VydCBpcyByZWFsbHlcbiAgICAgICAgLy8gIG9sZCwgd2hpY2ggd2lsbCB0cmlnZ2VyIHZhcmlvdXMgcHJvY2Vzc2VzIHRvIHRyeSB0byBnZXQgYSBmcmVzaGVyIG9uZSAoYXMgaWYgaXQgbGFzdHMgZm9yXG4gICAgICAgIC8vICAxIGhvdXIsIGJ1dCB3ZSBzZXQgbm90QmVmb3JlIHRvIDEgbW9udGggYWdvLCBpdCBsb29rcyAxIG1vbnRoIG9sZCwgYW5kIHNvIGFsbW9zdCBleHBpcmVkLFxuICAgICAgICAvLyAgd2hlbiBpdCBpc24ndC4uLilcbiAgICAgICAgY2VydE9iai52YWxpZGl0eS5ub3RCZWZvcmUgPSBuZXcgRGF0ZShEYXRlLm5vdygpIC0gMTAwMCAqIDYwICogNSk7XG4gICAgICAgIGNlcnRPYmoudmFsaWRpdHkubm90QWZ0ZXIgPSBuZXcgRGF0ZShEYXRlLm5vdygpICsgbGlmZVNwYW4pO1xuXG4gICAgICAgIGNvbnN0IGNvbW1vbk5hbWVBdHRycyA9IFt7IG5hbWU6IFwiY29tbW9uTmFtZVwiLCB2YWx1ZTogZG9tYWluIH1dO1xuICAgICAgICBjZXJ0T2JqLnNldFN1YmplY3QoY29tbW9uTmFtZUF0dHJzKTtcblxuICAgICAgICBpZiAoaXNzdWVyID09PSBcInNlbGZcIikge1xuICAgICAgICAgICAgY2VydE9iai5zZXRJc3N1ZXIoY29tbW9uTmFtZUF0dHJzKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGNlcnRPYmouc2V0SXNzdWVyKGZvcmdlLnBraS5jZXJ0aWZpY2F0ZUZyb21QZW0oaXNzdWVyLmNlcnQudG9TdHJpbmcoKSkuc3ViamVjdC5hdHRyaWJ1dGVzKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGxldCBleHRlbnNpb25zID0gW107XG4gICAgICAgIGNvbnN0IGlzQ0EgPSBpc3N1ZXIgPT09IFwic2VsZlwiO1xuICAgICAgICBpZiAoaXNDQSkge1xuICAgICAgICAgICAgZXh0ZW5zaW9ucy5wdXNoKHsgbmFtZTogXCJiYXNpY0NvbnN0cmFpbnRzXCIsIGNBOiB0cnVlIH0pO1xuICAgICAgICB9XG5cbiAgICAgICAgbGV0IGxvY2FsSG9zdERvbWFpbiA9IFwiMTI3LTAtMC0xLlwiICsgZG9tYWluLnNwbGl0KFwiLlwiKS5zbGljZSgtMikuam9pbihcIi5cIik7XG5cbiAgICAgICAgZXh0ZW5zaW9ucy5wdXNoKC4uLltcbiAgICAgICAgICAgIHsgbmFtZTogXCJrZXlVc2FnZVwiLCBrZXlDZXJ0U2lnbjogaXNDQSwgZGlnaXRhbFNpZ25hdHVyZTogdHJ1ZSwgbm9uUmVwdWRpYXRpb246IHRydWUsIGtleUVuY2lwaGVybWVudDogdHJ1ZSwgZGF0YUVuY2lwaGVybWVudDogdHJ1ZSB9LFxuICAgICAgICAgICAgeyBuYW1lOiBcInN1YmplY3RLZXlJZGVudGlmaWVyXCIgfSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBuYW1lOiBcInN1YmplY3RBbHROYW1lXCIsXG4gICAgICAgICAgICAgICAgYWx0TmFtZXM6IFtcbiAgICAgICAgICAgICAgICAgICAgeyB0eXBlOiAyLCB2YWx1ZTogZG9tYWluIH0sXG4gICAgICAgICAgICAgICAgICAgIHsgdHlwZTogMiwgdmFsdWU6IFwiKi5cIiArIGRvbWFpbiB9LFxuICAgICAgICAgICAgICAgICAgICB7IHR5cGU6IDIsIHZhbHVlOiBsb2NhbEhvc3REb21haW4gfSxcbiAgICAgICAgICAgICAgICAgICAgLy8gTk9URTogTm8gbG9uZ2VyIGFsbG93IDEyNy4wLjAuMSwgdG8gbWFrZSB0aGlzIG1vcmUgc2VjdXJlLiBXZSBtaWdodCBlbmFibGUgdGhpc1xuICAgICAgICAgICAgICAgICAgICAvLyAgYmVoYXZpb3IgYmVoaW5kIGEgZmxhZywgZm9yIGRldmVsb3BtZW50LlxuICAgICAgICAgICAgICAgICAgICAvL3sgdHlwZTogNywgaXA6IFwiMTI3LjAuMC4xXCIgfVxuICAgICAgICAgICAgICAgIF1cbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAvLyBOT1RFOiBuYW1lQ29uc3RyYWludHMgYXJlIHN1cHBvcnRlZCB3aXRoIG91ciBicmFuY2guIEJ1dC4uLiBjaHJvbWUgZG9lc24ndCBzdXBwb3J0IHRoZW0sIHNvIHRoZXJlJ3Mgbm8gcG9pbnQgaW4gdXNpbmcgdGhlbS5cbiAgICAgICAgICAgIC8vICAgICAgXCJub2RlLWZvcmdlXCI6IFwiaHR0cHM6Ly9naXRodWIuY29tL3NsaWZ0aXN0L2ZvcmdlI2U2MTgxODFiNDY5YjA3YmRjNzBiOTY4YjAzOTFiZWI4ZWY1ZmVjZDZcIixcbiAgICAgICAgICAgIC8vIHtcbiAgICAgICAgICAgIC8vICAgICBuYW1lOiBcIm5hbWVDb25zdHJhaW50c1wiLFxuICAgICAgICAgICAgLy8gICAgIHBlcm1pdHRlZFN1YnRyZWVzOiBbXG4gICAgICAgICAgICAvLyAgICAgICAgIC8vIENocm9tZSBkb2Vzbid0IHJlc3BlY3QgbmFtZUNvbnN0cmFpbnRzIHBlciBodHRwczovL2J1Z3MuY2hyb21pdW0ub3JnL3AvY2hyb21pdW0vaXNzdWVzL2RldGFpbD9pZD0xMDcyMDgzLFxuICAgICAgICAgICAgLy8gICAgICAgICAvLyAgYXMgdGhlIHNwZWMgZGVjaWRlZCB0aGF0IFwiZnJlZSB0byBwcm9jZXNzIG9yIGlnbm9yZSBzdWNoIGluZm9ybWF0aW9uXCIgKHdoZW4gcHJlc2VudCBpbiBzZWxmXG4gICAgICAgICAgICAvLyAgICAgICAgIC8vICBzaWduZWQgY2VydGlmaWNhdGVzKSwgYW5kIHRoZXJlZm9yZSB0aGUgY2hyb21lIGltcGxlbWVudGF0aW9uIGRlY2lkZWQgXCJUaGUgZmlyc3Qgb3JkZXIgaXMgdG8gYmVoYXZlIHByZWRpY3RhYmx5XCIsXG4gICAgICAgICAgICAvLyAgICAgICAgIC8vICBzby4uLiB0aGV5J3JlIG5vdCBnb2luZyB0byBzdXBwb3J0IGl0LCBiZWNhdXNlIHdoeSBoYXZlIGEgZmVhdHVyZSBpbiBvbmUgcGxhY2UsIGlmIGl0IGlzbid0XG4gICAgICAgICAgICAvLyAgICAgICAgIC8vICBvbiBhbmRyb2lkIGFzIHdlbGwuLi4gdWdoLi4uXG4gICAgICAgICAgICAvLyAgICAgICAgIC8vIFdvcmtzIGZpbmUgb24gRWRnZSB0aG91Z2hcbiAgICAgICAgICAgIC8vICAgICAgICAgeyB0eXBlOiAyLCB2YWx1ZTogZm9yZ2UudXRpbC5lbmNvZGVVdGY4KGRvbWFpbikgfSxcbiAgICAgICAgICAgIC8vICAgICAgICAgeyB0eXBlOiAyLCB2YWx1ZTogZm9yZ2UudXRpbC5lbmNvZGVVdGY4KGxvY2FsSG9zdERvbWFpbikgfSxcbiAgICAgICAgICAgIC8vICAgICBdXG4gICAgICAgICAgICAvLyB9LFxuICAgICAgICBdKTtcbiAgICAgICAgY2VydE9iai5zZXRFeHRlbnNpb25zKGV4dGVuc2lvbnMpO1xuXG5cbiAgICAgICAgbWVhc3VyZUJsb2NrKGZ1bmN0aW9uIHNpZ24oKSB7XG4gICAgICAgICAgICBpZiAoaXNzdWVyID09PSBcInNlbGZcIikge1xuICAgICAgICAgICAgICAgIGNlcnRPYmouc2lnbihrZXlQYWlyLnByaXZhdGVLZXkgYXMgYW55LCBmb3JnZS5tZC5zaGEyNTYuY3JlYXRlKCkpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBjZXJ0T2JqLnNpZ24ocHJpdmF0ZUtleUZyb21QZW0oaXNzdWVyLmtleS50b1N0cmluZygpKSBhcyBhbnksIGZvcmdlLm1kLnNoYTI1Ni5jcmVhdGUoKSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuXG4gICAgICAgIHJldHVybiBtZWFzdXJlQmxvY2soZnVuY3Rpb24gdG9QZW1zKCkge1xuICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgICBkb21haW4sXG4gICAgICAgICAgICAgICAgY2VydDogQnVmZmVyLmZyb20oZm9yZ2UucGtpLmNlcnRpZmljYXRlVG9QZW0oY2VydE9iaikpLFxuICAgICAgICAgICAgICAgIGtleTogQnVmZmVyLmZyb20ocHJpdmF0ZUtleVRvUGVtKGtleVBhaXIucHJpdmF0ZUtleSkpLFxuICAgICAgICAgICAgfTtcbiAgICAgICAgfSk7XG4gICAgfSk7XG59XG5leHBvcnQgZnVuY3Rpb24gcHJpdmF0ZUtleVRvUGVtKGJ1ZmZlcjogZm9yZ2UucGtpLlByaXZhdGVLZXkgfCBmb3JnZS5FZDI1NTE5UHJpdmF0ZUtleSkge1xuICAgIGlmIChcInByaXZhdGVLZXlCeXRlc1wiIGluIGJ1ZmZlcikge1xuICAgICAgICByZXR1cm4gZm9yZ2UuZWQyNTUxOS5wcml2YXRlS2V5VG9QZW0oYnVmZmVyKTtcbiAgICB9XG4gICAgcmV0dXJuIGZvcmdlLnBraS5wcml2YXRlS2V5VG9QZW0oYnVmZmVyKTtcbn1cbmZ1bmN0aW9uIHByaXZhdGVLZXlGcm9tUGVtKHBlbTogc3RyaW5nKSB7XG4gICAgLy8gV2Ugd2FudCB0byBndWVzcyB0aGUgdHlwZSBjb3JyZWN0bHksIGFzIGNhdWdodCBleGNlcHRpb25zIG1ha2UgZGVidWdnaW5nIGFubm95aW5nXG4gICAgaWYgKHBlbS5sZW5ndGggPCAyMDApIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIHJldHVybiBmb3JnZS5lZDI1NTE5LnByaXZhdGVLZXlGcm9tUGVtKHBlbSk7XG4gICAgICAgIH0gY2F0Y2ggeyB9XG4gICAgfVxuICAgIHRyeSB7XG4gICAgICAgIHJldHVybiBmb3JnZS5wa2kucHJpdmF0ZUtleUZyb21QZW0ocGVtKTtcbiAgICB9IGNhdGNoIHtcbiAgICAgICAgcmV0dXJuIGZvcmdlLmVkMjU1MTkucHJpdmF0ZUtleUZyb21QZW0ocGVtKTtcbiAgICB9XG59XG5mdW5jdGlvbiBwdWJsaWNLZXlGcm9tQ2VydChjZXJ0OiBzdHJpbmcpIHtcbiAgICByZXR1cm4gcGFyc2VDZXJ0KGNlcnQpLnB1YmxpY0tleTtcbn1cbmV4cG9ydCBmdW5jdGlvbiBwYXJzZUNlcnQoUEVNb3JERVI6IHN0cmluZyB8IEJ1ZmZlcikge1xuICAgIHJldHVybiBmb3JnZS5wa2kuY2VydGlmaWNhdGVGcm9tUGVtKG5vcm1hbGl6ZUNlcnRUb1BFTShQRU1vckRFUikpO1xufVxuXG4vLyBHZXRzIGEgdW5pcXVlIHZhbHVlIHRvIHJlcHJlc2VudCB0aGUgcHVibGljIGtleVxuZXhwb3J0IGZ1bmN0aW9uIGdldFB1YmxpY0lkZW50aWZpZXIoUEVNb3JERVI6IHN0cmluZyB8IEJ1ZmZlcik6IEJ1ZmZlciB7XG4gICAgbGV0IG9iaiA9IHBhcnNlQ2VydChQRU1vckRFUik7XG4gICAgbGV0IHB1YmxpY0tleSA9IG9iai5wdWJsaWNLZXk7XG4gICAgaWYgKFwicHVibGljS2V5Qnl0ZXNcIiBpbiBwdWJsaWNLZXkpIHtcbiAgICAgICAgcmV0dXJuIEJ1ZmZlci5mcm9tKHB1YmxpY0tleS5wdWJsaWNLZXlCeXRlcyBhcyBhbnkpO1xuICAgIH1cbiAgICByZXR1cm4gQnVmZmVyLmZyb20obmV3IFVpbnQzMkFycmF5KChwdWJsaWNLZXkgYXMgYW55KS5uLmRhdGEpLmJ1ZmZlcik7XG59XG5cbmZ1bmN0aW9uIGlzRUQyNTUxOShrZXk6IHN0cmluZyB8IEJ1ZmZlcikge1xuICAgIHJldHVybiBrZXkubGVuZ3RoIDwgMjU2O1xufVxuXG4vLyBFUVVJVkFMRU5UIFRPOiBgY3J5cHRvLmNyZWF0ZVNpZ24oXCJTSEEyNTZcIikudXBkYXRlKEpTT04uc3RyaW5naWZ5KHBheWxvYWQpKS5zaWduKGtleUNlcnQua2V5LCBcImJpbmFyeVwiKWBcbmV4cG9ydCBjb25zdCBzaWduID0gbWVhc3VyZVdyYXAoZnVuY3Rpb24gc2lnbihrZXlQYWlyOiB7IGtleTogc3RyaW5nIHwgQnVmZmVyIH0sIGRhdGE6IHVua25vd24pOiBzdHJpbmcge1xuICAgIGxldCBkYXRhU3RyID0gSlNPTi5zdHJpbmdpZnkoZGF0YSk7XG4gICAgaWYgKGlzRUQyNTUxOShrZXlQYWlyLmtleSkpIHtcbiAgICAgICAgbGV0IHByaXZhdGVLZXkgPSAoZm9yZ2UucGtpLmVkMjU1MTkgYXMgYW55KS5wcml2YXRlS2V5RnJvbVBlbShrZXlQYWlyLmtleS50b1N0cmluZygpKTtcbiAgICAgICAgcmV0dXJuIHByaXZhdGVLZXkuc2lnbihkYXRhU3RyKTtcbiAgICB9IGVsc2Uge1xuICAgICAgICBsZXQgcHJpdmF0ZUtleSA9IGZvcmdlLnBraS5wcml2YXRlS2V5RnJvbVBlbShrZXlQYWlyLmtleS50b1N0cmluZygpKTtcbiAgICAgICAgY29uc3QgbWQgPSBmb3JnZS5tZC5zaGEyNTYuY3JlYXRlKCk7XG4gICAgICAgIG1kLnVwZGF0ZShkYXRhU3RyKTtcbiAgICAgICAgcmV0dXJuIHByaXZhdGVLZXkuc2lnbihtZCk7XG4gICAgfVxufSk7XG5cbmV4cG9ydCBmdW5jdGlvbiB2ZXJpZnkoY2VydDogc3RyaW5nLCBzaWduYXR1cmU6IHN0cmluZywgZGF0YTogdW5rbm93bikge1xuICAgIGxldCBjZXJ0T2JqID0gcGFyc2VDZXJ0KGNlcnQpO1xuICAgIHJldHVybiAoY2VydE9iai5wdWJsaWNLZXkgYXMgZm9yZ2UucGtpLnJzYS5QdWJsaWNLZXkpLnZlcmlmeShKU09OLnN0cmluZ2lmeShkYXRhKSwgc2lnbmF0dXJlKTtcbn1cblxuZnVuY3Rpb24gbm9ybWFsaXplQ2VydFRvUEVNKFBFTW9yREVSOiBzdHJpbmcgfCBCdWZmZXIpOiBzdHJpbmcge1xuICAgIGlmIChQRU1vckRFUi50b1N0cmluZygpLnN0YXJ0c1dpdGgoXCItLS0tLUJFR0lOIENFUlRJRklDQVRFLS0tLS1cIikpIHtcbiAgICAgICAgcmV0dXJuIFBFTW9yREVSLnRvU3RyaW5nKCk7XG4gICAgfVxuICAgIFBFTW9yREVSID0gUEVNb3JERVIudG9TdHJpbmcoXCJiYXNlNjRcIik7XG4gICAgcmV0dXJuIFwiLS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tXFxuXCIgKyBQRU1vckRFUiArIFwiXFxuLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLVwiO1xufVxuXG5mdW5jdGlvbiBnZXREb21haW5QYXJ0RnJvbVB1YmxpY0tleShwdWJsaWNLZXk6IHsgcHVibGljS2V5Qnl0ZXM6IEJ1ZmZlciB9IHwgZm9yZ2UucGtpLktleVBhaXJbXCJwdWJsaWNLZXlcIl0gfCBCdWZmZXIpIHtcbiAgICBsZXQgYnl0ZXM6IEJ1ZmZlcjtcbiAgICBpZiAoXCJwdWJsaWNLZXlCeXRlc1wiIGluIHB1YmxpY0tleSkge1xuICAgICAgICBieXRlcyA9IHB1YmxpY0tleS5wdWJsaWNLZXlCeXRlcztcbiAgICB9IGVsc2UgaWYgKHB1YmxpY0tleSBpbnN0YW5jZW9mIEJ1ZmZlcikge1xuICAgICAgICBieXRlcyA9IHB1YmxpY0tleTtcbiAgICB9IGVsc2Uge1xuICAgICAgICBieXRlcyA9IEJ1ZmZlci5mcm9tKG5ldyBVaW50MzJBcnJheSgocHVibGljS2V5IGFzIGFueSkubi5kYXRhKS5idWZmZXIpO1xuICAgIH1cbiAgICByZXR1cm4gXCJiXCIgKyBzaGEyNjUuc2hhMjU2KEJ1ZmZlci5mcm9tKGJ5dGVzKSkuc2xpY2UoMCwgMTYpLnJlcGxhY2VBbGwoXCIrXCIsIFwiLVwiKS5yZXBsYWNlQWxsKFwiL1wiLCBcIl9cIik7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB2YWxpZGF0ZUNBQ2VydChkb21haW46IHN0cmluZywgY2VydDogc3RyaW5nIHwgQnVmZmVyKSB7XG4gICAgbGV0IGNlcnRQYXJzZWQgPSBwYXJzZUNlcnQoY2VydCk7XG5cbiAgICBsZXQgc3ViamVjdCA9IGNlcnRQYXJzZWQuc3ViamVjdC5nZXRGaWVsZChcIkNOXCIpLnZhbHVlIGFzIHN0cmluZztcbiAgICBsZXQgbG9jYWxob3N0RG9tYWluID0gXCIxMjctMC0wLTEuXCIgKyBzdWJqZWN0LnNwbGl0KFwiLlwiKS5zbGljZSgtMikuam9pbihcIi5cIik7XG5cbiAgICBsZXQgZG9tYWluUGFydHMgPSBzdWJqZWN0LnNwbGl0KFwiLlwiKS5yZXZlcnNlKCk7XG5cbiAgICBsZXQgcm9vdERvbWFpblBhcnNlZCA9IFtkb21haW5QYXJ0cy5zaGlmdCgpLCBkb21haW5QYXJ0cy5zaGlmdCgpXS5yZXZlcnNlKCkuam9pbihcIi5cIik7XG4gICAgaWYgKHJvb3REb21haW5QYXJzZWQgIT09IGRvbWFpbikge1xuICAgICAgICAvLyBUaGlzIGlzIGltcG9ydGFudCwgYXMgb3VyIHRydXN0IHN0b3JlIGNvbnRhaW5zIG1vcmUgdGhlbiBqdXN0IE9VUiBjZXJ0aWZpY2F0ZXMsXG4gICAgICAgIC8vICBzbyBpZiB3ZSBhbGxvdyBhbnkgZG9tYWlucyB0aGVuIHJlYWwgZG9tYWlucyBjYW4gaW1wZXJzb25hdGUgYW55b25lISBJdCBoYXMgdG9cbiAgICAgICAgLy8gIGJlIG9uZSBPVVIgZG9tYWluIHRvIGJlIHRydXN0ZWQhXG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgQ2VydGlmaWNhdGUgcm9vdCBkb21haW4gc2hvdWxkIGJlICR7ZG9tYWlufSwgYnV0IGlzICR7cm9vdERvbWFpblBhcnNlZH1gKTtcbiAgICB9XG4gICAgLy8gVE9ETzogTWF5YmUganVzdCBza2lwIGlmIGl0IGlzbid0IGEgaGFzaCBzdHJpbmc/XG4gICAgaWYgKGRvbWFpblBhcnRzWzBdID09PSBcIm5vcHJveHlcIikge1xuICAgICAgICBkb21haW5QYXJ0cy5zaGlmdCgpO1xuICAgIH1cblxuICAgIGxldCBjZXJ0RXhwZWN0ZWRQdWJsaWNLZXlQYXJ0ID0gKGRvbWFpblBhcnRzLnNoaWZ0KCkgfHwgXCJcIikuc3BsaXQoXCItXCIpLnNsaWNlKC0xKVswXTtcbiAgICBsZXQgY2VydEFjdHVhbFB1YmxpY0tleVBhcnQgPSBnZXREb21haW5QYXJ0RnJvbVB1YmxpY0tleShjZXJ0UGFyc2VkLnB1YmxpY0tleSk7XG4gICAgaWYgKGNlcnRFeHBlY3RlZFB1YmxpY0tleVBhcnQgIT09IGNlcnRBY3R1YWxQdWJsaWNLZXlQYXJ0KSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgQ2VydGlmaWNhdGUgcHVibGljIGtleSBpbiB0aGUgdXJsIGlzICR7Y2VydEV4cGVjdGVkUHVibGljS2V5UGFydH0sIGJ1dCBpbiB0aGUgY2VydCBpcyAke2NlcnRBY3R1YWxQdWJsaWNLZXlQYXJ0fWApO1xuICAgIH1cblxuICAgIC8vIEFMU08sIHJlcXVpcmUgbmFtZSBjb25zdHJhaW50cyB0byBiZSBwcmVzZW50LCBhbmQgdG8gcmVzdHJpY3QgdG8gdGhlIFwiQ05cIlxuICAgIGxldCBuYW1lQ29uc3RyYWludHMgPSBjZXJ0UGFyc2VkLmdldEV4dGVuc2lvbihcIm5hbWVDb25zdHJhaW50c1wiKSBhcyBhbnk7XG4gICAgaWYgKCFuYW1lQ29uc3RyYWludHMpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBDZXJ0aWZpY2F0ZSBtdXN0IGhhdmUgbmFtZUNvbnN0cmFpbnRzYCk7XG4gICAgfVxuICAgIGxldCBzdWJ0cmVlcyA9IG5hbWVDb25zdHJhaW50cy5wZXJtaXR0ZWRTdWJ0cmVlcztcbiAgICBpZiAoIXN1YnRyZWVzKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgQ2VydGlmaWNhdGUgbXVzdCBoYXZlIG5hbWVDb25zdHJhaW50cy5wZXJtaXR0ZWRTdWJ0cmVlc2ApO1xuICAgIH1cbiAgICBsZXQgc3VidHJlZVZhbHVlcyA9IHN1YnRyZWVzLm1hcCgoeDogYW55KSA9PiB4LnZhbHVlKTtcbiAgICAvLyBJZ25vcmUgbG9jYWxob3N0RG9tYWluLCBhcyBpdCBjYW4gYWx3YXlzIHNhZmVseSBiZSBhbGxvd2VkICh0aGUgc2FtZSBtYWNoaW5lXG4gICAgLy8gICAgICBpcyBhbHdheXMgYWxsb3dlZCkuXG4gICAgc3VidHJlZVZhbHVlcyA9IHN1YnRyZWVWYWx1ZXMuZmlsdGVyKCh4OiBzdHJpbmcpID0+IHggIT09IGxvY2FsaG9zdERvbWFpbik7XG4gICAgaWYgKHN1YnRyZWVWYWx1ZXMubGVuZ3RoICE9PSAxIHx8IHN1YnRyZWVWYWx1ZXNbMF0gIT09IHN1YmplY3QpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBDZXJ0aWZpY2F0ZSBtdXN0IGhhdmUgYSBzaW5nbGUgY29uc3RyYWluZWQgZG9tYWluIChoYWQgJHtKU09OLnN0cmluZ2lmeShzdWJ0cmVlVmFsdWVzKX0pYCk7XG4gICAgfVxuXG4gICAgdmFsaWRhdGVBbHROYW1lcyhjZXJ0UGFyc2VkLCBzdWJqZWN0KTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHZhbGlkYXRlQ2VydGlmaWNhdGUoZG9tYWluOiBzdHJpbmcsIGNlcnQ6IEJ1ZmZlciB8IHN0cmluZywgaXNzdWVyQ2VydDogQnVmZmVyIHwgc3RyaW5nKSB7XG4gICAgdmFsaWRhdGVDQUNlcnQoZG9tYWluLCBpc3N1ZXJDZXJ0KTtcblxuICAgIGxldCBjZXJ0UGFyc2VkID0gcGFyc2VDZXJ0KGNlcnQpO1xuICAgIGxldCBzdWJqZWN0ID0gY2VydFBhcnNlZC5zdWJqZWN0LmdldEZpZWxkKFwiQ05cIikudmFsdWUgYXMgc3RyaW5nO1xuICAgIGxldCBsb2NhbGhvc3REb21haW4gPSBcIjEyNy0wLTAtMS5cIiArIHN1YmplY3Quc3BsaXQoXCIuXCIpLnNsaWNlKC0yKS5qb2luKFwiLlwiKTtcblxuICAgIGxldCBkb21haW5QYXJ0cyA9IHN1YmplY3Quc3BsaXQoXCIuXCIpLnJldmVyc2UoKTtcblxuICAgIGxldCByb290RG9tYWluUGFyc2VkID0gW2RvbWFpblBhcnRzLnNoaWZ0KCksIGRvbWFpblBhcnRzLnNoaWZ0KCldLnJldmVyc2UoKS5qb2luKFwiLlwiKTtcbiAgICBpZiAocm9vdERvbWFpblBhcnNlZCAhPT0gZG9tYWluKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgQ2VydGlmaWNhdGUgcm9vdCBkb21haW4gc2hvdWxkIGJlICR7ZG9tYWlufSwgYnV0IGlzICR7cm9vdERvbWFpblBhcnNlZH1gKTtcbiAgICB9XG4gICAgLy8gVE9ETzogTWF5YmUganVzdCBza2lwIGlmIGl0IGlzbid0IGEgaGFzaCBzdHJpbmc/XG4gICAgaWYgKGRvbWFpblBhcnRzWzBdID09PSBcIm5vcHJveHlcIikge1xuICAgICAgICBkb21haW5QYXJ0cy5zaGlmdCgpO1xuICAgIH1cblxuICAgIGxldCBpc3N1ZXJDZXJ0UGFyc2VkID0gcGFyc2VDZXJ0KGlzc3VlckNlcnQpO1xuXG4gICAgbGV0IGlzc3VlckV4cGVjdGVkUHVibGljS2V5UGFydCA9IGRvbWFpblBhcnRzLnNoaWZ0KCkgfHwgXCJcIjtcbiAgICBsZXQgaXNzdWVyQWN0dWFsUHVibGljS2V5UGFydCA9IGdldERvbWFpblBhcnRGcm9tUHVibGljS2V5KGlzc3VlckNlcnRQYXJzZWQucHVibGljS2V5KTtcbiAgICBpZiAoaXNzdWVyRXhwZWN0ZWRQdWJsaWNLZXlQYXJ0ICE9PSBpc3N1ZXJBY3R1YWxQdWJsaWNLZXlQYXJ0KSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgSXNzdWVyIHB1YmxpYyBrZXkgaW4gdGhlIHVybCBpcyAke2lzc3VlckV4cGVjdGVkUHVibGljS2V5UGFydH0sIGJ1dCBpbiB0aGUgY2VydCBpcyAke2lzc3VlckFjdHVhbFB1YmxpY0tleVBhcnR9YCk7XG4gICAgfVxuXG4gICAgLy8gVGFrZSB0aGUgbGFzdCBwYXJ0XG4gICAgbGV0IGNlcnRFeHBlY3RlZFB1YmxpY0tleVBhcnQgPSBkb21haW5QYXJ0cy5zaGlmdCgpIHx8IFwiXCI7XG4gICAgbGV0IGNlcnRBY3R1YWxQdWJsaWNLZXlQYXJ0ID0gZ2V0RG9tYWluUGFydEZyb21QdWJsaWNLZXkoY2VydFBhcnNlZC5wdWJsaWNLZXkpO1xuICAgIGlmIChjZXJ0RXhwZWN0ZWRQdWJsaWNLZXlQYXJ0ICE9PSBjZXJ0QWN0dWFsUHVibGljS2V5UGFydCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYENlcnRpZmljYXRlIHB1YmxpYyBrZXkgaW4gdGhlIHVybCBpcyAke2NlcnRFeHBlY3RlZFB1YmxpY0tleVBhcnR9LCBidXQgaW4gdGhlIGNlcnQgaXMgJHtjZXJ0QWN0dWFsUHVibGljS2V5UGFydH1gKTtcbiAgICB9XG5cblxuICAgIGxldCBuYW1lQ29uc3RyYWludHMgPSBpc3N1ZXJDZXJ0UGFyc2VkLmdldEV4dGVuc2lvbihcIm5hbWVDb25zdHJhaW50c1wiKSBhcyBhbnk7XG4gICAgaWYgKCFuYW1lQ29uc3RyYWludHMpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBDQSBtdXN0IGhhdmUgbmFtZUNvbnN0cmFpbnRzYCk7XG4gICAgfVxuICAgIGxldCBzdWJ0cmVlcyA9IG5hbWVDb25zdHJhaW50cy5wZXJtaXR0ZWRTdWJ0cmVlcztcbiAgICBpZiAoIXN1YnRyZWVzKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgQ0EgbXVzdCBoYXZlIG5hbWVDb25zdHJhaW50cy5wZXJtaXR0ZWRTdWJ0cmVlc2ApO1xuICAgIH1cbiAgICBsZXQgc3VidHJlZVZhbHVlcyA9IHN1YnRyZWVzLm1hcCgoeDogYW55KSA9PiB4LnZhbHVlKTtcbiAgICAvLyBJZ25vcmUgbG9jYWxob3N0RG9tYWluLCBhcyBpdCBjYW4gYWx3YXlzIHNhZmVseSBiZSBhbGxvd2VkICh0aGUgc2FtZSBtYWNoaW5lXG4gICAgLy8gICAgICBpcyBhbHdheXMgYWxsb3dlZCkuXG4gICAgc3VidHJlZVZhbHVlcyA9IHN1YnRyZWVWYWx1ZXMuZmlsdGVyKCh4OiBzdHJpbmcpID0+IHggIT09IGxvY2FsaG9zdERvbWFpbik7XG4gICAgaWYgKHN1YnRyZWVWYWx1ZXMubGVuZ3RoICE9PSAxKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgQ0EgbXVzdCBoYXZlIGEgc2luZ2xlIGNvbnN0cmFpbmVkIGRvbWFpbiAoaGFkICR7SlNPTi5zdHJpbmdpZnkoc3VidHJlZVZhbHVlcyl9KWApO1xuICAgIH1cblxuICAgIGxldCBzdWJ0cmVlID0gc3VidHJlZVZhbHVlc1swXTtcbiAgICBpZiAoc3VidHJlZSAhPT0gc3ViamVjdCAmJiAhc3ViamVjdC5lbmRzV2l0aChcIi5cIiArIHN1YnRyZWUpKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgQ2VydGlmaWNhdGUgbXVzdCBiZSBhIHN1YnRyZWUgb2YgdGhlIENBIChDQTogJHtzdWJ0cmVlfSwgY2VydDogJHtzdWJqZWN0fSlgKTtcbiAgICB9XG5cbiAgICB2YWxpZGF0ZUFsdE5hbWVzKGNlcnRQYXJzZWQsIHN1YmplY3QpO1xuXG4gICAgLy8gVmVyaWZ5IGlzc3VlciBBQ1RVQUxMWSBzaWduZWQgY2VydFBhcnNlZFxuICAgIGlmICghaXNzdWVyQ2VydFBhcnNlZC52ZXJpZnkoY2VydFBhcnNlZCkpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBJc3N1ZXIgZGlkIG5vdCBzaWduIGNlcnRpZmljYXRlYCk7XG4gICAgfVxufVxuXG4vLyBSZXF1aXJlIGFsdCBuYW1lcyB0byBiZSBlaXRoZXIgZXF1YWwgdG8gXCJDTlwiLCBvciBhIHN1YnRyZWUgb2YgXCJDTlwiXG5mdW5jdGlvbiB2YWxpZGF0ZUFsdE5hbWVzKGNlcnRQYXJzZWQ6IGZvcmdlLnBraS5DZXJ0aWZpY2F0ZSwgc3ViamVjdDogc3RyaW5nKSB7XG4gICAgbGV0IGxvY2FsaG9zdERvbWFpbiA9IFwiMTI3LTAtMC0xLlwiICsgc3ViamVjdC5zcGxpdChcIi5cIikuc2xpY2UoLTIpLmpvaW4oXCIuXCIpO1xuXG4gICAgbGV0IGFsdE5hbWVzT2JqID0gY2VydFBhcnNlZC5nZXRFeHRlbnNpb24oXCJzdWJqZWN0QWx0TmFtZVwiKSBhcyBhbnk7XG4gICAgbGV0IGFsdE5hbWVzID0gYWx0TmFtZXNPYmo/LmFsdE5hbWVzLm1hcCgoeDogYW55KSA9PiB4LnZhbHVlKTtcbiAgICAvLyBBbGxvdyBsb2NhbGhvc3REb21haW4sIGFzIGl0IGNhbiBhbHdheXMgc2FmZWx5IGJlIGFsbG93ZWRcbiAgICBhbHROYW1lcyA9IGFsdE5hbWVzLmZpbHRlcigoeDogc3RyaW5nKSA9PiB4ICE9PSBsb2NhbGhvc3REb21haW4pO1xuICAgIGlmIChcbiAgICAgICAgYWx0TmFtZXMuc29tZSgoeDogc3RyaW5nKSA9PlxuICAgICAgICAgICAgIShcbiAgICAgICAgICAgICAgICB4ID09PSBzdWJqZWN0XG4gICAgICAgICAgICAgICAgfHwgeC5lbmRzV2l0aChcIi5cIiArIHN1YmplY3QpXG4gICAgICAgICAgICAgICAgLy8gQ29tbWVudGVkIG91dCwgYmVjYXVzZS4uLiBpdCBpcyBzbyBlYXN5IHRvIHB1Ymxpc2ggYSAxMjcuMC4wLjEgQSByZWNvcmQsXG4gICAgICAgICAgICAgICAgLy8gIGFuZCBldmVuIHRvIGdlbmVyYXRlIGEgcmVhbCBjZXJ0LCBzbywgd2Ugc2hvdWxkIGpzdXQgZG8gdGhhdCwgYW5kIGtlZXAgaXQgc2VjdXJlLlxuICAgICAgICAgICAgICAgIC8vICBJZiB3ZSBuZWVkIHRoaXMgZm9yIGRldmVsb3BtZW50IHdlIGNhbiBwdXQgaXQgYmVoaW5kIGEgZmxhZywgc28gbm9uLWRldmVsb3BtZW50XG4gICAgICAgICAgICAgICAgLy8gIGluc3RhbmNlcyBhcmUgc3RpbGwgc2VjdXJlLlxuICAgICAgICAgICAgICAgIC8vIC8vIEFsc28gYWxsb3cgMTI3LjAuMC4xLCBmb3IgbG9jYWwgdGVzdGluZywgZm9yIG5vdz9cbiAgICAgICAgICAgICAgICAvLyAvLyAgLSBUaGlzIG1pZ2h0IGJlIGluc2VjdXJlIGlmIHRoZXNlIGFyZSB0cnVzdGVkIGJ5IHRoZSBicm93c2VyLFxuICAgICAgICAgICAgICAgIC8vIC8vICAgICAgYXMgdGhlbiBhbnlvbmUgdGhhdCBzdG9yZXMgYW55IGNvb2tpZXMgaW4gdGhpcyBpcCBjYW4gaGF2ZVxuICAgICAgICAgICAgICAgIC8vIC8vICAgICAgdGhlIGNvb2tpZXMgc3RvbGVuLiBCdXQuLi4gaWYgdGhpcyBpcyBqdXN0IGNyb3NzLXNlcnZlci4uLlxuICAgICAgICAgICAgICAgIC8vIC8vICAgICAgSSBkb24ndCBzZWUgaG93IHRoaXMgY291bGQgY2F1c2UgYSBzZWN1cml0eSB2dWxuZXJhYmlsaXR5LlxuICAgICAgICAgICAgICAgIC8vIHx8IHggPT09IEJ1ZmZlci5mcm9tKFsxMjcsIDAsIDAsIDFdKS50b1N0cmluZygpXG4gICAgICAgICAgICApXG4gICAgICAgIClcbiAgICApIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBJbnZhbGlkIGFsdCBuYW1lcy4gTXVzdCBiZSBzdWJ0cmVlcyBvZiB0aGUgc3ViamVjdCAoQ04pICR7SlNPTi5zdHJpbmdpZnkoc3ViamVjdCl9LCB3YXMgJHtKU09OLnN0cmluZ2lmeShhbHROYW1lcyl9YCk7XG4gICAgfVxufVxuXG5cbmV4cG9ydCBmdW5jdGlvbiBnZW5lcmF0ZUtleVBhaXIoKSB7XG4gICAgcmV0dXJuIG1lYXN1cmVCbG9jayhmdW5jdGlvbiBnZW5lcmF0ZUtleVBhaXIoKSB7XG4gICAgICAgIC8vIE5PVEU6IFdlIHVzZSBFRDI1NTE5IGJlY2F1c2UgaXQgY2FuIGdlbmVyYXRlZCBrZXlzIGFib3V0IDEwWCBmYXN0ZXIsIFdISUNILCBpcyBzdGlsbCBzbG93XG4gICAgICAgIC8vICAofjZtcyBvbiBteSBtYWNoaW5lKS4gU28gd2UgREVGSU5JVEVMWSBkb24ndCB3YW50IGl0IHRvIGJlIDEwWCBzbG93ZXIhXG4gICAgICAgIC8vIE5PVEU6IEVEMjU1MTkgZG9lbnMndCBoYXZlIGdyZWF0IHN1cHBvcnQgaW4gYnJvd3NlcnMsIGJ1dCB3ZSBzaG91bGRuJ3QgbmVlZCBzZWxmIHNpZ25lZCBjZXJ0aWZpY2F0ZXNcbiAgICAgICAgLy8gIGluIHRoZSBicm93c2VyIGFueXdheS5cbiAgICAgICAgLy8gIC0gaHR0cHM6Ly9zZWN1cml0eS5zdGFja2V4Y2hhbmdlLmNvbS9hLzIzNjk0My8yODIzNjdcbiAgICAgICAgLy9sZXQga2V5UGFpciA9IGZvcmdlLmVkMjU1MTkuZ2VuZXJhdGVLZXlQYWlyKCk7XG4gICAgICAgIGxldCBrZXlQYWlyID0gZm9yZ2UucGtpLnJzYS5nZW5lcmF0ZUtleVBhaXIoKTtcbiAgICAgICAgcmV0dXJuIGtleVBhaXI7XG4gICAgfSk7XG59XG5leHBvcnQgZnVuY3Rpb24gZ2VuZXJhdGVSU0FLZXlQYWlyKCkge1xuICAgIHJldHVybiBtZWFzdXJlQmxvY2soZnVuY3Rpb24gZ2VuZXJhdGVLZXlQYWlyKCkge1xuICAgICAgICByZXR1cm4gZm9yZ2UucGtpLnJzYS5nZW5lcmF0ZUtleVBhaXIoKTtcbiAgICB9KTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdlbmVyYXRlVGVzdENBKGRvbWFpbjogc3RyaW5nKSB7XG4gICAgY29uc3Qga2V5UGFpciA9IGdlbmVyYXRlS2V5UGFpcigpO1xuICAgIGxldCBjYVB1YmxpY0tleVBhcnQgPSBnZXREb21haW5QYXJ0RnJvbVB1YmxpY0tleShrZXlQYWlyLnB1YmxpY0tleSk7XG4gICAgbGV0IGZ1bGxEb21haW4gPSBgJHtjYVB1YmxpY0tleVBhcnR9LiR7ZG9tYWlufWA7XG4gICAgaWYgKCFpc05vZGUoKSkge1xuICAgICAgICBmdWxsRG9tYWluID0gYCR7Y2FQdWJsaWNLZXlQYXJ0fS4ke2RvbWFpbn1gO1xuICAgIH1cblxuICAgIHJldHVybiBjcmVhdGVYNTA5KHsgZG9tYWluOiBmdWxsRG9tYWluLCBpc3N1ZXI6IFwic2VsZlwiLCBrZXlQYWlyLCBsaWZlU3BhbjogdGltZUluRGF5ICogMzY1ICogMjAgfSk7XG59XG5cbmxldCBpZGVudGl0eUNBID0gY2FjaGUoKGRvbWFpbjogc3RyaW5nKSA9PiB7XG4gICAgbGV0IGlkZW50aXR5Q0EgPSBsYXp5KChhc3luYyAoKTogUHJvbWlzZTxYNTA5S2V5UGFpcj4gPT4ge1xuICAgICAgICBsZXQgaWRlbnRpdHlDQUNhY2hlZCA9IGdldElkZW50aXR5U3RvcmUoZG9tYWluKTtcbiAgICAgICAgbGV0IGNhQ2FjaGVkID0gYXdhaXQgaWRlbnRpdHlDQUNhY2hlZC5nZXQoKTtcbiAgICAgICAgaWYgKCFjYUNhY2hlZCkge1xuICAgICAgICAgICAgY29uc29sZS5sb2coYEdlbmVyYXRpbmcgbmV3IGlkZW50aXR5IENBYCk7XG4gICAgICAgICAgICBjb25zdCBrZXlQYWlyID0gZ2VuZXJhdGVLZXlQYWlyKCk7XG4gICAgICAgICAgICBsZXQgY2FQdWJsaWNLZXlQYXJ0ID0gZ2V0RG9tYWluUGFydEZyb21QdWJsaWNLZXkoa2V5UGFpci5wdWJsaWNLZXkpO1xuICAgICAgICAgICAgbGV0IGZ1bGxEb21haW4gPSBgJHtjYVB1YmxpY0tleVBhcnR9LiR7ZG9tYWlufWA7XG4gICAgICAgICAgICBpZiAoIWlzTm9kZSgpKSB7XG4gICAgICAgICAgICAgICAgZnVsbERvbWFpbiA9IGAke2NhUHVibGljS2V5UGFydH0uJHtkb21haW59YDtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgbGV0IHZhbHVlID0gY3JlYXRlWDUwOSh7IGRvbWFpbjogZnVsbERvbWFpbiwgaXNzdWVyOiBcInNlbGZcIiwga2V5UGFpciwgbGlmZVNwYW46IHRpbWVJbkRheSAqIDM2NSAqIDIwIH0pO1xuXG4gICAgICAgICAgICBjYUNhY2hlZCA9IHtcbiAgICAgICAgICAgICAgICBkb21haW46IHZhbHVlLmRvbWFpbixcbiAgICAgICAgICAgICAgICBjZXJ0QjY0OiB2YWx1ZS5jZXJ0LnRvU3RyaW5nKFwiYmFzZTY0XCIpLFxuICAgICAgICAgICAgICAgIGtleUI2NDogdmFsdWUua2V5LnRvU3RyaW5nKFwiYmFzZTY0XCIpLFxuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIGF3YWl0IGlkZW50aXR5Q0FDYWNoZWQuc2V0KGNhQ2FjaGVkKTtcbiAgICAgICAgfVxuICAgICAgICBsZXQgcmVzdWx0ID0ge1xuICAgICAgICAgICAgZG9tYWluOiBjYUNhY2hlZC5kb21haW4sXG4gICAgICAgICAgICBjZXJ0OiBCdWZmZXIuZnJvbShjYUNhY2hlZC5jZXJ0QjY0LCBcImJhc2U2NFwiKSxcbiAgICAgICAgICAgIGtleTogQnVmZmVyLmZyb20oY2FDYWNoZWQua2V5QjY0LCBcImJhc2U2NFwiKSxcbiAgICAgICAgfTtcbiAgICAgICAgdHJ1c3RDZXJ0aWZpY2F0ZShyZXN1bHQuY2VydC50b1N0cmluZygpKTtcbiAgICAgICAgaWRlbnRpdHlDQS5zZXQocmVzdWx0KTtcbiAgICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICB9KSBhcyAoKCkgPT4gTWF5YmVQcm9taXNlPFg1MDlLZXlQYWlyPikpO1xuICAgIHJldHVybiBpZGVudGl0eUNBO1xufSk7XG5cbi8vIElNUE9SVEFOVCEgV2UgZG8gbm90IGVtYmVkIGFueSBkZWJ1ZyBpbmZvIGluIHRoaXMgZG9tYWluLiBJZiB3ZSBkaWQsIGl0IHdvdWxkIGJlIHVzZWZ1bCxcbi8vICBidXQuLi4gcG90ZW50YWxseSBhIHNlY3VyaXR5IHZ1bG5lcmFiaWxpdHksIGFzIGlmIHRoZSBkZWJ1ZyBpbmZvIChzdWNoIGFzIGEgcHJlZml4KVxuLy8gIGlzIHVzZWQgdG8gaWRlbnRpZnkgd2hhdCBhIGNlcnRpZmljYXRlIGlzIGZvciwgaXQgd291bGQgYmUgZWFzeSBmb3IgYW4gYXR0YWNrIHRvXG4vLyAgZm9yZ2UgdGhpcyAoYXMgdGhlIGRlYnVnIGluZm8gd29uJ3QgYmUgc2VjdXJlZCkuIFNvIGl0IGlzIG11Y2ggYmV0dGVyIHRvIGtlZXBcbi8vICB0aGUgY2VydGlmaWNhdGUgb3BhcXVlLCBhbmQgdGhlbiByZXF1aXJlIGFueSBtZXRhZGF0YSB0byBiZSBhY3R1YWxseSB2ZXR0ZWRcbi8vICAoYW5kIGhvcGVmdWxseSBzdG9yZWQgaW4gYSBVSSwgc2hvd2luZyBJUCwgdGltZSwgZXRjKS5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVDZXJ0RnJvbUNBKGNvbmZpZzoge1xuICAgIENBS2V5UGFpcjogWDUwOUtleVBhaXI7XG59KTogWDUwOUtleVBhaXIge1xuICAgIHJldHVybiBtZWFzdXJlQmxvY2soZnVuY3Rpb24gY3JlYXRlQ2VydEZyb21DQSgpIHtcbiAgICAgICAgbGV0IHsgQ0FLZXlQYWlyIH0gPSBjb25maWc7XG4gICAgICAgIGNvbnN0IGtleVBhaXIgPSBnZW5lcmF0ZUtleVBhaXIoKTtcbiAgICAgICAgbGV0IGRvbWFpbktleVBhcnQgPSBnZXREb21haW5QYXJ0RnJvbVB1YmxpY0tleShrZXlQYWlyLnB1YmxpY0tleSk7XG4gICAgICAgIGxldCBmdWxsRG9tYWluID0gYCR7ZG9tYWluS2V5UGFydH0uJHtjb25maWcuQ0FLZXlQYWlyLmRvbWFpbn1gO1xuICAgICAgICByZXR1cm4gY3JlYXRlWDUwOSh7XG4gICAgICAgICAgICBkb21haW46IGZ1bGxEb21haW4sXG4gICAgICAgICAgICBpc3N1ZXI6IENBS2V5UGFpcixcbiAgICAgICAgICAgIGtleVBhaXIsXG4gICAgICAgICAgICBsaWZlU3BhbjogdGltZUluRGF5ICogMzY1ICogMTAsXG4gICAgICAgIH0pO1xuICAgIH0pO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0TWFjaGluZUlkKGRvbWFpbk5hbWU6IHN0cmluZykge1xuICAgIHJldHVybiBkb21haW5OYW1lLnNwbGl0KFwiLlwiKS5zbGljZSgtMykuam9pbihcIi5cIik7XG59XG5cbmV4cG9ydCB0eXBlIE5vZGVJZFBhcnRzID0ge1xuICAgIHRocmVhZElkOiBzdHJpbmc7XG4gICAgbWFjaGluZUlkOiBzdHJpbmc7XG4gICAgZG9tYWluOiBzdHJpbmc7XG4gICAgcG9ydDogbnVtYmVyO1xufTtcbmV4cG9ydCBmdW5jdGlvbiBkZWNvZGVOb2RlSWQobm9kZUlkOiBzdHJpbmcpOiBOb2RlSWRQYXJ0cyB8IHVuZGVmaW5lZCB7XG4gICAgbGV0IGxvY2F0aW9uT2JqID0gZ2V0Tm9kZUlkTG9jYXRpb24obm9kZUlkKTtcbiAgICBpZiAoIWxvY2F0aW9uT2JqKSB7XG4gICAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgfVxuICAgIGxldCBwYXJ0cyA9IGxvY2F0aW9uT2JqLmFkZHJlc3Muc3BsaXQoXCIuXCIpO1xuICAgIGlmIChub2RlSWQuc3RhcnRzV2l0aChcIjEyNy0wLTAtMS5cIikgJiYgcGFydHMubGVuZ3RoID09PSAzKSB7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICB0aHJlYWRJZDogXCJcIixcbiAgICAgICAgICAgIG1hY2hpbmVJZDogcGFydHMuYXQoLTMpIHx8IFwiXCIsXG4gICAgICAgICAgICBkb21haW46IHBhcnRzLnNsaWNlKC0yKS5qb2luKFwiLlwiKSxcbiAgICAgICAgICAgIHBvcnQ6IGxvY2F0aW9uT2JqLnBvcnQsXG4gICAgICAgIH07XG4gICAgfVxuICAgIGlmIChwYXJ0cy5sZW5ndGggPCA0KSB7XG4gICAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgfVxuICAgIHJldHVybiB7XG4gICAgICAgIHRocmVhZElkOiBwYXJ0cy5hdCgtNCkgfHwgXCJcIixcbiAgICAgICAgbWFjaGluZUlkOiBwYXJ0cy5hdCgtMykgfHwgXCJcIixcbiAgICAgICAgZG9tYWluOiBwYXJ0cy5zbGljZSgtMikuam9pbihcIi5cIikgfHwgXCJcIixcbiAgICAgICAgcG9ydDogbG9jYXRpb25PYmoucG9ydCxcbiAgICB9O1xufVxuZXhwb3J0IGZ1bmN0aW9uIGRlY29kZU5vZGVJZEFzc2VydChub2RlSWQ6IHN0cmluZyk6IE5vZGVJZFBhcnRzIHtcbiAgICBsZXQgcmVzdWx0ID0gZGVjb2RlTm9kZUlkKG5vZGVJZCk7XG4gICAgaWYgKCFyZXN1bHQpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBJbnZhbGlkIG5vZGVJZDogJHtub2RlSWR9YCk7XG4gICAgfVxuICAgIHJldHVybiByZXN1bHQ7XG59XG5leHBvcnQgZnVuY3Rpb24gZW5jb2RlTm9kZUlkKHBhcnRzOiBOb2RlSWRQYXJ0cykge1xuICAgIHJldHVybiBgJHtwYXJ0cy50aHJlYWRJZH0uJHtwYXJ0cy5tYWNoaW5lSWR9LiR7cGFydHMuZG9tYWlufToke3BhcnRzLnBvcnR9YDtcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHNldElkZW50aXR5Q0FSYXcoZG9tYWluOiBzdHJpbmcsIGpzb246IHN0cmluZykge1xuICAgIGxldCBpZGVudGl0eUNBQ2FjaGVkID0gZ2V0SWRlbnRpdHlTdG9yZShkb21haW4pO1xuICAgIGxldCBvYmogPSBKU09OLnBhcnNlKGpzb24pIGFzIHtcbiAgICAgICAgZG9tYWluOiBzdHJpbmc7XG4gICAgICAgIGNlcnRCNjQ6IHN0cmluZztcbiAgICAgICAga2V5QjY0OiBzdHJpbmc7XG4gICAgfTtcbiAgICBsZXQgY2EgPSB7XG4gICAgICAgIGRvbWFpbjogb2JqLmRvbWFpbixcbiAgICAgICAgY2VydDogQnVmZmVyLmZyb20ob2JqLmNlcnRCNjQsIFwiYmFzZTY0XCIpLFxuICAgICAgICBrZXk6IEJ1ZmZlci5mcm9tKG9iai5rZXlCNjQsIFwiYmFzZTY0XCIpLFxuICAgIH07XG4gICAgdHJ1c3RDZXJ0aWZpY2F0ZShjYS5jZXJ0LnRvU3RyaW5nKCkpO1xuICAgIGlkZW50aXR5Q0EoZG9tYWluKS5zZXQoY2EpO1xuICAgIGdldFRocmVhZEtleUNlcnRCYXNlKGRvbWFpbikucmVzZXQoKTtcbiAgICBhd2FpdCBpZGVudGl0eUNBQ2FjaGVkLnNldChvYmopO1xuICAgIHJlc2V0QWxsTm9kZUNhbGxGYWN0b3JpZXMoKTtcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGxvYWRJZGVudGl0eUNBKGRvbWFpbjogc3RyaW5nKSB7XG4gICAgYXdhaXQgaWRlbnRpdHlDQShkb21haW4pKCk7XG59XG5leHBvcnQgZnVuY3Rpb24gZ2V0SWRlbnRpdHlDQShkb21haW46IHN0cmluZyk6IFg1MDlLZXlQYWlyIHtcbiAgICBsZXQgdmFsdWUgPSBpZGVudGl0eUNBKGRvbWFpbikoKTtcbiAgICBpZiAodmFsdWUgaW5zdGFuY2VvZiBQcm9taXNlKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcIklkZW50aXR5IENBIGlzIG5vdCB5ZXQgbG9hZGVkLiBDYWxsIGFuZCB3YWl0IGZvciBsb2FkSWRlbnRpdHlDQSgpIGluIHlvdXIgc3RhcnR1cCBiZWZvcmUgYWNjZXNzaW5nIHRoZSBpZGVudGl0eSAob3IgY2FsbCBnZXRJZGVudGl0eUNBUHJvbWlzZSgpKVwiKTtcbiAgICB9XG4gICAgcmV0dXJuIHZhbHVlO1xufVxuXG4vLyBUT0RPOiBSZXBsYWNlIHRoaXMgd2l0aCBhIGRhdGFiYXNlLCBzbyBpdCBpcyBlYXN5IGZvciB1cyB0byB0cnVzdCBDQXNcbi8vICBjcm9zcyBtYWNoaW5lLCBhbmQgZXZlbiBoYXZlIG11bHRpcGxlIHVzZXJzLCBldGMsIGV0Yy5cbmV4cG9ydCBmdW5jdGlvbiBnZXRJZGVudGl0eUNBUHJvbWlzZShkb21haW46IHN0cmluZyk6IE1heWJlUHJvbWlzZTxYNTA5S2V5UGFpcj4ge1xuICAgIHJldHVybiBpZGVudGl0eUNBKGRvbWFpbikoKTtcbn1cblxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0T3duTWFjaGluZUlkKGRvbWFpbjogc3RyaW5nKSB7XG4gICAgcmV0dXJuIGdldE1hY2hpbmVJZChnZXRJZGVudGl0eUNBKGRvbWFpbikuZG9tYWluKTtcbn1cblxuLyoqIFBhcnQgb2YgdGhlIG1hY2hpbmVJZCBjb21lcyBmcm9tIHRoZSBwdWJsaWNLZXksIHNvIHdlIGNhbiB1c2UgaXQgdG8gdmVyaWZ5ICovXG5leHBvcnQgZnVuY3Rpb24gdmVyaWZ5TWFjaGluZUlkRm9yUHVibGljS2V5KGNvbmZpZzoge1xuICAgIG1hY2hpbmVJZDogc3RyaW5nO1xuICAgIHB1YmxpY0tleTogQnVmZmVyO1xufSk6IGJvb2xlYW4ge1xuICAgIGxldCB7IG1hY2hpbmVJZCwgcHVibGljS2V5IH0gPSBjb25maWc7XG4gICAgbGV0IGRvbWFpblBhcnQgPSBnZXREb21haW5QYXJ0RnJvbVB1YmxpY0tleShwdWJsaWNLZXkpO1xuICAgIHJldHVybiBtYWNoaW5lSWQuc3BsaXQoXCIuXCIpLmF0KC0zKSA9PT0gZG9tYWluUGFydDtcbn1cblxuLy8gTk9URTogV2UgZG9uJ3QgaGF2ZSBhIGNhY2hlIHBlciBDQSwgYXMuLi4gdGhlIENBIHNob3VsZCBiZSBzZXQgZmlyc3Rcbi8vICBUT0RPOiBNYXliZSB0aHJvdyBpZiB0aGV5IHRyeSB0byBjaGFuZ2UgdGhlIENBIGFmdGVyIHRoZXkgZ2VuZXJhdGUgYW55IGNlcnRpZmljYXRlcz9cbi8vIFRPRE86IFJlZ2VuZXJhdGUgY2VydGlmaWNhdGVzIGFmdGVyIGVub3VnaCB0aW1lIChhcyB0aHJlYWQgY2VydHMgc2hvdWxkIGJlIHJlbGF0aXZlbHkgc2hvcnQgbGl2ZWQsXG4vLyAgc28gaXQgaXMgcGxhdXNpYmxlIGZvciB0aGVtIHRvIGV4cGlyZSlcbi8vICAtIFdlIHdpbGwgYWxzbyBuZWVkIHRvIHByb3ZpZGUgYSBjYWxsYmFjayBzbyB0aGF0IHVzZXJzIG9mIHRoZSBjZXJ0IGNhbiB1cGRhdGUgdGhlIGNlcnQgdGhleVxuLy8gICAgICBhcmUgdXNpbmcgYXMgd2VsbC5cbmV4cG9ydCBmdW5jdGlvbiBnZXRUaHJlYWRLZXlDZXJ0KGRvbWFpbjogc3RyaW5nKSB7XG4gICAgcmV0dXJuIGdldFRocmVhZEtleUNlcnRCYXNlKGRvbWFpbikoKTtcbn1cbmNvbnN0IGdldFRocmVhZEtleUNlcnRCYXNlID0gY2FjaGUoKGRvbWFpbjogc3RyaW5nKSA9PiBsYXp5KCgpID0+IHtcbiAgICBsZXQgY2EgPSBnZXRJZGVudGl0eUNBKGRvbWFpbik7XG4gICAgcmV0dXJuIGNyZWF0ZUNlcnRGcm9tQ0EoeyBDQUtleVBhaXI6IGNhIH0pO1xufSkpO1xuXG5leHBvcnQgY29uc3QgY3JlYXRlVGVzdEJyb3dzZXJLZXlDZXJ0ID0gbGF6eShhc3luYyAoKSA9PiB7XG4gICAgbGV0IGtleVBhaXIgPSBnZW5lcmF0ZVJTQUtleVBhaXIoKTtcbiAgICByZXR1cm4gYXdhaXQgY3JlYXRlWDUwOSh7IGRvbWFpbjogXCJ0ZXN0XCIsIGlzc3VlcjogXCJzZWxmXCIsIGtleVBhaXIsIGxpZmVTcGFuOiB0aW1lSW5EYXkgKiAzNjUgKiAyMCB9KTtcbn0pO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0T3duTm9kZUlkKCk6IHN0cmluZyB7XG4gICAgbGV0IG5vZGVJZCA9IFNvY2tldEZ1bmN0aW9uLm1vdW50ZWROb2RlSWQ7XG4gICAgaWYgKCFub2RlSWQpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBOb2RlIG11c3QgYmUgbW91bnRlZCBiZWZvcmUgbm9kZUlkIGlzIGFjY2Vzc2VkYCk7XG4gICAgfVxuICAgIHJldHVybiBub2RlSWQ7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRPd25Ob2RlSWRBbGxvd1VuZGVmaW5lZCgpIHtcbiAgICByZXR1cm4gU29ja2V0RnVuY3Rpb24ubW91bnRlZE5vZGVJZDtcbn0iXX0=
|
|
530
|
+
/* _JS_SOURCE_HASH = "be134c2204a9b9ba8ae9e00631d6a5b87b56d49723154c00ab8e24807b63c905"; */
|