-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fork cosign's replace op stuff (#87)
The way this is written in cosign, we re-fetch and parse each layer for every call to Replace. Since we're replacing 3 things with 3 new things, we end up having to fetch and parse 9 blobs, which can take several seconds altogether. Fixing this in cosign could be done by memoizing the predicateType, but we have a further narrowing case where we add the predicateType as an annotation on each attestation, so we can do a very fast lookup of that and skip fetching the blob altogether. This also drops the dupe checker because it's redundant with the replace operation and incurs a little bit of overhead we would rather avoid. Signed-off-by: Jon Johnson <jon.johnson@chainguard.dev>
- Loading branch information
1 parent
50b2bd6
commit c8b068e
Showing
2 changed files
with
111 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
package secant | ||
|
||
import ( | ||
"encoding/base64" | ||
"encoding/json" | ||
"fmt" | ||
"os" | ||
|
||
"github.com/sigstore/cosign/v2/pkg/oci" | ||
) | ||
|
||
func newReplaceOp(predicateType string) *ro { | ||
return &ro{predicateType: predicateType} | ||
} | ||
|
||
type ro struct { | ||
predicateType string | ||
} | ||
|
||
func (r *ro) Replace(signatures oci.Signatures, o oci.Signature) (oci.Signatures, error) { | ||
sigs, err := signatures.Get() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
ros := &replaceOCISignatures{Signatures: signatures} | ||
|
||
sigsCopy := make([]oci.Signature, 0, len(sigs)) | ||
sigsCopy = append(sigsCopy, o) | ||
|
||
if len(sigs) == 0 { | ||
ros.attestations = append(ros.attestations, sigsCopy...) | ||
return ros, nil | ||
} | ||
|
||
for _, s := range sigs { | ||
pt, err := getPredicateType(s) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
if r.predicateType == pt { | ||
fmt.Fprintln(os.Stderr, "Replacing attestation predicate:", r.predicateType) | ||
continue | ||
} | ||
|
||
fmt.Fprintln(os.Stderr, "Not replacing attestation predicate:", pt) | ||
sigsCopy = append(sigsCopy, s) | ||
} | ||
|
||
ros.attestations = append(ros.attestations, sigsCopy...) | ||
|
||
return ros, nil | ||
} | ||
|
||
func getPredicateType(s oci.Signature) (string, error) { | ||
anns, err := s.Annotations() | ||
if err != nil { | ||
return "", fmt.Errorf("could not get annotations: %w", err) | ||
} | ||
|
||
// Fast path: we have this in the top-level annotations. | ||
if pt, ok := anns["predicateType"]; ok { | ||
return pt, nil | ||
} | ||
|
||
// Otherwise we need to fetch and parse the payload. | ||
var signaturePayload map[string]interface{} | ||
p, err := s.Payload() | ||
if err != nil { | ||
return "", fmt.Errorf("could not get payload: %w", err) | ||
} | ||
err = json.Unmarshal(p, &signaturePayload) | ||
if err != nil { | ||
return "", fmt.Errorf("unmarshal payload data: %w", err) | ||
} | ||
|
||
val, ok := signaturePayload["payload"] | ||
if !ok { | ||
return "", fmt.Errorf("could not find 'payload' in payload data") | ||
} | ||
decodedPayload, err := base64.StdEncoding.DecodeString(val.(string)) | ||
if err != nil { | ||
return "", fmt.Errorf("could not decode 'payload': %w", err) | ||
} | ||
|
||
var payloadData map[string]interface{} | ||
if err := json.Unmarshal(decodedPayload, &payloadData); err != nil { | ||
return "", fmt.Errorf("unmarshal payloadData: %w", err) | ||
} | ||
val, ok = payloadData["predicateType"] | ||
if !ok { | ||
return "", fmt.Errorf("could not find 'predicateType' in payload data") | ||
} | ||
|
||
pt, ok := val.(string) | ||
if !ok { | ||
return "", fmt.Errorf("expected predicateType to be string, got type %T: %v", val, val) | ||
} | ||
return pt, nil | ||
} | ||
|
||
type replaceOCISignatures struct { | ||
oci.Signatures | ||
attestations []oci.Signature | ||
} | ||
|
||
func (r *replaceOCISignatures) Get() ([]oci.Signature, error) { | ||
return r.attestations, nil | ||
} |