Skip to content

Commit

Permalink
Verify SCTs returned by fulcio
Browse files Browse the repository at this point in the history
Added in the CT log public key for this verification.

Signed-off-by: Priya Wadhwa <priyawadhwa@google.com>
  • Loading branch information
Priya Wadhwa committed Aug 31, 2021
1 parent c79ba73 commit 435f59f
Show file tree
Hide file tree
Showing 5 changed files with 219 additions and 32 deletions.
4 changes: 4 additions & 0 deletions cmd/cosign/cli/fulcio/ct.pub
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEbfwR+RJudXscgRBRpKX1XFDy3Pyu
dDxz/SfnRi1fT8ekpfBd2O1uoz7jr3Z8nKzxA69EUQ+eFCFI3zeubPWU7w==
-----END PUBLIC KEY-----
78 changes: 66 additions & 12 deletions cmd/cosign/cli/fulcio/fulcio.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import (
"crypto/sha256"
"crypto/x509"
_ "embed" // To enable the `go:embed` directive.
"encoding/base64"
"encoding/json"
"encoding/pem"
"fmt"
"io/ioutil"
Expand All @@ -34,6 +36,10 @@ import (
"github.com/go-openapi/runtime"
httptransport "github.com/go-openapi/runtime/client"
"github.com/go-openapi/strfmt"
ct "github.com/google/certificate-transparency-go"
"github.com/google/certificate-transparency-go/ctutil"
ctx509 "github.com/google/certificate-transparency-go/x509"
"github.com/google/certificate-transparency-go/x509util"
"github.com/pkg/errors"
"golang.org/x/term"

Expand All @@ -53,11 +59,26 @@ const (
altRoot = "SIGSTORE_ROOT_FILE"
)

type Resp struct {
CertPEM []byte
ChainPEM []byte
SCT []byte
}

// This is the root in the fulcio project.
//go:embed fulcio.pem
var rootPem string

// This is the public key for the fulcio CT log
//go:embed ct.pub
var ctPublicKey string
var fulcioTargetStr = `fulcio.crt.pem`

var (
// For testing
VerifySCT = verifySCT
)

type oidcConnector interface {
OIDConnect(string, string, string) (*oauthflow.OIDCIDToken, error)
}
Expand All @@ -74,22 +95,22 @@ type signingCertProvider interface {
SigningCert(params *operations.SigningCertParams, authInfo runtime.ClientAuthInfoWriter, opts ...operations.ClientOption) (*operations.SigningCertCreated, error)
}

func getCertForOauthID(priv *ecdsa.PrivateKey, scp signingCertProvider, connector oidcConnector, oidcIssuer string, oidcClientID string) (certPem, chainPem []byte, err error) {
func getCertForOauthID(priv *ecdsa.PrivateKey, scp signingCertProvider, connector oidcConnector, oidcIssuer string, oidcClientID string) (Resp, error) {
pubBytes, err := x509.MarshalPKIXPublicKey(&priv.PublicKey)
if err != nil {
return nil, nil, err
return Resp{}, err
}

tok, err := connector.OIDConnect(oidcIssuer, oidcClientID, "")
if err != nil {
return nil, nil, err
return Resp{}, err
}

// Sign the email address as part of the request
h := sha256.Sum256([]byte(tok.Subject))
proof, err := ecdsa.SignASN1(rand.Reader, priv, h[:])
if err != nil {
return nil, nil, err
return Resp{}, err
}

bearerAuth := httptransport.BearerToken(tok.RawString)
Expand All @@ -109,17 +130,48 @@ func getCertForOauthID(priv *ecdsa.PrivateKey, scp signingCertProvider, connecto

resp, err := scp.SigningCert(params, bearerAuth)
if err != nil {
return nil, nil, err
return Resp{}, err
}
sct, err := base64.StdEncoding.DecodeString(resp.SCT.String())
if err != nil {
return Resp{}, err
}

// split the cert and the chain
certBlock, chainPem := pem.Decode([]byte(resp.Payload))
certPem = pem.EncodeToMemory(certBlock)
return certPem, chainPem, nil
certPem := pem.EncodeToMemory(certBlock)
fr := Resp{
CertPEM: certPem,
ChainPEM: chainPem,
SCT: sct,
}

// verify the sct
if err := VerifySCT(fr); err != nil {
return Resp{}, errors.Wrap(err, "verifying SCT")
}
fmt.Println("Successfully validated SCT...")
return fr, nil
}

func verifySCT(fr Resp) error {
pubKey, err := cosign.PemToECDSAKey([]byte(ctPublicKey))
if err != nil {
return err
}
cert, err := x509util.CertificateFromPEM(fr.CertPEM)
if err != nil {
return err
}
var sct ct.SignedCertificateTimestamp
if err := json.Unmarshal(fr.SCT, &sct); err != nil {
return errors.Wrap(err, "unmarshal")
}
return ctutil.VerifySCT(pubKey, []*ctx509.Certificate{cert}, &sct, false)
}

// GetCert returns the PEM-encoded signature of the OIDC identity returned as part of an interactive oauth2 flow plus the PEM-encoded cert chain.
func GetCert(ctx context.Context, priv *ecdsa.PrivateKey, idToken, flow, oidcIssuer, oidcClientID string, fClient *fulcioClient.Fulcio) (certPemBytes, chainPemBytes []byte, err error) {
func GetCert(ctx context.Context, priv *ecdsa.PrivateKey, idToken, flow, oidcIssuer, oidcClientID string, fClient *fulcioClient.Fulcio) (Resp, error) {
c := &realConnector{}
switch flow {
case FlowDevice:
Expand All @@ -130,7 +182,7 @@ func GetCert(ctx context.Context, priv *ecdsa.PrivateKey, idToken, flow, oidcIss
case FlowToken:
c.flow = &oauthflow.StaticTokenGetter{RawToken: idToken}
default:
return nil, nil, fmt.Errorf("unsupported oauth flow: %s", flow)
return Resp{}, fmt.Errorf("unsupported oauth flow: %s", flow)
}

return getCertForOauthID(priv, fClient.Operations, c, oidcIssuer, oidcClientID)
Expand All @@ -139,6 +191,7 @@ func GetCert(ctx context.Context, priv *ecdsa.PrivateKey, idToken, flow, oidcIss
type Signer struct {
Cert []byte
Chain []byte
SCT []byte
pub *ecdsa.PublicKey
*signature.ECDSASignerVerifier
}
Expand All @@ -164,15 +217,16 @@ func NewSigner(ctx context.Context, idToken, oidcIssuer, oidcClientID string, fC
default:
flow = FlowNormal
}
cert, chain, err := GetCert(ctx, priv, idToken, flow, oidcIssuer, oidcClientID, fClient) // TODO, use the chain.
Resp, err := GetCert(ctx, priv, idToken, flow, oidcIssuer, oidcClientID, fClient) // TODO, use the chain.
if err != nil {
return nil, errors.Wrap(err, "retrieving cert")
}
f := &Signer{
pub: &priv.PublicKey,
ECDSASignerVerifier: signer,
Cert: cert,
Chain: chain,
Cert: Resp.CertPEM,
Chain: Resp.ChainPEM,
SCT: Resp.SCT,
}
return f, nil

Expand Down
11 changes: 7 additions & 4 deletions cmd/cosign/cli/fulcio/fulcio_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,10 @@ func TestGetCertForOauthID(t *testing.T) {
err: tc.tokenGetterErr,
}

cert, chain, err := getCertForOauthID(testKey, tscp, &tf, "", "")
VerifySCT = func(Resp) error { return nil }
defer func() { VerifySCT = verifySCT }()

resp, err := getCertForOauthID(testKey, tscp, &tf, "", "")

if err != nil {
if !tc.expectErr {
Expand All @@ -122,16 +125,16 @@ func TestGetCertForOauthID(t *testing.T) {
return
}
if tc.expectErr {
t.Fatalf("getCertForOauthID got: %q, %q wanted error", cert, chain)
t.Fatalf("getCertForOauthID got: %q, %q wanted error", resp.CertPEM, resp.ChainPEM)
}

expectedCert := string(expectedCertBytes)
actualCert := string(cert)
actualCert := string(resp.CertPEM)
if actualCert != expectedCert {
t.Errorf("getCertForOauthID returned cert %q, wanted %q", actualCert, expectedCert)
}
expectedChain := string(expectedExtraBytes)
actualChain := string(chain)
actualChain := string(resp.ChainPEM)
if actualChain != expectedChain {
t.Errorf("getCertForOauthID returned chain %q, wanted %q", actualChain, expectedChain)
}
Expand Down
76 changes: 67 additions & 9 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/sigstore/cosign
go 1.17

require (
cloud.google.com/go v0.90.0 // indirect
cloud.google.com/go v0.92.3 // indirect
cloud.google.com/go/storage v1.16.0
github.com/Azure/azure-sdk-for-go v55.8.0+incompatible // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
Expand Down Expand Up @@ -45,7 +45,7 @@ require (
github.com/go-logr/logr v0.4.0 // indirect
github.com/go-logr/zapr v0.4.0 // indirect
github.com/go-openapi/analysis v0.20.1 // indirect
github.com/go-openapi/errors v0.20.0 // indirect
github.com/go-openapi/errors v0.20.1 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.19.6 // indirect
github.com/go-openapi/loads v0.20.2 // indirect
Expand Down Expand Up @@ -88,7 +88,6 @@ require (
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.11 // indirect
github.com/jstemmer/go-junit-report v0.9.1 // indirect
github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a // indirect
github.com/klauspost/compress v1.13.0 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
Expand Down Expand Up @@ -125,7 +124,7 @@ require (
github.com/sassoftware/relic v0.0.0-20210427151427-dfb082b79b74 // indirect
github.com/segmentio/ksuid v1.0.4 // indirect
github.com/shibumi/go-pathspec v1.2.0 // indirect
github.com/sigstore/fulcio v0.1.1
github.com/sigstore/fulcio v0.1.2-0.20210831152525-42f7422734bb
github.com/sigstore/rekor v0.3.0
github.com/sigstore/sigstore v0.0.0-20210729211320-56a91f560f44
github.com/sirupsen/logrus v1.8.1 // indirect
Expand All @@ -149,9 +148,8 @@ require (
go.uber.org/atomic v1.8.0 // indirect
go.uber.org/automaxprocs v1.4.0 // indirect
go.uber.org/multierr v1.7.0 // indirect
go.uber.org/zap v1.18.1 // indirect
go.uber.org/zap v1.19.0 // indirect
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect
golang.org/x/mod v0.4.2 // indirect
golang.org/x/net v0.0.0-20210614182718-04defd469f4e // indirect
golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a // indirect
Expand All @@ -160,12 +158,10 @@ require (
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b
golang.org/x/text v0.3.6 // indirect
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
golang.org/x/tools v0.1.5 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
google.golang.org/api v0.54.0
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67 // indirect
google.golang.org/genproto v0.0.0-20210813162853-db860fec028c // indirect
google.golang.org/grpc v1.39.1 // indirect
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
Expand All @@ -185,3 +181,65 @@ require (
sigs.k8s.io/structured-merge-diff/v4 v4.1.2 // indirect
sigs.k8s.io/yaml v1.2.0 // indirect
)

require (
cloud.google.com/go/kms v0.1.0 // indirect
cloud.google.com/go/security v0.1.0 // indirect
github.com/ThalesIgnite/crypto11 v1.2.4 // indirect
github.com/bgentry/speakeasy v0.1.0 // indirect
github.com/census-instrumentation/opencensus-proto v0.3.0 // indirect
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403 // indirect
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed // indirect
github.com/coreos/go-semver v0.3.0 // indirect
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
github.com/docker/go-units v0.4.0 // indirect
github.com/dustin/go-humanize v1.0.0 // indirect
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0 // indirect
github.com/envoyproxy/protoc-gen-validate v0.3.0-java // indirect
github.com/fullstorydev/grpcurl v1.8.1 // indirect
github.com/golang/glog v0.0.0-20210429001901-424d2337a529 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/google/btree v1.0.1 // indirect
github.com/google/certificate-transparency-go v1.1.2-0.20210512142713-bed466244fa6 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
github.com/jhump/protoreflect v1.8.2 // indirect
github.com/jonboulle/clockwork v0.2.2 // indirect
github.com/miekg/pkcs11 v1.0.3 // indirect
github.com/rs/cors v1.8.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/soheilhy/cmux v0.1.5 // indirect
github.com/thales-e-security/pool v0.0.2 // indirect
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 // indirect
github.com/urfave/cli v1.22.4 // indirect
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect
go.etcd.io/bbolt v1.3.6 // indirect
go.etcd.io/etcd/api/v3 v3.5.0 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.5.0 // indirect
go.etcd.io/etcd/client/v2 v2.305.0 // indirect
go.etcd.io/etcd/client/v3 v3.5.0 // indirect
go.etcd.io/etcd/etcdctl/v3 v3.5.0 // indirect
go.etcd.io/etcd/etcdutl/v3 v3.5.0 // indirect
go.etcd.io/etcd/pkg/v3 v3.5.0 // indirect
go.etcd.io/etcd/raft/v3 v3.5.0 // indirect
go.etcd.io/etcd/server/v3 v3.5.0 // indirect
go.etcd.io/etcd/tests/v3 v3.5.0 // indirect
go.etcd.io/etcd/v3 v3.5.0 // indirect
go.opentelemetry.io/contrib v0.20.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0 // indirect
go.opentelemetry.io/otel v0.20.0 // indirect
go.opentelemetry.io/otel/exporters/otlp v0.20.0 // indirect
go.opentelemetry.io/otel/metric v0.20.0 // indirect
go.opentelemetry.io/otel/sdk v0.20.0 // indirect
go.opentelemetry.io/otel/sdk/export/metric v0.20.0 // indirect
go.opentelemetry.io/otel/sdk/metric v0.20.0 // indirect
go.opentelemetry.io/otel/trace v0.20.0 // indirect
go.opentelemetry.io/proto/otlp v0.7.0 // indirect
golang.org/x/tools v0.1.5 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
gopkg.in/cheggaaa/pb.v1 v1.0.28 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
)
Loading

0 comments on commit 435f59f

Please sign in to comment.