Skip to content

Commit 37e45ae

Browse files
Refactor SelectService to return Service rather than URL, add supported API versions (#503)
Each signing service implementation sets a global variable for the list of URLs supported. This avoids clients that use the signing APIs needing to know which set of service API versions are supported when selecting services. This also refactors ServiceService(s) to return the Service struct itself rather than just a URL, since a signer will need to know the API version when initializing its client, like for Rekor. Also removed unnecessary nolints after un-deprecating TrustedRoot's LogId. Signed-off-by: Hayden B <[email protected]>
1 parent 3bd742e commit 37e45ae

File tree

9 files changed

+54
-58
lines changed

9 files changed

+54
-58
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@
88
/examples/oci-image-verification/oci-image-verification
99
/examples/sigstore-go-signing/sigstore-go-signing
1010
/examples/sigstore-go-verification/sigstore-go-verification
11+
/.vscode

examples/sigstore-go-signing/main.go

Lines changed: 12 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -145,12 +145,12 @@ func main() {
145145
}
146146

147147
if *idToken != "" {
148-
fulcioURL, err := root.SelectService(signingConfig.FulcioCertificateAuthorityURLs(), []uint32{1}, time.Now())
148+
fulcioService, err := root.SelectService(signingConfig.FulcioCertificateAuthorityURLs(), sign.FulcioAPIVersions, time.Now())
149149
if err != nil {
150150
log.Fatal(err)
151151
}
152152
fulcioOpts := &sign.FulcioOptions{
153-
BaseURL: fulcioURL,
153+
BaseURL: fulcioService.URL,
154154
Timeout: time.Duration(30 * time.Second),
155155
Retries: 1,
156156
}
@@ -180,14 +180,14 @@ func main() {
180180
}
181181

182182
if *tsa {
183-
tsaURLs, err := root.SelectServices(signingConfig.TimestampAuthorityURLs(),
184-
signingConfig.TimestampAuthorityURLsConfig(), []uint32{1}, time.Now())
183+
tsaServices, err := root.SelectServices(signingConfig.TimestampAuthorityURLs(),
184+
signingConfig.TimestampAuthorityURLsConfig(), sign.TimestampAuthorityAPIVersions, time.Now())
185185
if err != nil {
186186
log.Fatal(err)
187187
}
188-
for _, tsaURL := range tsaURLs {
188+
for _, tsaService := range tsaServices {
189189
tsaOpts := &sign.TimestampAuthorityOptions{
190-
URL: tsaURL,
190+
URL: tsaService.URL,
191191
Timeout: time.Duration(30 * time.Second),
192192
Retries: 1,
193193
}
@@ -196,31 +196,17 @@ func main() {
196196
}
197197

198198
if *rekor {
199-
rekorURLs, err := root.SelectServices(signingConfig.RekorLogURLs(),
200-
signingConfig.RekorLogURLsConfig(), []uint32{1}, time.Now())
199+
rekorServices, err := root.SelectServices(signingConfig.RekorLogURLs(),
200+
signingConfig.RekorLogURLsConfig(), sign.RekorAPIVersions, time.Now())
201201
if err != nil {
202-
log.Println("no Rekor logs found for version 1")
203-
}
204-
for _, rekorURL := range rekorURLs {
205-
rekorOpts := &sign.RekorOptions{
206-
BaseURL: rekorURL,
207-
Timeout: time.Duration(90 * time.Second),
208-
Retries: 1,
209-
Version: 1,
210-
}
211-
opts.TransparencyLogs = append(opts.TransparencyLogs, sign.NewRekor(rekorOpts))
212-
}
213-
rekorURLs, err = root.SelectServices(signingConfig.RekorLogURLs(),
214-
signingConfig.RekorLogURLsConfig(), []uint32{2}, time.Now())
215-
if err != nil {
216-
log.Println("no Rekor logs found for version 2")
202+
log.Fatal(err)
217203
}
218-
for _, rekorURL := range rekorURLs {
204+
for _, rekorService := range rekorServices {
219205
rekorOpts := &sign.RekorOptions{
220-
BaseURL: rekorURL,
206+
BaseURL: rekorService.URL,
221207
Timeout: time.Duration(90 * time.Second),
222208
Retries: 1,
223-
Version: 2,
209+
Version: rekorService.MajorAPIVersion,
224210
}
225211
opts.TransparencyLogs = append(opts.TransparencyLogs, sign.NewRekor(rekorOpts))
226212
}

pkg/root/signing_config.go

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,9 @@ func NewService(s *prototrustroot.Service) Service {
7373
// and current time. It will select the first service with the highest API version that matches
7474
// the criteria. Services should be sorted from newest to oldest validity period start time, to
7575
// minimize how far clients need to search to find a matching service.
76-
func SelectService(services []Service, supportedAPIVersions []uint32, currentTime time.Time) (string, error) {
76+
func SelectService(services []Service, supportedAPIVersions []uint32, currentTime time.Time) (Service, error) {
7777
if len(supportedAPIVersions) == 0 {
78-
return "", fmt.Errorf("no supported API versions")
78+
return Service{}, fmt.Errorf("no supported API versions")
7979
}
8080

8181
// Order supported versions from highest to lowest
@@ -95,12 +95,12 @@ func SelectService(services []Service, supportedAPIVersions []uint32, currentTim
9595
for _, version := range sortedVersions {
9696
for _, s := range sortedServices {
9797
if version == s.MajorAPIVersion && s.ValidAtTime(currentTime) {
98-
return s.URL, nil
98+
return s, nil
9999
}
100100
}
101101
}
102102

103-
return "", fmt.Errorf("no matching service found for API versions %v and current time %v", supportedAPIVersions, currentTime)
103+
return Service{}, fmt.Errorf("no matching service found for API versions %v and current time %v", supportedAPIVersions, currentTime)
104104
}
105105

106106
// SelectServices returns which service endpoints should be used based on supported API versions
@@ -110,7 +110,7 @@ func SelectService(services []Service, supportedAPIVersions []uint32, currentTim
110110
// It will select services from the highest supported API versions and will not select
111111
// services from different API versions. It will select distinct service operators, selecting
112112
// at most one service per operator.
113-
func SelectServices(services []Service, config ServiceConfiguration, supportedAPIVersions []uint32, currentTime time.Time) ([]string, error) {
113+
func SelectServices(services []Service, config ServiceConfiguration, supportedAPIVersions []uint32, currentTime time.Time) ([]Service, error) {
114114
if len(supportedAPIVersions) == 0 {
115115
return nil, fmt.Errorf("no supported API versions")
116116
}
@@ -130,36 +130,36 @@ func SelectServices(services []Service, config ServiceConfiguration, supportedAP
130130
slices.Reverse(sortedServices)
131131

132132
operators := make(map[string]bool)
133-
var urls []string
133+
var selectedServices []Service
134134
for _, version := range sortedVersions {
135135
for _, s := range sortedServices {
136136
if version == s.MajorAPIVersion && s.ValidAtTime(currentTime) {
137137
// Select the newest service for a given operator
138138
if !operators[s.Operator] {
139139
operators[s.Operator] = true
140-
urls = append(urls, s.URL)
140+
selectedServices = append(selectedServices, s)
141141
}
142142
}
143143
}
144144
// Exit once a list of services is found
145-
if len(urls) != 0 {
145+
if len(selectedServices) != 0 {
146146
break
147147
}
148148
}
149149

150-
if len(urls) == 0 {
150+
if len(selectedServices) == 0 {
151151
return nil, fmt.Errorf("no matching services found for API versions %v and current time %v", supportedAPIVersions, currentTime)
152152
}
153153

154154
// Select services from the highest supported API version
155155
switch config.Selector {
156156
case prototrustroot.ServiceSelector_ALL:
157-
return urls, nil
157+
return selectedServices, nil
158158
case prototrustroot.ServiceSelector_ANY:
159-
i := rand.Intn(len(urls)) // #nosec G404
160-
return []string{urls[i]}, nil
159+
i := rand.Intn(len(selectedServices)) // #nosec G404
160+
return []Service{selectedServices[i]}, nil
161161
case prototrustroot.ServiceSelector_EXACT:
162-
matchedUrls, err := selectExact(urls, config.Count)
162+
matchedUrls, err := selectExact(selectedServices, config.Count)
163163
if err != nil {
164164
return nil, err
165165
}

pkg/root/signing_config_test.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ func TestSelectService(t *testing.T) {
171171

172172
for _, tt := range tests {
173173
t.Run(tt.name, func(t *testing.T) {
174-
url, err := SelectService(tt.services, tt.supportedVersions, tt.currentTime)
174+
service, err := SelectService(tt.services, tt.supportedVersions, tt.currentTime)
175175
if (err != nil) != tt.expectedErr {
176176
t.Errorf("SelectService() error = %v, expectedErr %v", err, tt.expectedErr)
177177
return
@@ -180,8 +180,8 @@ func TestSelectService(t *testing.T) {
180180
if err.Error()[:len(tt.expectedErrMessage)] != tt.expectedErrMessage {
181181
t.Errorf("SelectService() error message = %v, expected %v", err.Error(), tt.expectedErrMessage)
182182
}
183-
} else if url != tt.expectedURL {
184-
t.Errorf("SelectService() got = %v, want %v", url, tt.expectedURL)
183+
} else if service.URL != tt.expectedURL {
184+
t.Errorf("SelectService() got = %v, want %v", service.URL, tt.expectedURL)
185185
}
186186
})
187187
}
@@ -481,11 +481,15 @@ func TestSelectServices(t *testing.T) {
481481

482482
for _, tt := range tests {
483483
t.Run(tt.name, func(t *testing.T) {
484-
urls, err := SelectServices(tt.services, tt.config, tt.supportedVersions, tt.currentTime)
484+
services, err := SelectServices(tt.services, tt.config, tt.supportedVersions, tt.currentTime)
485485
if (err != nil) != tt.expectedErr {
486486
t.Errorf("SelectServices() error = %v, expectedErr %v", err, tt.expectedErr)
487487
return
488488
}
489+
var urls []string
490+
for _, s := range services {
491+
urls = append(urls, s.URL)
492+
}
489493
if tt.expectedErr { //nolint:gocritic
490494
if err.Error()[:len(tt.expectedErrMessage)] != tt.expectedErrMessage {
491495
t.Errorf("SelectServices() error message = %v, expected %v", err.Error(), tt.expectedErrMessage)

pkg/root/trusted_root.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -121,14 +121,13 @@ func ParseTransparencyLogs(tlogs []*prototrustroot.TransparencyLogInstance) (tra
121121
if tlog.GetHashAlgorithm() != protocommon.HashAlgorithm_SHA2_256 {
122122
return nil, fmt.Errorf("unsupported tlog hash algorithm: %s", tlog.GetHashAlgorithm())
123123
}
124-
//nolint:staticcheck // Continuing to use log ID
125124
if tlog.GetLogId() == nil {
126125
return nil, fmt.Errorf("tlog missing log ID")
127126
}
128-
if tlog.GetLogId().GetKeyId() == nil { //nolint:staticcheck
127+
if tlog.GetLogId().GetKeyId() == nil {
129128
return nil, fmt.Errorf("tlog missing log ID key ID")
130129
}
131-
encodedKeyID := hex.EncodeToString(tlog.GetLogId().GetKeyId()) //nolint:staticcheck
130+
encodedKeyID := hex.EncodeToString(tlog.GetLogId().GetKeyId())
132131

133132
if tlog.GetPublicKey() == nil {
134133
return nil, fmt.Errorf("tlog missing public key")
@@ -147,7 +146,7 @@ func ParseTransparencyLogs(tlogs []*prototrustroot.TransparencyLogInstance) (tra
147146

148147
tlogEntry := &TransparencyLog{
149148
BaseURL: tlog.GetBaseUrl(),
150-
ID: tlog.GetLogId().GetKeyId(), //nolint:staticcheck
149+
ID: tlog.GetLogId().GetKeyId(),
151150
HashFunc: hashFunc,
152151
SignatureHashFunc: crypto.SHA256,
153152
}
@@ -186,7 +185,7 @@ func ParseTransparencyLogs(tlogs []*prototrustroot.TransparencyLogInstance) (tra
186185
return nil, fmt.Errorf("tlog public key is not RSA: %s", tlog.GetPublicKey().GetKeyDetails())
187186
}
188187
tlogEntry.PublicKey = rsaKey
189-
case protocommon.PublicKeyDetails_PKIX_ED25519: //nolint:staticcheck
188+
case protocommon.PublicKeyDetails_PKIX_ED25519:
190189
key, err := x509.ParsePKIXPublicKey(tlog.GetPublicKey().GetRawBytes())
191190
if err != nil {
192191
return nil, fmt.Errorf("failed to parse public key for tlog: %s %w",

pkg/sign/certificate.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ type FulcioOptions struct {
5757
Transport http.RoundTripper
5858
}
5959

60+
var FulcioAPIVersions = []uint32{1}
61+
6062
type fulcioCertRequest struct {
6163
PublicKeyRequest publicKeyRequest `json:"publicKeyRequest"`
6264
}

pkg/sign/timestamping.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ type TimestampAuthority struct {
4646
client *http.Client
4747
}
4848

49+
var TimestampAuthorityAPIVersions = []uint32{1}
50+
4951
func NewTimestampAuthority(opts *TimestampAuthorityOptions) *TimestampAuthority {
5052
ta := &TimestampAuthority{options: opts}
5153
ta.client = &http.Client{

pkg/sign/transparency.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ type RekorOptions struct {
7979
Version uint32
8080
}
8181

82+
var RekorAPIVersions = []uint32{1, 2}
83+
8284
func NewRekor(opts *RekorOptions) *Rekor {
8385
if opts.Version == 0 {
8486
opts.Version = rekorV1

test/e2e/e2e_test.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -133,27 +133,27 @@ func TestSignVerify(t *testing.T) {
133133
}
134134

135135
func signContent(signingConfig *root.SigningConfig, token string, content sign.Content, rekorVersion uint32, opts sign.BundleOptions) (*protobundle.Bundle, error) {
136-
rekorURLs, err := root.SelectServices(signingConfig.RekorLogURLs(), signingConfig.RekorLogURLsConfig(), []uint32{rekorVersion}, time.Now())
136+
rekorServices, err := root.SelectServices(signingConfig.RekorLogURLs(), signingConfig.RekorLogURLsConfig(), []uint32{rekorVersion}, time.Now())
137137
if err != nil {
138138
return nil, err
139139
}
140-
for _, rekorURL := range rekorURLs {
141-
log.Printf("using Rekor URL %s", rekorURL)
140+
for _, rekorService := range rekorServices {
141+
log.Printf("using Rekor URL %s", rekorService.URL)
142142
rekorOpts := &sign.RekorOptions{
143-
BaseURL: rekorURL,
143+
BaseURL: rekorService.URL,
144144
Timeout: time.Duration(90 * time.Second),
145145
Retries: 1,
146-
Version: rekorVersion,
146+
Version: rekorService.MajorAPIVersion,
147147
}
148148
opts.TransparencyLogs = append(opts.TransparencyLogs, sign.NewRekor(rekorOpts))
149149
}
150150

151-
fulcioURL, err := root.SelectService(signingConfig.FulcioCertificateAuthorityURLs(), []uint32{1}, time.Now())
151+
fulcioService, err := root.SelectService(signingConfig.FulcioCertificateAuthorityURLs(), []uint32{1}, time.Now())
152152
if err != nil {
153153
return nil, err
154154
}
155155
fulcioOpts := &sign.FulcioOptions{
156-
BaseURL: fulcioURL,
156+
BaseURL: fulcioService.URL,
157157
Timeout: time.Duration(30 * time.Second),
158158
Retries: 1,
159159
}
@@ -162,13 +162,13 @@ func signContent(signingConfig *root.SigningConfig, token string, content sign.C
162162
IDToken: token,
163163
}
164164

165-
tsaURLs, err := root.SelectServices(signingConfig.TimestampAuthorityURLs(), signingConfig.TimestampAuthorityURLsConfig(), []uint32{1}, time.Now())
165+
tsaServices, err := root.SelectServices(signingConfig.TimestampAuthorityURLs(), signingConfig.TimestampAuthorityURLsConfig(), []uint32{1}, time.Now())
166166
if err != nil {
167167
return nil, err
168168
}
169-
for _, tsaURL := range tsaURLs {
169+
for _, tsaService := range tsaServices {
170170
tsaOpts := &sign.TimestampAuthorityOptions{
171-
URL: tsaURL,
171+
URL: tsaService.URL,
172172
Timeout: time.Duration(30 * time.Second),
173173
Retries: 1,
174174
}

0 commit comments

Comments
 (0)