Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Use an in memory timestamping key #402

Merged
merged 2 commits into from
Jul 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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