|
for _, entry := range entries { |
|
err := tlog.ValidateEntry(entry) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
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()) |
|
if err != nil { |
|
// skip entries the trust root cannot verify |
|
continue |
|
} |
|
if trustIntegratedTime { |
|
verifiedTimestamps = append(verifiedTimestamps, entry.IntegratedTime()) |
|
} |
|
} |
|
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 |
|
} |
|
|
|
err = tlog.VerifyInclusion(entry, *verifier) |
|
if err != nil { |
|
return nil, err |
|
} |
|
// 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 |
|
} |
|
verifier, err := getVerifier(tlogVerifier.PublicKey, tlogVerifier.SignatureHashFunc) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
logIndex := entry.LogIndex() |
|
|
|
searchParams := rekorEntries.NewGetLogEntryByIndexParams() |
|
searchParams.LogIndex = logIndex |
|
|
|
resp, err := client.Entries.GetLogEntryByIndex(searchParams) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
if len(resp.Payload) == 0 { |
|
return nil, fmt.Errorf("unable to locate log entry %d", logIndex) |
|
} |
|
|
|
logEntry := resp.Payload |
|
|
|
for _, v := range logEntry { |
|
v := v |
|
err = rekorVerify.VerifyLogEntry(context.TODO(), &v, *verifier) |
|
if err != nil { |
|
return nil, err |
|
} |
|
} |
|
if trustIntegratedTime { |
|
verifiedTimestamps = append(verifiedTimestamps, entry.IntegratedTime()) |
|
} |
|
} |
|
// Ensure entry signature matches signature from bundle |
|
if !bytes.Equal(entry.Signature(), entitySignature) { |
|
return nil, errors.New("transparency log signature does not match") |
|
} |
|
|
|
// Ensure entry certificate matches bundle certificate |
|
if !verificationContent.CompareKey(entry.PublicKey(), trustedMaterial) { |
|
return nil, errors.New("transparency log certificate does not match") |
|
} |
|
|
|
// TODO: if you have access to artifact, check that it matches body subject |
|
|
|
// Check tlog entry time against bundle certificates |
|
if !verificationContent.ValidAtTime(entry.IntegratedTime(), trustedMaterial) { |
|
return nil, errors.New("integrated time outside certificate validity") |
|
} |
|
|
|
// successful log entry verification |
|
logEntriesVerified++ |
|
} |
Impact
sigstore-go is susceptible to a denial of service attack when a verifier is provided a maliciously crafted Sigstore Bundle containing large amounts of verifiable data, in the form of signed transparency log entries, RFC 3161 timestamps, and attestation subjects. The verification of these data structures is computationally expensive. This can be used to consume excessive CPU resources, leading to a denial of service attack. TUF's security model labels this type of vulnerability an "Endless data attack," and can lead to verification failing to complete and disrupting services that rely on sigstore-go for verification.
The vulnerable loops are in the verification functions in the package
github.com/sigstore/sigstore-go/pkg/verify
. The first is the DSSE envelope verification loop inverifyEnvelopeWithArtifact
, which decodes all the digests in an attestation can be found here:sigstore-go/pkg/verify/signature.go
Lines 183 to 193 in 725e508
The next loop is in the
VerifyArtifactTransparencyLog
function, which verifies all the signed entries in a bundle:sigstore-go/pkg/verify/tlog.go
Lines 74 to 178 in 725e508
The next loop is the
VerifyTimestampAuthority
function, which verifies all the RFC 3161 timestamps in a bundle:sigstore-go/pkg/verify/tsa.go
Lines 59 to 68 in 725e508
Patches
This vulnerability is addressed with sigstore-go 0.6.1, which adds hard limits to the number of verifiable data structures that can be processed in a bundle. Verification will fail if a bundle has data that exceeds these limits. The limits are:
These limits are intended to be high enough to accommodate the vast majority of use cases, while preventing the verification of maliciously crafted bundles that contain large amounts of verifiable data.
Workarounds
The best way to mitigate the risk is to upgrade to sigstore-go 0.6.1 or later. Users who are vulnerable but unable to quickly upgrade may consider adding manual bundle validation to enforce limits similar to those in the referenced patch prior to calling sigstore-go's verification functions.