Skip to content

Commit 0adcc43

Browse files
committed
Add SLSA v1.0 provenance attestation support
Implement parser and schema validation for SLSA v1.0 provenance attestations to support the updated SLSA specification while maintaining backward compatibility with v0.2. Co-authored-by: Claude Code <[email protected]> Ref: https://issues.redhat.com/browse/EC-1581
1 parent e64009c commit 0adcc43

File tree

9 files changed

+1863
-0
lines changed

9 files changed

+1863
-0
lines changed

internal/attestation/__snapshots__/slsa_provenance_v1_test.snap

Lines changed: 102 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// Copyright The Conforma Contributors
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+
//
15+
// SPDX-License-Identifier: Apache-2.0
16+
17+
package attestation
18+
19+
import (
20+
"encoding/json"
21+
"fmt"
22+
23+
"github.com/in-toto/in-toto-golang/in_toto"
24+
v1 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1"
25+
"github.com/sigstore/cosign/v2/pkg/oci"
26+
27+
"github.com/conforma/cli/internal/signature"
28+
)
29+
30+
const (
31+
// Make it visible elsewhere
32+
PredicateSLSAProvenanceV1 = v1.PredicateSLSAProvenance
33+
)
34+
35+
// SLSAProvenanceFromSignatureV1 parses the SLSA Provenance v1 from the provided OCI
36+
// layer. Expects that the layer contains DSSE JSON with the embedded SLSA
37+
// Provenance v1 payload.
38+
func SLSAProvenanceFromSignatureV1(sig oci.Signature) (Attestation, error) {
39+
payload, err := payloadFromSig(sig)
40+
if err != nil {
41+
return nil, err
42+
}
43+
44+
embedded, err := decodedPayload(payload)
45+
if err != nil {
46+
return nil, err
47+
}
48+
49+
var statement in_toto.ProvenanceStatementSLSA1
50+
if err := json.Unmarshal(embedded, &statement); err != nil {
51+
return nil, fmt.Errorf("malformed attestation data: %w", err)
52+
}
53+
54+
if statement.Type != in_toto.StatementInTotoV01 {
55+
return nil, fmt.Errorf("unsupported attestation type: %s", statement.Type)
56+
}
57+
58+
if statement.PredicateType != v1.PredicateSLSAProvenance {
59+
return nil, fmt.Errorf("unsupported attestation predicate type: %s", statement.PredicateType)
60+
}
61+
62+
signatures, err := createEntitySignatures(sig, payload)
63+
if err != nil {
64+
return nil, fmt.Errorf("cannot create signed entity: %w", err)
65+
}
66+
67+
return slsaProvenanceV1{statement: statement, data: embedded, signatures: signatures}, nil
68+
}
69+
70+
type slsaProvenanceV1 struct {
71+
statement in_toto.ProvenanceStatementSLSA1
72+
data []byte
73+
signatures []signature.EntitySignature
74+
}
75+
76+
func (a slsaProvenanceV1) Type() string {
77+
return in_toto.StatementInTotoV01
78+
}
79+
80+
func (a slsaProvenanceV1) PredicateType() string {
81+
return v1.PredicateSLSAProvenance
82+
}
83+
84+
// This returns the raw json, not the content of a.statement
85+
func (a slsaProvenanceV1) Statement() []byte {
86+
return a.data
87+
}
88+
89+
func (a slsaProvenanceV1) PredicateBuildType() string {
90+
return a.statement.Predicate.BuildDefinition.BuildType
91+
}
92+
93+
func (a slsaProvenanceV1) Signatures() []signature.EntitySignature {
94+
return a.signatures
95+
}
96+
97+
func (a slsaProvenanceV1) Subject() []in_toto.Subject {
98+
return a.statement.Subject
99+
}
100+
101+
// Todo: It seems odd that this does not contain the statement.
102+
// (See also the equivalent method in attestation.go)
103+
func (a slsaProvenanceV1) MarshalJSON() ([]byte, error) {
104+
val := struct {
105+
Type string `json:"type"`
106+
PredicateType string `json:"predicateType"`
107+
PredicateBuildType string `json:"predicateBuildType"`
108+
Signatures []signature.EntitySignature `json:"signatures"`
109+
}{
110+
Type: a.statement.Type,
111+
PredicateType: a.statement.PredicateType,
112+
PredicateBuildType: a.statement.Predicate.BuildDefinition.BuildType,
113+
Signatures: a.signatures,
114+
}
115+
116+
return json.Marshal(val)
117+
}

0 commit comments

Comments
 (0)