Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 81 additions & 82 deletions pkg/cmd/attestation/verification/sigstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@ type SigstoreVerifier interface {
}

type LiveSigstoreVerifier struct {
config SigstoreConfig
TrustedRoot string
Copy link
Contributor Author

@malancas malancas Dec 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for these to be nested in another struct. We probably don't need the SigstoreConfig struct either since it only contains three fields of different types.

Logger *io.Handler
NoPublicGood bool
// If tenancy mode is not used, trust domain is empty
TrustDomain string
}

var ErrNoAttestationsVerified = errors.New("no attestations were verified")
Expand All @@ -51,108 +55,98 @@ var ErrNoAttestationsVerified = errors.New("no attestations were verified")
// Public Good, GitHub, or a custom trusted root.
func NewLiveSigstoreVerifier(config SigstoreConfig) *LiveSigstoreVerifier {
return &LiveSigstoreVerifier{
config: config,
TrustedRoot: config.TrustedRoot,
Logger: config.Logger,
NoPublicGood: config.NoPublicGood,
TrustDomain: config.TrustDomain,
}
}

func (v *LiveSigstoreVerifier) chooseVerifier(b *bundle.Bundle) (*verify.SignedEntityVerifier, string, error) {
func getBundleIssuer(b *bundle.Bundle) (string, error) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic was originally inside of the chooseVerifier method. I moved it out so we could parse the bundle's issuer separately. This allowed me to clean up chooseVerifier a bit.

if !b.MinVersion("0.2") {
return nil, "", fmt.Errorf("unsupported bundle version: %s", b.MediaType)
return "", fmt.Errorf("unsupported bundle version: %s", b.MediaType)
}
verifyContent, err := b.VerificationContent()
if err != nil {
return nil, "", fmt.Errorf("failed to get bundle verification content: %v", err)
return "", fmt.Errorf("failed to get bundle verification content: %v", err)
}
leafCert := verifyContent.GetCertificate()
if leafCert == nil {
return nil, "", fmt.Errorf("leaf cert not found")
return "", fmt.Errorf("leaf cert not found")
}
if len(leafCert.Issuer.Organization) != 1 {
return nil, "", fmt.Errorf("expected the leaf certificate issuer to only have one organization")
return "", fmt.Errorf("expected the leaf certificate issuer to only have one organization")
}
issuer := leafCert.Issuer.Organization[0]
return leafCert.Issuer.Organization[0], nil
}

if v.config.TrustedRoot != "" {
customTrustRoots, err := os.ReadFile(v.config.TrustedRoot)
func (v *LiveSigstoreVerifier) chooseVerifier(issuer string) (*verify.SignedEntityVerifier, error) {
// if no custom trusted root is set, attempt to create a Public Good or
// GitHub Sigstore verifier
if v.TrustedRoot == "" {
switch issuer {
case PublicGoodIssuerOrg:
if v.NoPublicGood {
return nil, fmt.Errorf("detected public good instance but requested verification without public good instance")
}
return newPublicGoodVerifier()
case GitHubIssuerOrg:
return newGitHubVerifier(v.TrustDomain)
default:
return nil, fmt.Errorf("leaf certificate issuer is not recognized")
}
}

customTrustRoots, err := os.ReadFile(v.TrustedRoot)
if err != nil {
return nil, fmt.Errorf("unable to read file %s: %v", v.TrustedRoot, err)
}

reader := bufio.NewReader(bytes.NewReader(customTrustRoots))
var line []byte
var readError error
line, readError = reader.ReadBytes('\n')
for readError == nil {
// Load each trusted root
trustedRoot, err := root.NewTrustedRootFromJSON(line)
if err != nil {
return nil, "", fmt.Errorf("unable to read file %s: %v", v.config.TrustedRoot, err)
return nil, fmt.Errorf("failed to create custom verifier: %v", err)
}

reader := bufio.NewReader(bytes.NewReader(customTrustRoots))
var line []byte
var readError error
line, readError = reader.ReadBytes('\n')
for readError == nil {
// Load each trusted root
trustedRoot, err := root.NewTrustedRootFromJSON(line)
// Compare bundle leafCert issuer with trusted root cert authority
certAuthorities := trustedRoot.FulcioCertificateAuthorities()
for _, certAuthority := range certAuthorities {
lowestCert, err := getLowestCertInChain(&certAuthority)
if err != nil {
return nil, "", fmt.Errorf("failed to create custom verifier: %v", err)
return nil, err
}

// Compare bundle leafCert issuer with trusted root cert authority
certAuthorities := trustedRoot.FulcioCertificateAuthorities()
for _, certAuthority := range certAuthorities {
lowestCert, err := getLowestCertInChain(&certAuthority)
if err != nil {
return nil, "", err
}

if len(lowestCert.Issuer.Organization) == 0 {
continue
}
// if the custom trusted root issuer is not set or doesn't match the given issuer, skip it
if len(lowestCert.Issuer.Organization) == 0 || lowestCert.Issuer.Organization[0] != issuer {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I flipped the logic checking if the cert's issuer matches the incoming issuer for an early exit in a given loop iteration.

continue
}

if lowestCert.Issuer.Organization[0] == issuer {
// Determine what policy to use with this trusted root.
//
// Note that we are *only* inferring the policy with the
// issuer. We *must* use the trusted root provided.
if issuer == PublicGoodIssuerOrg {
if v.config.NoPublicGood {
return nil, "", fmt.Errorf("detected public good instance but requested verification without public good instance")
}
verifier, err := newPublicGoodVerifierWithTrustedRoot(trustedRoot)
if err != nil {
return nil, "", err
}
return verifier, issuer, nil
} else if issuer == GitHubIssuerOrg {
verifier, err := newGitHubVerifierWithTrustedRoot(trustedRoot)
if err != nil {
return nil, "", err
}
return verifier, issuer, nil
} else {
// Make best guess at reasonable policy
customVerifier, err := newCustomVerifier(trustedRoot)
if err != nil {
return nil, "", fmt.Errorf("failed to create custom verifier: %v", err)
}
return customVerifier, issuer, nil
}
// Determine what policy to use with this trusted root.
//
// Note that we are *only* inferring the policy with the
// issuer. We *must* use the trusted root provided.
switch issuer {
case PublicGoodIssuerOrg:
if v.NoPublicGood {
return nil, fmt.Errorf("detected public good instance but requested verification without public good instance")
}
return newPublicGoodVerifierWithTrustedRoot(trustedRoot)
case GitHubIssuerOrg:
return newGitHubVerifierWithTrustedRoot(trustedRoot)
default:
// Make best guess at reasonable policy
return newCustomVerifier(trustedRoot)
}
line, readError = reader.ReadBytes('\n')
}
return nil, "", fmt.Errorf("unable to use provided trusted roots")
}

if leafCert.Issuer.Organization[0] == PublicGoodIssuerOrg && !v.config.NoPublicGood {
publicGoodVerifier, err := newPublicGoodVerifier()
if err != nil {
return nil, "", fmt.Errorf("failed to create Public Good Sigstore verifier: %v", err)
}

return publicGoodVerifier, issuer, nil
} else if leafCert.Issuer.Organization[0] == GitHubIssuerOrg || v.config.NoPublicGood {
ghVerifier, err := newGitHubVerifier(v.config.TrustDomain)
if err != nil {
return nil, "", fmt.Errorf("failed to create GitHub Sigstore verifier: %v", err)
}

return ghVerifier, issuer, nil
line, readError = reader.ReadBytes('\n')
}

return nil, "", fmt.Errorf("leaf certificate issuer is not recognized")
return nil, fmt.Errorf("unable to use provided trusted roots")
}

func getLowestCertInChain(ca *root.CertificateAuthority) (*x509.Certificate, error) {
Expand All @@ -168,18 +162,23 @@ func getLowestCertInChain(ca *root.CertificateAuthority) (*x509.Certificate, err
}

func (v *LiveSigstoreVerifier) verify(attestation *api.Attestation, policy verify.PolicyBuilder) (*AttestationProcessingResult, error) {
issuer, err := getBundleIssuer(attestation.Bundle)
if err != nil {
return nil, fmt.Errorf("failed to get bundle issuer: %v", err)
}

// determine which verifier should attempt verification against the bundle
verifier, issuer, err := v.chooseVerifier(attestation.Bundle)
verifier, err := v.chooseVerifier(issuer)
if err != nil {
return nil, fmt.Errorf("failed to find recognized issuer from bundle content: %v", err)
}

v.config.Logger.VerbosePrintf("Attempting verification against issuer \"%s\"\n", issuer)
v.Logger.VerbosePrintf("Attempting verification against issuer \"%s\"\n", issuer)
// attempt to verify the attestation
result, err := verifier.Verify(attestation.Bundle, policy)
// if verification fails, create the error and exit verification early
if err != nil {
v.config.Logger.VerbosePrint(v.config.Logger.ColorScheme.Redf(
v.Logger.VerbosePrint(v.Logger.ColorScheme.Redf(
"Failed to verify against issuer \"%s\" \n\n", issuer,
))

Expand All @@ -188,7 +187,7 @@ func (v *LiveSigstoreVerifier) verify(attestation *api.Attestation, policy verif

// if verification is successful, add the result
// to the AttestationProcessingResult entry
v.config.Logger.VerbosePrint(v.config.Logger.ColorScheme.Greenf(
v.Logger.VerbosePrint(v.Logger.ColorScheme.Greenf(
"SUCCESS - attestation signature verified with \"%s\"\n", issuer,
))

Expand All @@ -208,7 +207,7 @@ func (v *LiveSigstoreVerifier) Verify(attestations []*api.Attestation, policy ve
var lastError error
totalAttestations := len(attestations)
for i, a := range attestations {
v.config.Logger.VerbosePrintf("Verifying attestation %d/%d against the configured Sigstore trust roots\n", i+1, totalAttestations)
v.Logger.VerbosePrintf("Verifying attestation %d/%d against the configured Sigstore trust roots\n", i+1, totalAttestations)

apr, err := v.verify(a, policy)
if err != nil {
Expand Down