Skip to content

Commit d565490

Browse files
committed
Move common functions to common.go
Signed-off-by: Colleen Murphy <colleenmurphy@google.com>
1 parent 3b862a6 commit d565490

File tree

2 files changed

+281
-278
lines changed

2 files changed

+281
-278
lines changed

cmd/cosign/cli/verify/common.go

Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,32 @@
1414
package verify
1515

1616
import (
17+
"bytes"
1718
"context"
1819
"crypto"
1920
"crypto/x509"
21+
"encoding/base64"
22+
"encoding/json"
23+
"errors"
2024
"fmt"
25+
"os"
2126
"reflect"
2227

28+
"github.com/sigstore/cosign/v3/cmd/cosign/cli/fulcio"
2329
"github.com/sigstore/cosign/v3/cmd/cosign/cli/options"
2430
"github.com/sigstore/cosign/v3/cmd/cosign/cli/rekor"
2531
"github.com/sigstore/cosign/v3/internal/ui"
32+
"github.com/sigstore/cosign/v3/pkg/blob"
2633
"github.com/sigstore/cosign/v3/pkg/cosign"
2734
"github.com/sigstore/cosign/v3/pkg/cosign/env"
2835
"github.com/sigstore/cosign/v3/pkg/cosign/pivkey"
2936
"github.com/sigstore/cosign/v3/pkg/cosign/pkcs11key"
37+
"github.com/sigstore/cosign/v3/pkg/oci"
3038
csignature "github.com/sigstore/cosign/v3/pkg/signature"
3139
"github.com/sigstore/sigstore-go/pkg/root"
40+
"github.com/sigstore/sigstore/pkg/cryptoutils"
3241
"github.com/sigstore/sigstore/pkg/signature"
42+
"github.com/sigstore/sigstore/pkg/signature/payload"
3343
)
3444

3545
// CheckSigstoreBundleUnsupportedOptions checks for incompatible settings on any Verify* command struct when NewBundleFormat is used.
@@ -182,3 +192,274 @@ func SetTrustedMaterial(ctx context.Context, trustedRootPath, certChain, caRoots
182192
}
183193
return nil
184194
}
195+
196+
// PrintVerificationHeader prints boilerplate information after successful verification.
197+
func PrintVerificationHeader(ctx context.Context, imgRef string, co *cosign.CheckOpts, bundleVerified, fulcioVerified bool) {
198+
ui.Infof(ctx, "\nVerification for %s --", imgRef)
199+
ui.Infof(ctx, "The following checks were performed on each of these signatures:")
200+
if co.ClaimVerifier != nil {
201+
if co.Annotations != nil {
202+
ui.Infof(ctx, " - The specified annotations were verified.")
203+
}
204+
ui.Infof(ctx, " - The cosign claims were validated")
205+
}
206+
if bundleVerified {
207+
ui.Infof(ctx, " - Existence of the claims in the transparency log was verified offline")
208+
} else if co.RekorClient != nil {
209+
ui.Infof(ctx, " - The claims were present in the transparency log")
210+
ui.Infof(ctx, " - The signatures were integrated into the transparency log when the certificate was valid")
211+
}
212+
if co.SigVerifier != nil {
213+
ui.Infof(ctx, " - The signatures were verified against the specified public key")
214+
}
215+
if fulcioVerified {
216+
ui.Infof(ctx, " - The code-signing certificate was verified using trusted certificate authority certificates")
217+
}
218+
}
219+
220+
// PrintVerification logs details about the verification to stdout.
221+
func PrintVerification(ctx context.Context, verified []oci.Signature, output string) {
222+
switch output {
223+
case "text":
224+
for _, sig := range verified {
225+
if cert, err := sig.Cert(); err == nil && cert != nil {
226+
ce := cosign.CertExtensions{Cert: cert}
227+
sub := ""
228+
if sans := cryptoutils.GetSubjectAlternateNames(cert); len(sans) > 0 {
229+
sub = sans[0]
230+
}
231+
ui.Infof(ctx, "Certificate subject: %s", sub)
232+
if issuerURL := ce.GetIssuer(); issuerURL != "" {
233+
ui.Infof(ctx, "Certificate issuer URL: %s", issuerURL)
234+
}
235+
236+
if githubWorkflowTrigger := ce.GetCertExtensionGithubWorkflowTrigger(); githubWorkflowTrigger != "" {
237+
ui.Infof(ctx, "GitHub Workflow Trigger: %s", githubWorkflowTrigger)
238+
}
239+
240+
if githubWorkflowSha := ce.GetExtensionGithubWorkflowSha(); githubWorkflowSha != "" {
241+
ui.Infof(ctx, "GitHub Workflow SHA: %s", githubWorkflowSha)
242+
}
243+
if githubWorkflowName := ce.GetCertExtensionGithubWorkflowName(); githubWorkflowName != "" {
244+
ui.Infof(ctx, "GitHub Workflow Name: %s", githubWorkflowName)
245+
}
246+
247+
if githubWorkflowRepository := ce.GetCertExtensionGithubWorkflowRepository(); githubWorkflowRepository != "" {
248+
ui.Infof(ctx, "GitHub Workflow Repository: %s", githubWorkflowRepository)
249+
}
250+
251+
if githubWorkflowRef := ce.GetCertExtensionGithubWorkflowRef(); githubWorkflowRef != "" {
252+
ui.Infof(ctx, "GitHub Workflow Ref: %s", githubWorkflowRef)
253+
}
254+
}
255+
256+
p, err := sig.Payload()
257+
if err != nil {
258+
fmt.Fprintf(os.Stderr, "Error fetching payload: %v", err)
259+
return
260+
}
261+
fmt.Println(string(p))
262+
}
263+
264+
default:
265+
var outputKeys []payload.SimpleContainerImage
266+
for _, sig := range verified {
267+
p, err := sig.Payload()
268+
if err != nil {
269+
fmt.Fprintf(os.Stderr, "Error fetching payload: %v", err)
270+
return
271+
}
272+
273+
ss := payload.SimpleContainerImage{}
274+
if err := json.Unmarshal(p, &ss); err != nil {
275+
fmt.Println("error decoding the payload:", err.Error())
276+
return
277+
}
278+
279+
if cert, err := sig.Cert(); err == nil && cert != nil {
280+
ce := cosign.CertExtensions{Cert: cert}
281+
if ss.Optional == nil {
282+
ss.Optional = make(map[string]interface{})
283+
}
284+
sub := ""
285+
if sans := cryptoutils.GetSubjectAlternateNames(cert); len(sans) > 0 {
286+
sub = sans[0]
287+
}
288+
ss.Optional["Subject"] = sub
289+
if issuerURL := ce.GetIssuer(); issuerURL != "" {
290+
ss.Optional["Issuer"] = issuerURL
291+
ss.Optional[cosign.CertExtensionOIDCIssuer] = issuerURL
292+
}
293+
if githubWorkflowTrigger := ce.GetCertExtensionGithubWorkflowTrigger(); githubWorkflowTrigger != "" {
294+
ss.Optional[cosign.CertExtensionMap[cosign.CertExtensionGithubWorkflowTrigger]] = githubWorkflowTrigger
295+
ss.Optional[cosign.CertExtensionGithubWorkflowTrigger] = githubWorkflowTrigger
296+
}
297+
298+
if githubWorkflowSha := ce.GetExtensionGithubWorkflowSha(); githubWorkflowSha != "" {
299+
ss.Optional[cosign.CertExtensionMap[cosign.CertExtensionGithubWorkflowSha]] = githubWorkflowSha
300+
ss.Optional[cosign.CertExtensionGithubWorkflowSha] = githubWorkflowSha
301+
}
302+
if githubWorkflowName := ce.GetCertExtensionGithubWorkflowName(); githubWorkflowName != "" {
303+
ss.Optional[cosign.CertExtensionMap[cosign.CertExtensionGithubWorkflowName]] = githubWorkflowName
304+
ss.Optional[cosign.CertExtensionGithubWorkflowName] = githubWorkflowName
305+
}
306+
307+
if githubWorkflowRepository := ce.GetCertExtensionGithubWorkflowRepository(); githubWorkflowRepository != "" {
308+
ss.Optional[cosign.CertExtensionMap[cosign.CertExtensionGithubWorkflowRepository]] = githubWorkflowRepository
309+
ss.Optional[cosign.CertExtensionGithubWorkflowRepository] = githubWorkflowRepository
310+
}
311+
312+
if githubWorkflowRef := ce.GetCertExtensionGithubWorkflowRef(); githubWorkflowRef != "" {
313+
ss.Optional[cosign.CertExtensionMap[cosign.CertExtensionGithubWorkflowRef]] = githubWorkflowRef
314+
ss.Optional[cosign.CertExtensionGithubWorkflowRef] = githubWorkflowRef
315+
}
316+
}
317+
if bundle, err := sig.Bundle(); err == nil && bundle != nil {
318+
if ss.Optional == nil {
319+
ss.Optional = make(map[string]interface{})
320+
}
321+
ss.Optional["Bundle"] = bundle
322+
}
323+
if rfc3161Timestamp, err := sig.RFC3161Timestamp(); err == nil && rfc3161Timestamp != nil {
324+
if ss.Optional == nil {
325+
ss.Optional = make(map[string]interface{})
326+
}
327+
ss.Optional["RFC3161Timestamp"] = rfc3161Timestamp
328+
}
329+
330+
outputKeys = append(outputKeys, ss)
331+
}
332+
333+
b, err := json.Marshal(outputKeys)
334+
if err != nil {
335+
fmt.Println("error when generating the output:", err.Error())
336+
return
337+
}
338+
339+
fmt.Printf("\n%s\n", string(b))
340+
}
341+
}
342+
343+
func loadCertFromFileOrURL(path string) (*x509.Certificate, error) {
344+
pems, err := blob.LoadFileOrURL(path)
345+
if err != nil {
346+
return nil, err
347+
}
348+
return loadCertFromPEM(pems)
349+
}
350+
351+
func loadCertFromPEM(pems []byte) (*x509.Certificate, error) {
352+
var out []byte
353+
out, err := base64.StdEncoding.DecodeString(string(pems))
354+
if err != nil {
355+
// not a base64
356+
out = pems
357+
}
358+
359+
certs, err := cryptoutils.UnmarshalCertificatesFromPEM(out)
360+
if err != nil {
361+
return nil, err
362+
}
363+
if len(certs) == 0 {
364+
return nil, errors.New("no certs found in pem file")
365+
}
366+
return certs[0], nil
367+
}
368+
369+
func loadCertChainFromFileOrURL(path string) ([]*x509.Certificate, error) {
370+
pems, err := blob.LoadFileOrURL(path)
371+
if err != nil {
372+
return nil, err
373+
}
374+
certs, err := cryptoutils.LoadCertificatesFromPEM(bytes.NewReader(pems))
375+
if err != nil {
376+
return nil, err
377+
}
378+
return certs, nil
379+
}
380+
381+
func keylessVerification(keyRef string, sk bool) bool {
382+
if keyRef != "" {
383+
return false
384+
}
385+
if sk {
386+
return false
387+
}
388+
return true
389+
}
390+
391+
func shouldVerifySCT(ignoreSCT bool, keyRef string, sk bool) bool {
392+
if keyRef != "" {
393+
return false
394+
}
395+
if sk {
396+
return false
397+
}
398+
if ignoreSCT {
399+
return false
400+
}
401+
return true
402+
}
403+
404+
// loadCertsKeylessVerification loads certificates provided as a certificate chain or CA roots + CA intermediate
405+
// certificate files. If both certChain and caRootsFile are empty strings, the Fulcio roots are loaded.
406+
//
407+
// The co *cosign.CheckOpts is both input and output parameter - it gets updated
408+
// with the root and intermediate certificates needed for verification.
409+
func loadCertsKeylessVerification(certChainFile string,
410+
caRootsFile string,
411+
caIntermediatesFile string,
412+
co *cosign.CheckOpts) error {
413+
var err error
414+
switch {
415+
case certChainFile != "":
416+
chain, err := loadCertChainFromFileOrURL(certChainFile)
417+
if err != nil {
418+
return err
419+
}
420+
co.RootCerts = x509.NewCertPool()
421+
co.RootCerts.AddCert(chain[len(chain)-1])
422+
if len(chain) > 1 {
423+
co.IntermediateCerts = x509.NewCertPool()
424+
for _, cert := range chain[:len(chain)-1] {
425+
co.IntermediateCerts.AddCert(cert)
426+
}
427+
}
428+
case caRootsFile != "":
429+
caRoots, err := loadCertChainFromFileOrURL(caRootsFile)
430+
if err != nil {
431+
return err
432+
}
433+
co.RootCerts = x509.NewCertPool()
434+
if len(caRoots) > 0 {
435+
for _, cert := range caRoots {
436+
co.RootCerts.AddCert(cert)
437+
}
438+
}
439+
if caIntermediatesFile != "" {
440+
caIntermediates, err := loadCertChainFromFileOrURL(caIntermediatesFile)
441+
if err != nil {
442+
return err
443+
}
444+
if len(caIntermediates) > 0 {
445+
co.IntermediateCerts = x509.NewCertPool()
446+
for _, cert := range caIntermediates {
447+
co.IntermediateCerts.AddCert(cert)
448+
}
449+
}
450+
}
451+
default:
452+
// This performs an online fetch of the Fulcio roots from a TUF repository.
453+
// This is needed for verifying keyless certificates (both online and offline).
454+
co.RootCerts, err = fulcio.GetRoots()
455+
if err != nil {
456+
return fmt.Errorf("getting Fulcio roots: %w", err)
457+
}
458+
co.IntermediateCerts, err = fulcio.GetIntermediates()
459+
if err != nil {
460+
return fmt.Errorf("getting Fulcio intermediates: %w", err)
461+
}
462+
}
463+
464+
return nil
465+
}

0 commit comments

Comments
 (0)