sliftutils 1.7.127 → 1.7.129
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/bin/addmachine.js +9 -0
- package/bin/machineclient.js +9 -0
- package/bin/machineserver.js +10 -0
- package/index.d.ts +15 -0
- package/misc/dist/ownIPs.ts.cache +43 -0
- package/misc/dist/strings.ts.cache +2 -2
- package/misc/https/dist/certs.ts.cache +2 -2
- package/misc/https/dist/persistentLocalStorage.ts.cache +2 -2
- package/misc/ownIPs.d.ts +11 -0
- package/misc/ownIPs.ts +34 -0
- package/package.json +9 -3
- package/security/authorizedKeys/authorizedKeys.ts +34 -0
- package/security/authorizedKeys/daemon/authLog.ts +12 -5
- package/security/authorizedKeys/daemon/changes.ts +16 -1
- package/security/authorizedKeys/daemon/daemon.ts +50 -40
- package/security/authorizedKeys/daemon/dist/changes.ts.cache +40 -0
- package/security/authorizedKeys/daemon/dist/git.ts.cache +121 -0
- package/security/authorizedKeys/daemon/dist/notify.ts.cache +39 -0
- package/security/authorizedKeys/daemon/dist/paths.ts.cache +29 -0
- package/security/authorizedKeys/daemon/dist/repoFiles.ts.cache +114 -0
- package/security/authorizedKeys/daemon/dist/revocation.ts.cache +0 -0
- package/security/authorizedKeys/daemon/dist/sessions.ts.cache +147 -0
- package/security/authorizedKeys/daemon/dist/state.ts.cache +74 -0
- package/security/authorizedKeys/daemon/dist/trust.ts.cache +295 -0
- package/security/authorizedKeys/daemon/git.ts +3 -3
- package/security/authorizedKeys/daemon/notify.ts +8 -2
- package/security/authorizedKeys/daemon/repoFiles.ts +118 -0
- package/security/authorizedKeys/daemon/revocation.ts +0 -0
- package/security/authorizedKeys/daemon/rootKeys.ts +77 -10
- package/security/authorizedKeys/daemon/state.ts +25 -7
- package/security/authorizedKeys/daemon/trust.ts +37 -16
- package/security/authorizedKeys/daemon/userKeys.ts +5 -4
- package/security/authorizedKeys/dist/authorizedKeys.ts.cache +37 -3
- package/security/authorizedKeys/dist/revokeSource.ts.cache +17 -4
- package/security/authorizedKeys/dist/sources.ts.cache +52 -8
- package/security/authorizedKeys/dist/unrevoke.ts.cache +113 -17
- package/security/authorizedKeys/revokeSource.ts +12 -2
- package/security/authorizedKeys/secureSSH.ts +66 -12
- package/security/authorizedKeys/sources.ts +47 -5
- package/security/authorizedKeys/unrevoke.ts +176 -16
- package/security/helpers/dist/remoteSSH.ts.cache +19 -9
- package/security/machines/addMachine.ts +83 -0
- package/security/machines/dist/addMachine.ts.cache +88 -0
- package/security/machines/dist/identity.ts.cache +176 -0
- package/security/machines/dist/machines.ts.cache +322 -0
- package/security/machines/dist/trustClient.ts.cache +48 -0
- package/security/machines/dist/trustState.ts.cache +57 -0
- package/security/machines/identity.ts +199 -0
- package/security/machines/machines.ts +355 -0
- package/security/machines/trustClient.ts +51 -0
- package/security/machines/trustServer.ts +85 -0
- package/security/machines/trustState.ts +53 -0
- package/security/notifications/discord.ts +14 -5
- package/security/notifications/dist/discord.ts.cache +17 -8
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from "fs/promises";
|
|
2
2
|
import path from "path";
|
|
3
3
|
import { spawnPromise } from "../../helpers/spawn";
|
|
4
|
-
import { sourceKeyPath, sourceRepoPath } from "../sources";
|
|
4
|
+
import { findSourceKey, sourceKeyPath, sourceRepoPath } from "../sources";
|
|
5
5
|
import { GIT_TIMEOUT, MAX_ERROR_BODY_LENGTH } from "./paths";
|
|
6
6
|
import { sourceState } from "./state";
|
|
7
7
|
|
|
@@ -68,7 +68,7 @@ export async function currentBranch(config: { repoPath: string; keyPath: string
|
|
|
68
68
|
|
|
69
69
|
async function ensureSourceRepo(repoURL: string) {
|
|
70
70
|
let repoPath = sourceRepoPath(repoURL);
|
|
71
|
-
let keyPath = sourceKeyPath(repoURL);
|
|
71
|
+
let keyPath = await findSourceKey(repoURL) || sourceKeyPath(repoURL);
|
|
72
72
|
if (!await repoIsUsable({ repoPath, keyPath })) {
|
|
73
73
|
await cloneRepo({ repoURL, repoPath, keyPath });
|
|
74
74
|
}
|
|
@@ -82,7 +82,7 @@ async function ensureSourceRepo(repoURL: string) {
|
|
|
82
82
|
export async function syncRepo(repoURL: string) {
|
|
83
83
|
await ensureSourceRepo(repoURL);
|
|
84
84
|
let repoPath = sourceRepoPath(repoURL);
|
|
85
|
-
let keyPath = sourceKeyPath(repoURL);
|
|
85
|
+
let keyPath = await findSourceKey(repoURL) || sourceKeyPath(repoURL);
|
|
86
86
|
let branch = sourceState(repoURL).branch;
|
|
87
87
|
let localSha = (await runGit({ args: ["rev-parse", "HEAD"], cwd: repoPath, keyPath })).stdout.trim();
|
|
88
88
|
|
|
@@ -11,8 +11,14 @@ export function setHostLabel(value: string) {
|
|
|
11
11
|
// notification is only ever added when the user explicitly asks for that specific case. Startup,
|
|
12
12
|
// success, errors, retries and recoveries all belong in console.log instead. The complete list of
|
|
13
13
|
// cases that are allowed to notify is at the top of daemon.ts.
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
/** A headline and the rest.
|
|
15
|
+
|
|
16
|
+
The headline is all a phone shows. It gets the whole preview to itself: no product name, no
|
|
17
|
+
host, no timestamp in front of it, because a person glancing at their lock screen needs to read
|
|
18
|
+
what happened, not which machine said it or when. Those go at the end, where they are there for
|
|
19
|
+
whoever opens the message and out of the way of whoever does not. */
|
|
20
|
+
export async function notify(headline: string, body: string) {
|
|
21
|
+
let full = `**${headline}**\n\n${body}\n\n${hostLabelValue || os.hostname()}`;
|
|
16
22
|
// Logged before it is sent, and whether or not it arrives, so the journal is a complete record
|
|
17
23
|
// of what this machine had to say even when Discord is unreachable or the webhook is wrong.
|
|
18
24
|
console.log(`Discord: ${full}`);
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import fs from "fs/promises";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { deriveRevokeKey, findRevokeKey, REVOKE_KEY_LABEL, revokeKeyPath, revokeRepoPath, revokeRepoURL } from "../revokeSource";
|
|
4
|
+
import { findSourceKey, sourceKeyPath, sourceRepoPath } from "../sources";
|
|
5
|
+
import { cloneRepo, repoIsUsable, runGit } from "./git";
|
|
6
|
+
|
|
7
|
+
/** Reading a repo, made to look like reading a directory. A caller asks for a file and gets its
|
|
8
|
+
contents; keeping a checkout on disk, cloning it once and pulling it since, is this module's
|
|
9
|
+
business and nobody else's.
|
|
10
|
+
|
|
11
|
+
Nothing here ever answers "there is nothing" when the truth is "I could not look". A repo that
|
|
12
|
+
cannot be reached throws, and the caller skips that source, because reading an unreachable
|
|
13
|
+
revoke repo as empty would un-revoke every key in it. */
|
|
14
|
+
export type RepoRef = {
|
|
15
|
+
repoURL: string;
|
|
16
|
+
repoPath: string;
|
|
17
|
+
keyPath: () => Promise<string>;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export function sourceRepo(sourceURL: string): RepoRef {
|
|
21
|
+
return {
|
|
22
|
+
repoURL: sourceURL,
|
|
23
|
+
repoPath: sourceRepoPath(sourceURL),
|
|
24
|
+
keyPath: async () => await findSourceKey(sourceURL) || sourceKeyPath(sourceURL),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function revokeRepo(sourceURL: string): RepoRef {
|
|
29
|
+
return {
|
|
30
|
+
repoURL: revokeRepoURL(sourceURL),
|
|
31
|
+
repoPath: revokeRepoPath(sourceURL),
|
|
32
|
+
keyPath: () => ensureRevokeKey(sourceURL),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** The revoke repo's key is worked out from the source's, so nothing extra had to be uploaded and
|
|
37
|
+
nothing extra is stored anywhere it could be taken from. */
|
|
38
|
+
export async function ensureRevokeKey(sourceURL: string) {
|
|
39
|
+
let existing = await findRevokeKey(sourceURL);
|
|
40
|
+
if (existing) {
|
|
41
|
+
return existing;
|
|
42
|
+
}
|
|
43
|
+
let sourceKey = await findSourceKey(sourceURL);
|
|
44
|
+
if (!sourceKey) {
|
|
45
|
+
throw new Error(`Expected a key for ${sourceURL} at ${sourceKeyPath(sourceURL)}, no such file exists`);
|
|
46
|
+
}
|
|
47
|
+
let derived = deriveRevokeKey(await fs.readFile(sourceKey, "utf8"));
|
|
48
|
+
let keyPath = revokeKeyPath(sourceURL);
|
|
49
|
+
await fs.mkdir(path.dirname(keyPath), { recursive: true, mode: 0o700 });
|
|
50
|
+
await fs.writeFile(keyPath, derived.privateKeyFile, { mode: 0o600 });
|
|
51
|
+
await fs.writeFile(`${keyPath}.pub`, `${derived.publicKey} ${REVOKE_KEY_LABEL}\n`, { mode: 0o644 });
|
|
52
|
+
console.log(`Derived the revoke key for ${sourceURL} into ${keyPath}`);
|
|
53
|
+
return keyPath;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function pathExists(filePath: string) {
|
|
57
|
+
try {
|
|
58
|
+
await fs.access(filePath);
|
|
59
|
+
return true;
|
|
60
|
+
} catch (e) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Brings the checkout in line with the remote. Cloned the first time, pulled after that, and the
|
|
66
|
+
usual case of nothing having changed costs one ref listing and no objects. Throws if the repo
|
|
67
|
+
cannot be read, so a caller cannot mistake that for an empty repo. */
|
|
68
|
+
export async function syncRepoFiles(repo: RepoRef) {
|
|
69
|
+
let { repoURL, repoPath } = repo;
|
|
70
|
+
let keyPath = await repo.keyPath();
|
|
71
|
+
if (!await repoIsUsable({ repoPath, keyPath })) {
|
|
72
|
+
await cloneRepo({ repoURL, repoPath, keyPath });
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
let localHead = await runGit({ args: ["rev-parse", "HEAD"], cwd: repoPath, keyPath, allowFailure: true });
|
|
76
|
+
if (localHead.status !== 0) {
|
|
77
|
+
// A repo with no commits yet, which is the state every revoke repo starts in. There is
|
|
78
|
+
// nothing to pull and nothing to say.
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
let branch = (await runGit({ args: ["rev-parse", "--abbrev-ref", "HEAD"], cwd: repoPath, keyPath })).stdout.trim();
|
|
82
|
+
let listing = (await runGit({ args: ["ls-remote", "origin", branch], cwd: repoPath, keyPath })).stdout;
|
|
83
|
+
let remoteSha = (listing.split(/\s+/)[0] || "").trim();
|
|
84
|
+
if (remoteSha && remoteSha === localHead.stdout.trim()) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
await runGit({ args: ["fetch", "--prune", "origin"], cwd: repoPath, keyPath });
|
|
88
|
+
await runGit({ args: ["reset", "--hard", `origin/${branch}`], cwd: repoPath, keyPath });
|
|
89
|
+
await runGit({ args: ["clean", "-fdx"], cwd: repoPath, keyPath });
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Throws when there is no checkout at all. "I have never managed to read this repo" and "this
|
|
93
|
+
repo is empty" are different answers and must not collapse into one. */
|
|
94
|
+
async function requireCheckout(repo: RepoRef) {
|
|
95
|
+
if (!await pathExists(path.join(repo.repoPath, ".git"))) {
|
|
96
|
+
throw new Error(`Expected a checkout of ${repo.repoURL} at ${repo.repoPath}, there is none`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** One file out of the repo, or undefined if the repo does not have that file. */
|
|
101
|
+
export async function readRepoFile(repo: RepoRef, filePath: string) {
|
|
102
|
+
await requireCheckout(repo);
|
|
103
|
+
try {
|
|
104
|
+
return await fs.readFile(path.join(repo.repoPath, filePath), "utf8");
|
|
105
|
+
} catch (e) {
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** The names in one of the repo's directories, or nothing if it has no such directory. */
|
|
111
|
+
export async function listRepoDir(repo: RepoRef, directory: string) {
|
|
112
|
+
await requireCheckout(repo);
|
|
113
|
+
try {
|
|
114
|
+
return (await fs.readdir(path.join(repo.repoPath, directory))).sort();
|
|
115
|
+
} catch (e) {
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
}
|
|
Binary file
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from "fs/promises";
|
|
2
2
|
import path from "path";
|
|
3
|
-
import { keyFingerprint, keyRestriction, keyRestrictionList, NO_RESTRICTION, summarizeKey } from "../authorizedKeys";
|
|
3
|
+
import { keyFingerprint, keyNiceName, keyRestriction, keyRestrictionList, NO_RESTRICTION, summarizeKey } from "../authorizedKeys";
|
|
4
4
|
import { KEYS_HISTORY_PATH, ROOT_AUTHORIZED_KEYS } from "./paths";
|
|
5
5
|
import { notify } from "./notify";
|
|
6
6
|
import { getState, saveState } from "./state";
|
|
@@ -53,6 +53,64 @@ function describeAddressChange(config: { previousLine: string; currentLine: stri
|
|
|
53
53
|
return lines.join("\n");
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
/** The change in one line, for the notification preview. One key added, one removed, or one
|
|
57
|
+
address gained or lost, said plainly - that covers nearly every real change. Anything with more
|
|
58
|
+
than one part to it falls back to counting, because a line that tries to say four things says
|
|
59
|
+
none of them in the space a preview gives it. */
|
|
60
|
+
export function summarizeKeyDifference(config: { before: string[]; after: string[] }) {
|
|
61
|
+
let { before, after } = config;
|
|
62
|
+
let beforeByKey = new Map(before.map(key => [keyIdentity(key), key]));
|
|
63
|
+
let afterByKey = new Map(after.map(key => [keyIdentity(key), key]));
|
|
64
|
+
|
|
65
|
+
let added = [...afterByKey].filter(([identity]) => !beforeByKey.has(identity)).map(([, key]) => key);
|
|
66
|
+
let removed = [...beforeByKey].filter(([identity]) => !afterByKey.has(identity)).map(([, key]) => key);
|
|
67
|
+
let addressesAdded: { key: string; address: string }[] = [];
|
|
68
|
+
let addressesRemoved: { key: string; address: string }[] = [];
|
|
69
|
+
let otherwiseChanged: string[] = [];
|
|
70
|
+
for (let [identity, previousLine] of beforeByKey) {
|
|
71
|
+
let currentLine = afterByKey.get(identity);
|
|
72
|
+
if (!currentLine || currentLine === previousLine) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
let keyLine = currentLine;
|
|
76
|
+
let previous = keyRestrictionList(previousLine) || [];
|
|
77
|
+
let current = keyRestrictionList(keyLine) || [];
|
|
78
|
+
let gained = current.filter(address => !previous.includes(address));
|
|
79
|
+
let lost = previous.filter(address => !current.includes(address));
|
|
80
|
+
if (!gained.length && !lost.length) {
|
|
81
|
+
otherwiseChanged.push(keyLine);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
addressesAdded.push(...gained.map(address => ({ key: keyLine, address })));
|
|
85
|
+
addressesRemoved.push(...lost.map(address => ({ key: keyLine, address })));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
let changes = added.length + removed.length + addressesAdded.length + addressesRemoved.length
|
|
89
|
+
+ otherwiseChanged.length;
|
|
90
|
+
if (changes === 1) {
|
|
91
|
+
if (added.length) {
|
|
92
|
+
return `SSH KEY ADDED ${keyNiceName(added[0])}`;
|
|
93
|
+
}
|
|
94
|
+
if (removed.length) {
|
|
95
|
+
return `SSH KEY REMOVED ${keyNiceName(removed[0])}`;
|
|
96
|
+
}
|
|
97
|
+
if (addressesAdded.length) {
|
|
98
|
+
return `IP ${addressesAdded[0].address} ALLOWED ON ${keyNiceName(addressesAdded[0].key)}`;
|
|
99
|
+
}
|
|
100
|
+
if (addressesRemoved.length) {
|
|
101
|
+
return `IP ${addressesRemoved[0].address} REMOVED FROM ${keyNiceName(addressesRemoved[0].key)}`;
|
|
102
|
+
}
|
|
103
|
+
return `SSH KEY CHANGED ${keyNiceName(otherwiseChanged[0])}`;
|
|
104
|
+
}
|
|
105
|
+
let counted = [
|
|
106
|
+
added.length && `${added.length} key(s) added` || "",
|
|
107
|
+
removed.length && `${removed.length} key(s) removed` || "",
|
|
108
|
+
addressesAdded.length && `${addressesAdded.length} IP(s) allowed` || "",
|
|
109
|
+
addressesRemoved.length && `${addressesRemoved.length} IP(s) removed` || "",
|
|
110
|
+
].filter(part => part);
|
|
111
|
+
return `SSH KEYS CHANGED: ${counted.join(", ") || "no keys differ"}`;
|
|
112
|
+
}
|
|
113
|
+
|
|
56
114
|
export function describeKeyDifference(config: { before: string[]; after: string[] }) {
|
|
57
115
|
let { before, after } = config;
|
|
58
116
|
let beforeByKey = new Map(before.map(key => [keyIdentity(key), key]));
|
|
@@ -181,18 +239,27 @@ export async function enforceRootKeys(keys: string[]) {
|
|
|
181
239
|
state.appliedKeys = keys;
|
|
182
240
|
await saveState();
|
|
183
241
|
|
|
184
|
-
let reasons = takeChangeReasons();
|
|
185
|
-
|
|
242
|
+
let { leading, reasons } = takeChangeReasons();
|
|
243
|
+
// An attack names the message. Whoever is looking at a preview needs to see that, not that
|
|
244
|
+
// some keys changed - the change is what the attack caused.
|
|
245
|
+
let alert = leading.map(entry => entry.body).join("\n\n");
|
|
246
|
+
let why = [alert, ...reasons].filter(part => part).join("\n\n");
|
|
247
|
+
let before = weChangedIt && previouslyApplied || keys;
|
|
248
|
+
let after = weChangedIt && keys || currentKeys;
|
|
249
|
+
let headline = leading[0]?.headline || summarizeKeyDifference({ before, after });
|
|
250
|
+
|
|
186
251
|
if (weChangedIt) {
|
|
187
|
-
await notify(
|
|
188
|
-
`
|
|
189
|
-
+ `\n\`\`\`\n${describeKeyDifference({ before
|
|
252
|
+
await notify(headline,
|
|
253
|
+
`Who can log in as root:`
|
|
254
|
+
+ `\n\`\`\`\n${describeKeyDifference({ before, after })}\n\`\`\``
|
|
255
|
+
+ `${why && `\n${why}` || ""}`
|
|
190
256
|
);
|
|
191
257
|
return;
|
|
192
258
|
}
|
|
193
|
-
await notify(
|
|
194
|
-
`
|
|
195
|
-
+ `
|
|
196
|
-
+ `\n\`\`\`\n${describeKeyDifference({ before
|
|
259
|
+
await notify(leading[0]?.headline || `SSH KEYS EDITED BY SOMETHING ELSE`,
|
|
260
|
+
`Something other than portsecure edited root's authorized_keys. The edit below has been`
|
|
261
|
+
+ ` undone, and the keys from the repos are back in place.`
|
|
262
|
+
+ `\n\`\`\`\n${describeKeyDifference({ before, after })}\n\`\`\``
|
|
263
|
+
+ `${why && `\n${why}` || ""}`
|
|
197
264
|
);
|
|
198
265
|
}
|
|
@@ -17,14 +17,26 @@ export type SourceState = {
|
|
|
17
17
|
reportedProblem: string;
|
|
18
18
|
};
|
|
19
19
|
|
|
20
|
-
/**
|
|
21
|
-
the deploy key that writes revocations lives on every
|
|
22
|
-
otherwise delete the revocation that locked them out.
|
|
20
|
+
/** One revocation event: this key, used from this address, so it is no longer accepted. Kept even
|
|
21
|
+
when the revoke repo no longer lists it: the deploy key that writes revocations lives on every
|
|
22
|
+
server, so an attacker who took one could otherwise delete the revocation that locked them out.
|
|
23
|
+
|
|
24
|
+
The id is unique to the event, never derived from the key. A key that has been forgiven for one
|
|
25
|
+
address is still an ordinary key, and using it from some other address revokes it again, with a
|
|
26
|
+
new id that no existing unrevoke names. */
|
|
23
27
|
export type RevocationState = {
|
|
24
|
-
fingerprint: string;
|
|
25
28
|
revocationId: string;
|
|
26
|
-
|
|
27
|
-
|
|
29
|
+
fingerprint: string;
|
|
30
|
+
// The address it was used from. Half of what a revocation says, and the half an unrevoke has
|
|
31
|
+
// to name to undo it.
|
|
32
|
+
ip: string;
|
|
33
|
+
// Why this key was revoked, carried from the revocation file so a machine that reads one
|
|
34
|
+
// written elsewhere can say what happened rather than only that something did.
|
|
35
|
+
revokedAt: string;
|
|
36
|
+
revokedBy: string;
|
|
37
|
+
reason: string;
|
|
38
|
+
// Which unrevoke allowed this key from this address, once one has been seen. When the wait it
|
|
39
|
+
// started runs out is in unrevokeFirstSeen, which outlives the daemon.
|
|
28
40
|
unrevokeId: string;
|
|
29
41
|
unrevoked: boolean;
|
|
30
42
|
// Whether we have said that this key actually left root's authorized_keys. The removal is the
|
|
@@ -50,7 +62,12 @@ export type DaemonState = {
|
|
|
50
62
|
// revocations is on every server, so whoever stole one could otherwise erase the record that
|
|
51
63
|
// shut them out. Restarting the daemon does clear it, which is the way back from a revocation
|
|
52
64
|
// that should not have happened.
|
|
53
|
-
revocations: { [
|
|
65
|
+
revocations: { [revocationId: string]: RevocationState };
|
|
66
|
+
// When this machine first saw each unrevoke, by unrevoke id. On disk, unlike the revocations
|
|
67
|
+
// themselves, because it is what the hour long wait is counted from: measuring from a time the
|
|
68
|
+
// unrevoke file states would let whoever wrote it backdate the file and skip the wait, and
|
|
69
|
+
// measuring from process start would restart the wait on every restart.
|
|
70
|
+
unrevokeFirstSeen: { [unrevokeId: string]: number };
|
|
54
71
|
pendingRevocations: PendingRevocation[];
|
|
55
72
|
// The keys we last wrote out. What tells a change we made apart from one somebody else made:
|
|
56
73
|
// if this still matches what we want, and the file does not, the file was edited behind us.
|
|
@@ -64,6 +81,7 @@ let state: DaemonState = {
|
|
|
64
81
|
sources: {},
|
|
65
82
|
userKeyHashes: {},
|
|
66
83
|
revocations: {},
|
|
84
|
+
unrevokeFirstSeen: {},
|
|
67
85
|
pendingRevocations: [],
|
|
68
86
|
appliedKeys: [],
|
|
69
87
|
authLogOffset: 0,
|
|
@@ -174,21 +174,31 @@ async function verifyCheckoutSigner(config: {
|
|
|
174
174
|
return publicKey;
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
+
/** Proves a checkout on disk was signed, and that the files in it are the ones that were signed.
|
|
178
|
+
Returns the signer, and throws if anything about that does not hold.
|
|
179
|
+
|
|
180
|
+
Anything reading a repo for something other than ssh keys goes through this first: the whole
|
|
181
|
+
checkout is covered by one manifest, so a machine list is exactly as trustworthy as the keys
|
|
182
|
+
beside it, and neither is worth reading unsigned. */
|
|
183
|
+
export async function verifyCheckout(repoPath: string) {
|
|
184
|
+
return await verifyCheckoutSigner({ repoPath, files: await readSignatureFiles(repoPath) });
|
|
185
|
+
}
|
|
186
|
+
|
|
177
187
|
export function describeSigner(signer: string) {
|
|
178
188
|
return signer === UNSIGNED && "<no public key>" || signer;
|
|
179
189
|
}
|
|
180
190
|
|
|
181
191
|
/** Reports a problem with a source's signature, but only when it is not the same problem we
|
|
182
192
|
already reported, so a fault that persists does not repeat every check. */
|
|
183
|
-
async function reportProblem(config: { repoURL: string; problem: string;
|
|
184
|
-
let { repoURL, problem,
|
|
193
|
+
async function reportProblem(config: { repoURL: string; problem: string; headline: string; body: string }) {
|
|
194
|
+
let { repoURL, problem, headline, body } = config;
|
|
185
195
|
let sourceStateValue = sourceState(repoURL);
|
|
186
196
|
if (sourceStateValue.reportedProblem === problem) {
|
|
187
197
|
return;
|
|
188
198
|
}
|
|
189
199
|
sourceStateValue.reportedProblem = problem;
|
|
190
200
|
await saveState();
|
|
191
|
-
await notify(
|
|
201
|
+
await notify(headline, body);
|
|
192
202
|
}
|
|
193
203
|
|
|
194
204
|
/** The keys a source is allowed to contribute right now. A source signed by someone we have not
|
|
@@ -210,17 +220,24 @@ export async function resolveSourceKeys(repoURL: string) {
|
|
|
210
220
|
await reportProblem({
|
|
211
221
|
repoURL,
|
|
212
222
|
problem: "stale",
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
+
|
|
223
|
+
headline: `KEY REPO CHANGED WITHOUT BEING SIGNED`,
|
|
224
|
+
body: `\`${repoURL}\` changed, but nobody signed the change, so it is being ignored`
|
|
225
|
+
+ ` and this machine is still using the keys signed by`
|
|
226
|
+
+ ` \`${describeSigner(sourceStateValue.acceptedSigner)}\`.`
|
|
227
|
+
+ `\n\nTo make the change take effect, run this in that repo:`
|
|
228
|
+
+ `\n\`\`\`\n${SIGN_COMMAND}\n\`\`\``,
|
|
216
229
|
});
|
|
217
230
|
} else {
|
|
218
231
|
await reportProblem({
|
|
219
232
|
repoURL,
|
|
220
233
|
problem: "corrupt",
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
+
|
|
234
|
+
headline: `KEY REPO SIGNATURE IS BROKEN`,
|
|
235
|
+
body: `The signature on \`${repoURL}\` does not check out, so everything in it is`
|
|
236
|
+
+ ` being ignored and this machine is still using the keys signed by`
|
|
237
|
+
+ ` \`${describeSigner(sourceStateValue.acceptedSigner)}\`.`
|
|
238
|
+
+ `\n\n${e}`
|
|
239
|
+
+ `\n\nTo replace the signature, run this in that repo:`
|
|
240
|
+
+ `\n\`\`\`\n${SIGN_COMMAND}\n\`\`\``,
|
|
224
241
|
});
|
|
225
242
|
}
|
|
226
243
|
return sourceStateValue.acceptedKeys;
|
|
@@ -259,9 +276,10 @@ export async function resolveSourceKeys(repoURL: string) {
|
|
|
259
276
|
|
|
260
277
|
// Going from nothing to something signed is only ever an improvement, so it does not wait.
|
|
261
278
|
if (sourceStateValue.acceptedSigner === UNSIGNED) {
|
|
262
|
-
await notify(
|
|
263
|
-
`\`${repoURL}\`
|
|
264
|
-
+ ` being applied right away.`
|
|
279
|
+
await notify(`KEY REPO IS NOW SIGNED`,
|
|
280
|
+
`\`${repoURL}\` was not signed at all before, so this is an improvement and its keys`
|
|
281
|
+
+ ` are being applied right away.`
|
|
282
|
+
+ `\n\nsigned by: \`${signer}\``
|
|
265
283
|
);
|
|
266
284
|
return await accept();
|
|
267
285
|
}
|
|
@@ -273,10 +291,13 @@ export async function resolveSourceKeys(repoURL: string) {
|
|
|
273
291
|
sourceStateValue.pendingSigner = signer;
|
|
274
292
|
sourceStateValue.pendingSince = Date.now();
|
|
275
293
|
await saveState();
|
|
276
|
-
await notify(
|
|
277
|
-
|
|
278
|
-
+ `
|
|
279
|
-
+ `
|
|
294
|
+
await notify(`KEY REPO SIGNED BY A DIFFERENT KEY`,
|
|
295
|
+
`Somebody else is signing \`${repoURL}\` now. Its keys are NOT being applied, and this`
|
|
296
|
+
+ ` machine is still using the ones it already had. If nothing changes, the new signer`
|
|
297
|
+
+ ` is accepted in 24 hours.`
|
|
298
|
+
+ `\n\nIf that was not you, fix the repo before the 24 hours are up.`
|
|
299
|
+
+ `\n\nsigned by now: \`${describeSigner(signer)}\``
|
|
300
|
+
+ `\nsigned by before: \`${describeSigner(sourceStateValue.acceptedSigner)}\``
|
|
280
301
|
);
|
|
281
302
|
return sourceStateValue.acceptedKeys;
|
|
282
303
|
}
|
|
@@ -65,10 +65,11 @@ export async function checkOtherUserKeys() {
|
|
|
65
65
|
if (previousHash === undefined || previousHash === hash) {
|
|
66
66
|
continue;
|
|
67
67
|
}
|
|
68
|
-
await notify(
|
|
69
|
-
`
|
|
70
|
-
+ `
|
|
71
|
-
+ `\n\`\`\`\n${keys.map(summarizeKey).join("\n") || "(now empty)"}\n\`\`\``
|
|
68
|
+
await notify(`SSH KEYS CHANGED FOR USER ${entry.name}`,
|
|
69
|
+
`Somebody changed who can log in as \`${entry.name}\`. portsecure does not manage that`
|
|
70
|
+
+ ` account, so the change was left alone. Who can log in as \`${entry.name}\` now:`
|
|
71
|
+
+ `\n\`\`\`\n${keys.map(summarizeKey).join("\n") || "(nobody, the file is now empty)"}\n\`\`\``
|
|
72
|
+
+ `\n${entry.filePath}`
|
|
72
73
|
);
|
|
73
74
|
}
|
|
74
75
|
state.userKeyHashes = hashes;
|
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule && mod.default) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true , configurable: true});
|
|
6
|
-
//exports.readRepoKeys = exports.findKeyProblems = exports.summarizeKey = exports.keyRestriction = exports.keyRestrictionList = exports.NO_RESTRICTION = exports.keyFingerprint = exports.normalizeKeys = void 0;
|
|
6
|
+
//exports.readRepoKeys = exports.findKeyProblems = exports.keyNiceName = exports.summarizeKey = exports.allowAddresses = exports.keyRestriction = exports.keyRestrictionList = exports.NO_RESTRICTION = exports.keyFingerprint = exports.normalizeKeys = void 0;
|
|
7
7
|
const crypto_1 = __importDefault(require("crypto"));
|
|
8
8
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
9
9
|
const path_1 = __importDefault(require("path"));
|
|
@@ -44,6 +44,27 @@ function keyRestriction(keyLine) {
|
|
|
44
44
|
return list.join(",");
|
|
45
45
|
}
|
|
46
46
|
exports.keyRestriction = keyRestriction;
|
|
47
|
+
/** Adds addresses to a key's from= list, leaving the rest of the line exactly as it was. Returns
|
|
48
|
+
which addresses were actually new, so a caller can report only what it changed.
|
|
49
|
+
|
|
50
|
+
A key with no from= at all is left alone: adding one would silently restrict a key that is
|
|
51
|
+
currently unrestricted, which is a different decision than the one being made here. */
|
|
52
|
+
function allowAddresses(keyLine, addresses) {
|
|
53
|
+
let list = keyRestrictionList(keyLine);
|
|
54
|
+
if (!list) {
|
|
55
|
+
return { keyLine, added: [] };
|
|
56
|
+
}
|
|
57
|
+
let allowed = list;
|
|
58
|
+
let added = addresses.filter(address => address && !allowed.includes(address));
|
|
59
|
+
if (!added.length) {
|
|
60
|
+
return { keyLine, added };
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
keyLine: keyLine.replace(/from="[^"]*"/, `from="${[...allowed, ...added].join(",")}"`),
|
|
64
|
+
added,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
exports.allowAddresses = allowAddresses;
|
|
47
68
|
/** Enough to recognise whose key this is without printing the whole blob. */
|
|
48
69
|
function summarizeKey(keyLine) {
|
|
49
70
|
let parts = keyLine.trim().split(/\s+/);
|
|
@@ -57,6 +78,19 @@ function summarizeKey(keyLine) {
|
|
|
57
78
|
return `${type} ...${blob.slice(-12)}${comment && ` ${comment}` || ""}`;
|
|
58
79
|
}
|
|
59
80
|
exports.summarizeKey = summarizeKey;
|
|
81
|
+
/** The shortest thing that still identifies a key to a person: the comment it carries, which is
|
|
82
|
+
usually user@machine, and otherwise the tail of the key itself. For the first line of a
|
|
83
|
+
notification, where there is only room for the one thing that matters. */
|
|
84
|
+
function keyNiceName(keyLine) {
|
|
85
|
+
let parts = keyLine.trim().split(/\s+/);
|
|
86
|
+
let typeIndex = parts.findIndex(part => /^(ssh-|ecdsa-|sk-)/.test(part));
|
|
87
|
+
if (typeIndex < 0) {
|
|
88
|
+
return keyLine.slice(0, 20);
|
|
89
|
+
}
|
|
90
|
+
let comment = parts.slice(typeIndex + 2).join(" ");
|
|
91
|
+
return comment || `...${(parts[typeIndex + 1] || "").slice(-8)}`;
|
|
92
|
+
}
|
|
93
|
+
exports.keyNiceName = keyNiceName;
|
|
60
94
|
/** What a set of keys has to satisfy before it is worth signing, as a list of complaints.
|
|
61
95
|
|
|
62
96
|
Every key needs a from=, because an unrestricted key can never be caught being used from the
|
|
@@ -107,5 +141,5 @@ async function readRepoKeys(repoPath) {
|
|
|
107
141
|
return keys;
|
|
108
142
|
}
|
|
109
143
|
exports.readRepoKeys = readRepoKeys;
|
|
110
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXV0aG9yaXplZEtleXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJhdXRob3JpemVkS2V5cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUFBQSxvREFBNEI7QUFDNUIsMkRBQTZCO0FBQzdCLGdEQUF3QjtBQUV4QixTQUFnQixhQUFhLENBQUMsUUFBZ0I7SUFDMUMsT0FBTyxRQUFRLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLElBQUksSUFBSSxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztBQUN2RyxDQUFDO0FBRkQsc0NBRUM7QUFFRDs4RUFDOEU7QUFDOUUsU0FBZ0IsY0FBYyxDQUFDLE9BQWU7SUFDMUMsSUFBSSxLQUFLLEdBQUcsT0FBTyxDQUFDLElBQUksRUFBRSxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUN4QyxJQUFJLFNBQVMsR0FBRyxLQUFLLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsb0JBQW9CLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7SUFDekUsSUFBSSxJQUFJLEdBQUcsU0FBUyxJQUFJLENBQUMsSUFBSSxLQUFLLENBQUMsU0FBUyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUN4RCxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDUixPQUFPLEVBQUUsQ0FBQztJQUNkLENBQUM7SUFDRCxPQUFPLFNBQVMsR0FBRyxnQkFBTSxDQUFDLFVBQVUsQ0FBQyxRQUFRLENBQUMsQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsUUFBUSxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLENBQUMsT0FB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QUFDO1FBQ25CLE1BQU0sSUFBSSxLQUFLLENBQUMseURBQXlELFFBQVEsaUJBQWlCLENBQUMsQ0FBQztJQUN4RyxDQUFDO0lBQ0QsSUFBSSxJQUFJLEdBQWEsRUFBRSxDQUFDO0lBQ3hCLEtBQUssSUFBSSxJQUFJLElBQUksUUFBUSxFQUFFLENBQUM7UUFDeEIsSUFBSSxDQUFDLElBQUksQ0FBQyxHQUFHLGFBQWEsQ0FBQyxNQUFNLGtCQUFFLENBQUMsUUFBUSxDQUFDLGNBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLElBQUksQ0FBQyxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUN0RixDQUFDO0lBQ0QsT0FBTyxJQUFJLENBQUM7QUFDaEIsQ0FBQztBQWZELG9DQWVDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IGNyeXB0byBmcm9tIFwiY3J5cHRvXCI7XG5pbXBvcnQgZnMgZnJvbSBcImZzL3Byb21pc2VzXCI7XG5pbXBvcnQgcGF0aCBmcm9tIFwicGF0aFwiO1xuXG5leHBvcnQgZnVuY3Rpb24gbm9ybWFsaXplS2V5cyhjb250ZW50czogc3RyaW5nKSB7XG4gICAgcmV0dXJuIGNvbnRlbnRzLnNwbGl0KFwiXFxuXCIpLm1hcChsaW5lID0+IGxpbmUudHJpbSgpKS5maWx0ZXIobGluZSA9PiBsaW5lICYmICFsaW5lLnN0YXJ0c1dpdGgoXCIjXCIpKTtcbn1cblxuLyoqIFRoZSBmaW5nZXJwcmludCBzc2ggaXRzZWxmIHJlcG9ydHMgZm9yIGEga2V5LCB3aGljaCBpcyB3aGF0IHRoZSBzc2hkIGxvZyBuYW1lcyBhbmQgdGhlcmVmb3JlXG4gICAgd2hhdCBhIHJldm9jYXRpb24gaXMga2V5ZWQgYnkuIFJldHVybnMgXCJcIiBmb3IgYSBsaW5lIHRoYXQgaG9sZHMgbm8ga2V5LiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGtleUZpbmdlcnByaW50KGtleUxpbmU6IHN0cmluZykge1xuICAgIGxldCBwYXJ0cyA9IGtleUxpbmUudHJpbSgpLnNwbGl0KC9cXHMrLyk7XG4gICAgbGV0IHR5cGVJbmRleCA9IHBhcnRzLmZpbmRJbmRleChwYXJ0ID0+IC9eKHNzaC18ZWNkc2EtfHNrLSkvLnRlc3QocGFydCkpO1xuICAgIGxldCBibG9iID0gdHlwZUluZGV4ID49IDAgJiYgcGFydHNbdHlwZUluZGV4ICsgMV0gfHwgXCJcIjtcbiAgICBpZiAoIWJsb2IpIHtcbiAgICAgICAgcmV0dXJuIFwiXCI7XG4gICAgfVxuICAgIHJldHVybiBcIlNIQTI1NjpcIiArIGNyeXB0by5jcmVhdGVIYXNoKFwic2hhMjU2XCIpLnVwZGF0ZShCdWZmZXIuZnJvbShibG9iLCBcImJhc2U2NFwiKSkuZGlnZXN0KFwiYmFzZTY0XCIpLnJlcGxhY2UoLz0rJC8sIFwiXCIpO1xufVxuXG5leHBvcnQgY29uc3QgTk9fUkVTVFJJQ1RJT04gPSBcIkFOWSBBRERSRVNTIChubyBmcm9tPSByZXN0cmljdGlvbilcIjtcblxuLyoqIFRoZSBhZGRyZXNzZXMgYSBrZXkgbWF5IGJlIHVzZWQgZnJvbSwgb25lIGJ5IG9uZSwgb3IgdW5kZWZpbmVkIHdoZW4gdGhlIGtleSBjYXJyaWVzIG5vIGZyb209XG4gICAgYXQgYWxsLiBVbmRlZmluZWQgYW5kIGFuIGVtcHR5IGxpc3QgYXJlIHZlcnkgZGlmZmVyZW50IHRoaW5ncywgc28gdGhleSBzdGF5IGRpc3Rpbmd1aXNoYWJsZS4gKi9cbmV4cG9ydCBmdW5jdGlvbiBrZXlSZXN0cmljdGlvbkxpc3Qoa2V5TGluZTogc3RyaW5nKSB7XG4gICAgbGV0IG1hdGNoID0ga2V5TGluZS5tYXRjaCgvZnJvbT1cIihbXlwiXSopXCIvKTtcbiAgICBpZiAoIW1hdGNoKSB7XG4gICAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgfVxuICAgIHJldHVybiBtYXRjaFsxXS5zcGxpdChcIixcIikubWFwKGVudHJ5ID0+IGVudHJ5LnRyaW0oKSkuZmlsdGVyKGVudHJ5ID0+IGVudHJ5KTtcbn1cblxuLyoqIFRoZSBhZGRyZXNzZXMgYSBrZXkgbWF5IGJlIHVzZWQgZnJvbSwgd2hpY2ggaXMgdGhlIHBhcnQgb2YgYW4gYXV0aG9yaXplZF9rZXlzIGxpbmUgdGhhdFxuICAgIGRlY2lkZXMgaG93IG11Y2ggYSBzdG9sZW4ga2V5IGlzIHdvcnRoLiBBIGtleSB3aXRoIG5vIHJlc3RyaWN0aW9uIHNheXMgc28gbG91ZGx5LiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGtleVJlc3RyaWN0aW9uKGtleUxpbmU6IHN0cmluZykge1xuICAgIGxldCBsaXN0ID0ga2V5UmVzdHJpY3Rpb25MaXN0KGtleUxpbmUpO1xuICAgIGlmICghbGlzdCkge1xuICAgICAgICByZXR1cm4gTk9fUkVTVFJJQ1RJT047XG4gICAgfVxuICAgIHJldHVybiBsaXN0LmpvaW4oXCIsXCIpO1xufVxuXG4vKiogRW5vdWdoIHRvIHJlY29nbmlzZSB3aG9zZSBrZXkgdGhpcyBpcyB3aXRob3V0IHByaW50aW5nIHRoZSB3aG9sZSBibG9iLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHN1bW1hcml6ZUtleShrZXlMaW5lOiBzdHJpbmcpIHtcbiAgICBsZXQgcGFydHMgPSBrZXlMaW5lLnRyaW0oKS5zcGxpdCgvXFxzKy8pO1xuICAgIGxldCB0eXBlSW5kZXggPSBwYXJ0cy5maW5kSW5kZXgocGFydCA9PiAvXihzc2gtfGVjZHNhLXxzay0pLy50ZXN0KHBhcnQpKTtcbiAgICBpZiAodHlwZUluZGV4IDwgMCkge1xuICAgICAgICByZXR1cm4ga2V5TGluZS5zbGljZSgwLCA2MCk7XG4gICAgfVxuICAgIGxldCB0eXBlID0gcGFydHNbdHlwZUluZGV4XTtcbiAgICBsZXQgYmxvYiA9IHBhcnRzW3R5cGVJbmRleCArIDFdIHx8IFwiXCI7XG4gICAgbGV0IGNvbW1lbnQgPSBwYXJ0cy5zbGljZSh0eXBlSW5kZXggKyAyKS5qb2luKFwiIFwiKTtcbiAgICByZXR1cm4gYCR7dHlwZX0gLi4uJHtibG9iLnNsaWNlKC0xMil9JHtjb21tZW50ICYmIGAgJHtjb21tZW50fWAgfHwgXCJcIn1gO1xufVxuXG4vKiogV2hhdCBhIHNldCBvZiBrZXlzIGhhcyB0byBzYXRpc2Z5IGJlZm9yZSBpdCBpcyB3b3J0aCBzaWduaW5nLCBhcyBhIGxpc3Qgb2YgY29tcGxhaW50cy5cblxuICAgIEV2ZXJ5IGtleSBuZWVkcyBhIGZyb209LCBiZWNhdXNlIGFuIHVucmVzdHJpY3RlZCBrZXkgY2FuIG5ldmVyIGJlIGNhdWdodCBiZWluZyB1c2VkIGZyb20gdGhlXG4gICAgd3JvbmcgcGxhY2UsIGFuZCBiZWluZyBjYXVnaHQgaXMgdGhlIG9ubHkgdGhpbmcgdGhhdCB0cmlnZ2VycyBhIHJldm9jYXRpb24uXG5cbiAgICBObyB0d28ga2V5cyBtYXkgYWxsb3cgdGhlIHNhbWUgYWRkcmVzc2VzLCBiZWNhdXNlIHRoYXQgaXMgb25lIHBlcnNvbiBob2xkaW5nIHR3byBrZXlzOiByZXZva2luZ1xuICAgIG9uZSBvZiB0aGVtIGxlYXZlcyB0aGUgb3RoZXIgd29ya2luZywgc28gdGhlIHJldm9jYXRpb24gYWNoaWV2ZXMgbm90aGluZy4gKi9cbmV4cG9ydCBmdW5jdGlvbiBmaW5kS2V5UHJvYmxlbXMoa2V5czogc3RyaW5nW10pIHtcbiAgICBsZXQgcHJvYmxlbXM6IHN0cmluZ1tdID0gW107XG4gICAgbGV0IGJ5UmVzdHJpY3Rpb24gPSBuZXcgTWFwPHN0cmluZywgc3RyaW5nW10+KCk7XG4gICAgZm9yIChsZXQga2V5IG9mIGtleXMpIHtcbiAgICAgICAgbGV0IHJlc3RyaWN0aW9uID0ga2V5UmVzdHJpY3Rpb25MaXN0KGtleSk7XG4gICAgICAgIGlmICghcmVzdHJpY3Rpb24pIHtcbiAgICAgICAgICAgIHByb2JsZW1zLnB1c2goYG5vIGZyb209IHJlc3RyaWN0aW9uLCBzbyBpdCBjYW4gYmUgdXNlZCBmcm9tIGFueXdoZXJlOlxcbiAgICAgICR7c3VtbWFyaXplS2V5KGtleSl9YCk7XG4gICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgfVxuICAgICAgICAvLyBTb3J0ZWQsIHNvIHRoZSBzYW1lIGFkZHJlc3NlcyB3cml0dGVuIGluIGEgZGlmZmVyZW50IG9yZGVyIHN0aWxsIGNvdW50IGFzIHRoZSBzYW1lLlxuICAgICAgICBsZXQgaWRlbnRpdHkgPSBbLi4ucmVzdHJpY3Rpb25dLnNvcnQoKS5qb2luKFwiLFwiKTtcbiAgICAgICAgYnlSZXN0cmljdGlvbi5zZXQoaWRlbnRpdHksIFsuLi4oYnlSZXN0cmljdGlvbi5nZXQoaWRlbnRpdHkpIHx8IFtdKSwga2V5XSk7XG4gICAgfVxuICAgIGZvciAobGV0IFtyZXN0cmljdGlvbiwgc2hhcmluZ10gb2YgYnlSZXN0cmljdGlvbikge1xuICAgICAgICBpZiAoc2hhcmluZy5sZW5ndGggPCAyKSB7XG4gICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgfVxuICAgICAgICBwcm9ibGVtcy5wdXNoKFxuICAgICAgICAgICAgYCR7c2hhcmluZy5sZW5ndGh9IGtleXMgYWxsb3cgZXhhY3RseSB0aGUgc2FtZSBhZGRyZXNzZXMgKCR7cmVzdHJpY3Rpb259KSwgd2hpY2ggaXMgb25lYFxuICAgICAgICAgICAgKyBgIHBlcnNvbiBob2xkaW5nIG1vcmUgdGhhbiBvbmUga2V5OlxcbmBcbiAgICAgICAgICAgICsgc2hhcmluZy5tYXAoa2V5ID0+IGAgICAgICAke3N1bW1hcml6ZUtleShrZXkpfWApLmpvaW4oXCJcXG5cIilcbiAgICAgICAgKTtcbiAgICB9XG4gICAgcmV0dXJuIHByb2JsZW1zO1xufVxuXG4vKiogUmVhZHMgdGhlIGF1dGhvcml6ZWQga2V5cyBhIHJlcG8gY2hlY2tvdXQgd2FudHMgYXBwbGllZC4gUHJlZmVycyBhIHRvcCBsZXZlbCBhdXRob3JpemVkX2tleXNcbiAgICBmaWxlIGFuZCBvdGhlcndpc2UgY29uY2F0ZW5hdGVzIGV2ZXJ5IC5wdWIgYXQgdGhlIHRvcCBsZXZlbC4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiByZWFkUmVwb0tleXMocmVwb1BhdGg6IHN0cmluZykge1xuICAgIGxldCBjb21iaW5lZFBhdGggPSBwYXRoLmpvaW4ocmVwb1BhdGgsIFwiYXV0aG9yaXplZF9rZXlzXCIpO1xuICAgIGxldCBlbnRyaWVzID0gYXdhaXQgZnMucmVhZGRpcihyZXBvUGF0aCk7XG4gICAgaWYgKGVudHJpZXMuaW5jbHVkZXMoXCJhdXRob3JpemVkX2tleXNcIikpIHtcbiAgICAgICAgcmV0dXJuIG5vcm1hbGl6ZUtleXMoYXdhaXQgZnMucmVhZEZpbGUoY29tYmluZWRQYXRoLCBcInV0ZjhcIikpO1xuICAgIH1cbiAgICBsZXQgcHViRmlsZXMgPSBlbnRyaWVzLmZpbHRlcihuYW1lID0+IG5hbWUuZW5kc1dpdGgoXCIucHViXCIpKS5zb3J0KCk7XG4gICAgaWYgKCFwdWJGaWxlcy5sZW5ndGgpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBFeHBlY3RlZCBhdXRob3JpemVkX2tleXMgb3IgYXQgbGVhc3Qgb25lIC5wdWIgZmlsZSBpbiAke3JlcG9QYXRofSwgZm91bmQgbmVpdGhlcmApO1xuICAgIH1cbiAgICBsZXQga2V5czogc3RyaW5nW10gPSBbXTtcbiAgICBmb3IgKGxldCBuYW1lIG9mIHB1YkZpbGVzKSB7XG4gICAgICAgIGtleXMucHVzaCguLi5ub3JtYWxpemVLZXlzKGF3YWl0IGZzLnJlYWRGaWxlKHBhdGguam9pbihyZXBvUGF0aCwgbmFtZSksIFwidXRmOFwiKSkpO1xuICAgIH1cbiAgICByZXR1cm4ga2V5cztcbn1cbiJdfQ==
|
|
111
|
-
/* _JS_SOURCE_HASH = "
|
|
144
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXV0aG9yaXplZEtleXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJhdXRob3JpemVkS2V5cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUFBQSxvREFBNEI7QUFDNUIsMkRBQTZCO0FBQzdCLGdEQUF3QjtBQUV4QixTQUFnQixhQUFhLENBQUMsUUFBZ0I7SUFDMUMsT0FBTyxRQUFRLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLElBQUksSUFBSSxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztBQUN2RyxDQUFDO0FBRkQsc0NBRUM7QUFFRDs4RUFDOEU7QUFDOUUsU0FBZ0IsY0FBYyxDQUFDLE9BQWU7SUFDMUMsSUFBSSxLQUFLLEdBQUcsT0FBTyxDQUFDLElBQUksRUFBRSxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUN4QyxJQUFJLFNBQVMsR0FBRyxLQUFLLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsb0JBQW9CLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7SUFDekUsSUFBSSxJQUFJLEdBQUcsU0FBUyxJQUFJLENBQUMsSUFBSSxLQUFLLENBQUMsU0FBUyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUN4RCxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDUixPQUFPLEVBQUUsQ0FBQztJQUNkLENBQUM7SUFDRCxPQUFPLFNBQVMsR0FBRyxnQkFBTSxDQUFDLFVBQVUsQ0FBQyxRQUFRLENBQUMsQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsUUFBUSxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLENBQUMsT0FBTyxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsQ0FBQztBQUMzSCxDQUFDO0FBUkQsd0NBUUM7QUFFWSxRQUFBLGNBQWMsR0FBRyxvQ0FBb0MsQ0FBQztBQUVuRTttR0FDbUc7QUFDbkcsU0FBZ0Isa0JBQWtCLENBQUMsT0FBZTtJQUM5QyxJQUFJLEtBQUssR0FBRyxPQUFPLENBQUMsS0FBSyxDQUFDLGdCQUFnQixDQUFDLENBQUM7SUFDNUMsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDO1FBQ1QsT0FBTyxTQUFTLENBQUM7SUFDckIsQ0FBQztJQUNELE9BQU8sS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQyxLQUFLLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQyxLQUFLLENBQUMsQ0FBQztBQUNqRixDQUFDO0FBTkQsZ0RBTUM7QUFFRDt3RkFDd0Y7QUFDeEYsU0FBZ0IsY0FBYyxDQUFDLE9BQWU7SUFDMUMsSUFBSSxJQUFJLEdBQUcsa0JBQWtCLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDdkMsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDO1FBQ1IsT0FBTyxzQkFBYyxDQUFDO0lBQzFCLENBQUM7SUFDRCxPQUFPLElBQUksQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUM7QUFDMUIsQ0FBQztBQU5ELHdDQU1DO0FBRUQ7Ozs7MEZBSTBGO0FBQzFGLFNBQWdCLGNBQWMsQ0FBQyxPQUFlLEVBQUUsU0FBbUI7SUFDL0QsSUFBSSxJQUFJLEdBQUcsa0JBQWtCLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDdkMsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDO1FBQ1IsT0FBTyxFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsRUFBYyxFQUFFLENBQUM7SUFDOUMsQ0FBQztJQUNELElBQUksT0FBTyxHQUFHLElBQUksQ0FBQztJQUNuQixJQUFJLEtBQUssR0FBRyxTQUFTLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUMsT0FBTyxJQUFJLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDO0lBQy9FLElBQUksQ0FBQyxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUM7UUFDaEIsT0FBTyxFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsQ0FBQztJQUM5QixDQUFDO0lBQ0QsT0FBTztRQUNILE9BQU8sRUFBRSxPQUFPLENBQUMsT0FBTyxDQUFDLGNBQWMsRUFBRSxTQUFTLENBQUMsR0FBRyxPQUFPLEVBQUUsR0FBRyxLQUFLLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQztRQUN0RixLQUFLO0tBQ1IsQ0FBQztBQUNOLENBQUM7QUFkRCx3Q0FjQztBQUVELDZFQUE2RTtBQUM3RSxTQUFnQixZQUFZLENBQUMsT0FBZTtJQUN4QyxJQUFJLEtBQUssR0FBRyxPQUFPLENBQUMsSUFBSSxFQUFFLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQ3hDLElBQUksU0FBUyxHQUFHLEtBQUssQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxvQkFBb0IsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQztJQUN6RSxJQUFJLFNBQVMsR0FBRyxDQUFDLEVBQUUsQ0FBQztRQUNoQixPQUFPLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0lBQ2hDLENBQUM7SUFDRCxJQUFJLElBQUksR0FBRyxLQUFLLENBQUMsU0FBUyxDQUFDLENBQUM7SUFDNUIsSUFBSSxJQUFJLEdBQUcsS0FBSyxDQUFDLFNBQVMsR0FBRyxDQUFDLENBQUMsSUFBSSxFQUFFLENBQUM7SUFDdEMsSUFBSSxPQUFPLEdBQUcsS0FBSyxDQUFDLEtBQUssQ0FBQyxTQUFTLEdBQUcsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQ25ELE9BQU8sR0FBRyxJQUFJLE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsQ0FBQyxHQUFHLE9BQU8sSUFBSSxJQUFJLE9BQU8sRUFBRSxJQUFJLEVBQUUsRUFBRSxDQUFDO0FBQzVFLENBQUM7QUFWRCxvQ0FVQztBQUVEOzs2RUFFNkU7QUFDN0UsU0FBZ0IsV0FBVyxDQUFDLE9BQWU7SUFDdkMsSUFBSSxLQUFLLEdBQUcsT0FBTyxDQUFDLElBQUksRUFBRSxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUN4QyxJQUFJLFNBQVMsR0FBRyxLQUFLLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsb0JBQW9CLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7SUFDekUsSUFBSSxTQUFTLEdBQUcsQ0FBQyxFQUFFLENBQUM7UUFDaEIsT0FBTyxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQztJQUNoQyxDQUFDO0lBQ0QsSUFBSSxPQUFPLEdBQUcsS0FBSyxDQUFDLEtBQUssQ0FBQyxTQUFTLEdBQUcsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQ25ELE9BQU8sT0FBTyxJQUFJLE1BQU0sQ0FBQyxLQUFLLENBQUMsU0FBUyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7QUFDckUsQ0FBQztBQVJELGtDQVFDO0FBRUQ7Ozs7OztnRkFNZ0Y7QUFDaEYsU0FBZ0IsZUFBZSxDQUFDLElBQWM7SUFDMUMsSUFBSSxRQUFRLEdBQWEsRUFBRSxDQUFDO0lBQzVCLElBQUksYUFBYSxHQUFHLElBQUksR0FBRyxFQUFvQixDQUFDO0lBQ2hELEtBQUssSUFBSSxHQUFHLElBQUksSUFBSSxFQUFFLENBQUM7UUFDbkIsSUFBSSxXQUFXLEdBQUcsa0JBQWtCLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDMUMsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDO1lBQ2YsUUFBUSxDQUFDLElBQUksQ0FBQyxpRUFBaUUsWUFBWSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsQ0FBQztZQUNwRyxTQUFTO1FBQ2IsQ0FBQztRQUNELHNGQUFzRjtRQUN0RixJQUFJLFFBQVEsR0FBRyxDQUFDLEdBQUcsV0FBVyxDQUFDLENBQUMsSUFBSSxFQUFFLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQ2pELGFBQWEsQ0FBQyxHQUFHLENBQUMsUUFBUSxFQUFFLENBQUMsR0FBRyxDQUFDLGFBQWEsQ0FBQyxHQUFHLENBQUMsUUFBUSxDQUFDLElBQUksRUFBRSxDQUFDLEVBQUUsR0FBRyxDQUFDLENBQUMsQ0FBQztJQUMvRSxDQUFDO0lBQ0QsS0FBSyxJQUFJLENBQUMsV0FBVyxFQUFFLE9BQU8sQ0FBQyxJQUFJLGFBQWEsRUFBRSxDQUFDO1FBQy9DLElBQUksT0FBTyxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUNyQixTQUFTO1FBQ2IsQ0FBQztRQUNELFFBQVEsQ0FBQyxJQUFJLENBQ1QsR0FBRyxPQUFPLENBQUMsTUFBTSwyQ0FBMkMsV0FBVyxpQkFBaUI7Y0FDdEYsc0NBQXNDO2NBQ3RDLE9BQU8sQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxTQUFTLFlBQVksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUNoRSxDQUFDO0lBQ04sQ0FBQztJQUNELE9BQU8sUUFBUSxDQUFDO0FBQ3BCLENBQUM7QUF4QkQsMENBd0JDO0FBRUQ7bUVBQ21FO0FBQzVELEtBQUssVUFBVSxZQUFZLENBQUMsUUFBZ0I7SUFDL0MsSUFBSSxZQUFZLEdBQUcsY0FBSSxDQUFDLElBQUksQ0FBQyxRQUFRLEVBQUUsaUJBQWlCLENBQUMsQ0FBQztJQUMxRCxJQUFJLE9BQU8sR0FBRyxNQUFNLGtCQUFFLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBQ3pDLElBQUksT0FBTyxDQUFDLFFBQVEsQ0FBQyxpQkFBaUIsQ0FBQyxFQUFFLENBQUM7UUFDdEMsT0FBTyxhQUFhLENBQUMsTUFBTSxrQkFBRSxDQUFDLFFBQVEsQ0FBQyxZQUFZLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQztJQUNsRSxDQUFDO0lBQ0QsSUFBSSxRQUFRLEdBQUcsT0FBTyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUNwRSxJQUFJLENBQUMsUUFBUSxDQUFDLE1BQU0sRUFBRSxDQUFDO1FBQ25CLE1BQU0sSUFBSSxLQUFLLENBQUMseURBQXlELFFBQVEsaUJBQWlCLENBQUMsQ0FBQztJQUN4RyxDQUFDO0lBQ0QsSUFBSSxJQUFJLEdBQWEsRUFBRSxDQUFDO0lBQ3hCLEtBQUssSUFBSSxJQUFJLElBQUksUUFBUSxFQUFFLENBQUM7UUFDeEIsSUFBSSxDQUFDLElBQUksQ0FBQyxHQUFHLGFBQWEsQ0FBQyxNQUFNLGtCQUFFLENBQUMsUUFBUSxDQUFDLGNBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLElBQUksQ0FBQyxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUN0RixDQUFDO0lBQ0QsT0FBTyxJQUFJLENBQUM7QUFDaEIsQ0FBQztBQWZELG9DQWVDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IGNyeXB0byBmcm9tIFwiY3J5cHRvXCI7XG5pbXBvcnQgZnMgZnJvbSBcImZzL3Byb21pc2VzXCI7XG5pbXBvcnQgcGF0aCBmcm9tIFwicGF0aFwiO1xuXG5leHBvcnQgZnVuY3Rpb24gbm9ybWFsaXplS2V5cyhjb250ZW50czogc3RyaW5nKSB7XG4gICAgcmV0dXJuIGNvbnRlbnRzLnNwbGl0KFwiXFxuXCIpLm1hcChsaW5lID0+IGxpbmUudHJpbSgpKS5maWx0ZXIobGluZSA9PiBsaW5lICYmICFsaW5lLnN0YXJ0c1dpdGgoXCIjXCIpKTtcbn1cblxuLyoqIFRoZSBmaW5nZXJwcmludCBzc2ggaXRzZWxmIHJlcG9ydHMgZm9yIGEga2V5LCB3aGljaCBpcyB3aGF0IHRoZSBzc2hkIGxvZyBuYW1lcyBhbmQgdGhlcmVmb3JlXG4gICAgd2hhdCBhIHJldm9jYXRpb24gaXMga2V5ZWQgYnkuIFJldHVybnMgXCJcIiBmb3IgYSBsaW5lIHRoYXQgaG9sZHMgbm8ga2V5LiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGtleUZpbmdlcnByaW50KGtleUxpbmU6IHN0cmluZykge1xuICAgIGxldCBwYXJ0cyA9IGtleUxpbmUudHJpbSgpLnNwbGl0KC9cXHMrLyk7XG4gICAgbGV0IHR5cGVJbmRleCA9IHBhcnRzLmZpbmRJbmRleChwYXJ0ID0+IC9eKHNzaC18ZWNkc2EtfHNrLSkvLnRlc3QocGFydCkpO1xuICAgIGxldCBibG9iID0gdHlwZUluZGV4ID49IDAgJiYgcGFydHNbdHlwZUluZGV4ICsgMV0gfHwgXCJcIjtcbiAgICBpZiAoIWJsb2IpIHtcbiAgICAgICAgcmV0dXJuIFwiXCI7XG4gICAgfVxuICAgIHJldHVybiBcIlNIQTI1NjpcIiArIGNyeXB0by5jcmVhdGVIYXNoKFwic2hhMjU2XCIpLnVwZGF0ZShCdWZmZXIuZnJvbShibG9iLCBcImJhc2U2NFwiKSkuZGlnZXN0KFwiYmFzZTY0XCIpLnJlcGxhY2UoLz0rJC8sIFwiXCIpO1xufVxuXG5leHBvcnQgY29uc3QgTk9fUkVTVFJJQ1RJT04gPSBcIkFOWSBBRERSRVNTIChubyBmcm9tPSByZXN0cmljdGlvbilcIjtcblxuLyoqIFRoZSBhZGRyZXNzZXMgYSBrZXkgbWF5IGJlIHVzZWQgZnJvbSwgb25lIGJ5IG9uZSwgb3IgdW5kZWZpbmVkIHdoZW4gdGhlIGtleSBjYXJyaWVzIG5vIGZyb209XG4gICAgYXQgYWxsLiBVbmRlZmluZWQgYW5kIGFuIGVtcHR5IGxpc3QgYXJlIHZlcnkgZGlmZmVyZW50IHRoaW5ncywgc28gdGhleSBzdGF5IGRpc3Rpbmd1aXNoYWJsZS4gKi9cbmV4cG9ydCBmdW5jdGlvbiBrZXlSZXN0cmljdGlvbkxpc3Qoa2V5TGluZTogc3RyaW5nKSB7XG4gICAgbGV0IG1hdGNoID0ga2V5TGluZS5tYXRjaCgvZnJvbT1cIihbXlwiXSopXCIvKTtcbiAgICBpZiAoIW1hdGNoKSB7XG4gICAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgfVxuICAgIHJldHVybiBtYXRjaFsxXS5zcGxpdChcIixcIikubWFwKGVudHJ5ID0+IGVudHJ5LnRyaW0oKSkuZmlsdGVyKGVudHJ5ID0+IGVudHJ5KTtcbn1cblxuLyoqIFRoZSBhZGRyZXNzZXMgYSBrZXkgbWF5IGJlIHVzZWQgZnJvbSwgd2hpY2ggaXMgdGhlIHBhcnQgb2YgYW4gYXV0aG9yaXplZF9rZXlzIGxpbmUgdGhhdFxuICAgIGRlY2lkZXMgaG93IG11Y2ggYSBzdG9sZW4ga2V5IGlzIHdvcnRoLiBBIGtleSB3aXRoIG5vIHJlc3RyaWN0aW9uIHNheXMgc28gbG91ZGx5LiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGtleVJlc3RyaWN0aW9uKGtleUxpbmU6IHN0cmluZykge1xuICAgIGxldCBsaXN0ID0ga2V5UmVzdHJpY3Rpb25MaXN0KGtleUxpbmUpO1xuICAgIGlmICghbGlzdCkge1xuICAgICAgICByZXR1cm4gTk9fUkVTVFJJQ1RJT047XG4gICAgfVxuICAgIHJldHVybiBsaXN0LmpvaW4oXCIsXCIpO1xufVxuXG4vKiogQWRkcyBhZGRyZXNzZXMgdG8gYSBrZXkncyBmcm9tPSBsaXN0LCBsZWF2aW5nIHRoZSByZXN0IG9mIHRoZSBsaW5lIGV4YWN0bHkgYXMgaXQgd2FzLiBSZXR1cm5zXG4gICAgd2hpY2ggYWRkcmVzc2VzIHdlcmUgYWN0dWFsbHkgbmV3LCBzbyBhIGNhbGxlciBjYW4gcmVwb3J0IG9ubHkgd2hhdCBpdCBjaGFuZ2VkLlxuXG4gICAgQSBrZXkgd2l0aCBubyBmcm9tPSBhdCBhbGwgaXMgbGVmdCBhbG9uZTogYWRkaW5nIG9uZSB3b3VsZCBzaWxlbnRseSByZXN0cmljdCBhIGtleSB0aGF0IGlzXG4gICAgY3VycmVudGx5IHVucmVzdHJpY3RlZCwgd2hpY2ggaXMgYSBkaWZmZXJlbnQgZGVjaXNpb24gdGhhbiB0aGUgb25lIGJlaW5nIG1hZGUgaGVyZS4gKi9cbmV4cG9ydCBmdW5jdGlvbiBhbGxvd0FkZHJlc3NlcyhrZXlMaW5lOiBzdHJpbmcsIGFkZHJlc3Nlczogc3RyaW5nW10pIHtcbiAgICBsZXQgbGlzdCA9IGtleVJlc3RyaWN0aW9uTGlzdChrZXlMaW5lKTtcbiAgICBpZiAoIWxpc3QpIHtcbiAgICAgICAgcmV0dXJuIHsga2V5TGluZSwgYWRkZWQ6IFtdIGFzIHN0cmluZ1tdIH07XG4gICAgfVxuICAgIGxldCBhbGxvd2VkID0gbGlzdDtcbiAgICBsZXQgYWRkZWQgPSBhZGRyZXNzZXMuZmlsdGVyKGFkZHJlc3MgPT4gYWRkcmVzcyAmJiAhYWxsb3dlZC5pbmNsdWRlcyhhZGRyZXNzKSk7XG4gICAgaWYgKCFhZGRlZC5sZW5ndGgpIHtcbiAgICAgICAgcmV0dXJuIHsga2V5TGluZSwgYWRkZWQgfTtcbiAgICB9XG4gICAgcmV0dXJuIHtcbiAgICAgICAga2V5TGluZToga2V5TGluZS5yZXBsYWNlKC9mcm9tPVwiW15cIl0qXCIvLCBgZnJvbT1cIiR7Wy4uLmFsbG93ZWQsIC4uLmFkZGVkXS5qb2luKFwiLFwiKX1cImApLFxuICAgICAgICBhZGRlZCxcbiAgICB9O1xufVxuXG4vKiogRW5vdWdoIHRvIHJlY29nbmlzZSB3aG9zZSBrZXkgdGhpcyBpcyB3aXRob3V0IHByaW50aW5nIHRoZSB3aG9sZSBibG9iLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHN1bW1hcml6ZUtleShrZXlMaW5lOiBzdHJpbmcpIHtcbiAgICBsZXQgcGFydHMgPSBrZXlMaW5lLnRyaW0oKS5zcGxpdCgvXFxzKy8pO1xuICAgIGxldCB0eXBlSW5kZXggPSBwYXJ0cy5maW5kSW5kZXgocGFydCA9PiAvXihzc2gtfGVjZHNhLXxzay0pLy50ZXN0KHBhcnQpKTtcbiAgICBpZiAodHlwZUluZGV4IDwgMCkge1xuICAgICAgICByZXR1cm4ga2V5TGluZS5zbGljZSgwLCA2MCk7XG4gICAgfVxuICAgIGxldCB0eXBlID0gcGFydHNbdHlwZUluZGV4XTtcbiAgICBsZXQgYmxvYiA9IHBhcnRzW3R5cGVJbmRleCArIDFdIHx8IFwiXCI7XG4gICAgbGV0IGNvbW1lbnQgPSBwYXJ0cy5zbGljZSh0eXBlSW5kZXggKyAyKS5qb2luKFwiIFwiKTtcbiAgICByZXR1cm4gYCR7dHlwZX0gLi4uJHtibG9iLnNsaWNlKC0xMil9JHtjb21tZW50ICYmIGAgJHtjb21tZW50fWAgfHwgXCJcIn1gO1xufVxuXG4vKiogVGhlIHNob3J0ZXN0IHRoaW5nIHRoYXQgc3RpbGwgaWRlbnRpZmllcyBhIGtleSB0byBhIHBlcnNvbjogdGhlIGNvbW1lbnQgaXQgY2Fycmllcywgd2hpY2ggaXNcbiAgICB1c3VhbGx5IHVzZXJAbWFjaGluZSwgYW5kIG90aGVyd2lzZSB0aGUgdGFpbCBvZiB0aGUga2V5IGl0c2VsZi4gRm9yIHRoZSBmaXJzdCBsaW5lIG9mIGFcbiAgICBub3RpZmljYXRpb24sIHdoZXJlIHRoZXJlIGlzIG9ubHkgcm9vbSBmb3IgdGhlIG9uZSB0aGluZyB0aGF0IG1hdHRlcnMuICovXG5leHBvcnQgZnVuY3Rpb24ga2V5TmljZU5hbWUoa2V5TGluZTogc3RyaW5nKSB7XG4gICAgbGV0IHBhcnRzID0ga2V5TGluZS50cmltKCkuc3BsaXQoL1xccysvKTtcbiAgICBsZXQgdHlwZUluZGV4ID0gcGFydHMuZmluZEluZGV4KHBhcnQgPT4gL14oc3NoLXxlY2RzYS18c2stKS8udGVzdChwYXJ0KSk7XG4gICAgaWYgKHR5cGVJbmRleCA8IDApIHtcbiAgICAgICAgcmV0dXJuIGtleUxpbmUuc2xpY2UoMCwgMjApO1xuICAgIH1cbiAgICBsZXQgY29tbWVudCA9IHBhcnRzLnNsaWNlKHR5cGVJbmRleCArIDIpLmpvaW4oXCIgXCIpO1xuICAgIHJldHVybiBjb21tZW50IHx8IGAuLi4keyhwYXJ0c1t0eXBlSW5kZXggKyAxXSB8fCBcIlwiKS5zbGljZSgtOCl9YDtcbn1cblxuLyoqIFdoYXQgYSBzZXQgb2Yga2V5cyBoYXMgdG8gc2F0aXNmeSBiZWZvcmUgaXQgaXMgd29ydGggc2lnbmluZywgYXMgYSBsaXN0IG9mIGNvbXBsYWludHMuXG5cbiAgICBFdmVyeSBrZXkgbmVlZHMgYSBmcm9tPSwgYmVjYXVzZSBhbiB1bnJlc3RyaWN0ZWQga2V5IGNhbiBuZXZlciBiZSBjYXVnaHQgYmVpbmcgdXNlZCBmcm9tIHRoZVxuICAgIHdyb25nIHBsYWNlLCBhbmQgYmVpbmcgY2F1Z2h0IGlzIHRoZSBvbmx5IHRoaW5nIHRoYXQgdHJpZ2dlcnMgYSByZXZvY2F0aW9uLlxuXG4gICAgTm8gdHdvIGtleXMgbWF5IGFsbG93IHRoZSBzYW1lIGFkZHJlc3NlcywgYmVjYXVzZSB0aGF0IGlzIG9uZSBwZXJzb24gaG9sZGluZyB0d28ga2V5czogcmV2b2tpbmdcbiAgICBvbmUgb2YgdGhlbSBsZWF2ZXMgdGhlIG90aGVyIHdvcmtpbmcsIHNvIHRoZSByZXZvY2F0aW9uIGFjaGlldmVzIG5vdGhpbmcuICovXG5leHBvcnQgZnVuY3Rpb24gZmluZEtleVByb2JsZW1zKGtleXM6IHN0cmluZ1tdKSB7XG4gICAgbGV0IHByb2JsZW1zOiBzdHJpbmdbXSA9IFtdO1xuICAgIGxldCBieVJlc3RyaWN0aW9uID0gbmV3IE1hcDxzdHJpbmcsIHN0cmluZ1tdPigpO1xuICAgIGZvciAobGV0IGtleSBvZiBrZXlzKSB7XG4gICAgICAgIGxldCByZXN0cmljdGlvbiA9IGtleVJlc3RyaWN0aW9uTGlzdChrZXkpO1xuICAgICAgICBpZiAoIXJlc3RyaWN0aW9uKSB7XG4gICAgICAgICAgICBwcm9ibGVtcy5wdXNoKGBubyBmcm9tPSByZXN0cmljdGlvbiwgc28gaXQgY2FuIGJlIHVzZWQgZnJvbSBhbnl3aGVyZTpcXG4gICAgICAke3N1bW1hcml6ZUtleShrZXkpfWApO1xuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cbiAgICAgICAgLy8gU29ydGVkLCBzbyB0aGUgc2FtZSBhZGRyZXNzZXMgd3JpdHRlbiBpbiBhIGRpZmZlcmVudCBvcmRlciBzdGlsbCBjb3VudCBhcyB0aGUgc2FtZS5cbiAgICAgICAgbGV0IGlkZW50aXR5ID0gWy4uLnJlc3RyaWN0aW9uXS5zb3J0KCkuam9pbihcIixcIik7XG4gICAgICAgIGJ5UmVzdHJpY3Rpb24uc2V0KGlkZW50aXR5LCBbLi4uKGJ5UmVzdHJpY3Rpb24uZ2V0KGlkZW50aXR5KSB8fCBbXSksIGtleV0pO1xuICAgIH1cbiAgICBmb3IgKGxldCBbcmVzdHJpY3Rpb24sIHNoYXJpbmddIG9mIGJ5UmVzdHJpY3Rpb24pIHtcbiAgICAgICAgaWYgKHNoYXJpbmcubGVuZ3RoIDwgMikge1xuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cbiAgICAgICAgcHJvYmxlbXMucHVzaChcbiAgICAgICAgICAgIGAke3NoYXJpbmcubGVuZ3RofSBrZXlzIGFsbG93IGV4YWN0bHkgdGhlIHNhbWUgYWRkcmVzc2VzICgke3Jlc3RyaWN0aW9ufSksIHdoaWNoIGlzIG9uZWBcbiAgICAgICAgICAgICsgYCBwZXJzb24gaG9sZGluZyBtb3JlIHRoYW4gb25lIGtleTpcXG5gXG4gICAgICAgICAgICArIHNoYXJpbmcubWFwKGtleSA9PiBgICAgICAgJHtzdW1tYXJpemVLZXkoa2V5KX1gKS5qb2luKFwiXFxuXCIpXG4gICAgICAgICk7XG4gICAgfVxuICAgIHJldHVybiBwcm9ibGVtcztcbn1cblxuLyoqIFJlYWRzIHRoZSBhdXRob3JpemVkIGtleXMgYSByZXBvIGNoZWNrb3V0IHdhbnRzIGFwcGxpZWQuIFByZWZlcnMgYSB0b3AgbGV2ZWwgYXV0aG9yaXplZF9rZXlzXG4gICAgZmlsZSBhbmQgb3RoZXJ3aXNlIGNvbmNhdGVuYXRlcyBldmVyeSAucHViIGF0IHRoZSB0b3AgbGV2ZWwuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcmVhZFJlcG9LZXlzKHJlcG9QYXRoOiBzdHJpbmcpIHtcbiAgICBsZXQgY29tYmluZWRQYXRoID0gcGF0aC5qb2luKHJlcG9QYXRoLCBcImF1dGhvcml6ZWRfa2V5c1wiKTtcbiAgICBsZXQgZW50cmllcyA9IGF3YWl0IGZzLnJlYWRkaXIocmVwb1BhdGgpO1xuICAgIGlmIChlbnRyaWVzLmluY2x1ZGVzKFwiYXV0aG9yaXplZF9rZXlzXCIpKSB7XG4gICAgICAgIHJldHVybiBub3JtYWxpemVLZXlzKGF3YWl0IGZzLnJlYWRGaWxlKGNvbWJpbmVkUGF0aCwgXCJ1dGY4XCIpKTtcbiAgICB9XG4gICAgbGV0IHB1YkZpbGVzID0gZW50cmllcy5maWx0ZXIobmFtZSA9PiBuYW1lLmVuZHNXaXRoKFwiLnB1YlwiKSkuc29ydCgpO1xuICAgIGlmICghcHViRmlsZXMubGVuZ3RoKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgRXhwZWN0ZWQgYXV0aG9yaXplZF9rZXlzIG9yIGF0IGxlYXN0IG9uZSAucHViIGZpbGUgaW4gJHtyZXBvUGF0aH0sIGZvdW5kIG5laXRoZXJgKTtcbiAgICB9XG4gICAgbGV0IGtleXM6IHN0cmluZ1tdID0gW107XG4gICAgZm9yIChsZXQgbmFtZSBvZiBwdWJGaWxlcykge1xuICAgICAgICBrZXlzLnB1c2goLi4ubm9ybWFsaXplS2V5cyhhd2FpdCBmcy5yZWFkRmlsZShwYXRoLmpvaW4ocmVwb1BhdGgsIG5hbWUpLCBcInV0ZjhcIikpKTtcbiAgICB9XG4gICAgcmV0dXJuIGtleXM7XG59XG4iXX0=
|
|
145
|
+
/* _JS_SOURCE_HASH = "5dc60c0f88ff3878003bf57017d1342326afa7c6a9f511d5896e4113565a7823"; */
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule && mod.default) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true , configurable: true});
|
|
3
|
-
//exports.deriveRevokeKey = exports.revokeRepoPath = exports.revokeKeyPath = exports.revokeRepoURL = exports.REVOKE_KEY_LABEL = void 0;
|
|
6
|
+
//exports.deriveRevokeKey = exports.revokeRepoPath = exports.findRevokeKey = exports.legacyRevokeKeyPath = exports.revokeKeyPath = exports.revokeRepoURL = exports.REVOKE_KEY_LABEL = void 0;
|
|
4
7
|
const deriveKey_1 = require("../keys/deriveKey");
|
|
5
8
|
const sshKeyFile_1 = require("../keys/sshKeyFile");
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
6
10
|
const sources_1 = require("./sources");
|
|
7
11
|
// A source's revocations live in a repo beside it. Derived rather than configured, so adding a
|
|
8
12
|
// source brings its revoke repo with it and there is nothing extra to pass on the command line.
|
|
@@ -16,9 +20,18 @@ function revokeRepoURL(sourceURL) {
|
|
|
16
20
|
}
|
|
17
21
|
exports.revokeRepoURL = revokeRepoURL;
|
|
18
22
|
function revokeKeyPath(sourceURL) {
|
|
19
|
-
return
|
|
23
|
+
return path_1.default.join((0, sources_1.keysDir)(), `${(0, sources_1.sourceName)(sourceURL)}${REVOKED_SUFFIX}`);
|
|
20
24
|
}
|
|
21
25
|
exports.revokeKeyPath = revokeKeyPath;
|
|
26
|
+
function legacyRevokeKeyPath(sourceURL) {
|
|
27
|
+
return `${sources_1.LEGACY_REPO_KEYS_DIR}/${(0, sources_1.sourceName)(sourceURL)}${REVOKED_SUFFIX}`;
|
|
28
|
+
}
|
|
29
|
+
exports.legacyRevokeKeyPath = legacyRevokeKeyPath;
|
|
30
|
+
/** Wherever this source's revoke key already is, or nothing if it has not been derived yet. */
|
|
31
|
+
async function findRevokeKey(sourceURL) {
|
|
32
|
+
return await (0, sources_1.findKey)({ current: revokeKeyPath(sourceURL), legacy: legacyRevokeKeyPath(sourceURL) });
|
|
33
|
+
}
|
|
34
|
+
exports.findRevokeKey = findRevokeKey;
|
|
22
35
|
function revokeRepoPath(sourceURL) {
|
|
23
36
|
return `${sources_1.REPOS_DIR}/${(0, sources_1.sourceName)(sourceURL)}${REVOKED_SUFFIX}`;
|
|
24
37
|
}
|
|
@@ -40,5 +53,5 @@ function deriveRevokeKey(sourcePrivateKey) {
|
|
|
40
53
|
};
|
|
41
54
|
}
|
|
42
55
|
exports.deriveRevokeKey = deriveRevokeKey;
|
|
43
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
44
|
-
/* _JS_SOURCE_HASH = "
|
|
56
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmV2b2tlU291cmNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsicmV2b2tlU291cmNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7OztBQUFBLGlEQUFxRDtBQUNyRCxtREFBcUY7QUFDckYsZ0RBQXdCO0FBQ3hCLHVDQUEwRjtBQUUxRiwrRkFBK0Y7QUFDL0YsZ0dBQWdHO0FBQ2hHLE1BQU0sY0FBYyxHQUFHLFVBQVUsQ0FBQztBQUNsQywrRkFBK0Y7QUFDL0Ysa0VBQWtFO0FBQ3JELFFBQUEsZ0JBQWdCLEdBQUcsaUJBQWlCLENBQUM7QUFFbEQsU0FBZ0IsYUFBYSxDQUFDLFNBQWlCO0lBQzNDLElBQUksVUFBVSxHQUFHLFNBQVMsQ0FBQyxPQUFPLENBQUMsUUFBUSxFQUFFLEVBQUUsQ0FBQyxDQUFDO0lBQ2pELE9BQU8sR0FBRyxVQUFVLEdBQUcsY0FBYyxNQUFNLENBQUM7QUFDaEQsQ0FBQztBQUhELHNDQUdDO0FBRUQsU0FBZ0IsYUFBYSxDQUFDLFNBQWlCO0lBQzNDLE9BQU8sY0FBSSxDQUFDLElBQUksQ0FBQyxJQUFBLGlCQUFPLEdBQUUsRUFBRSxHQUFHLElBQUEsb0JBQVUsRUFBQyxTQUFTLENBQUMsR0FBRyxjQUFjLEVBQUUsQ0FBQyxDQUFDO0FBQzdFLENBQUM7QUFGRCxzQ0FFQztBQUVELFNBQWdCLG1CQUFtQixDQUFDLFNBQWlCO0lBQ2pELE9BQU8sR0FBRyw4QkFBb0IsSUFBSSxJQUFBLG9CQUFVLEVBQUMsU0FBUyxDQUFDLEdBQUcsY0FBYyxFQUFFLENBQUM7QUFDL0UsQ0FBQztBQUZELGtEQUVDO0FBRUQsK0ZBQStGO0FBQ3hGLEtBQUssVUFBVSxhQUFhLENBQUMsU0FBaUI7SUFDakQsT0FBTyxNQUFNLElBQUEsaUJBQU8sRUFBQyxFQUFFLE9BQU8sRUFBRSxhQUFhLENBQUMsU0FBUyxDQUFDLEVBQUUsTUFBTSxFQUFFLG1CQUFtQixDQUFDLFNBQVMsQ0FBQyxFQUFFLENBQUMsQ0FBQztBQUN4RyxDQUFDO0FBRkQsc0NBRUM7QUFFRCxTQUFnQixjQUFjLENBQUMsU0FBaUI7SUFDNUMsT0FBTyxHQUFHLG1CQUFTLElBQUksSUFBQSxvQkFBVSxFQUFDLFNBQVMsQ0FBQyxHQUFHLGNBQWMsRUFBRSxDQUFDO0FBQ3BFLENBQUM7QUFGRCx3Q0FFQztBQUVEO3lGQUN5RjtBQUN6RixTQUFnQixlQUFlLENBQUMsZ0JBQXdCO0lBQ3BELElBQUksTUFBTSxHQUFHLElBQUEsbUNBQXNCLEVBQUMsZ0JBQWdCLENBQUMsQ0FBQztJQUN0RCxJQUFJLE9BQU8sR0FBRyxJQUFBLDRCQUFnQixFQUFDLEVBQUUsSUFBSSxFQUFFLE1BQU0sQ0FBQyxJQUFJLEVBQUUsS0FBSyxFQUFFLHdCQUFnQixFQUFFLENBQUMsQ0FBQztJQUMvRSxPQUFPO1FBQ0gsU0FBUyxFQUFFLGVBQWUsTUFBTSxDQUFDLE1BQU0sQ0FBQztZQUNwQyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsRUFBRSxNQUFNLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQztZQUN0RCxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsRUFBRSxPQUFPLENBQUMsU0FBUztTQUNoRCxDQUFDLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxFQUFFO1FBQ3ZCLGNBQWMsRUFBRSxJQUFBLG9DQUF1QixFQUFDO1lBQ3BDLElBQUksRUFBRSxPQUFPLENBQUMsSUFBSTtZQUNsQixPQUFPLEVBQUUsR0FBRyxNQUFNLENBQUMsT0FBTyxJQUFJLFlBQVksSUFBSSx3QkFBZ0IsRUFBRTtTQUNuRSxDQUFDO0tBQ0wsQ0FBQztBQUNOLENBQUM7QUFiRCwwQ0FhQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGRlcml2ZUVkMjU1MTlLZXkgfSBmcm9tIFwiLi4va2V5cy9kZXJpdmVLZXlcIjtcbmltcG9ydCB7IGZvcm1hdE9wZW5TU0hQcml2YXRlS2V5LCBwYXJzZU9wZW5TU0hQcml2YXRlS2V5IH0gZnJvbSBcIi4uL2tleXMvc3NoS2V5RmlsZVwiO1xuaW1wb3J0IHBhdGggZnJvbSBcInBhdGhcIjtcbmltcG9ydCB7IGZpbmRLZXksIGtleXNEaXIsIExFR0FDWV9SRVBPX0tFWVNfRElSLCBSRVBPU19ESVIsIHNvdXJjZU5hbWUgfSBmcm9tIFwiLi9zb3VyY2VzXCI7XG5cbi8vIEEgc291cmNlJ3MgcmV2b2NhdGlvbnMgbGl2ZSBpbiBhIHJlcG8gYmVzaWRlIGl0LiBEZXJpdmVkIHJhdGhlciB0aGFuIGNvbmZpZ3VyZWQsIHNvIGFkZGluZyBhXG4vLyBzb3VyY2UgYnJpbmdzIGl0cyByZXZva2UgcmVwbyB3aXRoIGl0IGFuZCB0aGVyZSBpcyBub3RoaW5nIGV4dHJhIHRvIHBhc3Mgb24gdGhlIGNvbW1hbmQgbGluZS5cbmNvbnN0IFJFVk9LRURfU1VGRklYID0gXCJfcmV2b2tlZFwiO1xuLy8gR2l0SHViIHdpbGwgbm90IHRha2UgdGhlIHNhbWUgcHVibGljIGtleSBhcyBhIGRlcGxveSBrZXkgb24gdHdvIHJlcG9zLCBzbyB0aGUgcmV2b2tlIHJlcG8gaXNcbi8vIHJlYWNoZWQgd2l0aCBhIGtleSBkZXJpdmVkIGZyb20gdGhlIHNvdXJjZSdzLCB1bmRlciB0aGlzIGxhYmVsLlxuZXhwb3J0IGNvbnN0IFJFVk9LRV9LRVlfTEFCRUwgPSBcInJldm9rZWdpdGh1YmtleVwiO1xuXG5leHBvcnQgZnVuY3Rpb24gcmV2b2tlUmVwb1VSTChzb3VyY2VVUkw6IHN0cmluZykge1xuICAgIGxldCB3aXRob3V0R2l0ID0gc291cmNlVVJMLnJlcGxhY2UoL1xcLmdpdCQvLCBcIlwiKTtcbiAgICByZXR1cm4gYCR7d2l0aG91dEdpdH0ke1JFVk9LRURfU1VGRklYfS5naXRgO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gcmV2b2tlS2V5UGF0aChzb3VyY2VVUkw6IHN0cmluZykge1xuICAgIHJldHVybiBwYXRoLmpvaW4oa2V5c0RpcigpLCBgJHtzb3VyY2VOYW1lKHNvdXJjZVVSTCl9JHtSRVZPS0VEX1NVRkZJWH1gKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGxlZ2FjeVJldm9rZUtleVBhdGgoc291cmNlVVJMOiBzdHJpbmcpIHtcbiAgICByZXR1cm4gYCR7TEVHQUNZX1JFUE9fS0VZU19ESVJ9LyR7c291cmNlTmFtZShzb3VyY2VVUkwpfSR7UkVWT0tFRF9TVUZGSVh9YDtcbn1cblxuLyoqIFdoZXJldmVyIHRoaXMgc291cmNlJ3MgcmV2b2tlIGtleSBhbHJlYWR5IGlzLCBvciBub3RoaW5nIGlmIGl0IGhhcyBub3QgYmVlbiBkZXJpdmVkIHlldC4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBmaW5kUmV2b2tlS2V5KHNvdXJjZVVSTDogc3RyaW5nKSB7XG4gICAgcmV0dXJuIGF3YWl0IGZpbmRLZXkoeyBjdXJyZW50OiByZXZva2VLZXlQYXRoKHNvdXJjZVVSTCksIGxlZ2FjeTogbGVnYWN5UmV2b2tlS2V5UGF0aChzb3VyY2VVUkwpIH0pO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gcmV2b2tlUmVwb1BhdGgoc291cmNlVVJMOiBzdHJpbmcpIHtcbiAgICByZXR1cm4gYCR7UkVQT1NfRElSfS8ke3NvdXJjZU5hbWUoc291cmNlVVJMKX0ke1JFVk9LRURfU1VGRklYfWA7XG59XG5cbi8qKiBUaGUgcmV2b2tlIHJlcG8ncyBrZXksIHdvcmtlZCBvdXQgZnJvbSB0aGUgc291cmNlIHJlcG8ncyBrZXkuIEFueXRoaW5nIGhvbGRpbmcgdGhlIHNvdXJjZSBrZXlcbiAgICBjYW4gcHJvZHVjZSBpdCwgc28gaXQgbmV2ZXIgaGFzIHRvIGJlIHN0b3JlZCBhbnl3aGVyZSBzZXBhcmF0ZWx5IG9yIGhhbmRlZCBhcm91bmQuICovXG5leHBvcnQgZnVuY3Rpb24gZGVyaXZlUmV2b2tlS2V5KHNvdXJjZVByaXZhdGVLZXk6IHN0cmluZykge1xuICAgIGxldCBzb3VyY2UgPSBwYXJzZU9wZW5TU0hQcml2YXRlS2V5KHNvdXJjZVByaXZhdGVLZXkpO1xuICAgIGxldCBkZXJpdmVkID0gZGVyaXZlRWQyNTUxOUtleSh7IHNlZWQ6IHNvdXJjZS5zZWVkLCBsYWJlbDogUkVWT0tFX0tFWV9MQUJFTCB9KTtcbiAgICByZXR1cm4ge1xuICAgICAgICBwdWJsaWNLZXk6IGBzc2gtZWQyNTUxOSAke0J1ZmZlci5jb25jYXQoW1xuICAgICAgICAgICAgQnVmZmVyLmZyb20oWzAsIDAsIDAsIDExXSksIEJ1ZmZlci5mcm9tKFwic3NoLWVkMjU1MTlcIiksXG4gICAgICAgICAgICBCdWZmZXIuZnJvbShbMCwgMCwgMCwgMzJdKSwgZGVyaXZlZC5wdWJsaWNLZXksXG4gICAgICAgIF0pLnRvU3RyaW5nKFwiYmFzZTY0XCIpfWAsXG4gICAgICAgIHByaXZhdGVLZXlGaWxlOiBmb3JtYXRPcGVuU1NIUHJpdmF0ZUtleSh7XG4gICAgICAgICAgICBzZWVkOiBkZXJpdmVkLnNlZWQsXG4gICAgICAgICAgICBjb21tZW50OiBgJHtzb3VyY2UuY29tbWVudCB8fCBcInBvcnRzZWN1cmVcIn0gJHtSRVZPS0VfS0VZX0xBQkVMfWAsXG4gICAgICAgIH0pLFxuICAgIH07XG59XG4iXX0=
|
|
57
|
+
/* _JS_SOURCE_HASH = "fa19b1fd43388ad835ac95f96f64acb37f8ebbc316741e6c48fc273ac174e1e7"; */
|