velocious 1.0.474 → 1.0.475
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 +7 -2
- package/build/configuration-types.js +38 -1
- package/build/configuration.js +41 -1
- package/build/database/drivers/base.js +51 -3
- package/build/database/drivers/mysql/index.js +74 -7
- package/build/database/drivers/sqlite/connection-sql-js.js +16 -5
- package/build/database/drivers/sqlite/index.web.js +29 -15
- package/build/database/drivers/sqlite/web-persistence.js +521 -0
- package/build/environment-handlers/node/cli/commands/generate/frontend-models.js +63 -0
- package/build/frontend-model-controller.js +127 -4
- package/build/frontend-model-resource/base-resource.js +5 -1
- package/build/frontend-models/base.js +146 -2
- package/build/frontend-models/query.js +3 -3
- package/build/frontend-models/resource-definition.js +304 -2
- package/build/routes/hooks/frontend-model-command-route-hook.js +8 -0
- package/build/src/configuration-types.d.ts +113 -2
- package/build/src/configuration-types.d.ts.map +1 -1
- package/build/src/configuration-types.js +35 -2
- package/build/src/configuration.d.ts +18 -1
- package/build/src/configuration.d.ts.map +1 -1
- package/build/src/configuration.js +37 -2
- package/build/src/database/drivers/base.d.ts +30 -0
- package/build/src/database/drivers/base.d.ts.map +1 -1
- package/build/src/database/drivers/base.js +47 -4
- package/build/src/database/drivers/mysql/index.d.ts +32 -3
- package/build/src/database/drivers/mysql/index.d.ts.map +1 -1
- package/build/src/database/drivers/mysql/index.js +65 -7
- package/build/src/database/drivers/sqlite/connection-sql-js.d.ts +8 -1
- package/build/src/database/drivers/sqlite/connection-sql-js.d.ts.map +1 -1
- package/build/src/database/drivers/sqlite/connection-sql-js.js +15 -6
- package/build/src/database/drivers/sqlite/index.web.d.ts +5 -5
- package/build/src/database/drivers/sqlite/index.web.d.ts.map +1 -1
- package/build/src/database/drivers/sqlite/index.web.js +27 -13
- package/build/src/database/drivers/sqlite/web-persistence.d.ts +77 -0
- package/build/src/database/drivers/sqlite/web-persistence.d.ts.map +1 -0
- package/build/src/database/drivers/sqlite/web-persistence.js +464 -0
- package/build/src/environment-handlers/node/cli/commands/generate/frontend-models.d.ts +30 -0
- package/build/src/environment-handlers/node/cli/commands/generate/frontend-models.d.ts.map +1 -1
- package/build/src/environment-handlers/node/cli/commands/generate/frontend-models.js +53 -1
- package/build/src/frontend-model-controller.d.ts +34 -0
- package/build/src/frontend-model-controller.d.ts.map +1 -1
- package/build/src/frontend-model-controller.js +113 -5
- package/build/src/frontend-model-resource/base-resource.d.ts +4 -2
- package/build/src/frontend-model-resource/base-resource.d.ts.map +1 -1
- package/build/src/frontend-model-resource/base-resource.js +7 -2
- package/build/src/frontend-models/base.d.ts +29 -0
- package/build/src/frontend-models/base.d.ts.map +1 -1
- package/build/src/frontend-models/base.js +131 -3
- package/build/src/frontend-models/query.js +4 -4
- package/build/src/frontend-models/resource-definition.d.ts +6 -0
- package/build/src/frontend-models/resource-definition.d.ts.map +1 -1
- package/build/src/frontend-models/resource-definition.js +272 -3
- package/build/src/routes/hooks/frontend-model-command-route-hook.d.ts.map +1 -1
- package/build/src/routes/hooks/frontend-model-command-route-hook.js +8 -1
- package/build/src/sync/device-identity.d.ts +240 -0
- package/build/src/sync/device-identity.d.ts.map +1 -0
- package/build/src/sync/device-identity.js +454 -0
- package/build/src/sync/local-mutation-log.d.ts +212 -0
- package/build/src/sync/local-mutation-log.d.ts.map +1 -0
- package/build/src/sync/local-mutation-log.js +404 -0
- package/build/src/sync/offline-grant.d.ts +157 -0
- package/build/src/sync/offline-grant.d.ts.map +1 -0
- package/build/src/sync/offline-grant.js +292 -0
- package/build/sync/device-identity.js +503 -0
- package/build/sync/local-mutation-log.js +448 -0
- package/build/sync/offline-grant.js +327 -0
- package/build/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/src/configuration-types.js +38 -1
- package/src/configuration.js +41 -1
- package/src/database/drivers/base.js +51 -3
- package/src/database/drivers/mysql/index.js +74 -7
- package/src/database/drivers/sqlite/connection-sql-js.js +16 -5
- package/src/database/drivers/sqlite/index.web.js +29 -15
- package/src/database/drivers/sqlite/web-persistence.js +521 -0
- package/src/environment-handlers/node/cli/commands/generate/frontend-models.js +63 -0
- package/src/frontend-model-controller.js +127 -4
- package/src/frontend-model-resource/base-resource.js +5 -1
- package/src/frontend-models/base.js +146 -2
- package/src/frontend-models/query.js +3 -3
- package/src/frontend-models/resource-definition.js +304 -2
- package/src/routes/hooks/frontend-model-command-route-hook.js +8 -0
- package/src/sync/device-identity.js +503 -0
- package/src/sync/local-mutation-log.js +448 -0
- package/src/sync/offline-grant.js +327 -0
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
const ECDSA_P256_SHA256_ALGORITHM = "ECDSA-P256-SHA256";
|
|
3
|
+
const ECDSA_P256_SHA256_SIGNATURE_PREFIX = "ecdsa-p256-sha256-";
|
|
4
|
+
/**
|
|
5
|
+
* JSON Web Key used by the sync signing helpers.
|
|
6
|
+
* @typedef {JsonWebKey} SyncJsonWebKey
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Backend-signed device certificate payload.
|
|
10
|
+
* @typedef {object} DeviceCertificatePayload
|
|
11
|
+
* @property {string} actorDeviceId - Device id allowed to sign mutations.
|
|
12
|
+
* @property {string} actorUserId - User id represented by the device.
|
|
13
|
+
* @property {string} certificateId - Certificate id.
|
|
14
|
+
* @property {SyncJsonWebKey} devicePublicKey - Device public key peers/backends use to verify mutations.
|
|
15
|
+
* @property {string} expiresAt - ISO timestamp after which the certificate is invalid.
|
|
16
|
+
* @property {string} issuedAt - ISO timestamp when the backend issued the certificate.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Backend-signed device certificate envelope.
|
|
20
|
+
* @typedef {object} DeviceCertificate
|
|
21
|
+
* @property {"ECDSA-P256-SHA256"} algorithm - Signature algorithm.
|
|
22
|
+
* @property {DeviceCertificatePayload} certificate - Certificate payload.
|
|
23
|
+
* @property {string} signature - Backend signature over the certificate payload.
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Sync mutation payload signed by a device.
|
|
27
|
+
* @typedef {object} SyncMutation
|
|
28
|
+
* @property {string} actorDeviceId - Device that signed the mutation.
|
|
29
|
+
* @property {string} actorUserId - User represented by the signing device.
|
|
30
|
+
* @property {Record<string, import("../configuration-types.js").FrontendModelSyncJsonValue>} [attributes] - Model attributes for CRUD mutations.
|
|
31
|
+
* @property {string | number | null} [baseVersion] - Base server/client version.
|
|
32
|
+
* @property {string} clientMutationId - Device-local idempotency id.
|
|
33
|
+
* @property {string} [command] - Domain command name for command mutations.
|
|
34
|
+
* @property {string} model - Sync model/resource name.
|
|
35
|
+
* @property {string} occurredAt - ISO timestamp when the mutation occurred.
|
|
36
|
+
* @property {string} offlineGrantId - Offline grant id authorizing the mutation.
|
|
37
|
+
* @property {string} operation - CRUD operation or command operation.
|
|
38
|
+
* @property {Record<string, import("../configuration-types.js").FrontendModelSyncJsonValue>} [payload] - Domain command payload.
|
|
39
|
+
* @property {string} policyHash - Sync policy hash the mutation was checked against.
|
|
40
|
+
*/
|
|
41
|
+
/**
|
|
42
|
+
* Device-signed mutation envelope.
|
|
43
|
+
* @typedef {object} SignedSyncMutation
|
|
44
|
+
* @property {"ECDSA-P256-SHA256"} algorithm - Signature algorithm.
|
|
45
|
+
* @property {DeviceCertificate} deviceCertificate - Backend-signed device certificate.
|
|
46
|
+
* @property {SyncMutation} mutation - Mutation payload.
|
|
47
|
+
* @property {string} signature - Device signature over the mutation envelope.
|
|
48
|
+
*/
|
|
49
|
+
/**
|
|
50
|
+
* Generates an ECDSA P-256 keypair exported as JWKs.
|
|
51
|
+
* @returns {Promise<{privateKey: SyncJsonWebKey, publicKey: SyncJsonWebKey}>} - Exported keypair.
|
|
52
|
+
*/
|
|
53
|
+
export async function generateSyncSigningKeyPair() {
|
|
54
|
+
const keyPair = await cryptoSubtle().generateKey({ name: "ECDSA", namedCurve: "P-256" }, true, ["sign", "verify"]);
|
|
55
|
+
return {
|
|
56
|
+
privateKey: await cryptoSubtle().exportKey("jwk", keyPair.privateKey),
|
|
57
|
+
publicKey: await cryptoSubtle().exportKey("jwk", keyPair.publicKey)
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Creates a backend-signed device certificate.
|
|
62
|
+
* @param {object} args - Arguments.
|
|
63
|
+
* @param {SyncJsonWebKey} args.backendPrivateKey - Backend private signing key.
|
|
64
|
+
* @param {DeviceCertificatePayload} args.certificate - Certificate payload.
|
|
65
|
+
* @returns {Promise<DeviceCertificate>} - Signed certificate.
|
|
66
|
+
*/
|
|
67
|
+
export async function createDeviceCertificate({ backendPrivateKey, certificate }) {
|
|
68
|
+
const normalizedCertificate = normalizeDeviceCertificatePayload(certificate);
|
|
69
|
+
return {
|
|
70
|
+
algorithm: ECDSA_P256_SHA256_ALGORITHM,
|
|
71
|
+
certificate: normalizedCertificate,
|
|
72
|
+
signature: await signStableJson({
|
|
73
|
+
privateKey: backendPrivateKey,
|
|
74
|
+
value: deviceCertificateSignatureValue(normalizedCertificate)
|
|
75
|
+
})
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Verifies a backend-signed device certificate.
|
|
80
|
+
* @param {object} args - Arguments.
|
|
81
|
+
* @param {SyncJsonWebKey} args.backendPublicKey - Backend public key.
|
|
82
|
+
* @param {DeviceCertificate} args.certificate - Signed certificate.
|
|
83
|
+
* @param {Date} [args.now] - Verification time. Defaults to current time.
|
|
84
|
+
* @returns {Promise<DeviceCertificatePayload>} - Verified certificate payload.
|
|
85
|
+
*/
|
|
86
|
+
export async function verifyDeviceCertificate({ backendPublicKey, certificate, now = new Date() }) {
|
|
87
|
+
const normalizedCertificate = normalizeDeviceCertificate(certificate);
|
|
88
|
+
const verified = await verifyStableJsonSignature({
|
|
89
|
+
publicKey: backendPublicKey,
|
|
90
|
+
signature: normalizedCertificate.signature,
|
|
91
|
+
value: deviceCertificateSignatureValue(normalizedCertificate.certificate)
|
|
92
|
+
});
|
|
93
|
+
if (!verified)
|
|
94
|
+
throw new Error("Device certificate signature did not match");
|
|
95
|
+
assertNotExpired({ expiresAt: normalizedCertificate.certificate.expiresAt, label: "Device certificate", now });
|
|
96
|
+
return normalizedCertificate.certificate;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Creates a device-signed mutation envelope.
|
|
100
|
+
* @param {object} args - Arguments.
|
|
101
|
+
* @param {DeviceCertificate} args.deviceCertificate - Backend-signed device certificate.
|
|
102
|
+
* @param {SyncJsonWebKey} args.devicePrivateKey - Device private signing key.
|
|
103
|
+
* @param {SyncMutation} args.mutation - Mutation payload.
|
|
104
|
+
* @returns {Promise<SignedSyncMutation>} - Signed mutation.
|
|
105
|
+
*/
|
|
106
|
+
export async function createSignedMutation({ deviceCertificate, devicePrivateKey, mutation }) {
|
|
107
|
+
const normalizedDeviceCertificate = normalizeDeviceCertificate(deviceCertificate);
|
|
108
|
+
const normalizedMutation = normalizeSyncMutation(mutation);
|
|
109
|
+
assertMutationMatchesCertificate({ certificate: normalizedDeviceCertificate.certificate, mutation: normalizedMutation });
|
|
110
|
+
return {
|
|
111
|
+
algorithm: ECDSA_P256_SHA256_ALGORITHM,
|
|
112
|
+
deviceCertificate: normalizedDeviceCertificate,
|
|
113
|
+
mutation: normalizedMutation,
|
|
114
|
+
signature: await signStableJson({
|
|
115
|
+
privateKey: devicePrivateKey,
|
|
116
|
+
value: signedMutationSignatureValue({
|
|
117
|
+
deviceCertificate: normalizedDeviceCertificate,
|
|
118
|
+
mutation: normalizedMutation
|
|
119
|
+
})
|
|
120
|
+
})
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Verifies a signed sync mutation and its device certificate.
|
|
125
|
+
* @param {object} args - Arguments.
|
|
126
|
+
* @param {SyncJsonWebKey} args.backendPublicKey - Backend public key used to verify the device certificate.
|
|
127
|
+
* @param {Date} [args.now] - Verification time. Defaults to current time.
|
|
128
|
+
* @param {SignedSyncMutation} args.signedMutation - Signed mutation envelope.
|
|
129
|
+
* @returns {Promise<SyncMutation>} - Verified mutation payload.
|
|
130
|
+
*/
|
|
131
|
+
export async function verifySignedMutation({ backendPublicKey, now = new Date(), signedMutation }) {
|
|
132
|
+
const normalizedSignedMutation = normalizeSignedSyncMutation(signedMutation);
|
|
133
|
+
const certificatePayload = await verifyDeviceCertificate({
|
|
134
|
+
backendPublicKey,
|
|
135
|
+
certificate: normalizedSignedMutation.deviceCertificate,
|
|
136
|
+
now
|
|
137
|
+
});
|
|
138
|
+
assertMutationMatchesCertificate({ certificate: certificatePayload, mutation: normalizedSignedMutation.mutation });
|
|
139
|
+
const verified = await verifyStableJsonSignature({
|
|
140
|
+
publicKey: certificatePayload.devicePublicKey,
|
|
141
|
+
signature: normalizedSignedMutation.signature,
|
|
142
|
+
value: signedMutationSignatureValue({
|
|
143
|
+
deviceCertificate: normalizedSignedMutation.deviceCertificate,
|
|
144
|
+
mutation: normalizedSignedMutation.mutation
|
|
145
|
+
})
|
|
146
|
+
});
|
|
147
|
+
if (!verified)
|
|
148
|
+
throw new Error("Sync mutation signature did not match");
|
|
149
|
+
return normalizedSignedMutation.mutation;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Returns the replay/idempotency key for a signed mutation.
|
|
153
|
+
* @param {{mutation: {actorDeviceId?: unknown, actorUserId?: unknown, clientMutationId?: unknown}}} signedMutation - Signed mutation-like object.
|
|
154
|
+
* @returns {string} - Idempotency key.
|
|
155
|
+
*/
|
|
156
|
+
export function mutationIdempotencyKey(signedMutation) {
|
|
157
|
+
const mutation = signedMutation.mutation;
|
|
158
|
+
return [
|
|
159
|
+
requiredString(mutation.actorUserId, "actorUserId"),
|
|
160
|
+
requiredString(mutation.actorDeviceId, "actorDeviceId"),
|
|
161
|
+
requiredString(mutation.clientMutationId, "clientMutationId")
|
|
162
|
+
].join(":");
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Builds the stable value signed by backend device certificates.
|
|
166
|
+
* @param {DeviceCertificatePayload} certificate - Certificate payload.
|
|
167
|
+
* @returns {Record<string, import("../configuration-types.js").FrontendModelSyncJsonValue>} - Stable signature value.
|
|
168
|
+
*/
|
|
169
|
+
function deviceCertificateSignatureValue(certificate) {
|
|
170
|
+
return {
|
|
171
|
+
algorithm: ECDSA_P256_SHA256_ALGORITHM,
|
|
172
|
+
certificate: /** @type {Record<string, import("../configuration-types.js").FrontendModelSyncJsonValue>} */ (deterministicJsonObject({ label: "certificate", value: certificate }))
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Builds the stable value signed by sync mutations.
|
|
177
|
+
* @param {object} args - Arguments.
|
|
178
|
+
* @param {DeviceCertificate} args.deviceCertificate - Device certificate.
|
|
179
|
+
* @param {SyncMutation} args.mutation - Mutation payload.
|
|
180
|
+
* @returns {Record<string, import("../configuration-types.js").FrontendModelSyncJsonValue>} - Stable signature value.
|
|
181
|
+
*/
|
|
182
|
+
function signedMutationSignatureValue({ deviceCertificate, mutation }) {
|
|
183
|
+
return {
|
|
184
|
+
algorithm: ECDSA_P256_SHA256_ALGORITHM,
|
|
185
|
+
deviceCertificate: /** @type {Record<string, import("../configuration-types.js").FrontendModelSyncJsonValue>} */ (deterministicJsonObject({ label: "deviceCertificate", value: deviceCertificate })),
|
|
186
|
+
mutation: /** @type {Record<string, import("../configuration-types.js").FrontendModelSyncJsonValue>} */ (deterministicJsonObject({ label: "mutation", value: mutation }))
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Signs a deterministic JSON value with ECDSA P-256/SHA-256.
|
|
191
|
+
* @param {object} args - Arguments.
|
|
192
|
+
* @param {SyncJsonWebKey} args.privateKey - Private JWK.
|
|
193
|
+
* @param {unknown} args.value - Value to sign.
|
|
194
|
+
* @returns {Promise<string>} - Prefixed base64url signature.
|
|
195
|
+
*/
|
|
196
|
+
async function signStableJson({ privateKey, value }) {
|
|
197
|
+
const key = await cryptoSubtle().importKey("jwk", privateKey, { name: "ECDSA", namedCurve: "P-256" }, false, ["sign"]);
|
|
198
|
+
const signature = await cryptoSubtle().sign({ hash: "SHA-256", name: "ECDSA" }, key, /** @type {BufferSource} */ (utf8(stableJsonStringify(value))));
|
|
199
|
+
return `${ECDSA_P256_SHA256_SIGNATURE_PREFIX}${base64UrlEncode(new Uint8Array(signature))}`;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Verifies a deterministic JSON value with ECDSA P-256/SHA-256.
|
|
203
|
+
* @param {object} args - Arguments.
|
|
204
|
+
* @param {SyncJsonWebKey} args.publicKey - Public JWK.
|
|
205
|
+
* @param {string} args.signature - Prefixed base64url signature.
|
|
206
|
+
* @param {unknown} args.value - Value to verify.
|
|
207
|
+
* @returns {Promise<boolean>} - Whether the signature matches.
|
|
208
|
+
*/
|
|
209
|
+
async function verifyStableJsonSignature({ publicKey, signature, value }) {
|
|
210
|
+
const key = await cryptoSubtle().importKey("jwk", publicKey, { name: "ECDSA", namedCurve: "P-256" }, false, ["verify"]);
|
|
211
|
+
const signatureBytes = base64UrlDecode(signatureWithoutPrefix(signature));
|
|
212
|
+
return await cryptoSubtle().verify({ hash: "SHA-256", name: "ECDSA" }, key, /** @type {BufferSource} */ (signatureBytes), /** @type {BufferSource} */ (utf8(stableJsonStringify(value))));
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Normalizes a signed device certificate envelope.
|
|
216
|
+
* @param {DeviceCertificate} certificate - Signed certificate.
|
|
217
|
+
* @returns {DeviceCertificate} - Normalized certificate.
|
|
218
|
+
*/
|
|
219
|
+
function normalizeDeviceCertificate(certificate) {
|
|
220
|
+
if (!certificate || typeof certificate !== "object" || Array.isArray(certificate))
|
|
221
|
+
throw new Error("Expected device certificate object");
|
|
222
|
+
if (certificate.algorithm !== ECDSA_P256_SHA256_ALGORITHM)
|
|
223
|
+
throw new Error(`Unsupported device certificate algorithm '${String(certificate.algorithm)}'`);
|
|
224
|
+
return {
|
|
225
|
+
algorithm: certificate.algorithm,
|
|
226
|
+
certificate: normalizeDeviceCertificatePayload(certificate.certificate),
|
|
227
|
+
signature: normalizeSignature(certificate.signature, "device certificate signature")
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Normalizes a device certificate payload.
|
|
232
|
+
* @param {DeviceCertificatePayload} certificate - Certificate payload.
|
|
233
|
+
* @returns {DeviceCertificatePayload} - Normalized certificate payload.
|
|
234
|
+
*/
|
|
235
|
+
function normalizeDeviceCertificatePayload(certificate) {
|
|
236
|
+
if (!certificate || typeof certificate !== "object" || Array.isArray(certificate))
|
|
237
|
+
throw new Error("Expected device certificate payload object");
|
|
238
|
+
return {
|
|
239
|
+
actorDeviceId: requiredString(certificate.actorDeviceId, "actorDeviceId"),
|
|
240
|
+
actorUserId: requiredString(certificate.actorUserId, "actorUserId"),
|
|
241
|
+
certificateId: requiredString(certificate.certificateId, "certificateId"),
|
|
242
|
+
devicePublicKey: normalizeJwk(certificate.devicePublicKey, "devicePublicKey"),
|
|
243
|
+
expiresAt: isoDateString(new Date(requiredString(certificate.expiresAt, "expiresAt")), "expiresAt"),
|
|
244
|
+
issuedAt: isoDateString(new Date(requiredString(certificate.issuedAt, "issuedAt")), "issuedAt")
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Normalizes a signed sync mutation envelope.
|
|
249
|
+
* @param {SignedSyncMutation} signedMutation - Signed mutation.
|
|
250
|
+
* @returns {SignedSyncMutation} - Normalized signed mutation.
|
|
251
|
+
*/
|
|
252
|
+
function normalizeSignedSyncMutation(signedMutation) {
|
|
253
|
+
if (!signedMutation || typeof signedMutation !== "object" || Array.isArray(signedMutation))
|
|
254
|
+
throw new Error("Expected signed sync mutation object");
|
|
255
|
+
if (signedMutation.algorithm !== ECDSA_P256_SHA256_ALGORITHM)
|
|
256
|
+
throw new Error(`Unsupported sync mutation algorithm '${String(signedMutation.algorithm)}'`);
|
|
257
|
+
return {
|
|
258
|
+
algorithm: signedMutation.algorithm,
|
|
259
|
+
deviceCertificate: normalizeDeviceCertificate(signedMutation.deviceCertificate),
|
|
260
|
+
mutation: normalizeSyncMutation(signedMutation.mutation),
|
|
261
|
+
signature: normalizeSignature(signedMutation.signature, "sync mutation signature")
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Normalizes a sync mutation payload.
|
|
266
|
+
* @param {SyncMutation} mutation - Mutation payload.
|
|
267
|
+
* @returns {SyncMutation} - Normalized mutation payload.
|
|
268
|
+
*/
|
|
269
|
+
function normalizeSyncMutation(mutation) {
|
|
270
|
+
if (!mutation || typeof mutation !== "object" || Array.isArray(mutation))
|
|
271
|
+
throw new Error("Expected sync mutation object");
|
|
272
|
+
/** @type {SyncMutation} */
|
|
273
|
+
const normalized = {
|
|
274
|
+
actorDeviceId: requiredString(mutation.actorDeviceId, "actorDeviceId"),
|
|
275
|
+
actorUserId: requiredString(mutation.actorUserId, "actorUserId"),
|
|
276
|
+
clientMutationId: requiredString(mutation.clientMutationId, "clientMutationId"),
|
|
277
|
+
model: requiredString(mutation.model, "model"),
|
|
278
|
+
occurredAt: isoDateString(new Date(requiredString(mutation.occurredAt, "occurredAt")), "occurredAt"),
|
|
279
|
+
offlineGrantId: requiredString(mutation.offlineGrantId, "offlineGrantId"),
|
|
280
|
+
operation: requiredString(mutation.operation, "operation"),
|
|
281
|
+
policyHash: requiredString(mutation.policyHash, "policyHash")
|
|
282
|
+
};
|
|
283
|
+
if (mutation.attributes !== undefined)
|
|
284
|
+
normalized.attributes = deterministicJsonObject({ label: "attributes", value: mutation.attributes });
|
|
285
|
+
if (mutation.baseVersion !== undefined)
|
|
286
|
+
normalized.baseVersion = /** @type {string | number | null} */ (deterministicJson({ label: "baseVersion", value: mutation.baseVersion }));
|
|
287
|
+
if (mutation.command !== undefined)
|
|
288
|
+
normalized.command = requiredString(mutation.command, "command");
|
|
289
|
+
if (mutation.payload !== undefined)
|
|
290
|
+
normalized.payload = deterministicJsonObject({ label: "payload", value: mutation.payload });
|
|
291
|
+
return normalized;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Asserts that mutation actor fields match the certificate actor.
|
|
295
|
+
* @param {object} args - Arguments.
|
|
296
|
+
* @param {DeviceCertificatePayload} args.certificate - Device certificate payload.
|
|
297
|
+
* @param {SyncMutation} args.mutation - Mutation payload.
|
|
298
|
+
* @returns {void} - No return value.
|
|
299
|
+
*/
|
|
300
|
+
function assertMutationMatchesCertificate({ certificate, mutation }) {
|
|
301
|
+
if (mutation.actorDeviceId !== certificate.actorDeviceId || mutation.actorUserId !== certificate.actorUserId) {
|
|
302
|
+
throw new Error("Sync mutation actor does not match device certificate");
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Asserts a timestamp is not expired.
|
|
307
|
+
* @param {object} args - Arguments.
|
|
308
|
+
* @param {string} args.expiresAt - Expiry timestamp.
|
|
309
|
+
* @param {string} args.label - Error label.
|
|
310
|
+
* @param {Date} args.now - Verification time.
|
|
311
|
+
* @returns {void} - No return value.
|
|
312
|
+
*/
|
|
313
|
+
function assertNotExpired({ expiresAt, label, now }) {
|
|
314
|
+
if (Number.isNaN(now.getTime()))
|
|
315
|
+
throw new Error("Invalid sync verification time");
|
|
316
|
+
if (new Date(expiresAt).getTime() <= now.getTime())
|
|
317
|
+
throw new Error(`${label} expired`);
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Normalizes a JSON Web Key object.
|
|
321
|
+
* @param {SyncJsonWebKey} key - JWK.
|
|
322
|
+
* @param {string} label - Key label.
|
|
323
|
+
* @returns {SyncJsonWebKey} - Normalized JWK.
|
|
324
|
+
*/
|
|
325
|
+
function normalizeJwk(key, label) {
|
|
326
|
+
const normalized = deterministicJsonObject({ label, value: key });
|
|
327
|
+
return /** @type {SyncJsonWebKey} */ (normalized);
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Normalizes a prefixed signature string.
|
|
331
|
+
* @param {unknown} signature - Signature value.
|
|
332
|
+
* @param {string} label - Error label.
|
|
333
|
+
* @returns {string} - Signature string.
|
|
334
|
+
*/
|
|
335
|
+
function normalizeSignature(signature, label) {
|
|
336
|
+
if (typeof signature !== "string" || !signature.startsWith(ECDSA_P256_SHA256_SIGNATURE_PREFIX))
|
|
337
|
+
throw new Error(`Expected ${label}`);
|
|
338
|
+
return signature;
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Removes the signature prefix.
|
|
342
|
+
* @param {string} signature - Prefixed signature.
|
|
343
|
+
* @returns {string} - Base64url signature body.
|
|
344
|
+
*/
|
|
345
|
+
function signatureWithoutPrefix(signature) {
|
|
346
|
+
return signature.slice(ECDSA_P256_SHA256_SIGNATURE_PREFIX.length);
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Requires a non-empty string field.
|
|
350
|
+
* @param {unknown} value - Value.
|
|
351
|
+
* @param {string} name - Field name.
|
|
352
|
+
* @returns {string} - String value.
|
|
353
|
+
*/
|
|
354
|
+
function requiredString(value, name) {
|
|
355
|
+
if (typeof value !== "string" || value.length < 1)
|
|
356
|
+
throw new Error(`Expected sync ${name}`);
|
|
357
|
+
return value;
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Normalizes a Date to an ISO timestamp.
|
|
361
|
+
* @param {Date} date - Date value.
|
|
362
|
+
* @param {string} label - Field label.
|
|
363
|
+
* @returns {string} - ISO timestamp.
|
|
364
|
+
*/
|
|
365
|
+
function isoDateString(date, label) {
|
|
366
|
+
if (Number.isNaN(date.getTime()))
|
|
367
|
+
throw new Error(`Invalid sync ${label}`);
|
|
368
|
+
return date.toISOString();
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Returns encoded UTF-8 bytes for a string.
|
|
372
|
+
* @param {string} value - String value.
|
|
373
|
+
* @returns {Uint8Array} - UTF-8 bytes.
|
|
374
|
+
*/
|
|
375
|
+
function utf8(value) {
|
|
376
|
+
return new TextEncoder().encode(value);
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Returns WebCrypto subtle API.
|
|
380
|
+
* @returns {SubtleCrypto} - SubtleCrypto implementation.
|
|
381
|
+
*/
|
|
382
|
+
function cryptoSubtle() {
|
|
383
|
+
if (!globalThis.crypto?.subtle)
|
|
384
|
+
throw new Error("WebCrypto subtle API is required for sync signing");
|
|
385
|
+
return globalThis.crypto.subtle;
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Base64url-encodes bytes.
|
|
389
|
+
* @param {Uint8Array} bytes - Bytes.
|
|
390
|
+
* @returns {string} - Base64url string.
|
|
391
|
+
*/
|
|
392
|
+
function base64UrlEncode(bytes) {
|
|
393
|
+
let binary = "";
|
|
394
|
+
for (const byte of bytes)
|
|
395
|
+
binary += String.fromCharCode(byte);
|
|
396
|
+
return btoa(binary).replaceAll("+", "-").replaceAll("/", "_").replaceAll("=", "");
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Base64url-decodes bytes.
|
|
400
|
+
* @param {string} value - Base64url string.
|
|
401
|
+
* @returns {Uint8Array} - Bytes.
|
|
402
|
+
*/
|
|
403
|
+
function base64UrlDecode(value) {
|
|
404
|
+
const base64 = value.replaceAll("-", "+").replaceAll("_", "/").padEnd(Math.ceil(value.length / 4) * 4, "=");
|
|
405
|
+
const binary = atob(base64);
|
|
406
|
+
return Uint8Array.from(binary, (char) => char.charCodeAt(0));
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Stable JSON stringifier with sorted object keys.
|
|
410
|
+
* @param {unknown} value - Value.
|
|
411
|
+
* @returns {string} - JSON string.
|
|
412
|
+
*/
|
|
413
|
+
function stableJsonStringify(value) {
|
|
414
|
+
return JSON.stringify(deterministicJson({ label: "root", value }));
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Normalizes a value as a JSON object.
|
|
418
|
+
* @param {object} args - Arguments.
|
|
419
|
+
* @param {string} args.label - Diagnostic label.
|
|
420
|
+
* @param {unknown} args.value - Value.
|
|
421
|
+
* @returns {Record<string, import("../configuration-types.js").FrontendModelSyncJsonValue>} - JSON object.
|
|
422
|
+
*/
|
|
423
|
+
function deterministicJsonObject({ label, value }) {
|
|
424
|
+
const normalized = deterministicJson({ label, value });
|
|
425
|
+
if (!normalized || typeof normalized !== "object" || Array.isArray(normalized))
|
|
426
|
+
throw new Error(`Expected sync ${label} object`);
|
|
427
|
+
return /** @type {Record<string, import("../configuration-types.js").FrontendModelSyncJsonValue>} */ (normalized);
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Normalizes deterministic JSON with sorted object keys.
|
|
431
|
+
* @param {object} args - Arguments.
|
|
432
|
+
* @param {string} args.label - Diagnostic label.
|
|
433
|
+
* @param {unknown} args.value - Value.
|
|
434
|
+
* @returns {import("../configuration-types.js").FrontendModelSyncJsonValue} - Normalized JSON value.
|
|
435
|
+
*/
|
|
436
|
+
function deterministicJson({ label, value }) {
|
|
437
|
+
if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean")
|
|
438
|
+
return value;
|
|
439
|
+
if (Array.isArray(value))
|
|
440
|
+
return value.map((entry, index) => deterministicJson({ label: `${label}/${index}`, value: entry }));
|
|
441
|
+
if (value && typeof value === "object" && Object.getPrototypeOf(value) === Object.prototype) {
|
|
442
|
+
/** @type {Record<string, import("../configuration-types.js").FrontendModelSyncJsonValue>} */
|
|
443
|
+
const normalized = {};
|
|
444
|
+
for (const key of Object.keys(value).sort()) {
|
|
445
|
+
const childValue = /** @type {Record<string, unknown>} */ (value)[key];
|
|
446
|
+
if (childValue === undefined)
|
|
447
|
+
continue;
|
|
448
|
+
normalized[key] = deterministicJson({ label: `${label}/${key}`, value: childValue });
|
|
449
|
+
}
|
|
450
|
+
return normalized;
|
|
451
|
+
}
|
|
452
|
+
throw new Error(`Sync ${label} must be deterministic JSON`);
|
|
453
|
+
}
|
|
454
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGV2aWNlLWlkZW50aXR5LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL3N5bmMvZGV2aWNlLWlkZW50aXR5LmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLFlBQVk7QUFFWixNQUFNLDJCQUEyQixHQUFHLG1CQUFtQixDQUFBO0FBQ3ZELE1BQU0sa0NBQWtDLEdBQUcsb0JBQW9CLENBQUE7QUFFL0Q7OztHQUdHO0FBRUg7Ozs7Ozs7OztHQVNHO0FBRUg7Ozs7OztHQU1HO0FBRUg7Ozs7Ozs7Ozs7Ozs7OztHQWVHO0FBRUg7Ozs7Ozs7R0FPRztBQUVIOzs7R0FHRztBQUNILE1BQU0sQ0FBQyxLQUFLLFVBQVUsMEJBQTBCO0lBQzlDLE1BQU0sT0FBTyxHQUFHLE1BQU0sWUFBWSxFQUFFLENBQUMsV0FBVyxDQUM5QyxFQUFDLElBQUksRUFBRSxPQUFPLEVBQUUsVUFBVSxFQUFFLE9BQU8sRUFBQyxFQUNwQyxJQUFJLEVBQ0osQ0FBQyxNQUFNLEVBQUUsUUFBUSxDQUFDLENBQ25CLENBQUE7SUFFRCxPQUFPO1FBQ0wsVUFBVSxFQUFFLE1BQU0sWUFBWSxFQUFFLENBQUMsU0FBUyxDQUFDLEtBQUssRUFBRSxPQUFPLENBQUMsVUFBVSxDQUFDO1FBQ3JFLFNBQVMsRUFBRSxNQUFNLFlBQVksRUFBRSxDQUFDLFNBQVMsQ0FBQyxLQUFLLEVBQUUsT0FBTyxDQUFDLFNBQVMsQ0FBQztLQUNwRSxDQUFBO0FBQ0gsQ0FBQztBQUVEOzs7Ozs7R0FNRztBQUNILE1BQU0sQ0FBQyxLQUFLLFVBQVUsdUJBQXVCLENBQUMsRUFBQyxpQkFBaUIsRUFBRSxXQUFXLEVBQUM7SUFDNUUsTUFBTSxxQkFBcUIsR0FBRyxpQ0FBaUMsQ0FBQyxXQUFXLENBQUMsQ0FBQTtJQUU1RSxPQUFPO1FBQ0wsU0FBUyxFQUFFLDJCQUEyQjtRQUN0QyxXQUFXLEVBQUUscUJBQXFCO1FBQ2xDLFNBQVMsRUFBRSxNQUFNLGNBQWMsQ0FBQztZQUM5QixVQUFVLEVB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QU0sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsSUFBSSxFQUFFLEVBQUUsQ0FBQztZQUM1QyxNQUFNLFVBQVUsR0FBRyxzQ0FBc0MsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFBO1lBRXRFLElBQUksVUFBVSxLQUFLLFNBQVM7Z0JBQUUsU0FBUTtZQUN0QyxVQUFVLENBQUMsR0FBRyxDQUFDLEdBQUcsaUJBQWlCLENBQUMsRUFBQyxLQUFLLEVBQUUsR0FBRyxLQUFLLElBQUksR0FBRyxFQUFFLEVBQUUsS0FBSyxFQUFFLFVBQVUsRUFBQyxDQUFDLENBQUE7UUFDcEYsQ0FBQztRQUVELE9BQU8sVUFBVSxDQUFBO0lBQ25CLENBQUM7SUFFRCxNQUFNLElBQUksS0FBSyxDQUFDLFFBQVEsS0FBSyw2QkFBNkIsQ0FBQyxDQUFBO0FBQzdELENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBAdHMtY2hlY2tcblxuY29uc3QgRUNEU0FfUDI1Nl9TSEEyNTZfQUxHT1JJVEhNID0gXCJFQ0RTQS1QMjU2LVNIQTI1NlwiXG5jb25zdCBFQ0RTQV9QMjU2X1NIQTI1Nl9TSUdOQVRVUkVfUFJFRklYID0gXCJlY2RzYS1wMjU2LXNoYTI1Ni1cIlxuXG4vKipcbiAqIEpTT04gV2ViIEtleSB1c2VkIGJ5IHRoZSBzeW5jIHNpZ25pbmcgaGVscGVycy5cbiAqIEB0eXBlZGVmIHtKc29uV2ViS2V5fSBTeW5jSnNvbldlYktleVxuICovXG5cbi8qKlxuICogQmFja2VuZC1zaWduZWQgZGV2aWNlIGNlcnRpZmljYXRlIHBheWxvYWQuXG4gKiBAdHlwZWRlZiB7b2JqZWN0fSBEZXZpY2VDZXJ0aWZpY2F0ZVBheWxvYWRcbiAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBhY3RvckRldmljZUlkIC0gRGV2aWNlIGlkIGFsbG93ZWQgdG8gc2lnbiBtdXRhdGlvbnMuXG4gKiBAcHJvcGVydHkge3N0cmluZ30gYWN0b3JVc2VySWQgLSBVc2VyIGlkIHJlcHJlc2VudGVkIGJ5IHRoZSBkZXZpY2UuXG4gKiBAcHJvcGVydHkge3N0cmluZ30gY2VydGlmaWNhdGVJZCAtIENlcnRpZmljYXRlIGlkLlxuICogQHByb3BlcnR5IHtTeW5jSnNvbldlYktleX0gZGV2aWNlUHVibGljS2V5IC0gRGV2aWNlIHB1YmxpYyBrZXkgcGVlcnMvYmFja2VuZHMgdXNlIHRvIHZlcmlmeSBtdXRhdGlvbnMuXG4gKiBAcHJvcGVydHkge3N0cmluZ30gZXhwaXJlc0F0IC0gSVNPIHRpbWVzdGFtcCBhZnRlciB3aGljaCB0aGUgY2VydGlmaWNhdGUgaXMgaW52YWxpZC5cbiAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBpc3N1ZWRBdCAtIElTTyB0aW1lc3RhbXAgd2hlbiB0aGUgYmFja2VuZCBpc3N1ZWQgdGhlIGNlcnRpZmljYXRlLlxuICovXG5cbi8qKlxuICogQmFja2VuZC1zaWduZWQgZGV2aWNlIGNlcnRpZmljYXRlIGVudmVsb3BlLlxuICogQHR5cGVkZWYge29iamVjdH0gRGV2aWNlQ2VydGlmaWNhdGVcbiAqIEBwcm9wZXJ0eSB7XCJFQ0RTQS1QMjU2LVNIQTI1NlwifSBhbGdvcml0aG0gLSBTaWduYXR1cmUgYWxnb3JpdGhtLlxuICogQHByb3BlcnR5IHtEZXZpY2VDZXJ0aWZpY2F0ZVBheWxvYWR9IGNlcnRpZmljYXRlIC0gQ2VydGlmaWNhdGUgcGF5bG9hZC5cbiAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBzaWduYXR1cmUgLSBCYWNrZW5kIHNpZ25hdHVyZSBvdmVyIHRoZSBjZXJ0aWZpY2F0ZSBwYXlsb2FkLlxuICovXG5cbi8qKlxuICogU3luYyBtdXRhdGlvbiBwYXlsb2FkIHNpZ25lZCBieSBhIGRldmljZS5cbiAqIEB0eXBlZGVmIHtvYmplY3R9IFN5bmNNdXRhdGlvblxuICogQHByb3BlcnR5IHtzdHJpbmd9IGFjdG9yRGV2aWNlSWQgLSBEZXZpY2UgdGhhdCBzaWduZWQgdGhlIG11dGF0aW9uLlxuICogQHByb3BlcnR5IHtzdHJpbmd9IGFjdG9yVXNlcklkIC0gVXNlciByZXByZXNlbnRlZCBieSB0aGUgc2lnbmluZyBkZXZpY2UuXG4gKiBAcHJvcGVydHkge1JlY29yZDxzdHJpbmcsIGltcG9ydChcIi4uL2NvbmZpZ3VyYXRpb24tdHlwZXMuanNcIikuRnJvbnRlbmRNb2RlbFN5bmNKc29uVmFsdWU+fSBbYXR0cmlidXRlc10gLSBNb2RlbCBhdHRyaWJ1dGVzIGZvciBDUlVEIG11dGF0aW9ucy5cbiAqIEBwcm9wZXJ0eSB7c3RyaW5nIHwgbnVtYmVyIHwgbnVsbH0gW2Jhc2VWZXJzaW9uXSAtIEJhc2Ugc2VydmVyL2NsaWVudCB2ZXJzaW9uLlxuICogQHByb3BlcnR5IHtzdHJpbmd9IGNsaWVudE11dGF0aW9uSWQgLSBEZXZpY2UtbG9jYWwgaWRlbXBvdGVuY3kgaWQuXG4gKiBAcHJvcGVydHkge3N0cmluZ30gW2NvbW1hbmRdIC0gRG9tYWluIGNvbW1hbmQgbmFtZSBmb3IgY29tbWFuZCBtdXRhdGlvbnMuXG4gKiBAcHJvcGVydHkge3N0cmluZ30gbW9kZWwgLSBTeW5jIG1vZGVsL3Jlc291cmNlIG5hbWUuXG4gKiBAcHJvcGVydHkge3N0cmluZ30gb2NjdXJyZWRBdCAtIElTTyB0aW1lc3RhbXAgd2hlbiB0aGUgbXV0YXRpb24gb2NjdXJyZWQuXG4gKiBAcHJvcGVydHkge3N0cmluZ30gb2ZmbGluZUdyYW50SWQgLSBPZmZsaW5lIGdyYW50IGlkIGF1dGhvcml6aW5nIHRoZSBtdXRhdGlvbi5cbiAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBvcGVyYXRpb24gLSBDUlVEIG9wZXJhdGlvbiBvciBjb21tYW5kIG9wZXJhdGlvbi5cbiAqIEBwcm9wZXJ0eSB7UmVjb3JkPHN0cmluZywgaW1wb3J0KFwiLi4vY29uZmlndXJhdGlvbi10eXBlcy5qc1wiKS5Gcm9udGVuZE1vZGVsU3luY0pzb25WYWx1ZT59IFtwYXlsb2FkXSAtIERvbWFpbiBjb21tYW5kIHBheWxvYWQuXG4gKiBAcHJvcGVydHkge3N0cmluZ30gcG9saWN5SGFzaCAtIFN5bmMgcG9saWN5IGhhc2ggdGhlIG11dGF0aW9uIHdhcyBjaGVja2VkIGFnYWluc3QuXG4gKi9cblxuLyoqXG4gKiBEZXZpY2Utc2lnbmVkIG11dGF0aW9uIGVudmVsb3BlLlxuICogQHR5cGVkZWYge29iamVjdH0gU2lnbmVkU3luY011dGF0aW9uXG4gKiBAcHJvcGVydHkge1wiRUNEU0EtUDI1Ni1TSEEyNTZcIn0gYWxnb3JpdGhtIC0gU2lnbmF0dXJlIGFsZ29yaXRobS5cbiAqIEBwcm9wZXJ0eSB7RGV2aWNlQ2VydGlmaWNhdGV9IGRldmljZUNlcnRpZmljYXRlIC0gQmFja2VuZC1zaWduZWQgZGV2aWNlIGNlcnRpZmljYXRlLlxuICogQHByb3BlcnR5IHtTeW5jTXV0YXRpb259IG11dGF0aW9uIC0gTXV0YXRpb24gcGF5bG9hZC5cbiAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBzaWduYXR1cmUgLSBEZXZpY2Ugc2lnbmF0dXJlIG92ZXIgdGhlIG11dGF0aW9uIGVudmVsb3BlLlxuICovXG5cbi8qKlxuICogR2VuZXJhdGVzIGFuIEVDRFNBIFAtMjU2IGtleXBhaXIgZXhwb3J0ZWQgYXMgSldLcy5cbiAqIEByZXR1cm5zIHtQcm9taXNlPHtwcml2YXRlS2V5OiBTeW5jSnNvbldlYktleSwgcHVibGljS2V5OiBTeW5jSnNvbldlYktleX0+fSAtIEV4cG9ydGVkIGtleXBhaXIuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBnZW5lcmF0ZVN5bmNTaWduaW5nS2V5UGFpcigpIHtcbiAgY29uc3Qga2V5UGFpciA9IGF3YWl0IGNyeXB0b1N1YnRsZSgpLmdlbmVyYXRlS2V5KFxuICAgIHtuYW1lOiBcIkVDRFNBXCIsIG5hbWVkQ3VydmU6IFwiUC0yNTZcIn0sXG4gICAgdHJ1ZSxcbiAgICBbXCJzaWduXCIsIFwidmVyaWZ5XCJdXG4gIClcblxuICByZXR1cm4ge1xuICAgIHByaXZhdGVLZXk6IGF3YWl0IGNyeXB0b1N1YnRsZSgpLmV4cG9ydEtleShcImp3a1wiLCBrZXlQYWlyLnByaXZhdGVLZXkpLFxuICAgIHB1YmxpY0tleTogYXdhaXQgY3J5cHRvU3VidGxlKCkuZXhwb3J0S2V5KFwiandrXCIsIGtleVBhaXIucHVibGljS2V5KVxuICB9XG59XG5cbi8qKlxuICogQ3JlYXRlcyBhIGJhY2tlbmQtc2lnbmVkIGRldmljZSBjZXJ0aWZpY2F0ZS5cbiAqIEBwYXJhbSB7b2JqZWN0fSBhcmdzIC0gQXJndW1lbnRzLlxuICogQHBhcmFtIHtTeW5jSnNvbldlYktleX0gYXJncy5iYWNrZW5kUHJpdmF0ZUtleSAtIEJhY2tlbmQgcHJpdmF0ZSBzaWduaW5nIGtleS5cbiAqIEBwYXJhbSB7RGV2aWNlQ2VydGlmaWNhdGVQYXlsb2FkfSBhcmdzLmNlcnRpZmljYXRlIC0gQ2VydGlmaWNhdGUgcGF5bG9hZC5cbiAqIEByZXR1cm5zIHtQcm9taXNlPERldmljZUNlcnRpZmljYXRlPn0gLSBTaWduZWQgY2VydGlmaWNhdGUuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjcmVhdGVEZXZpY2VDZXJ0aWZpY2F0ZSh7YmFja2VuZFByaXZhdGVLZXksIGNlcnRpZmljYXRlfSkge1xuICBjb25zdCBub3JtYWxpemVkQ2VydGlmaWNhdGUgPSBub3JtYWxpemVEZXZpY2VDZXJ0aWZpY2F0ZVBheWxvYWQoY2VydGlmaWNhdGUpXG5cbiAgcmV0dXJuIHtcbiAgICBhbGdvcml0aG06IEVDRFNBX1AyNTZfU0hBMjU2X0FMR09SSVRITSxcbiAgICBjZXJ0aWZpY2F0ZTogbm9ybWFsaXplZENlcnRpZmljYXRlLFxuICAgIHNpZ25hdHVyZTogYXdhaXQgc2lnblN0YWJsZUpzb24oe1xuICAgICAgcHJpdmF0ZUtleTogYmFja2VuZFByaXZhdGVLZXksXG4gICAgICB2YWx1ZTogZGV2aWNlQ2VydGlmaWNhdGVTaWduYXR1cmVWYWx1ZShub3JtYWxpemVkQ2VydGlmaWNhdGUpXG4gICAgfSlcbiAgfVxufVxuXG4vKipcbiAqIFZlcmlmaWVzIGEgYmFja2VuZC1zaWduZWQgZGV2aWNlIGNlcnRpZmljYXRlLlxuICogQHBhcmFtIHtvYmplY3R9IGFyZ3MgLSBBcmd1bWVudHMuXG4gKiBAcGFyYW0ge1N5bmNKc29uV2ViS2V5fSBhcmdzLmJhY2tlbmRQdWJsaWNLZXkgLSBCYWNrZW5kIHB1YmxpYyBrZXkuXG4gKiBAcGFyYW0ge0RldmljZUNlcnRpZmljYXRlfSBhcmdzLmNlcnRpZmljYXRlIC0gU2lnbmVkIGNlcnRpZmljYXRlLlxuICogQHBhcmFtIHtEYXRlfSBbYXJncy5ub3ddIC0gVmVyaWZpY2F0aW9uIHRpbWUuIERlZmF1bHRzIHRvIGN1cnJlbnQgdGltZS5cbiAqIEByZXR1cm5zIHtQcm9taXNlPERldmljZUNlcnRpZmljYXRlUGF5bG9hZD59IC0gVmVyaWZpZWQgY2VydGlmaWNhdGUgcGF5bG9hZC5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHZlcmlmeURldmljZUNlcnRpZmljYXRlKHtiYWNrZW5kUHVibGljS2V5LCBjZXJ0aWZpY2F0ZSwgbm93ID0gbmV3IERhdGUoKX0pIHtcbiAgY29uc3Qgbm9ybWFsaXplZENlcnRpZmljYXRlID0gbm9ybWFsaXplRGV2aWNlQ2VydGlmaWNhdGUoY2VydGlmaWNhdGUpXG4gIGNvbnN0IHZlcmlmaWVkID0gYXdhaXQgdmVyaWZ5U3RhYmxlSnNvblNpZ25hdHVyZSh7XG4gICAgcHVibGljS2V5OiBiYWNrZW5kUHVibGljS2V5LFxuICAgIHNpZ25hdHVyZTogbm9ybWFsaXplZENlcnRpZmljYXRlLnNpZ25hdHVyZSxcbiAgICB2YWx1ZTogZGV2aWNlQ2VydGlmaWNhdGVTaWduYXR1cmVWYWx1ZShub3JtYWxpemVkQ2VydGlmaWNhdGUuY2VydGlmaWNhdGUpXG4gIH0pXG5cbiAgaWYgKCF2ZXJpZmllZCkgdGhyb3cgbmV3IEVycm9yKFwiRGV2aWNlIGNlcnRpZmljYXRlIHNpZ25hdHVyZSBkaWQgbm90IG1hdGNoXCIpXG4gIGFzc2VydE5vdEV4cGlyZWQoe2V4cGlyZXNBdDogbm9ybWFsaXplZENlcnRpZmljYXRlLmNlcnRpZmljYXRlLmV4cGlyZXNBdCwgbGFiZWw6IFwiRGV2aWNlIGNlcnRpZmljYXRlXCIsIG5vd30pXG5cbiAgcmV0dXJuIG5vcm1hbGl6ZWRDZXJ0aWZpY2F0ZS5jZXJ0aWZpY2F0ZVxufVxuXG4vKipcbiAqIENyZWF0ZXMgYSBkZXZpY2Utc2lnbmVkIG11dGF0aW9uIGVudmVsb3BlLlxuICogQHBhcmFtIHtvYmplY3R9IGFyZ3MgLSBBcmd1bWVudHMuXG4gKiBAcGFyYW0ge0RldmljZUNlcnRpZmljYXRlfSBhcmdzLmRldmljZUNlcnRpZmljYXRlIC0gQmFja2VuZC1zaWduZWQgZGV2aWNlIGNlcnRpZmljYXRlLlxuICogQHBhcmFtIHtTeW5jSnNvbldlYktleX0gYXJncy5kZXZpY2VQcml2YXRlS2V5IC0gRGV2aWNlIHByaXZhdGUgc2lnbmluZyBrZXkuXG4gKiBAcGFyYW0ge1N5bmNNdXRhdGlvbn0gYXJncy5tdXRhdGlvbiAtIE11dGF0aW9uIHBheWxvYWQuXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxTaWduZWRTeW5jTXV0YXRpb24+fSAtIFNpZ25lZCBtdXRhdGlvbi5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNyZWF0ZVNpZ25lZE11dGF0aW9uKHtkZXZpY2VDZXJ0aWZpY2F0ZSwgZGV2aWNlUHJpdmF0ZUtleSwgbXV0YXRpb259KSB7XG4gIGNvbnN0IG5vcm1hbGl6ZWREZXZpY2VDZXJ0aWZpY2F0ZSA9IG5vcm1hbGl6ZURldmljZUNlcnRpZmljYXRlKGRldmljZUNlcnRpZmljYXRlKVxuICBjb25zdCBub3JtYWxpemVkTXV0YXRpb24gPSBub3JtYWxpemVTeW5jTXV0YXRpb24obXV0YXRpb24pXG5cbiAgYXNzZXJ0TXV0YXRpb25NYXRjaGVzQ2VydGlmaWNhdGUoe2NlcnRpZmljYXRlOiBub3JtYWxpemVkRGV2aWNlQ2VydGlmaWNhdGUuY2VydGlmaWNhdGUsIG11dGF0aW9uOiBub3JtYWxpemVkTXV0YXRpb259KVxuXG4gIHJldHVybiB7XG4gICAgYWxnb3JpdGhtOiBFQ0RTQV9QMjU2X1NIQTI1Nl9BTEdPUklUSE0sXG4gICAgZGV2aWNlQ2VydGlmaWNhdGU6IG5vcm1hbGl6ZWREZXZpY2VDZXJ0aWZpY2F0ZSxcbiAgICBtdXRhdGlvbjogbm9ybWFsaXplZE11dGF0aW9uLFxuICAgIHNpZ25hdHVyZTogYXdhaXQgc2lnblN0YWJsZUpzb24oe1xuICAgICAgcHJpdmF0ZUtleTogZGV2aWNlUHJpdmF0ZUtleSxcbiAgICAgIHZhbHVlOiBzaWduZWRNdXRhdGlvblNpZ25hdHVyZVZhbHVlKHtcbiAgICAgICAgZGV2aWNlQ2VydGlmaWNhdGU6IG5vcm1hbGl6ZWREZXZpY2VDZXJ0aWZpY2F0ZSxcbiAgICAgICAgbXV0YXRpb246IG5vcm1hbGl6ZWRNdXRhdGlvblxuICAgICAgfSlcbiAgICB9KVxuICB9XG59XG5cbi8qKlxuICogVmVyaWZpZXMgYSBzaWduZWQgc3luYyBtdXRhdGlvbiBhbmQgaXRzIGRldmljZSBjZXJ0aWZpY2F0ZS5cbiAqIEBwYXJhbSB7b2JqZWN0fSBhcmdzIC0gQXJndW1lbnRzLlxuICogQHBhcmFtIHtTeW5jSnNvbldlYktleX0gYXJncy5iYWNrZW5kUHVibGljS2V5IC0gQmFja2VuZCBwdWJsaWMga2V5IHVzZWQgdG8gdmVyaWZ5IHRoZSBkZXZpY2UgY2VydGlmaWNhdGUuXG4gKiBAcGFyYW0ge0RhdGV9IFthcmdzLm5vd10gLSBWZXJpZmljYXRpb24gdGltZS4gRGVmYXVsdHMgdG8gY3VycmVudCB0aW1lLlxuICogQHBhcmFtIHtTaWduZWRTeW5jTXV0YXRpb259IGFyZ3Muc2lnbmVkTXV0YXRpb24gLSBTaWduZWQgbXV0YXRpb24gZW52ZWxvcGUuXG4gKiBAcmV0dXJucyB7UHJvbWlzZTxTeW5jTXV0YXRpb24+fSAtIFZlcmlmaWVkIG11dGF0aW9uIHBheWxvYWQuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiB2ZXJpZnlTaWduZWRNdXRhdGlvbih7YmFja2VuZFB1YmxpY0tleSwgbm93ID0gbmV3IERhdGUoKSwgc2lnbmVkTXV0YXRpb259KSB7XG4gIGNvbnN0IG5vcm1hbGl6ZWRTaWduZWRNdXRhdGlvbiA9IG5vcm1hbGl6ZVNpZ25lZFN5bmNNdXRhdGlvbihzaWduZWRNdXRhdGlvbilcbiAgY29uc3QgY2VydGlmaWNhdGVQYXlsb2FkID0gYXdhaXQgdmVyaWZ5RGV2aWNlQ2VydGlmaWNhdGUoe1xuICAgIGJhY2tlbmRQdWJsaWNLZXksXG4gICAgY2VydGlmaWNhdGU6IG5vcm1hbGl6ZWRTaWduZWRNdXRhdGlvbi5kZXZpY2VDZXJ0aWZpY2F0ZSxcbiAgICBub3dcbiAgfSlcblxuICBhc3NlcnRNdXRhdGlvbk1hdGNoZXNDZXJ0aWZpY2F0ZSh7Y2VydGlmaWNhdGU6IGNlcnRpZmljYXRlUGF5bG9hZCwgbXV0YXRpb246IG5vcm1hbGl6ZWRTaWduZWRNdXRhdGlvbi5tdXRhdGlvbn0pXG5cbiAgY29uc3QgdmVyaWZpZWQgPSBhd2FpdCB2ZXJpZnlTdGFibGVKc29uU2lnbmF0dXJlKHtcbiAgICBwdWJsaWNLZXk6IGNlcnRpZmljYXRlUGF5bG9hZC5kZXZpY2VQdWJsaWNLZXksXG4gICAgc2lnbmF0dXJlOiBub3JtYWxpemVkU2lnbmVkTXV0YXRpb24uc2lnbmF0dXJlLFxuICAgIHZhbHVlOiBzaWduZWRNdXRhdGlvblNpZ25hdHVyZVZhbHVlKHtcbiAgICAgIGRldmljZUNlcnRpZmljYXRlOiBub3JtYWxpemVkU2lnbmVkTXV0YXRpb24uZGV2aWNlQ2VydGlmaWNhdGUsXG4gICAgICBtdXRhdGlvbjogbm9ybWFsaXplZFNpZ25lZE11dGF0aW9uLm11dGF0aW9uXG4gICAgfSlcbiAgfSlcblxuICBpZiAoIXZlcmlmaWVkKSB0aHJvdyBuZXcgRXJyb3IoXCJTeW5jIG11dGF0aW9uIHNpZ25hdHVyZSBkaWQgbm90IG1hdGNoXCIpXG5cbiAgcmV0dXJuIG5vcm1hbGl6ZWRTaWduZWRNdXRhdGlvbi5tdXRhdGlvblxufVxuXG4vKipcbiAqIFJldHVybnMgdGhlIHJlcGxheS9pZGVtcG90ZW5jeSBrZXkgZm9yIGEgc2lnbmVkIG11dGF0aW9uLlxuICogQHBhcmFtIHt7bXV0YXRpb246IHthY3RvckRldmljZUlkPzogdW5rbm93biwgYWN0b3JVc2VySWQ/OiB1bmtub3duLCBjbGllbnRNdXRhdGlvbklkPzogdW5rbm93bn19fSBzaWduZWRNdXRhdGlvbiAtIFNpZ25lZCBtdXRhdGlvbi1saWtlIG9iamVjdC5cbiAqIEByZXR1cm5zIHtzdHJpbmd9IC0gSWRlbXBvdGVuY3kga2V5LlxuICovXG5leHBvcnQgZnVuY3Rpb24gbXV0YXRpb25JZGVtcG90ZW5jeUtleShzaWduZWRNdXRhdGlvbikge1xuICBjb25zdCBtdXRhdGlvbiA9IHNpZ25lZE11dGF0aW9uLm11dGF0aW9uXG5cbiAgcmV0dXJuIFtcbiAgICByZXF1aXJlZFN0cmluZyhtdXRhdGlvbi5hY3RvclVzZXJJZCwgXCJhY3RvclVzZXJJZFwiKSxcbiAgICByZXF1aXJlZFN0cmluZyhtdXRhdGlvbi5hY3RvckRldmljZUlkLCBcImFjdG9yRGV2aWNlSWRcIiksXG4gICAgcmVxdWlyZWRTdHJpbmcobXV0YXRpb24uY2xpZW50TXV0YXRpb25JZCwgXCJjbGllbnRNdXRhdGlvbklkXCIpXG4gIF0uam9pbihcIjpcIilcbn1cblxuLyoqXG4gKiBCdWlsZHMgdGhlIHN0YWJsZSB2YWx1ZSBzaWduZWQgYnkgYmFja2VuZCBkZXZpY2UgY2VydGlmaWNhdGVzLlxuICogQHBhcmFtIHtEZXZpY2VDZXJ0aWZpY2F0ZVBheWxvYWR9IGNlcnRpZmljYXRlIC0gQ2VydGlmaWNhdGUgcGF5bG9hZC5cbiAqIEByZXR1cm5zIHtSZWNvcmQ8c3RyaW5nLCBpbXBvcnQoXCIuLi9jb25maWd1cmF0aW9uLXR5cGVzLmpzXCIpLkZyb250ZW5kTW9kZWxTeW5jSnNvblZhbHVlPn0gLSBTdGFibGUgc2lnbmF0dXJlIHZhbHVlLlxuICovXG5mdW5jdGlvbiBkZXZpY2VDZXJ0aWZpY2F0ZVNpZ25hdHVyZVZhbHVlKGNlcnRpZmljYXRlKSB7XG4gIHJldHVybiB7XG4gICAgYWxnb3JpdGhtOiBFQ0RTQV9QMjU2X1NIQTI1Nl9BTEdPUklUSE0sXG4gICAgY2VydGlmaWNhdGU6IC8qKiBAdHlwZSB7UmVjb3JkPHN0cmluZywgaW1wb3J0KFwiLi4vY29uZmlndXJhdGlvbi10eXBlcy5qc1wiKS5Gcm9udGVuZE1vZGVsU3luY0pzb25WYWx1ZT59ICovIChkZXRlcm1pbmlzdGljSnNvbk9iamVjdCh7bGFiZWw6IFwiY2VydGlmaWNhdGVcIiwgdmFsdWU6IGNlcnRpZmljYXRlfSkpXG4gIH1cbn1cblxuLyoqXG4gKiBCdWlsZHMgdGhlIHN0YWJsZSB2YWx1ZSBzaWduZWQgYnkgc3luYyBtdXRhdGlvbnMuXG4gKiBAcGFyYW0ge29iamVjdH0gYXJncyAtIEFyZ3VtZW50cy5cbiAqIEBwYXJhbSB7RGV2aWNlQ2VydGlmaWNhdGV9IGFyZ3MuZGV2aWNlQ2VydGlmaWNhdGUgLSBEZXZpY2UgY2VydGlmaWNhdGUuXG4gKiBAcGFyYW0ge1N5bmNNdXRhdGlvbn0gYXJncy5tdXRhdGlvbiAtIE11dGF0aW9uIHBheWxvYWQuXG4gKiBAcmV0dXJucyB7UmVjb3JkPHN0cmluZywgaW1wb3J0KFwiLi4vY29uZmlndXJhdGlvbi10eXBlcy5qc1wiKS5Gcm9udGVuZE1vZGVsU3luY0pzb25WYWx1ZT59IC0gU3RhYmxlIHNpZ25hdHVyZSB2YWx1ZS5cbiAqL1xuZnVuY3Rpb24gc2lnbmVkTXV0YXRpb25TaWduYXR1cmVWYWx1ZSh7ZGV2aWNlQ2VydGlmaWNhdGUsIG11dGF0aW9ufSkge1xuICByZXR1cm4ge1xuICAgIGFsZ29yaXRobTogRUNEU0FfUDI1Nl9TSEEyNTZfQUxHT1JJVEhNLFxuICAgIGRldmljZUNlcnRpZmljYXRlOiAvKiogQHR5cGUge1JlY29yZDxzdHJpbmcsIGltcG9ydChcIi4uL2NvbmZpZ3VyYXRpb24tdHlwZXMuanNcIikuRnJvbnRlbmRNb2RlbFN5bmNKc29uVmFsdWU+fSAqLyAoZGV0ZXJtaW5pc3RpY0pzb25PYmplY3Qoe2xhYmVsOiBcImRldmljZUNlcnRpZmljYXRlXCIsIHZhbHVlOiBkZXZpY2VDZXJ0aWZpY2F0ZX0pKSxcbiAgICBtdXRhdGlvbjogLyoqIEB0eXBlIHtSZWNvcmQ8c3RyaW5nLCBpbXBvcnQoXCIuLi9jb25maWd1cmF0aW9uLXR5cGVzLmpzXCIpLkZyb250ZW5kTW9kZWxTeW5jSnNvblZhbHVlPn0gKi8gKGRldGVybWluaXN0aWNKc29uT2JqZWN0KHtsYWJlbDogXCJtdXRhdGlvblwiLCB2YWx1ZTogbXV0YXRpb259KSlcbiAgfVxufVxuXG4vKipcbiAqIFNpZ25zIGEgZGV0ZXJtaW5pc3RpYyBKU09OIHZhbHVlIHdpdGggRUNEU0EgUC0yNTYvU0hBLTI1Ni5cbiAqIEBwYXJhbSB7b2JqZWN0fSBhcmdzIC0gQXJndW1lbnRzLlxuICogQHBhcmFtIHtTeW5jSnNvbldlYktleX0gYXJncy5wcml2YXRlS2V5IC0gUHJpdmF0ZSBKV0suXG4gKiBAcGFyYW0ge3Vua25vd259IGFyZ3MudmFsdWUgLSBWYWx1ZSB0byBzaWduLlxuICogQHJldHVybnMge1Byb21pc2U8c3RyaW5nPn0gLSBQcmVmaXhlZCBiYXNlNjR1cmwgc2lnbmF0dXJlLlxuICovXG5hc3luYyBmdW5jdGlvbiBzaWduU3RhYmxlSnNvbih7cHJpdmF0ZUtleSwgdmFsdWV9KSB7XG4gIGNvbnN0IGtleSA9IGF3YWl0IGNyeXB0b1N1YnRsZSgpLmltcG9ydEtleShcImp3a1wiLCBwcml2YXRlS2V5LCB7bmFtZTogXCJFQ0RTQVwiLCBuYW1lZEN1cnZlOiBcIlAtMjU2XCJ9LCBmYWxzZSwgW1wic2lnblwiXSlcbiAgY29uc3Qgc2lnbmF0dXJlID0gYXdhaXQgY3J5cHRvU3VidGxlKCkuc2lnbih7aGFzaDogXCJTSEEtMjU2XCIsIG5hbWU6IFwiRUNEU0FcIn0sIGtleSwgLyoqIEB0eXBlIHtCdWZmZXJTb3VyY2V9ICovICh1dGY4KHN0YWJsZUpzb25TdHJpbmdpZnkodmFsdWUpKSkpXG5cbiAgcmV0dXJuIGAke0VDRFNBX1AyNTZfU0hBMjU2X1NJR05BVFVSRV9QUkVGSVh9JHtiYXNlNjRVcmxFbmNvZGUobmV3IFVpbnQ4QXJyYXkoc2lnbmF0dXJlKSl9YFxufVxuXG4vKipcbiAqIFZlcmlmaWVzIGEgZGV0ZXJtaW5pc3RpYyBKU09OIHZhbHVlIHdpdGggRUNEU0EgUC0yNTYvU0hBLTI1Ni5cbiAqIEBwYXJhbSB7b2JqZWN0fSBhcmdzIC0gQXJndW1lbnRzLlxuICogQHBhcmFtIHtTeW5jSnNvbldlYktleX0gYXJncy5wdWJsaWNLZXkgLSBQdWJsaWMgSldLLlxuICogQHBhcmFtIHtzdHJpbmd9IGFyZ3Muc2lnbmF0dXJlIC0gUHJlZml4ZWQgYmFzZTY0dXJsIHNpZ25hdHVyZS5cbiAqIEBwYXJhbSB7dW5rbm93bn0gYXJncy52YWx1ZSAtIFZhbHVlIHRvIHZlcmlmeS5cbiAqIEByZXR1cm5zIHtQcm9taXNlPGJvb2xlYW4+fSAtIFdoZXRoZXIgdGhlIHNpZ25hdHVyZSBtYXRjaGVzLlxuICovXG5hc3luYyBmdW5jdGlvbiB2ZXJpZnlTdGFibGVKc29uU2lnbmF0dXJlKHtwdWJsaWNLZXksIHNpZ25hdHVyZSwgdmFsdWV9KSB7XG4gIGNvbnN0IGtleSA9IGF3YWl0IGNyeXB0b1N1YnRsZSgpLmltcG9ydEtleShcImp3a1wiLCBwdWJsaWNLZXksIHtuYW1lOiBcIkVDRFNBXCIsIG5hbWVkQ3VydmU6IFwiUC0yNTZcIn0sIGZhbHNlLCBbXCJ2ZXJpZnlcIl0pXG4gIGNvbnN0IHNpZ25hdHVyZUJ5dGVzID0gYmFzZTY0VXJsRGVjb2RlKHNpZ25hdHVyZVdpdGhvdXRQcmVmaXgoc2lnbmF0dXJlKSlcblxuICByZXR1cm4gYXdhaXQgY3J5cHRvU3VidGxlKCkudmVyaWZ5KHtoYXNoOiBcIlNIQS0yNTZcIiwgbmFtZTogXCJFQ0RTQVwifSwga2V5LCAvKiogQHR5cGUge0J1ZmZlclNvdXJjZX0gKi8gKHNpZ25hdHVyZUJ5dGVzKSwgLyoqIEB0eXBlIHtCdWZmZXJTb3VyY2V9ICovICh1dGY4KHN0YWJsZUpzb25TdHJpbmdpZnkodmFsdWUpKSkpXG59XG5cbi8qKlxuICogTm9ybWFsaXplcyBhIHNpZ25lZCBkZXZpY2UgY2VydGlmaWNhdGUgZW52ZWxvcGUuXG4gKiBAcGFyYW0ge0RldmljZUNlcnRpZmljYXRlfSBjZXJ0aWZpY2F0ZSAtIFNpZ25lZCBjZXJ0aWZpY2F0ZS5cbiAqIEByZXR1cm5zIHtEZXZpY2VDZXJ0aWZpY2F0ZX0gLSBOb3JtYWxpemVkIGNlcnRpZmljYXRlLlxuICovXG5mdW5jdGlvbiBub3JtYWxpemVEZXZpY2VDZXJ0aWZpY2F0ZShjZXJ0aWZpY2F0ZSkge1xuICBpZiAoIWNlcnRpZmljYXRlIHx8IHR5cGVvZiBjZXJ0aWZpY2F0ZSAhPT0gXCJvYmplY3RcIiB8fCBBcnJheS5pc0FycmF5KGNlcnRpZmljYXRlKSkgdGhyb3cgbmV3IEVycm9yKFwiRXhwZWN0ZWQgZGV2aWNlIGNlcnRpZmljYXRlIG9iamVjdFwiKVxuICBpZiAoY2VydGlmaWNhdGUuYWxnb3JpdGhtICE9PSBFQ0RTQV9QMjU2X1NIQTI1Nl9BTEdPUklUSE0pIHRocm93IG5ldyBFcnJvcihgVW5zdXBwb3J0ZWQgZGV2aWNlIGNlcnRpZmljYXRlIGFsZ29yaXRobSAnJHtTdHJpbmcoY2VydGlmaWNhdGUuYWxnb3JpdGhtKX0nYClcblxuICByZXR1cm4ge1xuICAgIGFsZ29yaXRobTogY2VydGlmaWNhdGUuYWxnb3JpdGhtLFxuICAgIGNlcnRpZmljYXRlOiBub3JtYWxpemVEZXZpY2VDZXJ0aWZpY2F0ZVBheWxvYWQoY2VydGlmaWNhdGUuY2VydGlmaWNhdGUpLFxuICAgIHNpZ25hdHVyZTogbm9ybWFsaXplU2lnbmF0dXJlKGNlcnRpZmljYXRlLnNpZ25hdHVyZSwgXCJkZXZpY2UgY2VydGlmaWNhdGUgc2lnbmF0dXJlXCIpXG4gIH1cbn1cblxuLyoqXG4gKiBOb3JtYWxpemVzIGEgZGV2aWNlIGNlcnRpZmljYXRlIHBheWxvYWQuXG4gKiBAcGFyYW0ge0RldmljZUNlcnRpZmljYXRlUGF5bG9hZH0gY2VydGlmaWNhdGUgLSBDZXJ0aWZpY2F0ZSBwYXlsb2FkLlxuICogQHJldHVybnMge0RldmljZUNlcnRpZmljYXRlUGF5bG9hZH0gLSBOb3JtYWxpemVkIGNlcnRpZmljYXRlIHBheWxvYWQuXG4gKi9cbmZ1bmN0aW9uIG5vcm1hbGl6ZURldmljZUNlcnRpZmljYXRlUGF5bG9hZChjZXJ0aWZpY2F0ZSkge1xuICBpZiAoIWNlcnRpZmljYXRlIHx8IHR5cGVvZiBjZXJ0aWZpY2F0ZSAhPT0gXCJvYmplY3RcIiB8fCBBcnJheS5pc0FycmF5KGNlcnRpZmljYXRlKSkgdGhyb3cgbmV3IEVycm9yKFwiRXhwZWN0ZWQgZGV2aWNlIGNlcnRpZmljYXRlIHBheWxvYWQgb2JqZWN0XCIpXG5cbiAgcmV0dXJuIHtcbiAgICBhY3RvckRldmljZUlkOiByZXF1aXJlZFN0cmluZyhjZXJ0aWZpY2F0ZS5hY3RvckRldmljZUlkLCBcImFjdG9yRGV2aWNlSWRcIiksXG4gICAgYWN0b3JVc2VySWQ6IHJlcXVpcmVkU3RyaW5nKGNlcnRpZmljYXRlLmFjdG9yVXNlcklkLCBcImFjdG9yVXNlcklkXCIpLFxuICAgIGNlcnRpZmljYXRlSWQ6IHJlcXVpcmVkU3RyaW5nKGNlcnRpZmljYXRlLmNlcnRpZmljYXRlSWQsIFwiY2VydGlmaWNhdGVJZFwiKSxcbiAgICBkZXZpY2VQdWJsaWNLZXk6IG5vcm1hbGl6ZUp3ayhjZXJ0aWZpY2F0ZS5kZXZpY2VQdWJsaWNLZXksIFwiZGV2aWNlUHVibGljS2V5XCIpLFxuICAgIGV4cGlyZXNBdDogaXNvRGF0ZVN0cmluZyhuZXcgRGF0ZShyZXF1aXJlZFN0cmluZyhjZXJ0aWZpY2F0ZS5leHBpcmVzQXQsIFwiZXhwaXJlc0F0XCIpKSwgXCJleHBpcmVzQXRcIiksXG4gICAgaXNzdWVkQXQ6IGlzb0RhdGVTdHJpbmcobmV3IERhdGUocmVxdWlyZWRTdHJpbmcoY2VydGlmaWNhdGUuaXNzdWVkQXQsIFwiaXNzdWVkQXRcIikpLCBcImlzc3VlZEF0XCIpXG4gIH1cbn1cblxuLyoqXG4gKiBOb3JtYWxpemVzIGEgc2lnbmVkIHN5bmMgbXV0YXRpb24gZW52ZWxvcGUuXG4gKiBAcGFyYW0ge1NpZ25lZFN5bmNNdXRhdGlvbn0gc2lnbmVkTXV0YXRpb24gLSBTaWduZWQgbXV0YXRpb24uXG4gKiBAcmV0dXJucyB7U2lnbmVkU3luY011dGF0aW9ufSAtIE5vcm1hbGl6ZWQgc2lnbmVkIG11dGF0aW9uLlxuICovXG5mdW5jdGlvbiBub3JtYWxpemVTaWduZWRTeW5jTXV0YXRpb24oc2lnbmVkTXV0YXRpb24pIHtcbiAgaWYgKCFzaWduZWRNdXRhdGlvbiB8fCB0eXBlb2Ygc2lnbmVkTXV0YXRpb24gIT09IFwib2JqZWN0XCIgfHwgQXJyYXkuaXNBcnJheShzaWduZWRNdXRhdGlvbikpIHRocm93IG5ldyBFcnJvcihcIkV4cGVjdGVkIHNpZ25lZCBzeW5jIG11dGF0aW9uIG9iamVjdFwiKVxuICBpZiAoc2lnbmVkTXV0YXRpb24uYWxnb3JpdGhtICE9PSBFQ0RTQV9QMjU2X1NIQTI1Nl9BTEdPUklUSE0pIHRocm93IG5ldyBFcnJvcihgVW5zdXBwb3J0ZWQgc3luYyBtdXRhdGlvbiBhbGdvcml0aG0gJyR7U3RyaW5nKHNpZ25lZE11dGF0aW9uLmFsZ29yaXRobSl9J2ApXG5cbiAgcmV0dXJuIHtcbiAgICBhbGdvcml0aG06IHNpZ25lZE11dGF0aW9uLmFsZ29yaXRobSxcbiAgICBkZXZpY2VDZXJ0aWZpY2F0ZTogbm9ybWFsaXplRGV2aWNlQ2VydGlmaWNhdGUoc2lnbmVkTXV0YXRpb24uZGV2aWNlQ2VydGlmaWNhdGUpLFxuICAgIG11dGF0aW9uOiBub3JtYWxpemVTeW5jTXV0YXRpb24oc2lnbmVkTXV0YXRpb24ubXV0YXRpb24pLFxuICAgIHNpZ25hdHVyZTogbm9ybWFsaXplU2lnbmF0dXJlKHNpZ25lZE11dGF0aW9uLnNpZ25hdHVyZSwgXCJzeW5jIG11dGF0aW9uIHNpZ25hdHVyZVwiKVxuICB9XG59XG5cbi8qKlxuICogTm9ybWFsaXplcyBhIHN5bmMgbXV0YXRpb24gcGF5bG9hZC5cbiAqIEBwYXJhbSB7U3luY011dGF0aW9ufSBtdXRhdGlvbiAtIE11dGF0aW9uIHBheWxvYWQuXG4gKiBAcmV0dXJucyB7U3luY011dGF0aW9ufSAtIE5vcm1hbGl6ZWQgbXV0YXRpb24gcGF5bG9hZC5cbiAqL1xuZnVuY3Rpb24gbm9ybWFsaXplU3luY011dGF0aW9uKG11dGF0aW9uKSB7XG4gIGlmICghbXV0YXRpb24gfHwgdHlwZW9mIG11dGF0aW9uICE9PSBcIm9iamVjdFwiIHx8IEFycmF5LmlzQXJyYXkobXV0YXRpb24pKSB0aHJvdyBuZXcgRXJyb3IoXCJFeHBlY3RlZCBzeW5jIG11dGF0aW9uIG9iamVjdFwiKVxuXG4gIC8qKiBAdHlwZSB7U3luY011dGF0aW9ufSAqL1xuICBjb25zdCBub3JtYWxpemVkID0ge1xuICAgIGFjdG9yRGV2aWNlSWQ6IHJlcXVpcmVkU3RyaW5nKG11dGF0aW9uLmFjdG9yRGV2aWNlSWQsIFwiYWN0b3JEZXZpY2VJZFwiKSxcbiAgICBhY3RvclVzZXJJZDogcmVxdWlyZWRTdHJpbmcobXV0YXRpb24uYWN0b3JVc2VySWQsIFwiYWN0b3JVc2VySWRcIiksXG4gICAgY2xpZW50TXV0YXRpb25JZDogcmVxdWlyZWRTdHJpbmcobXV0YXRpb24uY2xpZW50TXV0YXRpb25JZCwgXCJjbGllbnRNdXRhdGlvbklkXCIpLFxuICAgIG1vZGVsOiByZXF1aXJlZFN0cmluZyhtdXRhdGlvbi5tb2RlbCwgXCJtb2RlbFwiKSxcbiAgICBvY2N1cnJlZEF0OiBpc29EYXRlU3RyaW5nKG5ldyBEYXRlKHJlcXVpcmVkU3RyaW5nKG11dGF0aW9uLm9jY3VycmVkQXQsIFwib2NjdXJyZWRBdFwiKSksIFwib2NjdXJyZWRBdFwiKSxcbiAgICBvZmZsaW5lR3JhbnRJZDogcmVxdWlyZWRTdHJpbmcobXV0YXRpb24ub2ZmbGluZUdyYW50SWQsIFwib2ZmbGluZUdyYW50SWRcIiksXG4gICAgb3BlcmF0aW9uOiByZXF1aXJlZFN0cmluZyhtdXRhdGlvbi5vcGVyYXRpb24sIFwib3BlcmF0aW9uXCIpLFxuICAgIHBvbGljeUhhc2g6IHJlcXVpcmVkU3RyaW5nKG11dGF0aW9uLnBvbGljeUhhc2gsIFwicG9saWN5SGFzaFwiKVxuICB9XG5cbiAgaWYgKG11dGF0aW9uLmF0dHJpYnV0ZXMgIT09IHVuZGVmaW5lZCkgbm9ybWFsaXplZC5hdHRyaWJ1dGVzID0gZGV0ZXJtaW5pc3RpY0pzb25PYmplY3Qoe2xhYmVsOiBcImF0dHJpYnV0ZXNcIiwgdmFsdWU6IG11dGF0aW9uLmF0dHJpYnV0ZXN9KVxuICBpZiAobXV0YXRpb24uYmFzZVZlcnNpb24gIT09IHVuZGVmaW5lZCkgbm9ybWFsaXplZC5iYXNlVmVyc2lvbiA9IC8qKiBAdHlwZSB7c3RyaW5nIHwgbnVtYmVyIHwgbnVsbH0gKi8gKGRldGVybWluaXN0aWNKc29uKHtsYWJlbDogXCJiYXNlVmVyc2lvblwiLCB2YWx1ZTogbXV0YXRpb24uYmFzZVZlcnNpb259KSlcbiAgaWYgKG11dGF0aW9uLmNvbW1hbmQgIT09IHVuZGVmaW5lZCkgbm9ybWFsaXplZC5jb21tYW5kID0gcmVxdWlyZWRTdHJpbmcobXV0YXRpb24uY29tbWFuZCwgXCJjb21tYW5kXCIpXG4gIGlmIChtdXRhdGlvbi5wYXlsb2FkICE9PSB1bmRlZmluZWQpIG5vcm1hbGl6ZWQucGF5bG9hZCA9IGRldGVybWluaXN0aWNKc29uT2JqZWN0KHtsYWJlbDogXCJwYXlsb2FkXCIsIHZhbHVlOiBtdXRhdGlvbi5wYXlsb2FkfSlcblxuICByZXR1cm4gbm9ybWFsaXplZFxufVxuXG4vKipcbiAqIEFzc2VydHMgdGhhdCBtdXRhdGlvbiBhY3RvciBmaWVsZHMgbWF0Y2ggdGhlIGNlcnRpZmljYXRlIGFjdG9yLlxuICogQHBhcmFtIHtvYmplY3R9IGFyZ3MgLSBBcmd1bWVudHMuXG4gKiBAcGFyYW0ge0RldmljZUNlcnRpZmljYXRlUGF5bG9hZH0gYXJncy5jZXJ0aWZpY2F0ZSAtIERldmljZSBjZXJ0aWZpY2F0ZSBwYXlsb2FkLlxuICogQHBhcmFtIHtTeW5jTXV0YXRpb259IGFyZ3MubXV0YXRpb24gLSBNdXRhdGlvbiBwYXlsb2FkLlxuICogQHJldHVybnMge3ZvaWR9IC0gTm8gcmV0dXJuIHZhbHVlLlxuICovXG5mdW5jdGlvbiBhc3NlcnRNdXRhdGlvbk1hdGNoZXNDZXJ0aWZpY2F0ZSh7Y2VydGlmaWNhdGUsIG11dGF0aW9ufSkge1xuICBpZiAobXV0YXRpb24uYWN0b3JEZXZpY2VJZCAhPT0gY2VydGlmaWNhdGUuYWN0b3JEZXZpY2VJZCB8fCBtdXRhdGlvbi5hY3RvclVzZXJJZCAhPT0gY2VydGlmaWNhdGUuYWN0b3JVc2VySWQpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXCJTeW5jIG11dGF0aW9uIGFjdG9yIGRvZXMgbm90IG1hdGNoIGRldmljZSBjZXJ0aWZpY2F0ZVwiKVxuICB9XG59XG5cbi8qKlxuICogQXNzZXJ0cyBhIHRpbWVzdGFtcCBpcyBub3QgZXhwaXJlZC5cbiAqIEBwYXJhbSB7b2JqZWN0fSBhcmdzIC0gQXJndW1lbnRzLlxuICogQHBhcmFtIHtzdHJpbmd9IGFyZ3MuZXhwaXJlc0F0IC0gRXhwaXJ5IHRpbWVzdGFtcC5cbiAqIEBwYXJhbSB7c3RyaW5nfSBhcmdzLmxhYmVsIC0gRXJyb3IgbGFiZWwuXG4gKiBAcGFyYW0ge0RhdGV9IGFyZ3Mubm93IC0gVmVyaWZpY2F0aW9uIHRpbWUuXG4gKiBAcmV0dXJucyB7dm9pZH0gLSBObyByZXR1cm4gdmFsdWUuXG4gKi9cbmZ1bmN0aW9uIGFzc2VydE5vdEV4cGlyZWQoe2V4cGlyZXNBdCwgbGFiZWwsIG5vd30pIHtcbiAgaWYgKE51bWJlci5pc05hTihub3cuZ2V0VGltZSgpKSkgdGhyb3cgbmV3IEVycm9yKFwiSW52YWxpZCBzeW5jIHZlcmlmaWNhdGlvbiB0aW1lXCIpXG4gIGlmIChuZXcgRGF0ZShleHBpcmVzQXQpLmdldFRpbWUoKSA8PSBub3cuZ2V0VGltZSgpKSB0aHJvdyBuZXcgRXJyb3IoYCR7bGFiZWx9IGV4cGlyZWRgKVxufVxuXG4vKipcbiAqIE5vcm1hbGl6ZXMgYSBKU09OIFdlYiBLZXkgb2JqZWN0LlxuICogQHBhcmFtIHtTeW5jSnNvbldlYktleX0ga2V5IC0gSldLLlxuICogQHBhcmFtIHtzdHJpbmd9IGxhYmVsIC0gS2V5IGxhYmVsLlxuICogQHJldHVybnMge1N5bmNKc29uV2ViS2V5fSAtIE5vcm1hbGl6ZWQgSldLLlxuICovXG5mdW5jdGlvbiBub3JtYWxpemVKd2soa2V5LCBsYWJlbCkge1xuICBjb25zdCBub3JtYWxpemVkID0gZGV0ZXJtaW5pc3RpY0pzb25PYmplY3Qoe2xhYmVsLCB2YWx1ZToga2V5fSlcblxuICByZXR1cm4gLyoqIEB0eXBlIHtTeW5jSnNvbldlYktleX0gKi8gKG5vcm1hbGl6ZWQpXG59XG5cbi8qKlxuICogTm9ybWFsaXplcyBhIHByZWZpeGVkIHNpZ25hdHVyZSBzdHJpbmcuXG4gKiBAcGFyYW0ge3Vua25vd259IHNpZ25hdHVyZSAtIFNpZ25hdHVyZSB2YWx1ZS5cbiAqIEBwYXJhbSB7c3RyaW5nfSBsYWJlbCAtIEVycm9yIGxhYmVsLlxuICogQHJldHVybnMge3N0cmluZ30gLSBTaWduYXR1cmUgc3RyaW5nLlxuICovXG5mdW5jdGlvbiBub3JtYWxpemVTaWduYXR1cmUoc2lnbmF0dXJlLCBsYWJlbCkge1xuICBpZiAodHlwZW9mIHNpZ25hdHVyZSAhPT0gXCJzdHJpbmdcIiB8fCAhc2lnbmF0dXJlLnN0YXJ0c1dpdGgoRUNEU0FfUDI1Nl9TSEEyNTZfU0lHTkFUVVJFX1BSRUZJWCkpIHRocm93IG5ldyBFcnJvcihgRXhwZWN0ZWQgJHtsYWJlbH1gKVxuXG4gIHJldHVybiBzaWduYXR1cmVcbn1cblxuLyoqXG4gKiBSZW1vdmVzIHRoZSBzaWduYXR1cmUgcHJlZml4LlxuICogQHBhcmFtIHtzdHJpbmd9IHNpZ25hdHVyZSAtIFByZWZpeGVkIHNpZ25hdHVyZS5cbiAqIEByZXR1cm5zIHtzdHJpbmd9IC0gQmFzZTY0dXJsIHNpZ25hdHVyZSBib2R5LlxuICovXG5mdW5jdGlvbiBzaWduYXR1cmVXaXRob3V0UHJlZml4KHNpZ25hdHVyZSkge1xuICByZXR1cm4gc2lnbmF0dXJlLnNsaWNlKEVDRFNBX1AyNTZfU0hBMjU2X1NJR05BVFVSRV9QUkVGSVgubGVuZ3RoKVxufVxuXG4vKipcbiAqIFJlcXVpcmVzIGEgbm9uLWVtcHR5IHN0cmluZyBmaWVsZC5cbiAqIEBwYXJhbSB7dW5rbm93bn0gdmFsdWUgLSBWYWx1ZS5cbiAqIEBwYXJhbSB7c3RyaW5nfSBuYW1lIC0gRmllbGQgbmFtZS5cbiAqIEByZXR1cm5zIHtzdHJpbmd9IC0gU3RyaW5nIHZhbHVlLlxuICovXG5mdW5jdGlvbiByZXF1aXJlZFN0cmluZyh2YWx1ZSwgbmFtZSkge1xuICBpZiAodHlwZW9mIHZhbHVlICE9PSBcInN0cmluZ1wiIHx8IHZhbHVlLmxlbmd0aCA8IDEpIHRocm93IG5ldyBFcnJvcihgRXhwZWN0ZWQgc3luYyAke25hbWV9YClcblxuICByZXR1cm4gdmFsdWVcbn1cblxuLyoqXG4gKiBOb3JtYWxpemVzIGEgRGF0ZSB0byBhbiBJU08gdGltZXN0YW1wLlxuICogQHBhcmFtIHtEYXRlfSBkYXRlIC0gRGF0ZSB2YWx1ZS5cbiAqIEBwYXJhbSB7c3RyaW5nfSBsYWJlbCAtIEZpZWxkIGxhYmVsLlxuICogQHJldHVybnMge3N0cmluZ30gLSBJU08gdGltZXN0YW1wLlxuICovXG5mdW5jdGlvbiBpc29EYXRlU3RyaW5nKGRhdGUsIGxhYmVsKSB7XG4gIGlmIChOdW1iZXIuaXNOYU4oZGF0ZS5nZXRUaW1lKCkpKSB0aHJvdyBuZXcgRXJyb3IoYEludmFsaWQgc3luYyAke2xhYmVsfWApXG5cbiAgcmV0dXJuIGRhdGUudG9JU09TdHJpbmcoKVxufVxuXG4vKipcbiAqIFJldHVybnMgZW5jb2RlZCBVVEYtOCBieXRlcyBmb3IgYSBzdHJpbmcuXG4gKiBAcGFyYW0ge3N0cmluZ30gdmFsdWUgLSBTdHJpbmcgdmFsdWUuXG4gKiBAcmV0dXJucyB7VWludDhBcnJheX0gLSBVVEYtOCBieXRlcy5cbiAqL1xuZnVuY3Rpb24gdXRmOCh2YWx1ZSkge1xuICByZXR1cm4gbmV3IFRleHRFbmNvZGVyKCkuZW5jb2RlKHZhbHVlKVxufVxuXG4vKipcbiAqIFJldHVybnMgV2ViQ3J5cHRvIHN1YnRsZSBBUEkuXG4gKiBAcmV0dXJucyB7U3VidGxlQ3J5cHRvfSAtIFN1YnRsZUNyeXB0byBpbXBsZW1lbnRhdGlvbi5cbiAqL1xuZnVuY3Rpb24gY3J5cHRvU3VidGxlKCkge1xuICBpZiAoIWdsb2JhbFRoaXMuY3J5cHRvPy5zdWJ0bGUpIHRocm93IG5ldyBFcnJvcihcIldlYkNyeXB0byBzdWJ0bGUgQVBJIGlzIHJlcXVpcmVkIGZvciBzeW5jIHNpZ25pbmdcIilcblxuICByZXR1cm4gZ2xvYmFsVGhpcy5jcnlwdG8uc3VidGxlXG59XG5cbi8qKlxuICogQmFzZTY0dXJsLWVuY29kZXMgYnl0ZXMuXG4gKiBAcGFyYW0ge1VpbnQ4QXJyYXl9IGJ5dGVzIC0gQnl0ZXMuXG4gKiBAcmV0dXJucyB7c3RyaW5nfSAtIEJhc2U2NHVybCBzdHJpbmcuXG4gKi9cbmZ1bmN0aW9uIGJhc2U2NFVybEVuY29kZShieXRlcykge1xuICBsZXQgYmluYXJ5ID0gXCJcIlxuXG4gIGZvciAoY29uc3QgYnl0ZSBvZiBieXRlcykgYmluYXJ5ICs9IFN0cmluZy5mcm9tQ2hhckNvZGUoYnl0ZSlcblxuICByZXR1cm4gYnRvYShiaW5hcnkpLnJlcGxhY2VBbGwoXCIrXCIsIFwiLVwiKS5yZXBsYWNlQWxsKFwiL1wiLCBcIl9cIikucmVwbGFjZUFsbChcIj1cIiwgXCJcIilcbn1cblxuLyoqXG4gKiBCYXNlNjR1cmwtZGVjb2RlcyBieXRlcy5cbiAqIEBwYXJhbSB7c3RyaW5nfSB2YWx1ZSAtIEJhc2U2NHVybCBzdHJpbmcuXG4gKiBAcmV0dXJucyB7VWludDhBcnJheX0gLSBCeXRlcy5cbiAqL1xuZnVuY3Rpb24gYmFzZTY0VXJsRGVjb2RlKHZhbHVlKSB7XG4gIGNvbnN0IGJhc2U2NCA9IHZhbHVlLnJlcGxhY2VBbGwoXCItXCIsIFwiK1wiKS5yZXBsYWNlQWxsKFwiX1wiLCBcIi9cIikucGFkRW5kKE1hdGguY2VpbCh2YWx1ZS5sZW5ndGggLyA0KSAqIDQsIFwiPVwiKVxuICBjb25zdCBiaW5hcnkgPSBhdG9iKGJhc2U2NClcblxuICByZXR1cm4gVWludDhBcnJheS5mcm9tKGJpbmFyeSwgKGNoYXIpID0+IGNoYXIuY2hhckNvZGVBdCgwKSlcbn1cblxuLyoqXG4gKiBTdGFibGUgSlNPTiBzdHJpbmdpZmllciB3aXRoIHNvcnRlZCBvYmplY3Qga2V5cy5cbiAqIEBwYXJhbSB7dW5rbm93bn0gdmFsdWUgLSBWYWx1ZS5cbiAqIEByZXR1cm5zIHtzdHJpbmd9IC0gSlNPTiBzdHJpbmcuXG4gKi9cbmZ1bmN0aW9uIHN0YWJsZUpzb25TdHJpbmdpZnkodmFsdWUpIHtcbiAgcmV0dXJuIEpTT04uc3RyaW5naWZ5KGRldGVybWluaXN0aWNKc29uKHtsYWJlbDogXCJyb290XCIsIHZhbHVlfSkpXG59XG5cbi8qKlxuICogTm9ybWFsaXplcyBhIHZhbHVlIGFzIGEgSlNPTiBvYmplY3QuXG4gKiBAcGFyYW0ge29iamVjdH0gYXJncyAtIEFyZ3VtZW50cy5cbiAqIEBwYXJhbSB7c3RyaW5nfSBhcmdzLmxhYmVsIC0gRGlhZ25vc3RpYyBsYWJlbC5cbiAqIEBwYXJhbSB7dW5rbm93bn0gYXJncy52YWx1ZSAtIFZhbHVlLlxuICogQHJldHVybnMge1JlY29yZDxzdHJpbmcsIGltcG9ydChcIi4uL2NvbmZpZ3VyYXRpb24tdHlwZXMuanNcIikuRnJvbnRlbmRNb2RlbFN5bmNKc29uVmFsdWU+fSAtIEpTT04gb2JqZWN0LlxuICovXG5mdW5jdGlvbiBkZXRlcm1pbmlzdGljSnNvbk9iamVjdCh7bGFiZWwsIHZhbHVlfSkge1xuICBjb25zdCBub3JtYWxpemVkID0gZGV0ZXJtaW5pc3RpY0pzb24oe2xhYmVsLCB2YWx1ZX0pXG5cbiAgaWYgKCFub3JtYWxpemVkIHx8IHR5cGVvZiBub3JtYWxpemVkICE9PSBcIm9iamVjdFwiIHx8IEFycmF5LmlzQXJyYXkobm9ybWFsaXplZCkpIHRocm93IG5ldyBFcnJvcihgRXhwZWN0ZWQgc3luYyAke2xhYmVsfSBvYmplY3RgKVxuXG4gIHJldHVybiAvKiogQHR5cGUge1JlY29yZDxzdHJpbmcsIGltcG9ydChcIi4uL2NvbmZpZ3VyYXRpb24tdHlwZXMuanNcIikuRnJvbnRlbmRNb2RlbFN5bmNKc29uVmFsdWU+fSAqLyAobm9ybWFsaXplZClcbn1cblxuLyoqXG4gKiBOb3JtYWxpemVzIGRldGVybWluaXN0aWMgSlNPTiB3aXRoIHNvcnRlZCBvYmplY3Qga2V5cy5cbiAqIEBwYXJhbSB7b2JqZWN0fSBhcmdzIC0gQXJndW1lbnRzLlxuICogQHBhcmFtIHtzdHJpbmd9IGFyZ3MubGFiZWwgLSBEaWFnbm9zdGljIGxhYmVsLlxuICogQHBhcmFtIHt1bmtub3dufSBhcmdzLnZhbHVlIC0gVmFsdWUuXG4gKiBAcmV0dXJucyB7aW1wb3J0KFwiLi4vY29uZmlndXJhdGlvbi10eXBlcy5qc1wiKS5Gcm9udGVuZE1vZGVsU3luY0pzb25WYWx1ZX0gLSBOb3JtYWxpemVkIEpTT04gdmFsdWUuXG4gKi9cbmZ1bmN0aW9uIGRldGVybWluaXN0aWNKc29uKHtsYWJlbCwgdmFsdWV9KSB7XG4gIGlmICh2YWx1ZSA9PT0gbnVsbCB8fCB0eXBlb2YgdmFsdWUgPT09IFwic3RyaW5nXCIgfHwgdHlwZW9mIHZhbHVlID09PSBcIm51bWJlclwiIHx8IHR5cGVvZiB2YWx1ZSA9PT0gXCJib29sZWFuXCIpIHJldHVybiB2YWx1ZVxuXG4gIGlmIChBcnJheS5pc0FycmF5KHZhbHVlKSkgcmV0dXJuIHZhbHVlLm1hcCgoZW50cnksIGluZGV4KSA9PiBkZXRlcm1pbmlzdGljSnNvbih7bGFiZWw6IGAke2xhYmVsfS8ke2luZGV4fWAsIHZhbHVlOiBlbnRyeX0pKVxuXG4gIGlmICh2YWx1ZSAmJiB0eXBlb2YgdmFsdWUgPT09IFwib2JqZWN0XCIgJiYgT2JqZWN0LmdldFByb3RvdHlwZU9mKHZhbHVlKSA9PT0gT2JqZWN0LnByb3RvdHlwZSkge1xuICAgIC8qKiBAdHlwZSB7UmVjb3JkPHN0cmluZywgaW1wb3J0KFwiLi4vY29uZmlndXJhdGlvbi10eXBlcy5qc1wiKS5Gcm9udGVuZE1vZGVsU3luY0pzb25WYWx1ZT59ICovXG4gICAgY29uc3Qgbm9ybWFsaXplZCA9IHt9XG5cbiAgICBmb3IgKGNvbnN0IGtleSBvZiBPYmplY3Qua2V5cyh2YWx1ZSkuc29ydCgpKSB7XG4gICAgICBjb25zdCBjaGlsZFZhbHVlID0gLyoqIEB0eXBlIHtSZWNvcmQ8c3RyaW5nLCB1bmtub3duPn0gKi8gKHZhbHVlKVtrZXldXG5cbiAgICAgIGlmIChjaGlsZFZhbHVlID09PSB1bmRlZmluZWQpIGNvbnRpbnVlXG4gICAgICBub3JtYWxpemVkW2tleV0gPSBkZXRlcm1pbmlzdGljSnNvbih7bGFiZWw6IGAke2xhYmVsfS8ke2tleX1gLCB2YWx1ZTogY2hpbGRWYWx1ZX0pXG4gICAgfVxuXG4gICAgcmV0dXJuIG5vcm1hbGl6ZWRcbiAgfVxuXG4gIHRocm93IG5ldyBFcnJvcihgU3luYyAke2xhYmVsfSBtdXN0IGJlIGRldGVybWluaXN0aWMgSlNPTmApXG59XG4iXX0=
|