@workflow/core 5.0.0-beta.5 → 5.0.0-beta.51
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/README.md +5 -0
- package/dist/attribute-changes.d.ts +5 -0
- package/dist/attribute-changes.d.ts.map +1 -0
- package/dist/attribute-changes.js +25 -0
- package/dist/capabilities.d.ts +35 -2
- package/dist/capabilities.d.ts.map +1 -1
- package/dist/capabilities.js +74 -5
- package/dist/capture-stack.d.ts +1 -1
- package/dist/capture-stack.js +2 -2
- package/dist/class-serialization.d.ts +32 -0
- package/dist/class-serialization.d.ts.map +1 -1
- package/dist/class-serialization.js +37 -1
- package/dist/classify-error.d.ts +19 -3
- package/dist/classify-error.d.ts.map +1 -1
- package/dist/classify-error.js +98 -6
- package/dist/context-violation-error.d.ts +5 -5
- package/dist/context-violation-error.d.ts.map +1 -1
- package/dist/context-violation-error.js +8 -6
- package/dist/create-hook.d.ts +72 -7
- package/dist/create-hook.d.ts.map +1 -1
- package/dist/create-hook.js +1 -1
- package/dist/define-hook.js +2 -2
- package/dist/describe-error.d.ts +7 -7
- package/dist/describe-error.d.ts.map +1 -1
- package/dist/describe-error.js +97 -12
- package/dist/encryption.d.ts +38 -4
- package/dist/encryption.d.ts.map +1 -1
- package/dist/encryption.js +96 -14
- package/dist/events-consumer.d.ts +297 -0
- package/dist/events-consumer.d.ts.map +1 -1
- package/dist/events-consumer.js +673 -42
- package/dist/flushable-stream.d.ts +97 -1
- package/dist/flushable-stream.d.ts.map +1 -1
- package/dist/flushable-stream.js +291 -11
- package/dist/global.d.ts +36 -2
- package/dist/global.d.ts.map +1 -1
- package/dist/global.js +21 -6
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/log-format.d.ts +2 -2
- package/dist/log-format.js +19 -12
- package/dist/logger.d.ts +1 -0
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +10 -6
- package/dist/private.d.ts +226 -9
- package/dist/private.d.ts.map +1 -1
- package/dist/private.js +626 -7
- package/dist/replay-divergence.d.ts +25 -0
- package/dist/replay-divergence.d.ts.map +1 -0
- package/dist/replay-divergence.js +61 -0
- package/dist/replay-payload-cache.d.ts +64 -0
- package/dist/replay-payload-cache.d.ts.map +1 -0
- package/dist/replay-payload-cache.js +158 -0
- package/dist/runtime/compute-instance.d.ts +13 -0
- package/dist/runtime/compute-instance.d.ts.map +1 -0
- package/dist/runtime/compute-instance.js +14 -0
- package/dist/runtime/constants.d.ts +315 -0
- package/dist/runtime/constants.d.ts.map +1 -1
- package/dist/runtime/constants.js +502 -16
- package/dist/runtime/count-step-started-events.d.ts +52 -0
- package/dist/runtime/count-step-started-events.d.ts.map +1 -0
- package/dist/runtime/count-step-started-events.js +72 -0
- package/dist/runtime/deployment-guard.d.ts +99 -0
- package/dist/runtime/deployment-guard.d.ts.map +1 -0
- package/dist/runtime/deployment-guard.js +154 -0
- package/dist/runtime/get-port-lazy.d.ts +15 -0
- package/dist/runtime/get-port-lazy.d.ts.map +1 -1
- package/dist/runtime/get-port-lazy.js +75 -6
- package/dist/runtime/get-world-lazy.d.ts +5 -14
- package/dist/runtime/get-world-lazy.d.ts.map +1 -1
- package/dist/runtime/get-world-lazy.js +19 -29
- package/dist/runtime/helpers.d.ts +415 -29
- package/dist/runtime/helpers.d.ts.map +1 -1
- package/dist/runtime/helpers.js +686 -57
- package/dist/runtime/quickjs-assets.generated.d.ts +14 -0
- package/dist/runtime/quickjs-assets.generated.d.ts.map +1 -0
- package/dist/runtime/quickjs-assets.generated.js +30 -0
- package/dist/runtime/quickjs-entrypoint.d.ts +158 -0
- package/dist/runtime/quickjs-entrypoint.d.ts.map +1 -0
- package/dist/runtime/quickjs-entrypoint.js +1863 -0
- package/dist/runtime/quickjs-log-view.d.ts +129 -0
- package/dist/runtime/quickjs-log-view.d.ts.map +1 -0
- package/dist/runtime/quickjs-log-view.js +229 -0
- package/dist/runtime/quickjs-runtime.d.ts +244 -0
- package/dist/runtime/quickjs-runtime.d.ts.map +1 -0
- package/dist/runtime/quickjs-runtime.js +2427 -0
- package/dist/runtime/quickjs-serde.d.ts +107 -0
- package/dist/runtime/quickjs-serde.d.ts.map +1 -0
- package/dist/runtime/quickjs-serde.js +2159 -0
- package/dist/runtime/replay-budget.d.ts +97 -0
- package/dist/runtime/replay-budget.d.ts.map +1 -0
- package/dist/runtime/replay-budget.js +139 -0
- package/dist/runtime/replay-recovery-reporter.d.ts +36 -0
- package/dist/runtime/replay-recovery-reporter.d.ts.map +1 -0
- package/dist/runtime/replay-recovery-reporter.js +64 -0
- package/dist/runtime/resume-hook.d.ts +91 -8
- package/dist/runtime/resume-hook.d.ts.map +1 -1
- package/dist/runtime/resume-hook.js +512 -89
- package/dist/runtime/resume-latency.d.ts +150 -0
- package/dist/runtime/resume-latency.d.ts.map +1 -0
- package/dist/runtime/resume-latency.js +155 -0
- package/dist/runtime/run-id-time.d.ts +19 -0
- package/dist/runtime/run-id-time.d.ts.map +1 -0
- package/dist/runtime/run-id-time.js +42 -0
- package/dist/runtime/run.d.ts +72 -7
- package/dist/runtime/run.d.ts.map +1 -1
- package/dist/runtime/run.js +280 -53
- package/dist/runtime/runs.d.ts +54 -3
- package/dist/runtime/runs.d.ts.map +1 -1
- package/dist/runtime/runs.js +126 -16
- package/dist/runtime/start.d.ts +101 -2
- package/dist/runtime/start.d.ts.map +1 -1
- package/dist/runtime/start.js +352 -61
- package/dist/runtime/step-executor.d.ts +186 -5
- package/dist/runtime/step-executor.d.ts.map +1 -1
- package/dist/runtime/step-executor.js +768 -130
- package/dist/runtime/step-latency.d.ts +220 -0
- package/dist/runtime/step-latency.d.ts.map +1 -0
- package/dist/runtime/step-latency.js +215 -0
- package/dist/runtime/step-ownership.d.ts +72 -0
- package/dist/runtime/step-ownership.d.ts.map +1 -0
- package/dist/runtime/step-ownership.js +114 -0
- package/dist/runtime/step-single-flight.d.ts +12 -0
- package/dist/runtime/step-single-flight.d.ts.map +1 -0
- package/dist/runtime/step-single-flight.js +75 -0
- package/dist/runtime/suspension-handler.d.ts +247 -7
- package/dist/runtime/suspension-handler.d.ts.map +1 -1
- package/dist/runtime/suspension-handler.js +1304 -125
- package/dist/runtime/unserializable-step.d.ts +34 -0
- package/dist/runtime/unserializable-step.d.ts.map +1 -0
- package/dist/runtime/unserializable-step.js +52 -0
- package/dist/runtime/vm-mode.d.ts +44 -0
- package/dist/runtime/vm-mode.d.ts.map +1 -0
- package/dist/runtime/vm-mode.js +62 -0
- package/dist/runtime/vm-serde-bundle.generated.d.ts +14 -0
- package/dist/runtime/vm-serde-bundle.generated.d.ts.map +1 -0
- package/dist/runtime/vm-serde-bundle.generated.js +16 -0
- package/dist/runtime/wait-continuation.d.ts +106 -0
- package/dist/runtime/wait-continuation.d.ts.map +1 -0
- package/dist/runtime/wait-continuation.js +136 -0
- package/dist/runtime/wait-until.d.ts +18 -0
- package/dist/runtime/wait-until.d.ts.map +1 -0
- package/dist/runtime/wait-until.js +42 -0
- package/dist/runtime/world-compatibility.d.ts +32 -0
- package/dist/runtime/world-compatibility.d.ts.map +1 -0
- package/dist/runtime/world-compatibility.js +44 -0
- package/dist/runtime/world-init.d.ts +17 -42
- package/dist/runtime/world-init.d.ts.map +1 -1
- package/dist/runtime/world-init.js +18 -43
- package/dist/runtime/world.d.ts +14 -2
- package/dist/runtime/world.d.ts.map +1 -1
- package/dist/runtime/world.js +75 -19
- package/dist/runtime.d.ts +10 -6
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +3911 -489
- package/dist/sealed-box.d.ts +167 -0
- package/dist/sealed-box.d.ts.map +1 -0
- package/dist/sealed-box.js +571 -0
- package/dist/serialization/client.d.ts +3 -3
- package/dist/serialization/client.d.ts.map +1 -1
- package/dist/serialization/client.js +8 -4
- package/dist/serialization/codec-devalue-vm.d.ts +16 -0
- package/dist/serialization/codec-devalue-vm.d.ts.map +1 -0
- package/dist/serialization/codec-devalue-vm.js +148 -0
- package/dist/serialization/codec-devalue.d.ts +1 -1
- package/dist/serialization/codec-devalue.d.ts.map +1 -1
- package/dist/serialization/codec-devalue.js +24 -6
- package/dist/serialization/codec.d.ts +37 -2
- package/dist/serialization/codec.d.ts.map +1 -1
- package/dist/serialization/codec.js +2 -2
- package/dist/serialization/compression.d.ts +105 -0
- package/dist/serialization/compression.d.ts.map +1 -0
- package/dist/serialization/compression.js +261 -0
- package/dist/serialization/encryption.d.ts +119 -17
- package/dist/serialization/encryption.d.ts.map +1 -1
- package/dist/serialization/encryption.js +152 -17
- package/dist/serialization/errors.d.ts +13 -0
- package/dist/serialization/errors.d.ts.map +1 -1
- package/dist/serialization/errors.js +20 -2
- package/dist/serialization/format.d.ts +6 -6
- package/dist/serialization/format.js +7 -7
- package/dist/serialization/hardened.d.ts +156 -0
- package/dist/serialization/hardened.d.ts.map +1 -0
- package/dist/serialization/hardened.js +538 -0
- package/dist/serialization/index.d.ts +10 -8
- package/dist/serialization/index.d.ts.map +1 -1
- package/dist/serialization/index.js +10 -8
- package/dist/serialization/reducers/class-vm.d.ts +20 -0
- package/dist/serialization/reducers/class-vm.d.ts.map +1 -0
- package/dist/serialization/reducers/class-vm.js +77 -0
- package/dist/serialization/reducers/class.d.ts.map +1 -1
- package/dist/serialization/reducers/class.js +11 -6
- package/dist/serialization/reducers/common-vm.d.ts +15 -0
- package/dist/serialization/reducers/common-vm.d.ts.map +1 -0
- package/dist/serialization/reducers/common-vm.js +616 -0
- package/dist/serialization/reducers/common.d.ts +1 -1
- package/dist/serialization/reducers/common.d.ts.map +1 -1
- package/dist/serialization/reducers/common.js +202 -62
- package/dist/serialization/reducers/step-function-vm.d.ts +44 -0
- package/dist/serialization/reducers/step-function-vm.d.ts.map +1 -0
- package/dist/serialization/reducers/step-function-vm.js +97 -0
- package/dist/serialization/reducers/step-function.d.ts.map +1 -1
- package/dist/serialization/reducers/step-function.js +25 -9
- package/dist/serialization/step.d.ts +4 -4
- package/dist/serialization/step.d.ts.map +1 -1
- package/dist/serialization/step.js +9 -5
- package/dist/serialization/types.d.ts +80 -1
- package/dist/serialization/types.d.ts.map +1 -1
- package/dist/serialization/types.js +15 -1
- package/dist/serialization/workflow-vm.d.ts +29 -0
- package/dist/serialization/workflow-vm.d.ts.map +1 -0
- package/dist/serialization/workflow-vm.js +74 -0
- package/dist/serialization-format.d.ts +64 -8
- package/dist/serialization-format.d.ts.map +1 -1
- package/dist/serialization-format.js +225 -73
- package/dist/serialization.d.ts +262 -34
- package/dist/serialization.d.ts.map +1 -1
- package/dist/serialization.js +1885 -277
- package/dist/set-attributes.d.ts +13 -0
- package/dist/set-attributes.d.ts.map +1 -0
- package/dist/set-attributes.js +60 -0
- package/dist/source-map.d.ts +25 -0
- package/dist/source-map.d.ts.map +1 -1
- package/dist/source-map.js +154 -10
- package/dist/step/context-storage.d.ts +63 -2
- package/dist/step/context-storage.d.ts.map +1 -1
- package/dist/step/context-storage.js +7 -5
- package/dist/step/writable-stream.d.ts +3 -3
- package/dist/step/writable-stream.d.ts.map +1 -1
- package/dist/step/writable-stream.js +80 -13
- package/dist/step.d.ts.map +1 -1
- package/dist/step.js +148 -27
- package/dist/symbols.d.ts +50 -0
- package/dist/symbols.d.ts.map +1 -1
- package/dist/symbols.js +51 -1
- package/dist/telemetry/semantic-conventions.d.ts +433 -2
- package/dist/telemetry/semantic-conventions.d.ts.map +1 -1
- package/dist/telemetry/semantic-conventions.js +319 -3
- package/dist/telemetry.d.ts +91 -0
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +244 -17
- package/dist/test-support/orchestrator-context.d.ts +25 -0
- package/dist/test-support/orchestrator-context.d.ts.map +1 -0
- package/dist/test-support/orchestrator-context.js +107 -0
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +23 -1
- package/dist/util.d.ts +0 -5
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +1 -15
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +2 -2
- package/dist/vm/index.d.ts.map +1 -1
- package/dist/vm/index.js +84 -14
- package/dist/vm/script-cache.d.ts +26 -0
- package/dist/vm/script-cache.d.ts.map +1 -0
- package/dist/vm/script-cache.js +131 -0
- package/dist/vm/uint8array-base64.js +1 -1
- package/dist/workflow/abort-controller.d.ts +2 -2
- package/dist/workflow/abort-controller.d.ts.map +1 -1
- package/dist/workflow/abort-controller.js +88 -21
- package/dist/workflow/attribute-dispatcher.d.ts +6 -0
- package/dist/workflow/attribute-dispatcher.d.ts.map +1 -0
- package/dist/workflow/attribute-dispatcher.js +45 -0
- package/dist/workflow/create-hook.d.ts.map +1 -1
- package/dist/workflow/create-hook.js +24 -3
- package/dist/workflow/get-workflow-metadata.js +2 -2
- package/dist/workflow/hook.d.ts.map +1 -1
- package/dist/workflow/hook.js +304 -47
- package/dist/workflow/index.d.ts +1 -0
- package/dist/workflow/index.d.ts.map +1 -1
- package/dist/workflow/index.js +2 -1
- package/dist/workflow/set-attributes.d.ts +68 -0
- package/dist/workflow/set-attributes.d.ts.map +1 -0
- package/dist/workflow/set-attributes.js +60 -0
- package/dist/workflow/sleep.d.ts.map +1 -1
- package/dist/workflow/sleep.js +58 -12
- package/dist/workflow/world-init-stub.d.ts +3 -3
- package/dist/workflow/world-init-stub.js +4 -4
- package/dist/workflow.d.ts +95 -3
- package/dist/workflow.d.ts.map +1 -1
- package/dist/workflow.js +900 -561
- package/docs/api-reference/create-hook.mdx +89 -10
- package/docs/api-reference/create-webhook.mdx +16 -15
- package/docs/api-reference/define-hook.mdx +35 -33
- package/docs/api-reference/fatal-error.mdx +30 -8
- package/docs/api-reference/fetch.mdx +14 -10
- package/docs/api-reference/get-step-metadata.mdx +2 -2
- package/docs/api-reference/get-workflow-metadata.mdx +3 -3
- package/docs/api-reference/get-writable.mdx +7 -7
- package/docs/api-reference/index.mdx +4 -1
- package/docs/api-reference/retryable-error.mdx +1 -1
- package/docs/api-reference/set-attributes.mdx +61 -0
- package/docs/api-reference/sleep.mdx +4 -4
- package/docs/foundations/cancellation.mdx +31 -32
- package/docs/foundations/errors-and-retries.mdx +42 -11
- package/docs/foundations/hooks.mdx +64 -35
- package/docs/foundations/idempotency.mdx +244 -12
- package/docs/foundations/index.mdx +1 -23
- package/docs/foundations/meta.json +2 -1
- package/docs/foundations/serialization.mdx +21 -22
- package/docs/foundations/starting-workflows.mdx +106 -30
- package/docs/foundations/streaming.mdx +108 -60
- package/docs/foundations/versioning.mdx +263 -0
- package/docs/foundations/workflows-and-steps.mdx +9 -9
- package/docs/how-it-works/cancellation.mdx +63 -63
- package/docs/how-it-works/code-transform.mdx +83 -67
- package/docs/how-it-works/encryption.mdx +30 -26
- package/docs/how-it-works/event-sourcing.mdx +125 -34
- package/docs/how-it-works/framework-integrations.mdx +96 -337
- package/docs/how-it-works/understanding-directives.mdx +22 -22
- package/package.json +24 -15
- package/dist/runtime/step-handler.d.ts +0 -2
- package/dist/runtime/step-handler.d.ts.map +0 -1
- package/dist/runtime/step-handler.js +0 -776
|
@@ -0,0 +1,571 @@
|
|
|
1
|
+
import { RuntimeDecryptionError, WorkflowRuntimeError } from '@workflow/errors';
|
|
2
|
+
import { decrypt as aesGcmDecrypt, encrypt as aesGcmEncrypt, NONCE_LENGTH, TAG_BYTES, } from './encryption.js';
|
|
3
|
+
/**
|
|
4
|
+
* Browser-compatible sealed-box (asymmetric) encryption for cross-run writes.
|
|
5
|
+
*
|
|
6
|
+
* ## Why this exists
|
|
7
|
+
*
|
|
8
|
+
* The symmetric `encr` path requires the writer to hold the recipient run's
|
|
9
|
+
* key, which also grants decrypt capability. For *cross-run* writes (a hook
|
|
10
|
+
* resumption targeting another run, or a child workflow writing into a
|
|
11
|
+
* parent's forwarded `WritableStream`) that is more authority than the writer
|
|
12
|
+
* needs, and obtaining the symmetric key across a deployment boundary costs a
|
|
13
|
+
* round trip to the Vercel API.
|
|
14
|
+
*
|
|
15
|
+
* A sealed box fixes both: the writer needs only the recipient's **public**
|
|
16
|
+
* key, which is not secret and therefore travels on the run entity (and in
|
|
17
|
+
* stream descriptors) at no cost. "Encrypt-only" stops being an honor-system
|
|
18
|
+
* convention enforced by `importKey(raw, ['encrypt'])` and becomes a
|
|
19
|
+
* cryptographic guarantee: a writer holding only the public key provably
|
|
20
|
+
* cannot read anything.
|
|
21
|
+
*
|
|
22
|
+
* ## Key hierarchy
|
|
23
|
+
*
|
|
24
|
+
* Both keys descend from the same per-run key material `K` that
|
|
25
|
+
* `World.getEncryptionKeyForRun()` already returns today, so nothing about
|
|
26
|
+
* key acquisition, the World interface, or the Vercel API changes:
|
|
27
|
+
*
|
|
28
|
+
* ```
|
|
29
|
+
* K (32 bytes, per-run, = HKDF(VERCEL_DEPLOYMENT_KEY, "projectId|runId"))
|
|
30
|
+
* ├── AES-256 key = K used directly → 'encr' envelopes (unchanged)
|
|
31
|
+
* └── X25519 scalar = HKDF(K, SCALAR_INFO) → 'encp' envelopes
|
|
32
|
+
* └── public key = X25519 basepoint · scalar (published, not secret)
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* `K` is therefore both an AEAD key and a key-derivation key. This is sound:
|
|
36
|
+
* AES-GCM never exposes its key, and HKDF-SHA256 output is computationally
|
|
37
|
+
* independent of the AES keystream. A strict reading of NIST SP 800-108
|
|
38
|
+
* prefers these roles be separated, but `K`'s direct AES use is fixed by
|
|
39
|
+
* backward compatibility with every `encr` payload already written.
|
|
40
|
+
*
|
|
41
|
+
* ## Construction
|
|
42
|
+
*
|
|
43
|
+
* An ECIES-style sealed box using the same primitives as HPKE (RFC 9180)
|
|
44
|
+
* base mode with DHKEM(X25519, HKDF-SHA256) and AES-256-GCM, but without the
|
|
45
|
+
* full `LabeledExtract`/`LabeledExpand` suite-ID ceremony:
|
|
46
|
+
*
|
|
47
|
+
* ```
|
|
48
|
+
* ephemeral = X25519 keypair, freshly generated per encryption context
|
|
49
|
+
* sharedSecret = X25519(ephemeralPrivate, recipientPublic)
|
|
50
|
+
* contentKey = HKDF-SHA256(
|
|
51
|
+
* ikm = sharedSecret,
|
|
52
|
+
* salt = zeros(32),
|
|
53
|
+
* info = CONTENT_KEY_INFO ‖ ephemeralPublic ‖ recipientPublic,
|
|
54
|
+
* )
|
|
55
|
+
* ```
|
|
56
|
+
*
|
|
57
|
+
* Binding both public keys into `info` is the security-critical part (it is
|
|
58
|
+
* HPKE's `kem_context`): it prevents key-substitution/unknown-key-share
|
|
59
|
+
* attacks in which an adversary replays a ciphertext as though it had been
|
|
60
|
+
* sealed to a different recipient. Deviating from strict RFC 9180 framing is
|
|
61
|
+
* a deliberate, documented choice: the envelope is versioned via its format
|
|
62
|
+
* prefix and the HKDF labels below, so a conformant profile can be added
|
|
63
|
+
* later as a new version without touching existing payloads.
|
|
64
|
+
*
|
|
65
|
+
* Wire format (the `encp` format prefix is attached by the serialization
|
|
66
|
+
* layer, not by this module):
|
|
67
|
+
*
|
|
68
|
+
* ```
|
|
69
|
+
* [ephemeral public key (32 bytes)][nonce (12 bytes)][ciphertext + GCM tag (16 bytes)]
|
|
70
|
+
* ```
|
|
71
|
+
*
|
|
72
|
+
* ## Nonce discipline
|
|
73
|
+
*
|
|
74
|
+
* One-shot {@link seal} generates a fresh ephemeral keypair (and therefore a
|
|
75
|
+
* fresh content key) for every call, so nonce reuse is impossible.
|
|
76
|
+
*
|
|
77
|
+
* Callers that amortize the KEM across many frames via {@link encapsulate}
|
|
78
|
+
* take on nonce discipline themselves, because the content key then outlives a
|
|
79
|
+
* single message. Two rules apply:
|
|
80
|
+
*
|
|
81
|
+
* 1. Every frame MUST use a fresh random nonce (which `aesGcmEncrypt` does).
|
|
82
|
+
* Never a counter: a counter restarts at zero after a stream reconnect or a
|
|
83
|
+
* durable replay, reusing `(contentKey, nonce)`, which under AES-GCM leaks
|
|
84
|
+
* the plaintext XOR of the two frames and the GCM auth subkey.
|
|
85
|
+
* 2. A writer must not inherit a previous incarnation's content key, so
|
|
86
|
+
* {@link encapsulate} is re-run per writer instance.
|
|
87
|
+
*
|
|
88
|
+
* Nothing in this module enforces either rule; it is the streaming caller's
|
|
89
|
+
* contract.
|
|
90
|
+
*/
|
|
91
|
+
/** Length of an X25519 private scalar and public key, in bytes. */
|
|
92
|
+
const X25519_KEY_LENGTH = 32;
|
|
93
|
+
/** Length of the AES-256 content key derived from the shared secret. */
|
|
94
|
+
const CONTENT_KEY_LENGTH = 32;
|
|
95
|
+
/**
|
|
96
|
+
* HKDF `info` label for deriving the per-run X25519 scalar from the per-run
|
|
97
|
+
* key material.
|
|
98
|
+
*
|
|
99
|
+
* Versioned and namespaced so that any future derivation from the same `K`
|
|
100
|
+
* (a second keypair, a MAC key, a different curve) can never collide with
|
|
101
|
+
* this one.
|
|
102
|
+
*/
|
|
103
|
+
const SCALAR_INFO = 'workflow/encp/x25519/v1';
|
|
104
|
+
/**
|
|
105
|
+
* HKDF `info` prefix for deriving the AES-256-GCM content key from an X25519
|
|
106
|
+
* shared secret. The ephemeral and recipient public keys are appended to this
|
|
107
|
+
* label to form the full `info` (HPKE's `kem_context`).
|
|
108
|
+
*/
|
|
109
|
+
const CONTENT_KEY_INFO = 'workflow/encp/aes256gcm/v1';
|
|
110
|
+
/**
|
|
111
|
+
* DER prefix for an RFC 8410 `PrivateKeyInfo` wrapping a raw X25519 scalar.
|
|
112
|
+
*
|
|
113
|
+
* Web Crypto cannot import a raw X25519 private key (`importKey('raw', ...)`
|
|
114
|
+
* is only defined for public keys on the Secure Curves algorithms), so a
|
|
115
|
+
* derived scalar has to be wrapped in PKCS#8 before it can be used. The
|
|
116
|
+
* structure is fully determined for a 32-byte X25519 key:
|
|
117
|
+
*
|
|
118
|
+
* ```
|
|
119
|
+
* SEQUENCE (46 bytes) 30 2e
|
|
120
|
+
* INTEGER 0 02 01 00
|
|
121
|
+
* SEQUENCE (5 bytes) 30 05
|
|
122
|
+
* OBJECT IDENTIFIER 1.3.101.110 (X25519) 06 03 2b 65 6e
|
|
123
|
+
* OCTET STRING (34 bytes) 04 22
|
|
124
|
+
* OCTET STRING (32 bytes) 04 20
|
|
125
|
+
* <scalar>
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
const PKCS8_X25519_PREFIX = new Uint8Array([
|
|
129
|
+
0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x6e, 0x04,
|
|
130
|
+
0x22, 0x04, 0x20,
|
|
131
|
+
]);
|
|
132
|
+
const infoEncoder = new TextEncoder();
|
|
133
|
+
function assertKeyMaterialLength(runKeyMaterial) {
|
|
134
|
+
if (runKeyMaterial.byteLength !== CONTENT_KEY_LENGTH) {
|
|
135
|
+
throw new WorkflowRuntimeError(`Run key material must be exactly ${CONTENT_KEY_LENGTH} bytes, got ${runKeyMaterial.byteLength}`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function assertPublicKeyLength(publicKey, label) {
|
|
139
|
+
if (publicKey.byteLength !== X25519_KEY_LENGTH) {
|
|
140
|
+
throw new WorkflowRuntimeError(`${label} must be exactly ${X25519_KEY_LENGTH} bytes, got ${publicKey.byteLength}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Derive the per-run X25519 keypair from the run's 32-byte key material.
|
|
145
|
+
*
|
|
146
|
+
* Deterministic: the same key material always yields the same keypair, which
|
|
147
|
+
* is what lets the owning deployment re-derive its private scalar on demand
|
|
148
|
+
* (from `VERCEL_DEPLOYMENT_KEY`) instead of storing it anywhere.
|
|
149
|
+
*
|
|
150
|
+
* @param runKeyMaterial - The 32 bytes returned by `World.getEncryptionKeyForRun()`
|
|
151
|
+
* @returns The run's X25519 scalar and public key
|
|
152
|
+
*/
|
|
153
|
+
export async function deriveRunKeyPair(runKeyMaterial) {
|
|
154
|
+
assertKeyMaterialLength(runKeyMaterial);
|
|
155
|
+
const hkdfKey = await globalThis.crypto.subtle.importKey('raw', runKeyMaterial, 'HKDF', false, ['deriveBits']);
|
|
156
|
+
// Zero salt is acceptable per RFC 5869 §3.1: the IKM here is itself HKDF
|
|
157
|
+
// output (or a high-entropy BYOK value), so it is already uniform. The
|
|
158
|
+
// `info` label provides domain separation.
|
|
159
|
+
const scalarBits = await globalThis.crypto.subtle.deriveBits({
|
|
160
|
+
name: 'HKDF',
|
|
161
|
+
hash: 'SHA-256',
|
|
162
|
+
salt: new Uint8Array(32),
|
|
163
|
+
info: infoEncoder.encode(SCALAR_INFO),
|
|
164
|
+
}, hkdfKey, X25519_KEY_LENGTH * 8);
|
|
165
|
+
const scalar = new Uint8Array(scalarBits);
|
|
166
|
+
// Any 32-byte string is a valid X25519 scalar (the low/high bits are
|
|
167
|
+
// clamped during scalar multiplication), so the HKDF output needs no
|
|
168
|
+
// rejection sampling.
|
|
169
|
+
const publicKey = await derivePublicKeyFromScalar(scalar);
|
|
170
|
+
return { scalar, publicKey };
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Recover the raw public key for a scalar.
|
|
174
|
+
*
|
|
175
|
+
* Web Crypto exposes no scalar-multiplication primitive, so the scalar is
|
|
176
|
+
* imported as a PKCS#8 private key and its public half read back out of the
|
|
177
|
+
* JWK export (`x`), which the Secure Curves spec defines as the base64url
|
|
178
|
+
* raw public key.
|
|
179
|
+
*/
|
|
180
|
+
async function derivePublicKeyFromScalar(scalar) {
|
|
181
|
+
const privateKey = await importScalar(scalar, /* extractable */ true);
|
|
182
|
+
const jwk = await globalThis.crypto.subtle.exportKey('jwk', privateKey);
|
|
183
|
+
if (!jwk.x) {
|
|
184
|
+
throw new WorkflowRuntimeError('X25519 JWK export did not include a public key component ("x")');
|
|
185
|
+
}
|
|
186
|
+
const publicKey = base64UrlToBytes(jwk.x);
|
|
187
|
+
// Guard the one step in this module that trusts an external encoding. A
|
|
188
|
+
// short or malformed value here would otherwise flow onward and fail much
|
|
189
|
+
// later, inside key agreement, with a far less obvious message.
|
|
190
|
+
if (publicKey.byteLength !== X25519_KEY_LENGTH) {
|
|
191
|
+
throw new WorkflowRuntimeError(`X25519 JWK export produced a ${publicKey.byteLength}-byte public key, expected ${X25519_KEY_LENGTH}`);
|
|
192
|
+
}
|
|
193
|
+
return publicKey;
|
|
194
|
+
}
|
|
195
|
+
async function importScalar(scalar, extractable) {
|
|
196
|
+
const pkcs8 = new Uint8Array(PKCS8_X25519_PREFIX.length + scalar.byteLength);
|
|
197
|
+
pkcs8.set(PKCS8_X25519_PREFIX, 0);
|
|
198
|
+
pkcs8.set(scalar, PKCS8_X25519_PREFIX.length);
|
|
199
|
+
return globalThis.crypto.subtle.importKey('pkcs8', pkcs8, { name: 'X25519' }, extractable, ['deriveBits']);
|
|
200
|
+
}
|
|
201
|
+
function importPublicKey(publicKey) {
|
|
202
|
+
return globalThis.crypto.subtle.importKey('raw', publicKey, { name: 'X25519' },
|
|
203
|
+
/* extractable */ true,
|
|
204
|
+
// Public keys carry no usages for X25519: the private key does the
|
|
205
|
+
// deriving; the public key is only ever an argument to it.
|
|
206
|
+
[]);
|
|
207
|
+
}
|
|
208
|
+
const BASE64_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
209
|
+
const BASE64URL_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
|
|
210
|
+
/**
|
|
211
|
+
* Encode bytes as standard (padded) base64.
|
|
212
|
+
*
|
|
213
|
+
* Hand-rolled for the same reason as {@link base64UrlToBytes}: this module
|
|
214
|
+
* runs in the browser (o11y decryption) and inside the workflow VM, so neither
|
|
215
|
+
* `Buffer` nor `btoa` can be assumed. Used for the wire encoding of run public
|
|
216
|
+
* keys, matching the base64 convention already used for `VERCEL_DEPLOYMENT_KEY`
|
|
217
|
+
* and the `run-key` API response.
|
|
218
|
+
*/
|
|
219
|
+
export function bytesToBase64(bytes) {
|
|
220
|
+
let out = '';
|
|
221
|
+
for (let i = 0; i < bytes.length; i += 3) {
|
|
222
|
+
const b0 = bytes[i];
|
|
223
|
+
const b1 = i + 1 < bytes.length ? bytes[i + 1] : 0;
|
|
224
|
+
const b2 = i + 2 < bytes.length ? bytes[i + 2] : 0;
|
|
225
|
+
out += BASE64_CHARS[(b0 >> 2) & 0x3f];
|
|
226
|
+
out += BASE64_CHARS[((b0 & 0x03) << 4) | ((b1 >> 4) & 0x0f)];
|
|
227
|
+
out +=
|
|
228
|
+
i + 1 < bytes.length
|
|
229
|
+
? BASE64_CHARS[((b1 & 0x0f) << 2) | ((b2 >> 6) & 0x03)]
|
|
230
|
+
: '=';
|
|
231
|
+
out += i + 2 < bytes.length ? BASE64_CHARS[b2 & 0x3f] : '=';
|
|
232
|
+
}
|
|
233
|
+
return out;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Decode standard base64 (padding optional) to bytes.
|
|
237
|
+
*
|
|
238
|
+
* Returns `undefined` for malformed input rather than throwing: callers decode
|
|
239
|
+
* public keys that arrive from storage or over the wire, where a corrupt value
|
|
240
|
+
* should degrade to "this run has no usable public key" (and fall back to the
|
|
241
|
+
* symmetric path) rather than crash a resumption.
|
|
242
|
+
*
|
|
243
|
+
* Validation is strict, because a lenient decoder is worse than a throwing one
|
|
244
|
+
* here: silently returning a short or truncated key makes a corrupt value look
|
|
245
|
+
* *present*, so the caller seals to garbage instead of taking the fallback.
|
|
246
|
+
* Rejected: characters outside the alphabet, a length that cannot describe a
|
|
247
|
+
* whole number of bytes (`length % 4 === 1`), padding anywhere but the end, and
|
|
248
|
+
* a final quantum whose unused low bits are not zero.
|
|
249
|
+
*/
|
|
250
|
+
export function base64ToBytes(value) {
|
|
251
|
+
// Strip trailing padding, then require what remains to be padding-free.
|
|
252
|
+
let end = value.length;
|
|
253
|
+
while (end > 0 && value[end - 1] === '=')
|
|
254
|
+
end--;
|
|
255
|
+
const body = value.slice(0, end);
|
|
256
|
+
if (body.includes('='))
|
|
257
|
+
return undefined;
|
|
258
|
+
// 4 chars -> 3 bytes; 3 -> 2; 2 -> 1. A remainder of 1 encodes no byte.
|
|
259
|
+
if (body.length % 4 === 1)
|
|
260
|
+
return undefined;
|
|
261
|
+
// Padding, when present, must bring the total to a multiple of 4.
|
|
262
|
+
if (end !== value.length && value.length % 4 !== 0)
|
|
263
|
+
return undefined;
|
|
264
|
+
let accumulator = 0;
|
|
265
|
+
let bitCount = 0;
|
|
266
|
+
const bytes = [];
|
|
267
|
+
for (let i = 0; i < body.length; i++) {
|
|
268
|
+
const index = BASE64_CHARS.indexOf(body[i]);
|
|
269
|
+
if (index === -1)
|
|
270
|
+
return undefined;
|
|
271
|
+
accumulator = (accumulator << 6) | index;
|
|
272
|
+
bitCount += 6;
|
|
273
|
+
if (bitCount >= 8) {
|
|
274
|
+
bitCount -= 8;
|
|
275
|
+
bytes.push((accumulator >> bitCount) & 0xff);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
// Leftover bits belong to no byte and must be zero in canonical base64.
|
|
279
|
+
if (bitCount > 0 && (accumulator & ((1 << bitCount) - 1)) !== 0) {
|
|
280
|
+
return undefined;
|
|
281
|
+
}
|
|
282
|
+
return new Uint8Array(bytes);
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Decode a base64url string (JWK encoding) to bytes.
|
|
286
|
+
*
|
|
287
|
+
* Hand-rolled rather than using `atob` or `Buffer`: this module also runs
|
|
288
|
+
* inside the workflow VM, whose global surface is deliberately minimal and
|
|
289
|
+
* does not include either. The inputs here are fixed-size JWK key components,
|
|
290
|
+
* so a compact decoder is sufficient: it accepts unpadded base64url only,
|
|
291
|
+
* which is what RFC 7515 §2 mandates for JWK members.
|
|
292
|
+
*/
|
|
293
|
+
function base64UrlToBytes(value) {
|
|
294
|
+
let accumulator = 0;
|
|
295
|
+
let bitCount = 0;
|
|
296
|
+
const bytes = [];
|
|
297
|
+
for (let i = 0; i < value.length; i++) {
|
|
298
|
+
const char = value[i];
|
|
299
|
+
if (char === '=')
|
|
300
|
+
break;
|
|
301
|
+
const index = BASE64URL_CHARS.indexOf(char);
|
|
302
|
+
if (index === -1) {
|
|
303
|
+
throw new WorkflowRuntimeError(`Invalid base64url character "${char}" in X25519 JWK key component`);
|
|
304
|
+
}
|
|
305
|
+
accumulator = (accumulator << 6) | index;
|
|
306
|
+
bitCount += 6;
|
|
307
|
+
if (bitCount >= 8) {
|
|
308
|
+
bitCount -= 8;
|
|
309
|
+
bytes.push((accumulator >> bitCount) & 0xff);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return new Uint8Array(bytes);
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Derive the AES-256-GCM content key from an X25519 shared secret, binding
|
|
316
|
+
* it to both public keys (HPKE's `kem_context`).
|
|
317
|
+
*/
|
|
318
|
+
async function deriveContentKey(sharedSecret, ephemeralPublicKey, recipientPublicKey, usages) {
|
|
319
|
+
const label = infoEncoder.encode(CONTENT_KEY_INFO);
|
|
320
|
+
const info = new Uint8Array(label.length + ephemeralPublicKey.length + recipientPublicKey.length);
|
|
321
|
+
info.set(label, 0);
|
|
322
|
+
info.set(ephemeralPublicKey, label.length);
|
|
323
|
+
info.set(recipientPublicKey, label.length + ephemeralPublicKey.length);
|
|
324
|
+
const hkdfKey = await globalThis.crypto.subtle.importKey('raw', sharedSecret, 'HKDF', false, ['deriveBits']);
|
|
325
|
+
const contentKeyBits = await globalThis.crypto.subtle.deriveBits({ name: 'HKDF', hash: 'SHA-256', salt: new Uint8Array(32), info }, hkdfKey, CONTENT_KEY_LENGTH * 8);
|
|
326
|
+
return globalThis.crypto.subtle.importKey('raw', new Uint8Array(contentKeyBits), 'AES-GCM', false,
|
|
327
|
+
// `KeyUsage` is a DOM-lib type that is not in scope under `es2022`; the
|
|
328
|
+
// parameter type is a strict subset of it, so this cast is sound.
|
|
329
|
+
usages);
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* The writer half of the KEM: generate an ephemeral keypair and derive a
|
|
333
|
+
* content key for a recipient's public key.
|
|
334
|
+
*
|
|
335
|
+
* Use this when many payloads share one KEM operation, i.e. stream frames.
|
|
336
|
+
* The returned `contentKey` can only encrypt, so a stream writer provably
|
|
337
|
+
* cannot read the recipient run's data even by mistake.
|
|
338
|
+
*
|
|
339
|
+
* **Callers own nonce discipline.** Encrypt each frame with a fresh random
|
|
340
|
+
* nonce, and call this function again for every new connection attempt or
|
|
341
|
+
* replay so that a restarted writer never reuses a content key. For one-shot
|
|
342
|
+
* payloads prefer {@link seal}, which handles this automatically.
|
|
343
|
+
*
|
|
344
|
+
* @param recipientPublicKey - The recipient run's raw 32-byte X25519 public key
|
|
345
|
+
* @returns The ephemeral public key to publish alongside the ciphertext, and
|
|
346
|
+
* the encrypt-only content key
|
|
347
|
+
*/
|
|
348
|
+
export async function encapsulate(recipientPublicKey) {
|
|
349
|
+
assertPublicKeyLength(recipientPublicKey, 'Recipient public key');
|
|
350
|
+
let recipientKey;
|
|
351
|
+
try {
|
|
352
|
+
recipientKey = await importPublicKey(recipientPublicKey);
|
|
353
|
+
}
|
|
354
|
+
catch (cause) {
|
|
355
|
+
throw new WorkflowRuntimeError(`Invalid X25519 recipient public key: ${cause instanceof Error ? cause.message : String(cause)}`, { cause });
|
|
356
|
+
}
|
|
357
|
+
const ephemeral = (await globalThis.crypto.subtle.generateKey({ name: 'X25519' },
|
|
358
|
+
/* extractable */ true, ['deriveBits']));
|
|
359
|
+
const ephemeralPublicKey = new Uint8Array(await globalThis.crypto.subtle.exportKey('raw', ephemeral.publicKey));
|
|
360
|
+
let sharedSecretBits;
|
|
361
|
+
try {
|
|
362
|
+
sharedSecretBits = await globalThis.crypto.subtle.deriveBits({ name: 'X25519', public: recipientKey }, ephemeral.privateKey, X25519_KEY_LENGTH * 8);
|
|
363
|
+
}
|
|
364
|
+
catch (cause) {
|
|
365
|
+
// Web Crypto throws OperationError when the shared secret is all-zero,
|
|
366
|
+
// which happens exactly when the recipient public key is a low-order
|
|
367
|
+
// point. This is the platform giving us HPKE's contributory-behavior
|
|
368
|
+
// check for free.
|
|
369
|
+
throw new WorkflowRuntimeError('X25519 key agreement failed: the recipient public key is not a valid ' +
|
|
370
|
+
'curve point (low-order point or corrupted key material)', { cause });
|
|
371
|
+
}
|
|
372
|
+
const contentKey = await deriveContentKey(new Uint8Array(sharedSecretBits), ephemeralPublicKey, recipientPublicKey, ['encrypt']);
|
|
373
|
+
return { ephemeralPublicKey, contentKey };
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* The recipient half of the KEM: recover the content key for a sealed
|
|
377
|
+
* payload from the run's own keypair and the sender's ephemeral public key.
|
|
378
|
+
*
|
|
379
|
+
* @param keyPair - The recipient run's keypair, from {@link deriveRunKeyPair}
|
|
380
|
+
* @param ephemeralPublicKey - The sender's raw 32-byte X25519 public key,
|
|
381
|
+
* read from the head of the sealed envelope
|
|
382
|
+
* @returns A decrypt-only content key
|
|
383
|
+
*/
|
|
384
|
+
export async function decapsulate(keyPair, ephemeralPublicKey) {
|
|
385
|
+
if (ephemeralPublicKey.byteLength !== X25519_KEY_LENGTH) {
|
|
386
|
+
throw new RuntimeDecryptionError(`Sealed payload has a malformed ephemeral public key: expected ${X25519_KEY_LENGTH} bytes, got ${ephemeralPublicKey.byteLength}`, {
|
|
387
|
+
context: {
|
|
388
|
+
operation: 'decrypt',
|
|
389
|
+
byteLength: ephemeralPublicKey.byteLength,
|
|
390
|
+
},
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
let sharedSecretBits;
|
|
394
|
+
try {
|
|
395
|
+
const [privateKey, senderKey] = await Promise.all([
|
|
396
|
+
importScalar(keyPair.scalar, /* extractable */ false),
|
|
397
|
+
importPublicKey(ephemeralPublicKey),
|
|
398
|
+
]);
|
|
399
|
+
sharedSecretBits = await globalThis.crypto.subtle.deriveBits({ name: 'X25519', public: senderKey }, privateKey, X25519_KEY_LENGTH * 8);
|
|
400
|
+
}
|
|
401
|
+
catch (cause) {
|
|
402
|
+
throw new RuntimeDecryptionError(`X25519 key agreement failed while opening a sealed payload: ${cause instanceof Error ? cause.message : String(cause)}`, {
|
|
403
|
+
cause,
|
|
404
|
+
context: {
|
|
405
|
+
operation: 'decrypt',
|
|
406
|
+
byteLength: ephemeralPublicKey.byteLength,
|
|
407
|
+
},
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
return deriveContentKey(new Uint8Array(sharedSecretBits), ephemeralPublicKey, keyPair.publicKey, ['decrypt']);
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Seal a payload to a run's public key.
|
|
414
|
+
*
|
|
415
|
+
* Each call performs its own KEM operation, so every sealed payload gets an
|
|
416
|
+
* independent content key: nonce reuse across calls is impossible by
|
|
417
|
+
* construction.
|
|
418
|
+
*
|
|
419
|
+
* @param recipientPublicKey - The recipient run's raw 32-byte X25519 public key
|
|
420
|
+
* @param data - Plaintext to seal
|
|
421
|
+
* @param aad - Optional additional authenticated data, covered by the GCM tag
|
|
422
|
+
* but not encrypted. Callers pass the recipient's `projectId|runId` so a
|
|
423
|
+
* sealed payload cannot be replayed against a different run.
|
|
424
|
+
* @returns `[ephemeral public key (32)][nonce (12)][ciphertext + tag]`
|
|
425
|
+
*/
|
|
426
|
+
export async function seal(recipientPublicKey, data, aad) {
|
|
427
|
+
const { ephemeralPublicKey, contentKey } = await encapsulate(recipientPublicKey);
|
|
428
|
+
const encrypted = await aesGcmEncrypt(contentKey, data, aad);
|
|
429
|
+
const result = new Uint8Array(ephemeralPublicKey.byteLength + encrypted.byteLength);
|
|
430
|
+
result.set(ephemeralPublicKey, 0);
|
|
431
|
+
result.set(encrypted, ephemeralPublicKey.byteLength);
|
|
432
|
+
return result;
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Open a payload sealed to this run's public key.
|
|
436
|
+
*
|
|
437
|
+
* @param keyPair - The recipient run's keypair, from {@link deriveRunKeyPair}
|
|
438
|
+
* @param sealed - `[ephemeral public key (32)][nonce (12)][ciphertext + tag]`
|
|
439
|
+
* @param aad - The exact additional authenticated data passed to {@link seal}
|
|
440
|
+
* @returns The decrypted plaintext
|
|
441
|
+
*/
|
|
442
|
+
export async function open(keyPair, sealed, aad) {
|
|
443
|
+
// Ephemeral public key + AES-GCM nonce + AES-GCM tag.
|
|
444
|
+
const minLength = X25519_KEY_LENGTH + NONCE_LENGTH + TAG_BYTES;
|
|
445
|
+
if (sealed.byteLength < minLength) {
|
|
446
|
+
throw new RuntimeDecryptionError(`Sealed payload too short: expected at least ${minLength} bytes, got ${sealed.byteLength}`, { context: { operation: 'decrypt', byteLength: sealed.byteLength } });
|
|
447
|
+
}
|
|
448
|
+
const ephemeralPublicKey = sealed.subarray(0, X25519_KEY_LENGTH);
|
|
449
|
+
const encrypted = sealed.subarray(X25519_KEY_LENGTH);
|
|
450
|
+
const contentKey = await decapsulate(keyPair, ephemeralPublicKey);
|
|
451
|
+
return aesGcmDecrypt(contentKey, encrypted, aad);
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* A writer-side session that amortizes one KEM operation across many sealed
|
|
455
|
+
* payloads. Use it for streams, where one-shot {@link seal} would perform a
|
|
456
|
+
* fresh keygen + ECDH + HKDF for every frame.
|
|
457
|
+
*
|
|
458
|
+
* Safety rests on two properties:
|
|
459
|
+
*
|
|
460
|
+
* - Every payload still gets a **fresh random nonce** from `aesGcmEncrypt`, so
|
|
461
|
+
* sharing the content key does not risk `(key, nonce)` reuse. (A counter
|
|
462
|
+
* would: it restarts at zero whenever a writer restarts.)
|
|
463
|
+
* - The session is bound to one writer instance. A reconnect or a durable
|
|
464
|
+
* replay constructs a new session, so a restarted writer never inherits a
|
|
465
|
+
* previous incarnation's content key.
|
|
466
|
+
*
|
|
467
|
+
* The envelope layout is byte-identical to {@link seal}, so a reader cannot
|
|
468
|
+
* tell which was used and needs no matching session.
|
|
469
|
+
*/
|
|
470
|
+
export function createSealSession(recipientPublicKey, aad) {
|
|
471
|
+
let encapsulation;
|
|
472
|
+
return {
|
|
473
|
+
async seal(data) {
|
|
474
|
+
// Lazily, so constructing a session for a stream that is never written
|
|
475
|
+
// to costs nothing.
|
|
476
|
+
encapsulation ??= encapsulate(recipientPublicKey);
|
|
477
|
+
const { ephemeralPublicKey, contentKey } = await encapsulation;
|
|
478
|
+
const encrypted = await aesGcmEncrypt(contentKey, data, aad);
|
|
479
|
+
const result = new Uint8Array(ephemeralPublicKey.byteLength + encrypted.byteLength);
|
|
480
|
+
result.set(ephemeralPublicKey, 0);
|
|
481
|
+
result.set(encrypted, ephemeralPublicKey.byteLength);
|
|
482
|
+
return result;
|
|
483
|
+
},
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* A reader-side session that caches decapsulation per sender.
|
|
488
|
+
*
|
|
489
|
+
* The mirror of {@link createSealSession}: because every frame from one writer
|
|
490
|
+
* carries the same ephemeral public key, this turns an ECDH per frame into an
|
|
491
|
+
* ECDH per writer. Correctness does not depend on the writer having used a
|
|
492
|
+
* session: a stream of independently sealed payloads misses the cache
|
|
493
|
+
* on each new ephemeral key.
|
|
494
|
+
*
|
|
495
|
+
* The cache is keyed by the ephemeral public key and holds one entry, which is
|
|
496
|
+
* the common case (one writer per stream). A second writer evicts the first
|
|
497
|
+
* rather than growing without bound.
|
|
498
|
+
*/
|
|
499
|
+
export function createOpenSession(keyPair, aad) {
|
|
500
|
+
let cachedSender;
|
|
501
|
+
let cachedKey;
|
|
502
|
+
return {
|
|
503
|
+
async open(sealed) {
|
|
504
|
+
const minLength = X25519_KEY_LENGTH + NONCE_LENGTH + TAG_BYTES;
|
|
505
|
+
if (sealed.byteLength < minLength) {
|
|
506
|
+
throw new RuntimeDecryptionError(`Sealed payload too short: expected at least ${minLength} bytes, got ${sealed.byteLength}`, { context: { operation: 'decrypt', byteLength: sealed.byteLength } });
|
|
507
|
+
}
|
|
508
|
+
const ephemeralPublicKey = sealed.subarray(0, X25519_KEY_LENGTH);
|
|
509
|
+
const encrypted = sealed.subarray(X25519_KEY_LENGTH);
|
|
510
|
+
const sender = keyCacheId(ephemeralPublicKey);
|
|
511
|
+
if (sender !== cachedSender || !cachedKey) {
|
|
512
|
+
cachedSender = sender;
|
|
513
|
+
cachedKey = decapsulate(keyPair, ephemeralPublicKey);
|
|
514
|
+
}
|
|
515
|
+
let contentKey;
|
|
516
|
+
try {
|
|
517
|
+
contentKey = await cachedKey;
|
|
518
|
+
}
|
|
519
|
+
catch (error) {
|
|
520
|
+
// Do not let one bad frame poison the cache for subsequent frames.
|
|
521
|
+
cachedSender = undefined;
|
|
522
|
+
cachedKey = undefined;
|
|
523
|
+
throw error;
|
|
524
|
+
}
|
|
525
|
+
return aesGcmDecrypt(contentKey, encrypted, aad);
|
|
526
|
+
},
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
/** Stable cache key for a 32-byte public key. */
|
|
530
|
+
function keyCacheId(publicKey) {
|
|
531
|
+
let id = '';
|
|
532
|
+
for (let i = 0; i < publicKey.length; i++) {
|
|
533
|
+
id += publicKey[i].toString(16).padStart(2, '0');
|
|
534
|
+
}
|
|
535
|
+
return id;
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* Build the additional authenticated data that binds a sealed payload to a
|
|
539
|
+
* specific run.
|
|
540
|
+
*
|
|
541
|
+
* Mirrors the `info` used for symmetric per-run key derivation so the two
|
|
542
|
+
* schemes agree on what "this run" means.
|
|
543
|
+
*
|
|
544
|
+
* Note that the sealed-box construction already binds a payload to its
|
|
545
|
+
* recipient without any AAD: the content key is derived over
|
|
546
|
+
* `ephemeralPublicKey ‖ recipientPublicKey`, and a recipient public key is
|
|
547
|
+
* unique per (deployment key × project × run). Replaying a sealed payload at
|
|
548
|
+
* a different run therefore fails at key agreement regardless. AAD is
|
|
549
|
+
* available for callers that want an additional, KDF-independent binding:
|
|
550
|
+
* both sides must supply byte-identical values or the payload will not open.
|
|
551
|
+
*/
|
|
552
|
+
export function runAad(projectId, runId) {
|
|
553
|
+
return infoEncoder.encode(`${projectId}|${runId}`);
|
|
554
|
+
}
|
|
555
|
+
/**
|
|
556
|
+
* Decode a run's published public key, validating both encoding and length.
|
|
557
|
+
*
|
|
558
|
+
* Returns `undefined` when the value is absent, not valid base64, or not
|
|
559
|
+
* exactly 32 bytes. Callers treat that as "this run has no usable public key"
|
|
560
|
+
* and fall back to the symmetric path, so a corrupt or truncated stored value
|
|
561
|
+
* degrades to the pre-existing behavior instead of failing a resumption.
|
|
562
|
+
*/
|
|
563
|
+
export function decodeRunPublicKey(value) {
|
|
564
|
+
if (!value)
|
|
565
|
+
return undefined;
|
|
566
|
+
const bytes = base64ToBytes(value);
|
|
567
|
+
if (!bytes || bytes.byteLength !== X25519_KEY_LENGTH)
|
|
568
|
+
return undefined;
|
|
569
|
+
return bytes;
|
|
570
|
+
}
|
|
571
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VhbGVkLWJveC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9zZWFsZWQtYm94LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxzQkFBc0IsRUFBRSxvQkFBb0IsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBQ2hGLE9BQU8sRUFDTCxPQUFPLElBQUksYUFBYSxFQUN4QixPQUFPLElBQUksYUFBYSxFQUV4QixZQUFZLEVBQ1osU0FBUyxHQUNWLE1BQU0saUJBQWlCLENBQUM7QUFFekI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQXVGRztBQUVILG1FQUFtRTtBQUNuRSxNQUFNLGlCQUFpQixHQUFHLEVBQUUsQ0FBQztBQUU3Qix3RUFBd0U7QUFDeEUsTUFBTSxrQkFBa0IsR0FBRyxFQUFFLENBQUM7QUFFOUI7Ozs7Ozs7R0FPRztBQUNILE1BQU0sV0FBVyxHQUFHLHlCQUF5QixDQUFDO0FBRTlDOzs7O0dBSUc7QUFDSCxNQUFNLGdCQUFnQixHQUFHLDRCQUE0QixDQUFDO0FBRXREOzs7Ozs7Ozs7Ozs7Ozs7OztHQWlCRztBQUNILE1BQU0sbUJBQW1CLEdBQUcsSUFBSSxVQUFVLENBQUM7SUFDekMsSUFBSSxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsSUFBSTtJQUM1RSxJQUFJLEVBQUUsSUFBSSxFQUFFLElBQUk7Q0FDakIsQ0FBQyxDQUFDO0FBRUgsTUFBTSxXQUFXLEdBQUcsSUFBSSxXQUFXLEVBQUUsQ0FBQztBQWV0QyxTQUFTLHVCQUF1QixDQUFDLGNBQTBCO0lBQ3pELElBQUksY0FBYyxDQUFDLFVBQVUsS0FBSyxrQkFBa0IsRUFBRSxDQUFDO1FBQ3JELE1BQU0sSUFBSSxvQkFBb0IsQ0FDNUIsb0NBQW9DLGtCQUFrQixlQUFlLGNBQWMsQ0FBQyxVQUFVLEVBQUUsQ0FDakcsQ0FBQztJQUNKLENBQUM7QUFDSCxDQUFDO0FBRUQsU0FBUyxxQkFBcUIsQ0FBQyxTQUFxQixFQUFFLEtBQWE7SUFDakUsSUFBSSxTQUFTLENBQUMsVUFBVSxLQUFLLGlCQUFpQixFQUFFLENBQUM7UUFDL0MsTUFBTSxJQUFJLG9CQUFvQixDQUM1QixHQUFHLEtBQUssb0JBQW9CLGlCQUFpQixlQUFlLFNBQVMsQ0FBQyxVQUFVLEVBQUUsQ0FDbkYsQ0FBQztJQUNKLENBQUM7QUFDSCxDQUFDO0FBRUQ7Ozs7Ozs7OztHQVNHO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSxnQkFBZ0IsQ0FDcEMsY0FBMEI7SUFFMUIsdUJBQXVCLENBQUMsY0FBYyxDQUFDLENBQUM7SUFFeEMsTUFBTSxPQUFPLEdBQUcsTUFBTSxVQUFVLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQ3RELEtBQUssRUFDTCxjQUFjLEVBQ2QsTUFBTSxFQUNOLEtBQUssRUFDTCxDQUFDLFlBQVksQ0FBQyxDQUNmLENBQUM7SUFFRix5RUFBeUU7SUFDekUsdUVBQXVFO0lBQ3ZFLDJDQUEyQztJQUMzQyxNQUFNLFVBQVUsR0FBRyxNQUFNLFVBQVUsQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLFVBQVUsQ0FDMUQ7UUFDRSxJQUFJLEVBQUUsTUFBTTtRQUNaLElBQUksRUFBRSxTQUFTO1FBQ2YsSUFBSSxFQUFFLElBQUksVUFBVSxDQUFDLEVBQUUsQ0FBQztRQUN4QixJQUFJLEVBQUUsV0FBVyxDQUFDLE1BQU0sQ0FBQyxXQUFXLENBQUM7S0FDdEMsRUFDRCxPQUFPLEVBQ1AsaUJBQWlCLEdBQUcsQ0FBQyxDQUN0QixDQUFDO0lBQ0YsTUFBTSxNQUFNLEdBQUcsSUFBSSxVQUFVLENBQUMsVUFBVSxDQUFDLENBQUM7SUFFMUMscUVBQXFFO0lBQ3JFLHFFQUFxRTtJQUNyRSxzQkFBc0I7SUFDdEIsTUFBTSxTQUFTLEdBQUcsTUFBTSx5QkFBeUIsQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUMxRCxPQUFPLEVBQUUsTUFBTSxFQUFFLFNBQVMsRUFBRSxDQUFDO0FBQy9CLENBQUM7QUFFRDs7Ozs7OztHQU9HO0FBQ0gsS0FBSyxVQUFVLHlCQUF5QixDQUN0QyxNQUFrQjtJQUVsQixNQUFNLFVBQVUsR0FBRyxNQUFNLFlBQVksQ0FBQyxNQUFNLEVBQUUsaUJBQWlCLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDdEUsTUFBTSxHQUFHLEdBQUcsTUFBTSxVQUFVLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsS0FBSyxFQUFFLFVBQVUsQ0FBQyxDQUFDO0lBQ3hFLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLENBQUM7UUFDWCxNQUFNLElBQUksb0JBQW9CLENBQzVCLGdFQUFnRSxDQUNqRSxDQUFDO0lBQ0osQ0FBQztJQUNELE1BQU0sU0FBUyxHQUFHLGdCQUFnQixDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUMxQyx3RUFBd0U7SUFDeEUsMEVBQTBFO0lBQzFFLGdFQUFnRTtJQUNoRSxJQUFJLFNBQVMsQ0FBQyxVQUFVLEtBQUssaUJBQWlCLEVBQUUsQ0FBQztRQUMvQyxNQUFNLElBQUksb0JBQW9CLENBQzVCLGdDQUFnQyxTQUFTLENBQUMsVUFBVSw4QkFBOEIsaUJBQWlCLEVBQUUsQ0FDdEcsQ0FBQztJQUNKLENBQUM7SUFDRCxPQUFPLFNBQVMsQ0FBQztBQUNuQixDQUFDO0FBRUQsS0FBSyxVQUFVLFlBQVksQ0FDekIsTUFBa0IsRUFDbEIsV0FBb0I7SUFFcEIsTUFBTSxLQUFLLEdBQUcsSUFBSSxVQUFVLENBQUMsbUJBQW1CLENBQUMsTUFBTSxHQUFHLE1BQU0sQ0FBQyxVQUFVLENBQUMsQ0FBQztJQUM3RSxLQUFLLENBQUMsR0FBRyxDQUFDLG1CQUFtQixFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQ2xDLEtBQUssQ0FBQyxHQUFHLENBQUMsTUFBTSxFQUFFLG1CQUFtQixDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQzlDLE9BQU8sVUFBVSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUN2QyxPQUFPLEVBQ1AsS0FBSyxFQUNMLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRSxFQUNsQixXQUFXLEVBQ1gsQ0FBQyxZQUFZLENBQUMsQ0FDZixDQUFDO0FBQ0osQ0FBQztBQUVELFNBQVMsZUFBZSxDQUFDLFNBQXFCO0lBQzVDLE9BQU8sVUFBVSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUN2QyxLQUFLLEVBQ0wsU0FBUyxFQUNULEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRTtJQUNsQixpQkFBaUIsQ0FBQyxJQUFJO0lBQ3RCLG1FQUFtRTtJQUNuRSwyREFBMkQ7SUFDM0QsRUFBRSxDQUNILENBQUM7QUFDSixDQUFDO0FBRUQsTUFBTSxZQUFZLEdBQ2hCLGtFQUFrRSxDQUFDO0FBRXJFLE1BQU0sZUFBZSxHQUNuQixrRUFBa0UsQ0FBQztBQUVyRTs7Ozs7Ozs7R0FRRztBQUNILE1BQU0sVUFBVSxhQUFhLENBQUMsS0FBaUI7SUFDN0MsSUFBSSxHQUFHLEdBQUcsRUFBRSxDQUFDO0lBQ2IsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLEtBQUssQ0FBQyxNQUFNLEVBQUUsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDO1FBQ3pDLE1BQU0sRUFBRSxHQUFHLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNwQixNQUFNLEVBQUUsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLEtBQUssQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNuRCxNQUFNLEVBQUUsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLEtBQUssQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUVuRCxHQUFHLElBQUksWUFBWSxDQUFDLENBQUMsRUFBRSxJQUFJLENBQUMsQ0FBQyxHQUFHLElBQUksQ0FBQyxDQUFDO1FBQ3RDLEdBQUcsSUFBSSxZQUFZLENBQUMsQ0FBQyxDQUFDLEVBQUUsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSxJQUFJLENBQUMsQ0FBQyxHQUFHLElBQUksQ0FBQyxDQUFDLENBQUM7UUFDN0QsR0FBRztZQUNELENBQUMsR0FBRyxDQUFDLEdBQUcsS0FBSyxDQUFDLE1BQU07Z0JBQ2xCLENBQUMsQ0FBQyxZQUFZLENBQUMsQ0FBQyxDQUFDLEVBQUUsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSxJQUFJLENBQUMsQ0FBQyxHQUFHLElBQUksQ0FBQyxDQUFDO2dCQUN2RCxDQUFDLENBQUMsR0FBRyxDQUFDO1FBQ1YsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLEdBQUcsS0FBSyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsWUFBWSxDQUFDLEVBQUUsR0FBRyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDO0lBQzlELENBQUM7SUFDRCxPQUFPLEdBQUcsQ0FBQztBQUNiLENBQUM7QUFFRDs7Ozs7Ozs7Ozs7Ozs7R0FjRztBQUNILE1BQU0sVUFBVSxhQUFhLENBQUMsS0FBYTtJQUN6Qyx3RUFBd0U7SUFDeEUsSUFBSSxHQUFHLEdBQUcsS0FBSyxDQUFDLE1BQU0sQ0FBQztJQUN2QixPQUFPLEdBQUcsR0FBRyxDQUFDLElBQUksS0FBSyxDQUFDLEdBQUcsR0FBRyxDQUFDLENBQUMsS0FBSyxHQUFHO1FBQUUsR0FBRyxFQUFFLENBQUM7SUFDaEQsTUFBTSxJQUFJLEdBQUcsS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsR0FBRyxDQUFDLENBQUM7SUFDakMsSUFBSSxJQUFJLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQztRQUFFLE9BQU8sU0FBUyxDQUFDO0lBRXpDLHdFQUF3RTtJQUN4RSxJQUFJLElBQUksQ0FBQyxNQUFNLEdBQUcsQ0FBQyxLQUFLLENBQUM7UUFBRSxPQUFPLFNBQVMsQ0FBQztJQUM1QyxrRUFBa0U7SUFDbEUsSUFBSSxHQUFHLEtBQUssS0FBSyxDQUFDLE1BQU0sSUFBSSxLQUFLLENBQUMsTUFBTSxHQUFHLENBQUMsS0FBSyxDQUFDO1FBQUUsT0FBTyxTQUFTLENBQUM7SUFFckUsSUFBSSxXQUFXLEdBQUcsQ0FBQyxDQUFDO0lBQ3BCLElBQUksUUFBUSxHQUFHLENBQUMsQ0FBQztJQUNqQixNQUFNLEtBQUssR0FBYSxFQUFFLENBQUM7SUFFM0IsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQztRQUNyQyxNQUFNLEtBQUssR0FBRyxZQUFZLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQzVDLElBQUksS0FBSyxLQUFLLENBQUMsQ0FBQztZQUFFLE9BQU8sU0FBUyxDQUFDO1FBQ25DLFdBQVcsR0FBRyxDQUFDLFdBQVcsSUFBSSxDQUFDLENBQUMsR0FBRyxLQUFLLENBQUM7UUFDekMsUUFBUSxJQUFJLENBQUMsQ0FBQztRQUNkLElBQUksUUFBUSxJQUFJLENBQUMsRUFBRSxDQUFDO1lBQ2xCLFFBQVEsSUFBSSxDQUFDLENBQUM7WUFDZCxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsV0FBVyxJQUFJLFFBQVEsQ0FBQyxHQUFHLElBQUksQ0FBQyxDQUFDO1FBQy9DLENBQUM7SUFDSCxDQUFDO0lBRUQsd0VBQXdFO0lBQ3hFLElBQUksUUFBUSxHQUFHLENBQUMsSUFBSSxDQUFDLFdBQVcsR0FBRyxDQUFDLENBQUMsQ0FBQyxJQUFJLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUM7UUFDaEUsT0FBTyxTQUFTLENBQUM7SUFDbkIsQ0FBQztJQUVELE9BQU8sSUFBSSxVQUFVLENBQUMsS0FBSyxDQUFDLENBQUM7QUFDL0IsQ0FBQztBQUVEOzs7Ozs7OztHQVFHO0FBQ0gsU0FBUyxnQkFBZ0IsQ0FBQyxLQUFhO0lBQ3JDLElBQUksV0FBVyxHQUFHLENBQUMsQ0FBQztJQUNwQixJQUFJLFFBQVEsR0FBRyxDQUFDLENBQUM7SUFDakIsTUFBTSxLQUFLLEdBQWEsRUFBRSxDQUFDO0lBRTNCLEtBQUssSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUM7UUFDdEMsTUFBTSxJQUFJLEdBQUcsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ3RCLElBQUksSUFBSSxLQUFLLEdBQUc7WUFBRSxNQUFNO1FBQ3hCLE1BQU0sS0FBSyxHQUFHLGVBQWUsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDNUMsSUFBSSxLQUFLLEtBQUssQ0FBQyxDQUFDLEVBQUUsQ0FBQztZQUNqQixNQUFNLElBQUksb0JBQW9CLENBQzVCLGdDQUFnQyxJQUFJLCtCQUErQixDQUNwRSxDQUFDO1FBQ0osQ0FBQztRQUNELFdBQVcsR0FBRyxDQUFDLFdBQVcsSUFBSSxDQUFDLENBQUMsR0FBRyxLQUFLLENBQUM7UUFDekMsUUFBUSxJQUFJLENBQUMsQ0FBQztRQUNkLElBQUksUUFBUSxJQUFJLENBQUMsRUFBRSxDQUFDO1lBQ2xCLFFBQVEsSUFBSSxDQUFDLENBQUM7WUFDZCxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsV0FBVyxJQUFJLFFBQVEsQ0FBQyxHQUFHLElBQUksQ0FBQyxDQUFDO1FBQy9DLENBQUM7SUFDSCxDQUFDO0lBRUQsT0FBTyxJQUFJLFVBQVUsQ0FBQyxLQUFLLENBQUMsQ0FBQztBQUMvQixDQUFDO0FBRUQ7OztHQUdHO0FBQ0gsS0FBSyxVQUFVLGdCQUFnQixDQUM3QixZQUF3QixFQUN4QixrQkFBOEIsRUFDOUIsa0JBQThCLEVBQzlCLE1BQTRDO0lBRTVDLE1BQU0sS0FBSyxHQUFHLFdBQVcsQ0FBQyxNQUFNLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztJQUNuRCxNQUFNLElBQUksR0FBRyxJQUFJLFVBQVUsQ0FDekIsS0FBSyxDQUFDLE1BQU0sR0FBRyxrQkFBa0IsQ0FBQyxNQUFNLEdBQUcsa0JBQWtCLENBQUMsTUFBTSxDQUNyRSxDQUFDO0lBQ0YsSUFBSSxDQUFDLEdBQUcsQ0FBQyxLQUFLLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDbkIsSUFBSSxDQUFDLEdBQUcsQ0FBQyxrQkFBa0IsRUFBRSxLQUFLLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDM0MsSUFBSSxDQUFDLEdBQUcsQ0FBQyxrQkFBa0IsRUFBRSxLQUFLLENBQUMsTUFBTSxHQUFHLGtCQUFrQixDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBRXZFLE1BQU0sT0FBTyxHQUFHLE1BQU0sVUFBVSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUN0RCxLQUFLLEVBQ0wsWUFBWSxFQUNaLE1BQU0sRUFDTixLQUFLLEVBQ0wsQ0FBQyxZQUFZLENBQUMsQ0FDZixDQUFDO0lBQ0YsTUFBTSxjQUFjLEdBQUcsTUFBTSxVQUFVLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxVQUFVLENBQzlELEVBQUUsSUFBSSxFQUFFLE1BQU0sRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLElBQUksRUFBRSxJQUFJLFVBQVUsQ0FBQyxFQUFFLENBQUMsRUFBRSxJQUFJLEVBQUUsRUFDakUsT0FBTyxFQUNQLGtCQUFrQixHQUFHLENBQUMsQ0FDdkIsQ0FBQztJQUVGLE9BQU8sVUFBVSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUN2QyxLQUFLLEVBQ0wsSUFBSSxVQUFVLENBQUMsY0FBYyxDQUFDLEVBQzlCLFNBQVMsRUFDVCxLQUFLO0lBQ0wsd0VBQXdFO0lBQ3hFLGtFQUFrRTtJQUNsRSxNQUFtQyxDQUNwQyxDQUFDO0FBQ0osQ0FBQztBQUVEOzs7Ozs7Ozs7Ozs7Ozs7O0dBZ0JHO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSxXQUFXLENBQUMsa0JBQThCO0lBSTlELHFCQUFxQixDQUFDLGtCQUFrQixFQUFFLHNCQUFzQixDQUFDLENBQUM7SUFFbEUsSUFBSSxZQUF1QixDQUFDO0lBQzVCLElBQUksQ0FBQztRQUNILFlBQVksR0FBRyxNQUFNLGVBQWUsQ0FBQyxrQkFBa0IsQ0FBQyxDQUFDO0lBQzNELENBQUM7SUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1FBQ2YsTUFBTSxJQUFJLG9CQUFvQixDQUM1Qix3Q0FBd0MsS0FBSyxZQUFZLEtBQUssQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxFQUFFLEVBQ2hHLEVBQUUsS0FBSyxFQUFFLENBQ1YsQ0FBQztJQUNKLENBQUM7SUFFRCxNQUFNLFNBQVMsR0FBRyxDQUFDLE1BQU0sVUFBVSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsV0FBVyxDQUMzRCxFQUFFLElBQUksRUFBRSxRQUFRLEVBQUU7SUFDbEIsaUJBQWlCLENBQUMsSUFBSSxFQUN0QixDQUFDLFlBQVksQ0FBQyxDQUNmLENBQW9ELENBQUM7SUFFdEQsTUFBTSxrQkFBa0IsR0FBRyxJQUFJLFVBQVUsQ0FDdkMsTUFBTSxVQUFVLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsS0FBSyxFQUFFLFNBQVMsQ0FBQyxTQUFTLENBQUMsQ0FDckUsQ0FBQztJQUVGLElBQUksZ0JBQTZCLENBQUM7SUFDbEMsSUFBSSxDQUFDO1FBQ0gsZ0JBQWdCLEdBQUcsTUFBTSxVQUFVLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxVQUFVLENBQzFELEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRSxNQUFNLEVBQUUsWUFBWSxFQUFFLEVBQ3hDLFNBQVMsQ0FBQyxVQUFVLEVBQ3BCLGlCQUFpQixHQUFHLENBQUMsQ0FDdEIsQ0FBQztJQUNKLENBQUM7SUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1FBQ2YsdUVBQXVFO1FBQ3ZFLHFFQUFxRTtRQUNyRSxxRUFBcUU7UUFDckUsa0JBQWtCO1FBQ2xCLE1BQU0sSUFBSSxvQkFBb0IsQ0FDNUIsdUVBQXVFO1lBQ3JFLHlEQUF5RCxFQUMzRCxFQUFFLEtBQUssRUFBRSxDQUNWLENBQUM7SUFDSixDQUFDO0lBRUQsTUFBTSxVQUFVLEdBQUcsTUFBTSxnQkFBZ0IsQ0FDdkMsSUFBSSxVQUFVLENBQUMsZ0JBQWdCLENBQUMsRUFDaEMsa0JBQWtCLEVBQ2xCLGtCQUFrQixFQUNsQixDQUFDLFNBQVMsQ0FBQyxDQUNaLENBQUM7SUFFRixPQUFPLEVBQUUsa0JBQWtCLEVBQUUsVUFBVSxFQUFFLENBQUM7QUFDNUMsQ0FBQztBQUVEOzs7Ozs7OztHQVFHO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSxXQUFXLENBQy9CLE9BQW1CLEVBQ25CLGtCQUE4QjtJQUU5QixJQUFJLGtCQUFrQixDQUFDLFVBQVUsS0FBSyxpQkFBaUIsRUFBRSxDQUFDO1FBQ3hELE1BQU0sSUFBSSxzQkFBc0IsQ0FDOUIsaUVBQWlFLGlCQUFpQixlQUFlLGtCQUFrQixDQUFDLFVBQVUsRUFBRSxFQUNoSTtZQUNFLE9BQU8sRUFBRTtnQkFDUCxTQUFTLEVBQUUsU0FBUztnQkFDcEIsVUFBVSxFQUFFLGtCQUFrQixDQUFDLFVBQVU7YUFDMUM7U0FDRixDQUNGLENBQUM7SUFDSixDQUFDO0lBRUQsSUFBSSxnQkFBNkIsQ0FBQztJQUNsQyxJQUFJLENBQUM7UUFDSCxNQUFNLENBQUMsVUFBVSxFQUFFLFNBQVMsQ0FBQyxHQUFHLE1BQU0sT0FBTyxDQUFDLEdBQUcsQ0FBQztZQUNoRCxZQUFZLENBQUMsT0FBTyxDQUFDLE1BQU0sRUFBRSxpQkFBaUIsQ0FBQyxLQUFLLENBQUM7WUFDckQsZUFBZSxDQUFDLGtCQUFrQixDQUFDO1NBQ3BDLENBQUMsQ0FBQztRQUNILGdCQUFnQixHQUFHLE1BQU0sVUFBVSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsVUFBVSxDQUMxRCxFQUFFLElBQUksRUFBRSxRQUFRLEVBQUUsTUFBTSxFQUFFLFNBQVMsRUFBRSxFQUNyQyxVQUFVLEVBQ1YsaUJBQWlCLEdBQUcsQ0FBQyxDQUN0QixDQUFDO0lBQ0osQ0FBQztJQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7UUFDZixNQUFNLElBQUksc0JBQXNCLENBQzlCLCtEQUErRCxLQUFLLFlBQVksS0FBSyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLEVBQUUsRUFDdkg7WUFDRSxLQUFLO1lBQ0wsT0FBTyxFQUFFO2dCQUNQLFNBQVMsRUFBRSxTQUFTO2dCQUNwQixVQUFVLEVBQUUsa0JBQWtCLENBQUMsVUFBVTthQUMxQztTQUNGLENBQ0YsQ0FBQztJQUNKLENBQUM7SUFFRCxPQUFPLGdCQUFnQixDQUNyQixJQUFJLFVBQVUsQ0FBQyxnQkFBZ0IsQ0FBQyxFQUNoQyxrQkFBa0IsRUFDbEIsT0FBTyxDQUFDLFNBQVMsRUFDakIsQ0FBQyxTQUFTLENBQUMsQ0FDWixDQUFDO0FBQ0osQ0FBQztBQUVEOzs7Ozs7Ozs7Ozs7O0dBYUc7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLElBQUksQ0FDeEIsa0JBQThCLEVBQzlCLElBQWdCLEVBQ2hCLEdBQWdCO0lBRWhCLE1BQU0sRUFBRSxrQkFBa0IsRUFBRSxVQUFVLEVBQUUsR0FDdEMsTUFBTSxXQUFXLENBQUMsa0JBQWtCLENBQUMsQ0FBQztJQUN4QyxNQUFNLFNBQVMsR0FBRyxNQUFNLGFBQWEsQ0FBQyxVQUFVLEVBQUUsSUFBSSxFQUFFLEdBQUcsQ0FBQyxDQUFDO0lBRTdELE1BQU0sTUFBTSxHQUFHLElBQUksVUFBVSxDQUMzQixrQkFBa0IsQ0FBQyxVQUFVLEdBQUcsU0FBUyxDQUFDLFVBQVUsQ0FDckQsQ0FBQztJQUNGLE1BQU0sQ0FBQyxHQUFHLENBQUMsa0JBQWtCLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDbEMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxTQUFTLEVBQUUsa0JBQWtCLENBQUMsVUFBVSxDQUFDLENBQUM7SUFDckQsT0FBTyxNQUFNLENBQUM7QUFDaEIsQ0FBQztBQUVEOzs7Ozs7O0dBT0c7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLElBQUksQ0FDeEIsT0FBbUIsRUFDbkIsTUFBa0IsRUFDbEIsR0FBZ0I7SUFFaEIsc0RBQXNEO0lBQ3RELE1BQU0sU0FBUyxHQUFHLGlCQUFpQixHQUFHLFlBQVksR0FBRyxTQUFTLENBQUM7SUFDL0QsSUFBSSxNQUFNLENBQUMsVUFBVSxHQUFHLFNBQVMsRUFBRSxDQUFDO1FBQ2xDLE1BQU0sSUFBSSxzQkFBc0IsQ0FDOUIsK0NBQStDLFNBQVMsZUFBZSxNQUFNLENBQUMsVUFBVSxFQUFFLEVBQzFGLEVBQUUsT0FBTyxFQUFFLEVBQUUsU0FBUyxFQUFFLFNBQVMsRUFBRSxVQUFVLEVBQUUsTUFBTSxDQUFDLFVBQVUsRUFBRSxFQUFFLENBQ3JFLENBQUM7SUFDSixDQUFDO0lBRUQsTUFBTSxrQkFBa0IsR0FBRyxNQUFNLENBQUMsUUFBUSxDQUFDLENBQUMsRUFBRSxpQkFBaUIsQ0FBQyxDQUFDO0lBQ2pFLE1BQU0sU0FBUyxHQUFHLE1BQU0sQ0FBQyxRQUFRLENBQUMsaUJBQWlCLENBQUMsQ0FBQztJQUNyRCxNQUFNLFVBQVUsR0FBRyxNQUFNLFdBQVcsQ0FBQyxPQUFPLEVBQUUsa0JBQWtCLENBQUMsQ0FBQztJQUNsRSxPQUFPLGFBQWEsQ0FBQyxVQUFVLEVBQUUsU0FBUyxFQUFFLEdBQUcsQ0FBQyxDQUFDO0FBQ25ELENBQUM7QUFFRDs7Ozs7Ozs7Ozs7Ozs7OztHQWdCRztBQUNILE1BQU0sVUFBVSxpQkFBaUIsQ0FDL0Isa0JBQThCLEVBQzlCLEdBQWdCO0lBRWhCLElBQUksYUFFUyxDQUFDO0lBRWQsT0FBTztRQUNMLEtBQUssQ0FBQyxJQUFJLENBQUMsSUFBZ0I7WUFDekIsdUVBQXVFO1lBQ3ZFLG9CQUFvQjtZQUNwQixhQUFhLEtBQUssV0FBVyxDQUFDLGtCQUFrQixDQUFDLENBQUM7WUFDbEQsTUFBTSxFQUFFLGtCQUFrQixFQUFFLFVBQVUsRUFBRSxHQUFHLE1BQU0sYUFBYSxDQUFDO1lBQy9ELE1BQU0sU0FBUyxHQUFHLE1BQU0sYUFBYSxDQUFDLFVBQVUsRUFBRSxJQUFJLEVBQUUsR0FBRyxDQUFDLENBQUM7WUFFN0QsTUFBTSxNQUFNLEdBQUcsSUFBSSxVQUFVLENBQzNCLGtCQUFrQixDQUFDLFVBQVUsR0FBRyxTQUFTLENBQUMsVUFBVSxDQUNyRCxDQUFDO1lBQ0YsTUFBTSxDQUFDLEdBQUcsQ0FBQyxrQkFBa0IsRUFBRSxDQUFDLENBQUMsQ0FBQztZQUNsQyxNQUFNLENBQUMsR0FBRyxDQUFDLFNBQVMsRUFBRSxrQkFBa0IsQ0FBQyxVQUFVLENBQUMsQ0FBQztZQUNyRCxPQUFPLE1BQU0sQ0FBQztRQUNoQixDQUFDO0tBQ0YsQ0FBQztBQUNKLENBQUM7QUFFRDs7Ozs7Ozs7Ozs7O0dBWUc7QUFDSCxNQUFNLFVBQVUsaUJBQWlCLENBQy9CLE9BQW1CLEVBQ25CLEdBQWdCO0lBRWhCLElBQUksWUFBZ0MsQ0FBQztJQUNyQyxJQUFJLFNBQXlDLENBQUM7SUFFOUMsT0FBTztRQUNMLEtBQUssQ0FBQyxJQUFJLENBQUMsTUFBa0I7WUFDM0IsTUFBTSxTQUFTLEdBQUcsaUJBQWlCLEdBQUcsWUFBWSxHQUFHLFNBQVMsQ0FBQztZQUMvRCxJQUFJLE1BQU0sQ0FBQyxVQUFVLEdBQUcsU0FBUyxFQUFFLENBQUM7Z0JBQ2xDLE1BQU0sSUFBSSxzQkFBc0IsQ0FDOUIsK0NBQStDLFNBQVMsZUFBZSxNQUFNLENBQUMsVUFBVSxFQUFFLEVBQzFGLEVBQUUsT0FBTyxFQUFFLEVBQUUsU0FBUyxFQUFFLFNBQVMsRUFBRSxVQUFVLEVBQUUsTUFBTSxDQUFDLFVBQVUsRUFBRSxFQUFFLENBQ3JFLENBQUM7WUFDSixDQUFDO1lBRUQsTUFBTSxrQkFBa0IsR0FBRyxNQUFNLENBQUMsUUFBUSxDQUFDLENBQUMsRUFBRSxpQkFBaUIsQ0FBQyxDQUFDO1lBQ2pFLE1BQU0sU0FBUyxHQUFHLE1BQU0sQ0FBQyxRQUFRLENBQUMsaUJBQWlCLENBQUMsQ0FBQztZQUVyRCxNQUFNLE1BQU0sR0FBRyxVQUFVLENBQUMsa0JBQWtCLENBQUMsQ0FBQztZQUM5QyxJQUFJLE1BQU0sS0FBSyxZQUFZLElBQUksQ0FBQyxTQUFTLEVBQUUsQ0FBQztnQkFDMUMsWUFBWSxHQUFHLE1BQU0sQ0FBQztnQkFDdEIsU0FBUyxHQUFHLFdBQVcsQ0FBQyxPQUFPLEVBQUUsa0JBQWtCLENBQUMsQ0FBQztZQUN2RCxDQUFDO1lBRUQsSUFBSSxVQUFxQixDQUFDO1lBQzFCLElBQUksQ0FBQztnQkFDSCxVQUFVLEdBQUcsTUFBTSxTQUFTLENBQUM7WUFDL0IsQ0FBQztZQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7Z0JBQ2YsbUVBQW1FO2dCQUNuRSxZQUFZLEdBQUcsU0FBUyxDQUFDO2dCQUN6QixTQUFTLEdBQUcsU0FBUyxDQUFDO2dCQUN0QixNQUFNLEtBQUssQ0FBQztZQUNkLENBQUM7WUFFRCxPQUFPLGFBQWEsQ0FBQyxVQUFVLEVBQUUsU0FBUyxFQUFFLEdBQUcsQ0FBQyxDQUFDO1FBQ25ELENBQUM7S0FDRixDQUFDO0FBQ0osQ0FBQztBQUVELGlEQUFpRDtBQUNqRCxTQUFTLFVBQVUsQ0FBQyxTQUFxQjtJQUN2QyxJQUFJLEVBQUUsR0FBRyxFQUFFLENBQUM7SUFDWixLQUFLLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsU0FBUyxDQUFDLE1BQU0sRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDO1FBQzFDLEVBQUUsSUFBSSxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLEVBQUUsR0FBRyxDQUFDLENBQUM7SUFDbkQsQ0FBQztJQUNELE9BQU8sRUFBRSxDQUFDO0FBQ1osQ0FBQztBQUVEOzs7Ozs7Ozs7Ozs7OztHQWNHO0FBQ0gsTUFBTSxVQUFVLE1BQU0sQ0FBQyxTQUFpQixFQUFFLEtBQWE7SUFDckQsT0FBTyxXQUFXLENBQUMsTUFBTSxDQUFDLEdBQUcsU0FBUyxJQUFJLEtBQUssRUFBRSxDQUFDLENBQUM7QUFDckQsQ0FBQztBQUVEOzs7Ozs7O0dBT0c7QUFDSCxNQUFNLFVBQVUsa0JBQWtCLENBQ2hDLEtBQXlCO0lBRXpCLElBQUksQ0FBQyxLQUFLO1FBQUUsT0FBTyxTQUFTLENBQUM7SUFDN0IsTUFBTSxLQUFLLEdBQUcsYUFBYSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQ25DLElBQUksQ0FBQyxLQUFLLElBQUksS0FBSyxDQUFDLFVBQVUsS0FBSyxpQkFBaUI7UUFBRSxPQUFPLFNBQVMsQ0FBQztJQUN2RSxPQUFPLEtBQUssQ0FBQztBQUNmLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBSdW50aW1lRGVjcnlwdGlvbkVycm9yLCBXb3JrZmxvd1J1bnRpbWVFcnJvciB9IGZyb20gJ0B3b3JrZmxvdy9lcnJvcnMnO1xuaW1wb3J0IHtcbiAgZGVjcnlwdCBhcyBhZXNHY21EZWNyeXB0LFxuICBlbmNyeXB0IGFzIGFlc0djbUVuY3J5cHQsXG4gIHR5cGUgQ3J5cHRvS2V5LFxuICBOT05DRV9MRU5HVEgsXG4gIFRBR19CWVRFUyxcbn0gZnJvbSAnLi9lbmNyeXB0aW9uLmpzJztcblxuLyoqXG4gKiBCcm93c2VyLWNvbXBhdGlibGUgc2VhbGVkLWJveCAoYXN5bW1ldHJpYykgZW5jcnlwdGlvbiBmb3IgY3Jvc3MtcnVuIHdyaXRlcy5cbiAqXG4gKiAjIyBXaHkgdGhpcyBleGlzdHNcbiAqXG4gKiBUaGUgc3ltbWV0cmljIGBlbmNyYCBwYXRoIHJlcXVpcmVzIHRoZSB3cml0ZXIgdG8gaG9sZCB0aGUgcmVjaXBpZW50IHJ1bidzXG4gKiBrZXksIHdoaWNoIGFsc28gZ3JhbnRzIGRlY3J5cHQgY2FwYWJpbGl0eS4gRm9yICpjcm9zcy1ydW4qIHdyaXRlcyAoYSBob29rXG4gKiByZXN1bXB0aW9uIHRhcmdldGluZyBhbm90aGVyIHJ1biwgb3IgYSBjaGlsZCB3b3JrZmxvdyB3cml0aW5nIGludG8gYVxuICogcGFyZW50J3MgZm9yd2FyZGVkIGBXcml0YWJsZVN0cmVhbWApIHRoYXQgaXMgbW9yZSBhdXRob3JpdHkgdGhhbiB0aGUgd3JpdGVyXG4gKiBuZWVkcywgYW5kIG9idGFpbmluZyB0aGUgc3ltbWV0cmljIGtleSBhY3Jvc3MgYSBkZXBsb3ltZW50IGJvdW5kYXJ5IGNvc3RzIGFcbiAqIHJvdW5kIHRyaXAgdG8gdGhlIFZlcmNlbCBBUEkuXG4gKlxuICogQSBzZWFsZWQgYm94IGZpeGVzIGJvdGg6IHRoZSB3cml0ZXIgbmVlZHMgb25seSB0aGUgcmVjaXBpZW50J3MgKipwdWJsaWMqKlxuICoga2V5LCB3aGljaCBpcyBub3Qgc2VjcmV0IGFuZCB0aGVyZWZvcmUgdHJhdmVscyBvbiB0aGUgcnVuIGVudGl0eSAoYW5kIGluXG4gKiBzdHJlYW0gZGVzY3JpcHRvcnMpIGF0IG5vIGNvc3QuIFwiRW5jcnlwdC1vbmx5XCIgc3RvcHMgYmVpbmcgYW4gaG9ub3Itc3lzdGVtXG4gKiBjb252ZW50aW9uIGVuZm9yY2VkIGJ5IGBpbXBvcnRLZXkocmF3LCBbJ2VuY3J5cHQnXSlgIGFuZCBiZWNvbWVzIGFcbiAqIGNyeXB0b2dyYXBoaWMgZ3VhcmFudGVlOiBhIHdyaXRlciBob2xkaW5nIG9ubHkgdGhlIHB1YmxpYyBrZXkgcHJvdmFibHlcbiAqIGNhbm5vdCByZWFkIGFueXRoaW5nLlxuICpcbiAqICMjIEtleSBoaWVyYXJjaHlcbiAqXG4gKiBCb3RoIGtleXMgZGVzY2VuZCBmcm9tIHRoZSBzYW1lIHBlci1ydW4ga2V5IG1hdGVyaWFsIGBLYCB0aGF0XG4gKiBgV29ybGQuZ2V0RW5jcnlwdGlvbktleUZvclJ1bigpYCBhbHJlYWR5IHJldHVybnMgdG9kYXksIHNvIG5vdGhpbmcgYWJvdXRcbiAqIGtleSBhY3F1aXNpdGlvbiwgdGhlIFdvcmxkIGludGVyZmFjZSwgb3IgdGhlIFZlcmNlbCBBUEkgY2hhbmdlczpcbiAqXG4gKiBgYGBcbiAqIEsgICgzMiBieXRlcywgcGVyLXJ1biwgPSBIS0RGKFZFUkNFTF9ERVBMT1lNRU5UX0tFWSwgXCJwcm9qZWN0SWR8cnVuSWRcIikpXG4gKiDilJzilIDilIAgQUVTLTI1NiBrZXkgPSBLIHVzZWQgZGlyZWN0bHkgICAgICAgICAgICDihpIgJ2VuY3InIGVudmVsb3BlcyAodW5jaGFuZ2VkKVxuICog4pSU4pSA4pSAIFgyNTUxOSBzY2FsYXIgPSBIS0RGKEssIFNDQUxBUl9JTkZPKSAgICAg4oaSICdlbmNwJyBlbnZlbG9wZXNcbiAqICAgICDilJTilIDilIAgcHVibGljIGtleSA9IFgyNTUxOSBiYXNlcG9pbnQgwrcgc2NhbGFyICAgKHB1Ymxpc2hlZCwgbm90IHNlY3JldClcbiAqIGBgYFxuICpcbiAqIGBLYCBpcyB0aGVyZWZvcmUgYm90aCBhbiBBRUFEIGtleSBhbmQgYSBrZXktZGVyaXZhdGlvbiBrZXkuIFRoaXMgaXMgc291bmQ6XG4gKiBBRVMtR0NNIG5ldmVyIGV4cG9zZXMgaXRzIGtleSwgYW5kIEhLREYtU0hBMjU2IG91dHB1dCBpcyBjb21wdXRhdGlvbmFsbHlcbiAqIGluZGVwZW5kZW50IG9mIHRoZSBBRVMga2V5c3RyZWFtLiBBIHN0cmljdCByZWFkaW5nIG9mIE5JU1QgU1AgODAwLTEwOFxuICogcHJlZmVycyB0aGVzZSByb2xlcyBiZSBzZXBhcmF0ZWQsIGJ1dCBgS2AncyBkaXJlY3QgQUVTIHVzZSBpcyBmaXhlZCBieVxuICogYmFja3dhcmQgY29tcGF0aWJpbGl0eSB3aXRoIGV2ZXJ5IGBlbmNyYCBwYXlsb2FkIGFscmVhZHkgd3JpdHRlbi5cbiAqXG4gKiAjIyBDb25zdHJ1Y3Rpb25cbiAqXG4gKiBBbiBFQ0lFUy1zdHlsZSBzZWFsZWQgYm94IHVzaW5nIHRoZSBzYW1lIHByaW1pdGl2ZXMgYXMgSFBLRSAoUkZDIDkxODApXG4gKiBiYXNlIG1vZGUgd2l0aCBESEtFTShYMjU1MTksIEhLREYtU0hBMjU2KSBhbmQgQUVTLTI1Ni1HQ00sIGJ1dCB3aXRob3V0IHRoZVxuICogZnVsbCBgTGFiZWxlZEV4dHJhY3RgL2BMYWJlbGVkRXhwYW5kYCBzdWl0ZS1JRCBjZXJlbW9ueTpcbiAqXG4gKiBgYGBcbiAqIGVwaGVtZXJhbCAgICA9IFgyNTUxOSBrZXlwYWlyLCBmcmVzaGx5IGdlbmVyYXRlZCBwZXIgZW5jcnlwdGlvbiBjb250ZXh0XG4gKiBzaGFyZWRTZWNyZXQgPSBYMjU1MTkoZXBoZW1lcmFsUHJpdmF0ZSwgcmVjaXBpZW50UHVibGljKVxuICogY29udGVudEtleSAgID0gSEtERi1TSEEyNTYoXG4gKiAgICAgICAgICAgICAgICAgIGlrbSAgPSBzaGFyZWRTZWNyZXQsXG4gKiAgICAgICAgICAgICAgICAgIHNhbHQgPSB6ZXJvcygzMiksXG4gKiAgICAgICAgICAgICAgICAgIGluZm8gPSBDT05URU5UX0tFWV9JTkZPIOKAliBlcGhlbWVyYWxQdWJsaWMg4oCWIHJlY2lwaWVudFB1YmxpYyxcbiAqICAgICAgICAgICAgICAgIClcbiAqIGBgYFxuICpcbiAqIEJpbmRpbmcgYm90aCBwdWJsaWMga2V5cyBpbnRvIGBpbmZvYCBpcyB0aGUgc2VjdXJpdHktY3JpdGljYWwgcGFydCAoaXQgaXNcbiAqIEhQS0UncyBga2VtX2NvbnRleHRgKTogaXQgcHJldmVudHMga2V5LXN1YnN0aXR1dGlvbi91bmtub3duLWtleS1zaGFyZVxuICogYXR0YWNrcyBpbiB3aGljaCBhbiBhZHZlcnNhcnkgcmVwbGF5cyBhIGNpcGhlcnRleHQgYXMgdGhvdWdoIGl0IGhhZCBiZWVuXG4gKiBzZWFsZWQgdG8gYSBkaWZmZXJlbnQgcmVjaXBpZW50LiBEZXZpYXRpbmcgZnJvbSBzdHJpY3QgUkZDIDkxODAgZnJhbWluZyBpc1xuICogYSBkZWxpYmVyYXRlLCBkb2N1bWVudGVkIGNob2ljZTogdGhlIGVudmVsb3BlIGlzIHZlcnNpb25lZCB2aWEgaXRzIGZvcm1hdFxuICogcHJlZml4IGFuZCB0aGUgSEtERiBsYWJlbHMgYmVsb3csIHNvIGEgY29uZm9ybWFudCBwcm9maWxlIGNhbiBiZSBhZGRlZFxuICogbGF0ZXIgYXMgYSBuZXcgdmVyc2lvbiB3aXRob3V0IHRvdWNoaW5nIGV4aXN0aW5nIHBheWxvYWRzLlxuICpcbiAqIFdpcmUgZm9ybWF0ICh0aGUgYGVuY3BgIGZvcm1hdCBwcmVmaXggaXMgYXR0YWNoZWQgYnkgdGhlIHNlcmlhbGl6YXRpb25cbiAqIGxheWVyLCBub3QgYnkgdGhpcyBtb2R1bGUpOlxuICpcbiAqIGBgYFxuICogW2VwaGVtZXJhbCBwdWJsaWMga2V5ICgzMiBieXRlcyldW25vbmNlICgxMiBieXRlcyldW2NpcGhlcnRleHQgKyBHQ00gdGFnICgxNiBieXRlcyldXG4gKiBgYGBcbiAqXG4gKiAjIyBOb25jZSBkaXNjaXBsaW5lXG4gKlxuICogT25lLXNob3Qge0BsaW5rIHNlYWx9IGdlbmVyYXRlcyBhIGZyZXNoIGVwaGVtZXJhbCBrZXlwYWlyIChhbmQgdGhlcmVmb3JlIGFcbiAqIGZyZXNoIGNvbnRlbnQga2V5KSBmb3IgZXZlcnkgY2FsbCwgc28gbm9uY2UgcmV1c2UgaXMgaW1wb3NzaWJsZS5cbiAqXG4gKiBDYWxsZXJzIHRoYXQgYW1vcnRpemUgdGhlIEtFTSBhY3Jvc3MgbWFueSBmcmFtZXMgdmlhIHtAbGluayBlbmNhcHN1bGF0ZX1cbiAqIHRha2Ugb24gbm9uY2UgZGlzY2lwbGluZSB0aGVtc2VsdmVzLCBiZWNhdXNlIHRoZSBjb250ZW50IGtleSB0aGVuIG91dGxpdmVzIGFcbiAqIHNpbmdsZSBtZXNzYWdlLiBUd28gcnVsZXMgYXBwbHk6XG4gKlxuICogMS4gRXZlcnkgZnJhbWUgTVVTVCB1c2UgYSBmcmVzaCByYW5kb20gbm9uY2UgKHdoaWNoIGBhZXNHY21FbmNyeXB0YCBkb2VzKS5cbiAqICAgIE5ldmVyIGEgY291bnRlcjogYSBjb3VudGVyIHJlc3RhcnRzIGF0IHplcm8gYWZ0ZXIgYSBzdHJlYW0gcmVjb25uZWN0IG9yIGFcbiAqICAgIGR1cmFibGUgcmVwbGF5LCByZXVzaW5nIGAoY29udGVudEtleSwgbm9uY2UpYCwgd2hpY2ggdW5kZXIgQUVTLUdDTSBsZWFrc1xuICogICAgdGhlIHBsYWludGV4dCBYT1Igb2YgdGhlIHR3byBmcmFtZXMgYW5kIHRoZSBHQ00gYXV0aCBzdWJrZXkuXG4gKiAyLiBBIHdyaXRlciBtdXN0IG5vdCBpbmhlcml0IGEgcHJldmlvdXMgaW5jYXJuYXRpb24ncyBjb250ZW50IGtleSwgc29cbiAqICAgIHtAbGluayBlbmNhcHN1bGF0ZX0gaXMgcmUtcnVuIHBlciB3cml0ZXIgaW5zdGFuY2UuXG4gKlxuICogTm90aGluZyBpbiB0aGlzIG1vZHVsZSBlbmZvcmNlcyBlaXRoZXIgcnVsZTsgaXQgaXMgdGhlIHN0cmVhbWluZyBjYWxsZXInc1xuICogY29udHJhY3QuXG4gKi9cblxuLyoqIExlbmd0aCBvZiBhbiBYMjU1MTkgcHJpdmF0ZSBzY2FsYXIgYW5kIHB1YmxpYyBrZXksIGluIGJ5dGVzLiAqL1xuY29uc3QgWDI1NTE5X0tFWV9MRU5HVEggPSAzMjtcblxuLyoqIExlbmd0aCBvZiB0aGUgQUVTLTI1NiBjb250ZW50IGtleSBkZXJpdmVkIGZyb20gdGhlIHNoYXJlZCBzZWNyZXQuICovXG5jb25zdCBDT05URU5UX0tFWV9MRU5HVEggPSAzMjtcblxuLyoqXG4gKiBIS0RGIGBpbmZvYCBsYWJlbCBmb3IgZGVyaXZpbmcgdGhlIHBlci1ydW4gWDI1NTE5IHNjYWxhciBmcm9tIHRoZSBwZXItcnVuXG4gKiBrZXkgbWF0ZXJpYWwuXG4gKlxuICogVmVyc2lvbmVkIGFuZCBuYW1lc3BhY2VkIHNvIHRoYXQgYW55IGZ1dHVyZSBkZXJpdmF0aW9uIGZyb20gdGhlIHNhbWUgYEtgXG4gKiAoYSBzZWNvbmQga2V5cGFpciwgYSBNQUMga2V5LCBhIGRpZmZlcmVudCBjdXJ2ZSkgY2FuIG5ldmVyIGNvbGxpZGUgd2l0aFxuICogdGhpcyBvbmUuXG4gKi9cbmNvbnN0IFNDQUxBUl9JTkZPID0gJ3dvcmtmbG93L2VuY3AveDI1NTE5L3YxJztcblxuLyoqXG4gKiBIS0RGIGBpbmZvYCBwcmVmaXggZm9yIGRlcml2aW5nIHRoZSBBRVMtMjU2LUdDTSBjb250ZW50IGtleSBmcm9tIGFuIFgyNTUxOVxuICogc2hhcmVkIHNlY3JldC4gVGhlIGVwaGVtZXJhbCBhbmQgcmVjaXBpZW50IHB1YmxpYyBrZXlzIGFyZSBhcHBlbmRlZCB0byB0aGlzXG4gKiBsYWJlbCB0byBmb3JtIHRoZSBmdWxsIGBpbmZvYCAoSFBLRSdzIGBrZW1fY29udGV4dGApLlxuICovXG5jb25zdCBDT05URU5UX0tFWV9JTkZPID0gJ3dvcmtmbG93L2VuY3AvYWVzMjU2Z2NtL3YxJztcblxuLyoqXG4gKiBERVIgcHJlZml4IGZvciBhbiBSRkMgODQxMCBgUHJpdmF0ZUtleUluZm9gIHdyYXBwaW5nIGEgcmF3IFgyNTUxOSBzY2FsYXIuXG4gKlxuICogV2ViIENyeXB0byBjYW5ub3QgaW1wb3J0IGEgcmF3IFgyNTUxOSBwcml2YXRlIGtleSAoYGltcG9ydEtleSgncmF3JywgLi4uKWBcbiAqIGlzIG9ubHkgZGVmaW5lZCBmb3IgcHVibGljIGtleXMgb24gdGhlIFNlY3VyZSBDdXJ2ZXMgYWxnb3JpdGhtcyksIHNvIGFcbiAqIGRlcml2ZWQgc2NhbGFyIGhhcyB0byBiZSB3cmFwcGVkIGluIFBLQ1MjOCBiZWZvcmUgaXQgY2FuIGJlIHVzZWQuIFRoZVxuICogc3RydWN0dXJlIGlzIGZ1bGx5IGRldGVybWluZWQgZm9yIGEgMzItYnl0ZSBYMjU1MTkga2V5OlxuICpcbiAqIGBgYFxuICogU0VRVUVOQ0UgKDQ2IGJ5dGVzKSAgICAgICAgICAgICAgICAgICAgICAgICAzMCAyZVxuICogICBJTlRFR0VSIDAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAwMiAwMSAwMFxuICogICBTRVFVRU5DRSAoNSBieXRlcykgICAgICAgICAgICAgICAgICAgICAgICAzMCAwNVxuICogICAgIE9CSkVDVCBJREVOVElGSUVSIDEuMy4xMDEuMTEwIChYMjU1MTkpICAwNiAwMyAyYiA2NSA2ZVxuICogICBPQ1RFVCBTVFJJTkcgKDM0IGJ5dGVzKSAgICAgICAgICAgICAgICAgICAwNCAyMlxuICogICAgIE9DVEVUIFNUUklORyAoMzIgYnl0ZXMpICAgICAgICAgICAgICAgICAwNCAyMFxuICogICAgICAgPHNjYWxhcj5cbiAqIGBgYFxuICovXG5jb25zdCBQS0NTOF9YMjU1MTlfUFJFRklYID0gbmV3IFVpbnQ4QXJyYXkoW1xuICAweDMwLCAweDJlLCAweDAyLCAweDAxLCAweDAwLCAweDMwLCAweDA1LCAweDA2LCAweDAzLCAweDJiLCAweDY1LCAweDZlLCAweDA0LFxuICAweDIyLCAweDA0LCAweDIwLFxuXSk7XG5cbmNvbnN0IGluZm9FbmNvZGVyID0gbmV3IFRleHRFbmNvZGVyKCk7XG5cbi8qKlxuICogQSBwZXItcnVuIFgyNTUxOSBrZXlwYWlyLCBkZXJpdmVkIGZyb20gdGhlIHJ1bidzIGtleSBtYXRlcmlhbC5cbiAqXG4gKiBDYWxsZXJzIHNob3VsZCBkZXJpdmUgdGhpcyBvbmNlIHBlciBydW4gYW5kIG1lbW9pemUgaXQgYWxvbmdzaWRlIHRoZVxuICogc3ltbWV0cmljIGtleSwgc2luY2UgZGVyaXZhdGlvbiBjb3N0cyBzZXZlcmFsIFdlYiBDcnlwdG8gcm91bmQgdHJpcHMuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgUnVuS2V5UGFpciB7XG4gIC8qKiBSYXcgMzItYnl0ZSBYMjU1MTkgcHJpdmF0ZSBzY2FsYXIuIFNlY3JldC4gKi9cbiAgcmVhZG9ubHkgc2NhbGFyOiBVaW50OEFycmF5O1xuICAvKiogUmF3IDMyLWJ5dGUgWDI1NTE5IHB1YmxpYyBrZXkuIFNhZmUgdG8gcHVibGlzaC4gKi9cbiAgcmVhZG9ubHkgcHVibGljS2V5OiBVaW50OEFycmF5O1xufVxuXG5mdW5jdGlvbiBhc3NlcnRLZXlNYXRlcmlhbExlbmd0aChydW5LZXlNYXRlcmlhbDogVWludDhBcnJheSk6IHZvaWQge1xuICBpZiAocnVuS2V5TWF0ZXJpYWwuYnl0ZUxlbmd0aCAhPT0gQ09OVEVOVF9LRVlfTEVOR1RIKSB7XG4gICAgdGhyb3cgbmV3IFdvcmtmbG93UnVudGltZUVycm9yKFxuICAgICAgYFJ1biBrZXkgbWF0ZXJpYWwgbXVzdCBiZSBleGFjdGx5ICR7Q09OVEVOVF9LRVlfTEVOR1RIfSBieXRlcywgZ290ICR7cnVuS2V5TWF0ZXJpYWwuYnl0ZUxlbmd0aH1gXG4gICAgKTtcbiAgfVxufVxuXG5mdW5jdGlvbiBhc3NlcnRQdWJsaWNLZXlMZW5ndGgocHVibGljS2V5OiBVaW50OEFycmF5LCBsYWJlbDogc3RyaW5nKTogdm9pZCB7XG4gIGlmIChwdWJsaWNLZXkuYnl0ZUxlbmd0aCAhPT0gWDI1NTE5X0tFWV9MRU5HVEgpIHtcbiAgICB0aHJvdyBuZXcgV29ya2Zsb3dSdW50aW1lRXJyb3IoXG4gICAgICBgJHtsYWJlbH0gbXVzdCBiZSBleGFjdGx5ICR7WDI1NTE5X0tFWV9MRU5HVEh9IGJ5dGVzLCBnb3QgJHtwdWJsaWNLZXkuYnl0ZUxlbmd0aH1gXG4gICAgKTtcbiAgfVxufVxuXG4vKipcbiAqIERlcml2ZSB0aGUgcGVyLXJ1biBYMjU1MTkga2V5cGFpciBmcm9tIHRoZSBydW4ncyAzMi1ieXRlIGtleSBtYXRlcmlhbC5cbiAqXG4gKiBEZXRlcm1pbmlzdGljOiB0aGUgc2FtZSBrZXkgbWF0ZXJpYWwgYWx3YXlzIHlpZWxkcyB0aGUgc2FtZSBrZXlwYWlyLCB3aGljaFxuICogaXMgd2hhdCBsZXRzIHRoZSBvd25pbmcgZGVwbG95bWVudCByZS1kZXJpdmUgaXRzIHByaXZhdGUgc2NhbGFyIG9uIGRlbWFuZFxuICogKGZyb20gYFZFUkNFTF9ERVBMT1lNRU5UX0tFWWApIGluc3RlYWQgb2Ygc3RvcmluZyBpdCBhbnl3aGVyZS5cbiAqXG4gKiBAcGFyYW0gcnVuS2V5TWF0ZXJpYWwgLSBUaGUgMzIgYnl0ZXMgcmV0dXJuZWQgYnkgYFdvcmxkLmdldEVuY3J5cHRpb25LZXlGb3JSdW4oKWBcbiAqIEByZXR1cm5zIFRoZSBydW4ncyBYMjU1MTkgc2NhbGFyIGFuZCBwdWJsaWMga2V5XG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBkZXJpdmVSdW5LZXlQYWlyKFxuICBydW5LZXlNYXRlcmlhbDogVWludDhBcnJheVxuKTogUHJvbWlzZTxSdW5LZXlQYWlyPiB7XG4gIGFzc2VydEtleU1hdGVyaWFsTGVuZ3RoKHJ1bktleU1hdGVyaWFsKTtcblxuICBjb25zdCBoa2RmS2V5ID0gYXdhaXQgZ2xvYmFsVGhpcy5jcnlwdG8uc3VidGxlLmltcG9ydEtleShcbiAgICAncmF3JyxcbiAgICBydW5LZXlNYXRlcmlhbCxcbiAgICAnSEtERicsXG4gICAgZmFsc2UsXG4gICAgWydkZXJpdmVCaXRzJ11cbiAgKTtcblxuICAvLyBaZXJvIHNhbHQgaXMgYWNjZXB0YWJsZSBwZXIgUkZDIDU4NjkgwqczLjE6IHRoZSBJS00gaGVyZSBpcyBpdHNlbGYgSEtERlxuICAvLyBvdXRwdXQgKG9yIGEgaGlnaC1lbnRyb3B5IEJZT0sgdmFsdWUpLCBzbyBpdCBpcyBhbHJlYWR5IHVuaWZvcm0uIFRoZVxuICAvLyBgaW5mb2AgbGFiZWwgcHJvdmlkZXMgZG9tYWluIHNlcGFyYXRpb24uXG4gIGNvbnN0IHNjYWxhckJpdHMgPSBhd2FpdCBnbG9iYWxUaGlzLmNyeXB0by5zdWJ0bGUuZGVyaXZlQml0cyhcbiAgICB7XG4gICAgICBuYW1lOiAnSEtERicsXG4gICAgICBoYXNoOiAnU0hBLTI1NicsXG4gICAgICBzYWx0OiBuZXcgVWludDhBcnJheSgzMiksXG4gICAgICBpbmZvOiBpbmZvRW5jb2Rlci5lbmNvZGUoU0NBTEFSX0lORk8pLFxuICAgIH0sXG4gICAgaGtkZktleSxcbiAgICBYMjU1MTlfS0VZX0xFTkdUSCAqIDhcbiAgKTtcbiAgY29uc3Qgc2NhbGFyID0gbmV3IFVpbnQ4QXJyYXkoc2NhbGFyQml0cyk7XG5cbiAgLy8gQW55IDMyLWJ5dGUgc3RyaW5nIGlzIGEgdmFsaWQgWDI1NTE5IHNjYWxhciAodGhlIGxvdy9oaWdoIGJpdHMgYXJlXG4gIC8vIGNsYW1wZWQgZHVyaW5nIHNjYWxhciBtdWx0aXBsaWNhdGlvbiksIHNvIHRoZSBIS0RGIG91dHB1dCBuZWVkcyBub1xuICAvLyByZWplY3Rpb24gc2FtcGxpbmcuXG4gIGNvbnN0IHB1YmxpY0tleSA9IGF3YWl0IGRlcml2ZVB1YmxpY0tleUZyb21TY2FsYXIoc2NhbGFyKTtcbiAgcmV0dXJuIHsgc2NhbGFyLCBwdWJsaWNLZXkgfTtcbn1cblxuLyoqXG4gKiBSZWNvdmVyIHRoZSByYXcgcHVibGljIGtleSBmb3IgYSBzY2FsYXIuXG4gKlxuICogV2ViIENyeXB0byBleHBvc2VzIG5vIHNjYWxhci1tdWx0aXBsaWNhdGlvbiBwcmltaXRpdmUsIHNvIHRoZSBzY2FsYXIgaXNcbiAqIGltcG9ydGVkIGFzIGEgUEtDUyM4IHByaXZhdGUga2V5IGFuZCBpdHMgcHVibGljIGhhbGYgcmVhZCBiYWNrIG91dCBvZiB0aGVcbiAqIEpXSyBleHBvcnQgKGB4YCksIHdoaWNoIHRoZSBTZWN1cmUgQ3VydmVzIHNwZWMgZGVmaW5lcyBhcyB0aGUgYmFzZTY0dXJsXG4gKiByYXcgcHVibGljIGtleS5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gZGVyaXZlUHVibGljS2V5RnJvbVNjYWxhcihcbiAgc2NhbGFyOiBVaW50OEFycmF5XG4pOiBQcm9taXNlPFVpbnQ4QXJyYXk+IHtcbiAgY29uc3QgcHJpdmF0ZUtleSA9IGF3YWl0IGltcG9ydFNjYWxhcihzY2FsYXIsIC8qIGV4dHJhY3RhYmxlICovIHRydWUpO1xuICBjb25zdCBqd2sgPSBhd2FpdCBnbG9iYWxUaGlzLmNyeXB0by5zdWJ0bGUuZXhwb3J0S2V5KCdqd2snLCBwcml2YXRlS2V5KTtcbiAgaWYgKCFqd2sueCkge1xuICAgIHRocm93IG5ldyBXb3JrZmxvd1J1bnRpbWVFcnJvcihcbiAgICAgICdYMjU1MTkgSldLIGV4cG9ydCBkaWQgbm90IGluY2x1ZGUgYSBwdWJsaWMga2V5IGNvbXBvbmVudCAoXCJ4XCIpJ1xuICAgICk7XG4gIH1cbiAgY29uc3QgcHVibGljS2V5ID0gYmFzZTY0VXJsVG9CeXRlcyhqd2sueCk7XG4gIC8vIEd1YXJkIHRoZSBvbmUgc3RlcCBpbiB0aGlzIG1vZHVsZSB0aGF0IHRydXN0cyBhbiBleHRlcm5hbCBlbmNvZGluZy4gQVxuICAvLyBzaG9ydCBvciBtYWxmb3JtZWQgdmFsdWUgaGVyZSB3b3VsZCBvdGhlcndpc2UgZmxvdyBvbndhcmQgYW5kIGZhaWwgbXVjaFxuICAvLyBsYXRlciwgaW5zaWRlIGtleSBhZ3JlZW1lbnQsIHdpdGggYSBmYXIgbGVzcyBvYnZpb3VzIG1lc3NhZ2UuXG4gIGlmIChwdWJsaWNLZXkuYnl0ZUxlbmd0aCAhPT0gWDI1NTE5X0tFWV9MRU5HVEgpIHtcbiAgICB0aHJvdyBuZXcgV29ya2Zsb3dSdW50aW1lRXJyb3IoXG4gICAgICBgWDI1NTE5IEpXSyBleHBvcnQgcHJvZHVjZWQgYSAke3B1YmxpY0tleS5ieXRlTGVuZ3RofS1ieXRlIHB1YmxpYyBrZXksIGV4cGVjdGVkICR7WDI1NTE5X0tFWV9MRU5HVEh9YFxuICAgICk7XG4gIH1cbiAgcmV0dXJuIHB1YmxpY0tleTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gaW1wb3J0U2NhbGFyKFxuICBzY2FsYXI6IFVpbnQ4QXJyYXksXG4gIGV4dHJhY3RhYmxlOiBib29sZWFuXG4pOiBQcm9taXNlPENyeXB0b0tleT4ge1xuICBjb25zdCBwa2NzOCA9IG5ldyBVaW50OEFycmF5KFBLQ1M4X1gyNTUxOV9QUkVGSVgubGVuZ3RoICsgc2NhbGFyLmJ5dGVMZW5ndGgpO1xuICBwa2NzOC5zZXQoUEtDUzhfWDI1NTE5X1BSRUZJWCwgMCk7XG4gIHBrY3M4LnNldChzY2FsYXIsIFBLQ1M4X1gyNTUxOV9QUkVGSVgubGVuZ3RoKTtcbiAgcmV0dXJuIGdsb2JhbFRoaXMuY3J5cHRvLnN1YnRsZS5pbXBvcnRLZXkoXG4gICAgJ3BrY3M4JyxcbiAgICBwa2NzOCxcbiAgICB7IG5hbWU6ICdYMjU1MTknIH0sXG4gICAgZXh0cmFjdGFibGUsXG4gICAgWydkZXJpdmVCaXRzJ11cbiAgKTtcbn1cblxuZnVuY3Rpb24gaW1wb3J0UHVibGljS2V5KHB1YmxpY0tleTogVWludDhBcnJheSk6IFByb21pc2U8Q3J5cHRvS2V5PiB7XG4gIHJldHVybiBnbG9iYWxUaGlzLmNyeXB0by5zdWJ0bGUuaW1wb3J0S2V5KFxuICAgICdyYXcnLFxuICAgIHB1YmxpY0tleSxcbiAgICB7IG5hbWU6ICdYMjU1MTknIH0sXG4gICAgLyogZXh0cmFjdGFibGUgKi8gdHJ1ZSxcbiAgICAvLyBQdWJsaWMga2V5cyBjYXJyeSBubyB1c2FnZXMgZm9yIFgyNTUxOTogdGhlIHByaXZhdGUga2V5IGRvZXMgdGhlXG4gICAgLy8gZGVyaXZpbmc7IHRoZSBwdWJsaWMga2V5IGlzIG9ubHkgZXZlciBhbiBhcmd1bWVudCB0byBpdC5cbiAgICBbXVxuICApO1xufVxuXG5jb25zdCBCQVNFNjRfQ0hBUlMgPVxuICAnQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejAxMjM0NTY3ODkrLyc7XG5cbmNvbnN0IEJBU0U2NFVSTF9DSEFSUyA9XG4gICdBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWmFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6MDEyMzQ1Njc4OS1fJztcblxuLyoqXG4gKiBFbmNvZGUgYnl0ZXMgYXMgc3RhbmRhcmQgKHBhZGRlZCkgYmFzZTY0LlxuICpcbiAqIEhhbmQtcm9sbGVkIGZvciB0aGUgc2FtZSByZWFzb24gYXMge0BsaW5rIGJhc2U2NFVybFRvQnl0ZXN9OiB0aGlzIG1vZHVsZVxuICogcnVucyBpbiB0aGUgYnJvd3NlciAobzExeSBkZWNyeXB0aW9uKSBhbmQgaW5zaWRlIHRoZSB3b3JrZmxvdyBWTSwgc28gbmVpdGhlclxuICogYEJ1ZmZlcmAgbm9yIGBidG9hYCBjYW4gYmUgYXNzdW1lZC4gVXNlZCBmb3IgdGhlIHdpcmUgZW5jb2Rpbmcgb2YgcnVuIHB1YmxpY1xuICoga2V5cywgbWF0Y2hpbmcgdGhlIGJhc2U2NCBjb252ZW50aW9uIGFscmVhZHkgdXNlZCBmb3IgYFZFUkNFTF9ERVBMT1lNRU5UX0tFWWBcbiAqIGFuZCB0aGUgYHJ1bi1rZXlgIEFQSSByZXNwb25zZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGJ5dGVzVG9CYXNlNjQoYnl0ZXM6IFVpbnQ4QXJyYXkpOiBzdHJpbmcge1xuICBsZXQgb3V0ID0gJyc7XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgYnl0ZXMubGVuZ3RoOyBpICs9IDMpIHtcbiAgICBjb25zdCBiMCA9IGJ5dGVzW2ldO1xuICAgIGNvbnN0IGIxID0gaSArIDEgPCBieXRlcy5sZW5ndGggPyBieXRlc1tpICsgMV0gOiAwO1xuICAgIGNvbnN0IGIyID0gaSArIDIgPCBieXRlcy5sZW5ndGggPyBieXRlc1tpICsgMl0gOiAwO1xuXG4gICAgb3V0ICs9IEJBU0U2NF9DSEFSU1soYjAgPj4gMikgJiAweDNmXTtcbiAgICBvdXQgKz0gQkFTRTY0X0NIQVJTWygoYjAgJiAweDAzKSA8PCA0KSB8ICgoYjEgPj4gNCkgJiAweDBmKV07XG4gICAgb3V0ICs9XG4gICAgICBpICsgMSA8IGJ5dGVzLmxlbmd0aFxuICAgICAgICA/IEJBU0U2NF9DSEFSU1soKGIxICYgMHgwZikgPDwgMikgfCAoKGIyID4+IDYpICYgMHgwMyldXG4gICAgICAgIDogJz0nO1xuICAgIG91dCArPSBpICsgMiA8IGJ5dGVzLmxlbmd0aCA/IEJBU0U2NF9DSEFSU1tiMiAmIDB4M2ZdIDogJz0nO1xuICB9XG4gIHJldHVybiBvdXQ7XG59XG5cbi8qKlxuICogRGVjb2RlIHN0YW5kYXJkIGJhc2U2NCAocGFkZGluZyBvcHRpb25hbCkgdG8gYnl0ZXMuXG4gKlxuICogUmV0dXJucyBgdW5kZWZpbmVkYCBmb3IgbWFsZm9ybWVkIGlucHV0IHJhdGhlciB0aGFuIHRocm93aW5nOiBjYWxsZXJzIGRlY29kZVxuICogcHVibGljIGtleXMgdGhhdCBhcnJpdmUgZnJvbSBzdG9yYWdlIG9yIG92ZXIgdGhlIHdpcmUsIHdoZXJlIGEgY29ycnVwdCB2YWx1ZVxuICogc2hvdWxkIGRlZ3JhZGUgdG8gXCJ0aGlzIHJ1biBoYXMgbm8gdXNhYmxlIHB1YmxpYyBrZXlcIiAoYW5kIGZhbGwgYmFjayB0byB0aGVcbiAqIHN5bW1ldHJpYyBwYXRoKSByYXRoZXIgdGhhbiBjcmFzaCBhIHJlc3VtcHRpb24uXG4gKlxuICogVmFsaWRhdGlvbiBpcyBzdHJpY3QsIGJlY2F1c2UgYSBsZW5pZW50IGRlY29kZXIgaXMgd29yc2UgdGhhbiBhIHRocm93aW5nIG9uZVxuICogaGVyZTogc2lsZW50bHkgcmV0dXJuaW5nIGEgc2hvcnQgb3IgdHJ1bmNhdGVkIGtleSBtYWtlcyBhIGNvcnJ1cHQgdmFsdWUgbG9va1xuICogKnByZXNlbnQqLCBzbyB0aGUgY2FsbGVyIHNlYWxzIHRvIGdhcmJhZ2UgaW5zdGVhZCBvZiB0YWtpbmcgdGhlIGZhbGxiYWNrLlxuICogUmVqZWN0ZWQ6IGNoYXJhY3RlcnMgb3V0c2lkZSB0aGUgYWxwaGFiZXQsIGEgbGVuZ3RoIHRoYXQgY2Fubm90IGRlc2NyaWJlIGFcbiAqIHdob2xlIG51bWJlciBvZiBieXRlcyAoYGxlbmd0aCAlIDQgPT09IDFgKSwgcGFkZGluZyBhbnl3aGVyZSBidXQgdGhlIGVuZCwgYW5kXG4gKiBhIGZpbmFsIHF1YW50dW0gd2hvc2UgdW51c2VkIGxvdyBiaXRzIGFyZSBub3QgemVyby5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGJhc2U2NFRvQnl0ZXModmFsdWU6IHN0cmluZyk6IFVpbnQ4QXJyYXkgfCB1bmRlZmluZWQge1xuICAvLyBTdHJpcCB0cmFpbGluZyBwYWRkaW5nLCB0aGVuIHJlcXVpcmUgd2hhdCByZW1haW5zIHRvIGJlIHBhZGRpbmctZnJlZS5cbiAgbGV0IGVuZCA9IHZhbHVlLmxlbmd0aDtcbiAgd2hpbGUgKGVuZCA+IDAgJiYgdmFsdWVbZW5kIC0gMV0gPT09ICc9JykgZW5kLS07XG4gIGNvbnN0IGJvZHkgPSB2YWx1ZS5zbGljZSgwLCBlbmQpO1xuICBpZiAoYm9keS5pbmNsdWRlcygnPScpKSByZXR1cm4gdW5kZWZpbmVkO1xuXG4gIC8vIDQgY2hhcnMgLT4gMyBieXRlczsgMyAtPiAyOyAyIC0+IDEuIEEgcmVtYWluZGVyIG9mIDEgZW5jb2RlcyBubyBieXRlLlxuICBpZiAoYm9keS5sZW5ndGggJSA0ID09PSAxKSByZXR1cm4gdW5kZWZpbmVkO1xuICAvLyBQYWRkaW5nLCB3aGVuIHByZXNlbnQsIG11c3QgYnJpbmcgdGhlIHRvdGFsIHRvIGEgbXVsdGlwbGUgb2YgNC5cbiAgaWYgKGVuZCAhPT0gdmFsdWUubGVuZ3RoICYmIHZhbHVlLmxlbmd0aCAlIDQgIT09IDApIHJldHVybiB1bmRlZmluZWQ7XG5cbiAgbGV0IGFjY3VtdWxhdG9yID0gMDtcbiAgbGV0IGJpdENvdW50ID0gMDtcbiAgY29uc3QgYnl0ZXM6IG51bWJlcltdID0gW107XG5cbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBib2R5Lmxlbmd0aDsgaSsrKSB7XG4gICAgY29uc3QgaW5kZXggPSBCQVNFNjRfQ0hBUlMuaW5kZXhPZihib2R5W2ldKTtcbiAgICBpZiAoaW5kZXggPT09IC0xKSByZXR1cm4gdW5kZWZpbmVkO1xuICAgIGFjY3VtdWxhdG9yID0gKGFjY3VtdWxhdG9yIDw8IDYpIHwgaW5kZXg7XG4gICAgYml0Q291bnQgKz0gNjtcbiAgICBpZiAoYml0Q291bnQgPj0gOCkge1xuICAgICAgYml0Q291bnQgLT0gODtcbiAgICAgIGJ5dGVzLnB1c2goKGFjY3VtdWxhdG9yID4+IGJpdENvdW50KSAmIDB4ZmYpO1xuICAgIH1cbiAgfVxuXG4gIC8vIExlZnRvdmVyIGJpdHMgYmVsb25nIHRvIG5vIGJ5dGUgYW5kIG11c3QgYmUgemVybyBpbiBjYW5vbmljYWwgYmFzZTY0LlxuICBpZiAoYml0Q291bnQgPiAwICYmIChhY2N1bXVsYXRvciAmICgoMSA8PCBiaXRDb3VudCkgLSAxKSkgIT09IDApIHtcbiAgICByZXR1cm4gdW5kZWZpbmVkO1xuICB9XG5cbiAgcmV0dXJuIG5ldyBVaW50OEFycmF5KGJ5dGVzKTtcbn1cblxuLyoqXG4gKiBEZWNvZGUgYSBiYXNlNjR1cmwgc3RyaW5nIChKV0sgZW5jb2RpbmcpIHRvIGJ5dGVzLlxuICpcbiAqIEhhbmQtcm9sbGVkIHJhdGhlciB0aGFuIHVzaW5nIGBhdG9iYCBvciBgQnVmZmVyYDogdGhpcyBtb2R1bGUgYWxzbyBydW5zXG4gKiBpbnNpZGUgdGhlIHdvcmtmbG93IFZNLCB3aG9zZSBnbG9iYWwgc3VyZmFjZSBpcyBkZWxpYmVyYXRlbHkgbWluaW1hbCBhbmRcbiAqIGRvZXMgbm90IGluY2x1ZGUgZWl0aGVyLiBUaGUgaW5wdXRzIGhlcmUgYXJlIGZpeGVkLXNpemUgSldLIGtleSBjb21wb25lbnRzLFxuICogc28gYSBjb21wYWN0IGRlY29kZXIgaXMgc3VmZmljaWVudDogaXQgYWNjZXB0cyB1bnBhZGRlZCBiYXNlNjR1cmwgb25seSxcbiAqIHdoaWNoIGlzIHdoYXQgUkZDIDc1MTUgwqcyIG1hbmRhdGVzIGZvciBKV0sgbWVtYmVycy5cbiAqL1xuZnVuY3Rpb24gYmFzZTY0VXJsVG9CeXRlcyh2YWx1ZTogc3RyaW5nKTogVWludDhBcnJheSB7XG4gIGxldCBhY2N1bXVsYXRvciA9IDA7XG4gIGxldCBiaXRDb3VudCA9IDA7XG4gIGNvbnN0IGJ5dGVzOiBudW1iZXJbXSA9IFtdO1xuXG4gIGZvciAobGV0IGkgPSAwOyBpIDwgdmFsdWUubGVuZ3RoOyBpKyspIHtcbiAgICBjb25zdCBjaGFyID0gdmFsdWVbaV07XG4gICAgaWYgKGNoYXIgPT09ICc9JykgYnJlYWs7XG4gICAgY29uc3QgaW5kZXggPSBCQVNFNjRVUkxfQ0hBUlMuaW5kZXhPZihjaGFyKTtcbiAgICBpZiAoaW5kZXggPT09IC0xKSB7XG4gICAgICB0aHJvdyBuZXcgV29ya2Zsb3dSdW50aW1lRXJyb3IoXG4gICAgICAgIGBJbnZhbGlkIGJhc2U2NHVybCBjaGFyYWN0ZXIgXCIke2NoYXJ9XCIgaW4gWDI1NTE5IEpXSyBrZXkgY29tcG9uZW50YFxuICAgICAgKTtcbiAgICB9XG4gICAgYWNjdW11bGF0b3IgPSAoYWNjdW11bGF0b3IgPDwgNikgfCBpbmRleDtcbiAgICBiaXRDb3VudCArPSA2O1xuICAgIGlmIChiaXRDb3VudCA+PSA4KSB7XG4gICAgICBiaXRDb3VudCAtPSA4O1xuICAgICAgYnl0ZXMucHVzaCgoYWNjdW11bGF0b3IgPj4gYml0Q291bnQpICYgMHhmZik7XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIG5ldyBVaW50OEFycmF5KGJ5dGVzKTtcbn1cblxuLyoqXG4gKiBEZXJpdmUgdGhlIEFFUy0yNTYtR0NNIGNvbnRlbnQga2V5IGZyb20gYW4gWDI1NTE5IHNoYXJlZCBzZWNyZXQsIGJpbmRpbmdcbiAqIGl0IHRvIGJvdGggcHVibGljIGtleXMgKEhQS0UncyBga2VtX2NvbnRleHRgKS5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gZGVyaXZlQ29udGVudEtleShcbiAgc2hhcmVkU2VjcmV0OiBVaW50OEFycmF5LFxuICBlcGhlbWVyYWxQdWJsaWNLZXk6IFVpbnQ4QXJyYXksXG4gIHJlY2lwaWVudFB1YmxpY0tleTogVWludDhBcnJheSxcbiAgdXNhZ2VzOiBSZWFkb25seUFycmF5PCdlbmNyeXB0JyB8ICdkZWNyeXB0Jz5cbik6IFByb21pc2U8Q3J5cHRvS2V5PiB7XG4gIGNvbnN0IGxhYmVsID0gaW5mb0VuY29kZXIuZW5jb2RlKENPTlRFTlRfS0VZX0lORk8pO1xuICBjb25zdCBpbmZvID0gbmV3IFVpbnQ4QXJyYXkoXG4gICAgbGFiZWwubGVuZ3RoICsgZXBoZW1lcmFsUHVibGljS2V5Lmxlbmd0aCArIHJlY2lwaWVudFB1YmxpY0tleS5sZW5ndGhcbiAgKTtcbiAgaW5mby5zZXQobGFiZWwsIDApO1xuICBpbmZvLnNldChlcGhlbWVyYWxQdWJsaWNLZXksIGxhYmVsLmxlbmd0aCk7XG4gIGluZm8uc2V0KHJlY2lwaWVudFB1YmxpY0tleSwgbGFiZWwubGVuZ3RoICsgZXBoZW1lcmFsUHVibGljS2V5Lmxlbmd0aCk7XG5cbiAgY29uc3QgaGtkZktleSA9IGF3YWl0IGdsb2JhbFRoaXMuY3J5cHRvLnN1YnRsZS5pbXBvcnRLZXkoXG4gICAgJ3JhdycsXG4gICAgc2hhcmVkU2VjcmV0LFxuICAgICdIS0RGJyxcbiAgICBmYWxzZSxcbiAgICBbJ2Rlcml2ZUJpdHMnXVxuICApO1xuICBjb25zdCBjb250ZW50S2V5Qml0cyA9IGF3YWl0IGdsb2JhbFRoaXMuY3J5cHRvLnN1YnRsZS5kZXJpdmVCaXRzKFxuICAgIHsgbmFtZTogJ0hLREYnLCBoYXNoOiAnU0hBLTI1NicsIHNhbHQ6IG5ldyBVaW50OEFycmF5KDMyKSwgaW5mbyB9LFxuICAgIGhrZGZLZXksXG4gICAgQ09OVEVOVF9LRVlfTEVOR1RIICogOFxuICApO1xuXG4gIHJldHVybiBnbG9iYWxUaGlzLmNyeXB0by5zdWJ0bGUuaW1wb3J0S2V5KFxuICAgICdyYXcnLFxuICAgIG5ldyBVaW50OEFycmF5KGNvbnRlbnRLZXlCaXRzKSxcbiAgICAnQUVTLUdDTScsXG4gICAgZmFsc2UsXG4gICAgLy8gYEtleVVzYWdlYCBpcyBhIERPTS1saWIgdHlwZSB0aGF0IGlzIG5vdCBpbiBzY29wZSB1bmRlciBgZXMyMDIyYDsgdGhlXG4gICAgLy8gcGFyYW1ldGVyIHR5cGUgaXMgYSBzdHJpY3Qgc3Vic2V0IG9mIGl0LCBzbyB0aGlzIGNhc3QgaXMgc291bmQuXG4gICAgdXNhZ2VzIGFzICgnZW5jcnlwdCcgfCAnZGVjcnlwdCcpW11cbiAgKTtcbn1cblxuLyoqXG4gKiBUaGUgd3JpdGVyIGhhbGYgb2YgdGhlIEtFTTogZ2VuZXJhdGUgYW4gZXBoZW1lcmFsIGtleXBhaXIgYW5kIGRlcml2ZSBhXG4gKiBjb250ZW50IGtleSBmb3IgYSByZWNpcGllbnQncyBwdWJsaWMga2V5LlxuICpcbiAqIFVzZSB0aGlzIHdoZW4gbWFueSBwYXlsb2FkcyBzaGFyZSBvbmUgS0VNIG9wZXJhdGlvbiwgaS5lLiBzdHJlYW0gZnJhbWVzLlxuICogVGhlIHJldHVybmVkIGBjb250ZW50S2V5YCBjYW4gb25seSBlbmNyeXB0LCBzbyBhIHN0cmVhbSB3cml0ZXIgcHJvdmFibHlcbiAqIGNhbm5vdCByZWFkIHRoZSByZWNpcGllbnQgcnVuJ3MgZGF0YSBldmVuIGJ5IG1pc3Rha2UuXG4gKlxuICogKipDYWxsZXJzIG93biBub25jZSBkaXNjaXBsaW5lLioqIEVuY3J5cHQgZWFjaCBmcmFtZSB3aXRoIGEgZnJlc2ggcmFuZG9tXG4gKiBub25jZSwgYW5kIGNhbGwgdGhpcyBmdW5jdGlvbiBhZ2FpbiBmb3IgZXZlcnkgbmV3IGNvbm5lY3Rpb24gYXR0ZW1wdCBvclxuICogcmVwbGF5IHNvIHRoYXQgYSByZXN0YXJ0ZWQgd3JpdGVyIG5ldmVyIHJldXNlcyBhIGNvbnRlbnQga2V5LiBGb3Igb25lLXNob3RcbiAqIHBheWxvYWRzIHByZWZlciB7QGxpbmsgc2VhbH0sIHdoaWNoIGhhbmRsZXMgdGhpcyBhdXRvbWF0aWNhbGx5LlxuICpcbiAqIEBwYXJhbSByZWNpcGllbnRQdWJsaWNLZXkgLSBUaGUgcmVjaXBpZW50IHJ1bidzIHJhdyAzMi1ieXRlIFgyNTUxOSBwdWJsaWMga2V5XG4gKiBAcmV0dXJucyBUaGUgZXBoZW1lcmFsIHB1YmxpYyBrZXkgdG8gcHVibGlzaCBhbG9uZ3NpZGUgdGhlIGNpcGhlcnRleHQsIGFuZFxuICogICB0aGUgZW5jcnlwdC1vbmx5IGNvbnRlbnQga2V5XG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBlbmNhcHN1bGF0ZShyZWNpcGllbnRQdWJsaWNLZXk6IFVpbnQ4QXJyYXkpOiBQcm9taXNlPHtcbiAgZXBoZW1lcmFsUHVibGljS2V5OiBVaW50OEFycmF5O1xuICBjb250ZW50S2V5OiBDcnlwdG9LZXk7XG59PiB7XG4gIGFzc2VydFB1YmxpY0tleUxlbmd0aChyZWNpcGllbnRQdWJsaWNLZXksICdSZWNpcGllbnQgcHVibGljIGtleScpO1xuXG4gIGxldCByZWNpcGllbnRLZXk6IENyeXB0b0tleTtcbiAgdHJ5IHtcbiAgICByZWNpcGllbnRLZXkgPSBhd2FpdCBpbXBvcnRQdWJsaWNLZXkocmVjaXBpZW50UHVibGljS2V5KTtcbiAgfSBjYXRjaCAoY2F1c2UpIHtcbiAgICB0aHJvdyBuZXcgV29ya2Zsb3dSdW50aW1lRXJyb3IoXG4gICAgICBgSW52YWxpZCBYMjU1MTkgcmVjaXBpZW50IHB1YmxpYyBrZXk6ICR7Y2F1c2UgaW5zdGFuY2VvZiBFcnJvciA/IGNhdXNlLm1lc3NhZ2UgOiBTdHJpbmcoY2F1c2UpfWAsXG4gICAgICB7IGNhdXNlIH1cbiAgICApO1xuICB9XG5cbiAgY29uc3QgZXBoZW1lcmFsID0gKGF3YWl0IGdsb2JhbFRoaXMuY3J5cHRvLnN1YnRsZS5nZW5lcmF0ZUtleShcbiAgICB7IG5hbWU6ICdYMjU1MTknIH0sXG4gICAgLyogZXh0cmFjdGFibGUgKi8gdHJ1ZSxcbiAgICBbJ2Rlcml2ZUJpdHMnXVxuICApKSBhcyB7IHByaXZhdGVLZXk6IENyeXB0b0tleTsgcHVibGljS2V5OiBDcnlwdG9LZXkgfTtcblxuICBjb25zdCBlcGhlbWVyYWxQdWJsaWNLZXkgPSBuZXcgVWludDhBcnJheShcbiAgICBhd2FpdCBnbG9iYWxUaGlzLmNyeXB0by5zdWJ0bGUuZXhwb3J0S2V5KCdyYXcnLCBlcGhlbWVyYWwucHVibGljS2V5KVxuICApO1xuXG4gIGxldCBzaGFyZWRTZWNyZXRCaXRzOiBBcnJheUJ1ZmZlcjtcbiAgdHJ5IHtcbiAgICBzaGFyZWRTZWNyZXRCaXRzID0gYXdhaXQgZ2xvYmFsVGhpcy5jcnlwdG8uc3VidGxlLmRlcml2ZUJpdHMoXG4gICAgICB7IG5hbWU6ICdYMjU1MTknLCBwdWJsaWM6IHJlY2lwaWVudEtleSB9LFxuICAgICAgZXBoZW1lcmFsLnByaXZhdGVLZXksXG4gICAgICBYMjU1MTlfS0VZX0xFTkdUSCAqIDhcbiAgICApO1xuICB9IGNhdGNoIChjYXVzZSkge1xuICAgIC8vIFdlYiBDcnlwdG8gdGhyb3dzIE9wZXJhdGlvbkVycm9yIHdoZW4gdGhlIHNoYXJlZCBzZWNyZXQgaXMgYWxsLXplcm8sXG4gICAgLy8gd2hpY2ggaGFwcGVucyBleGFjdGx5IHdoZW4gdGhlIHJlY2lwaWVudCBwdWJsaWMga2V5IGlzIGEgbG93LW9yZGVyXG4gICAgLy8gcG9pbnQuIFRoaXMgaXMgdGhlIHBsYXRmb3JtIGdpdmluZyB1cyBIUEtFJ3MgY29udHJpYnV0b3J5LWJlaGF2aW9yXG4gICAgLy8gY2hlY2sgZm9yIGZyZWUuXG4gICAgdGhyb3cgbmV3IFdvcmtmbG93UnVudGltZUVycm9yKFxuICAgICAgJ1gyNTUxOSBrZXkgYWdyZWVtZW50IGZhaWxlZDogdGhlIHJlY2lwaWVudCBwdWJsaWMga2V5IGlzIG5vdCBhIHZhbGlkICcgK1xuICAgICAgICAnY3VydmUgcG9pbnQgKGxvdy1vcmRlciBwb2ludCBvciBjb3JydXB0ZWQga2V5IG1hdGVyaWFsKScsXG4gICAgICB7IGNhdXNlIH1cbiAgICApO1xuICB9XG5cbiAgY29uc3QgY29udGVudEtleSA9IGF3YWl0IGRlcml2ZUNvbnRlbnRLZXkoXG4gICAgbmV3IFVpbnQ4QXJyYXkoc2hhcmVkU2VjcmV0Qml0cyksXG4gICAgZXBoZW1lcmFsUHVibGljS2V5LFxuICAgIHJlY2lwaWVudFB1YmxpY0tleSxcbiAgICBbJ2VuY3J5cHQnXVxuICApO1xuXG4gIHJldHVybiB7IGVwaGVtZXJhbFB1YmxpY0tleSwgY29udGVudEtleSB9O1xufVxuXG4vKipcbiAqIFRoZSByZWNpcGllbnQgaGFsZiBvZiB0aGUgS0VNOiByZWNvdmVyIHRoZSBjb250ZW50IGtleSBmb3IgYSBzZWFsZWRcbiAqIHBheWxvYWQgZnJvbSB0aGUgcnVuJ3Mgb3duIGtleXBhaXIgYW5kIHRoZSBzZW5kZXIncyBlcGhlbWVyYWwgcHVibGljIGtleS5cbiAqXG4gKiBAcGFyYW0ga2V5UGFpciAtIFRoZSByZWNpcGllbnQgcnVuJ3Mga2V5cGFpciwgZnJvbSB7QGxpbmsgZGVyaXZlUnVuS2V5UGFpcn1cbiAqIEBwYXJhbSBlcGhlbWVyYWxQdWJsaWNLZXkgLSBUaGUgc2VuZGVyJ3MgcmF3IDMyLWJ5dGUgWDI1NTE5IHB1YmxpYyBrZXksXG4gKiAgIHJlYWQgZnJvbSB0aGUgaGVhZCBvZiB0aGUgc2VhbGVkIGVudmVsb3BlXG4gKiBAcmV0dXJucyBBIGRlY3J5cHQtb25seSBjb250ZW50IGtleVxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZGVjYXBzdWxhdGUoXG4gIGtleVBhaXI6IFJ1bktleVBhaXIsXG4gIGVwaGVtZXJhbFB1YmxpY0tleTogVWludDhBcnJheVxuKTogUHJvbWlzZTxDcnlwdG9LZXk+IHtcbiAgaWYgKGVwaGVtZXJhbFB1YmxpY0tleS5ieXRlTGVuZ3RoICE9PSBYMjU1MTlfS0VZX0xFTkdUSCkge1xuICAgIHRocm93IG5ldyBSdW50aW1lRGVjcnlwdGlvbkVycm9yKFxuICAgICAgYFNlYWxlZCBwYXlsb2FkIGhhcyBhIG1hbGZvcm1lZCBlcGhlbWVyYWwgcHVibGljIGtleTogZXhwZWN0ZWQgJHtYMjU1MTlfS0VZX0xFTkdUSH0gYnl0ZXMsIGdvdCAke2VwaGVtZXJhbFB1YmxpY0tleS5ieXRlTGVuZ3RofWAsXG4gICAgICB7XG4gICAgICAgIGNvbnRleHQ6IHtcbiAgICAgICAgICBvcGVyYXRpb246ICdkZWNyeXB0JyxcbiAgICAgICAgICBieXRlTGVuZ3RoOiBlcGhlbWVyYWxQdWJsaWNLZXkuYnl0ZUxlbmd0aCxcbiAgICAgICAgfSxcbiAgICAgIH1cbiAgICApO1xuICB9XG5cbiAgbGV0IHNoYXJlZFNlY3JldEJpdHM6IEFycmF5QnVmZmVyO1xuICB0cnkge1xuICAgIGNvbnN0IFtwcml2YXRlS2V5LCBzZW5kZXJLZXldID0gYXdhaXQgUHJvbWlzZS5hbGwoW1xuICAgICAgaW1wb3J0U2NhbGFyKGtleVBhaXIuc2NhbGFyLCAvKiBleHRyYWN0YWJsZSAqLyBmYWxzZSksXG4gICAgICBpbXBvcnRQdWJsaWNLZXkoZXBoZW1lcmFsUHVibGljS2V5KSxcbiAgICBdKTtcbiAgICBzaGFyZWRTZWNyZXRCaXRzID0gYXdhaXQgZ2xvYmFsVGhpcy5jcnlwdG8uc3VidGxlLmRlcml2ZUJpdHMoXG4gICAgICB7IG5hbWU6ICdYMjU1MTknLCBwdWJsaWM6IHNlbmRlcktleSB9LFxuICAgICAgcHJpdmF0ZUtleSxcbiAgICAgIFgyNTUxOV9LRVlfTEVOR1RIICogOFxuICAgICk7XG4gIH0gY2F0Y2ggKGNhdXNlKSB7XG4gICAgdGhyb3cgbmV3IFJ1bnRpbWVEZWNyeXB0aW9uRXJyb3IoXG4gICAgICBgWDI1NTE5IGtleSBhZ3JlZW1lbnQgZmFpbGVkIHdoaWxlIG9wZW5pbmcgYSBzZWFsZWQgcGF5bG9hZDogJHtjYXVzZSBpbnN0YW5jZW9mIEVycm9yID8gY2F1c2UubWVzc2FnZSA6IFN0cmluZyhjYXVzZSl9YCxcbiAgICAgIHtcbiAgICAgICAgY2F1c2UsXG4gICAgICAgIGNvbnRleHQ6IHtcbiAgICAgICAgICBvcGVyYXRpb246ICdkZWNyeXB0JyxcbiAgICAgICAgICBieXRlTGVuZ3RoOiBlcGhlbWVyYWxQdWJsaWNLZXkuYnl0ZUxlbmd0aCxcbiAgICAgICAgfSxcbiAgICAgIH1cbiAgICApO1xuICB9XG5cbiAgcmV0dXJuIGRlcml2ZUNvbnRlbnRLZXkoXG4gICAgbmV3IFVpbnQ4QXJyYXkoc2hhcmVkU2VjcmV0Qml0cyksXG4gICAgZXBoZW1lcmFsUHVibGljS2V5LFxuICAgIGtleVBhaXIucHVibGljS2V5LFxuICAgIFsnZGVjcnlwdCddXG4gICk7XG59XG5cbi8qKlxuICogU2VhbCBhIHBheWxvYWQgdG8gYSBydW4ncyBwdWJsaWMga2V5LlxuICpcbiAqIEVhY2ggY2FsbCBwZXJmb3JtcyBpdHMgb3duIEtFTSBvcGVyYXRpb24sIHNvIGV2ZXJ5IHNlYWxlZCBwYXlsb2FkIGdldHMgYW5cbiAqIGluZGVwZW5kZW50IGNvbnRlbnQga2V5OiBub25jZSByZXVzZSBhY3Jvc3MgY2FsbHMgaXMgaW1wb3NzaWJsZSBieVxuICogY29uc3RydWN0aW9uLlxuICpcbiAqIEBwYXJhbSByZWNpcGllbnRQdWJsaWNLZXkgLSBUaGUgcmVjaXBpZW50IHJ1bidzIHJhdyAzMi1ieXRlIFgyNTUxOSBwdWJsaWMga2V5XG4gKiBAcGFyYW0gZGF0YSAtIFBsYWludGV4dCB0byBzZWFsXG4gKiBAcGFyYW0gYWFkIC0gT3B0aW9uYWwgYWRkaXRpb25hbCBhdXRoZW50aWNhdGVkIGRhdGEsIGNvdmVyZWQgYnkgdGhlIEdDTSB0YWdcbiAqICAgYnV0IG5vdCBlbmNyeXB0ZWQuIENhbGxlcnMgcGFzcyB0aGUgcmVjaXBpZW50J3MgYHByb2plY3RJZHxydW5JZGAgc28gYVxuICogICBzZWFsZWQgcGF5bG9hZCBjYW5ub3QgYmUgcmVwbGF5ZWQgYWdhaW5zdCBhIGRpZmZlcmVudCBydW4uXG4gKiBAcmV0dXJucyBgW2VwaGVtZXJhbCBwdWJsaWMga2V5ICgzMildW25vbmNlICgxMildW2NpcGhlcnRleHQgKyB0YWddYFxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gc2VhbChcbiAgcmVjaXBpZW50UHVibGljS2V5OiBVaW50OEFycmF5LFxuICBkYXRhOiBVaW50OEFycmF5LFxuICBhYWQ/OiBVaW50OEFycmF5XG4pOiBQcm9taXNlPFVpbnQ4QXJyYXk+IHtcbiAgY29uc3QgeyBlcGhlbWVyYWxQdWJsaWNLZXksIGNvbnRlbnRLZXkgfSA9XG4gICAgYXdhaXQgZW5jYXBzdWxhdGUocmVjaXBpZW50UHVibGljS2V5KTtcbiAgY29uc3QgZW5jcnlwdGVkID0gYXdhaXQgYWVzR2NtRW5jcnlwdChjb250ZW50S2V5LCBkYXRhLCBhYWQpO1xuXG4gIGNvbnN0IHJlc3VsdCA9IG5ldyBVaW50OEFycmF5KFxuICAgIGVwaGVtZXJhbFB1YmxpY0tleS5ieXRlTGVuZ3RoICsgZW5jcnlwdGVkLmJ5dGVMZW5ndGhcbiAgKTtcbiAgcmVzdWx0LnNldChlcGhlbWVyYWxQdWJsaWNLZXksIDApO1xuICByZXN1bHQuc2V0KGVuY3J5cHRlZCwgZXBoZW1lcmFsUHVibGljS2V5LmJ5dGVMZW5ndGgpO1xuICByZXR1cm4gcmVzdWx0O1xufVxuXG4vKipcbiAqIE9wZW4gYSBwYXlsb2FkIHNlYWxlZCB0byB0aGlzIHJ1bidzIHB1YmxpYyBrZXkuXG4gKlxuICogQHBhcmFtIGtleVBhaXIgLSBUaGUgcmVjaXBpZW50IHJ1bidzIGtleXBhaXIsIGZyb20ge0BsaW5rIGRlcml2ZVJ1bktleVBhaXJ9XG4gKiBAcGFyYW0gc2VhbGVkIC0gYFtlcGhlbWVyYWwgcHVibGljIGtleSAoMzIpXVtub25jZSAoMTIpXVtjaXBoZXJ0ZXh0ICsgdGFnXWBcbiAqIEBwYXJhbSBhYWQgLSBUaGUgZXhhY3QgYWRkaXRpb25hbCBhdXRoZW50aWNhdGVkIGRhdGEgcGFzc2VkIHRvIHtAbGluayBzZWFsfVxuICogQHJldHVybnMgVGhlIGRlY3J5cHRlZCBwbGFpbnRleHRcbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIG9wZW4oXG4gIGtleVBhaXI6IFJ1bktleVBhaXIsXG4gIHNlYWxlZDogVWludDhBcnJheSxcbiAgYWFkPzogVWludDhBcnJheVxuKTogUHJvbWlzZTxVaW50OEFycmF5PiB7XG4gIC8vIEVwaGVtZXJhbCBwdWJsaWMga2V5ICsgQUVTLUdDTSBub25jZSArIEFFUy1HQ00gdGFnLlxuICBjb25zdCBtaW5MZW5ndGggPSBYMjU1MTlfS0VZX0xFTkdUSCArIE5PTkNFX0xFTkdUSCArIFRBR19CWVRFUztcbiAgaWYgKHNlYWxlZC5ieXRlTGVuZ3RoIDwgbWluTGVuZ3RoKSB7XG4gICAgdGhyb3cgbmV3IFJ1bnRpbWVEZWNyeXB0aW9uRXJyb3IoXG4gICAgICBgU2VhbGVkIHBheWxvYWQgdG9vIHNob3J0OiBleHBlY3RlZCBhdCBsZWFzdCAke21pbkxlbmd0aH0gYnl0ZXMsIGdvdCAke3NlYWxlZC5ieXRlTGVuZ3RofWAsXG4gICAgICB7IGNvbnRleHQ6IHsgb3BlcmF0aW9uOiAnZGVjcnlwdCcsIGJ5dGVMZW5ndGg6IHNlYWxlZC5ieXRlTGVuZ3RoIH0gfVxuICAgICk7XG4gIH1cblxuICBjb25zdCBlcGhlbWVyYWxQdWJsaWNLZXkgPSBzZWFsZWQuc3ViYXJyYXkoMCwgWDI1NTE5X0tFWV9MRU5HVEgpO1xuICBjb25zdCBlbmNyeXB0ZWQgPSBzZWFsZWQuc3ViYXJyYXkoWDI1NTE5X0tFWV9MRU5HVEgpO1xuICBjb25zdCBjb250ZW50S2V5ID0gYXdhaXQgZGVjYXBzdWxhdGUoa2V5UGFpciwgZXBoZW1lcmFsUHVibGljS2V5KTtcbiAgcmV0dXJuIGFlc0djbURlY3J5cHQoY29udGVudEtleSwgZW5jcnlwdGVkLCBhYWQpO1xufVxuXG4vKipcbiAqIEEgd3JpdGVyLXNpZGUgc2Vzc2lvbiB0aGF0IGFtb3J0aXplcyBvbmUgS0VNIG9wZXJhdGlvbiBhY3Jvc3MgbWFueSBzZWFsZWRcbiAqIHBheWxvYWRzLiBVc2UgaXQgZm9yIHN0cmVhbXMsIHdoZXJlIG9uZS1zaG90IHtAbGluayBzZWFsfSB3b3VsZCBwZXJmb3JtIGFcbiAqIGZyZXNoIGtleWdlbiArIEVDREggKyBIS0RGIGZvciBldmVyeSBmcmFtZS5cbiAqXG4gKiBTYWZldHkgcmVzdHMgb24gdHdvIHByb3BlcnRpZXM6XG4gKlxuICogLSBFdmVyeSBwYXlsb2FkIHN0aWxsIGdldHMgYSAqKmZyZXNoIHJhbmRvbSBub25jZSoqIGZyb20gYGFlc0djbUVuY3J5cHRgLCBzb1xuICogICBzaGFyaW5nIHRoZSBjb250ZW50IGtleSBkb2VzIG5vdCByaXNrIGAoa2V5LCBub25jZSlgIHJldXNlLiAoQSBjb3VudGVyXG4gKiAgIHdvdWxkOiBpdCByZXN0YXJ0cyBhdCB6ZXJvIHdoZW5ldmVyIGEgd3JpdGVyIHJlc3RhcnRzLilcbiAqIC0gVGhlIHNlc3Npb24gaXMgYm91bmQgdG8gb25lIHdyaXRlciBpbnN0YW5jZS4gQSByZWNvbm5lY3Qgb3IgYSBkdXJhYmxlXG4gKiAgIHJlcGxheSBjb25zdHJ1Y3RzIGEgbmV3IHNlc3Npb24sIHNvIGEgcmVzdGFydGVkIHdyaXRlciBuZXZlciBpbmhlcml0cyBhXG4gKiAgIHByZXZpb3VzIGluY2FybmF0aW9uJ3MgY29udGVudCBrZXkuXG4gKlxuICogVGhlIGVudmVsb3BlIGxheW91dCBpcyBieXRlLWlkZW50aWNhbCB0byB7QGxpbmsgc2VhbH0sIHNvIGEgcmVhZGVyIGNhbm5vdFxuICogdGVsbCB3aGljaCB3YXMgdXNlZCBhbmQgbmVlZHMgbm8gbWF0Y2hpbmcgc2Vzc2lvbi5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZVNlYWxTZXNzaW9uKFxuICByZWNpcGllbnRQdWJsaWNLZXk6IFVpbnQ4QXJyYXksXG4gIGFhZD86IFVpbnQ4QXJyYXlcbik6IHsgc2VhbChkYXRhOiBVaW50OEFycmF5KTogUHJvbWlzZTxVaW50OEFycmF5PiB9IHtcbiAgbGV0IGVuY2Fwc3VsYXRpb246XG4gICAgfCBQcm9taXNlPHsgZXBoZW1lcmFsUHVibGljS2V5OiBVaW50OEFycmF5OyBjb250ZW50S2V5OiBDcnlwdG9LZXkgfT5cbiAgICB8IHVuZGVmaW5lZDtcblxuICByZXR1cm4ge1xuICAgIGFzeW5jIHNlYWwoZGF0YTogVWludDhBcnJheSk6IFByb21pc2U8VWludDhBcnJheT4ge1xuICAgICAgLy8gTGF6aWx5LCBzbyBjb25zdHJ1Y3RpbmcgYSBzZXNzaW9uIGZvciBhIHN0cmVhbSB0aGF0IGlzIG5ldmVyIHdyaXR0ZW5cbiAgICAgIC8vIHRvIGNvc3RzIG5vdGhpbmcuXG4gICAgICBlbmNhcHN1bGF0aW9uID8/PSBlbmNhcHN1bGF0ZShyZWNpcGllbnRQdWJsaWNLZXkpO1xuICAgICAgY29uc3QgeyBlcGhlbWVyYWxQdWJsaWNLZXksIGNvbnRlbnRLZXkgfSA9IGF3YWl0IGVuY2Fwc3VsYXRpb247XG4gICAgICBjb25zdCBlbmNyeXB0ZWQgPSBhd2FpdCBhZXNHY21FbmNyeXB0KGNvbnRlbnRLZXksIGRhdGEsIGFhZCk7XG5cbiAgICAgIGNvbnN0IHJlc3VsdCA9IG5ldyBVaW50OEFycmF5KFxuICAgICAgICBlcGhlbWVyYWxQdWJsaWNLZXkuYnl0ZUxlbmd0aCArIGVuY3J5cHRlZC5ieXRlTGVuZ3RoXG4gICAgICApO1xuICAgICAgcmVzdWx0LnNldChlcGhlbWVyYWxQdWJsaWNLZXksIDApO1xuICAgICAgcmVzdWx0LnNldChlbmNyeXB0ZWQsIGVwaGVtZXJhbFB1YmxpY0tleS5ieXRlTGVuZ3RoKTtcbiAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgfSxcbiAgfTtcbn1cblxuLyoqXG4gKiBBIHJlYWRlci1zaWRlIHNlc3Npb24gdGhhdCBjYWNoZXMgZGVjYXBzdWxhdGlvbiBwZXIgc2VuZGVyLlxuICpcbiAqIFRoZSBtaXJyb3Igb2Yge0BsaW5rIGNyZWF0ZVNlYWxTZXNzaW9ufTogYmVjYXVzZSBldmVyeSBmcmFtZSBmcm9tIG9uZSB3cml0ZXJcbiAqIGNhcnJpZXMgdGhlIHNhbWUgZXBoZW1lcmFsIHB1YmxpYyBrZXksIHRoaXMgdHVybnMgYW4gRUNESCBwZXIgZnJhbWUgaW50byBhblxuICogRUNESCBwZXIgd3JpdGVyLiBDb3JyZWN0bmVzcyBkb2VzIG5vdCBkZXBlbmQgb24gdGhlIHdyaXRlciBoYXZpbmcgdXNlZCBhXG4gKiBzZXNzaW9uOiBhIHN0cmVhbSBvZiBpbmRlcGVuZGVudGx5IHNlYWxlZCBwYXlsb2FkcyBtaXNzZXMgdGhlIGNhY2hlXG4gKiBvbiBlYWNoIG5ldyBlcGhlbWVyYWwga2V5LlxuICpcbiAqIFRoZSBjYWNoZSBpcyBrZXllZCBieSB0aGUgZXBoZW1lcmFsIHB1YmxpYyBrZXkgYW5kIGhvbGRzIG9uZSBlbnRyeSwgd2hpY2ggaXNcbiAqIHRoZSBjb21tb24gY2FzZSAob25lIHdyaXRlciBwZXIgc3RyZWFtKS4gQSBzZWNvbmQgd3JpdGVyIGV2aWN0cyB0aGUgZmlyc3RcbiAqIHJhdGhlciB0aGFuIGdyb3dpbmcgd2l0aG91dCBib3VuZC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZU9wZW5TZXNzaW9uKFxuICBrZXlQYWlyOiBSdW5LZXlQYWlyLFxuICBhYWQ/OiBVaW50OEFycmF5XG4pOiB7IG9wZW4oc2VhbGVkOiBVaW50OEFycmF5KTogUHJvbWlzZTxVaW50OEFycmF5PiB9IHtcbiAgbGV0IGNhY2hlZFNlbmRlcjogc3RyaW5nIHwgdW5kZWZpbmVkO1xuICBsZXQgY2FjaGVkS2V5OiBQcm9taXNlPENyeXB0b0tleT4gfCB1bmRlZmluZWQ7XG5cbiAgcmV0dXJuIHtcbiAgICBhc3luYyBvcGVuKHNlYWxlZDogVWludDhBcnJheSk6IFByb21pc2U8VWludDhBcnJheT4ge1xuICAgICAgY29uc3QgbWluTGVuZ3RoID0gWDI1NTE5X0tFWV9MRU5HVEggKyBOT05DRV9MRU5HVEggKyBUQUdfQllURVM7XG4gICAgICBpZiAoc2VhbGVkLmJ5dGVMZW5ndGggPCBtaW5MZW5ndGgpIHtcbiAgICAgICAgdGhyb3cgbmV3IFJ1bnRpbWVEZWNyeXB0aW9uRXJyb3IoXG4gICAgICAgICAgYFNlYWxlZCBwYXlsb2FkIHRvbyBzaG9ydDogZXhwZWN0ZWQgYXQgbGVhc3QgJHttaW5MZW5ndGh9IGJ5dGVzLCBnb3QgJHtzZWFsZWQuYnl0ZUxlbmd0aH1gLFxuICAgICAgICAgIHsgY29udGV4dDogeyBvcGVyYXRpb246ICdkZWNyeXB0JywgYnl0ZUxlbmd0aDogc2VhbGVkLmJ5dGVMZW5ndGggfSB9XG4gICAgICAgICk7XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IGVwaGVtZXJhbFB1YmxpY0tleSA9IHNlYWxlZC5zdWJhcnJheSgwLCBYMjU1MTlfS0VZX0xFTkdUSCk7XG4gICAgICBjb25zdCBlbmNyeXB0ZWQgPSBzZWFsZWQuc3ViYXJyYXkoWDI1NTE5X0tFWV9MRU5HVEgpO1xuXG4gICAgICBjb25zdCBzZW5kZXIgPSBrZXlDYWNoZUlkKGVwaGVtZXJhbFB1YmxpY0tleSk7XG4gICAgICBpZiAoc2VuZGVyICE9PSBjYWNoZWRTZW5kZXIgfHwgIWNhY2hlZEtleSkge1xuICAgICAgICBjYWNoZWRTZW5kZXIgPSBzZW5kZXI7XG4gICAgICAgIGNhY2hlZEtleSA9IGRlY2Fwc3VsYXRlKGtleVBhaXIsIGVwaGVtZXJhbFB1YmxpY0tleSk7XG4gICAgICB9XG5cbiAgICAgIGxldCBjb250ZW50S2V5OiBDcnlwdG9LZXk7XG4gICAgICB0cnkge1xuICAgICAgICBjb250ZW50S2V5ID0gYXdhaXQgY2FjaGVkS2V5O1xuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgLy8gRG8gbm90IGxldCBvbmUgYmFkIGZyYW1lIHBvaXNvbiB0aGUgY2FjaGUgZm9yIHN1YnNlcXVlbnQgZnJhbWVzLlxuICAgICAgICBjYWNoZWRTZW5kZXIgPSB1bmRlZmluZWQ7XG4gICAgICAgIGNhY2hlZEtleSA9IHVuZGVmaW5lZDtcbiAgICAgICAgdGhyb3cgZXJyb3I7XG4gICAgICB9XG5cbiAgICAgIHJldHVybiBhZXNHY21EZWNyeXB0KGNvbnRlbnRLZXksIGVuY3J5cHRlZCwgYWFkKTtcbiAgICB9LFxuICB9O1xufVxuXG4vKiogU3RhYmxlIGNhY2hlIGtleSBmb3IgYSAzMi1ieXRlIHB1YmxpYyBrZXkuICovXG5mdW5jdGlvbiBrZXlDYWNoZUlkKHB1YmxpY0tleTogVWludDhBcnJheSk6IHN0cmluZyB7XG4gIGxldCBpZCA9ICcnO1xuICBmb3IgKGxldCBpID0gMDsgaSA8IHB1YmxpY0tleS5sZW5ndGg7IGkrKykge1xuICAgIGlkICs9IHB1YmxpY0tleVtpXS50b1N0cmluZygxNikucGFkU3RhcnQoMiwgJzAnKTtcbiAgfVxuICByZXR1cm4gaWQ7XG59XG5cbi8qKlxuICogQnVpbGQgdGhlIGFkZGl0aW9uYWwgYXV0aGVudGljYXRlZCBkYXRhIHRoYXQgYmluZHMgYSBzZWFsZWQgcGF5bG9hZCB0byBhXG4gKiBzcGVjaWZpYyBydW4uXG4gKlxuICogTWlycm9ycyB0aGUgYGluZm9gIHVzZWQgZm9yIHN5bW1ldHJpYyBwZXItcnVuIGtleSBkZXJpdmF0aW9uIHNvIHRoZSB0d29cbiAqIHNjaGVtZXMgYWdyZWUgb24gd2hhdCBcInRoaXMgcnVuXCIgbWVhbnMuXG4gKlxuICogTm90ZSB0aGF0IHRoZSBzZWFsZWQtYm94IGNvbnN0cnVjdGlvbiBhbHJlYWR5IGJpbmRzIGEgcGF5bG9hZCB0byBpdHNcbiAqIHJlY2lwaWVudCB3aXRob3V0IGFueSBBQUQ6IHRoZSBjb250ZW50IGtleSBpcyBkZXJpdmVkIG92ZXJcbiAqIGBlcGhlbWVyYWxQdWJsaWNLZXkg4oCWIHJlY2lwaWVudFB1YmxpY0tleWAsIGFuZCBhIHJlY2lwaWVudCBwdWJsaWMga2V5IGlzXG4gKiB1bmlxdWUgcGVyIChkZXBsb3ltZW50IGtleSDDlyBwcm9qZWN0IMOXIHJ1bikuIFJlcGxheWluZyBhIHNlYWxlZCBwYXlsb2FkIGF0XG4gKiBhIGRpZmZlcmVudCBydW4gdGhlcmVmb3JlIGZhaWxzIGF0IGtleSBhZ3JlZW1lbnQgcmVnYXJkbGVzcy4gQUFEIGlzXG4gKiBhdmFpbGFibGUgZm9yIGNhbGxlcnMgdGhhdCB3YW50IGFuIGFkZGl0aW9uYWwsIEtERi1pbmRlcGVuZGVudCBiaW5kaW5nOlxuICogYm90aCBzaWRlcyBtdXN0IHN1cHBseSBieXRlLWlkZW50aWNhbCB2YWx1ZXMgb3IgdGhlIHBheWxvYWQgd2lsbCBub3Qgb3Blbi5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJ1bkFhZChwcm9qZWN0SWQ6IHN0cmluZywgcnVuSWQ6IHN0cmluZyk6IFVpbnQ4QXJyYXkge1xuICByZXR1cm4gaW5mb0VuY29kZXIuZW5jb2RlKGAke3Byb2plY3RJZH18JHtydW5JZH1gKTtcbn1cblxuLyoqXG4gKiBEZWNvZGUgYSBydW4ncyBwdWJsaXNoZWQgcHVibGljIGtleSwgdmFsaWRhdGluZyBib3RoIGVuY29kaW5nIGFuZCBsZW5ndGguXG4gKlxuICogUmV0dXJucyBgdW5kZWZpbmVkYCB3aGVuIHRoZSB2YWx1ZSBpcyBhYnNlbnQsIG5vdCB2YWxpZCBiYXNlNjQsIG9yIG5vdFxuICogZXhhY3RseSAzMiBieXRlcy4gQ2FsbGVycyB0cmVhdCB0aGF0IGFzIFwidGhpcyBydW4gaGFzIG5vIHVzYWJsZSBwdWJsaWMga2V5XCJcbiAqIGFuZCBmYWxsIGJhY2sgdG8gdGhlIHN5bW1ldHJpYyBwYXRoLCBzbyBhIGNvcnJ1cHQgb3IgdHJ1bmNhdGVkIHN0b3JlZCB2YWx1ZVxuICogZGVncmFkZXMgdG8gdGhlIHByZS1leGlzdGluZyBiZWhhdmlvciBpbnN0ZWFkIG9mIGZhaWxpbmcgYSByZXN1bXB0aW9uLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZGVjb2RlUnVuUHVibGljS2V5KFxuICB2YWx1ZTogc3RyaW5nIHwgdW5kZWZpbmVkXG4pOiBVaW50OEFycmF5IHwgdW5kZWZpbmVkIHtcbiAgaWYgKCF2YWx1ZSkgcmV0dXJuIHVuZGVmaW5lZDtcbiAgY29uc3QgYnl0ZXMgPSBiYXNlNjRUb0J5dGVzKHZhbHVlKTtcbiAgaWYgKCFieXRlcyB8fCBieXRlcy5ieXRlTGVuZ3RoICE9PSBYMjU1MTlfS0VZX0xFTkdUSCkgcmV0dXJuIHVuZGVmaW5lZDtcbiAgcmV0dXJuIGJ5dGVzO1xufVxuIl19
|