Skip to content

Commit

Permalink
Reject unsupported encryption/decryption updates in UpdateLayerInfos
Browse files Browse the repository at this point in the history
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
  • Loading branch information
mtrmac committed May 4, 2023
1 parent b3f44a7 commit 2699a9e
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 0 deletions.
3 changes: 3 additions & 0 deletions manifest/docker_schema1.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,9 @@ func (m *Schema1) UpdateLayerInfos(layerInfos []types.BlobInfo) error {
// but (docker pull) ignores them in favor of computing DiffIDs from uncompressed data, except verifying the child->parent links and uniqueness.
// So, we don't bother recomputing the IDs in m.History.V1Compatibility.
m.FSLayers[(len(layerInfos)-1)-i].BlobSum = info.Digest
if info.CryptoOperation != types.PreserveOriginalCrypto {
return fmt.Errorf("encryption change (for layer %q) is not supported in schema2 manifests", info.Digest)
}
}
return nil
}
Expand Down
86 changes: 86 additions & 0 deletions manifest/docker_schema1_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"testing"
"time"

"github.com/containers/image/v5/pkg/compression"
"github.com/containers/image/v5/types"
"github.com/opencontainers/go-digest"
"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -177,6 +178,91 @@ func TestSchema1LayerInfos(t *testing.T) {
}, m.LayerInfos())
}

func TestSchema1UpdateLayerInfos(t *testing.T) {
for _, c := range []struct {
name string
sourceFixture string
updates []types.BlobInfo
expectedFixture string // or "" to indicate an expected failure
}{
// Many more tests cases could be added here
{
name: "uncompressed → gzip encrypted",
sourceFixture: "v2s1.manifest.json",
updates: []types.BlobInfo{
{
Digest: "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
Size: 32654,
Annotations: map[string]string{"org.opencontainers.image.enc.…": "layer1"},
CompressionOperation: types.Compress,
CompressionAlgorithm: &compression.Gzip,
CryptoOperation: types.Encrypt,
},
{
Digest: "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
Size: 16724,
Annotations: map[string]string{"org.opencontainers.image.enc.…": "layer2"},
CompressionOperation: types.Compress,
CompressionAlgorithm: &compression.Gzip,
CryptoOperation: types.Encrypt,
},
{
Digest: "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
Size: 73109,
Annotations: map[string]string{"org.opencontainers.image.enc.…": "layer2"},
CompressionOperation: types.Compress,
CompressionAlgorithm: &compression.Gzip,
CryptoOperation: types.Encrypt,
},
},
expectedFixture: "", // Encryption is not supported
},
{
name: "gzip → uncompressed decrypted", // We can’t represent encrypted images anyway, but verify that we reject decryption attempts.
sourceFixture: "v2s1.manifest.json",
updates: []types.BlobInfo{
{
Digest: "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
Size: 32654,
CompressionOperation: types.Decompress,
CryptoOperation: types.Decrypt,
},
{
Digest: "sha256:3c3a4604a545cdc127456d94e421cd355bca5b528f4a9c1905b15da2eb4a4c6b",
Size: 16724,
CompressionOperation: types.Decompress,
CryptoOperation: types.Decrypt,
},
{
Digest: "sha256:ec4b8955958665577945c89419d1af06b5f7636b4ac3da7f12184802ad867736",
Size: 73109,
CompressionOperation: types.Decompress,
CryptoOperation: types.Decrypt,
},
},
expectedFixture: "", // Decryption is not supported
},
} {
manifest := manifestSchema1FromFixture(t, c.sourceFixture)

err := manifest.UpdateLayerInfos(c.updates)
if c.expectedFixture == "" {
assert.Error(t, err, c.name)
} else {
require.NoError(t, err, c.name)

updatedManifestBytes, err := manifest.Serialize()
require.NoError(t, err, c.name)

expectedManifest := manifestSchema1FromFixture(t, c.expectedFixture)
expectedManifestBytes, err := expectedManifest.Serialize()
require.NoError(t, err, c.name)

assert.Equal(t, string(expectedManifestBytes), string(updatedManifestBytes), c.name)
}
}
}

func TestSchema1ImageID(t *testing.T) {
m := manifestSchema1FromFixture(t, "schema2-to-schema1-by-docker.json")
id, err := m.ImageID(schema1FixtureLayerDiffIDs)
Expand Down
3 changes: 3 additions & 0 deletions manifest/docker_schema2.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,9 @@ func (m *Schema2) UpdateLayerInfos(layerInfos []types.BlobInfo) error {
m.LayersDescriptors[i].Digest = info.Digest
m.LayersDescriptors[i].Size = info.Size
m.LayersDescriptors[i].URLs = info.URLs
if info.CryptoOperation != types.PreserveOriginalCrypto {
return fmt.Errorf("encryption change (for layer %q) is not supported in schema2 manifests", info.Digest)
}
}
return nil
}
Expand Down
62 changes: 62 additions & 0 deletions manifest/docker_schema2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,68 @@ func TestSchema2UpdateLayerInfos(t *testing.T) {
},
expectedFixture: "v2s2.nondistributable.manifest.json",
},
{
name: "uncompressed → gzip encrypted",
sourceFixture: "v2s2.uncompressed.manifest.json",
updates: []types.BlobInfo{
{
Digest: "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
Size: 32654,
Annotations: map[string]string{"org.opencontainers.image.enc.…": "layer1"},
MediaType: DockerV2SchemaLayerMediaTypeUncompressed,
CompressionOperation: types.Compress,
CompressionAlgorithm: &compression.Gzip,
CryptoOperation: types.Encrypt,
},
{
Digest: "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
Size: 16724,
Annotations: map[string]string{"org.opencontainers.image.enc.…": "layer2"},
MediaType: DockerV2SchemaLayerMediaTypeUncompressed,
CompressionOperation: types.Compress,
CompressionAlgorithm: &compression.Gzip,
CryptoOperation: types.Encrypt,
},
{
Digest: "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
Size: 73109,
Annotations: map[string]string{"org.opencontainers.image.enc.…": "layer2"},
MediaType: DockerV2SchemaLayerMediaTypeUncompressed,
CompressionOperation: types.Compress,
CompressionAlgorithm: &compression.Gzip,
CryptoOperation: types.Encrypt,
},
},
expectedFixture: "", // Encryption is not supported
},
{
name: "gzip → uncompressed decrypted", // We can’t represent encrypted images anyway, but verify that we reject decryption attempts.
sourceFixture: "v2s2.manifest.json",
updates: []types.BlobInfo{
{
Digest: "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
Size: 32654,
MediaType: DockerV2Schema2LayerMediaType,
CompressionOperation: types.Decompress,
CryptoOperation: types.Decrypt,
},
{
Digest: "sha256:3c3a4604a545cdc127456d94e421cd355bca5b528f4a9c1905b15da2eb4a4c6b",
Size: 16724,
MediaType: DockerV2Schema2LayerMediaType,
CompressionOperation: types.Decompress,
CryptoOperation: types.Decrypt,
},
{
Digest: "sha256:ec4b8955958665577945c89419d1af06b5f7636b4ac3da7f12184802ad867736",
Size: 73109,
MediaType: DockerV2Schema2LayerMediaType,
CompressionOperation: types.Decompress,
CryptoOperation: types.Decrypt,
},
},
expectedFixture: "", // Decryption is not supported
},
} {
manifest := manifestSchema2FromFixture(t, c.sourceFixture)

Expand Down

0 comments on commit 2699a9e

Please sign in to comment.