Skip to content

Commit

Permalink
Include URI for CA verified timestamps (#270)
Browse files Browse the repository at this point in the history
Include the URI of the transparency log and timestamp authority for
verified timestamps in the final verification result. The URIs are
helpful for human readability of the result.

When the WithoutAnyObserverTimestampsUnsafe verify policy is selected,
there is no certificate authority, so the URI is left empty.

Signed-off-by: Colleen Murphy <colleenmurphy@google.com>
  • Loading branch information
cmurphy committed Sep 13, 2024
1 parent fbc922a commit 057b442
Show file tree
Hide file tree
Showing 7 changed files with 33 additions and 34 deletions.
4 changes: 3 additions & 1 deletion pkg/root/trusted_root.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,9 @@ func ParseCertificateAuthority(certAuthority *prototrustroot.CertificateAuthorit
}
}

// TODO: Should we inspect/enforce ca.Subject and ca.Uri?
certificateAuthority.URI = certAuthority.Uri

// TODO: Should we inspect/enforce ca.Subject?
// TODO: Handle validity period (ca.ValidFor)

return certificateAuthority, nil
Expand Down
6 changes: 3 additions & 3 deletions pkg/verify/signed_entity.go
Original file line number Diff line number Diff line change
Expand Up @@ -667,7 +667,7 @@ func (v *SignedEntityVerifier) VerifyTransparencyLogInclusion(entity SignedEntit
}

for _, vts := range verifiedTlogTimestamps {
verifiedTimestamps = append(verifiedTimestamps, TimestampVerificationResult{Type: "Tlog", URI: "TODO", Timestamp: vts})
verifiedTimestamps = append(verifiedTimestamps, TimestampVerificationResult{Type: "Tlog", URI: vts.URI, Timestamp: vts.Time})
}
}

Expand All @@ -692,7 +692,7 @@ func (v *SignedEntityVerifier) VerifyObserverTimestamps(entity SignedEntity, log
return nil, err
}
for _, vts := range verifiedSignedTimestamps {
verifiedTimestamps = append(verifiedTimestamps, TimestampVerificationResult{Type: "TimestampAuthority", URI: "TODO", Timestamp: vts})
verifiedTimestamps = append(verifiedTimestamps, TimestampVerificationResult{Type: "TimestampAuthority", URI: vts.URI, Timestamp: vts.Time})
}
}

Expand All @@ -719,7 +719,7 @@ func (v *SignedEntityVerifier) VerifyObserverTimestamps(entity SignedEntity, log
// append all timestamps
verifiedTimestamps = append(verifiedTimestamps, logTimestamps...)
for _, vts := range verifiedSignedTimestamps {
verifiedTimestamps = append(verifiedTimestamps, TimestampVerificationResult{Type: "TimestampAuthority", URI: "TODO", Timestamp: vts})
verifiedTimestamps = append(verifiedTimestamps, TimestampVerificationResult{Type: "TimestampAuthority", URI: vts.URI, Timestamp: vts.Time})
}
}

Expand Down
1 change: 1 addition & 0 deletions pkg/verify/signed_entity_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ func TestEntitySignedByPublicGoodWithTlogVerifiesSuccessfully(t *testing.T) {
assert.NotNil(t, res.Signature.Certificate)
assert.Equal(t, "https://github.com/sigstore/sigstore-js/.github/workflows/release.yml@refs/heads/main", res.Signature.Certificate.SubjectAlternativeName)
assert.NotEmpty(t, res.VerifiedTimestamps)
assert.Equal(t, "https://rekor.sigstore.dev", res.VerifiedTimestamps[0].URI)

// verifies with integrated timestamp threshold too
v, err = verify.NewSignedEntityVerifier(tr, verify.WithTransparencyLog(1), verify.WithIntegratedTimestamps(1))
Expand Down
35 changes: 13 additions & 22 deletions pkg/verify/tlog.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (
"encoding/hex"
"errors"
"fmt"
"time"

rekorClient "github.com/sigstore/rekor/pkg/client"
rekorGeneratedClient "github.com/sigstore/rekor/pkg/generated/client"
Expand All @@ -43,7 +42,7 @@ const maxAllowedTlogEntries = 32
// 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) ([]Timestamp, error) { //nolint:revive
entries, err := entity.TlogEntries()
if err != nil {
return nil, err
Expand Down Expand Up @@ -75,7 +74,7 @@ func VerifyArtifactTransparencyLog(entity SignedEntity, trustedMaterial root.Tru
return nil, err
}

verifiedTimestamps := []time.Time{}
verifiedTimestamps := []Timestamp{}
logEntriesVerified := 0

for _, entry := range entries {
Expand All @@ -84,29 +83,29 @@ func VerifyArtifactTransparencyLog(entity SignedEntity, trustedMaterial root.Tru
return nil, err
}

rekorLogs := trustedMaterial.RekorLogs()
keyID := entry.LogKeyID()
hex64Key := hex.EncodeToString([]byte(keyID))
tlogVerifier, ok := trustedMaterial.RekorLogs()[hex64Key]
if !ok {
// skip entries the trust root cannot verify
continue
}
if !online {
if !entry.HasInclusionPromise() && !entry.HasInclusionProof() {
return nil, fmt.Errorf("entry must contain an inclusion proof and/or promise")
}
if entry.HasInclusionPromise() {
err = tlog.VerifySET(entry, trustedMaterial.RekorLogs())
err = tlog.VerifySET(entry, rekorLogs)
if err != nil {
// skip entries the trust root cannot verify
continue
}
if trustIntegratedTime {
verifiedTimestamps = append(verifiedTimestamps, entry.IntegratedTime())
verifiedTimestamps = append(verifiedTimestamps, Timestamp{Time: entry.IntegratedTime(), URI: tlogVerifier.BaseURL})
}
}
if entity.HasInclusionProof() {
keyID := entry.LogKeyID()
hex64Key := hex.EncodeToString([]byte(keyID))
tlogVerifier, ok := trustedMaterial.RekorLogs()[hex64Key]
if !ok {
// skip entries the trust root cannot verify
continue
}

verifier, err := getVerifier(tlogVerifier.PublicKey, tlogVerifier.SignatureHashFunc)
if err != nil {
return nil, err
Expand All @@ -119,14 +118,6 @@ func VerifyArtifactTransparencyLog(entity SignedEntity, trustedMaterial root.Tru
// DO NOT use timestamp with only an inclusion proof, because it is not signed metadata
}
} else {
keyID := entry.LogKeyID()
hex64Key := hex.EncodeToString([]byte(keyID))
tlogVerifier, ok := trustedMaterial.RekorLogs()[hex64Key]
if !ok {
// skip entries the trust root cannot verify
continue
}

client, err := getRekorClient(tlogVerifier.BaseURL)
if err != nil {
return nil, err
Expand Down Expand Up @@ -160,7 +151,7 @@ func VerifyArtifactTransparencyLog(entity SignedEntity, trustedMaterial root.Tru
}
}
if trustIntegratedTime {
verifiedTimestamps = append(verifiedTimestamps, entry.IntegratedTime())
verifiedTimestamps = append(verifiedTimestamps, Timestamp{Time: entry.IntegratedTime(), URI: tlogVerifier.BaseURL})
}
}
// Ensure entry signature matches signature from bundle
Expand Down
2 changes: 1 addition & 1 deletion pkg/verify/tlog_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func TestTlogVerifier(t *testing.T) {
entity, err := virtualSigstore.Attest("foo@example.com", "issuer", statement)
assert.NoError(t, err)

var ts []time.Time
var ts []verify.Timestamp
ts, err = verify.VerifyArtifactTransparencyLog(entity, virtualSigstore, 1, true, false)
assert.NoError(t, err)
// 1 verified timestamp
Expand Down
17 changes: 11 additions & 6 deletions pkg/verify/tsa.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,14 @@ import (

const maxAllowedTimestamps = 32

type Timestamp struct {
Time time.Time
URI string
}

// VerifyTimestampAuthority verifies that the given entity has been timestamped
// by a trusted timestamp authority and that the timestamp is valid.
func VerifyTimestampAuthority(entity SignedEntity, trustedMaterial root.TrustedMaterial) ([]time.Time, error) { //nolint:revive
func VerifyTimestampAuthority(entity SignedEntity, trustedMaterial root.TrustedMaterial) ([]Timestamp, error) { //nolint:revive
signedTimestamps, err := entity.Timestamps()
if err != nil {
return nil, err
Expand Down Expand Up @@ -62,7 +67,7 @@ func VerifyTimestampAuthority(entity SignedEntity, trustedMaterial root.TrustedM
return nil, err
}

verifiedTimestamps := []time.Time{}
verifiedTimestamps := []Timestamp{}
for _, timestamp := range signedTimestamps {
verifiedSignedTimestamp, err := verifySignedTimestamp(timestamp, signatureBytes, trustedMaterial, verificationContent)

Expand All @@ -82,7 +87,7 @@ func VerifyTimestampAuthority(entity SignedEntity, trustedMaterial root.TrustedM
//
// The threshold parameter is the number of unique timestamps that must be
// verified.
func VerifyTimestampAuthorityWithThreshold(entity SignedEntity, trustedMaterial root.TrustedMaterial, threshold int) ([]time.Time, error) { //nolint:revive
func VerifyTimestampAuthorityWithThreshold(entity SignedEntity, trustedMaterial root.TrustedMaterial, threshold int) ([]Timestamp, error) { //nolint:revive
verifiedTimestamps, err := VerifyTimestampAuthority(entity, trustedMaterial)
if err != nil {
return nil, err
Expand All @@ -93,7 +98,7 @@ func VerifyTimestampAuthorityWithThreshold(entity SignedEntity, trustedMaterial
return verifiedTimestamps, nil
}

func verifySignedTimestamp(signedTimestamp []byte, dsseSignatureBytes []byte, trustedMaterial root.TrustedMaterial, verificationContent VerificationContent) (time.Time, error) {
func verifySignedTimestamp(signedTimestamp []byte, dsseSignatureBytes []byte, trustedMaterial root.TrustedMaterial, verificationContent VerificationContent) (Timestamp, error) {
certAuthorities := trustedMaterial.TimestampingAuthorities()

// Iterate through TSA certificate authorities to find one that verifies
Expand Down Expand Up @@ -124,8 +129,8 @@ func verifySignedTimestamp(signedTimestamp []byte, dsseSignatureBytes []byte, tr
}

// All above verification successful, so return nil
return timestamp.Time, nil
return Timestamp{Time: timestamp.Time, URI: ca.URI}, nil
}

return time.Time{}, errors.New("unable to verify signed timestamps")
return Timestamp{}, errors.New("unable to verify signed timestamps")
}
2 changes: 1 addition & 1 deletion pkg/verify/tsa_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func TestTimestampAuthorityVerifierWithoutThreshold(t *testing.T) {
virtualSigstore2, err := ca.NewVirtualSigstore()
assert.NoError(t, err)

var ts []time.Time
var ts []verify.Timestamp

// expect one verified timestamp
ts, err = verify.VerifyTimestampAuthority(entity, virtualSigstore)
Expand Down

0 comments on commit 057b442

Please sign in to comment.