Files
attest/pkg/attestation/registry.go
2024-08-12 16:36:18 -05:00

102 lines
3.2 KiB
Go

package attestation
import (
"context"
"fmt"
"github.com/docker/attest/pkg/oci"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/remote"
)
type RegistryResolver struct {
*oci.RegistryImageDetailsResolver
*Manifest
}
func NewRegistryResolver(src *oci.RegistryImageDetailsResolver) (*RegistryResolver, error) {
return &RegistryResolver{
RegistryImageDetailsResolver: src,
}, nil
}
func (r *RegistryResolver) Attestations(ctx context.Context, predicateType string) ([]*Envelope, error) {
if r.Manifest == nil {
attest, err := FetchManifest(ctx, r.Identifier, r.ImageSpec.Platform)
if err != nil {
return nil, err
}
r.Manifest = attest
}
return ExtractEnvelopes(r.Manifest, predicateType)
}
func attestationDigestForImage(ix *v1.IndexManifest, imageDigest string, attestType string) (string, error) {
for i := range ix.Manifests {
m := &ix.Manifests[i]
if v, ok := m.Annotations[DockerReferenceType]; ok && v == attestType {
if d, ok := m.Annotations[DockerReferenceDigest]; ok && d == imageDigest {
return m.Digest.String(), nil
}
}
}
return "", fmt.Errorf("no attestation found for image %s", imageDigest)
}
func FetchManifest(ctx context.Context, image string, platform *v1.Platform) (*Manifest, error) {
// we want to get to the image index, so ignoring platform for now
options := oci.WithOptions(ctx, nil)
ref, err := name.ParseReference(image)
if err != nil {
return nil, fmt.Errorf("failed to parse reference: %w", err)
}
index, err := remote.Index(ref, options...)
if err != nil {
return nil, fmt.Errorf("failed to get index: %w", err)
}
indexManifest, err := index.IndexManifest()
if err != nil {
return nil, fmt.Errorf("failed to get index manifest: %w", err)
}
subjectDescriptor, err := oci.ImageDescriptor(indexManifest, platform)
if err != nil {
return nil, fmt.Errorf("failed to obtain image for platform: %w", err)
}
digest := subjectDescriptor.Digest.String()
ref, err = name.ParseReference(fmt.Sprintf("%s@%s", ref.Context().Name(), digest))
if err != nil {
return nil, fmt.Errorf("failed to parse attestation reference: %w", err)
}
attestationDigest, err := attestationDigestForImage(indexManifest, digest, "attestation-manifest")
if err != nil {
return nil, fmt.Errorf("failed to obtain attestation for image: %w", err)
}
ref, err = name.ParseReference(fmt.Sprintf("%s@%s", ref.Context().Name(), attestationDigest))
if err != nil {
return nil, fmt.Errorf("failed to parse attestation reference: %w", err)
}
remoteDescriptor, err := remote.Get(ref, options...)
if err != nil {
return nil, fmt.Errorf("failed to get attestation: %w", err)
}
attestationImage, err := remoteDescriptor.Image()
if err != nil {
return nil, fmt.Errorf("failed to get attestation image: %w", err)
}
layers, err := layersFromImage(attestationImage)
if err != nil {
return nil, fmt.Errorf("failed to get attestations from image: %w", err)
}
attest := &Manifest{
OriginalLayers: layers,
OriginalDescriptor: &remoteDescriptor.Descriptor,
SubjectName: image,
SubjectDescriptor: subjectDescriptor,
}
return attest, nil
}