Skip to content

Commit ae37ebf

Browse files
committed
Wrap sigstore isntance in sigstore options
Signed-off-by: Adolfo García Veytia (Puerco) <[email protected]>
1 parent 050e440 commit ae37ebf

File tree

4 files changed

+94
-116
lines changed

4 files changed

+94
-116
lines changed

options/sigstore.go

Lines changed: 26 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -4,147 +4,60 @@
44
package options
55

66
import (
7-
"errors"
87
"fmt"
9-
"net/url"
108

119
"github.com/spf13/cobra"
1210

1311
"github.com/carabiner-dev/signer/internal/tuf"
12+
"github.com/carabiner-dev/signer/sigstore"
1413
)
1514

1615
// Sigstore options to control how signer handles signing with sigstore
1716
type Sigstore struct {
18-
// Embed the tuf options struct
19-
tuf.TufOptions
20-
21-
Timestamp bool
22-
23-
// AppendToRekor controls if the signing operation is recorded into the
24-
// transparency log.
25-
AppendToRekor bool `json:"rekor-append"`
26-
DisableSTS bool
27-
28-
// FulcioURL url of the Fulcio CA (defaults to the public good instance)
29-
FulcioURL string `json:"fulcio-url"`
30-
31-
// RekorURL url of the Rekor transparency log (defaults to the public good instance)
32-
RekorURL string `json:"rekor-url"`
33-
34-
// Hide the OIDC options in the CLI --help
35-
HideOIDCOptions bool
36-
// FlagPrefix adds a prefix to the CLI strings, these help grouping them
37-
FlagPrefix string
38-
39-
// OidcRedirectURL defines the URL that the browser will redirect to.
40-
// if the port is set to 0, bind will randomize it to a high number
41-
// port before starting the OIDC flow.
42-
OidcRedirectURL string `json:"oidc-redirect-url"`
43-
44-
// OIDC token issuer endpoint
45-
OidcIssuer string `json:"oidc-issuer"`
46-
47-
// Client ID to stamp on the tokens
48-
OidcClientID string `json:"oidc-client-id"`
49-
50-
// Client secret to pass in OIDC calls
51-
OidcClientSecret string `json:"oidc-client-secret"`
52-
53-
// Time stamp verification options
54-
55-
// Look for a signed timestamp in the cert and verify with the CTLog Auth
56-
RequireCTlog bool `json:"require-ct-log"`
57-
// Verify the cert validity in the transparency log
58-
RequireTlog bool `json:"require-tlog"`
59-
// Verify the certificate validity time with a signed timestamp
60-
RequireSignedTimestamps bool `json:"require-signed-timestamps"`
61-
// Allow no timestamp, for keys instead of certs
62-
RequireObserverTimestamp bool `json:"require-observer-timestamp"`
17+
sigstore.Instance
6318
}
6419

6520
// Ensure the options have the required OIDC fields
6621
func (s *Sigstore) ValidateOIDC() error {
67-
errs := []error{}
68-
if s.OidcClientID == "" {
69-
errs = append(errs, errors.New("OIDC client ID is missing"))
70-
}
71-
72-
if s.OidcIssuer == "" {
73-
errs = append(errs, errors.New("OIDC issuer URL missing"))
74-
}
75-
76-
if s.OidcRedirectURL == "" {
77-
errs = append(errs, errors.New("OIDC redirect URL missing"))
78-
}
79-
return errors.Join(errs...)
22+
return s.Instance.ValidateOIDC()
8023
}
8124

8225
var DefaultSigstore = Sigstore{
83-
Timestamp: true,
84-
AppendToRekor: true,
85-
86-
TufOptions: tuf.TufOptions{
87-
TufRootURL: "https://tuf-repo-cdn.sigstore.dev",
26+
Instance: sigstore.Instance{
27+
Timestamp: true,
28+
AppendToRekor: true,
29+
30+
TufOptions: tuf.TufOptions{
31+
TufRootURL: "https://tuf-repo-cdn.sigstore.dev",
32+
},
33+
34+
HideOIDCOptions: true,
35+
OidcRedirectURL: "http://localhost:0/auth/callback",
36+
OidcIssuer: "https://oauth2.sigstore.dev/auth",
37+
OidcClientID: "sigstore",
38+
39+
// URLs default the public good instances
40+
FulcioURL: "https://fulcio.sigstore.dev",
41+
RekorURL: "https://rekor.sigstore.dev",
42+
43+
RequireCTlog: true,
44+
RequireTlog: true,
45+
RequireObserverTimestamp: true,
8846
},
89-
90-
HideOIDCOptions: true,
91-
OidcRedirectURL: "http://localhost:0/auth/callback",
92-
OidcIssuer: "https://oauth2.sigstore.dev/auth",
93-
OidcClientID: "sigstore",
94-
95-
// URLs default the public good instances
96-
FulcioURL: "https://fulcio.sigstore.dev",
97-
RekorURL: "https://rekor.sigstore.dev",
98-
99-
RequireCTlog: true,
100-
RequireTlog: true,
101-
RequireObserverTimestamp: true,
10247
}
10348

10449
// ValidateTimestamps
10550
func (s *Sigstore) ValidateTimestamps() error {
106-
if !s.RequireCTlog && !s.RequireTlog && !s.RequireObserverTimestamp && !s.RequireSignedTimestamps {
107-
return errors.New("at least one method to check timestamps must be set")
108-
}
109-
return nil
51+
return s.Instance.ValidateTimestamps()
11052
}
11153

11254
// ValidateSigner check the options required to sign
11355
func (s *Sigstore) ValidateSigner() error {
114-
errs := []error{
115-
s.ValidateOIDC(),
116-
}
117-
if s.RekorURL == "" {
118-
if s.AppendToRekor {
119-
errs = append(errs, errors.New("rekor url not set (and append to rekor is set)"))
120-
}
121-
} else {
122-
if _, err := url.Parse(s.RekorURL); err != nil {
123-
errs = append(errs, fmt.Errorf("invalid Rekor URL"))
124-
}
125-
}
126-
if s.FulcioURL == "" {
127-
errs = append(errs, errors.New("fulcio url not set"))
128-
} else {
129-
if _, err := url.Parse(s.RekorURL); err != nil {
130-
errs = append(errs, fmt.Errorf("invalid Rekor URL"))
131-
}
132-
}
133-
return errors.Join(errs...)
56+
return s.Instance.ValidateSigner()
13457
}
13558

13659
func (s *Sigstore) ValidateVerifier() error {
137-
errs := []error{
138-
s.ValidateTimestamps(),
139-
}
140-
if s.FulcioURL == "" {
141-
errs = append(errs, errors.New("fulcio url not set"))
142-
} else {
143-
if _, err := url.Parse(s.RekorURL); err != nil {
144-
errs = append(errs, fmt.Errorf("invalid Rekor URL value"))
145-
}
146-
}
147-
return errors.Join(errs...)
60+
return s.Instance.ValidateVerifier()
14861
}
14962

15063
// Validate checks the integrity of the sigstore options

options/sigstore_test.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// SPDX-FileCopyrightText: Copyright 2025 Carabiner Systems, Inc
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package options
5+
6+
import (
7+
"testing"
8+
9+
"github.com/google/go-cmp/cmp"
10+
"github.com/stretchr/testify/require"
11+
12+
"github.com/carabiner-dev/signer/sigstore"
13+
)
14+
15+
// TestEnsureDefaultSigstore checks that the default sigstore
16+
// options match the first instance in the roots file.
17+
func TestEnsureDefaultSigstore(t *testing.T) {
18+
conf, err := sigstore.ParseRoots(DefaultRoots)
19+
require.NoError(t, err)
20+
21+
moded := DefaultSigstore
22+
// Timestamp is not exposed in json :/
23+
moded.Timestamp = false
24+
moded.HideOIDCOptions = false
25+
26+
conf.Roots[0].RootData = nil
27+
require.Empty(t, cmp.Diff(conf.Roots[0].Instance, moded.Instance))
28+
}
29+
30+
// TestDefaultRoots checks that the default roots are valid and that the first
31+
// root is sign-capable
32+
func TestDefaultRoots(t *testing.T) {
33+
t.Parallel()
34+
for _, tt := range []struct {
35+
name string
36+
getRoots func(t *testing.T) *sigstore.SigstoreRoots
37+
}{
38+
{"top-level-file", func(t *testing.T) *sigstore.SigstoreRoots {
39+
t.Helper()
40+
roots, err := sigstore.ParseRootsFile("sigstore-roots.json")
41+
require.NoError(t, err)
42+
return roots
43+
}},
44+
{"options-embed", func(t *testing.T) *sigstore.SigstoreRoots {
45+
t.Helper()
46+
roots, err := sigstore.ParseRoots(DefaultRoots)
47+
require.NoError(t, err)
48+
return roots
49+
}},
50+
} {
51+
t.Run(tt.name, func(t *testing.T) {
52+
t.Parallel()
53+
roots := tt.getRoots(t)
54+
55+
// Require at least one root
56+
require.GreaterOrEqual(t, len(roots.Roots), 1)
57+
require.NoError(t, roots.Roots[0].ValidateSigner())
58+
59+
// Verify all returned sets are valid
60+
for _, r := range roots.Roots {
61+
require.NoError(t, r.ValidateVerifier())
62+
}
63+
})
64+
}
65+
}

signer.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ func NewSigner() *Signer {
2929
opts := options.DefaultSigner
3030
roots, err := sigstore.ParseRoots(opts.SigstoreRootsData)
3131
if err == nil && len(roots.Roots) > 0 {
32-
opts.Sigstore = roots.Roots[0].Sigstore
32+
opts.Instance = roots.Roots[0].Instance
3333
} else {
3434
// This is a fatal err. This should never happen as the package embeds
3535
// the roots information and we have unit tests to check they are valid

signer_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ func TestSignStatement(t *testing.T) {
3535
// Parse the roots
3636
roots, err := sigstore.ParseRoots(opts.SigstoreRootsData)
3737
require.NoError(t, err)
38-
opts.Sigstore = roots.Roots[0].Sigstore
38+
opts.Instance = roots.Roots[0].Instance
3939

4040
for _, tt := range []struct {
4141
name string
@@ -110,7 +110,7 @@ func TestSignMessage(t *testing.T) {
110110
opts := options.DefaultSigner
111111
roots, err := sigstore.ParseRoots(opts.SigstoreRootsData)
112112
require.NoError(t, err)
113-
opts.Sigstore = roots.Roots[0].Sigstore
113+
opts.Instance = roots.Roots[0].Instance
114114

115115
for _, tt := range []struct {
116116
name string

0 commit comments

Comments
 (0)