Skip to content

Commit

Permalink
Use principal in CA abstraction
Browse files Browse the repository at this point in the history
Modifies the CA abstraction to consume identity.Principal instead of
challenges.ChallengeResult. Also implements the indentity.Principal
methods for ChallengeResult.

Signed-off-by: Nathan Smith <nathan@chainguard.dev>
  • Loading branch information
Nathan Smith committed May 12, 2022
1 parent 8d307b5 commit 380c64c
Show file tree
Hide file tree
Showing 11 changed files with 290 additions and 310 deletions.
4 changes: 2 additions & 2 deletions pkg/api/grpc_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ func (g *grpcCAServer) CreateSigningCertificate(ctx context.Context, request *fu
// For CAs that do not support embedded SCTs or if the CT log is not configured
if sctCa, ok := g.ca.(certauth.EmbeddedSCTCA); !ok || g.ct == nil {
// currently configured CA doesn't support pre-certificate flow required to embed SCT in final certificate
csc, err = g.ca.CreateCertificate(ctx, subject)
csc, err = g.ca.CreateCertificate(ctx, subject, publicKey)
if err != nil {
// if the error was due to invalid input in the request, return HTTP 400
if _, ok := err.(certauth.ValidationError); ok {
Expand Down Expand Up @@ -165,7 +165,7 @@ func (g *grpcCAServer) CreateSigningCertificate(ctx context.Context, request *fu
result.GetSignedCertificateDetachedSct().SignedCertificateTimestamp = sctBytes
}
} else {
precert, err := sctCa.CreatePrecertificate(ctx, subject)
precert, err := sctCa.CreatePrecertificate(ctx, subject, publicKey)
if err != nil {
// if the error was due to invalid input in the request, return HTTP 400
if _, ok := err.(certauth.ValidationError); ok {
Expand Down
5 changes: 3 additions & 2 deletions pkg/api/grpc_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package api

import (
"context"
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
Expand All @@ -42,9 +43,9 @@ import (
"github.com/google/certificate-transparency-go/jsonclient"
"github.com/sigstore/fulcio/pkg/ca"
"github.com/sigstore/fulcio/pkg/ca/ephemeralca"
"github.com/sigstore/fulcio/pkg/challenges"
"github.com/sigstore/fulcio/pkg/config"
"github.com/sigstore/fulcio/pkg/generated/protobuf"
"github.com/sigstore/fulcio/pkg/identity"
"github.com/sigstore/sigstore/pkg/cryptoutils"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
Expand Down Expand Up @@ -1250,7 +1251,7 @@ func verifyResponse(resp *protobuf.SigningCertificate, eca *ephemeralca.Ephemera
type FailingCertificateAuthority struct {
}

func (fca *FailingCertificateAuthority) CreateCertificate(ctx context.Context, challenge *challenges.ChallengeResult) (*ca.CodeSigningCertificate, error) {
func (fca *FailingCertificateAuthority) CreateCertificate(context.Context, identity.Principal, crypto.PublicKey) (*ca.CodeSigningCertificate, error) {
return nil, errors.New("CreateCertificate always fails for testing")
}
func (fca *FailingCertificateAuthority) Root(ctx context.Context) ([]byte, error) {
Expand Down
5 changes: 3 additions & 2 deletions pkg/ca/ca.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@ package ca

import (
"context"
"crypto"

"github.com/sigstore/fulcio/pkg/challenges"
"github.com/sigstore/fulcio/pkg/identity"
)

// CertificateAuthority implements certificate creation with a detached SCT and
// fetching the CA trust bundle.
type CertificateAuthority interface {
CreateCertificate(ctx context.Context, challenge *challenges.ChallengeResult) (*CodeSigningCertificate, error)
CreateCertificate(context.Context, identity.Principal, crypto.PublicKey) (*CodeSigningCertificate, error)
Root(ctx context.Context) ([]byte, error)
}
5 changes: 3 additions & 2 deletions pkg/ca/embeddedca.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@ package ca

import (
"context"
"crypto"

ct "github.com/google/certificate-transparency-go"
"github.com/sigstore/fulcio/pkg/challenges"
"github.com/sigstore/fulcio/pkg/identity"
)

// EmbeddedSCTCA implements precertificate and certificate issuance. Certificates will contain an embedded SCT.
type EmbeddedSCTCA interface {
CreatePrecertificate(ctx context.Context, challenge *challenges.ChallengeResult) (*CodeSigningPreCertificate, error)
CreatePrecertificate(context.Context, identity.Principal, crypto.PublicKey) (*CodeSigningPreCertificate, error)
IssueFinalCertificate(ctx context.Context, precert *CodeSigningPreCertificate, sct *ct.SignedCertificateTimestamp) (*CodeSigningCertificate, error)
}
9 changes: 5 additions & 4 deletions pkg/ca/googleca/v1/googleca.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package v1

import (
"context"
"crypto"
"crypto/ecdsa"
"crypto/rsa"
"crypto/x509"
Expand All @@ -30,7 +31,7 @@ import (
privateca "cloud.google.com/go/security/privateca/apiv1"
"github.com/sigstore/fulcio/pkg/ca"
"github.com/sigstore/fulcio/pkg/ca/x509ca"
"github.com/sigstore/fulcio/pkg/challenges"
"github.com/sigstore/fulcio/pkg/identity"
"github.com/sigstore/sigstore/pkg/cryptoutils"
"google.golang.org/api/iterator"
privatecapb "google.golang.org/genproto/googleapis/cloud/security/privateca/v1"
Expand Down Expand Up @@ -160,13 +161,13 @@ func (c *CertAuthorityService) Root(ctx context.Context) ([]byte, error) {
return c.cachedRoots, nil
}

func (c *CertAuthorityService) CreateCertificate(ctx context.Context, subj *challenges.ChallengeResult) (*ca.CodeSigningCertificate, error) {
cert, err := x509ca.MakeX509(subj)
func (c *CertAuthorityService) CreateCertificate(ctx context.Context, principal identity.Principal, publicKey crypto.PublicKey) (*ca.CodeSigningCertificate, error) {
cert, err := x509ca.MakeX509(ctx, principal, publicKey)
if err != nil {
return nil, ca.ValidationError(err)
}

pubKeyBytes, err := cryptoutils.MarshalPublicKeyToPEM(subj.PublicKey)
pubKeyBytes, err := cryptoutils.MarshalPublicKeyToPEM(publicKey)
if err != nil {
return nil, ca.ValidationError(err)
}
Expand Down
14 changes: 7 additions & 7 deletions pkg/ca/intermediateca/intermediateca.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import (
ctx509 "github.com/google/certificate-transparency-go/x509"
"github.com/sigstore/fulcio/pkg/ca"
"github.com/sigstore/fulcio/pkg/ca/x509ca"
"github.com/sigstore/fulcio/pkg/challenges"
"github.com/sigstore/fulcio/pkg/identity"
"github.com/sigstore/sigstore/pkg/cryptoutils"
)

Expand All @@ -49,8 +49,8 @@ type IntermediateCA struct {
Signer crypto.Signer
}

func (ica *IntermediateCA) CreatePrecertificate(ctx context.Context, challenge *challenges.ChallengeResult) (*ca.CodeSigningPreCertificate, error) {
cert, err := x509ca.MakeX509(challenge)
func (ica *IntermediateCA) CreatePrecertificate(ctx context.Context, principal identity.Principal, publicKey crypto.PublicKey) (*ca.CodeSigningPreCertificate, error) {
cert, err := x509ca.MakeX509(ctx, principal, publicKey)
if err != nil {
return nil, err
}
Expand All @@ -64,7 +64,7 @@ func (ica *IntermediateCA) CreatePrecertificate(ctx context.Context, challenge *
Value: asn1.NullBytes,
})

finalCertBytes, err := x509.CreateCertificate(rand.Reader, cert, certChain[0], challenge.PublicKey, privateKey)
finalCertBytes, err := x509.CreateCertificate(rand.Reader, cert, certChain[0], publicKey, privateKey)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -130,15 +130,15 @@ func (ica *IntermediateCA) IssueFinalCertificate(ctx context.Context, precert *c
return ca.CreateCSCFromDER(finalCertBytes, precert.CertChain)
}

func (ica *IntermediateCA) CreateCertificate(ctx context.Context, challenge *challenges.ChallengeResult) (*ca.CodeSigningCertificate, error) {
cert, err := x509ca.MakeX509(challenge)
func (ica *IntermediateCA) CreateCertificate(ctx context.Context, principal identity.Principal, publicKey crypto.PublicKey) (*ca.CodeSigningCertificate, error) {
cert, err := x509ca.MakeX509(ctx, principal, publicKey)
if err != nil {
return nil, err
}

certChain, privateKey := ica.getX509KeyPair()

finalCertBytes, err := x509.CreateCertificate(rand.Reader, cert, certChain[0], challenge.PublicKey, privateKey)
finalCertBytes, err := x509.CreateCertificate(rand.Reader, cert, certChain[0], publicKey, privateKey)
if err != nil {
return nil, err
}
Expand Down
9 changes: 4 additions & 5 deletions pkg/ca/intermediateca/intermediateca_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,11 +165,10 @@ func TestCreatePrecertificateAndIssueFinalCertificate(t *testing.T) {

ica := IntermediateCA{Certs: certChain, Signer: subKey}
precsc, err := ica.CreatePrecertificate(context.TODO(), &challenges.ChallengeResult{
Issuer: "iss",
TypeVal: challenges.EmailValue,
Value: "foo@example.com",
PublicKey: priv.Public(),
})
Issuer: "iss",
TypeVal: challenges.EmailValue,
Value: "foo@example.com",
}, priv.Public())

if err != nil {
t.Fatalf("error generating precertificate: %v", err)
Expand Down
76 changes: 8 additions & 68 deletions pkg/ca/x509ca/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,10 @@ import (
"crypto/rand"
"crypto/x509"
"encoding/pem"
"net/url"
"time"

"github.com/pkg/errors"
"github.com/sigstore/fulcio/pkg/ca"
"github.com/sigstore/fulcio/pkg/challenges"
"github.com/sigstore/fulcio/pkg/identity"
"github.com/sigstore/sigstore/pkg/cryptoutils"
)

Expand All @@ -35,13 +33,13 @@ type X509CA struct {
PrivKey crypto.Signer
}

func MakeX509(subject *challenges.ChallengeResult) (*x509.Certificate, error) {
func MakeX509(ctx context.Context, principal identity.Principal, publicKey crypto.PublicKey) (*x509.Certificate, error) {
serialNumber, err := cryptoutils.GenerateSerialNumber()
if err != nil {
return nil, err
}

skid, err := cryptoutils.SKID(subject.PublicKey)
skid, err := cryptoutils.SKID(publicKey)
if err != nil {
return nil, err
}
Expand All @@ -55,67 +53,9 @@ func MakeX509(subject *challenges.ChallengeResult) (*x509.Certificate, error) {
KeyUsage: x509.KeyUsageDigitalSignature,
}

switch subject.TypeVal {
case challenges.EmailValue:
cert.EmailAddresses = []string{subject.Value}
case challenges.SpiffeValue:
challengeURL, err := url.Parse(subject.Value)
if err != nil {
return nil, ca.ValidationError(err)
}
cert.URIs = []*url.URL{challengeURL}
case challenges.GithubWorkflowValue:
jobWorkflowURI, err := url.Parse(subject.Value)
if err != nil {
return nil, ca.ValidationError(err)
}
cert.URIs = []*url.URL{jobWorkflowURI}
case challenges.KubernetesValue:
k8sURI, err := url.Parse(subject.Value)
if err != nil {
return nil, ca.ValidationError(err)
}
cert.URIs = []*url.URL{k8sURI}
case challenges.URIValue:
subjectURI, err := url.Parse(subject.Value)
if err != nil {
return nil, ca.ValidationError(err)
}
cert.URIs = []*url.URL{subjectURI}
case challenges.UsernameValue:
cert.EmailAddresses = []string{subject.Value}
}

exts := Extensions{
Issuer: subject.Issuer,
}
if subject.TypeVal == challenges.GithubWorkflowValue {
var ok bool
exts.GithubWorkflowTrigger, ok = subject.AdditionalInfo[challenges.GithubWorkflowTrigger]
if !ok {
return nil, errors.New("x509ca: github workflow missing trigger claim")
}
exts.GithubWorkflowSHA, ok = subject.AdditionalInfo[challenges.GithubWorkflowSha]
if !ok {
return nil, errors.New("x509ca: github workflow missing SHA claim")
}
exts.GithubWorkflowName, ok = subject.AdditionalInfo[challenges.GithubWorkflowName]
if !ok {
return nil, errors.New("x509ca: github workflow missing workflow name claim")
}
exts.GithubWorkflowRepository, ok = subject.AdditionalInfo[challenges.GithubWorkflowRepository]
if !ok {
return nil, errors.New("x509ca: github workflow missing repository claim")
}
exts.GithubWorkflowRef, ok = subject.AdditionalInfo[challenges.GithubWorkflowRef]
if !ok {
return nil, errors.New("x509ca: github workflow missing ref claim")
}
}

cert.ExtraExtensions, err = exts.Render()
err = principal.Embed(ctx, cert)
if err != nil {
return nil, err
return nil, ca.ValidationError(err)
}

return cert, nil
Expand All @@ -128,13 +68,13 @@ func (x *X509CA) Root(ctx context.Context) ([]byte, error) {
}), nil
}

func (x *X509CA) CreateCertificate(_ context.Context, subject *challenges.ChallengeResult) (*ca.CodeSigningCertificate, error) {
cert, err := MakeX509(subject)
func (x *X509CA) CreateCertificate(ctx context.Context, principal identity.Principal, publicKey crypto.PublicKey) (*ca.CodeSigningCertificate, error) {
cert, err := MakeX509(ctx, principal, publicKey)
if err != nil {
return nil, err
}

finalCertBytes, err := x509.CreateCertificate(rand.Reader, cert, x.RootCA, subject.PublicKey, x.PrivKey)
finalCertBytes, err := x509.CreateCertificate(rand.Reader, cert, x.RootCA, publicKey, x.PrivKey)
if err != nil {
return nil, err
}
Expand Down

0 comments on commit 380c64c

Please sign in to comment.