Skip to content

Commit

Permalink
Add user agent to online Rekor requests and TUF requests.
Browse files Browse the repository at this point in the history
Depends on theupdateframework/go-tuf#642.

Signed-off-by: Zach Steindler <steiza@github.com>
  • Loading branch information
steiza committed Jun 25, 2024
1 parent 261418b commit 5b05f45
Show file tree
Hide file tree
Showing 10 changed files with 67 additions and 32 deletions.
9 changes: 7 additions & 2 deletions cmd/conformance/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@ import (

protobundle "github.com/sigstore/protobuf-specs/gen/pb-go/bundle/v1"
protocommon "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1"
"github.com/theupdateframework/go-tuf/v2/metadata/fetcher"
"google.golang.org/protobuf/encoding/protojson"

"github.com/sigstore/sigstore-go/pkg/bundle"
"github.com/sigstore/sigstore-go/pkg/root"
"github.com/sigstore/sigstore-go/pkg/sign"
"github.com/sigstore/sigstore-go/pkg/tuf"
"github.com/sigstore/sigstore-go/pkg/util"
"github.com/sigstore/sigstore-go/pkg/verify"
)

Expand Down Expand Up @@ -60,6 +62,9 @@ func getTrustedRoot(staging bool) root.TrustedMaterial {
trustedRootJSON, err = os.ReadFile(*trustedRootPath)
} else {
opts := tuf.DefaultOptions()
fetcher := fetcher.DefaultFetcher{}
fetcher.SetHTTPUserAgent(util.ConstructUserAgent(Version))

Check failure on line 66 in cmd/conformance/main.go

View workflow job for this annotation

GitHub Actions / lint

fetcher.SetHTTPUserAgent undefined (type fetcher.DefaultFetcher has no field or method SetHTTPUserAgent) (typecheck)
opts.Fetcher = &fetcher

if staging {
opts.Root = tuf.StagingRoot()
Expand Down Expand Up @@ -295,7 +300,7 @@ func main() {
tr := getTrustedRoot(staging)

verifierConfig := []verify.VerifierOption{}
verifierConfig = append(verifierConfig, verify.WithoutAnyObserverTimestampsUnsafe(), verify.WithSignedCertificateTimestamps(1))
verifierConfig = append(verifierConfig, verify.WithVersionString(Version), verify.WithoutAnyObserverTimestampsUnsafe(), verify.WithSignedCertificateTimestamps(1))
if len(tr.RekorLogs()) > 0 {
verifierConfig = append(verifierConfig, verify.WithOnlineVerification())
}
Expand Down Expand Up @@ -344,7 +349,7 @@ func main() {
tr := getTrustedRoot(staging)

verifierConfig := []verify.VerifierOption{}
verifierConfig = append(verifierConfig, verify.WithSignedCertificateTimestamps(1))
verifierConfig = append(verifierConfig, verify.WithVersionString(Version), verify.WithSignedCertificateTimestamps(1))

// Check bundle and trusted root for signed timestamp information
bundleTimestamps, err := b.Timestamps()
Expand Down
11 changes: 10 additions & 1 deletion cmd/sigstore-go/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,17 @@ import (
"os"
"time"

"github.com/sigstore/sigstore/pkg/signature"
"github.com/theupdateframework/go-tuf/v2/metadata/fetcher"

"github.com/sigstore/sigstore-go/pkg/bundle"
"github.com/sigstore/sigstore-go/pkg/root"
"github.com/sigstore/sigstore-go/pkg/tuf"
"github.com/sigstore/sigstore-go/pkg/util"
"github.com/sigstore/sigstore-go/pkg/verify"
"github.com/sigstore/sigstore/pkg/signature"
)

var Version string
var artifact *string
var artifactDigest *string
var artifactDigestAlgorithm *string
Expand Down Expand Up @@ -101,6 +105,8 @@ func run() error {
identityPolicies := []verify.PolicyOption{}
var artifactPolicy verify.ArtifactPolicyOption

verifierConfig = append(verifierConfig, verify.WithVersionString(Version))

if *requireCTlog {
verifierConfig = append(verifierConfig, verify.WithSignedCertificateTimestamps(1))
}
Expand Down Expand Up @@ -129,6 +135,9 @@ func run() error {
if *tufRootURL != "" {
opts := tuf.DefaultOptions()
opts.RepositoryBaseURL = *tufRootURL
fetcher := fetcher.DefaultFetcher{}
fetcher.SetHTTPUserAgent(util.ConstructUserAgent(Version))

Check failure on line 139 in cmd/sigstore-go/main.go

View workflow job for this annotation

GitHub Actions / lint

fetcher.SetHTTPUserAgent undefined (type fetcher.DefaultFetcher has no field or method SetHTTPUserAgent) (typecheck)
opts.Fetcher = &fetcher

// Load the tuf root.json if provided, if not use public good
if *tufTrustedRoot != "" {
Expand Down
6 changes: 6 additions & 0 deletions examples/sigstore-go-signing/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ import (
"os"
"time"

"github.com/theupdateframework/go-tuf/v2/metadata/fetcher"
"google.golang.org/protobuf/encoding/protojson"

"github.com/sigstore/sigstore-go/pkg/root"
"github.com/sigstore/sigstore-go/pkg/sign"
"github.com/sigstore/sigstore-go/pkg/tuf"
"github.com/sigstore/sigstore-go/pkg/util"
)

var Version string
Expand Down Expand Up @@ -83,9 +85,13 @@ func main() {
opts := sign.BundleOptions{}

// Get trusted_root.json
fetcher := fetcher.DefaultFetcher{}
fetcher.SetHTTPUserAgent(util.ConstructUserAgent(Version))

tufOptions := &tuf.Options{
Root: tuf.StagingRoot(),
RepositoryBaseURL: tuf.StagingMirror,
Fetcher: &fetcher,
}
tufClient, err := tuf.New(tufOptions)
if err != nil {
Expand Down
4 changes: 3 additions & 1 deletion pkg/sign/certificate.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import (
"net/http"
"strings"
"time"

"github.com/sigstore/sigstore-go/pkg/util"
)

type CertificateProviderOptions struct {
Expand Down Expand Up @@ -169,7 +171,7 @@ func (f *Fulcio) GetCertificate(ctx context.Context, keypair Keypair, opts *Cert
}
request.Header.Add("Authorization", "Bearer "+opts.IDToken)
request.Header.Add("Content-Type", "application/json")
request.Header.Add("User-Agent", constructUserAgent(f.options.LibraryVersion))
request.Header.Add("User-Agent", util.ConstructUserAgent(f.options.LibraryVersion))

response, err = f.client.Do(request)
if err != nil {
Expand Down
14 changes: 3 additions & 11 deletions pkg/sign/timestamping.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import (
"time"

"github.com/digitorus/timestamp"

"github.com/sigstore/sigstore-go/pkg/util"
)

type TimestampAuthorityOptions struct {
Expand Down Expand Up @@ -83,7 +85,7 @@ func (ta *TimestampAuthority) GetTimestamp(ctx context.Context, signature []byte
return nil, err
}
request.Header.Add("Content-Type", "application/timestamp-query")
request.Header.Add("User-Agent", constructUserAgent(ta.options.LibraryVersion))
request.Header.Add("User-Agent", util.ConstructUserAgent(ta.options.LibraryVersion))

response, err = ta.client.Do(request)
if err != nil {
Expand Down Expand Up @@ -122,13 +124,3 @@ func (ta *TimestampAuthority) GetTimestamp(ctx context.Context, signature []byte

return body, nil
}

func constructUserAgent(version string) string {
userAgent := "sigstore-go"
if version != "" {
userAgent += "/"
userAgent += version
}

return userAgent
}
8 changes: 5 additions & 3 deletions pkg/sign/transparency.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@ import (
"github.com/sigstore/rekor/pkg/types"
"github.com/sigstore/rekor/pkg/types/dsse"
"github.com/sigstore/rekor/pkg/types/hashedrekord"
"github.com/sigstore/rekor/pkg/util"
rekorUtil "github.com/sigstore/rekor/pkg/util"

// To initialize rekor types
_ "github.com/sigstore/rekor/pkg/types/dsse/v0.0.1"
_ "github.com/sigstore/rekor/pkg/types/hashedrekord/v0.0.1"

"github.com/sigstore/sigstore-go/pkg/util"
)

type RekorClient interface {
Expand Down Expand Up @@ -105,7 +107,7 @@ func (r *Rekor) GetTransparencyLogEntry(pubKeyPEM []byte, b *protobundle.Bundle)

artifactProperties.PKIFormat = string(pki.X509)
artifactProperties.SignatureBytes = messageSignature.Signature
artifactProperties.ArtifactHash = util.PrefixSHA(hexDigest)
artifactProperties.ArtifactHash = rekorUtil.PrefixSHA(hexDigest)

var err error
proposedEntry, err = hashedrekordType.CreateProposedEntry(context.TODO(), "", artifactProperties)
Expand All @@ -126,7 +128,7 @@ func (r *Rekor) GetTransparencyLogEntry(pubKeyPEM []byte, b *protobundle.Bundle)
params.SetProposedEntry(proposedEntry)

if r.options.Client == nil {
client, err := client.GetRekorClient(r.options.BaseURL, client.WithUserAgent(constructUserAgent(r.options.LibraryVersion)), client.WithRetryCount(r.options.Retries))
client, err := client.GetRekorClient(r.options.BaseURL, client.WithUserAgent(util.ConstructUserAgent(r.options.LibraryVersion)), client.WithRetryCount(r.options.Retries))
if err != nil {
return err
}
Expand Down
7 changes: 7 additions & 0 deletions pkg/tuf/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ import (
"time"

"github.com/theupdateframework/go-tuf/v2/metadata/config"
"github.com/theupdateframework/go-tuf/v2/metadata/fetcher"
"github.com/theupdateframework/go-tuf/v2/metadata/updater"

"github.com/sigstore/sigstore-go/pkg/util"

Check failure on line 27 in pkg/tuf/client.go

View workflow job for this annotation

GitHub Actions / build (macos-latest)

no required module provides package github.com/sigstore/sigstore-go/pkg/util; to add it:

Check failure on line 27 in pkg/tuf/client.go

View workflow job for this annotation

GitHub Actions / conformance

no required module provides package github.com/sigstore/sigstore-go/pkg/util; to add it:

Check failure on line 27 in pkg/tuf/client.go

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

no required module provides package github.com/sigstore/sigstore-go/pkg/util; to add it:

Check failure on line 27 in pkg/tuf/client.go

View workflow job for this annotation

GitHub Actions / Analyze (go)

no required module provides package github.com/sigstore/sigstore-go/pkg/util; to add it:

Check failure on line 27 in pkg/tuf/client.go

View workflow job for this annotation

GitHub Actions / Analyze (go)

no required module provides package github.com/sigstore/sigstore-go/pkg/util; to add it:

Check failure on line 27 in pkg/tuf/client.go

View workflow job for this annotation

GitHub Actions / build (windows-latest)

no required module provides package github.com/sigstore/sigstore-go/pkg/util; to add it:
)

// Client is a Sigstore TUF client
Expand Down Expand Up @@ -56,6 +59,10 @@ func New(opts *Options) (*Client, error) {

if opts.Fetcher != nil {
c.cfg.Fetcher = opts.Fetcher
} else {
fetcher := fetcher.DefaultFetcher{}
fetcher.SetHTTPUserAgent(util.ConstructUserAgent(""))

Check failure on line 64 in pkg/tuf/client.go

View workflow job for this annotation

GitHub Actions / lint

fetcher.SetHTTPUserAgent undefined (type fetcher.DefaultFetcher has no field or method SetHTTPUserAgent) (typecheck)
c.cfg.Fetcher = &fetcher
}

// Upon client creation, we may not perform a full TUF update,
Expand Down
13 changes: 12 additions & 1 deletion pkg/verify/signed_entity.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ type VerifierConfig struct { // nolint: revive
// rather than a provided signed or log timestamp. Most workflows will
// not use this option
weDoNotExpectAnyObserverTimestamps bool
// optional sigstore-go version to use in user agent for outbound requests
version string
}

type VerifierOption func(*VerifierConfig) error
Expand Down Expand Up @@ -202,6 +204,15 @@ func WithoutAnyObserverTimestampsUnsafe() VerifierOption {
}
}

// WithVersionString is used to optionally specify a specific version of
// sigstore-go, to use in user agent for outbound requests
func WithVersionString(version string) VerifierOption {
return func(c *VerifierConfig) error {
c.version = version
return nil
}
}

func (c *VerifierConfig) Validate() error {
if !c.requireObserverTimestamps && !c.weExpectSignedTimestamps && !c.requireIntegratedTimestamps && !c.weDoNotExpectAnyObserverTimestamps {
return errors.New("when initializing a new SignedEntityVerifier, you must specify at least one of " +
Expand Down Expand Up @@ -616,7 +627,7 @@ func (v *SignedEntityVerifier) VerifyTransparencyLogInclusion(entity SignedEntit
if v.config.weExpectTlogEntries {
// log timestamps should be verified if with WithIntegratedTimestamps or WithObserverTimestamps is used
verifiedTlogTimestamps, err := VerifyArtifactTransparencyLog(entity, v.trustedMaterial, v.config.tlogEntriesThreshold,
v.config.requireIntegratedTimestamps || v.config.requireObserverTimestamps, v.config.performOnlineVerification)
v.config.requireIntegratedTimestamps || v.config.requireObserverTimestamps, v.config.performOnlineVerification, v.config.version)
if err != nil {
return nil, err
}
Expand Down
9 changes: 5 additions & 4 deletions pkg/verify/tlog.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (

"github.com/sigstore/sigstore-go/pkg/root"
"github.com/sigstore/sigstore-go/pkg/tlog"
"github.com/sigstore/sigstore-go/pkg/util"
)

// VerifyArtifactTransparencyLog verifies that the given entity has been logged
Expand All @@ -41,7 +42,7 @@ import (
// that must be verified.
//
// If online is true, the log entry is verified against the Rekor server.
func VerifyArtifactTransparencyLog(entity SignedEntity, trustedMaterial root.TrustedMaterial, logThreshold int, trustIntegratedTime, online bool) ([]time.Time, error) { //nolint:revive
func VerifyArtifactTransparencyLog(entity SignedEntity, trustedMaterial root.TrustedMaterial, logThreshold int, trustIntegratedTime, online bool, version string) ([]time.Time, error) { //nolint:revive
entries, err := entity.TlogEntries()
if err != nil {
return nil, err
Expand Down Expand Up @@ -120,7 +121,7 @@ func VerifyArtifactTransparencyLog(entity SignedEntity, trustedMaterial root.Tru
continue
}

client, err := getRekorClient(tlogVerifier.BaseURL)
client, err := getRekorClient(tlogVerifier.BaseURL, version)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -198,8 +199,8 @@ func getVerifier(publicKey crypto.PublicKey, hashFunc crypto.Hash) (*signature.V
return &verifier, nil
}

func getRekorClient(baseURL string) (*rekorGeneratedClient.Rekor, error) {
client, err := rekorClient.GetRekorClient(baseURL)
func getRekorClient(baseURL, version string) (*rekorGeneratedClient.Rekor, error) {
client, err := rekorClient.GetRekorClient(baseURL, rekorClient.WithUserAgent(util.ConstructUserAgent(version)))
if err != nil {
return nil, err
}
Expand Down
18 changes: 9 additions & 9 deletions pkg/verify/tlog_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,20 @@ func TestTlogVerifier(t *testing.T) {
assert.NoError(t, err)

var ts []time.Time
ts, err = verify.VerifyArtifactTransparencyLog(entity, virtualSigstore, 1, true, false)
ts, err = verify.VerifyArtifactTransparencyLog(entity, virtualSigstore, 1, true, false, "")
assert.NoError(t, err)
// 1 verified timestamp
assert.Len(t, ts, 1)

ts, err = verify.VerifyArtifactTransparencyLog(entity, virtualSigstore, 1, false, false)
ts, err = verify.VerifyArtifactTransparencyLog(entity, virtualSigstore, 1, false, false, "")
assert.NoError(t, err)
// 0 verified timestamps, since integrated timestamps are ignored
assert.Len(t, ts, 0)

virtualSigstore2, err := ca.NewVirtualSigstore()
assert.NoError(t, err)

_, err = verify.VerifyArtifactTransparencyLog(entity, virtualSigstore2, 1, true, false)
_, err = verify.VerifyArtifactTransparencyLog(entity, virtualSigstore2, 1, true, false, "")
assert.Error(t, err) // different sigstore instance should fail to verify

// Attempt to use tlog with integrated time outside certificate validity.
Expand All @@ -59,7 +59,7 @@ func TestTlogVerifier(t *testing.T) {
entity, err = virtualSigstore.AttestAtTime("foo@fighters.com", "issuer", statement, time.Now().Add(30*time.Minute))
assert.NoError(t, err)

_, err = verify.VerifyArtifactTransparencyLog(entity, virtualSigstore, 1, true, false)
_, err = verify.VerifyArtifactTransparencyLog(entity, virtualSigstore, 1, true, false, "")
assert.Error(t, err)
}

Expand Down Expand Up @@ -96,11 +96,11 @@ func TestIgnoredTLogEntries(t *testing.T) {
assert.NoError(t, err)

// success: entry that cannot be verified is ignored
_, err = verify.VerifyArtifactTransparencyLog(&oneTrustedOneUntrustedLogEntry{entity, untrustedEntity}, virtualSigstore, 1, true, false)
_, err = verify.VerifyArtifactTransparencyLog(&oneTrustedOneUntrustedLogEntry{entity, untrustedEntity}, virtualSigstore, 1, true, false, "")
assert.NoError(t, err)

// failure: threshold of 2 is not met since 1 untrusted entry is ignored
_, err = verify.VerifyArtifactTransparencyLog(&oneTrustedOneUntrustedLogEntry{entity, untrustedEntity}, virtualSigstore, 2, true, false)
_, err = verify.VerifyArtifactTransparencyLog(&oneTrustedOneUntrustedLogEntry{entity, untrustedEntity}, virtualSigstore, 2, true, false, "")
assert.Error(t, err)
}

Expand Down Expand Up @@ -138,7 +138,7 @@ func TestInvalidTLogEntries(t *testing.T) {
assert.NoError(t, err)

// failure: threshold of 1 is not met with invalid entry
_, err = verify.VerifyArtifactTransparencyLog(&invalidTLogEntity{entity}, virtualSigstore, 1, true, false)
_, err = verify.VerifyArtifactTransparencyLog(&invalidTLogEntity{entity}, virtualSigstore, 1, true, false, "")
assert.Error(t, err)
if err.Error() != "entry must contain an inclusion proof and/or promise" {
t.Errorf("expected error with missing proof/promises, got: %v", err.Error())
Expand All @@ -162,7 +162,7 @@ func TestNoTLogEntries(t *testing.T) {
assert.NoError(t, err)

// failure: threshold of 1 is not met with no entries
_, err = verify.VerifyArtifactTransparencyLog(&noTLogEntity{entity}, virtualSigstore, 1, true, false)
_, err = verify.VerifyArtifactTransparencyLog(&noTLogEntity{entity}, virtualSigstore, 1, true, false, "")
assert.Error(t, err)
if !strings.Contains(err.Error(), "not enough verified log entries from transparency log") {
t.Errorf("expected error with timestamp threshold, got: %v", err.Error())
Expand Down Expand Up @@ -190,6 +190,6 @@ func TestDuplicateTlogEntries(t *testing.T) {
entity, err := virtualSigstore.Attest("foofighters@example.com", "issuer", statement)
assert.NoError(t, err)

_, err = verify.VerifyArtifactTransparencyLog(&dupTlogEntity{entity}, virtualSigstore, 1, true, false)
_, err = verify.VerifyArtifactTransparencyLog(&dupTlogEntity{entity}, virtualSigstore, 1, true, false, "")
assert.Error(t, err) // duplicate tlog entries should fail to verify
}

0 comments on commit 5b05f45

Please sign in to comment.