podman
439 строк · 14.5 Кб
1// Copyright 2022 The Sigstore Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package certificate
16
17import (
18"crypto/x509/pkix"
19"encoding/asn1"
20"errors"
21"fmt"
22)
23
24var (
25// Deprecated: Use OIDIssuerV2
26OIDIssuer = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 1}
27// Deprecated: Use OIDBuildTrigger
28OIDGitHubWorkflowTrigger = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 2}
29// Deprecated: Use OIDSourceRepositoryDigest
30OIDGitHubWorkflowSHA = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 3}
31// Deprecated: Use OIDBuildConfigURI or OIDBuildConfigDigest
32OIDGitHubWorkflowName = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 4}
33// Deprecated: Use SourceRepositoryURI
34OIDGitHubWorkflowRepository = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 5}
35// Deprecated: Use OIDSourceRepositoryRef
36OIDGitHubWorkflowRef = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 6}
37
38OIDOtherName = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 7}
39OIDIssuerV2 = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 8}
40
41// CI extensions
42OIDBuildSignerURI = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 9}
43OIDBuildSignerDigest = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 10}
44OIDRunnerEnvironment = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 11}
45OIDSourceRepositoryURI = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 12}
46OIDSourceRepositoryDigest = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 13}
47OIDSourceRepositoryRef = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 14}
48OIDSourceRepositoryIdentifier = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 15}
49OIDSourceRepositoryOwnerURI = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 16}
50OIDSourceRepositoryOwnerIdentifier = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 17}
51OIDBuildConfigURI = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 18}
52OIDBuildConfigDigest = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 19}
53OIDBuildTrigger = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 20}
54OIDRunInvocationURI = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 21}
55OIDSourceRepositoryVisibilityAtSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 57264, 1, 22}
56)
57
58// Extensions contains all custom x509 extensions defined by Fulcio
59type Extensions struct {
60// NB: New extensions must be added here and documented
61// at docs/oidc-info.md
62
63// The OIDC issuer. Should match `iss` claim of ID token or, in the case of
64// a federated login like Dex it should match the issuer URL of the
65// upstream issuer. The issuer is not set the extensions are invalid and
66// will fail to render.
67Issuer string // OID 1.3.6.1.4.1.57264.1.8 and 1.3.6.1.4.1.57264.1.1 (Deprecated)
68
69// Deprecated
70// Triggering event of the Github Workflow. Matches the `event_name` claim of ID
71// tokens from Github Actions
72GithubWorkflowTrigger string // OID 1.3.6.1.4.1.57264.1.2
73
74// Deprecated
75// SHA of git commit being built in Github Actions. Matches the `sha` claim of ID
76// tokens from Github Actions
77GithubWorkflowSHA string // OID 1.3.6.1.4.1.57264.1.3
78
79// Deprecated
80// Name of Github Actions Workflow. Matches the `workflow` claim of the ID
81// tokens from Github Actions
82GithubWorkflowName string // OID 1.3.6.1.4.1.57264.1.4
83
84// Deprecated
85// Repository of the Github Actions Workflow. Matches the `repository` claim of the ID
86// tokens from Github Actions
87GithubWorkflowRepository string // OID 1.3.6.1.4.1.57264.1.5
88
89// Deprecated
90// Git Ref of the Github Actions Workflow. Matches the `ref` claim of the ID tokens
91// from Github Actions
92GithubWorkflowRef string // 1.3.6.1.4.1.57264.1.6
93
94// Reference to specific build instructions that are responsible for signing.
95BuildSignerURI string // 1.3.6.1.4.1.57264.1.9
96
97// Immutable reference to the specific version of the build instructions that is responsible for signing.
98BuildSignerDigest string // 1.3.6.1.4.1.57264.1.10
99
100// Specifies whether the build took place in platform-hosted cloud infrastructure or customer/self-hosted infrastructure.
101RunnerEnvironment string // 1.3.6.1.4.1.57264.1.11
102
103// Source repository URL that the build was based on.
104SourceRepositoryURI string // 1.3.6.1.4.1.57264.1.12
105
106// Immutable reference to a specific version of the source code that the build was based upon.
107SourceRepositoryDigest string // 1.3.6.1.4.1.57264.1.13
108
109// Source Repository Ref that the build run was based upon.
110SourceRepositoryRef string // 1.3.6.1.4.1.57264.1.14
111
112// Immutable identifier for the source repository the workflow was based upon.
113SourceRepositoryIdentifier string // 1.3.6.1.4.1.57264.1.15
114
115// Source repository owner URL of the owner of the source repository that the build was based on.
116SourceRepositoryOwnerURI string // 1.3.6.1.4.1.57264.1.16
117
118// Immutable identifier for the owner of the source repository that the workflow was based upon.
119SourceRepositoryOwnerIdentifier string // 1.3.6.1.4.1.57264.1.17
120
121// Build Config URL to the top-level/initiating build instructions.
122BuildConfigURI string // 1.3.6.1.4.1.57264.1.18
123
124// Immutable reference to the specific version of the top-level/initiating build instructions.
125BuildConfigDigest string // 1.3.6.1.4.1.57264.1.19
126
127// Event or action that initiated the build.
128BuildTrigger string // 1.3.6.1.4.1.57264.1.20
129
130// Run Invocation URL to uniquely identify the build execution.
131RunInvocationURI string // 1.3.6.1.4.1.57264.1.21
132
133// Source repository visibility at the time of signing the certificate.
134SourceRepositoryVisibilityAtSigning string // 1.3.6.1.4.1.57264.1.22
135}
136
137func (e Extensions) Render() ([]pkix.Extension, error) {
138var exts []pkix.Extension
139
140// BEGIN: Deprecated
141if e.Issuer != "" {
142// deprecated issuer extension due to incorrect encoding
143exts = append(exts, pkix.Extension{
144Id: OIDIssuer,
145Value: []byte(e.Issuer),
146})
147} else {
148return nil, errors.New("extensions must have a non-empty issuer url")
149}
150if e.GithubWorkflowTrigger != "" {
151exts = append(exts, pkix.Extension{
152Id: OIDGitHubWorkflowTrigger,
153Value: []byte(e.GithubWorkflowTrigger),
154})
155}
156if e.GithubWorkflowSHA != "" {
157exts = append(exts, pkix.Extension{
158Id: OIDGitHubWorkflowSHA,
159Value: []byte(e.GithubWorkflowSHA),
160})
161}
162if e.GithubWorkflowName != "" {
163exts = append(exts, pkix.Extension{
164Id: OIDGitHubWorkflowName,
165Value: []byte(e.GithubWorkflowName),
166})
167}
168if e.GithubWorkflowRepository != "" {
169exts = append(exts, pkix.Extension{
170Id: OIDGitHubWorkflowRepository,
171Value: []byte(e.GithubWorkflowRepository),
172})
173}
174if e.GithubWorkflowRef != "" {
175exts = append(exts, pkix.Extension{
176Id: OIDGitHubWorkflowRef,
177Value: []byte(e.GithubWorkflowRef),
178})
179}
180// END: Deprecated
181
182// duplicate issuer with correct RFC 5280 encoding
183if e.Issuer != "" {
184// construct DER encoding of issuer string
185val, err := asn1.MarshalWithParams(e.Issuer, "utf8")
186if err != nil {
187return nil, err
188}
189exts = append(exts, pkix.Extension{
190Id: OIDIssuerV2,
191Value: val,
192})
193} else {
194return nil, errors.New("extensions must have a non-empty issuer url")
195}
196
197if e.BuildSignerURI != "" {
198val, err := asn1.MarshalWithParams(e.BuildSignerURI, "utf8")
199if err != nil {
200return nil, err
201}
202exts = append(exts, pkix.Extension{
203Id: OIDBuildSignerURI,
204Value: val,
205})
206}
207if e.BuildSignerDigest != "" {
208val, err := asn1.MarshalWithParams(e.BuildSignerDigest, "utf8")
209if err != nil {
210return nil, err
211}
212exts = append(exts, pkix.Extension{
213Id: OIDBuildSignerDigest,
214Value: val,
215})
216}
217if e.RunnerEnvironment != "" {
218val, err := asn1.MarshalWithParams(e.RunnerEnvironment, "utf8")
219if err != nil {
220return nil, err
221}
222exts = append(exts, pkix.Extension{
223Id: OIDRunnerEnvironment,
224Value: val,
225})
226}
227if e.SourceRepositoryURI != "" {
228val, err := asn1.MarshalWithParams(e.SourceRepositoryURI, "utf8")
229if err != nil {
230return nil, err
231}
232exts = append(exts, pkix.Extension{
233Id: OIDSourceRepositoryURI,
234Value: val,
235})
236}
237if e.SourceRepositoryDigest != "" {
238val, err := asn1.MarshalWithParams(e.SourceRepositoryDigest, "utf8")
239if err != nil {
240return nil, err
241}
242exts = append(exts, pkix.Extension{
243Id: OIDSourceRepositoryDigest,
244Value: val,
245})
246}
247if e.SourceRepositoryRef != "" {
248val, err := asn1.MarshalWithParams(e.SourceRepositoryRef, "utf8")
249if err != nil {
250return nil, err
251}
252exts = append(exts, pkix.Extension{
253Id: OIDSourceRepositoryRef,
254Value: val,
255})
256}
257if e.SourceRepositoryIdentifier != "" {
258val, err := asn1.MarshalWithParams(e.SourceRepositoryIdentifier, "utf8")
259if err != nil {
260return nil, err
261}
262exts = append(exts, pkix.Extension{
263Id: OIDSourceRepositoryIdentifier,
264Value: val,
265})
266}
267if e.SourceRepositoryOwnerURI != "" {
268val, err := asn1.MarshalWithParams(e.SourceRepositoryOwnerURI, "utf8")
269if err != nil {
270return nil, err
271}
272exts = append(exts, pkix.Extension{
273Id: OIDSourceRepositoryOwnerURI,
274Value: val,
275})
276}
277if e.SourceRepositoryOwnerIdentifier != "" {
278val, err := asn1.MarshalWithParams(e.SourceRepositoryOwnerIdentifier, "utf8")
279if err != nil {
280return nil, err
281}
282exts = append(exts, pkix.Extension{
283Id: OIDSourceRepositoryOwnerIdentifier,
284Value: val,
285})
286}
287if e.BuildConfigURI != "" {
288val, err := asn1.MarshalWithParams(e.BuildConfigURI, "utf8")
289if err != nil {
290return nil, err
291}
292exts = append(exts, pkix.Extension{
293Id: OIDBuildConfigURI,
294Value: val,
295})
296}
297if e.BuildConfigDigest != "" {
298val, err := asn1.MarshalWithParams(e.BuildConfigDigest, "utf8")
299if err != nil {
300return nil, err
301}
302exts = append(exts, pkix.Extension{
303Id: OIDBuildConfigDigest,
304Value: val,
305})
306}
307if e.BuildTrigger != "" {
308val, err := asn1.MarshalWithParams(e.BuildTrigger, "utf8")
309if err != nil {
310return nil, err
311}
312exts = append(exts, pkix.Extension{
313Id: OIDBuildTrigger,
314Value: val,
315})
316}
317if e.RunInvocationURI != "" {
318val, err := asn1.MarshalWithParams(e.RunInvocationURI, "utf8")
319if err != nil {
320return nil, err
321}
322exts = append(exts, pkix.Extension{
323Id: OIDRunInvocationURI,
324Value: val,
325})
326}
327if e.SourceRepositoryVisibilityAtSigning != "" {
328val, err := asn1.MarshalWithParams(e.SourceRepositoryVisibilityAtSigning, "utf8")
329if err != nil {
330return nil, err
331}
332exts = append(exts, pkix.Extension{
333Id: OIDSourceRepositoryVisibilityAtSigning,
334Value: val,
335})
336}
337
338return exts, nil
339}
340
341func parseExtensions(ext []pkix.Extension) (Extensions, error) {
342out := Extensions{}
343
344for _, e := range ext {
345switch {
346// BEGIN: Deprecated
347case e.Id.Equal(OIDIssuer):
348out.Issuer = string(e.Value)
349case e.Id.Equal(OIDGitHubWorkflowTrigger):
350out.GithubWorkflowTrigger = string(e.Value)
351case e.Id.Equal(OIDGitHubWorkflowSHA):
352out.GithubWorkflowSHA = string(e.Value)
353case e.Id.Equal(OIDGitHubWorkflowName):
354out.GithubWorkflowName = string(e.Value)
355case e.Id.Equal(OIDGitHubWorkflowRepository):
356out.GithubWorkflowRepository = string(e.Value)
357case e.Id.Equal(OIDGitHubWorkflowRef):
358out.GithubWorkflowRef = string(e.Value)
359// END: Deprecated
360case e.Id.Equal(OIDIssuerV2):
361if err := ParseDERString(e.Value, &out.Issuer); err != nil {
362return Extensions{}, err
363}
364case e.Id.Equal(OIDBuildSignerURI):
365if err := ParseDERString(e.Value, &out.BuildSignerURI); err != nil {
366return Extensions{}, err
367}
368case e.Id.Equal(OIDBuildSignerDigest):
369if err := ParseDERString(e.Value, &out.BuildSignerDigest); err != nil {
370return Extensions{}, err
371}
372case e.Id.Equal(OIDRunnerEnvironment):
373if err := ParseDERString(e.Value, &out.RunnerEnvironment); err != nil {
374return Extensions{}, err
375}
376case e.Id.Equal(OIDSourceRepositoryURI):
377if err := ParseDERString(e.Value, &out.SourceRepositoryURI); err != nil {
378return Extensions{}, err
379}
380case e.Id.Equal(OIDSourceRepositoryDigest):
381if err := ParseDERString(e.Value, &out.SourceRepositoryDigest); err != nil {
382return Extensions{}, err
383}
384case e.Id.Equal(OIDSourceRepositoryRef):
385if err := ParseDERString(e.Value, &out.SourceRepositoryRef); err != nil {
386return Extensions{}, err
387}
388case e.Id.Equal(OIDSourceRepositoryIdentifier):
389if err := ParseDERString(e.Value, &out.SourceRepositoryIdentifier); err != nil {
390return Extensions{}, err
391}
392case e.Id.Equal(OIDSourceRepositoryOwnerURI):
393if err := ParseDERString(e.Value, &out.SourceRepositoryOwnerURI); err != nil {
394return Extensions{}, err
395}
396case e.Id.Equal(OIDSourceRepositoryOwnerIdentifier):
397if err := ParseDERString(e.Value, &out.SourceRepositoryOwnerIdentifier); err != nil {
398return Extensions{}, err
399}
400case e.Id.Equal(OIDBuildConfigURI):
401if err := ParseDERString(e.Value, &out.BuildConfigURI); err != nil {
402return Extensions{}, err
403}
404case e.Id.Equal(OIDBuildConfigDigest):
405if err := ParseDERString(e.Value, &out.BuildConfigDigest); err != nil {
406return Extensions{}, err
407}
408case e.Id.Equal(OIDBuildTrigger):
409if err := ParseDERString(e.Value, &out.BuildTrigger); err != nil {
410return Extensions{}, err
411}
412case e.Id.Equal(OIDRunInvocationURI):
413if err := ParseDERString(e.Value, &out.RunInvocationURI); err != nil {
414return Extensions{}, err
415}
416case e.Id.Equal(OIDSourceRepositoryVisibilityAtSigning):
417if err := ParseDERString(e.Value, &out.SourceRepositoryVisibilityAtSigning); err != nil {
418return Extensions{}, err
419}
420}
421}
422
423// We only ever return nil, but leaving error in place so that we can add
424// more complex parsing of fields in a backwards compatible way if needed.
425return out, nil
426}
427
428// ParseDERString decodes a DER-encoded string and puts the value in parsedVal.
429// Returns an error if the unmarshalling fails or if there are trailing bytes in the encoding.
430func ParseDERString(val []byte, parsedVal *string) error {
431rest, err := asn1.Unmarshal(val, parsedVal)
432if err != nil {
433return fmt.Errorf("unexpected error unmarshalling DER-encoded string: %v", err)
434}
435if len(rest) != 0 {
436return errors.New("unexpected trailing bytes in DER-encoded string")
437}
438return nil
439}
440