Skip to content

Commit

Permalink
Generalize copy.Image to be able to copy signatures with any format
Browse files Browse the repository at this point in the history
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
  • Loading branch information
mtrmac committed Jul 5, 2022
1 parent 8b5d96b commit 287c3cd
Show file tree
Hide file tree
Showing 7 changed files with 50 additions and 21 deletions.
17 changes: 9 additions & 8 deletions copy/copy.go
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/containers/image/v5/internal/imagesource"
"github.com/containers/image/v5/internal/pkg/platform"
"github.com/containers/image/v5/internal/private"
internalsig "github.com/containers/image/v5/internal/signature"
"github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/pkg/blobinfocache"
"github.com/containers/image/v5/pkg/compression"
Expand Down Expand Up @@ -396,12 +397,12 @@ func (c *copier) copyMultipleImages(ctx context.Context, policyContext *signatur
updatedList := originalList.Clone()

// Read and/or clear the set of signatures for this list.
var sigs [][]byte
var sigs []internalsig.Signature
if options.RemoveSignatures {
sigs = [][]byte{}
sigs = []internalsig.Signature{}
} else {
c.Printf("Getting image list signatures\n")
s, err := c.rawSource.GetSignatures(ctx, nil)
s, err := c.rawSource.GetSignaturesWithFormat(ctx, nil)
if err != nil {
return nil, perrors.Wrap(err, "reading signatures")
}
Expand Down Expand Up @@ -576,7 +577,7 @@ func (c *copier) copyMultipleImages(ctx context.Context, policyContext *signatur
}

c.Printf("Storing list signatures\n")
if err := c.dest.PutSignatures(ctx, sigs, nil); err != nil {
if err := c.dest.PutSignaturesWithFormat(ctx, sigs, nil); err != nil {
return nil, perrors.Wrap(err, "writing signatures")
}

Expand Down Expand Up @@ -639,12 +640,12 @@ func (c *copier) copyOneImage(ctx context.Context, policyContext *signature.Poli
return nil, "", "", err
}

var sigs [][]byte
var sigs []internalsig.Signature
if options.RemoveSignatures {
sigs = [][]byte{}
sigs = []internalsig.Signature{}
} else {
c.Printf("Getting image source signatures\n")
s, err := src.Signatures(ctx)
s, err := src.SignaturesWithFormat(ctx)
if err != nil {
return nil, "", "", perrors.Wrap(err, "reading signatures")
}
Expand Down Expand Up @@ -807,7 +808,7 @@ func (c *copier) copyOneImage(ctx context.Context, policyContext *signature.Poli
}

c.Printf("Storing signatures\n")
if err := c.dest.PutSignatures(ctx, sigs, targetInstance); err != nil {
if err := c.dest.PutSignaturesWithFormat(ctx, sigs, targetInstance); err != nil {
return nil, "", "", perrors.Wrap(err, "writing signatures")
}

Expand Down
5 changes: 3 additions & 2 deletions copy/sign.go
Expand Up @@ -4,13 +4,14 @@ import (
"fmt"

"github.com/containers/image/v5/docker/reference"
internalsig "github.com/containers/image/v5/internal/signature"
"github.com/containers/image/v5/signature"
"github.com/containers/image/v5/transports"
perrors "github.com/pkg/errors"
)

// createSignature creates a new signature of manifest using keyIdentity.
func (c *copier) createSignature(manifest []byte, keyIdentity string, passphrase string, identity reference.Named) ([]byte, error) {
func (c *copier) createSignature(manifest []byte, keyIdentity string, passphrase string, identity reference.Named) (internalsig.Signature, error) {
mech, err := signature.NewGPGSigningMechanism()
if err != nil {
return nil, perrors.Wrap(err, "initializing GPG")
Expand All @@ -36,5 +37,5 @@ func (c *copier) createSignature(manifest []byte, keyIdentity string, passphrase
if err != nil {
return nil, perrors.Wrap(err, "creating signature")
}
return newSig, nil
return internalsig.SimpleSigningFromBlob(newSig), nil
}
9 changes: 7 additions & 2 deletions copy/sign_test.go
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/containers/image/v5/directory"
"github.com/containers/image/v5/docker"
"github.com/containers/image/v5/internal/imagedestination"
internalsig "github.com/containers/image/v5/internal/signature"
"github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/signature"
"github.com/containers/image/v5/types"
Expand Down Expand Up @@ -81,7 +82,9 @@ func TestCreateSignature(t *testing.T) {
// Signing without overriding the identity uses the docker reference
sig, err := c.createSignature(manifestBlob, testKeyFingerprint, "", nil)
require.NoError(t, err)
verified, err := signature.VerifyDockerManifestSignature(sig, manifestBlob, "docker.io/library/busybox:latest", mech, testKeyFingerprint)
simpleSig, ok := sig.(internalsig.SimpleSigning)
require.True(t, ok)
verified, err := signature.VerifyDockerManifestSignature(simpleSig.UntrustedSignature, manifestBlob, "docker.io/library/busybox:latest", mech, testKeyFingerprint)
require.NoError(t, err)
assert.Equal(t, "docker.io/library/busybox:latest", verified.DockerReference)
assert.Equal(t, manifestDigest, verified.DockerManifestDigest)
Expand All @@ -91,7 +94,9 @@ func TestCreateSignature(t *testing.T) {
require.NoError(t, err)
sig, err = c.createSignature(manifestBlob, testKeyFingerprint, "", ref)
require.NoError(t, err)
verified, err = signature.VerifyDockerManifestSignature(sig, manifestBlob, "myregistry.io/myrepo:mytag", mech, testKeyFingerprint)
simpleSig, ok = sig.(internalsig.SimpleSigning)
require.True(t, ok)
verified, err = signature.VerifyDockerManifestSignature(simpleSig.UntrustedSignature, manifestBlob, "myregistry.io/myrepo:mytag", mech, testKeyFingerprint)
require.NoError(t, err)
assert.Equal(t, "myregistry.io/myrepo:mytag", verified.DockerReference)
assert.Equal(t, manifestDigest, verified.DockerManifestDigest)
Expand Down
6 changes: 6 additions & 0 deletions internal/image/sourced.go
Expand Up @@ -6,6 +6,7 @@ package image
import (
"context"

"github.com/containers/image/v5/internal/signature"
"github.com/containers/image/v5/types"
)

Expand Down Expand Up @@ -129,6 +130,11 @@ func (i *SourcedImage) Manifest(ctx context.Context) ([]byte, string, error) {
return i.ManifestBlob, i.ManifestMIMEType, nil
}

// SignaturesWithFormat is like ImageSource.GetSignatures, but the result is cached; it is OK to call this however often you need.
func (i *SourcedImage) SignaturesWithFormat(ctx context.Context) ([]signature.Signature, error) {
return i.UnparsedImage.signaturesWithFormat(ctx)
}

func (i *SourcedImage) LayerInfosForCopy(ctx context.Context) ([]types.BlobInfo, error) {
return i.UnparsedImage.src.LayerInfosForCopy(ctx, i.UnparsedImage.instanceDigest)
}
26 changes: 22 additions & 4 deletions internal/image/unparsed.go
Expand Up @@ -5,6 +5,9 @@ import (
"fmt"

"github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/internal/imagesource"
"github.com/containers/image/v5/internal/private"
"github.com/containers/image/v5/internal/signature"
"github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/types"
"github.com/opencontainers/go-digest"
Expand All @@ -16,13 +19,13 @@ import (
//
// This is publicly visible as c/image/image.UnparsedImage.
type UnparsedImage struct {
src types.ImageSource
src private.ImageSource
instanceDigest *digest.Digest
cachedManifest []byte // A private cache for Manifest(); nil if not yet known.
// A private cache for Manifest(), may be the empty string if guessing failed.
// Valid iff cachedManifest is not nil.
cachedManifestMIMEType string
cachedSignatures [][]byte // A private cache for Signatures(); nil if not yet known.
cachedSignatures []signature.Signature // A private cache for Signatures(); nil if not yet known.
}

// UnparsedInstance returns a types.UnparsedImage implementation for (source, instanceDigest).
Expand All @@ -33,7 +36,7 @@ type UnparsedImage struct {
// This is publicly visible as c/image/image.UnparsedInstance.
func UnparsedInstance(src types.ImageSource, instanceDigest *digest.Digest) *UnparsedImage {
return &UnparsedImage{
src: src,
src: imagesource.FromPublic(src),
instanceDigest: instanceDigest,
}
}
Expand Down Expand Up @@ -89,8 +92,23 @@ func (i *UnparsedImage) expectedManifestDigest() (digest.Digest, bool) {

// Signatures is like ImageSource.GetSignatures, but the result is cached; it is OK to call this however often you need.
func (i *UnparsedImage) Signatures(ctx context.Context) ([][]byte, error) {
sigs, err := i.signaturesWithFormat(ctx)
if err != nil {
return nil, err
}
simpleSigs := [][]byte{}
for _, sig := range sigs {
if sig, ok := sig.(signature.SimpleSigning); ok {
simpleSigs = append(simpleSigs, sig.UntrustedSignature)
}
}
return simpleSigs, nil
}

// signaturesWithFormat is like ImageSource.GetSignatures, but the result is cached; it is OK to call this however often you need.
func (i *UnparsedImage) signaturesWithFormat(ctx context.Context) ([]signature.Signature, error) {
if i.cachedSignatures == nil {
sigs, err := i.src.GetSignatures(ctx, i.instanceDigest)
sigs, err := i.src.GetSignaturesWithFormat(ctx, i.instanceDigest)
if err != nil {
return nil, err
}
Expand Down
2 changes: 2 additions & 0 deletions internal/private/private.go
Expand Up @@ -37,6 +37,8 @@ type ImageSource interface {
type ImageDestinationInternalOnly interface {
// SupportsPutBlobPartial returns true if PutBlobPartial is supported.
SupportsPutBlobPartial() bool
// FIXME: Add SupportsSignaturesWithFormat or something like that, to allow early failures
// on unsupported formats.

// PutBlobWithOptions writes contents of stream and returns data representing the result.
// inputInfo.Digest can be optionally provided if known; if provided, and stream is read to the end without error, the digest MUST match the stream contents.
Expand Down
6 changes: 1 addition & 5 deletions signature/policy_reference_match_test.go
Expand Up @@ -69,11 +69,7 @@ type refImageReferenceMock struct {
}

func (ref refImageReferenceMock) Transport() types.ImageTransport {
// We use this in error messages, so sady we must return something. But right now we do so only when DockerReference is nil, so restrict to that.
if ref.ref == nil {
return mocks.NameImageTransport("== Transport mock")
}
panic("unexpected call to a mock function")
return mocks.NameImageTransport("== Transport mock")
}
func (ref refImageReferenceMock) StringWithinTransport() string {
// We use this in error messages, so sadly we must return something. But right now we do so only when DockerReference is nil, so restrict to that.
Expand Down

0 comments on commit 287c3cd

Please sign in to comment.