Skip to content

Commit

Permalink
Add intermediate CA certificate pool for Fulcio (sigstore#1749)
Browse files Browse the repository at this point in the history
This separates roots and intermediates from the TUF targets. This will
be used to configure the default intermediate certificates when none are
found. In particular, this will be used by verify-blob when fetching an
entry from Rekor.

An intermediate CA certificate will be added to the v3 TUF root.

Signed-off-by: Hayden Blauzvern <hblauzvern@google.com>
  • Loading branch information
haydentherapper authored and mlieberman85 committed May 6, 2022
1 parent 7d35b88 commit 4a592ea
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 23 deletions.
4 changes: 4 additions & 0 deletions cmd/cosign/cli/fulcio/fulcio.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,10 @@ func GetRoots() *x509.CertPool {
return fulcioroots.Get()
}

func GetIntermediates() *x509.CertPool {
return fulcioroots.GetIntermediates()
}

func NewClient(fulcioURL string) (api.Client, error) {
fulcioServer, err := url.Parse(fulcioURL)
if err != nil {
Expand Down
62 changes: 48 additions & 14 deletions cmd/cosign/cli/fulcio/fulcioroots/fulcioroots.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,21 @@
package fulcioroots

import (
"bytes"
"context"
"crypto/x509"
"os"
"sync"

"github.com/pkg/errors"
"github.com/sigstore/cosign/pkg/cosign/tuf"
"github.com/sigstore/sigstore/pkg/cryptoutils"
)

var (
rootsOnce sync.Once
roots *x509.CertPool
rootsOnce sync.Once
roots *x509.CertPool
intermediates *x509.CertPool
)

// This is the root in the fulcio project.
Expand All @@ -43,45 +46,76 @@ const (
func Get() *x509.CertPool {
rootsOnce.Do(func() {
var err error
roots, err = initRoots()
roots, intermediates, err = initRoots()
if err != nil {
panic(err)
}
})
return roots
}

func initRoots() (*x509.CertPool, error) {
cp := x509.NewCertPool()
func GetIntermediates() *x509.CertPool {
rootsOnce.Do(func() {
var err error
roots, intermediates, err = initRoots()
if err != nil {
panic(err)
}
})
return intermediates
}

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

rootEnv := os.Getenv(altRoot)
if rootEnv != "" {
raw, err := os.ReadFile(rootEnv)
if err != nil {
return nil, errors.Wrap(err, "error reading root PEM file")
return nil, nil, errors.Wrap(err, "error reading root PEM file")
}
certs, err := cryptoutils.UnmarshalCertificatesFromPEM(raw)
if err != nil {
return nil, nil, errors.Wrap(err, "error unmarshalling certificates")
}
if !cp.AppendCertsFromPEM(raw) {
return nil, errors.New("error creating root cert pool")
for _, cert := range certs {
// root certificates are self-signed
if bytes.Equal(cert.RawSubject, cert.RawIssuer) {
rootPool.AddCert(cert)
} else {
intermediatePool.AddCert(cert)
}
}
} else {
tufClient, err := tuf.NewFromEnv(context.Background())
if err != nil {
return nil, errors.Wrap(err, "initializing tuf")
return nil, nil, errors.Wrap(err, "initializing tuf")
}
defer tufClient.Close()
// Retrieve from the embedded or cached TUF root. If expired, a network
// call is made to update the root.
targets, err := tufClient.GetTargetsByMeta(tuf.Fulcio, []string{fulcioTargetStr, fulcioV1TargetStr})
if err != nil {
return nil, errors.New("error getting targets")
return nil, nil, errors.New("error getting targets")
}
if len(targets) == 0 {
return nil, errors.New("none of the Fulcio roots have been found")
return nil, nil, errors.New("none of the Fulcio roots have been found")
}
for _, t := range targets {
if !cp.AppendCertsFromPEM(t.Target) {
return nil, errors.New("error creating root cert pool")
certs, err := cryptoutils.UnmarshalCertificatesFromPEM(t.Target)
if err != nil {
return nil, nil, errors.Wrap(err, "error unmarshalling certificates")
}
for _, cert := range certs {
// root certificates are self-signed
if bytes.Equal(cert.RawSubject, cert.RawIssuer) {
rootPool.AddCert(cert)
} else {
intermediatePool.AddCert(cert)
}
}
}
}
return cp, nil
return rootPool, intermediatePool, nil
}
24 changes: 18 additions & 6 deletions cmd/cosign/cli/fulcio/fulcioroots/fulcioroots_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,23 +23,35 @@ import (
)

func TestGetFulcioRoots(t *testing.T) {
rootCert, _, _ := test.GenerateRootCa()
pemCert, _ := cryptoutils.MarshalCertificateToPEM(rootCert)
rootCert, rootPriv, _ := test.GenerateRootCa()
rootPemCert, _ := cryptoutils.MarshalCertificateToPEM(rootCert)
subCert, _, _ := test.GenerateSubordinateCa(rootCert, rootPriv)
subPemCert, _ := cryptoutils.MarshalCertificateToPEM(subCert)

var chain []byte
chain = append(chain, subPemCert...)
chain = append(chain, rootPemCert...)

tmpCertFile, err := os.CreateTemp(t.TempDir(), "cosign_fulcio_root_*.cert")
if err != nil {
t.Fatalf("failed to create temp cert file: %v", err)
}
defer tmpCertFile.Close()
if _, err := tmpCertFile.Write(pemCert); err != nil {
if _, err := tmpCertFile.Write(chain); err != nil {
t.Fatalf("failed to write cert file: %v", err)
}
os.Setenv("SIGSTORE_ROOT_FILE", tmpCertFile.Name())
defer os.Unsetenv("SIGSTORE_ROOT_FILE")

certPool := Get()
rootCertPool := Get()
// ignore deprecation error because certificates do not contain from SystemCertPool
if len(rootCertPool.Subjects()) != 1 { // nolint:staticcheck
t.Errorf("expected 1 root certificate, got 0")
}

subCertPool := GetIntermediates()
// ignore deprecation error because certificates do not contain from SystemCertPool
if len(certPool.Subjects()) == 0 { // nolint:staticcheck
t.Errorf("expected 1 or more certificates, got 0")
if len(subCertPool.Subjects()) != 1 { // nolint:staticcheck
t.Errorf("expected 1 intermediate certificate, got 0")
}
}
7 changes: 4 additions & 3 deletions cmd/cosign/cli/verify/verify_blob.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,9 +198,10 @@ func verifySigByUUID(ctx context.Context, ko sign.KeyOpts, rClient *client.Rekor
}

co := &cosign.CheckOpts{
RootCerts: fulcio.GetRoots(),
CertEmail: certEmail,
CertOidcIssuer: certOidcIssuer,
RootCerts: fulcio.GetRoots(),
IntermediateCerts: fulcio.GetIntermediates(),
CertEmail: certEmail,
CertOidcIssuer: certOidcIssuer,
}
cert := certs[0]
verifier, err := cosign.ValidateAndUnpackCert(cert, co)
Expand Down

0 comments on commit 4a592ea

Please sign in to comment.