Skip to content

Commit faae13b

Browse files
authored
Added --client-signing-algorithms flag (#1974)
* Add agile client signing algorithms support Signed-off-by: Riccardo Schirone <[email protected]> * pkg/api: refactor code and restrict checks to hashedrekord Signed-off-by: Riccardo Schirone <[email protected]> * remove jar changes Signed-off-by: Riccardo Schirone <[email protected]> * update go.mod Signed-off-by: Riccardo Schirone <[email protected]> * Add e2e tests for client-signing-algorithms server flag Signed-off-by: Riccardo Schirone <[email protected]> --------- Signed-off-by: Riccardo Schirone <[email protected]>
1 parent 3503130 commit faae13b

File tree

20 files changed

+473
-3
lines changed

20 files changed

+473
-3
lines changed

.github/workflows/main.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,31 @@ jobs:
227227
name: Docker Compose logs
228228
path: /tmp/*docker-compose.log
229229

230+
client-algorithms-e2e:
231+
runs-on: ubuntu-latest
232+
needs: build
233+
234+
steps:
235+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
236+
with:
237+
persist-credentials: false
238+
- name: Docker Build
239+
run: docker compose build
240+
- name: Extract version of Go to use
241+
run: echo "GOVERSION=$(awk -F'[:@]' '/FROM golang/{print $2; exit}' Dockerfile)" >> $GITHUB_ENV
242+
- uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
243+
with:
244+
go-version: ${{ env.GOVERSION }}
245+
246+
- name: Test for supported client algorithms
247+
run: ./tests/client-algos-e2e-test.sh
248+
- name: Upload logs if they exist
249+
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
250+
if: failure()
251+
with:
252+
name: Docker Compose logs
253+
path: /tmp/*docker-compose.log
254+
230255
harness:
231256
runs-on: ubuntu-latest
232257
needs: build

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: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ require (
2828
github.com/rs/cors v1.11.1
2929
github.com/sassoftware/relic v7.2.1+incompatible
3030
github.com/secure-systems-lab/go-securesystemslib v0.9.0
31-
github.com/sigstore/sigstore v1.8.12
31+
github.com/sigstore/sigstore v1.8.13-0.20250204232249-4b62818325b7
3232
github.com/spf13/cobra v1.8.1
3333
github.com/spf13/pflag v1.0.6
3434
github.com/spf13/viper v1.19.0

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -413,8 +413,8 @@ 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.4.0 h1:yoZbdh0kZYKOSiVbYyA8J3f2wLh5aUk2SQB7LgAfIdU=
415415
github.com/sigstore/protobuf-specs v0.4.0/go.mod h1:FKW5NYhnnFQ/Vb9RKtQk91iYd0MKJ9AxyqInEwU6+OI=
416-
github.com/sigstore/sigstore v1.8.12 h1:S8xMVZbE2z9ZBuQUEG737pxdLjnbOIcFi5v9UFfkJFc=
417-
github.com/sigstore/sigstore v1.8.12/go.mod h1:+PYQAa8rfw0QdPpBcT+Gl3egKD9c+TUgAlF12H3Nmjo=
416+
github.com/sigstore/sigstore v1.8.13-0.20250204232249-4b62818325b7 h1:dKgngAj90pDWGKB/yBt/AmSvNFzLOPvYGfEgEKRQWc4=
417+
github.com/sigstore/sigstore v1.8.13-0.20250204232249-4b62818325b7/go.mod h1:2lXojNsjZjkqu1//FWxq7qUcPB8Lq1KsR5hc+GkcC/4=
418418
github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.12 h1:EC3UmIaa7nV9sCgSpVevmvgvTYTkMqyrRbj5ojPp7tE=
419419
github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.12/go.mod h1:aw60vs3crnQdM/DYH+yF2P0MVKtItwAX34nuaMrY7Lk=
420420
github.com/sigstore/sigstore/pkg/signature/kms/azure v1.8.12 h1:FPpliDTywSy0woLHMAdmTSZ5IS/lVBZ0dY0I+2HmnSY=

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: 97 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"
@@ -43,11 +49,13 @@ import (
4349
"github.com/sigstore/rekor/pkg/generated/models"
4450
"github.com/sigstore/rekor/pkg/generated/restapi/operations/entries"
4551
"github.com/sigstore/rekor/pkg/log"
52+
"github.com/sigstore/rekor/pkg/pki/identity"
4653
"github.com/sigstore/rekor/pkg/pubsub"
4754
"github.com/sigstore/rekor/pkg/sharding"
4855
"github.com/sigstore/rekor/pkg/tle"
4956
"github.com/sigstore/rekor/pkg/trillianclient"
5057
"github.com/sigstore/rekor/pkg/types"
58+
hashedrekord "github.com/sigstore/rekor/pkg/types/hashedrekord/v0.0.1"
5159
"github.com/sigstore/rekor/pkg/util"
5260
"github.com/sigstore/sigstore/pkg/signature"
5361
"github.com/sigstore/sigstore/pkg/signature/options"
@@ -192,12 +200,101 @@ func GetLogEntryByIndexHandler(params entries.GetLogEntryByIndexParams) middlewa
192200
return entries.NewGetLogEntryByIndexOK().WithPayload(logEntry)
193201
}
194202

203+
func getArtifactHashValue(entry types.EntryImpl) crypto.Hash {
204+
artifactHash, err := entry.ArtifactHash()
205+
if err != nil {
206+
// Default to SHA256 if no artifact hash is specified
207+
return crypto.SHA256
208+
}
209+
210+
var artifactHashAlgorithm string
211+
algoPosition := strings.Index(artifactHash, ":")
212+
if algoPosition != -1 {
213+
artifactHashAlgorithm = artifactHash[:algoPosition]
214+
}
215+
switch artifactHashAlgorithm {
216+
case "sha256":
217+
return crypto.SHA256
218+
case "sha384":
219+
return crypto.SHA384
220+
case "sha512":
221+
return crypto.SHA512
222+
default:
223+
return crypto.SHA256
224+
}
225+
}
226+
227+
func getPublicKey(identity identity.Identity) (crypto.PublicKey, error) {
228+
switch identityCrypto := identity.Crypto.(type) {
229+
case *x509.Certificate:
230+
return identityCrypto.PublicKey, nil
231+
case *rsa.PublicKey:
232+
return identityCrypto, nil
233+
case *ecdsa.PublicKey:
234+
return identityCrypto, nil
235+
case ed25519.PublicKey:
236+
return identityCrypto, nil
237+
default:
238+
return nil, fmt.Errorf("unsupported public key type: %T", identityCrypto)
239+
}
240+
}
241+
242+
func checkEntryAlgorithms(entry types.EntryImpl) (bool, error) {
243+
// Only check algorithms for hashedrekord entries
244+
switch entry.(type) {
245+
case *hashedrekord.V001Entry:
246+
break
247+
default:
248+
return true, nil
249+
}
250+
251+
verifiers, err := entry.Verifiers()
252+
if err != nil {
253+
return false, err
254+
}
255+
256+
artifactHashValue := getArtifactHashValue(entry)
257+
258+
// Check if all the verifiers public keys (together with the
259+
// artifactHashValue) are allowed according to the policy
260+
for _, v := range verifiers {
261+
identities, err := v.Identities()
262+
if err != nil {
263+
return false, err
264+
}
265+
266+
for _, identity := range identities {
267+
publicKey, err := getPublicKey(identity)
268+
if err != nil {
269+
return false, err
270+
}
271+
isPermitted, err := api.algorithmRegistry.IsAlgorithmPermitted(publicKey, artifactHashValue)
272+
if err != nil {
273+
return false, fmt.Errorf("checking if algorithm is permitted: %w", err)
274+
}
275+
if !isPermitted {
276+
return false, nil
277+
}
278+
}
279+
}
280+
return true, nil
281+
}
282+
195283
func createLogEntry(params entries.CreateLogEntryParams) (models.LogEntry, middleware.Responder) {
196284
ctx := params.HTTPRequest.Context()
197285
entry, err := types.CreateVersionedEntry(params.ProposedEntry)
198286
if err != nil {
199287
return nil, handleRekorAPIError(params, http.StatusBadRequest, err, fmt.Sprintf(validationError, err))
200288
}
289+
290+
areEntryAlgorithmsAllowed, err := checkEntryAlgorithms(entry)
291+
if err != nil {
292+
return nil, handleRekorAPIError(params, http.StatusBadRequest, err, fmt.Sprintf(validationError, err))
293+
}
294+
if !areEntryAlgorithmsAllowed {
295+
return nil, handleRekorAPIError(params, http.StatusBadRequest, errors.New("entry algorithms are not allowed"), fmt.Sprintf(validationError, "entry algorithms are not allowed"))
296+
}
297+
201298
leaf, err := types.CanonicalizeEntry(ctx, entry)
202299
if err != nil {
203300
var validationErr *types.InputValidationError

0 commit comments

Comments
 (0)