Skip to content

Commit

Permalink
Add agile client signing algorithms support
Browse files Browse the repository at this point in the history
Signed-off-by: Riccardo Schirone <[email protected]>
  • Loading branch information
ret2libc committed Jan 30, 2025
1 parent 6e8d290 commit c31e028
Show file tree
Hide file tree
Showing 6 changed files with 159 additions and 5 deletions.
16 changes: 16 additions & 0 deletions cmd/rekor-server/app/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,15 @@ import (
"net/http"
"net/http/pprof"
"os"
"sort"
"strings"
"time"

"github.com/go-chi/chi/middleware"
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"

Expand Down Expand Up @@ -141,6 +145,18 @@ Memory and file-based signers should only be used for testing.`)
rootCmd.PersistentFlags().String("http-request-id-header-name", middleware.RequestIDHeader, "name of HTTP Request Header to use as request correlation ID")
rootCmd.PersistentFlags().String("trace-string-prefix", "", "if set, this will be used to prefix the 'trace' field when outputting structured logs")

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)
}
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -222,3 +222,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-20250129154658-d51867d5be49
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -413,8 +413,6 @@ github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh
github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE=
github.com/sigstore/protobuf-specs v0.3.3 h1:RMZQgXTD/pF7KW6b5NaRLYxFYZ/wzx44PQFXN2PEo5g=
github.com/sigstore/protobuf-specs v0.3.3/go.mod h1:vIhZ6Uor1a38+wvRrKcqL2PtYNlgoIW9lhzYzkyy4EU=
github.com/sigstore/sigstore v1.8.12 h1:S8xMVZbE2z9ZBuQUEG737pxdLjnbOIcFi5v9UFfkJFc=
github.com/sigstore/sigstore v1.8.12/go.mod h1:+PYQAa8rfw0QdPpBcT+Gl3egKD9c+TUgAlF12H3Nmjo=
github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.12 h1:EC3UmIaa7nV9sCgSpVevmvgvTYTkMqyrRbj5ojPp7tE=
github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.12/go.mod h1:aw60vs3crnQdM/DYH+yF2P0MVKtItwAX34nuaMrY7Lk=
github.com/sigstore/sigstore/pkg/signature/kms/azure v1.8.12 h1:FPpliDTywSy0woLHMAdmTSZ5IS/lVBZ0dY0I+2HmnSY=
Expand Down Expand Up @@ -463,6 +461,8 @@ github.com/tink-crypto/tink-go/v2 v2.3.0 h1:4/TA0lw0lA/iVKBL9f8R5eP7397bfc4antAM
github.com/tink-crypto/tink-go/v2 v2.3.0/go.mod h1:kfPOtXIadHlekBTeBtJrHWqoGL+Fm3JQg0wtltPuxLU=
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-20250129154658-d51867d5be49 h1:SMp4GM0QEmE50JorRByHxTCvX6HyxelazUXbTKyYYsY=
github.com/trail-of-forks/sigstore v0.0.0-20250129154658-d51867d5be49/go.mod h1:Uqpph/R+dKBUYE1FgpD1CVjkSSorfm4/KT1CAtjBtR8=
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.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
Expand Down
42 changes: 42 additions & 0 deletions pkg/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
"google.golang.org/grpc/credentials"
"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"
Expand All @@ -43,6 +44,7 @@ import (
"github.com/sigstore/rekor/pkg/trillianclient"
"github.com/sigstore/rekor/pkg/util"
"github.com/sigstore/rekor/pkg/witness"
"github.com/sigstore/sigstore/pkg/signature"

_ "github.com/sigstore/rekor/pkg/pubsub/gcp" // Load GCP pubsub implementation
)
Expand Down Expand Up @@ -98,13 +100,26 @@ 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
// Stores map of inactive tree IDs to checkpoints
// Inactive shards will always return the same checkpoint,
// so we can fetch the checkpoint on service startup to
// minimize signature generations
cachedCheckpoints map[int64]string
}

var AllowedClientSigningAlgorithms = []v1.PublicKeyDetails{
v1.PublicKeyDetails_PKIX_RSA_PKCS1V15_2048_SHA256,
v1.PublicKeyDetails_PKIX_RSA_PKCS1V15_3072_SHA256,
v1.PublicKeyDetails_PKIX_RSA_PKCS1V15_4096_SHA256,
v1.PublicKeyDetails_PKIX_ECDSA_P256_SHA_256,
v1.PublicKeyDetails_PKIX_ECDSA_P384_SHA_384,
v1.PublicKeyDetails_PKIX_ECDSA_P521_SHA_512,
v1.PublicKeyDetails_PKIX_ED25519,
v1.PublicKeyDetails_PKIX_ED25519_PH,
}
var DefaultClientSigningAlgorithms = AllowedClientSigningAlgorithms

func NewAPI(treeID uint) (*API, error) {
logRPCServer := fmt.Sprintf("%s:%d",
viper.GetString("trillian_log_server.address"),
Expand All @@ -128,6 +143,32 @@ func NewAPI(treeID uint) (*API, error) {
}
log.Logger.Infof("Starting Rekor server with active tree %v", tid)

algorithmsOption := viper.GetStringSlice("client-signing-algorithms")
var algorithms []v1.PublicKeyDetails
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)

shardingConfig := viper.GetString("trillian_log_server.sharding_config")
signingConfig := signer.SigningConfig{
SigningSchemeOrKeyPath: viper.GetString("rekor_server.signer"),
Expand Down Expand Up @@ -179,6 +220,7 @@ func NewAPI(treeID uint) (*API, error) {
logRanges: ranges,
// Utility functionality not required for operation of the core service
newEntryPublisher: newEntryPublisher,
algorithmRegistry: algorithmRegistry,
cachedCheckpoints: cachedCheckpoints,
}, nil
}
Expand Down
76 changes: 76 additions & 0 deletions pkg/api/entries.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,18 @@ package api
import (
"bytes"
"context"
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa"
"crypto/x509"
"encoding/hex"
"errors"
"fmt"
"net/http"
"net/url"
"strconv"
"strings"
"time"

"github.com/cyberphone/json-canonicalization/go/src/webpki.org/jsoncanonicalizer"
Expand Down Expand Up @@ -192,12 +198,82 @@ 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)
}
// Default to SHA256 if no artifact hash is specified
artifactHashValue := crypto.SHA256
// Get artifact hash from entry
artifactHash, err := entry.ArtifactHash()
if err == nil {
var artifactHashAlgorithm string
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 {
var validationErr *types.InputValidationError
Expand Down
24 changes: 21 additions & 3 deletions pkg/types/jar/v0.0.1/entry.go
Original file line number Diff line number Diff line change
Expand Up @@ -323,12 +323,30 @@ func (v *V001Entry) CreateFromArtifactProperties(ctx context.Context, props type
}
re.JARModel.Archive.Content = (strfmt.Base64)(artifactBytes)

if err := re.validate(); err != nil {
keyObj, sigObj, err := re.fetchExternalEntities(ctx)
if err != nil {
return nil, fmt.Errorf("error retrieving external entities: %v", err)
}

// need to canonicalize key content
keyContent, err := keyObj.CanonicalValue()
if err != nil {
return nil, err
}
sigContent, err := sigObj.CanonicalValue()
if err != nil {
return nil, err
}

if _, _, err := re.fetchExternalEntities(ctx); err != nil {
return nil, fmt.Errorf("error retrieving external entities: %w", err)
re.JARModel.Signature = &models.JarV001SchemaSignature{
PublicKey: &models.JarV001SchemaSignaturePublicKey{
Content: (*strfmt.Base64)(&keyContent),
},
Content: sigContent,
}

if err := re.validate(); err != nil {
return nil, err
}

returnVal.APIVersion = swag.String(re.APIVersion())
Expand Down

0 comments on commit c31e028

Please sign in to comment.