Skip to content

Commit

Permalink
Load in intermediate cert pool from TUF (sigstore#1804)
Browse files Browse the repository at this point in the history
With the v3 TUF root, the intermediate CA certificate will be included,
so that if the intermediate signing key was compromised, the
intermediate certificate could be revoked by removing it from the TUF
targets and replacing it with a trusted certificate.

This change loads the intermediate certificate from TUF. However, we
don't want to force all users to follow this structure - They may choose
to use CRLs to detect revoked intermediates. Also, I don't want to
enforce TUF usage in the Verify package. Therefore, for TUF, we lazily create
a certificate pool only if an intermediate certificate is found, and if
it's not found, then VerifyImageSignature will create a pool using the
chain provided in the annotation.

Signed-off-by: Hayden Blauzvern <[email protected]>
  • Loading branch information
haydentherapper authored and mlieberman85 committed May 6, 2022
1 parent 5acebd4 commit bb7c39d
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 4 deletions.
19 changes: 17 additions & 2 deletions cmd/cosign/cli/fulcio/fulcioroots/fulcioroots.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ func GetIntermediates() *x509.CertPool {
}

func initRoots() (*x509.CertPool, *x509.CertPool, error) {
rootPool := x509.NewCertPool()
intermediatePool := x509.NewCertPool()
var rootPool *x509.CertPool
var intermediatePool *x509.CertPool

rootEnv := os.Getenv(altRoot)
if rootEnv != "" {
Expand All @@ -99,8 +99,14 @@ func initRoots() (*x509.CertPool, *x509.CertPool, error) {
for _, cert := range certs {
// root certificates are self-signed
if bytes.Equal(cert.RawSubject, cert.RawIssuer) {
if rootPool == nil {
rootPool = x509.NewCertPool()
}
rootPool.AddCert(cert)
} else {
if intermediatePool == nil {
intermediatePool = x509.NewCertPool()
}
intermediatePool.AddCert(cert)
}
}
Expand All @@ -127,12 +133,21 @@ func initRoots() (*x509.CertPool, *x509.CertPool, error) {
for _, cert := range certs {
// root certificates are self-signed
if bytes.Equal(cert.RawSubject, cert.RawIssuer) {
if rootPool == nil {
rootPool = x509.NewCertPool()
}
rootPool.AddCert(cert)
} else {
if intermediatePool == nil {
intermediatePool = x509.NewCertPool()
}
intermediatePool.AddCert(cert)
}
}
}
if intermediatePool == nil {
intermediatePool = x509.NewCertPool()
}
intermediatePool.AppendCertsFromPEM([]byte(fulcioIntermediateV1))
}
return rootPool, intermediatePool, nil
Expand Down
1 change: 1 addition & 0 deletions cmd/cosign/cli/verify/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ func (c *VerifyCommand) Exec(ctx context.Context, images []string) (err error) {
co.RekorClient = rekorClient
}
co.RootCerts = fulcio.GetRoots()
co.IntermediateCerts = fulcio.GetIntermediates()
}
keyRef := c.KeyRef
certRef := c.CertRef
Expand Down
1 change: 1 addition & 0 deletions cmd/cosign/cli/verify/verify_attestation.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ func (c *VerifyAttestationCommand) Exec(ctx context.Context, images []string) (e
co.RekorClient = rekorClient
}
co.RootCerts = fulcio.GetRoots()
co.IntermediateCerts = fulcio.GetIntermediates()
}
keyRef := c.KeyRef

Expand Down
6 changes: 4 additions & 2 deletions pkg/cosign/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,8 @@ func VerifyImageSignature(ctx context.Context, sig oci.Signature, h v1.Hash, co
// If the chain annotation is not present or there is only a root
if chain == nil || len(chain) <= 1 {
co.IntermediateCerts = nil
} else {
} else if co.IntermediateCerts == nil {
// If the intermediate certs have not been loaded in by TUF
pool := x509.NewCertPool()
for _, cert := range chain[:len(chain)-1] {
pool.AddCert(cert)
Expand Down Expand Up @@ -653,7 +654,8 @@ func verifyImageAttestations(ctx context.Context, atts oci.Signatures, h v1.Hash
// If the chain annotation is not present or there is only a root
if chain == nil || len(chain) <= 1 {
co.IntermediateCerts = nil
} else {
} else if co.IntermediateCerts == nil {
// If the intermediate certs have not been loaded in by TUF
pool := x509.NewCertPool()
for _, cert := range chain[:len(chain)-1] {
pool.AddCert(cert)
Expand Down
36 changes: 36 additions & 0 deletions pkg/cosign/verify_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,42 @@ func TestVerifyImageSignatureWithMissingSub(t *testing.T) {
}
}

func TestVerifyImageSignatureWithExistingSub(t *testing.T) {
rootCert, rootKey, _ := test.GenerateRootCa()
subCert, subKey, _ := test.GenerateSubordinateCa(rootCert, rootKey)
leafCert, privKey, _ := test.GenerateLeafCert("subject", "oidc-issuer", subCert, subKey)
pemRoot := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: rootCert.Raw})
pemSub := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: subCert.Raw})
pemLeaf := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: leafCert.Raw})

otherSubCert, _, _ := test.GenerateSubordinateCa(rootCert, rootKey)

rootPool := x509.NewCertPool()
rootPool.AddCert(rootCert)
subPool := x509.NewCertPool()
// Load in different sub cert so the chain doesn't verify
rootPool.AddCert(otherSubCert)

payload := []byte{1, 2, 3, 4}
h := sha256.Sum256(payload)
signature, _ := privKey.Sign(rand.Reader, h[:], crypto.SHA256)

ociSig, _ := static.NewSignature(payload,
base64.StdEncoding.EncodeToString(signature),
static.WithCertChain(pemLeaf, appendSlices([][]byte{pemSub, pemRoot})))
verified, err := VerifyImageSignature(context.TODO(), ociSig, v1.Hash{}, &CheckOpts{RootCerts: rootPool, IntermediateCerts: subPool})
if err == nil {
t.Fatal("expected error while verifying signature")
}
if !strings.Contains(err.Error(), "certificate signed by unknown authority") {
t.Fatal("expected error while verifying signature")
}
// TODO: Create fake bundle and test verification
if verified == true {
t.Fatalf("expected verified=false, got verified=true")
}
}

func TestValidateAndUnpackCertSuccess(t *testing.T) {
subject := "email@email"
oidcIssuer := "https://accounts.google.com"
Expand Down
1 change: 1 addition & 0 deletions pkg/sget/sget.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ func (sg *SecureGet) Do(ctx context.Context) error {
fulcioVerified := (co.SigVerifier == nil)

co.RootCerts = fulcio.GetRoots()
co.IntermediateCerts = fulcio.GetIntermediates()

sp, bundleVerified, err := cosign.VerifyImageSignatures(ctx, ref, co)
if err != nil {
Expand Down

0 comments on commit bb7c39d

Please sign in to comment.