@freedomofpress/sigstore-browser 0.1.5 → 0.1.7
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/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/policy.d.ts +266 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +371 -0
- package/dist/policy.js.map +1 -0
- package/dist/trust/tuf.d.ts.map +1 -1
- package/dist/trust/tuf.js +3 -1
- package/dist/trust/tuf.js.map +1 -1
- package/dist/x509/cert.d.ts +42 -1
- package/dist/x509/cert.d.ts.map +1 -1
- package/dist/x509/cert.js +103 -4
- package/dist/x509/cert.js.map +1 -1
- package/dist/x509/ext.d.ts +66 -2
- package/dist/x509/ext.d.ts.map +1 -1
- package/dist/x509/ext.js +133 -8
- package/dist/x509/ext.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +78 -2
- package/src/policy.ts +476 -0
- package/src/trust/tuf.ts +3 -1
- package/src/x509/cert.ts +141 -3
- package/src/x509/ext.ts +154 -8
package/src/policy.ts
ADDED
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapted from sigstore-python sigstore/verify/policy.py
|
|
3
|
+
*
|
|
4
|
+
* This module provides policy verification for checking certificate identity claims.
|
|
5
|
+
* Each policy class verifies a specific X.509 extension value against an expected value.
|
|
6
|
+
*
|
|
7
|
+
* There are two types of extension formats:
|
|
8
|
+
* - V1 extensions: Raw OCTET STRING containing the value directly
|
|
9
|
+
* - V2 extensions: DER-encoded UTF8String (RFC 5280 compliant)
|
|
10
|
+
*
|
|
11
|
+
* Policies can be combined using:
|
|
12
|
+
* - AllOf: All child policies must pass (logical AND)
|
|
13
|
+
* - AnyOf: At least one child policy must pass (logical OR)
|
|
14
|
+
* - Identity: Verifies SAN with optional issuer check
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { PolicyError } from "./errors.js";
|
|
18
|
+
import {
|
|
19
|
+
X509Certificate,
|
|
20
|
+
EXTENSION_OID_FULCIO_ISSUER_V1,
|
|
21
|
+
EXTENSION_OID_GITHUB_WORKFLOW_TRIGGER,
|
|
22
|
+
EXTENSION_OID_GITHUB_WORKFLOW_SHA,
|
|
23
|
+
EXTENSION_OID_GITHUB_WORKFLOW_NAME,
|
|
24
|
+
EXTENSION_OID_GITHUB_WORKFLOW_REPOSITORY,
|
|
25
|
+
EXTENSION_OID_GITHUB_WORKFLOW_REF,
|
|
26
|
+
EXTENSION_OID_OTHERNAME,
|
|
27
|
+
EXTENSION_OID_FULCIO_ISSUER_V2,
|
|
28
|
+
EXTENSION_OID_BUILD_SIGNER_URI,
|
|
29
|
+
EXTENSION_OID_BUILD_SIGNER_DIGEST,
|
|
30
|
+
EXTENSION_OID_RUNNER_ENVIRONMENT,
|
|
31
|
+
EXTENSION_OID_SOURCE_REPOSITORY_URI,
|
|
32
|
+
EXTENSION_OID_SOURCE_REPOSITORY_DIGEST,
|
|
33
|
+
EXTENSION_OID_SOURCE_REPOSITORY_REF,
|
|
34
|
+
EXTENSION_OID_SOURCE_REPOSITORY_IDENTIFIER,
|
|
35
|
+
EXTENSION_OID_SOURCE_REPOSITORY_OWNER_URI,
|
|
36
|
+
EXTENSION_OID_SOURCE_REPOSITORY_OWNER_IDENTIFIER,
|
|
37
|
+
EXTENSION_OID_BUILD_CONFIG_URI,
|
|
38
|
+
EXTENSION_OID_BUILD_CONFIG_DIGEST,
|
|
39
|
+
EXTENSION_OID_BUILD_TRIGGER,
|
|
40
|
+
EXTENSION_OID_RUN_INVOCATION_URI,
|
|
41
|
+
EXTENSION_OID_SOURCE_REPOSITORY_VISIBILITY,
|
|
42
|
+
} from "./x509/cert.js";
|
|
43
|
+
|
|
44
|
+
// Standard GitHub Actions OIDC issuer
|
|
45
|
+
// https://docs.github.com/en/actions/security-for-github-actions/security-hardening-your-deployments/about-security-hardening-with-openid-connect
|
|
46
|
+
export const GITHUB_OIDC_ISSUER = "https://token.actions.githubusercontent.com";
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Interface that all verification policies conform to.
|
|
50
|
+
*/
|
|
51
|
+
export interface VerificationPolicy {
|
|
52
|
+
verify(cert: X509Certificate): void;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Base class for V1 extension policies (raw OCTET STRING value).
|
|
57
|
+
*/
|
|
58
|
+
abstract class SingleX509ExtPolicyV1 implements VerificationPolicy {
|
|
59
|
+
abstract readonly oid: string;
|
|
60
|
+
abstract readonly name: string;
|
|
61
|
+
protected readonly expectedValue: string;
|
|
62
|
+
|
|
63
|
+
constructor(value: string) {
|
|
64
|
+
this.expectedValue = value;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
protected abstract getExtensionValue(cert: X509Certificate): string | undefined;
|
|
68
|
+
|
|
69
|
+
verify(cert: X509Certificate): void {
|
|
70
|
+
const extValue = this.getExtensionValue(cert);
|
|
71
|
+
if (extValue === undefined) {
|
|
72
|
+
throw new PolicyError(
|
|
73
|
+
`Certificate does not contain ${this.name} (${this.oid}) extension`
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
if (extValue !== this.expectedValue) {
|
|
77
|
+
throw new PolicyError(
|
|
78
|
+
`Certificate's ${this.name} does not match ` +
|
|
79
|
+
`(got '${extValue}', expected '${this.expectedValue}')`
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Base class for V2 extension policies (DER-encoded UTF8String).
|
|
87
|
+
* Uses the same verify logic as V1 since our extension classes handle the parsing.
|
|
88
|
+
*/
|
|
89
|
+
abstract class SingleX509ExtPolicyV2 extends SingleX509ExtPolicyV1 {}
|
|
90
|
+
|
|
91
|
+
// ============================================================================
|
|
92
|
+
// V1 Extension Policies (OID 1.3.6.1.4.1.57264.1.1 - 1.3.6.1.4.1.57264.1.6)
|
|
93
|
+
// ============================================================================
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Verifies the certificate's OIDC issuer (V1 format).
|
|
97
|
+
* OID: 1.3.6.1.4.1.57264.1.1
|
|
98
|
+
*/
|
|
99
|
+
export class OIDCIssuer extends SingleX509ExtPolicyV1 {
|
|
100
|
+
readonly oid = EXTENSION_OID_FULCIO_ISSUER_V1;
|
|
101
|
+
readonly name = "OIDCIssuer";
|
|
102
|
+
|
|
103
|
+
protected getExtensionValue(cert: X509Certificate): string | undefined {
|
|
104
|
+
return cert.extFulcioIssuerV1?.issuer;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Verifies the certificate's GitHub Actions workflow trigger.
|
|
110
|
+
* OID: 1.3.6.1.4.1.57264.1.2
|
|
111
|
+
*/
|
|
112
|
+
export class GitHubWorkflowTrigger extends SingleX509ExtPolicyV1 {
|
|
113
|
+
readonly oid = EXTENSION_OID_GITHUB_WORKFLOW_TRIGGER;
|
|
114
|
+
readonly name = "GitHubWorkflowTrigger";
|
|
115
|
+
|
|
116
|
+
protected getExtensionValue(cert: X509Certificate): string | undefined {
|
|
117
|
+
return cert.extGitHubWorkflowTrigger?.workflowTrigger;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Verifies the certificate's GitHub Actions workflow commit SHA.
|
|
123
|
+
* OID: 1.3.6.1.4.1.57264.1.3
|
|
124
|
+
*/
|
|
125
|
+
export class GitHubWorkflowSHA extends SingleX509ExtPolicyV1 {
|
|
126
|
+
readonly oid = EXTENSION_OID_GITHUB_WORKFLOW_SHA;
|
|
127
|
+
readonly name = "GitHubWorkflowSHA";
|
|
128
|
+
|
|
129
|
+
protected getExtensionValue(cert: X509Certificate): string | undefined {
|
|
130
|
+
return cert.extGitHubWorkflowSHA?.workflowSHA;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Verifies the certificate's GitHub Actions workflow name.
|
|
136
|
+
* OID: 1.3.6.1.4.1.57264.1.4
|
|
137
|
+
*/
|
|
138
|
+
export class GitHubWorkflowName extends SingleX509ExtPolicyV1 {
|
|
139
|
+
readonly oid = EXTENSION_OID_GITHUB_WORKFLOW_NAME;
|
|
140
|
+
readonly name = "GitHubWorkflowName";
|
|
141
|
+
|
|
142
|
+
protected getExtensionValue(cert: X509Certificate): string | undefined {
|
|
143
|
+
return cert.extGitHubWorkflowName?.workflowName;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Verifies the certificate's GitHub Actions workflow repository.
|
|
149
|
+
* OID: 1.3.6.1.4.1.57264.1.5
|
|
150
|
+
*/
|
|
151
|
+
export class GitHubWorkflowRepository extends SingleX509ExtPolicyV1 {
|
|
152
|
+
readonly oid = EXTENSION_OID_GITHUB_WORKFLOW_REPOSITORY;
|
|
153
|
+
readonly name = "GitHubWorkflowRepository";
|
|
154
|
+
|
|
155
|
+
protected getExtensionValue(cert: X509Certificate): string | undefined {
|
|
156
|
+
return cert.extGitHubWorkflowRepository?.workflowRepository;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Verifies the certificate's GitHub Actions workflow ref.
|
|
162
|
+
* OID: 1.3.6.1.4.1.57264.1.6
|
|
163
|
+
*/
|
|
164
|
+
export class GitHubWorkflowRef extends SingleX509ExtPolicyV1 {
|
|
165
|
+
readonly oid = EXTENSION_OID_GITHUB_WORKFLOW_REF;
|
|
166
|
+
readonly name = "GitHubWorkflowRef";
|
|
167
|
+
|
|
168
|
+
protected getExtensionValue(cert: X509Certificate): string | undefined {
|
|
169
|
+
return cert.extGitHubWorkflowRef?.workflowRef;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// ============================================================================
|
|
174
|
+
// V2 Extension Policies (OID 1.3.6.1.4.1.57264.1.8 - 1.3.6.1.4.1.57264.1.22)
|
|
175
|
+
// ============================================================================
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Verifies the certificate's OIDC issuer (V2 format, DER-encoded).
|
|
179
|
+
* OID: 1.3.6.1.4.1.57264.1.8
|
|
180
|
+
*/
|
|
181
|
+
export class OIDCIssuerV2 extends SingleX509ExtPolicyV2 {
|
|
182
|
+
readonly oid = EXTENSION_OID_FULCIO_ISSUER_V2;
|
|
183
|
+
readonly name = "OIDCIssuerV2";
|
|
184
|
+
|
|
185
|
+
protected getExtensionValue(cert: X509Certificate): string | undefined {
|
|
186
|
+
return cert.extFulcioIssuerV2?.issuer;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Verifies the certificate's Build Signer URI.
|
|
192
|
+
* OID: 1.3.6.1.4.1.57264.1.9
|
|
193
|
+
*/
|
|
194
|
+
export class OIDCBuildSignerURI extends SingleX509ExtPolicyV2 {
|
|
195
|
+
readonly oid = EXTENSION_OID_BUILD_SIGNER_URI;
|
|
196
|
+
readonly name = "OIDCBuildSignerURI";
|
|
197
|
+
|
|
198
|
+
protected getExtensionValue(cert: X509Certificate): string | undefined {
|
|
199
|
+
return cert.extBuildSignerURI?.buildSignerURI;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Verifies the certificate's Build Signer Digest.
|
|
205
|
+
* OID: 1.3.6.1.4.1.57264.1.10
|
|
206
|
+
*/
|
|
207
|
+
export class OIDCBuildSignerDigest extends SingleX509ExtPolicyV2 {
|
|
208
|
+
readonly oid = EXTENSION_OID_BUILD_SIGNER_DIGEST;
|
|
209
|
+
readonly name = "OIDCBuildSignerDigest";
|
|
210
|
+
|
|
211
|
+
protected getExtensionValue(cert: X509Certificate): string | undefined {
|
|
212
|
+
return cert.extBuildSignerDigest?.buildSignerDigest;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Verifies the certificate's Runner Environment.
|
|
218
|
+
* OID: 1.3.6.1.4.1.57264.1.11
|
|
219
|
+
*/
|
|
220
|
+
export class OIDCRunnerEnvironment extends SingleX509ExtPolicyV2 {
|
|
221
|
+
readonly oid = EXTENSION_OID_RUNNER_ENVIRONMENT;
|
|
222
|
+
readonly name = "OIDCRunnerEnvironment";
|
|
223
|
+
|
|
224
|
+
protected getExtensionValue(cert: X509Certificate): string | undefined {
|
|
225
|
+
return cert.extRunnerEnvironment?.runnerEnvironment;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Verifies the certificate's Source Repository URI.
|
|
231
|
+
* OID: 1.3.6.1.4.1.57264.1.12
|
|
232
|
+
*/
|
|
233
|
+
export class OIDCSourceRepositoryURI extends SingleX509ExtPolicyV2 {
|
|
234
|
+
readonly oid = EXTENSION_OID_SOURCE_REPOSITORY_URI;
|
|
235
|
+
readonly name = "OIDCSourceRepositoryURI";
|
|
236
|
+
|
|
237
|
+
protected getExtensionValue(cert: X509Certificate): string | undefined {
|
|
238
|
+
return cert.extSourceRepositoryURI?.sourceRepositoryURI;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Verifies the certificate's Source Repository Digest.
|
|
244
|
+
* OID: 1.3.6.1.4.1.57264.1.13
|
|
245
|
+
*/
|
|
246
|
+
export class OIDCSourceRepositoryDigest extends SingleX509ExtPolicyV2 {
|
|
247
|
+
readonly oid = EXTENSION_OID_SOURCE_REPOSITORY_DIGEST;
|
|
248
|
+
readonly name = "OIDCSourceRepositoryDigest";
|
|
249
|
+
|
|
250
|
+
protected getExtensionValue(cert: X509Certificate): string | undefined {
|
|
251
|
+
return cert.extSourceRepositoryDigest?.sourceRepositoryDigest;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Verifies the certificate's Source Repository Ref.
|
|
257
|
+
* OID: 1.3.6.1.4.1.57264.1.14
|
|
258
|
+
*/
|
|
259
|
+
export class OIDCSourceRepositoryRef extends SingleX509ExtPolicyV2 {
|
|
260
|
+
readonly oid = EXTENSION_OID_SOURCE_REPOSITORY_REF;
|
|
261
|
+
readonly name = "OIDCSourceRepositoryRef";
|
|
262
|
+
|
|
263
|
+
protected getExtensionValue(cert: X509Certificate): string | undefined {
|
|
264
|
+
return cert.extSourceRepositoryRef?.sourceRepositoryRef;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Verifies the certificate's Source Repository Identifier.
|
|
270
|
+
* OID: 1.3.6.1.4.1.57264.1.15
|
|
271
|
+
*/
|
|
272
|
+
export class OIDCSourceRepositoryIdentifier extends SingleX509ExtPolicyV2 {
|
|
273
|
+
readonly oid = EXTENSION_OID_SOURCE_REPOSITORY_IDENTIFIER;
|
|
274
|
+
readonly name = "OIDCSourceRepositoryIdentifier";
|
|
275
|
+
|
|
276
|
+
protected getExtensionValue(cert: X509Certificate): string | undefined {
|
|
277
|
+
return cert.extSourceRepositoryIdentifier?.sourceRepositoryIdentifier;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Verifies the certificate's Source Repository Owner URI.
|
|
283
|
+
* OID: 1.3.6.1.4.1.57264.1.16
|
|
284
|
+
*/
|
|
285
|
+
export class OIDCSourceRepositoryOwnerURI extends SingleX509ExtPolicyV2 {
|
|
286
|
+
readonly oid = EXTENSION_OID_SOURCE_REPOSITORY_OWNER_URI;
|
|
287
|
+
readonly name = "OIDCSourceRepositoryOwnerURI";
|
|
288
|
+
|
|
289
|
+
protected getExtensionValue(cert: X509Certificate): string | undefined {
|
|
290
|
+
return cert.extSourceRepositoryOwnerURI?.sourceRepositoryOwnerURI;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Verifies the certificate's Source Repository Owner Identifier.
|
|
296
|
+
* OID: 1.3.6.1.4.1.57264.1.17
|
|
297
|
+
*/
|
|
298
|
+
export class OIDCSourceRepositoryOwnerIdentifier extends SingleX509ExtPolicyV2 {
|
|
299
|
+
readonly oid = EXTENSION_OID_SOURCE_REPOSITORY_OWNER_IDENTIFIER;
|
|
300
|
+
readonly name = "OIDCSourceRepositoryOwnerIdentifier";
|
|
301
|
+
|
|
302
|
+
protected getExtensionValue(cert: X509Certificate): string | undefined {
|
|
303
|
+
return cert.extSourceRepositoryOwnerIdentifier?.sourceRepositoryOwnerIdentifier;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Verifies the certificate's Build Config URI.
|
|
309
|
+
* OID: 1.3.6.1.4.1.57264.1.18
|
|
310
|
+
*/
|
|
311
|
+
export class OIDCBuildConfigURI extends SingleX509ExtPolicyV2 {
|
|
312
|
+
readonly oid = EXTENSION_OID_BUILD_CONFIG_URI;
|
|
313
|
+
readonly name = "OIDCBuildConfigURI";
|
|
314
|
+
|
|
315
|
+
protected getExtensionValue(cert: X509Certificate): string | undefined {
|
|
316
|
+
return cert.extBuildConfigURI?.buildConfigURI;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Verifies the certificate's Build Config Digest.
|
|
322
|
+
* OID: 1.3.6.1.4.1.57264.1.19
|
|
323
|
+
*/
|
|
324
|
+
export class OIDCBuildConfigDigest extends SingleX509ExtPolicyV2 {
|
|
325
|
+
readonly oid = EXTENSION_OID_BUILD_CONFIG_DIGEST;
|
|
326
|
+
readonly name = "OIDCBuildConfigDigest";
|
|
327
|
+
|
|
328
|
+
protected getExtensionValue(cert: X509Certificate): string | undefined {
|
|
329
|
+
return cert.extBuildConfigDigest?.buildConfigDigest;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Verifies the certificate's Build Trigger.
|
|
335
|
+
* OID: 1.3.6.1.4.1.57264.1.20
|
|
336
|
+
*/
|
|
337
|
+
export class OIDCBuildTrigger extends SingleX509ExtPolicyV2 {
|
|
338
|
+
readonly oid = EXTENSION_OID_BUILD_TRIGGER;
|
|
339
|
+
readonly name = "OIDCBuildTrigger";
|
|
340
|
+
|
|
341
|
+
protected getExtensionValue(cert: X509Certificate): string | undefined {
|
|
342
|
+
return cert.extBuildTrigger?.buildTrigger;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Verifies the certificate's Run Invocation URI.
|
|
348
|
+
* OID: 1.3.6.1.4.1.57264.1.21
|
|
349
|
+
*/
|
|
350
|
+
export class OIDCRunInvocationURI extends SingleX509ExtPolicyV2 {
|
|
351
|
+
readonly oid = EXTENSION_OID_RUN_INVOCATION_URI;
|
|
352
|
+
readonly name = "OIDCRunInvocationURI";
|
|
353
|
+
|
|
354
|
+
protected getExtensionValue(cert: X509Certificate): string | undefined {
|
|
355
|
+
return cert.extRunInvocationURI?.runInvocationURI;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Verifies the certificate's Source Repository Visibility at signing.
|
|
361
|
+
* OID: 1.3.6.1.4.1.57264.1.22
|
|
362
|
+
*/
|
|
363
|
+
export class OIDCSourceRepositoryVisibility extends SingleX509ExtPolicyV2 {
|
|
364
|
+
readonly oid = EXTENSION_OID_SOURCE_REPOSITORY_VISIBILITY;
|
|
365
|
+
readonly name = "OIDCSourceRepositoryVisibility";
|
|
366
|
+
|
|
367
|
+
protected getExtensionValue(cert: X509Certificate): string | undefined {
|
|
368
|
+
return cert.extSourceRepositoryVisibility?.sourceRepositoryVisibility;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// ============================================================================
|
|
373
|
+
// Policy Combinators
|
|
374
|
+
// ============================================================================
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* The "any of" policy, corresponding to a logical OR between child policies.
|
|
378
|
+
* An empty list of child policies is considered trivially invalid.
|
|
379
|
+
*/
|
|
380
|
+
export class AnyOf implements VerificationPolicy {
|
|
381
|
+
private children: VerificationPolicy[];
|
|
382
|
+
|
|
383
|
+
constructor(children: VerificationPolicy[]) {
|
|
384
|
+
this.children = children;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
verify(cert: X509Certificate): void {
|
|
388
|
+
for (const child of this.children) {
|
|
389
|
+
try {
|
|
390
|
+
child.verify(cert);
|
|
391
|
+
return;
|
|
392
|
+
} catch {
|
|
393
|
+
// Continue to next policy
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
throw new PolicyError(`0 of ${this.children.length} policies succeeded`);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* The "all of" policy, corresponding to a logical AND between child policies.
|
|
402
|
+
* An empty list of child policies is considered trivially invalid.
|
|
403
|
+
*/
|
|
404
|
+
export class AllOf implements VerificationPolicy {
|
|
405
|
+
private children: VerificationPolicy[];
|
|
406
|
+
|
|
407
|
+
constructor(children: VerificationPolicy[]) {
|
|
408
|
+
this.children = children;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
verify(cert: X509Certificate): void {
|
|
412
|
+
if (this.children.length < 1) {
|
|
413
|
+
throw new PolicyError("no child policies to verify");
|
|
414
|
+
}
|
|
415
|
+
for (const child of this.children) {
|
|
416
|
+
child.verify(cert);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// ============================================================================
|
|
422
|
+
// Identity Policy
|
|
423
|
+
// ============================================================================
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Verifies the certificate's "identity", corresponding to the X.509v3 SAN.
|
|
427
|
+
*
|
|
428
|
+
* Identities can be verified modulo an OIDC issuer, to prevent an unexpected
|
|
429
|
+
* issuer from offering a particular identity.
|
|
430
|
+
*
|
|
431
|
+
* Supported SAN types include emails, URIs, and Sigstore-specific "other names".
|
|
432
|
+
*/
|
|
433
|
+
export class Identity implements VerificationPolicy {
|
|
434
|
+
private identity: string;
|
|
435
|
+
private issuerPolicy: OIDCIssuer | null;
|
|
436
|
+
|
|
437
|
+
constructor(options: { identity: string; issuer?: string }) {
|
|
438
|
+
this.identity = options.identity;
|
|
439
|
+
this.issuerPolicy = options.issuer ? new OIDCIssuer(options.issuer) : null;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
verify(cert: X509Certificate): void {
|
|
443
|
+
if (this.issuerPolicy) {
|
|
444
|
+
this.issuerPolicy.verify(cert);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const sanExt = cert.extSubjectAltName;
|
|
448
|
+
if (!sanExt) {
|
|
449
|
+
throw new PolicyError("Certificate does not contain SubjectAlternativeName extension");
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
const allSans = new Set<string>();
|
|
453
|
+
|
|
454
|
+
// Add email (rfc822Name)
|
|
455
|
+
if (sanExt.rfc822Name) {
|
|
456
|
+
allSans.add(sanExt.rfc822Name);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// Add URI
|
|
460
|
+
if (sanExt.uri) {
|
|
461
|
+
allSans.add(sanExt.uri);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// Add Sigstore-specific otherName
|
|
465
|
+
const otherName = sanExt.otherName(EXTENSION_OID_OTHERNAME);
|
|
466
|
+
if (otherName) {
|
|
467
|
+
allSans.add(otherName);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
if (!allSans.has(this.identity)) {
|
|
471
|
+
throw new PolicyError(
|
|
472
|
+
`Certificate's SANs do not match ${this.identity}; actual SANs: ${Array.from(allSans).join(", ")}`
|
|
473
|
+
);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
package/src/trust/tuf.ts
CHANGED
|
@@ -57,6 +57,7 @@ export interface TrustedRootProviderOptions {
|
|
|
57
57
|
*/
|
|
58
58
|
const DEFAULT_CONFIG = {
|
|
59
59
|
metadataUrl: 'https://tuf-repo-cdn.sigstore.dev/',
|
|
60
|
+
targetBaseUrl: 'https://tuf-repo-cdn.sigstore.dev/targets/',
|
|
60
61
|
namespace: 'sigstore-browser',
|
|
61
62
|
trustedRootTarget: 'trusted_root.json',
|
|
62
63
|
cacheTTL: 3600000, // 1 hour
|
|
@@ -89,7 +90,8 @@ export class TrustedRootProvider {
|
|
|
89
90
|
constructor(options: TrustedRootProviderOptions = {}) {
|
|
90
91
|
const metadataUrl = options.metadataUrl || DEFAULT_CONFIG.metadataUrl;
|
|
91
92
|
this.metadataUrl = metadataUrl.endsWith('/') ? metadataUrl : `${metadataUrl}/`;
|
|
92
|
-
|
|
93
|
+
const targetBaseUrl = options.targetBaseUrl || DEFAULT_CONFIG.targetBaseUrl;
|
|
94
|
+
this.targetBaseUrl = targetBaseUrl.endsWith('/') ? targetBaseUrl : `${targetBaseUrl}/`;
|
|
93
95
|
this.initialRoot = options.initialRoot;
|
|
94
96
|
this.namespace = options.namespace || DEFAULT_CONFIG.namespace;
|
|
95
97
|
this.trustedRootTarget = options.trustedRootTarget || DEFAULT_CONFIG.trustedRootTarget;
|
package/src/x509/cert.ts
CHANGED
|
@@ -26,26 +26,69 @@ import { DEFAULT_HASH_ALGORITHM, ECDSA_CURVE_NAMES, ECDSA_SIGNATURE_ALGOS, OID_R
|
|
|
26
26
|
import {
|
|
27
27
|
X509AuthorityKeyIDExtension,
|
|
28
28
|
X509BasicConstraintsExtension,
|
|
29
|
+
X509BuildConfigDigestExtension,
|
|
30
|
+
X509BuildConfigURIExtension,
|
|
31
|
+
X509BuildSignerDigestExtension,
|
|
32
|
+
X509BuildSignerURIExtension,
|
|
33
|
+
X509BuildTriggerExtension,
|
|
29
34
|
X509Extension,
|
|
30
35
|
X509FulcioIssuerV1,
|
|
31
36
|
X509FulcioIssuerV2,
|
|
37
|
+
X509GitHubWorkflowNameExtension,
|
|
38
|
+
X509GitHubWorkflowRefExtension,
|
|
39
|
+
X509GitHubWorkflowRepositoryExtension,
|
|
40
|
+
X509GitHubWorkflowSHAExtension,
|
|
41
|
+
X509GitHubWorkflowTriggerExtension,
|
|
32
42
|
X509KeyUsageExtension,
|
|
43
|
+
X509RunInvocationURIExtension,
|
|
44
|
+
X509RunnerEnvironmentExtension,
|
|
33
45
|
X509SCTExtension,
|
|
46
|
+
X509SourceRepositoryDigestExtension,
|
|
47
|
+
X509SourceRepositoryIdentifierExtension,
|
|
48
|
+
X509SourceRepositoryOwnerIdentifierExtension,
|
|
49
|
+
X509SourceRepositoryOwnerURIExtension,
|
|
50
|
+
X509SourceRepositoryRefExtension,
|
|
51
|
+
X509SourceRepositoryURIExtension,
|
|
52
|
+
X509SourceRepositoryVisibilityExtension,
|
|
34
53
|
X509SubjectAlternativeNameExtension,
|
|
35
54
|
X509SubjectKeyIDExtension,
|
|
36
55
|
} from "./ext.js";
|
|
37
56
|
|
|
38
|
-
//
|
|
57
|
+
// Standard X.509 extension OIDs
|
|
39
58
|
const EXTENSION_OID_SUBJECT_KEY_ID = "2.5.29.14";
|
|
40
59
|
const EXTENSION_OID_KEY_USAGE = "2.5.29.15";
|
|
41
60
|
const EXTENSION_OID_SUBJECT_ALT_NAME = "2.5.29.17";
|
|
42
61
|
const EXTENSION_OID_BASIC_CONSTRAINTS = "2.5.29.19";
|
|
43
62
|
const EXTENSION_OID_AUTHORITY_KEY_ID = "2.5.29.35";
|
|
44
|
-
const EXTENSION_OID_FULCIO_ISSUER_V1 = "1.3.6.1.4.1.57264.1.1";
|
|
45
|
-
const EXTENSION_OID_FULCIO_ISSUER_V2 = "1.3.6.1.4.1.57264.1.8";
|
|
46
63
|
|
|
64
|
+
// CT Log SCT extension OID
|
|
47
65
|
export const EXTENSION_OID_SCT = "1.3.6.1.4.1.11129.2.4.2";
|
|
48
66
|
|
|
67
|
+
// Fulcio extension OIDs
|
|
68
|
+
// https://github.com/sigstore/fulcio/blob/main/docs/oid-info.md
|
|
69
|
+
export const EXTENSION_OID_FULCIO_ISSUER_V1 = "1.3.6.1.4.1.57264.1.1";
|
|
70
|
+
export const EXTENSION_OID_GITHUB_WORKFLOW_TRIGGER = "1.3.6.1.4.1.57264.1.2";
|
|
71
|
+
export const EXTENSION_OID_GITHUB_WORKFLOW_SHA = "1.3.6.1.4.1.57264.1.3";
|
|
72
|
+
export const EXTENSION_OID_GITHUB_WORKFLOW_NAME = "1.3.6.1.4.1.57264.1.4";
|
|
73
|
+
export const EXTENSION_OID_GITHUB_WORKFLOW_REPOSITORY = "1.3.6.1.4.1.57264.1.5";
|
|
74
|
+
export const EXTENSION_OID_GITHUB_WORKFLOW_REF = "1.3.6.1.4.1.57264.1.6";
|
|
75
|
+
export const EXTENSION_OID_OTHERNAME = "1.3.6.1.4.1.57264.1.7";
|
|
76
|
+
export const EXTENSION_OID_FULCIO_ISSUER_V2 = "1.3.6.1.4.1.57264.1.8";
|
|
77
|
+
export const EXTENSION_OID_BUILD_SIGNER_URI = "1.3.6.1.4.1.57264.1.9";
|
|
78
|
+
export const EXTENSION_OID_BUILD_SIGNER_DIGEST = "1.3.6.1.4.1.57264.1.10";
|
|
79
|
+
export const EXTENSION_OID_RUNNER_ENVIRONMENT = "1.3.6.1.4.1.57264.1.11";
|
|
80
|
+
export const EXTENSION_OID_SOURCE_REPOSITORY_URI = "1.3.6.1.4.1.57264.1.12";
|
|
81
|
+
export const EXTENSION_OID_SOURCE_REPOSITORY_DIGEST = "1.3.6.1.4.1.57264.1.13";
|
|
82
|
+
export const EXTENSION_OID_SOURCE_REPOSITORY_REF = "1.3.6.1.4.1.57264.1.14";
|
|
83
|
+
export const EXTENSION_OID_SOURCE_REPOSITORY_IDENTIFIER = "1.3.6.1.4.1.57264.1.15";
|
|
84
|
+
export const EXTENSION_OID_SOURCE_REPOSITORY_OWNER_URI = "1.3.6.1.4.1.57264.1.16";
|
|
85
|
+
export const EXTENSION_OID_SOURCE_REPOSITORY_OWNER_IDENTIFIER = "1.3.6.1.4.1.57264.1.17";
|
|
86
|
+
export const EXTENSION_OID_BUILD_CONFIG_URI = "1.3.6.1.4.1.57264.1.18";
|
|
87
|
+
export const EXTENSION_OID_BUILD_CONFIG_DIGEST = "1.3.6.1.4.1.57264.1.19";
|
|
88
|
+
export const EXTENSION_OID_BUILD_TRIGGER = "1.3.6.1.4.1.57264.1.20";
|
|
89
|
+
export const EXTENSION_OID_RUN_INVOCATION_URI = "1.3.6.1.4.1.57264.1.21";
|
|
90
|
+
export const EXTENSION_OID_SOURCE_REPOSITORY_VISIBILITY = "1.3.6.1.4.1.57264.1.22";
|
|
91
|
+
|
|
49
92
|
export class X509Certificate {
|
|
50
93
|
public root: ASN1Obj;
|
|
51
94
|
|
|
@@ -236,6 +279,101 @@ export class X509Certificate {
|
|
|
236
279
|
return ext ? new X509FulcioIssuerV2(ext) : undefined;
|
|
237
280
|
}
|
|
238
281
|
|
|
282
|
+
get extGitHubWorkflowTrigger(): X509GitHubWorkflowTriggerExtension | undefined {
|
|
283
|
+
const ext = this.findExtension(EXTENSION_OID_GITHUB_WORKFLOW_TRIGGER);
|
|
284
|
+
return ext ? new X509GitHubWorkflowTriggerExtension(ext) : undefined;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
get extGitHubWorkflowSHA(): X509GitHubWorkflowSHAExtension | undefined {
|
|
288
|
+
const ext = this.findExtension(EXTENSION_OID_GITHUB_WORKFLOW_SHA);
|
|
289
|
+
return ext ? new X509GitHubWorkflowSHAExtension(ext) : undefined;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
get extGitHubWorkflowName(): X509GitHubWorkflowNameExtension | undefined {
|
|
293
|
+
const ext = this.findExtension(EXTENSION_OID_GITHUB_WORKFLOW_NAME);
|
|
294
|
+
return ext ? new X509GitHubWorkflowNameExtension(ext) : undefined;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
get extGitHubWorkflowRepository(): X509GitHubWorkflowRepositoryExtension | undefined {
|
|
298
|
+
const ext = this.findExtension(EXTENSION_OID_GITHUB_WORKFLOW_REPOSITORY);
|
|
299
|
+
return ext ? new X509GitHubWorkflowRepositoryExtension(ext) : undefined;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
get extGitHubWorkflowRef(): X509GitHubWorkflowRefExtension | undefined {
|
|
303
|
+
const ext = this.findExtension(EXTENSION_OID_GITHUB_WORKFLOW_REF);
|
|
304
|
+
return ext ? new X509GitHubWorkflowRefExtension(ext) : undefined;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
get extBuildSignerURI(): X509BuildSignerURIExtension | undefined {
|
|
308
|
+
const ext = this.findExtension(EXTENSION_OID_BUILD_SIGNER_URI);
|
|
309
|
+
return ext ? new X509BuildSignerURIExtension(ext) : undefined;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
get extBuildSignerDigest(): X509BuildSignerDigestExtension | undefined {
|
|
313
|
+
const ext = this.findExtension(EXTENSION_OID_BUILD_SIGNER_DIGEST);
|
|
314
|
+
return ext ? new X509BuildSignerDigestExtension(ext) : undefined;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
get extRunnerEnvironment(): X509RunnerEnvironmentExtension | undefined {
|
|
318
|
+
const ext = this.findExtension(EXTENSION_OID_RUNNER_ENVIRONMENT);
|
|
319
|
+
return ext ? new X509RunnerEnvironmentExtension(ext) : undefined;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
get extSourceRepositoryURI(): X509SourceRepositoryURIExtension | undefined {
|
|
323
|
+
const ext = this.findExtension(EXTENSION_OID_SOURCE_REPOSITORY_URI);
|
|
324
|
+
return ext ? new X509SourceRepositoryURIExtension(ext) : undefined;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
get extSourceRepositoryDigest(): X509SourceRepositoryDigestExtension | undefined {
|
|
328
|
+
const ext = this.findExtension(EXTENSION_OID_SOURCE_REPOSITORY_DIGEST);
|
|
329
|
+
return ext ? new X509SourceRepositoryDigestExtension(ext) : undefined;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
get extSourceRepositoryRef(): X509SourceRepositoryRefExtension | undefined {
|
|
333
|
+
const ext = this.findExtension(EXTENSION_OID_SOURCE_REPOSITORY_REF);
|
|
334
|
+
return ext ? new X509SourceRepositoryRefExtension(ext) : undefined;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
get extSourceRepositoryIdentifier(): X509SourceRepositoryIdentifierExtension | undefined {
|
|
338
|
+
const ext = this.findExtension(EXTENSION_OID_SOURCE_REPOSITORY_IDENTIFIER);
|
|
339
|
+
return ext ? new X509SourceRepositoryIdentifierExtension(ext) : undefined;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
get extSourceRepositoryOwnerURI(): X509SourceRepositoryOwnerURIExtension | undefined {
|
|
343
|
+
const ext = this.findExtension(EXTENSION_OID_SOURCE_REPOSITORY_OWNER_URI);
|
|
344
|
+
return ext ? new X509SourceRepositoryOwnerURIExtension(ext) : undefined;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
get extSourceRepositoryOwnerIdentifier(): X509SourceRepositoryOwnerIdentifierExtension | undefined {
|
|
348
|
+
const ext = this.findExtension(EXTENSION_OID_SOURCE_REPOSITORY_OWNER_IDENTIFIER);
|
|
349
|
+
return ext ? new X509SourceRepositoryOwnerIdentifierExtension(ext) : undefined;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
get extBuildConfigURI(): X509BuildConfigURIExtension | undefined {
|
|
353
|
+
const ext = this.findExtension(EXTENSION_OID_BUILD_CONFIG_URI);
|
|
354
|
+
return ext ? new X509BuildConfigURIExtension(ext) : undefined;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
get extBuildConfigDigest(): X509BuildConfigDigestExtension | undefined {
|
|
358
|
+
const ext = this.findExtension(EXTENSION_OID_BUILD_CONFIG_DIGEST);
|
|
359
|
+
return ext ? new X509BuildConfigDigestExtension(ext) : undefined;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
get extBuildTrigger(): X509BuildTriggerExtension | undefined {
|
|
363
|
+
const ext = this.findExtension(EXTENSION_OID_BUILD_TRIGGER);
|
|
364
|
+
return ext ? new X509BuildTriggerExtension(ext) : undefined;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
get extRunInvocationURI(): X509RunInvocationURIExtension | undefined {
|
|
368
|
+
const ext = this.findExtension(EXTENSION_OID_RUN_INVOCATION_URI);
|
|
369
|
+
return ext ? new X509RunInvocationURIExtension(ext) : undefined;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
get extSourceRepositoryVisibility(): X509SourceRepositoryVisibilityExtension | undefined {
|
|
373
|
+
const ext = this.findExtension(EXTENSION_OID_SOURCE_REPOSITORY_VISIBILITY);
|
|
374
|
+
return ext ? new X509SourceRepositoryVisibilityExtension(ext) : undefined;
|
|
375
|
+
}
|
|
376
|
+
|
|
239
377
|
get isCA(): boolean {
|
|
240
378
|
const ca = this.extBasicConstraints?.isCA || false;
|
|
241
379
|
|