From bffe52e67d942354d9709aa262a626896d2c016d Mon Sep 17 00:00:00 2001 From: Riccardo Schirone Date: Tue, 23 Jan 2024 15:20:46 +0100 Subject: [PATCH] Added --client-signing-algorithms flag Signed-off-by: Riccardo Schirone --- cmd/rekor-server/app/root.go | 16 ++++++++ go.mod | 8 ++-- go.sum | 12 +++--- pkg/api/api.go | 41 +++++++++++++++++++ pkg/api/entries.go | 77 ++++++++++++++++++++++++++++++++++++ 5 files changed, 145 insertions(+), 9 deletions(-) diff --git a/cmd/rekor-server/app/root.go b/cmd/rekor-server/app/root.go index 7e6bbb584..0f165a941 100644 --- a/cmd/rekor-server/app/root.go +++ b/cmd/rekor-server/app/root.go @@ -20,10 +20,14 @@ import ( "net/http" "net/http/pprof" "os" + "sort" + "strings" "time" homedir "github.com/mitchellh/go-homedir" + "github.com/sigstore/rekor/pkg/api" "github.com/sigstore/rekor/pkg/log" + "github.com/sigstore/sigstore/pkg/signature" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -131,6 +135,18 @@ Memory and file-based signers should only be used for testing.`) rootCmd.PersistentFlags().Int("search_index.mysql.max_open_connections", 0, "maximum open connections") rootCmd.PersistentFlags().Int("search_index.mysql.max_idle_connections", 0, "maximum idle connections") + keyAlgorithmTypes := []string{} + for _, keyAlgorithm := range api.AllowedClientSigningAlgorithms { + keyFlag, err := signature.FormatSignatureAlgorithmFlag(keyAlgorithm) + if err != nil { + panic(err) + } + keyAlgorithmTypes = append(keyAlgorithmTypes, keyFlag) + } + sort.Strings(keyAlgorithmTypes) + keyAlgorithmHelp := fmt.Sprintf("signing algorithm to use for signing/hashing (allowed %s)", strings.Join(keyAlgorithmTypes, ", ")) + rootCmd.PersistentFlags().StringSlice("client-signing-algorithms", keyAlgorithmTypes, keyAlgorithmHelp) + if err := viper.BindPFlags(rootCmd.PersistentFlags()); err != nil { log.Logger.Fatal(err) } diff --git a/go.mod b/go.mod index 4b337816d..dd9948b04 100644 --- a/go.mod +++ b/go.mod @@ -26,7 +26,6 @@ require ( github.com/rs/cors v1.10.1 github.com/sassoftware/relic v7.2.1+incompatible github.com/secure-systems-lab/go-securesystemslib v0.8.0 - github.com/sigstore/sigstore v1.8.1 github.com/spf13/cobra v1.8.0 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.18.2 @@ -63,7 +62,8 @@ require ( github.com/jmoiron/sqlx v1.3.5 github.com/redis/go-redis/v9 v9.4.0 github.com/sassoftware/relic/v7 v7.6.1 - github.com/sigstore/protobuf-specs v0.2.1 + github.com/sigstore/protobuf-specs v0.3.0-beta.2 + github.com/sigstore/sigstore v1.6.4 github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.1 github.com/sigstore/sigstore/pkg/signature/kms/azure v1.8.1 github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.8.1 @@ -164,7 +164,7 @@ require ( github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect - github.com/google/go-containerregistry v0.17.0 // indirect + github.com/google/go-containerregistry v0.18.0 // indirect github.com/google/uuid v1.5.0 // indirect github.com/google/wire v0.5.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect @@ -204,3 +204,5 @@ require ( gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 // indirect ) + +replace github.com/sigstore/sigstore => github.com/trail-of-forks/sigstore v0.0.0-20240129055912-d89eade746d5 diff --git a/go.sum b/go.sum index 9ad5cad47..f1a69bc96 100644 --- a/go.sum +++ b/go.sum @@ -215,8 +215,8 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-containerregistry v0.17.0 h1:5p+zYs/R4VGHkhyvgWurWrpJ2hW4Vv9fQI+GzdcwXLk= -github.com/google/go-containerregistry v0.17.0/go.mod h1:u0qB2l7mvtWVR5kNcbFIhFY1hLbf8eeGapA+vbFDCtQ= +github.com/google/go-containerregistry v0.18.0 h1:ShE7erKNPqRh5ue6Z9DUOlk04WsnFWPO6YGr3OxnfoQ= +github.com/google/go-containerregistry v0.18.0/go.mod h1:u0qB2l7mvtWVR5kNcbFIhFY1hLbf8eeGapA+vbFDCtQ= github.com/google/go-replayers/grpcreplay v1.1.0 h1:S5+I3zYyZ+GQz68OfbURDdt/+cSMqCK1wrvNx7WBzTE= github.com/google/go-replayers/grpcreplay v1.1.0/go.mod h1:qzAvJ8/wi57zq7gWqaE6AwLM6miiXUQwP1S+I9icmhk= github.com/google/go-replayers/httpreplay v1.2.0 h1:VM1wEyyjaoU53BwrOnaf9VhAyQQEEioJvFYxYcLRKzk= @@ -387,10 +387,8 @@ github.com/secure-systems-lab/go-securesystemslib v0.8.0 h1:mr5An6X45Kb2nddcFlbm github.com/secure-systems-lab/go-securesystemslib v0.8.0/go.mod h1:UH2VZVuJfCYR8WgMlCU1uFsOUU+KeyrTWcSS73NBOzU= github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI= github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE= -github.com/sigstore/protobuf-specs v0.2.1 h1:KIoM7E3C4uaK092q8YoSj/XSf9720f8dlsbYwwOmgEA= -github.com/sigstore/protobuf-specs v0.2.1/go.mod h1:xPqQGnH/HllKuZ4VFPz/g+78epWM/NLRGl7Fuy45UdE= -github.com/sigstore/sigstore v1.8.1 h1:mAVposMb14oplk2h/bayPmIVdzbq2IhCgy4g6R0ZSjo= -github.com/sigstore/sigstore v1.8.1/go.mod h1:02SL1158BSj15bZyOFz7m+/nJzLZfFd9A8ab3Kz7w/E= +github.com/sigstore/protobuf-specs v0.3.0-beta.2 h1:neHS0O1z7qz4q21vyXqSaKuKYxA0upzJERT88NrgYlM= +github.com/sigstore/protobuf-specs v0.3.0-beta.2/go.mod h1:ynKzXpqr3dUj2Xk9O/5ZUhjnpi0F53DNi5AdH6pS3jc= github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.1 h1:rEDdUefulkIQaMJyzLwtgPDLNXBIltBABiFYfb0YmgQ= github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.1/go.mod h1:RCdYCc1IxCYWzh2IdzdA6Yf7JIY0cMRqH08fpQYechw= github.com/sigstore/sigstore/pkg/signature/kms/azure v1.8.1 h1:DvRWG99QGWZC5mp42SEde2Xke/Q384Idnj2da7yB+Mk= @@ -432,6 +430,8 @@ github.com/theupdateframework/go-tuf v0.7.0 h1:CqbQFrWo1ae3/I0UCblSbczevCCbS31Qv github.com/theupdateframework/go-tuf v0.7.0/go.mod h1:uEB7WSY+7ZIugK6R1hiBMBjQftaFzn7ZCDJcp1tCUug= github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 h1:e/5i7d4oYZ+C1wj2THlRK+oAhjeS/TRQwMfkIuet3w0= github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399/go.mod h1:LdwHTNJT99C5fTAzDz0ud328OgXz+gierycbcIx2fRs= +github.com/trail-of-forks/sigstore v0.0.0-20240129055912-d89eade746d5 h1:G0g5M0EnhCixAnBMMpilo6IlNDkK67BNStUUOt5/arc= +github.com/trail-of-forks/sigstore v0.0.0-20240129055912-d89eade746d5/go.mod h1:CO6jK1y+gQzIQBTV3/qZG7Nqem6Kxy+YQR4cOi2axLk= github.com/transparency-dev/merkle v0.0.2 h1:Q9nBoQcZcgPamMkGn7ghV8XiTZ/kRxn1yCG81+twTK4= github.com/transparency-dev/merkle v0.0.2/go.mod h1:pqSy+OXefQ1EDUVmAJ8MUhHB9TXGuzVAT58PqBoHz1A= github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= diff --git a/pkg/api/api.go b/pkg/api/api.go index 9eabb747c..5a95ed69e 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -30,6 +30,7 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" + v1 "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1" "github.com/sigstore/rekor/pkg/indexstorage" "github.com/sigstore/rekor/pkg/log" "github.com/sigstore/rekor/pkg/pubsub" @@ -70,8 +71,21 @@ type API struct { // Publishes notifications when new entries are added to the log. May be // nil if no publisher is configured. newEntryPublisher pubsub.Publisher + algorithmRegistry *signature.AlgorithmRegistryConfig } +var AllowedClientSigningAlgorithms = []v1.KnownSignatureAlgorithm{ + v1.KnownSignatureAlgorithm_RSA_SIGN_PKCS1_2048_SHA256, + v1.KnownSignatureAlgorithm_RSA_SIGN_PKCS1_3072_SHA256, + v1.KnownSignatureAlgorithm_RSA_SIGN_PKCS1_4096_SHA256, + v1.KnownSignatureAlgorithm_ECDSA_SHA2_256_NISTP256, + v1.KnownSignatureAlgorithm_ECDSA_SHA2_384_NISTP384, + v1.KnownSignatureAlgorithm_ECDSA_SHA2_512_NISTP521, + v1.KnownSignatureAlgorithm_ED25519, + v1.KnownSignatureAlgorithm_ED25519_PH, +} +var DefaultClientSigningAlgorithms = AllowedClientSigningAlgorithms + func NewAPI(treeID uint) (*API, error) { logRPCServer := fmt.Sprintf("%s:%d", viper.GetString("trillian_log_server.address"), @@ -102,6 +116,32 @@ func NewAPI(treeID uint) (*API, error) { log.Logger.Infof("Starting Rekor server with active tree %v", tid) ranges.SetActive(tid) + algorithmsOption := viper.GetStringSlice("client-signing-algorithms") + var algorithms []v1.KnownSignatureAlgorithm + if algorithmsOption == nil { + algorithms = DefaultClientSigningAlgorithms + } else { + for _, a := range algorithmsOption { + algorithm, err := signature.ParseSignatureAlgorithmFlag(a) + if err != nil { + return nil, fmt.Errorf("parsing signature algorithm flag: %w", err) + } + algorithms = append(algorithms, algorithm) + } + } + algorithmsStr := make([]string, len(algorithms)) + for i, a := range algorithms { + algorithmsStr[i], err = signature.FormatSignatureAlgorithmFlag(a) + if err != nil { + return nil, fmt.Errorf("formatting signature algorithm flag: %w", err) + } + } + algorithmRegistry, err := signature.NewAlgorithmRegistryConfig(algorithms) + if err != nil { + return nil, fmt.Errorf("getting algorithm registry: %w", err) + } + log.Logger.Infof("Allowed client signing algorithms: %v", algorithmsStr) + rekorSigner, err := signer.New(ctx, viper.GetString("rekor_server.signer"), viper.GetString("rekor_server.signer-passwd")) if err != nil { @@ -142,6 +182,7 @@ func NewAPI(treeID uint) (*API, error) { signer: rekorSigner, // Utility functionality not required for operation of the core service newEntryPublisher: newEntryPublisher, + algorithmRegistry: algorithmRegistry, }, nil } diff --git a/pkg/api/entries.go b/pkg/api/entries.go index 4efba7ba3..5792a132f 100644 --- a/pkg/api/entries.go +++ b/pkg/api/entries.go @@ -18,11 +18,17 @@ package api import ( "bytes" "context" + "crypto" + "crypto/ecdsa" + "crypto/ed25519" + "crypto/rsa" + "crypto/x509" "encoding/hex" "errors" "fmt" "net/http" "net/url" + "strings" "github.com/cyberphone/json-canonicalization/go/src/webpki.org/jsoncanonicalizer" "github.com/go-openapi/runtime" @@ -177,12 +183,83 @@ func GetLogEntryByIndexHandler(params entries.GetLogEntryByIndexParams) middlewa return entries.NewGetLogEntryByIndexOK().WithPayload(logEntry) } +func checkEntryAlgorithms(entry types.EntryImpl) (bool, error) { + verifiers, err := entry.Verifiers() + if err != nil { + return false, fmt.Errorf("getting verifiers: %w", err) + } + // Get artifact hash from entry + artifactHash, err := entry.ArtifactHash() + if err != nil { + return false, fmt.Errorf("getting artifact hash: %w", err) + } + + var artifactHashAlgorithm string + var artifactHashValue crypto.Hash + algoPosition := strings.Index(artifactHash, ":") + if algoPosition != -1 { + artifactHashAlgorithm = artifactHash[:algoPosition] + } + switch artifactHashAlgorithm { + case "sha256": + artifactHashValue = crypto.SHA256 + case "sha384": + artifactHashValue = crypto.SHA384 + case "sha512": + artifactHashValue = crypto.SHA512 + default: + artifactHashValue = crypto.SHA256 + } + + // Check if all the verifiers public keys (together with the ArtifactHash) + // are allowed according to the policy + for _, v := range verifiers { + identities, err := v.Identities() + if err != nil { + return false, fmt.Errorf("getting identities: %w", err) + } + + for _, identity := range identities { + var publicKey crypto.PublicKey + switch identityCrypto := identity.Crypto.(type) { + case *x509.Certificate: + publicKey = identityCrypto.PublicKey + case *rsa.PublicKey: + publicKey = identityCrypto + case *ecdsa.PublicKey: + publicKey = identityCrypto + case ed25519.PublicKey: + publicKey = identityCrypto + default: + continue + } + isPermitted, err := api.algorithmRegistry.IsAlgorithmPermitted(publicKey, artifactHashValue) + if err != nil { + return false, fmt.Errorf("checking if algorithm is permitted: %w", err) + } + if !isPermitted { + return false, nil + } + } + } + return true, nil +} + func createLogEntry(params entries.CreateLogEntryParams) (models.LogEntry, middleware.Responder) { ctx := params.HTTPRequest.Context() entry, err := types.CreateVersionedEntry(params.ProposedEntry) if err != nil { return nil, handleRekorAPIError(params, http.StatusBadRequest, err, fmt.Sprintf(validationError, err)) } + + areEntryAlgorithmsAllowed, err := checkEntryAlgorithms(entry) + if err != nil { + return nil, handleRekorAPIError(params, http.StatusBadRequest, err, fmt.Sprintf(validationError, err)) + } + if !areEntryAlgorithmsAllowed { + return nil, handleRekorAPIError(params, http.StatusBadRequest, errors.New("entry algorithms are not allowed"), fmt.Sprintf(validationError, "entry algorithms are not allowed")) + } + leaf, err := types.CanonicalizeEntry(ctx, entry) if err != nil { if _, ok := (err).(types.ValidationError); ok {