diff --git a/pkg/root/trusted_root.go b/pkg/root/trusted_root.go index 3112aebb..7291960b 100644 --- a/pkg/root/trusted_root.go +++ b/pkg/root/trusted_root.go @@ -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 diff --git a/pkg/verify/signed_entity.go b/pkg/verify/signed_entity.go index 2ab826e9..b33fbd5f 100644 --- a/pkg/verify/signed_entity.go +++ b/pkg/verify/signed_entity.go @@ -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}) } } @@ -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}) } } @@ -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}) } } diff --git a/pkg/verify/signed_entity_test.go b/pkg/verify/signed_entity_test.go index ab04c37f..98ea657e 100644 --- a/pkg/verify/signed_entity_test.go +++ b/pkg/verify/signed_entity_test.go @@ -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)) diff --git a/pkg/verify/tlog.go b/pkg/verify/tlog.go index e91baafd..4dcacde1 100644 --- a/pkg/verify/tlog.go +++ b/pkg/verify/tlog.go @@ -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" @@ -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 @@ -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 { @@ -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 @@ -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 @@ -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 diff --git a/pkg/verify/tlog_test.go b/pkg/verify/tlog_test.go index dc16bf3c..be8d60bc 100644 --- a/pkg/verify/tlog_test.go +++ b/pkg/verify/tlog_test.go @@ -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 diff --git a/pkg/verify/tsa.go b/pkg/verify/tsa.go index 966fad20..2db892b0 100644 --- a/pkg/verify/tsa.go +++ b/pkg/verify/tsa.go @@ -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 @@ -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) @@ -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 @@ -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 @@ -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") } diff --git a/pkg/verify/tsa_test.go b/pkg/verify/tsa_test.go index 40972fc2..3ebd4eac 100644 --- a/pkg/verify/tsa_test.go +++ b/pkg/verify/tsa_test.go @@ -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)