@@ -9,12 +9,23 @@ import (
99 "fmt"
1010 "io"
1111 "os"
12+ "time"
1213
1314 "github.com/sigstore/sigstore-go/pkg/bundle"
1415 "github.com/sigstore/sigstore-go/pkg/root"
1516 "github.com/sigstore/sigstore-go/pkg/verify"
17+ log "github.com/sirupsen/logrus"
1618)
1719
20+ // VerificationFailedError is returned when SLSA verification fails
21+ type VerificationFailedError struct {
22+ Reason string
23+ }
24+
25+ func (e VerificationFailedError ) Error () string {
26+ return fmt .Sprintf ("SLSA verification failed: %s" , e .Reason )
27+ }
28+
1829// VerifierInterface defines the interface for SLSA verification
1930type VerifierInterface interface {
2031 VerifyArtifact (ctx context.Context , artifactPath , attestationPath string ) error
@@ -38,19 +49,30 @@ func NewVerifier(sourceURI string, trustedRoots []string) *Verifier {
3849// This implementation uses the official Sigstore Go library which natively supports
3950// Sigstore Bundle format and uses embedded transparency log entries for verification.
4051func (v * Verifier ) VerifyArtifact (ctx context.Context , artifactPath , attestationPath string ) error {
52+ startTime := time .Now ()
53+
54+ log .WithFields (log.Fields {
55+ "artifact" : artifactPath ,
56+ "attestation" : attestationPath ,
57+ }).Debug ("Starting SLSA verification" )
58+
4159 // Step 1: Load the Sigstore Bundle
4260 // This parses the attestation file as a Sigstore Bundle v0.3 format
4361 b , err := bundle .LoadJSONFromPath (attestationPath )
4462 if err != nil {
45- return fmt .Errorf ("failed to load attestation bundle: %w" , err )
63+ return VerificationFailedError {
64+ Reason : fmt .Sprintf ("failed to load attestation bundle: %v" , err ),
65+ }
4666 }
4767
4868 // Step 2: Get trusted root from Sigstore public good instance
4969 // This fetches the current trusted root (CA certificates, Rekor public keys, etc.)
5070 // from Sigstore's TUF repository
5171 trustedRoot , err := root .FetchTrustedRoot ()
5272 if err != nil {
53- return fmt .Errorf ("failed to fetch trusted root: %w" , err )
73+ return VerificationFailedError {
74+ Reason : fmt .Sprintf ("failed to fetch trusted root: %v" , err ),
75+ }
5476 }
5577
5678 // Step 3: Create a verifier with transparency log verification
@@ -62,13 +84,17 @@ func (v *Verifier) VerifyArtifact(ctx context.Context, artifactPath, attestation
6284 verify .WithIntegratedTimestamps (1 ),
6385 )
6486 if err != nil {
65- return fmt .Errorf ("failed to create verifier: %w" , err )
87+ return VerificationFailedError {
88+ Reason : fmt .Sprintf ("failed to create verifier: %v" , err ),
89+ }
6690 }
6791
6892 // Step 4: Open the artifact file for verification
6993 artifactFile , err := os .Open (artifactPath )
7094 if err != nil {
71- return fmt .Errorf ("failed to open artifact: %w" , err )
95+ return VerificationFailedError {
96+ Reason : fmt .Sprintf ("failed to open artifact: %v" , err ),
97+ }
7298 }
7399 defer artifactFile .Close ()
74100
@@ -90,20 +116,26 @@ func (v *Verifier) VerifyArtifact(ctx context.Context, artifactPath, attestation
90116 // - Artifact hash matches (if provided)
91117 _ , err = verifier .Verify (b , policy )
92118 if err != nil {
93- return fmt .Errorf ("signature verification failed: %w" , err )
119+ return VerificationFailedError {
120+ Reason : fmt .Sprintf ("signature verification failed: %v" , err ),
121+ }
94122 }
95123
96124 // Step 7: Extract and verify the subject hash from the attestation
97125 // The attestation contains the expected hash of the artifact in the SLSA provenance
98126 envelope , err := b .Envelope ()
99127 if err != nil {
100- return fmt .Errorf ("failed to get envelope: %w" , err )
128+ return VerificationFailedError {
129+ Reason : fmt .Sprintf ("failed to get envelope: %v" , err ),
130+ }
101131 }
102132
103133 // Decode the base64-encoded payload
104134 payloadBytes , err := base64 .StdEncoding .DecodeString (envelope .Payload )
105135 if err != nil {
106- return fmt .Errorf ("failed to decode payload: %w" , err )
136+ return VerificationFailedError {
137+ Reason : fmt .Sprintf ("failed to decode payload: %v" , err ),
138+ }
107139 }
108140
109141 // Parse the SLSA provenance to get the subject
@@ -116,35 +148,54 @@ func (v *Verifier) VerifyArtifact(ctx context.Context, artifactPath, attestation
116148 }
117149
118150 if err := json .Unmarshal (payloadBytes , & payload ); err != nil {
119- return fmt .Errorf ("failed to parse payload: %w" , err )
151+ return VerificationFailedError {
152+ Reason : fmt .Sprintf ("failed to parse payload: %v" , err ),
153+ }
120154 }
121155
122156 if len (payload .Subject ) == 0 {
123- return fmt .Errorf ("no subject in attestation" )
157+ return VerificationFailedError {
158+ Reason : "no subject in attestation" ,
159+ }
124160 }
125161
126162 expectedHash := payload .Subject [0 ].Digest .Sha256
127163 if expectedHash == "" {
128- return fmt .Errorf ("SLSA provenance subject has no SHA256 digest" )
164+ return VerificationFailedError {
165+ Reason : "SLSA provenance subject has no SHA256 digest" ,
166+ }
129167 }
130168
131169 // Step 8: Hash the actual artifact and compare
132170 artifactFile .Seek (0 , 0 ) // Reset file pointer
133171 h := sha256 .New ()
134172 if _ , err := io .Copy (h , artifactFile ); err != nil {
135- return fmt .Errorf ("failed to hash artifact: %w" , err )
173+ return VerificationFailedError {
174+ Reason : fmt .Sprintf ("failed to hash artifact: %v" , err ),
175+ }
136176 }
137177 actualHash := hex .EncodeToString (h .Sum (nil ))
138178
139179 if actualHash != expectedHash {
140- return fmt .Errorf ("hash mismatch: expected %s, got %s" , expectedHash , actualHash )
180+ return VerificationFailedError {
181+ Reason : fmt .Sprintf ("hash mismatch: expected %s, got %s" , expectedHash , actualHash ),
182+ }
141183 }
142184
143185 // Success! The artifact is verified:
144186 // ✅ Signature is valid
145187 // ✅ Certificate chain is valid
146188 // ✅ Transparency log entry is valid
147189 // ✅ Hash matches
190+
191+ duration := time .Since (startTime )
192+ log .WithFields (log.Fields {
193+ "artifact" : artifactPath ,
194+ "expectedHash" : expectedHash ,
195+ "actualHash" : actualHash ,
196+ "verificationMs" : duration .Milliseconds (),
197+ }).Info ("SLSA verification successful" )
198+
148199 return nil
149200}
150201
0 commit comments