Skip to content

Commit

Permalink
Use an in memory timestamping key (#402)
Browse files Browse the repository at this point in the history
* use an in memory timestamping key

Signed-off-by: Asra Ali <asraa@google.com>

* address comments

Signed-off-by: Asra Ali <asraa@google.com>
  • Loading branch information
asraa authored Jul 30, 2021
1 parent 71ed018 commit cfb395d
Show file tree
Hide file tree
Showing 9 changed files with 164 additions and 91 deletions.
2 changes: 1 addition & 1 deletion cmd/rekor-server/app/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func init() {
rootCmd.PersistentFlags().String("rekor_server.hostname", "rekor.sigstore.dev", "public hostname of instance")
rootCmd.PersistentFlags().String("rekor_server.address", "127.0.0.1", "Address to bind to")
rootCmd.PersistentFlags().String("rekor_server.signer", "memory", "Rekor signer to use. Current valid options include: [gcpkms, memory]")
rootCmd.PersistentFlags().String("rekor_server.timestamp_chain", "", "PEM encoded cert chain to use for timestamping")
rootCmd.PersistentFlags().String("rekor_server.timestamp_chain", "", "PEM encoded cert chain signing authorizing the signer to be a CA to sign a timestamping cert")

rootCmd.PersistentFlags().Uint16("port", 3000, "Port to bind to")

Expand Down
22 changes: 15 additions & 7 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/sigstore/rekor
go 1.16

require (
cloud.google.com/go v0.89.0 // indirect
cloud.google.com/go/storage v1.16.0 // indirect
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d
github.com/blang/semver v3.5.1+incompatible
Expand All @@ -12,7 +13,9 @@ require (
github.com/danieljoos/wincred v1.1.1 // indirect
github.com/ghodss/yaml v1.0.0
github.com/go-chi/chi v4.1.2+incompatible
github.com/go-openapi/analysis v0.20.1 // indirect
github.com/go-openapi/errors v0.20.0
github.com/go-openapi/jsonreference v0.19.6 // indirect
github.com/go-openapi/loads v0.20.2
github.com/go-openapi/runtime v0.19.29
github.com/go-openapi/spec v0.20.3
Expand All @@ -24,37 +27,42 @@ require (
github.com/google/go-cmp v0.5.6
github.com/google/rpmpack v0.0.0-20210518075352-dc539ef4f2ea
github.com/google/trillian v1.3.14-0.20210713114448-df474653733c
github.com/google/uuid v1.3.0 // indirect
github.com/in-toto/in-toto-golang v0.2.1-0.20210627200632-886210ae2ab9
github.com/jedisct1/go-minisign v0.0.0-20210703085342-c1f07ee84431
github.com/leodido/go-urn v1.2.1 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mediocregopher/radix/v4 v4.0.0-beta.1
github.com/mitchellh/go-homedir v1.1.0
github.com/mitchellh/mapstructure v1.4.1
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.11.0
github.com/prometheus/common v0.29.0 // indirect
github.com/prometheus/procfs v0.7.0 // indirect
github.com/prometheus/common v0.30.0 // indirect
github.com/prometheus/procfs v0.7.1 // indirect
github.com/rs/cors v1.8.0
github.com/sassoftware/relic v0.0.0-20210427151427-dfb082b79b74
github.com/sigstore/sigstore v0.0.0-20210713222344-1fee53516622
github.com/sigstore/sigstore v0.0.0-20210729211320-56a91f560f44
github.com/spf13/cast v1.4.0 // indirect
github.com/spf13/cobra v1.2.1
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.8.1
github.com/tidwall/pretty v1.2.0 // indirect
github.com/tilinna/clock v1.1.0 // indirect
github.com/ulikunitz/xz v0.5.10 // indirect
github.com/urfave/negroni v1.0.0
github.com/zalando/go-keyring v0.1.1 // indirect
go.uber.org/atomic v1.8.0 // indirect
go.mongodb.org/mongo-driver v1.7.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/goleak v1.1.10
go.uber.org/multierr v1.7.0 // indirect
go.uber.org/zap v1.18.1
gocloud.dev v0.23.0
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
golang.org/x/mod v0.4.2
golang.org/x/net v0.0.0-20210614182718-04defd469f4e
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
golang.org/x/tools v0.1.5 // indirect
google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a
google.golang.org/api v0.52.0 // indirect
google.golang.org/genproto v0.0.0-20210729151513-df9385d47c1b
google.golang.org/grpc v1.39.0
google.golang.org/protobuf v1.27.1
gopkg.in/ini.v1 v1.62.0
Expand Down
75 changes: 41 additions & 34 deletions go.sum

Large diffs are not rendered by default.

24 changes: 17 additions & 7 deletions pkg/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ type API struct {
pubkey string // PEM encoded public key
pubkeyHash string // SHA256 hash of DER-encoded public key
signer signature.Signer
tsaSigner signature.Signer // the signer to use for timestamping
certChain []*x509.Certificate // timestamping cert chain
certChainPem string // PEM encoded timestamping cert chain
verifier *client.LogVerifier
Expand Down Expand Up @@ -112,6 +113,16 @@ func NewAPI() (*API, error) {
return nil, errors.Wrap(err, "new verifier")
}

// Use an in-memory key for timestamping
tsaSigner, err := signer.New(ctx, signer.MemoryScheme)
if err != nil {
return nil, errors.Wrap(err, "getting new tsa signer")
}
tsaPk, err := tsaSigner.PublicKey(options.WithContext(ctx))
if err != nil {
return nil, errors.Wrap(err, "getting public key")
}

var certChain []*x509.Certificate
b64CertChainStr := viper.GetString("rekor_server.timestamp_chain")
if b64CertChainStr != "" {
Expand All @@ -122,15 +133,13 @@ func NewAPI() (*API, error) {
if certChain, err = pki.ParseTimestampCertChain([]byte(certChainStr)); err != nil {
return nil, errors.Wrap(err, "parsing timestamp cert chain")
}
} else if viper.GetString("rekor_server.signer") == signer.MemoryScheme {
// Generate a timestaming cert with a self signed CA if we are configured with an in-memory signer.
var err error
certChain, err = signer.NewTimestampingCertWithSelfSignedCA(pk)
if err != nil {
return nil, errors.Wrap(err, "generating timestaping cert chain")
}
}

// Generate a tsa certificate from the rekor signer and provided certificate chain
certChain, err = signer.NewTimestampingCertWithChain(ctx, tsaPk, rekorSigner, certChain)
if err != nil {
return nil, errors.Wrap(err, "generating timestamping cert chain")
}
certChainPem, err := pki.CertChainToPEM(certChain)
if err != nil {
return nil, errors.Wrap(err, "timestamping cert chain")
Expand All @@ -142,6 +151,7 @@ func NewAPI() (*API, error) {
pubkey: string(pubkey),
pubkeyHash: hex.EncodeToString(pubkeyHashBytes[:]),
signer: rekorSigner,
tsaSigner: tsaSigner,
certChain: certChain,
certChainPem: string(certChainPem),
verifier: verifier,
Expand Down
2 changes: 1 addition & 1 deletion pkg/api/timestamp.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import (
)

func RequestFromRekor(ctx context.Context, req pkcs9.TimeStampReq) ([]byte, error) {
resp, err := util.CreateRfc3161Response(ctx, req, api.certChain, api.signer)
resp, err := util.CreateRfc3161Response(ctx, req, api.certChain, api.tsaSigner)
if err != nil {
return nil, err
}
Expand Down
9 changes: 7 additions & 2 deletions pkg/pki/x509/x509_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,17 +205,22 @@ func TestNilCertChainToPEM(t *testing.T) {
}

func TestCertChain_Verify(t *testing.T) {
mem, err := signer.NewMemory()
caSigner, err := signer.NewMemory()
if err != nil {
t.Fatal(err)
}
// A properly created cert chain should encode to PEM OK.
ctx := context.Background()
mem, err := signer.NewMemory()
if err != nil {
t.Fatal(err)
}
pk, err := mem.PublicKey(options.WithContext(ctx))
if err != nil {
t.Fatal(err)
}
certChain, err := signer.NewTimestampingCertWithSelfSignedCA(pk)

certChain, err := signer.NewTimestampingCertWithChain(ctx, pk, caSigner, nil)
if err != nil {
t.Fatal(err)
}
Expand Down
98 changes: 66 additions & 32 deletions pkg/signer/memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ limitations under the License.
package signer

import (
"context"
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
Expand All @@ -30,6 +30,8 @@ import (

"github.com/pkg/errors"
"github.com/sigstore/sigstore/pkg/signature"
"github.com/sigstore/sigstore/pkg/signature/kms/gcp"
"github.com/sigstore/sigstore/pkg/signature/options"
)

const MemoryScheme = "memory"
Expand All @@ -39,33 +41,48 @@ type Memory struct {
signature.ECDSASignerVerifier
}

// create a self-signed CA and generate a timestamping certificate to rekor
func NewTimestampingCertWithSelfSignedCA(pub crypto.PublicKey) ([]*x509.Certificate, error) {
// generate self-signed CA
caPrivKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
// Generate a timestamping certificate for pub using the signer. The chain must verify the signer's public key if provided.
// Otherwise, a self-signed root CA will be generated.
func NewTimestampingCertWithChain(ctx context.Context, pub crypto.PublicKey, signer signature.Signer, chain []*x509.Certificate) ([]*x509.Certificate, error) {
// Get the signer's (rekor's) public key
signerPubKey, err := signer.PublicKey(options.WithContext(ctx))
if err != nil {
return nil, errors.Wrap(err, "generating private key")
return nil, err
}
ca := &x509.Certificate{
SerialNumber: big.NewInt(2019),
Subject: pkix.Name{
Organization: []string{"Root CA Test"},
Country: []string{"US"},
Province: []string{""},
Locality: []string{"San Francisco"},
StreetAddress: []string{"Golden Gate Bridge"},
PostalCode: []string{"94016"},
},
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(10, 0, 0),
IsCA: true,
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageContentCommitment | x509.KeyUsageCertSign,
BasicConstraintsValid: true,

// If the signer is not in-memory, retrieve the crypto.Signer
var cryptoSigner crypto.Signer
if s, ok := signer.(*gcp.SignerVerifier); ok {
if cryptoSigner, _, err = s.CryptoSigner(ctx, func(err error) {}); err != nil {
return nil, errors.Wrap(err, "getting kms signer")
}
} else {
cryptoSigner = signer.(crypto.Signer)
}
caBytes, err := x509.CreateCertificate(rand.Reader, ca, ca, &caPrivKey.PublicKey, caPrivKey)
if err != nil {
return nil, err

if len(chain) == 0 {
// Generate an in-memory self-signed root CA.
ca := &x509.Certificate{
SerialNumber: big.NewInt(2019),
Subject: pkix.Name{
Organization: []string{"rekor in-memory root CA"},
},
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(10, 0, 0),
IsCA: true,
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageContentCommitment | x509.KeyUsageCertSign,
BasicConstraintsValid: true,
}
caBytes, err := x509.CreateCertificate(rand.Reader, ca, ca, signerPubKey, cryptoSigner)
if err != nil {
return nil, errors.Wrap(err, "creating self-signed CA")
}
chain, err = x509.ParseCertificates(caBytes)
if err != nil {
return nil, err
}
}

timestampExt, err := asn1.Marshal([]asn1.ObjectIdentifier{{1, 3, 6, 1, 5, 5, 7, 3, 8}})
if err != nil {
return nil, err
Expand All @@ -74,12 +91,7 @@ func NewTimestampingCertWithSelfSignedCA(pub crypto.PublicKey) ([]*x509.Certific
cert := &x509.Certificate{
SerialNumber: big.NewInt(1658),
Subject: pkix.Name{
Organization: []string{"Rekor Test"},
Country: []string{"US"},
Province: []string{""},
Locality: []string{"San Francisco"},
StreetAddress: []string{"Golden Gate Bridge"},
PostalCode: []string{"94016"},
Organization: []string{"Rekor Timestamping Cert"},
},
IPAddresses: []net.IP{net.IPv4(127, 0, 0, 1), net.IPv6loopback},
NotBefore: time.Now(),
Expand All @@ -97,11 +109,33 @@ func NewTimestampingCertWithSelfSignedCA(pub crypto.PublicKey) ([]*x509.Certific
},
BasicConstraintsValid: true,
}
certBytes, err := x509.CreateCertificate(rand.Reader, cert, ca, pub.(*ecdsa.PublicKey), caPrivKey)

// Create the certificate
certBytes, err := x509.CreateCertificate(rand.Reader, cert, chain[0], pub, cryptoSigner)
if err != nil {
return nil, errors.Wrap(err, "creating tsa certificate")
}
tsaCert, err := x509.ParseCertificates(certBytes)
if err != nil {
return nil, err
}

// Verify and return the certificate chain
root := x509.NewCertPool()
root.AddCert(chain[len(chain)-1])
intermediates := x509.NewCertPool()
for _, intermediate := range chain[:len(chain)-1] {
intermediates.AddCert(intermediate)
}
verifyOptions := x509.VerifyOptions{
Roots: root,
Intermediates: intermediates,
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageTimeStamping},
}
if _, err = tsaCert[0].Verify(verifyOptions); err != nil {
return nil, err
}
return x509.ParseCertificates(append(certBytes, caBytes...))
return append(tsaCert, chain...), nil
}

func NewMemory() (*Memory, error) {
Expand Down
13 changes: 9 additions & 4 deletions pkg/signer/memory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,21 @@ func TestMemory(t *testing.T) {
if err != nil {
t.Fatalf("new memory: %v", err)
}
tsaKey, err := New(ctx, "memory")
if err != nil {
t.Fatalf("new memory: %v", err)
}

payload := []byte("payload")

// sign a payload
sig, err := m.SignMessage(bytes.NewReader(payload), options.WithContext(ctx))
// sign a payload with the tsa key
sig, err := tsaKey.SignMessage(bytes.NewReader(payload), options.WithContext(ctx))
if err != nil {
t.Fatalf("signing payload: %v", err)
}

// verify the signature against public key
pubKey, err := m.PublicKey(options.WithContext(ctx))
pubKey, err := tsaKey.PublicKey(options.WithContext(ctx))
if err != nil {
t.Fatalf("public key: %v", err)
}
Expand All @@ -58,7 +63,7 @@ func TestMemory(t *testing.T) {
}

// verify signature using the cert's public key
certChain, err := NewTimestampingCertWithSelfSignedCA(pubKey)
certChain, err := NewTimestampingCertWithChain(ctx, pubKey, m, nil)
if err != nil {
t.Fatalf("generating timestamping cert: %v", err)
}
Expand Down
10 changes: 7 additions & 3 deletions pkg/util/rfc3161_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,15 @@ func TestCreateRFC3161Response(t *testing.T) {
if err != nil {
t.Error(err)
}
pk, err := mem.PublicKey(options.WithContext(ctx))
tsa, err := signer.NewMemory()
if err != nil {
t.Error(err)
}
pk, err := tsa.PublicKey(options.WithContext(ctx))
if err != nil {
t.Fatal(err)
}
certChain, err := signer.NewTimestampingCertWithSelfSignedCA(pk)
certChain, err := signer.NewTimestampingCertWithChain(ctx, pk, mem, nil)
if err != nil {
t.Error(err)
}
Expand All @@ -154,7 +158,7 @@ func TestCreateRFC3161Response(t *testing.T) {
t.Error(err)
}

resp, err := CreateRfc3161Response(ctx, *req, certChain, mem)
resp, err := CreateRfc3161Response(ctx, *req, certChain, tsa)
if err != nil {
t.Error(err)
}
Expand Down

0 comments on commit cfb395d

Please sign in to comment.