Skip to content

Commit c31e028

Browse files
committed
Add agile client signing algorithms support
Signed-off-by: Riccardo Schirone <[email protected]>
1 parent 6e8d290 commit c31e028

File tree

6 files changed

+159
-5
lines changed

6 files changed

+159
-5
lines changed

cmd/rekor-server/app/root.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,15 @@ import (
2020
"net/http"
2121
"net/http/pprof"
2222
"os"
23+
"sort"
24+
"strings"
2325
"time"
2426

2527
"github.com/go-chi/chi/middleware"
2628
homedir "github.com/mitchellh/go-homedir"
29+
"github.com/sigstore/rekor/pkg/api"
2730
"github.com/sigstore/rekor/pkg/log"
31+
"github.com/sigstore/sigstore/pkg/signature"
2832
"github.com/spf13/cobra"
2933
"github.com/spf13/viper"
3034

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

148+
keyAlgorithmTypes := []string{}
149+
for _, keyAlgorithm := range api.AllowedClientSigningAlgorithms {
150+
keyFlag, err := signature.FormatSignatureAlgorithmFlag(keyAlgorithm)
151+
if err != nil {
152+
panic(err)
153+
}
154+
keyAlgorithmTypes = append(keyAlgorithmTypes, keyFlag)
155+
}
156+
sort.Strings(keyAlgorithmTypes)
157+
keyAlgorithmHelp := fmt.Sprintf("signing algorithm to use for signing/hashing (allowed %s)", strings.Join(keyAlgorithmTypes, ", "))
158+
rootCmd.PersistentFlags().StringSlice("client-signing-algorithms", keyAlgorithmTypes, keyAlgorithmHelp)
159+
144160
if err := viper.BindPFlags(rootCmd.PersistentFlags()); err != nil {
145161
log.Logger.Fatal(err)
146162
}

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,3 +222,5 @@ require (
222222
gopkg.in/yaml.v2 v2.4.0
223223
gopkg.in/yaml.v3 v3.0.1 // indirect
224224
)
225+
226+
replace github.com/sigstore/sigstore => github.com/trail-of-forks/sigstore v0.0.0-20250129154658-d51867d5be49

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -413,8 +413,6 @@ github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh
413413
github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE=
414414
github.com/sigstore/protobuf-specs v0.3.3 h1:RMZQgXTD/pF7KW6b5NaRLYxFYZ/wzx44PQFXN2PEo5g=
415415
github.com/sigstore/protobuf-specs v0.3.3/go.mod h1:vIhZ6Uor1a38+wvRrKcqL2PtYNlgoIW9lhzYzkyy4EU=
416-
github.com/sigstore/sigstore v1.8.12 h1:S8xMVZbE2z9ZBuQUEG737pxdLjnbOIcFi5v9UFfkJFc=
417-
github.com/sigstore/sigstore v1.8.12/go.mod h1:+PYQAa8rfw0QdPpBcT+Gl3egKD9c+TUgAlF12H3Nmjo=
418416
github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.12 h1:EC3UmIaa7nV9sCgSpVevmvgvTYTkMqyrRbj5ojPp7tE=
419417
github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.12/go.mod h1:aw60vs3crnQdM/DYH+yF2P0MVKtItwAX34nuaMrY7Lk=
420418
github.com/sigstore/sigstore/pkg/signature/kms/azure v1.8.12 h1:FPpliDTywSy0woLHMAdmTSZ5IS/lVBZ0dY0I+2HmnSY=
@@ -463,6 +461,8 @@ github.com/tink-crypto/tink-go/v2 v2.3.0 h1:4/TA0lw0lA/iVKBL9f8R5eP7397bfc4antAM
463461
github.com/tink-crypto/tink-go/v2 v2.3.0/go.mod h1:kfPOtXIadHlekBTeBtJrHWqoGL+Fm3JQg0wtltPuxLU=
464462
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 h1:e/5i7d4oYZ+C1wj2THlRK+oAhjeS/TRQwMfkIuet3w0=
465463
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399/go.mod h1:LdwHTNJT99C5fTAzDz0ud328OgXz+gierycbcIx2fRs=
464+
github.com/trail-of-forks/sigstore v0.0.0-20250129154658-d51867d5be49 h1:SMp4GM0QEmE50JorRByHxTCvX6HyxelazUXbTKyYYsY=
465+
github.com/trail-of-forks/sigstore v0.0.0-20250129154658-d51867d5be49/go.mod h1:Uqpph/R+dKBUYE1FgpD1CVjkSSorfm4/KT1CAtjBtR8=
466466
github.com/transparency-dev/merkle v0.0.2 h1:Q9nBoQcZcgPamMkGn7ghV8XiTZ/kRxn1yCG81+twTK4=
467467
github.com/transparency-dev/merkle v0.0.2/go.mod h1:pqSy+OXefQ1EDUVmAJ8MUhHB9TXGuzVAT58PqBoHz1A=
468468
github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=

pkg/api/api.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import (
3434
"google.golang.org/grpc/credentials"
3535
"google.golang.org/grpc/credentials/insecure"
3636

37+
v1 "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1"
3738
"github.com/sigstore/rekor/pkg/indexstorage"
3839
"github.com/sigstore/rekor/pkg/log"
3940
"github.com/sigstore/rekor/pkg/pubsub"
@@ -43,6 +44,7 @@ import (
4344
"github.com/sigstore/rekor/pkg/trillianclient"
4445
"github.com/sigstore/rekor/pkg/util"
4546
"github.com/sigstore/rekor/pkg/witness"
47+
"github.com/sigstore/sigstore/pkg/signature"
4648

4749
_ "github.com/sigstore/rekor/pkg/pubsub/gcp" // Load GCP pubsub implementation
4850
)
@@ -98,13 +100,26 @@ type API struct {
98100
// Publishes notifications when new entries are added to the log. May be
99101
// nil if no publisher is configured.
100102
newEntryPublisher pubsub.Publisher
103+
algorithmRegistry *signature.AlgorithmRegistryConfig
101104
// Stores map of inactive tree IDs to checkpoints
102105
// Inactive shards will always return the same checkpoint,
103106
// so we can fetch the checkpoint on service startup to
104107
// minimize signature generations
105108
cachedCheckpoints map[int64]string
106109
}
107110

111+
var AllowedClientSigningAlgorithms = []v1.PublicKeyDetails{
112+
v1.PublicKeyDetails_PKIX_RSA_PKCS1V15_2048_SHA256,
113+
v1.PublicKeyDetails_PKIX_RSA_PKCS1V15_3072_SHA256,
114+
v1.PublicKeyDetails_PKIX_RSA_PKCS1V15_4096_SHA256,
115+
v1.PublicKeyDetails_PKIX_ECDSA_P256_SHA_256,
116+
v1.PublicKeyDetails_PKIX_ECDSA_P384_SHA_384,
117+
v1.PublicKeyDetails_PKIX_ECDSA_P521_SHA_512,
118+
v1.PublicKeyDetails_PKIX_ED25519,
119+
v1.PublicKeyDetails_PKIX_ED25519_PH,
120+
}
121+
var DefaultClientSigningAlgorithms = AllowedClientSigningAlgorithms
122+
108123
func NewAPI(treeID uint) (*API, error) {
109124
logRPCServer := fmt.Sprintf("%s:%d",
110125
viper.GetString("trillian_log_server.address"),
@@ -128,6 +143,32 @@ func NewAPI(treeID uint) (*API, error) {
128143
}
129144
log.Logger.Infof("Starting Rekor server with active tree %v", tid)
130145

146+
algorithmsOption := viper.GetStringSlice("client-signing-algorithms")
147+
var algorithms []v1.PublicKeyDetails
148+
if algorithmsOption == nil {
149+
algorithms = DefaultClientSigningAlgorithms
150+
} else {
151+
for _, a := range algorithmsOption {
152+
algorithm, err := signature.ParseSignatureAlgorithmFlag(a)
153+
if err != nil {
154+
return nil, fmt.Errorf("parsing signature algorithm flag: %w", err)
155+
}
156+
algorithms = append(algorithms, algorithm)
157+
}
158+
}
159+
algorithmsStr := make([]string, len(algorithms))
160+
for i, a := range algorithms {
161+
algorithmsStr[i], err = signature.FormatSignatureAlgorithmFlag(a)
162+
if err != nil {
163+
return nil, fmt.Errorf("formatting signature algorithm flag: %w", err)
164+
}
165+
}
166+
algorithmRegistry, err := signature.NewAlgorithmRegistryConfig(algorithms)
167+
if err != nil {
168+
return nil, fmt.Errorf("getting algorithm registry: %w", err)
169+
}
170+
log.Logger.Infof("Allowed client signing algorithms: %v", algorithmsStr)
171+
131172
shardingConfig := viper.GetString("trillian_log_server.sharding_config")
132173
signingConfig := signer.SigningConfig{
133174
SigningSchemeOrKeyPath: viper.GetString("rekor_server.signer"),
@@ -179,6 +220,7 @@ func NewAPI(treeID uint) (*API, error) {
179220
logRanges: ranges,
180221
// Utility functionality not required for operation of the core service
181222
newEntryPublisher: newEntryPublisher,
223+
algorithmRegistry: algorithmRegistry,
182224
cachedCheckpoints: cachedCheckpoints,
183225
}, nil
184226
}

pkg/api/entries.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,18 @@ package api
1818
import (
1919
"bytes"
2020
"context"
21+
"crypto"
22+
"crypto/ecdsa"
23+
"crypto/ed25519"
24+
"crypto/rsa"
25+
"crypto/x509"
2126
"encoding/hex"
2227
"errors"
2328
"fmt"
2429
"net/http"
2530
"net/url"
2631
"strconv"
32+
"strings"
2733
"time"
2834

2935
"github.com/cyberphone/json-canonicalization/go/src/webpki.org/jsoncanonicalizer"
@@ -192,12 +198,82 @@ func GetLogEntryByIndexHandler(params entries.GetLogEntryByIndexParams) middlewa
192198
return entries.NewGetLogEntryByIndexOK().WithPayload(logEntry)
193199
}
194200

201+
func checkEntryAlgorithms(entry types.EntryImpl) (bool, error) {
202+
verifiers, err := entry.Verifiers()
203+
if err != nil {
204+
return false, fmt.Errorf("getting verifiers: %w", err)
205+
}
206+
// Default to SHA256 if no artifact hash is specified
207+
artifactHashValue := crypto.SHA256
208+
// Get artifact hash from entry
209+
artifactHash, err := entry.ArtifactHash()
210+
if err == nil {
211+
var artifactHashAlgorithm string
212+
algoPosition := strings.Index(artifactHash, ":")
213+
if algoPosition != -1 {
214+
artifactHashAlgorithm = artifactHash[:algoPosition]
215+
}
216+
switch artifactHashAlgorithm {
217+
case "sha256":
218+
artifactHashValue = crypto.SHA256
219+
case "sha384":
220+
artifactHashValue = crypto.SHA384
221+
case "sha512":
222+
artifactHashValue = crypto.SHA512
223+
default:
224+
artifactHashValue = crypto.SHA256
225+
}
226+
}
227+
228+
// Check if all the verifiers public keys (together with the ArtifactHash)
229+
// are allowed according to the policy
230+
for _, v := range verifiers {
231+
identities, err := v.Identities()
232+
if err != nil {
233+
return false, fmt.Errorf("getting identities: %w", err)
234+
}
235+
236+
for _, identity := range identities {
237+
var publicKey crypto.PublicKey
238+
switch identityCrypto := identity.Crypto.(type) {
239+
case *x509.Certificate:
240+
publicKey = identityCrypto.PublicKey
241+
case *rsa.PublicKey:
242+
publicKey = identityCrypto
243+
case *ecdsa.PublicKey:
244+
publicKey = identityCrypto
245+
case ed25519.PublicKey:
246+
publicKey = identityCrypto
247+
default:
248+
continue
249+
}
250+
isPermitted, err := api.algorithmRegistry.IsAlgorithmPermitted(publicKey, artifactHashValue)
251+
if err != nil {
252+
return false, fmt.Errorf("checking if algorithm is permitted: %w", err)
253+
}
254+
if !isPermitted {
255+
return false, nil
256+
}
257+
}
258+
}
259+
return true, nil
260+
}
261+
195262
func createLogEntry(params entries.CreateLogEntryParams) (models.LogEntry, middleware.Responder) {
196263
ctx := params.HTTPRequest.Context()
197264
entry, err := types.CreateVersionedEntry(params.ProposedEntry)
198265
if err != nil {
199266
return nil, handleRekorAPIError(params, http.StatusBadRequest, err, fmt.Sprintf(validationError, err))
200267
}
268+
269+
areEntryAlgorithmsAllowed, err := checkEntryAlgorithms(entry)
270+
if err != nil {
271+
return nil, handleRekorAPIError(params, http.StatusBadRequest, err, fmt.Sprintf(validationError, err))
272+
}
273+
if !areEntryAlgorithmsAllowed {
274+
return nil, handleRekorAPIError(params, http.StatusBadRequest, errors.New("entry algorithms are not allowed"), fmt.Sprintf(validationError, "entry algorithms are not allowed"))
275+
}
276+
201277
leaf, err := types.CanonicalizeEntry(ctx, entry)
202278
if err != nil {
203279
var validationErr *types.InputValidationError

pkg/types/jar/v0.0.1/entry.go

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -323,12 +323,30 @@ func (v *V001Entry) CreateFromArtifactProperties(ctx context.Context, props type
323323
}
324324
re.JARModel.Archive.Content = (strfmt.Base64)(artifactBytes)
325325

326-
if err := re.validate(); err != nil {
326+
keyObj, sigObj, err := re.fetchExternalEntities(ctx)
327+
if err != nil {
328+
return nil, fmt.Errorf("error retrieving external entities: %v", err)
329+
}
330+
331+
// need to canonicalize key content
332+
keyContent, err := keyObj.CanonicalValue()
333+
if err != nil {
334+
return nil, err
335+
}
336+
sigContent, err := sigObj.CanonicalValue()
337+
if err != nil {
327338
return nil, err
328339
}
329340

330-
if _, _, err := re.fetchExternalEntities(ctx); err != nil {
331-
return nil, fmt.Errorf("error retrieving external entities: %w", err)
341+
re.JARModel.Signature = &models.JarV001SchemaSignature{
342+
PublicKey: &models.JarV001SchemaSignaturePublicKey{
343+
Content: (*strfmt.Base64)(&keyContent),
344+
},
345+
Content: sigContent,
346+
}
347+
348+
if err := re.validate(); err != nil {
349+
return nil, err
332350
}
333351

334352
returnVal.APIVersion = swag.String(re.APIVersion())

0 commit comments

Comments
 (0)