@floegence/redevplugin-contracts 0.0.0-dev
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/dist/contracts.gen.d.ts +595 -0
- package/dist/contracts.gen.js +706 -0
- package/dist/index.d.ts +310 -0
- package/dist/index.js +33 -0
- package/dist/release-signing.d.ts +434 -0
- package/dist/release-signing.js +1152 -0
- package/package.json +32 -0
|
@@ -0,0 +1,1152 @@
|
|
|
1
|
+
export const rootDelegationSchemaVersion = "redevplugin.release_root_delegation.v1";
|
|
2
|
+
export const packageSignatureSchemaVersion = "redevplugin.package_signature.v1";
|
|
3
|
+
export const releaseMetadataSchemaVersion = "redevplugin.release_metadata.v5";
|
|
4
|
+
export const sourcePolicySchemaVersion = "redevplugin.release_source_policy.v2";
|
|
5
|
+
export const sourcePolicyPointerSchemaVersion = "redevplugin.release_source_policy_pointer.v1";
|
|
6
|
+
export const revocationSchemaVersion = "redevplugin.release_revocation.v2";
|
|
7
|
+
export const revocationPointerSchemaVersion = "redevplugin.release_revocation_pointer.v1";
|
|
8
|
+
export const releaseSigningLedgerEvidenceSchemaVersion = "redevplugin.release_signing_ledger_evidence.v1";
|
|
9
|
+
export const signingSubjectSchemaVersion = "redevplugin.release_signing_subject.v1";
|
|
10
|
+
export const signatureEnvelopeSchemaVersion = "redevplugin.release_signature_envelope.v1";
|
|
11
|
+
export const signingLedgerSchemaVersion = "redevplugin.release_signing_ledger.v1";
|
|
12
|
+
export const signingLedgerEntrySchemaVersion = "redevplugin.release_signing_ledger_entry.v1";
|
|
13
|
+
export const signingLedgerLogLeafSchemaVersion = "redevplugin.release_signing_ledger_log_leaf.v1";
|
|
14
|
+
export const signingLedgerReceiptSchemaVersion = "redevplugin.release_signing_ledger_receipt.v1";
|
|
15
|
+
export const signatureAlgorithmEd25519 = "ed25519";
|
|
16
|
+
export const genesisPreviousEpoch = "0";
|
|
17
|
+
export const genesisPreviousDocumentSHA256 = "0".repeat(64);
|
|
18
|
+
export const signingUsages = {
|
|
19
|
+
rootDelegation: "redevplugin.release-signing.root-delegation.v1",
|
|
20
|
+
package: "redevplugin.release-signing.package.v1",
|
|
21
|
+
releaseMetadata: "redevplugin.release-signing.release-metadata.v1",
|
|
22
|
+
sourcePolicy: "redevplugin.release-signing.source-policy-document.v1",
|
|
23
|
+
sourcePolicyPointer: "redevplugin.release-signing.source-policy-pointer.v1",
|
|
24
|
+
revocation: "redevplugin.release-signing.revocation-document.v1",
|
|
25
|
+
revocationPointer: "redevplugin.release-signing.revocation-pointer.v1",
|
|
26
|
+
};
|
|
27
|
+
export const defaultSourcePolicyLimits = Object.freeze({
|
|
28
|
+
document_max_lifetime_seconds: 86400,
|
|
29
|
+
future_skew_seconds: 300,
|
|
30
|
+
activation_lease_max_seconds: 300,
|
|
31
|
+
refresh_interval_max_seconds: 60,
|
|
32
|
+
failure_teardown_deadline_seconds: 30,
|
|
33
|
+
});
|
|
34
|
+
export class InvalidReleaseDocumentError extends TypeError {
|
|
35
|
+
constructor() {
|
|
36
|
+
super("release contract document is invalid");
|
|
37
|
+
this.name = "InvalidReleaseDocumentError";
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export class InvalidReleaseSignatureError extends Error {
|
|
41
|
+
constructor() {
|
|
42
|
+
super("release contract signature is invalid");
|
|
43
|
+
this.name = "InvalidReleaseSignatureError";
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const textEncoder = new TextEncoder();
|
|
47
|
+
const textDecoder = new TextDecoder("utf-8", { fatal: true });
|
|
48
|
+
const maximumDocumentBytes = 1024 * 1024;
|
|
49
|
+
const signingPrefix = textEncoder.encode("REDEVPLUGIN-SIGNING-V1\0");
|
|
50
|
+
const newIDPattern = /^[a-z][a-z0-9._-]{0,127}$/;
|
|
51
|
+
const legacyIDPattern = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
52
|
+
const epochPattern = /^(0|[1-9][0-9]*)$/;
|
|
53
|
+
const positiveEpochPattern = /^[1-9][0-9]*$/;
|
|
54
|
+
const sha256Pattern = /^[0-9a-f]{64}$/;
|
|
55
|
+
const prefixedSHA256Pattern = /^sha256:[0-9a-f]{64}$/;
|
|
56
|
+
const legacySHA256Pattern = /^(?:sha256:)?[0-9a-f]{64}$/;
|
|
57
|
+
const artifactRefPattern = /^[A-Za-z0-9._/@+-]+$/;
|
|
58
|
+
const hostnamePattern = /^[A-Za-z0-9](?:[A-Za-z0-9.-]*[A-Za-z0-9])?$/;
|
|
59
|
+
const semverPattern = /^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(?:-(?:(?:0|[1-9][0-9]*|[0-9A-Za-z-]*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9][0-9]*|[0-9A-Za-z-]*[A-Za-z-][0-9A-Za-z-]*))*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
60
|
+
const canonicalTimePattern = /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$/;
|
|
61
|
+
const publicKeyPattern = /^[A-Za-z0-9+/]{43}=$/;
|
|
62
|
+
const signaturePattern = /^[A-Za-z0-9+/]{86}==$/;
|
|
63
|
+
const delegatedUsageOrder = [
|
|
64
|
+
"package",
|
|
65
|
+
"release_metadata",
|
|
66
|
+
"host_capability_contract",
|
|
67
|
+
"revocation_document",
|
|
68
|
+
"revocation_pointer",
|
|
69
|
+
"source_policy_document",
|
|
70
|
+
"source_policy_pointer",
|
|
71
|
+
"signing_ledger",
|
|
72
|
+
"trusted_time",
|
|
73
|
+
];
|
|
74
|
+
function invalidDocument() {
|
|
75
|
+
throw new InvalidReleaseDocumentError();
|
|
76
|
+
}
|
|
77
|
+
function isRecord(value) {
|
|
78
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
79
|
+
}
|
|
80
|
+
function assertRecord(value) {
|
|
81
|
+
if (!isRecord(value))
|
|
82
|
+
invalidDocument();
|
|
83
|
+
}
|
|
84
|
+
function matchesPattern(pattern, value) {
|
|
85
|
+
return typeof value === "string" && pattern.test(value);
|
|
86
|
+
}
|
|
87
|
+
function exactKeys(value, keys) {
|
|
88
|
+
const actual = Object.keys(value).sort(compareUTF8);
|
|
89
|
+
const expected = [...keys].sort(compareUTF8);
|
|
90
|
+
if (actual.length !== expected.length || actual.some((key, index) => key !== expected[index])) {
|
|
91
|
+
invalidDocument();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function exactOptionalKeys(value, required, optional) {
|
|
95
|
+
const allowed = new Set([...required, ...optional]);
|
|
96
|
+
if (required.some((key) => !(key in value)) || Object.keys(value).some((key) => !allowed.has(key))) {
|
|
97
|
+
invalidDocument();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function compareUTF8(left, right) {
|
|
101
|
+
const leftBytes = textEncoder.encode(left);
|
|
102
|
+
const rightBytes = textEncoder.encode(right);
|
|
103
|
+
const length = Math.min(leftBytes.length, rightBytes.length);
|
|
104
|
+
for (let index = 0; index < length; index += 1) {
|
|
105
|
+
if (leftBytes[index] !== rightBytes[index])
|
|
106
|
+
return leftBytes[index] - rightBytes[index];
|
|
107
|
+
}
|
|
108
|
+
return leftBytes.length - rightBytes.length;
|
|
109
|
+
}
|
|
110
|
+
function assertWellFormedString(value) {
|
|
111
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
112
|
+
const code = value.charCodeAt(index);
|
|
113
|
+
if (code >= 0xd800 && code <= 0xdbff) {
|
|
114
|
+
const next = value.charCodeAt(index + 1);
|
|
115
|
+
if (!(next >= 0xdc00 && next <= 0xdfff))
|
|
116
|
+
invalidDocument();
|
|
117
|
+
index += 1;
|
|
118
|
+
}
|
|
119
|
+
else if (code >= 0xdc00 && code <= 0xdfff) {
|
|
120
|
+
invalidDocument();
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
function canonicalJSONString(value) {
|
|
125
|
+
if (value === null)
|
|
126
|
+
return "null";
|
|
127
|
+
if (typeof value === "boolean")
|
|
128
|
+
return value ? "true" : "false";
|
|
129
|
+
if (typeof value === "string") {
|
|
130
|
+
assertWellFormedString(value);
|
|
131
|
+
return JSON.stringify(value);
|
|
132
|
+
}
|
|
133
|
+
if (typeof value === "number") {
|
|
134
|
+
if (!Number.isSafeInteger(value) || value < 0)
|
|
135
|
+
invalidDocument();
|
|
136
|
+
return String(value);
|
|
137
|
+
}
|
|
138
|
+
if (Array.isArray(value)) {
|
|
139
|
+
return `[${value.map((item) => canonicalJSONString(item)).join(",")}]`;
|
|
140
|
+
}
|
|
141
|
+
if (isRecord(value)) {
|
|
142
|
+
const keys = Object.keys(value).filter((key) => value[key] !== undefined).sort(compareUTF8);
|
|
143
|
+
return `{${keys.map((key) => `${canonicalJSONString(key)}:${canonicalJSONString(value[key])}`).join(",")}}`;
|
|
144
|
+
}
|
|
145
|
+
invalidDocument();
|
|
146
|
+
}
|
|
147
|
+
function canonicalJSON(value) {
|
|
148
|
+
return textEncoder.encode(canonicalJSONString(value));
|
|
149
|
+
}
|
|
150
|
+
function signingPreimage(usage, value) {
|
|
151
|
+
const domain = textEncoder.encode(usage);
|
|
152
|
+
const payload = canonicalJSON(value);
|
|
153
|
+
const result = new Uint8Array(signingPrefix.length + domain.length + 1 + payload.length);
|
|
154
|
+
result.set(signingPrefix, 0);
|
|
155
|
+
result.set(domain, signingPrefix.length);
|
|
156
|
+
result[signingPrefix.length + domain.length] = 0;
|
|
157
|
+
result.set(payload, signingPrefix.length + domain.length + 1);
|
|
158
|
+
return result;
|
|
159
|
+
}
|
|
160
|
+
function preimageWithoutTopLevelSignature(usage, value) {
|
|
161
|
+
const payload = cloneJSON(value);
|
|
162
|
+
delete payload.signature;
|
|
163
|
+
return signingPreimage(usage, payload);
|
|
164
|
+
}
|
|
165
|
+
function cloneJSON(value) {
|
|
166
|
+
return JSON.parse(JSON.stringify(value));
|
|
167
|
+
}
|
|
168
|
+
function deepFreeze(value) {
|
|
169
|
+
if (value !== null && typeof value === "object" && !Object.isFrozen(value)) {
|
|
170
|
+
for (const nested of Object.values(value))
|
|
171
|
+
deepFreeze(nested);
|
|
172
|
+
Object.freeze(value);
|
|
173
|
+
}
|
|
174
|
+
return value;
|
|
175
|
+
}
|
|
176
|
+
function encodeBase64(bytes) {
|
|
177
|
+
let binary = "";
|
|
178
|
+
for (const byte of bytes)
|
|
179
|
+
binary += String.fromCharCode(byte);
|
|
180
|
+
return btoa(binary);
|
|
181
|
+
}
|
|
182
|
+
function decodeBase64(value, expectedLength) {
|
|
183
|
+
const pattern = expectedLength === 32 ? publicKeyPattern : signaturePattern;
|
|
184
|
+
if (!matchesPattern(pattern, value))
|
|
185
|
+
invalidDocument();
|
|
186
|
+
let binary;
|
|
187
|
+
try {
|
|
188
|
+
binary = atob(value);
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
invalidDocument();
|
|
192
|
+
}
|
|
193
|
+
if (binary.length !== expectedLength)
|
|
194
|
+
invalidDocument();
|
|
195
|
+
const result = Uint8Array.from(binary, (item) => item.charCodeAt(0));
|
|
196
|
+
if (encodeBase64(result) !== value)
|
|
197
|
+
invalidDocument();
|
|
198
|
+
return result;
|
|
199
|
+
}
|
|
200
|
+
function requireSignatureBytes(signature) {
|
|
201
|
+
if (!(signature instanceof Uint8Array) || signature.length !== 64)
|
|
202
|
+
invalidDocument();
|
|
203
|
+
}
|
|
204
|
+
function parseCanonicalTime(value) {
|
|
205
|
+
if (!matchesPattern(canonicalTimePattern, value))
|
|
206
|
+
invalidDocument();
|
|
207
|
+
const parsed = Date.parse(value);
|
|
208
|
+
if (!Number.isFinite(parsed) || new Date(parsed).toISOString() !== value.replace("Z", ".000Z")) {
|
|
209
|
+
invalidDocument();
|
|
210
|
+
}
|
|
211
|
+
return parsed;
|
|
212
|
+
}
|
|
213
|
+
function validateTimeRange(generatedAt, expiresAt, maximumMilliseconds = 0) {
|
|
214
|
+
const generated = parseCanonicalTime(generatedAt);
|
|
215
|
+
const expires = parseCanonicalTime(expiresAt);
|
|
216
|
+
if (expires <= generated || (maximumMilliseconds > 0 && expires - generated > maximumMilliseconds)) {
|
|
217
|
+
invalidDocument();
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
function validateEpochChain(epoch, previousEpoch, previousDigest) {
|
|
221
|
+
if (!matchesPattern(positiveEpochPattern, epoch) || !matchesPattern(epochPattern, previousEpoch) || !matchesPattern(sha256Pattern, previousDigest)) {
|
|
222
|
+
invalidDocument();
|
|
223
|
+
}
|
|
224
|
+
if (BigInt(previousEpoch) + 1n !== BigInt(epoch))
|
|
225
|
+
invalidDocument();
|
|
226
|
+
if (previousEpoch === genesisPreviousEpoch) {
|
|
227
|
+
if (previousDigest !== genesisPreviousDocumentSHA256)
|
|
228
|
+
invalidDocument();
|
|
229
|
+
}
|
|
230
|
+
else if (previousDigest === genesisPreviousDocumentSHA256) {
|
|
231
|
+
invalidDocument();
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
function validateSortedIDs(values, minimum, maximum, lower = true) {
|
|
235
|
+
if (!Array.isArray(values) || values.length < minimum || values.length > maximum)
|
|
236
|
+
invalidDocument();
|
|
237
|
+
let previous = "";
|
|
238
|
+
const pattern = lower ? newIDPattern : legacyIDPattern;
|
|
239
|
+
for (const value of values) {
|
|
240
|
+
if (!matchesPattern(pattern, value) || compareUTF8(value, previous) <= 0)
|
|
241
|
+
invalidDocument();
|
|
242
|
+
previous = value;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
function validArtifactRef(value) {
|
|
246
|
+
if (!matchesPattern(artifactRefPattern, value) || value.length < 1 || value.length > 1024 || value.startsWith("/") || value.includes("\\") || /[?#]/.test(value)) {
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
249
|
+
return value.split("/").every((segment) => segment !== "" && segment !== "." && segment !== "..");
|
|
250
|
+
}
|
|
251
|
+
function validateRootDelegation(value, requireSignature) {
|
|
252
|
+
assertRecord(value);
|
|
253
|
+
closeRootDelegation(value);
|
|
254
|
+
if (value.schema_version !== rootDelegationSchemaVersion || !matchesPattern(newIDPattern, value.source_id) || !matchesPattern(newIDPattern, value.key_id))
|
|
255
|
+
invalidDocument();
|
|
256
|
+
validateEpochChain(value.root_epoch, value.previous_root_epoch, value.previous_delegation_sha256);
|
|
257
|
+
validateTimeRange(value.generated_at, value.expires_at);
|
|
258
|
+
if (!Array.isArray(value.delegated_keys) || value.delegated_keys.length < 1 || value.delegated_keys.length > 32)
|
|
259
|
+
invalidDocument();
|
|
260
|
+
let previous = "";
|
|
261
|
+
for (const key of value.delegated_keys) {
|
|
262
|
+
if (key.algorithm !== signatureAlgorithmEd25519 || !matchesPattern(newIDPattern, key.key_id) || compareUTF8(key.key_id, previous) <= 0)
|
|
263
|
+
invalidDocument();
|
|
264
|
+
decodeBase64(key.public_key, 32);
|
|
265
|
+
if (!Array.isArray(key.usages) || key.usages.length < 1 || key.usages.length > 8)
|
|
266
|
+
invalidDocument();
|
|
267
|
+
let previousUsage = -1;
|
|
268
|
+
for (const usage of key.usages) {
|
|
269
|
+
const rank = delegatedUsageOrder.indexOf(usage);
|
|
270
|
+
if (rank < 0 || rank <= previousUsage)
|
|
271
|
+
invalidDocument();
|
|
272
|
+
previousUsage = rank;
|
|
273
|
+
}
|
|
274
|
+
const sourceWide = key.usages.every((usage) => usage === "signing_ledger" || usage === "trusted_time");
|
|
275
|
+
const hasSourceWide = key.usages.some((usage) => usage === "signing_ledger" || usage === "trusted_time");
|
|
276
|
+
if (hasSourceWide !== sourceWide)
|
|
277
|
+
invalidDocument();
|
|
278
|
+
validateSortedIDs(key.channels, sourceWide ? 0 : 1, sourceWide ? 0 : 16);
|
|
279
|
+
validateTimeRange(key.valid_from, key.valid_until);
|
|
280
|
+
if (parseCanonicalTime(key.valid_until) > parseCanonicalTime(value.expires_at))
|
|
281
|
+
invalidDocument();
|
|
282
|
+
previous = key.key_id;
|
|
283
|
+
}
|
|
284
|
+
if (requireSignature)
|
|
285
|
+
decodeBase64(value.signature, 64);
|
|
286
|
+
else if (value.signature !== "")
|
|
287
|
+
invalidDocument();
|
|
288
|
+
}
|
|
289
|
+
function validatePackageInput(value) {
|
|
290
|
+
assertRecord(value);
|
|
291
|
+
exactKeys(value, ["source_id", "channel", "version", "algorithm", "key_id", "publisher_id", "plugin_id", "package_hash", "manifest_hash", "entries_hash", "signed_at"]);
|
|
292
|
+
if (!matchesPattern(newIDPattern, value.source_id) || !matchesPattern(newIDPattern, value.channel) || value.algorithm !== signatureAlgorithmEd25519 || !matchesPattern(newIDPattern, value.key_id))
|
|
293
|
+
invalidDocument();
|
|
294
|
+
if (!matchesPattern(legacyIDPattern, value.publisher_id) || !matchesPattern(legacyIDPattern, value.plugin_id) || !matchesPattern(semverPattern, value.version))
|
|
295
|
+
invalidDocument();
|
|
296
|
+
if (!matchesPattern(prefixedSHA256Pattern, value.package_hash) || !matchesPattern(prefixedSHA256Pattern, value.manifest_hash) || !matchesPattern(prefixedSHA256Pattern, value.entries_hash))
|
|
297
|
+
invalidDocument();
|
|
298
|
+
parseCanonicalTime(value.signed_at);
|
|
299
|
+
}
|
|
300
|
+
function packageInputFromDocument(context, value) {
|
|
301
|
+
return {
|
|
302
|
+
...context,
|
|
303
|
+
algorithm: value.algorithm,
|
|
304
|
+
key_id: value.key_id,
|
|
305
|
+
publisher_id: value.publisher_id ?? "",
|
|
306
|
+
plugin_id: value.plugin_id ?? "",
|
|
307
|
+
package_hash: value.package_hash,
|
|
308
|
+
manifest_hash: value.manifest_hash,
|
|
309
|
+
entries_hash: value.entries_hash,
|
|
310
|
+
signed_at: value.signed_at ?? "",
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
function validatePackageSignature(context, value, requireSignature) {
|
|
314
|
+
assertRecord(context);
|
|
315
|
+
exactKeys(context, ["source_id", "channel", "version"]);
|
|
316
|
+
assertRecord(value);
|
|
317
|
+
closePackageSignature(value);
|
|
318
|
+
if (value.schema_version !== packageSignatureSchemaVersion || value.publisher_id === undefined || value.plugin_id === undefined || value.signed_at === undefined)
|
|
319
|
+
invalidDocument();
|
|
320
|
+
validatePackageInput(packageInputFromDocument(context, value));
|
|
321
|
+
if (requireSignature)
|
|
322
|
+
decodeBase64(value.signature, 64);
|
|
323
|
+
else if (value.signature !== "")
|
|
324
|
+
invalidDocument();
|
|
325
|
+
}
|
|
326
|
+
function validateReleaseMetadata(value) {
|
|
327
|
+
assertRecord(value);
|
|
328
|
+
closeReleaseMetadata(value);
|
|
329
|
+
if (value.schema_version !== releaseMetadataSchemaVersion || !matchesPattern(newIDPattern, value.source_id))
|
|
330
|
+
invalidDocument();
|
|
331
|
+
if (!matchesPattern(legacyIDPattern, value.publisher_id) || !matchesPattern(legacyIDPattern, value.plugin_id) || !matchesPattern(semverPattern, value.version) || !validArtifactRef(value.release_metadata_ref))
|
|
332
|
+
invalidDocument();
|
|
333
|
+
if ((value.distribution_ref.distribution !== "registry_ref" && value.distribution_ref.distribution !== "host_artifact_ref") || !validArtifactRef(value.distribution_ref.artifact_ref))
|
|
334
|
+
invalidDocument();
|
|
335
|
+
if (!legacySHA256Pattern.test(value.hashes.package_sha256) || !legacySHA256Pattern.test(value.hashes.manifest_sha256) || !legacySHA256Pattern.test(value.hashes.entries_sha256))
|
|
336
|
+
invalidDocument();
|
|
337
|
+
const metadataSignature = value.release_metadata_signature;
|
|
338
|
+
if (metadataSignature.algorithm !== signatureAlgorithmEd25519 || !matchesPattern(newIDPattern, metadataSignature.key_id) || !validArtifactRef(metadataSignature.signature_ref) || !matchesPattern(epochPattern, metadataSignature.source_policy_epoch) || !matchesPattern(epochPattern, metadataSignature.revocation_epoch))
|
|
339
|
+
invalidDocument();
|
|
340
|
+
const packageSignature = value.package_signature;
|
|
341
|
+
if (packageSignature.algorithm !== signatureAlgorithmEd25519 || !matchesPattern(newIDPattern, packageSignature.key_id) || !validArtifactRef(packageSignature.signature_bundle_ref) || !matchesPattern(epochPattern, packageSignature.source_policy_epoch) || !matchesPattern(epochPattern, packageSignature.revocation_epoch))
|
|
342
|
+
invalidDocument();
|
|
343
|
+
if (!matchesPattern(semverPattern, value.compatibility.min_redevplugin_version) || !matchesPattern(semverPattern, value.compatibility.min_runtime_version) || value.compatibility.ui_protocol_version !== "plugin-ui-v5")
|
|
344
|
+
invalidDocument();
|
|
345
|
+
if (value.compatibility.supported_targets !== undefined) {
|
|
346
|
+
if (!Array.isArray(value.compatibility.supported_targets))
|
|
347
|
+
invalidDocument();
|
|
348
|
+
const allowed = new Set(["darwin/amd64", "darwin/arm64", "linux/amd64", "linux/arm64"]);
|
|
349
|
+
let previous = "";
|
|
350
|
+
for (const target of value.compatibility.supported_targets) {
|
|
351
|
+
if (typeof target !== "string" || !allowed.has(target) || compareUTF8(target, previous) <= 0)
|
|
352
|
+
invalidDocument();
|
|
353
|
+
previous = target;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
let previousHost = "";
|
|
357
|
+
for (const requirement of value.host_requirements ?? []) {
|
|
358
|
+
if (!matchesPattern(legacyIDPattern, requirement.host_id) || compareUTF8(requirement.host_id, previousHost) <= 0)
|
|
359
|
+
invalidDocument();
|
|
360
|
+
if (requirement.min_host_version !== undefined && !matchesPattern(semverPattern, requirement.min_host_version))
|
|
361
|
+
invalidDocument();
|
|
362
|
+
let previousCapability = "";
|
|
363
|
+
for (const capability of requirement.required_capability_contracts ?? []) {
|
|
364
|
+
if (!matchesPattern(legacyIDPattern, capability.capability_id) || !matchesPattern(semverPattern, capability.capability_version))
|
|
365
|
+
invalidDocument();
|
|
366
|
+
const identity = `${capability.capability_id}\0${capability.capability_version}`;
|
|
367
|
+
if (compareUTF8(identity, previousCapability) <= 0)
|
|
368
|
+
invalidDocument();
|
|
369
|
+
validateCapabilityContractRef(capability.contract);
|
|
370
|
+
previousCapability = identity;
|
|
371
|
+
}
|
|
372
|
+
previousHost = requirement.host_id;
|
|
373
|
+
}
|
|
374
|
+
if (value.release_evidence !== undefined) {
|
|
375
|
+
if (value.release_evidence.notices_sha256 !== undefined && !legacySHA256Pattern.test(value.release_evidence.notices_sha256))
|
|
376
|
+
invalidDocument();
|
|
377
|
+
if (value.release_evidence.provenance_sha256 !== undefined && !legacySHA256Pattern.test(value.release_evidence.provenance_sha256))
|
|
378
|
+
invalidDocument();
|
|
379
|
+
if (value.release_evidence.generated_at !== undefined)
|
|
380
|
+
parseCanonicalTime(value.release_evidence.generated_at);
|
|
381
|
+
}
|
|
382
|
+
if (value.metadata !== undefined) {
|
|
383
|
+
const entries = Object.entries(value.metadata);
|
|
384
|
+
if (entries.length > 128 || entries.some(([key, item]) => {
|
|
385
|
+
if (key.length < 1 || key.length > 128 || typeof item !== "string" || item.length > 4096)
|
|
386
|
+
return true;
|
|
387
|
+
assertWellFormedString(key);
|
|
388
|
+
assertWellFormedString(item);
|
|
389
|
+
return false;
|
|
390
|
+
}))
|
|
391
|
+
invalidDocument();
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
function validateCapabilityContractRef(value) {
|
|
395
|
+
if (![value.publisher_id, value.contract_id, value.signature_key_id].every((item) => matchesPattern(legacyIDPattern, item)) || !matchesPattern(semverPattern, value.contract_version))
|
|
396
|
+
invalidDocument();
|
|
397
|
+
if (!matchesPattern(epochPattern, value.signature_policy_epoch) || !matchesPattern(epochPattern, value.signature_revocation_epoch))
|
|
398
|
+
invalidDocument();
|
|
399
|
+
if (![value.artifact_ref, value.manifest_ref, value.signature_ref, value.compatibility_ref, value.generated_client_ref, value.notices_ref].every(validArtifactRef))
|
|
400
|
+
invalidDocument();
|
|
401
|
+
if (![value.artifact_sha256, value.manifest_sha256, value.signature_sha256, value.compatibility_sha256, value.generated_client_sha256, value.notices_sha256].every((item) => sha256Pattern.test(item)))
|
|
402
|
+
invalidDocument();
|
|
403
|
+
}
|
|
404
|
+
function validateSourcePolicy(value, requireSignature) {
|
|
405
|
+
assertRecord(value);
|
|
406
|
+
closeSourcePolicy(value);
|
|
407
|
+
if (value.schema_version !== sourcePolicySchemaVersion || !matchesPattern(newIDPattern, value.source_id) || !matchesPattern(newIDPattern, value.channel) || !matchesPattern(newIDPattern, value.key_id))
|
|
408
|
+
invalidDocument();
|
|
409
|
+
validateEpochChain(value.epoch, value.previous_epoch, value.previous_document_sha256);
|
|
410
|
+
if (!matchesPattern(positiveEpochPattern, value.root_epoch) || !matchesPattern(epochPattern, value.minimum_revocation_epoch))
|
|
411
|
+
invalidDocument();
|
|
412
|
+
if (value.source_type !== "registry" && value.source_type !== "host_artifact")
|
|
413
|
+
invalidDocument();
|
|
414
|
+
if (!new Set(["official", "curated", "community", "private"]).has(value.source_class))
|
|
415
|
+
invalidDocument();
|
|
416
|
+
validateSortedIDs(value.allowed_publishers, 1, 1024);
|
|
417
|
+
if (!Array.isArray(value.allowed_artifact_hosts) || value.allowed_artifact_hosts.length > 1024)
|
|
418
|
+
invalidDocument();
|
|
419
|
+
let previousHost = "";
|
|
420
|
+
for (const host of value.allowed_artifact_hosts) {
|
|
421
|
+
if (!matchesPattern(hostnamePattern, host) || host.length > 253 || host.toLowerCase() !== host || compareUTF8(host, previousHost) <= 0)
|
|
422
|
+
invalidDocument();
|
|
423
|
+
previousHost = host;
|
|
424
|
+
}
|
|
425
|
+
for (const keys of [
|
|
426
|
+
value.active_keys.package,
|
|
427
|
+
value.active_keys.release_metadata,
|
|
428
|
+
value.active_keys.source_policy_pointer,
|
|
429
|
+
value.active_keys.revocation_document,
|
|
430
|
+
value.active_keys.revocation_pointer,
|
|
431
|
+
])
|
|
432
|
+
validateSortedIDs(keys, 1, 16);
|
|
433
|
+
validateSortedIDs(value.active_keys.host_capability_contract, 0, 16);
|
|
434
|
+
if (!Array.isArray(value.capability_publisher_scopes) || value.capability_publisher_scopes.length !== value.active_keys.host_capability_contract.length)
|
|
435
|
+
invalidDocument();
|
|
436
|
+
for (let index = 0; index < value.capability_publisher_scopes.length; index += 1) {
|
|
437
|
+
const scope = value.capability_publisher_scopes[index];
|
|
438
|
+
assertRecord(scope);
|
|
439
|
+
exactKeys(scope, ["key_id", "allowed_publishers"]);
|
|
440
|
+
if (scope.key_id !== value.active_keys.host_capability_contract[index])
|
|
441
|
+
invalidDocument();
|
|
442
|
+
validateSortedIDs(scope.allowed_publishers, 1, 1024, false);
|
|
443
|
+
}
|
|
444
|
+
if (!new Set(["allow", "review_required", "block"]).has(value.install_policy))
|
|
445
|
+
invalidDocument();
|
|
446
|
+
if (!new Set(["dev_only", "review_required", "block"]).has(value.unsigned_policy))
|
|
447
|
+
invalidDocument();
|
|
448
|
+
if (!new Set(["review_required", "block"]).has(value.downgrade_policy))
|
|
449
|
+
invalidDocument();
|
|
450
|
+
if (canonicalJSONString(value.limits) !== canonicalJSONString(defaultSourcePolicyLimits))
|
|
451
|
+
invalidDocument();
|
|
452
|
+
validateTimeRange(value.generated_at, value.expires_at, 24 * 60 * 60 * 1000);
|
|
453
|
+
if (requireSignature)
|
|
454
|
+
decodeBase64(value.signature, 64);
|
|
455
|
+
else if (value.signature !== "")
|
|
456
|
+
invalidDocument();
|
|
457
|
+
}
|
|
458
|
+
function validatePointer(value, expectedSchemaVersion, requireSignature) {
|
|
459
|
+
assertRecord(value);
|
|
460
|
+
closePointer(value);
|
|
461
|
+
if (value.schema_version !== expectedSchemaVersion || !matchesPattern(newIDPattern, value.source_id) || !matchesPattern(newIDPattern, value.channel) || !matchesPattern(newIDPattern, value.key_id))
|
|
462
|
+
invalidDocument();
|
|
463
|
+
validateEpochChain(value.epoch, value.previous_epoch, value.previous_document_sha256);
|
|
464
|
+
if (!validArtifactRef(value.ref) || !sha256Pattern.test(value.document_sha256) || value.document_sha256 === genesisPreviousDocumentSHA256)
|
|
465
|
+
invalidDocument();
|
|
466
|
+
validateTimeRange(value.generated_at, value.expires_at, 24 * 60 * 60 * 1000);
|
|
467
|
+
if (requireSignature)
|
|
468
|
+
decodeBase64(value.signature, 64);
|
|
469
|
+
else if (value.signature !== "")
|
|
470
|
+
invalidDocument();
|
|
471
|
+
}
|
|
472
|
+
function validateRevocation(value, requireSignature) {
|
|
473
|
+
assertRecord(value);
|
|
474
|
+
closeRevocation(value);
|
|
475
|
+
if (value.schema_version !== revocationSchemaVersion || !matchesPattern(newIDPattern, value.source_id) || !matchesPattern(newIDPattern, value.channel) || !matchesPattern(newIDPattern, value.key_id))
|
|
476
|
+
invalidDocument();
|
|
477
|
+
validateEpochChain(value.epoch, value.previous_epoch, value.previous_document_sha256);
|
|
478
|
+
if (!matchesPattern(positiveEpochPattern, value.root_epoch))
|
|
479
|
+
invalidDocument();
|
|
480
|
+
validateTimeRange(value.generated_at, value.expires_at, 24 * 60 * 60 * 1000);
|
|
481
|
+
validateSortedIDs(value.revoked_key_ids, 0, 4096);
|
|
482
|
+
if (!Array.isArray(value.revoked_releases) || value.revoked_releases.length > 16384)
|
|
483
|
+
invalidDocument();
|
|
484
|
+
let previous = "";
|
|
485
|
+
for (const revoked of value.revoked_releases) {
|
|
486
|
+
if (!matchesPattern(legacyIDPattern, revoked.publisher_id) || !matchesPattern(legacyIDPattern, revoked.plugin_id) || !matchesPattern(semverPattern, revoked.version) || !matchesPattern(sha256Pattern, revoked.release_metadata_sha256))
|
|
487
|
+
invalidDocument();
|
|
488
|
+
const identity = `${revoked.publisher_id}\0${revoked.plugin_id}\0${revoked.version}\0${revoked.release_metadata_sha256}`;
|
|
489
|
+
if (compareUTF8(identity, previous) <= 0)
|
|
490
|
+
invalidDocument();
|
|
491
|
+
if (parseCanonicalTime(revoked.revoked_at) > parseCanonicalTime(value.expires_at))
|
|
492
|
+
invalidDocument();
|
|
493
|
+
previous = identity;
|
|
494
|
+
}
|
|
495
|
+
if (requireSignature)
|
|
496
|
+
decodeBase64(value.signature, 64);
|
|
497
|
+
else if (value.signature !== "")
|
|
498
|
+
invalidDocument();
|
|
499
|
+
}
|
|
500
|
+
function validateSigningLedgerEvidence(value) {
|
|
501
|
+
assertRecord(value);
|
|
502
|
+
closeSigningLedgerEvidence(value);
|
|
503
|
+
if (value.schema_version !== releaseSigningLedgerEvidenceSchemaVersion || !matchesPattern(newIDPattern, value.source_id))
|
|
504
|
+
invalidDocument();
|
|
505
|
+
if (value.channel !== undefined && !matchesPattern(newIDPattern, value.channel))
|
|
506
|
+
invalidDocument();
|
|
507
|
+
for (const digest of [
|
|
508
|
+
value.subject_identity_sha256,
|
|
509
|
+
value.signing_preimage_sha256,
|
|
510
|
+
value.signature_envelope_sha256,
|
|
511
|
+
value.receipt_sha256,
|
|
512
|
+
value.checkpoint_sha256,
|
|
513
|
+
value.inclusion_proof_sha256,
|
|
514
|
+
value.latest_proof_sha256,
|
|
515
|
+
]) {
|
|
516
|
+
if (!sha256Pattern.test(digest))
|
|
517
|
+
invalidDocument();
|
|
518
|
+
}
|
|
519
|
+
for (const reference of [
|
|
520
|
+
value.receipt_ref,
|
|
521
|
+
value.checkpoint_ref,
|
|
522
|
+
value.inclusion_proof_ref,
|
|
523
|
+
value.latest_proof_ref,
|
|
524
|
+
]) {
|
|
525
|
+
if (!validArtifactRef(reference))
|
|
526
|
+
invalidDocument();
|
|
527
|
+
}
|
|
528
|
+
if ((value.consistency_proof_ref === undefined) !== (value.consistency_proof_sha256 === undefined))
|
|
529
|
+
invalidDocument();
|
|
530
|
+
if (value.consistency_proof_ref !== undefined && (!validArtifactRef(value.consistency_proof_ref) || !sha256Pattern.test(value.consistency_proof_sha256)))
|
|
531
|
+
invalidDocument();
|
|
532
|
+
}
|
|
533
|
+
function validateSigningSubject(value) {
|
|
534
|
+
assertRecord(value);
|
|
535
|
+
closeSigningSubject(value);
|
|
536
|
+
if (value.schema_version !== signingSubjectSchemaVersion || !matchesPattern(newIDPattern, value.source_id))
|
|
537
|
+
invalidDocument();
|
|
538
|
+
if (value.usage === "root_delegation") {
|
|
539
|
+
if (!matchesPattern(positiveEpochPattern, value.root_epoch))
|
|
540
|
+
invalidDocument();
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
if (value.usage === "package" || value.usage === "release_metadata") {
|
|
544
|
+
if (!matchesPattern(newIDPattern, value.channel) || !matchesPattern(legacyIDPattern, value.publisher_id) ||
|
|
545
|
+
!matchesPattern(legacyIDPattern, value.plugin_id) || !matchesPattern(semverPattern, value.version) ||
|
|
546
|
+
!matchesPattern(sha256Pattern, value.artifact_or_metadata_identity_sha256))
|
|
547
|
+
invalidDocument();
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
if (["source_policy_document", "source_policy_pointer", "revocation_document", "revocation_pointer"].includes(value.usage)) {
|
|
551
|
+
if (!matchesPattern(newIDPattern, value.channel) || !matchesPattern(positiveEpochPattern, value.epoch))
|
|
552
|
+
invalidDocument();
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
invalidDocument();
|
|
556
|
+
}
|
|
557
|
+
function validateSignatureEnvelope(value) {
|
|
558
|
+
assertRecord(value);
|
|
559
|
+
closeSignatureEnvelope(value);
|
|
560
|
+
if (value.schema_version !== signatureEnvelopeSchemaVersion || !matchesPattern(sha256Pattern, value.subject_identity_sha256) ||
|
|
561
|
+
!matchesPattern(sha256Pattern, value.signing_preimage_sha256) || value.algorithm !== signatureAlgorithmEd25519 ||
|
|
562
|
+
!matchesPattern(newIDPattern, value.key_id))
|
|
563
|
+
invalidDocument();
|
|
564
|
+
decodeBase64(value.signature, 64);
|
|
565
|
+
}
|
|
566
|
+
function validateSigningLedgerEntry(value) {
|
|
567
|
+
assertRecord(value);
|
|
568
|
+
closeSigningLedgerEntry(value);
|
|
569
|
+
if (value.schema_version !== signingLedgerEntrySchemaVersion || !matchesPattern(sha256Pattern, value.subject_identity_sha256) ||
|
|
570
|
+
!matchesPattern(sha256Pattern, value.signing_preimage_sha256) || value.algorithm !== signatureAlgorithmEd25519 ||
|
|
571
|
+
!matchesPattern(newIDPattern, value.key_id) || !Number.isSafeInteger(value.revision) || value.revision < 1)
|
|
572
|
+
invalidDocument();
|
|
573
|
+
validateSigningSubject(value.subject);
|
|
574
|
+
const reservedAt = parseCanonicalTime(value.reserved_at);
|
|
575
|
+
if (value.state === "reserved") {
|
|
576
|
+
if (value.signature_envelope !== undefined || value.signature_envelope_sha256 !== undefined || value.finalized_at !== undefined ||
|
|
577
|
+
value.failure_code !== undefined || value.failed_at !== undefined)
|
|
578
|
+
invalidDocument();
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
if (value.state === "finalized") {
|
|
582
|
+
if (value.signature_envelope === undefined || !matchesPattern(sha256Pattern, value.signature_envelope_sha256) ||
|
|
583
|
+
value.finalized_at === undefined || value.failure_code !== undefined || value.failed_at !== undefined)
|
|
584
|
+
invalidDocument();
|
|
585
|
+
validateSignatureEnvelope(value.signature_envelope);
|
|
586
|
+
if (value.signature_envelope.subject_identity_sha256 !== value.subject_identity_sha256 ||
|
|
587
|
+
value.signature_envelope.signing_preimage_sha256 !== value.signing_preimage_sha256 ||
|
|
588
|
+
value.signature_envelope.algorithm !== value.algorithm || value.signature_envelope.key_id !== value.key_id ||
|
|
589
|
+
parseCanonicalTime(value.finalized_at) < reservedAt)
|
|
590
|
+
invalidDocument();
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
if (value.state === "terminal_failed") {
|
|
594
|
+
if (value.signature_envelope !== undefined || value.signature_envelope_sha256 !== undefined || value.finalized_at !== undefined ||
|
|
595
|
+
value.failure_code === undefined || !["signer_rejected", "subject_conflict", "ledger_rejected"].includes(value.failure_code) ||
|
|
596
|
+
value.failed_at === undefined || parseCanonicalTime(value.failed_at) < reservedAt)
|
|
597
|
+
invalidDocument();
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
invalidDocument();
|
|
601
|
+
}
|
|
602
|
+
function validateSigningLedgerLogLeaf(value) {
|
|
603
|
+
assertRecord(value);
|
|
604
|
+
closeSigningLedgerLogLeaf(value);
|
|
605
|
+
if (value.schema_version !== signingLedgerLogLeafSchemaVersion || !matchesPattern(newIDPattern, value.source_id) ||
|
|
606
|
+
(value.channel !== undefined && !matchesPattern(newIDPattern, value.channel)) ||
|
|
607
|
+
!matchesPattern(sha256Pattern, value.subject_identity_sha256) || !matchesPattern(sha256Pattern, value.signing_preimage_sha256) ||
|
|
608
|
+
!matchesPattern(sha256Pattern, value.signature_envelope_sha256) || !Number.isSafeInteger(value.sequence) || value.sequence < 1)
|
|
609
|
+
invalidDocument();
|
|
610
|
+
}
|
|
611
|
+
function validateSigningLedgerCheckpoint(value) {
|
|
612
|
+
assertRecord(value);
|
|
613
|
+
closeSigningLedgerCheckpoint(value);
|
|
614
|
+
if (value.schema_version !== signingLedgerSchemaVersion || value.kind !== "checkpoint" || !matchesPattern(newIDPattern, value.log_id) ||
|
|
615
|
+
!Number.isSafeInteger(value.tree_size) || value.tree_size < 1 || !matchesPattern(sha256Pattern, value.log_root_hash) ||
|
|
616
|
+
!matchesPattern(sha256Pattern, value.latest_map_root_hash) || !matchesPattern(newIDPattern, value.key_id))
|
|
617
|
+
invalidDocument();
|
|
618
|
+
parseCanonicalTime(value.checkpoint_time);
|
|
619
|
+
decodeBase64(value.signature, 64);
|
|
620
|
+
}
|
|
621
|
+
function validateSigningLedgerReceipt(value) {
|
|
622
|
+
assertRecord(value);
|
|
623
|
+
closeSigningLedgerReceipt(value);
|
|
624
|
+
if (value.schema_version !== signingLedgerReceiptSchemaVersion || !matchesPattern(newIDPattern, value.log_id) ||
|
|
625
|
+
!matchesPattern(newIDPattern, value.source_id) || (value.channel !== undefined && !matchesPattern(newIDPattern, value.channel)) ||
|
|
626
|
+
!matchesPattern(sha256Pattern, value.subject_identity_sha256) || !matchesPattern(sha256Pattern, value.signing_preimage_sha256) ||
|
|
627
|
+
!matchesPattern(sha256Pattern, value.signature_envelope_sha256) || !Number.isSafeInteger(value.sequence) || value.sequence < 1 ||
|
|
628
|
+
!Number.isSafeInteger(value.leaf_index) || value.leaf_index !== value.sequence - 1 || !Number.isSafeInteger(value.tree_size) ||
|
|
629
|
+
value.tree_size < value.sequence || !matchesPattern(sha256Pattern, value.log_root_hash) ||
|
|
630
|
+
!matchesPattern(sha256Pattern, value.latest_map_root_hash) || !matchesPattern(sha256Pattern, value.checkpoint_sha256) ||
|
|
631
|
+
!matchesPattern(newIDPattern, value.key_id))
|
|
632
|
+
invalidDocument();
|
|
633
|
+
parseCanonicalTime(value.checkpoint_time);
|
|
634
|
+
decodeBase64(value.signature, 64);
|
|
635
|
+
}
|
|
636
|
+
function validateLedgerNodes(value, exact) {
|
|
637
|
+
if (!Array.isArray(value) || (exact === undefined ? value.length > 64 : value.length !== exact) ||
|
|
638
|
+
value.some((node) => !matchesPattern(sha256Pattern, node)))
|
|
639
|
+
invalidDocument();
|
|
640
|
+
}
|
|
641
|
+
function validateSigningLedgerInclusionProof(value) {
|
|
642
|
+
assertRecord(value);
|
|
643
|
+
closeSigningLedgerInclusionProof(value);
|
|
644
|
+
if (value.schema_version !== signingLedgerSchemaVersion || value.kind !== "inclusion_proof" || !matchesPattern(newIDPattern, value.log_id) ||
|
|
645
|
+
!Number.isSafeInteger(value.leaf_index) || value.leaf_index < 0 || !Number.isSafeInteger(value.tree_size) ||
|
|
646
|
+
value.tree_size < 1 || value.leaf_index >= value.tree_size)
|
|
647
|
+
invalidDocument();
|
|
648
|
+
validateLedgerNodes(value.nodes);
|
|
649
|
+
}
|
|
650
|
+
function validateSigningLedgerLatestProof(value) {
|
|
651
|
+
assertRecord(value);
|
|
652
|
+
closeSigningLedgerLatestProof(value);
|
|
653
|
+
if (value.schema_version !== signingLedgerSchemaVersion || value.kind !== "latest_proof" || !matchesPattern(newIDPattern, value.log_id) ||
|
|
654
|
+
!matchesPattern(sha256Pattern, value.subject_identity_sha256) || typeof value.present !== "boolean")
|
|
655
|
+
invalidDocument();
|
|
656
|
+
validateLedgerNodes(value.siblings, 256);
|
|
657
|
+
if (value.present) {
|
|
658
|
+
if (!Number.isSafeInteger(value.sequence) || value.sequence < 1 || !matchesPattern(sha256Pattern, value.signing_preimage_sha256) ||
|
|
659
|
+
!matchesPattern(sha256Pattern, value.signature_envelope_sha256))
|
|
660
|
+
invalidDocument();
|
|
661
|
+
}
|
|
662
|
+
else if (value.sequence !== undefined || value.signing_preimage_sha256 !== undefined || value.signature_envelope_sha256 !== undefined) {
|
|
663
|
+
invalidDocument();
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
function validateSigningLedgerConsistencyProof(value) {
|
|
667
|
+
assertRecord(value);
|
|
668
|
+
closeSigningLedgerConsistencyProof(value);
|
|
669
|
+
if (value.schema_version !== signingLedgerSchemaVersion || value.kind !== "consistency_proof" || !matchesPattern(newIDPattern, value.log_id) ||
|
|
670
|
+
!Number.isSafeInteger(value.old_tree_size) || value.old_tree_size < 1 || !Number.isSafeInteger(value.new_tree_size) ||
|
|
671
|
+
value.new_tree_size < value.old_tree_size)
|
|
672
|
+
invalidDocument();
|
|
673
|
+
validateLedgerNodes(value.nodes);
|
|
674
|
+
}
|
|
675
|
+
function decodeCanonicalDocument(raw, close, validate) {
|
|
676
|
+
const bytes = typeof raw === "string" ? textEncoder.encode(raw) : raw;
|
|
677
|
+
if (!(bytes instanceof Uint8Array) || bytes.length < 1 || bytes.length > maximumDocumentBytes)
|
|
678
|
+
invalidDocument();
|
|
679
|
+
let source;
|
|
680
|
+
try {
|
|
681
|
+
source = textDecoder.decode(bytes);
|
|
682
|
+
}
|
|
683
|
+
catch {
|
|
684
|
+
invalidDocument();
|
|
685
|
+
}
|
|
686
|
+
let parsed;
|
|
687
|
+
try {
|
|
688
|
+
parsed = JSON.parse(source);
|
|
689
|
+
}
|
|
690
|
+
catch {
|
|
691
|
+
invalidDocument();
|
|
692
|
+
}
|
|
693
|
+
assertRecord(parsed);
|
|
694
|
+
close(parsed);
|
|
695
|
+
validate(parsed);
|
|
696
|
+
if (canonicalJSONString(parsed) !== source)
|
|
697
|
+
invalidDocument();
|
|
698
|
+
return deepFreeze(cloneJSON(parsed));
|
|
699
|
+
}
|
|
700
|
+
function closeRootDelegation(value) {
|
|
701
|
+
exactKeys(value, ["schema_version", "source_id", "root_epoch", "previous_root_epoch", "previous_delegation_sha256", "generated_at", "expires_at", "delegated_keys", "key_id", "signature"]);
|
|
702
|
+
if (!Array.isArray(value.delegated_keys))
|
|
703
|
+
invalidDocument();
|
|
704
|
+
for (const item of value.delegated_keys) {
|
|
705
|
+
assertRecord(item);
|
|
706
|
+
exactKeys(item, ["algorithm", "key_id", "public_key", "usages", "channels", "valid_from", "valid_until"]);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
function closePackageSignature(value) {
|
|
710
|
+
exactOptionalKeys(value, ["schema_version", "algorithm", "key_id", "package_hash", "manifest_hash", "entries_hash", "signature"], ["publisher_id", "plugin_id", "signed_at"]);
|
|
711
|
+
}
|
|
712
|
+
function closeReleaseMetadata(value) {
|
|
713
|
+
exactOptionalKeys(value, ["schema_version", "source_id", "release_metadata_ref", "publisher_id", "plugin_id", "version", "distribution_ref", "hashes", "release_metadata_signature", "package_signature", "compatibility"], ["host_requirements", "release_evidence", "metadata"]);
|
|
714
|
+
for (const [key, keys] of [
|
|
715
|
+
["distribution_ref", ["distribution", "artifact_ref"]],
|
|
716
|
+
["hashes", ["package_sha256", "manifest_sha256", "entries_sha256"]],
|
|
717
|
+
["release_metadata_signature", ["algorithm", "key_id", "signature_ref", "source_policy_epoch", "revocation_epoch"]],
|
|
718
|
+
["package_signature", ["algorithm", "key_id", "signature_bundle_ref", "source_policy_epoch", "revocation_epoch"]],
|
|
719
|
+
]) {
|
|
720
|
+
assertRecord(value[key]);
|
|
721
|
+
exactKeys(value[key], keys);
|
|
722
|
+
}
|
|
723
|
+
assertRecord(value.compatibility);
|
|
724
|
+
exactOptionalKeys(value.compatibility, ["min_redevplugin_version", "min_runtime_version", "ui_protocol_version"], ["supported_targets"]);
|
|
725
|
+
if (value.host_requirements !== undefined) {
|
|
726
|
+
if (!Array.isArray(value.host_requirements))
|
|
727
|
+
invalidDocument();
|
|
728
|
+
for (const host of value.host_requirements) {
|
|
729
|
+
assertRecord(host);
|
|
730
|
+
exactOptionalKeys(host, ["host_id"], ["min_host_version", "required_capability_contracts"]);
|
|
731
|
+
if (host.required_capability_contracts !== undefined) {
|
|
732
|
+
if (!Array.isArray(host.required_capability_contracts))
|
|
733
|
+
invalidDocument();
|
|
734
|
+
for (const requirement of host.required_capability_contracts) {
|
|
735
|
+
assertRecord(requirement);
|
|
736
|
+
exactKeys(requirement, ["capability_id", "capability_version", "contract"]);
|
|
737
|
+
assertRecord(requirement.contract);
|
|
738
|
+
exactKeys(requirement.contract, ["publisher_id", "contract_id", "contract_version", "artifact_ref", "artifact_sha256", "manifest_ref", "manifest_sha256", "signature_ref", "signature_sha256", "signature_key_id", "signature_policy_epoch", "signature_revocation_epoch", "compatibility_ref", "compatibility_sha256", "generated_client_ref", "generated_client_sha256", "notices_ref", "notices_sha256"]);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
if (value.release_evidence !== undefined) {
|
|
744
|
+
assertRecord(value.release_evidence);
|
|
745
|
+
exactOptionalKeys(value.release_evidence, [], ["notices_sha256", "provenance_sha256", "generated_at"]);
|
|
746
|
+
}
|
|
747
|
+
if (value.metadata !== undefined)
|
|
748
|
+
assertRecord(value.metadata);
|
|
749
|
+
}
|
|
750
|
+
function closeSourcePolicy(value) {
|
|
751
|
+
exactKeys(value, ["schema_version", "source_id", "channel", "epoch", "previous_epoch", "previous_document_sha256", "root_epoch", "source_type", "source_class", "allowed_publishers", "allowed_artifact_hosts", "active_keys", "capability_publisher_scopes", "require_signature", "install_policy", "unsigned_policy", "downgrade_policy", "minimum_revocation_epoch", "limits", "generated_at", "expires_at", "key_id", "signature"]);
|
|
752
|
+
assertRecord(value.active_keys);
|
|
753
|
+
exactKeys(value.active_keys, ["package", "release_metadata", "host_capability_contract", "source_policy_pointer", "revocation_document", "revocation_pointer"]);
|
|
754
|
+
assertRecord(value.limits);
|
|
755
|
+
exactKeys(value.limits, ["document_max_lifetime_seconds", "future_skew_seconds", "activation_lease_max_seconds", "refresh_interval_max_seconds", "failure_teardown_deadline_seconds"]);
|
|
756
|
+
}
|
|
757
|
+
function closePointer(value) {
|
|
758
|
+
exactKeys(value, ["schema_version", "source_id", "channel", "epoch", "previous_epoch", "previous_document_sha256", "ref", "document_sha256", "generated_at", "expires_at", "key_id", "signature"]);
|
|
759
|
+
}
|
|
760
|
+
function closeRevocation(value) {
|
|
761
|
+
exactKeys(value, ["schema_version", "source_id", "channel", "epoch", "previous_epoch", "previous_document_sha256", "root_epoch", "generated_at", "expires_at", "revoked_key_ids", "revoked_releases", "key_id", "signature"]);
|
|
762
|
+
if (!Array.isArray(value.revoked_releases))
|
|
763
|
+
invalidDocument();
|
|
764
|
+
for (const item of value.revoked_releases) {
|
|
765
|
+
assertRecord(item);
|
|
766
|
+
exactKeys(item, ["publisher_id", "plugin_id", "version", "release_metadata_sha256", "revoked_at"]);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
function closeSigningLedgerEvidence(value) {
|
|
770
|
+
exactOptionalKeys(value, [
|
|
771
|
+
"schema_version",
|
|
772
|
+
"source_id",
|
|
773
|
+
"subject_identity_sha256",
|
|
774
|
+
"signing_preimage_sha256",
|
|
775
|
+
"signature_envelope_sha256",
|
|
776
|
+
"receipt_ref",
|
|
777
|
+
"receipt_sha256",
|
|
778
|
+
"checkpoint_ref",
|
|
779
|
+
"checkpoint_sha256",
|
|
780
|
+
"inclusion_proof_ref",
|
|
781
|
+
"inclusion_proof_sha256",
|
|
782
|
+
"latest_proof_ref",
|
|
783
|
+
"latest_proof_sha256",
|
|
784
|
+
], ["channel", "consistency_proof_ref", "consistency_proof_sha256"]);
|
|
785
|
+
}
|
|
786
|
+
function closeSigningSubject(value) {
|
|
787
|
+
if (value.usage === "root_delegation") {
|
|
788
|
+
exactKeys(value, ["schema_version", "usage", "source_id", "root_epoch"]);
|
|
789
|
+
}
|
|
790
|
+
else if (value.usage === "package" || value.usage === "release_metadata") {
|
|
791
|
+
exactKeys(value, ["schema_version", "usage", "source_id", "channel", "publisher_id", "plugin_id", "version", "artifact_or_metadata_identity_sha256"]);
|
|
792
|
+
}
|
|
793
|
+
else {
|
|
794
|
+
exactKeys(value, ["schema_version", "usage", "source_id", "channel", "epoch"]);
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
function closeSignatureEnvelope(value) {
|
|
798
|
+
exactKeys(value, ["schema_version", "subject_identity_sha256", "signing_preimage_sha256", "algorithm", "key_id", "signature"]);
|
|
799
|
+
}
|
|
800
|
+
function closeSigningLedgerEntry(value) {
|
|
801
|
+
exactOptionalKeys(value, [
|
|
802
|
+
"schema_version", "state", "subject", "subject_identity_sha256", "signing_preimage_sha256",
|
|
803
|
+
"algorithm", "key_id", "revision", "reserved_at",
|
|
804
|
+
], ["signature_envelope", "signature_envelope_sha256", "finalized_at", "failure_code", "failed_at"]);
|
|
805
|
+
assertRecord(value.subject);
|
|
806
|
+
closeSigningSubject(value.subject);
|
|
807
|
+
if (value.signature_envelope !== undefined) {
|
|
808
|
+
assertRecord(value.signature_envelope);
|
|
809
|
+
closeSignatureEnvelope(value.signature_envelope);
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
function closeSigningLedgerLogLeaf(value) {
|
|
813
|
+
exactOptionalKeys(value, [
|
|
814
|
+
"schema_version", "source_id", "subject_identity_sha256", "signing_preimage_sha256", "signature_envelope_sha256", "sequence",
|
|
815
|
+
], ["channel"]);
|
|
816
|
+
}
|
|
817
|
+
function closeSigningLedgerCheckpoint(value) {
|
|
818
|
+
exactKeys(value, [
|
|
819
|
+
"schema_version", "kind", "log_id", "tree_size", "log_root_hash", "latest_map_root_hash", "checkpoint_time", "key_id", "signature",
|
|
820
|
+
]);
|
|
821
|
+
}
|
|
822
|
+
function closeSigningLedgerReceipt(value) {
|
|
823
|
+
exactOptionalKeys(value, [
|
|
824
|
+
"schema_version", "log_id", "source_id", "subject_identity_sha256", "signing_preimage_sha256",
|
|
825
|
+
"signature_envelope_sha256", "sequence", "leaf_index", "tree_size", "log_root_hash", "latest_map_root_hash",
|
|
826
|
+
"checkpoint_sha256", "checkpoint_time", "key_id", "signature",
|
|
827
|
+
], ["channel"]);
|
|
828
|
+
}
|
|
829
|
+
function closeSigningLedgerInclusionProof(value) {
|
|
830
|
+
exactKeys(value, ["schema_version", "kind", "log_id", "leaf_index", "tree_size", "nodes"]);
|
|
831
|
+
}
|
|
832
|
+
function closeSigningLedgerLatestProof(value) {
|
|
833
|
+
exactOptionalKeys(value, ["schema_version", "kind", "log_id", "subject_identity_sha256", "present", "siblings"], [
|
|
834
|
+
"sequence", "signing_preimage_sha256", "signature_envelope_sha256",
|
|
835
|
+
]);
|
|
836
|
+
}
|
|
837
|
+
function closeSigningLedgerConsistencyProof(value) {
|
|
838
|
+
exactKeys(value, ["schema_version", "kind", "log_id", "old_tree_size", "new_tree_size", "nodes"]);
|
|
839
|
+
}
|
|
840
|
+
export function buildRootDelegation(input, signature) {
|
|
841
|
+
requireSignatureBytes(signature);
|
|
842
|
+
const document = {
|
|
843
|
+
schema_version: rootDelegationSchemaVersion,
|
|
844
|
+
...cloneJSON(input),
|
|
845
|
+
signature: encodeBase64(signature),
|
|
846
|
+
};
|
|
847
|
+
validateRootDelegation(document, true);
|
|
848
|
+
return deepFreeze(document);
|
|
849
|
+
}
|
|
850
|
+
export function rootDelegationSigningPreimage(input) {
|
|
851
|
+
const document = { schema_version: rootDelegationSchemaVersion, ...cloneJSON(input), signature: "" };
|
|
852
|
+
validateRootDelegation(document, false);
|
|
853
|
+
return preimageWithoutTopLevelSignature(signingUsages.rootDelegation, document);
|
|
854
|
+
}
|
|
855
|
+
export function canonicalRootDelegation(document) {
|
|
856
|
+
validateRootDelegation(document, true);
|
|
857
|
+
return canonicalJSON(document);
|
|
858
|
+
}
|
|
859
|
+
export async function verifyRootDelegation(document, verifier) {
|
|
860
|
+
await verifyEncoded(signingUsages.rootDelegation, document.key_id, rootDelegationSigningPreimage(unsignedDocument(document)), document.signature, verifier);
|
|
861
|
+
}
|
|
862
|
+
export function buildPackageSignature(input, signature) {
|
|
863
|
+
requireSignatureBytes(signature);
|
|
864
|
+
validatePackageInput(input);
|
|
865
|
+
const document = {
|
|
866
|
+
schema_version: packageSignatureSchemaVersion,
|
|
867
|
+
algorithm: input.algorithm,
|
|
868
|
+
key_id: input.key_id,
|
|
869
|
+
publisher_id: input.publisher_id,
|
|
870
|
+
plugin_id: input.plugin_id,
|
|
871
|
+
package_hash: input.package_hash,
|
|
872
|
+
manifest_hash: input.manifest_hash,
|
|
873
|
+
entries_hash: input.entries_hash,
|
|
874
|
+
signature: encodeBase64(signature),
|
|
875
|
+
signed_at: input.signed_at,
|
|
876
|
+
};
|
|
877
|
+
validatePackageSignature({ source_id: input.source_id, channel: input.channel, version: input.version }, document, true);
|
|
878
|
+
return deepFreeze(document);
|
|
879
|
+
}
|
|
880
|
+
export function packageSigningPreimage(input) {
|
|
881
|
+
validatePackageInput(input);
|
|
882
|
+
return signingPreimage(signingUsages.package, {
|
|
883
|
+
channel: input.channel,
|
|
884
|
+
package_signature: {
|
|
885
|
+
algorithm: input.algorithm,
|
|
886
|
+
entries_hash: input.entries_hash,
|
|
887
|
+
key_id: input.key_id,
|
|
888
|
+
manifest_hash: input.manifest_hash,
|
|
889
|
+
package_hash: input.package_hash,
|
|
890
|
+
plugin_id: input.plugin_id,
|
|
891
|
+
publisher_id: input.publisher_id,
|
|
892
|
+
schema_version: packageSignatureSchemaVersion,
|
|
893
|
+
signed_at: input.signed_at,
|
|
894
|
+
},
|
|
895
|
+
source_id: input.source_id,
|
|
896
|
+
version: input.version,
|
|
897
|
+
});
|
|
898
|
+
}
|
|
899
|
+
export function canonicalPackageSignature(context, document) {
|
|
900
|
+
validatePackageSignature(context, document, true);
|
|
901
|
+
return canonicalJSON(document);
|
|
902
|
+
}
|
|
903
|
+
export async function verifyPackageSignature(context, document, verifier) {
|
|
904
|
+
validatePackageSignature(context, document, true);
|
|
905
|
+
const input = packageInputFromDocument(context, document);
|
|
906
|
+
await verifyEncoded(signingUsages.package, document.key_id, packageSigningPreimage(input), document.signature, verifier);
|
|
907
|
+
}
|
|
908
|
+
export function buildReleaseMetadata(document) {
|
|
909
|
+
const cloned = cloneJSON(document);
|
|
910
|
+
validateReleaseMetadata(cloned);
|
|
911
|
+
return deepFreeze(cloned);
|
|
912
|
+
}
|
|
913
|
+
export function releaseMetadataSigningPreimage(channel, document) {
|
|
914
|
+
if (!matchesPattern(newIDPattern, channel))
|
|
915
|
+
invalidDocument();
|
|
916
|
+
const built = buildReleaseMetadata(document);
|
|
917
|
+
return signingPreimage(signingUsages.releaseMetadata, { channel, release_metadata: built });
|
|
918
|
+
}
|
|
919
|
+
export function canonicalReleaseMetadata(document) {
|
|
920
|
+
return canonicalJSON(buildReleaseMetadata(document));
|
|
921
|
+
}
|
|
922
|
+
export async function verifyReleaseMetadata(channel, document, signature, verifier) {
|
|
923
|
+
if (!(signature instanceof Uint8Array) || signature.length !== 64) {
|
|
924
|
+
throw new InvalidReleaseSignatureError();
|
|
925
|
+
}
|
|
926
|
+
await verifyRaw(signingUsages.releaseMetadata, document.release_metadata_signature.key_id, releaseMetadataSigningPreimage(channel, document), signature, verifier);
|
|
927
|
+
}
|
|
928
|
+
export function buildSourcePolicy(input, signature) {
|
|
929
|
+
requireSignatureBytes(signature);
|
|
930
|
+
const document = {
|
|
931
|
+
schema_version: sourcePolicySchemaVersion,
|
|
932
|
+
...cloneJSON(input),
|
|
933
|
+
signature: encodeBase64(signature),
|
|
934
|
+
};
|
|
935
|
+
validateSourcePolicy(document, true);
|
|
936
|
+
return deepFreeze(document);
|
|
937
|
+
}
|
|
938
|
+
export function sourcePolicySigningPreimage(input) {
|
|
939
|
+
const document = { schema_version: sourcePolicySchemaVersion, ...cloneJSON(input), signature: "" };
|
|
940
|
+
validateSourcePolicy(document, false);
|
|
941
|
+
return preimageWithoutTopLevelSignature(signingUsages.sourcePolicy, document);
|
|
942
|
+
}
|
|
943
|
+
export function canonicalSourcePolicy(document) {
|
|
944
|
+
validateSourcePolicy(document, true);
|
|
945
|
+
return canonicalJSON(document);
|
|
946
|
+
}
|
|
947
|
+
export async function verifySourcePolicy(document, verifier) {
|
|
948
|
+
await verifyEncoded(signingUsages.sourcePolicy, document.key_id, sourcePolicySigningPreimage(unsignedDocument(document)), document.signature, verifier);
|
|
949
|
+
}
|
|
950
|
+
export function buildSourcePolicyPointer(input, signature) {
|
|
951
|
+
requireSignatureBytes(signature);
|
|
952
|
+
const document = {
|
|
953
|
+
schema_version: sourcePolicyPointerSchemaVersion,
|
|
954
|
+
...cloneJSON(input),
|
|
955
|
+
signature: encodeBase64(signature),
|
|
956
|
+
};
|
|
957
|
+
validatePointer(document, sourcePolicyPointerSchemaVersion, true);
|
|
958
|
+
return deepFreeze(document);
|
|
959
|
+
}
|
|
960
|
+
export function sourcePolicyPointerSigningPreimage(input) {
|
|
961
|
+
const document = { schema_version: sourcePolicyPointerSchemaVersion, ...cloneJSON(input), signature: "" };
|
|
962
|
+
validatePointer(document, sourcePolicyPointerSchemaVersion, false);
|
|
963
|
+
return preimageWithoutTopLevelSignature(signingUsages.sourcePolicyPointer, document);
|
|
964
|
+
}
|
|
965
|
+
export function canonicalSourcePolicyPointer(document) {
|
|
966
|
+
validatePointer(document, sourcePolicyPointerSchemaVersion, true);
|
|
967
|
+
return canonicalJSON(document);
|
|
968
|
+
}
|
|
969
|
+
export async function verifySourcePolicyPointer(document, verifier) {
|
|
970
|
+
await verifyEncoded(signingUsages.sourcePolicyPointer, document.key_id, sourcePolicyPointerSigningPreimage(unsignedDocument(document)), document.signature, verifier);
|
|
971
|
+
}
|
|
972
|
+
export function buildRevocation(input, signature) {
|
|
973
|
+
requireSignatureBytes(signature);
|
|
974
|
+
const document = {
|
|
975
|
+
schema_version: revocationSchemaVersion,
|
|
976
|
+
...cloneJSON(input),
|
|
977
|
+
signature: encodeBase64(signature),
|
|
978
|
+
};
|
|
979
|
+
validateRevocation(document, true);
|
|
980
|
+
return deepFreeze(document);
|
|
981
|
+
}
|
|
982
|
+
export function revocationSigningPreimage(input) {
|
|
983
|
+
const document = { schema_version: revocationSchemaVersion, ...cloneJSON(input), signature: "" };
|
|
984
|
+
validateRevocation(document, false);
|
|
985
|
+
return preimageWithoutTopLevelSignature(signingUsages.revocation, document);
|
|
986
|
+
}
|
|
987
|
+
export function canonicalRevocation(document) {
|
|
988
|
+
validateRevocation(document, true);
|
|
989
|
+
return canonicalJSON(document);
|
|
990
|
+
}
|
|
991
|
+
export async function verifyRevocation(document, verifier) {
|
|
992
|
+
await verifyEncoded(signingUsages.revocation, document.key_id, revocationSigningPreimage(unsignedDocument(document)), document.signature, verifier);
|
|
993
|
+
}
|
|
994
|
+
export function buildRevocationPointer(input, signature) {
|
|
995
|
+
requireSignatureBytes(signature);
|
|
996
|
+
const document = {
|
|
997
|
+
schema_version: revocationPointerSchemaVersion,
|
|
998
|
+
...cloneJSON(input),
|
|
999
|
+
signature: encodeBase64(signature),
|
|
1000
|
+
};
|
|
1001
|
+
validatePointer(document, revocationPointerSchemaVersion, true);
|
|
1002
|
+
return deepFreeze(document);
|
|
1003
|
+
}
|
|
1004
|
+
export function revocationPointerSigningPreimage(input) {
|
|
1005
|
+
const document = { schema_version: revocationPointerSchemaVersion, ...cloneJSON(input), signature: "" };
|
|
1006
|
+
validatePointer(document, revocationPointerSchemaVersion, false);
|
|
1007
|
+
return preimageWithoutTopLevelSignature(signingUsages.revocationPointer, document);
|
|
1008
|
+
}
|
|
1009
|
+
export function canonicalRevocationPointer(document) {
|
|
1010
|
+
validatePointer(document, revocationPointerSchemaVersion, true);
|
|
1011
|
+
return canonicalJSON(document);
|
|
1012
|
+
}
|
|
1013
|
+
export async function verifyRevocationPointer(document, verifier) {
|
|
1014
|
+
await verifyEncoded(signingUsages.revocationPointer, document.key_id, revocationPointerSigningPreimage(unsignedDocument(document)), document.signature, verifier);
|
|
1015
|
+
}
|
|
1016
|
+
export function decodeRootDelegation(raw) {
|
|
1017
|
+
return decodeCanonicalDocument(raw, closeRootDelegation, (value) => validateRootDelegation(value, true));
|
|
1018
|
+
}
|
|
1019
|
+
export function decodePackageSignature(raw, context) {
|
|
1020
|
+
return decodeCanonicalDocument(raw, closePackageSignature, (value) => validatePackageSignature(context, value, true));
|
|
1021
|
+
}
|
|
1022
|
+
export function decodeReleaseMetadata(raw) {
|
|
1023
|
+
return decodeCanonicalDocument(raw, closeReleaseMetadata, validateReleaseMetadata);
|
|
1024
|
+
}
|
|
1025
|
+
export function decodeSourcePolicy(raw) {
|
|
1026
|
+
return decodeCanonicalDocument(raw, closeSourcePolicy, (value) => validateSourcePolicy(value, true));
|
|
1027
|
+
}
|
|
1028
|
+
export function decodeSourcePolicyPointer(raw) {
|
|
1029
|
+
return decodeCanonicalDocument(raw, closePointer, (value) => validatePointer(value, sourcePolicyPointerSchemaVersion, true));
|
|
1030
|
+
}
|
|
1031
|
+
export function decodeRevocation(raw) {
|
|
1032
|
+
return decodeCanonicalDocument(raw, closeRevocation, (value) => validateRevocation(value, true));
|
|
1033
|
+
}
|
|
1034
|
+
export function decodeRevocationPointer(raw) {
|
|
1035
|
+
return decodeCanonicalDocument(raw, closePointer, (value) => validatePointer(value, revocationPointerSchemaVersion, true));
|
|
1036
|
+
}
|
|
1037
|
+
export function decodeSigningLedgerEvidence(raw) {
|
|
1038
|
+
const bytes = typeof raw === "string" ? textEncoder.encode(raw) : raw;
|
|
1039
|
+
if (!(bytes instanceof Uint8Array) || bytes.length > 64 * 1024)
|
|
1040
|
+
invalidDocument();
|
|
1041
|
+
return decodeCanonicalDocument(bytes, closeSigningLedgerEvidence, validateSigningLedgerEvidence);
|
|
1042
|
+
}
|
|
1043
|
+
export function canonicalSigningSubject(value) {
|
|
1044
|
+
validateSigningSubject(value);
|
|
1045
|
+
return canonicalJSON(value);
|
|
1046
|
+
}
|
|
1047
|
+
export function decodeSigningSubject(raw) {
|
|
1048
|
+
return decodeCanonicalDocument(raw, closeSigningSubject, validateSigningSubject);
|
|
1049
|
+
}
|
|
1050
|
+
export function canonicalSignatureEnvelope(value) {
|
|
1051
|
+
validateSignatureEnvelope(value);
|
|
1052
|
+
return canonicalJSON(value);
|
|
1053
|
+
}
|
|
1054
|
+
export function decodeSignatureEnvelope(raw) {
|
|
1055
|
+
return decodeCanonicalDocument(raw, closeSignatureEnvelope, validateSignatureEnvelope);
|
|
1056
|
+
}
|
|
1057
|
+
export function canonicalSigningLedgerEntry(value) {
|
|
1058
|
+
validateSigningLedgerEntry(value);
|
|
1059
|
+
return canonicalJSON(value);
|
|
1060
|
+
}
|
|
1061
|
+
export function decodeSigningLedgerEntry(raw) {
|
|
1062
|
+
return decodeCanonicalDocument(raw, closeSigningLedgerEntry, validateSigningLedgerEntry);
|
|
1063
|
+
}
|
|
1064
|
+
export async function verifySigningLedgerEntryBindings(value) {
|
|
1065
|
+
validateSigningLedgerEntry(value);
|
|
1066
|
+
if (await sha256Hex(canonicalSigningSubject(value.subject)) !== value.subject_identity_sha256)
|
|
1067
|
+
invalidDocument();
|
|
1068
|
+
if (value.state === "finalized") {
|
|
1069
|
+
if (value.signature_envelope === undefined || value.signature_envelope_sha256 === undefined ||
|
|
1070
|
+
await sha256Hex(canonicalSignatureEnvelope(value.signature_envelope)) !== value.signature_envelope_sha256)
|
|
1071
|
+
invalidDocument();
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
export function decodeSigningLedgerLogLeaf(raw) {
|
|
1075
|
+
return decodeCanonicalDocument(raw, closeSigningLedgerLogLeaf, validateSigningLedgerLogLeaf);
|
|
1076
|
+
}
|
|
1077
|
+
export function decodeSigningLedgerCheckpoint(raw) {
|
|
1078
|
+
return decodeCanonicalDocument(raw, closeSigningLedgerCheckpoint, validateSigningLedgerCheckpoint);
|
|
1079
|
+
}
|
|
1080
|
+
export function decodeSigningLedgerReceipt(raw) {
|
|
1081
|
+
return decodeCanonicalDocument(raw, closeSigningLedgerReceipt, validateSigningLedgerReceipt);
|
|
1082
|
+
}
|
|
1083
|
+
export function decodeSigningLedgerInclusionProof(raw) {
|
|
1084
|
+
return decodeCanonicalDocument(raw, closeSigningLedgerInclusionProof, validateSigningLedgerInclusionProof);
|
|
1085
|
+
}
|
|
1086
|
+
export function decodeSigningLedgerLatestProof(raw) {
|
|
1087
|
+
return decodeCanonicalDocument(raw, closeSigningLedgerLatestProof, validateSigningLedgerLatestProof);
|
|
1088
|
+
}
|
|
1089
|
+
export function decodeSigningLedgerConsistencyProof(raw) {
|
|
1090
|
+
return decodeCanonicalDocument(raw, closeSigningLedgerConsistencyProof, validateSigningLedgerConsistencyProof);
|
|
1091
|
+
}
|
|
1092
|
+
export function createEd25519Verifier(publicKeys, subtle) {
|
|
1093
|
+
const selectedSubtle = subtle ?? globalThis.crypto?.subtle;
|
|
1094
|
+
return async (request) => {
|
|
1095
|
+
const publicKey = publicKeys[request.keyID];
|
|
1096
|
+
if (selectedSubtle === undefined || publicKey === undefined || publicKey.length !== 32)
|
|
1097
|
+
return false;
|
|
1098
|
+
try {
|
|
1099
|
+
const key = await selectedSubtle.importKey("raw", toArrayBuffer(publicKey), { name: "Ed25519" }, false, ["verify"]);
|
|
1100
|
+
return await selectedSubtle.verify({ name: "Ed25519" }, key, toArrayBuffer(request.signature), toArrayBuffer(request.signingPreimageSHA256));
|
|
1101
|
+
}
|
|
1102
|
+
catch {
|
|
1103
|
+
return false;
|
|
1104
|
+
}
|
|
1105
|
+
};
|
|
1106
|
+
}
|
|
1107
|
+
async function verifyEncoded(usage, keyID, preimage, encodedSignature, verifier) {
|
|
1108
|
+
let signature;
|
|
1109
|
+
try {
|
|
1110
|
+
signature = decodeBase64(encodedSignature, 64);
|
|
1111
|
+
}
|
|
1112
|
+
catch {
|
|
1113
|
+
throw new InvalidReleaseSignatureError();
|
|
1114
|
+
}
|
|
1115
|
+
await verifyRaw(usage, keyID, preimage, signature, verifier);
|
|
1116
|
+
}
|
|
1117
|
+
async function verifyRaw(usage, keyID, preimage, signature, verifier) {
|
|
1118
|
+
let valid = false;
|
|
1119
|
+
try {
|
|
1120
|
+
const subtle = globalThis.crypto?.subtle;
|
|
1121
|
+
if (subtle === undefined)
|
|
1122
|
+
throw new Error("Web Crypto SHA-256 is unavailable");
|
|
1123
|
+
const digest = new Uint8Array(await subtle.digest("SHA-256", toArrayBuffer(preimage)));
|
|
1124
|
+
valid = await verifier({
|
|
1125
|
+
usage,
|
|
1126
|
+
keyID,
|
|
1127
|
+
signingPreimageSHA256: digest,
|
|
1128
|
+
signature: signature.slice(),
|
|
1129
|
+
});
|
|
1130
|
+
}
|
|
1131
|
+
catch {
|
|
1132
|
+
valid = false;
|
|
1133
|
+
}
|
|
1134
|
+
if (!valid)
|
|
1135
|
+
throw new InvalidReleaseSignatureError();
|
|
1136
|
+
}
|
|
1137
|
+
function toArrayBuffer(value) {
|
|
1138
|
+
return Uint8Array.from(value).buffer;
|
|
1139
|
+
}
|
|
1140
|
+
async function sha256Hex(value) {
|
|
1141
|
+
const subtle = globalThis.crypto?.subtle;
|
|
1142
|
+
if (subtle === undefined)
|
|
1143
|
+
invalidDocument();
|
|
1144
|
+
const digest = new Uint8Array(await subtle.digest("SHA-256", toArrayBuffer(value)));
|
|
1145
|
+
return Array.from(digest, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
1146
|
+
}
|
|
1147
|
+
function unsignedDocument(value) {
|
|
1148
|
+
const input = cloneJSON(value);
|
|
1149
|
+
delete input.schema_version;
|
|
1150
|
+
delete input.signature;
|
|
1151
|
+
return input;
|
|
1152
|
+
}
|