Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions image/storage/storage_dest.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import (
"go.podman.io/storage/pkg/chunked"
"go.podman.io/storage/pkg/chunked/toc"
"go.podman.io/storage/pkg/ioutils"
supporteddigests "go.podman.io/storage/pkg/supported-digests"
)

var (
Expand Down Expand Up @@ -288,7 +289,7 @@ func (s *storageImageDestination) putBlobToPendingFile(stream io.Reader, blobinf
}
defer decompressed.Close()

diffID := digest.Canonical.Digester()
diffID := supporteddigests.TmpDigestForNewObjects().Digester()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is more complex — the “Ensure that we always see the same “view” of a layer” code will compare the value to config’s RootFS.DiffIDs, and that might use a different digest.

Potentially, I guess we might need to compute two uncompressed digests: one to match against config, and one to identify the layer in c/storage at the user-wanted level of collision resistance.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a more generic version of this concern, note that all over the place layer.UncompressedDigest and layer.TOCDigest only contains one value per layer; and options.Cache.Cache.UncompressedDigest{,ForTOC} only records one value per blob.

I don’t know what we want to do there… at least for the BlobInfoCache, we might determine that a compressed-sha256=A matches uncompressed-sha512=B (if pulling an old image configured with …ForNewObjects=sha512). Do we want to use that knowledge? Maybe we do, for future pulls (a sha256 collision means a collision at the registry), or at least for pulls from the same registry, but not for future pushes (if we have a sha512 image, we don’t want to regress to sha256 strength on uploads)??

// Copy the data to the file.
// TODO: This can take quite some time, and should ideally be cancellable using context.Context.
_, err = io.Copy(diffID.Hash(), decompressed)
Expand Down Expand Up @@ -856,7 +857,7 @@ func (s *storageImageDestination) computeID(m manifest.Manifest) (string, error)
}
// ordinaryImageID is a digest of a config, which is a JSON value.
// To avoid the risk of collisions, start the input with @ so that the input is not a valid JSON.
tocImageID := digest.FromString("@With TOC:" + tocIDInput).Encoded()
tocImageID := supporteddigests.TmpDigestForNewObjects().FromString("@With TOC:" + tocIDInput).String()
logrus.Debugf("Ordinary storage image ID %s; a layer was looked up by TOC, so using image ID %s", ordinaryImageID, tocImageID)
return tocImageID, nil
}
Expand Down Expand Up @@ -1070,7 +1071,7 @@ func layerID(parentID string, trusted trustedLayerIdentityData) string {
if parentID == "" && !mustHash {
return component
}
return digest.Canonical.FromString(parentID + "+" + component).Encoded()
return supporteddigests.TmpDigestForNewObjects().FromString(parentID + "+" + component).String()
}

// createNewLayer creates a new layer newLayerID for (index, trusted) on top of parentLayer (which may be "").
Expand Down Expand Up @@ -1488,13 +1489,13 @@ func (s *storageImageDestination) CommitWithOptions(ctx context.Context, options
imgOptions.BigData = append(imgOptions.BigData, storage.ImageBigDataOption{
Key: s.lockProtected.configDigest.String(),
Data: v,
Digest: digest.Canonical.FromBytes(v),
Digest: supporteddigests.TmpDigestForNewObjects().FromBytes(v),
})
}
// Set up to save the options.UnparsedToplevel's manifest if it differs from
// the per-platform one, which is saved below.
if !bytes.Equal(toplevelManifest, s.manifest) {
manifestDigest, err := manifest.Digest(toplevelManifest)
manifestDigest, err := manifest.DigestWithAlgorithm(toplevelManifest, supporteddigests.TmpDigestForNewObjects())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These values end up in storage.Image.Digests and are used for lookups (see “Look for an image with the specified digest that has the same name,”); so, if the user pulls a repo@digest, we must allow lookup by exactly that digest.

I don’t immediately know what we do when pulling by tag. One option might be to pre-compute and allow lookup only by (probably, Canonical and) the preferred digest. Another might be to add support for lookups by an arbitrary digest algorithm to c/storage, where c/storage would actually digest all recorded manifests using the provided algorithm (and, I guess, cache the values once computed, although that might require RW locks where RO locks used to suffice).

if err != nil {
return fmt.Errorf("digesting top-level manifest: %w", err)
}
Expand Down Expand Up @@ -1530,7 +1531,7 @@ func (s *storageImageDestination) CommitWithOptions(ctx context.Context, options
imgOptions.BigData = append(imgOptions.BigData, storage.ImageBigDataOption{
Key: "signatures",
Data: s.signatures,
Digest: digest.Canonical.FromBytes(s.signatures),
Digest: supporteddigests.TmpDigestForNewObjects().FromBytes(s.signatures),
})
}
for instanceDigest, signatures := range s.signatureses {
Expand All @@ -1541,7 +1542,7 @@ func (s *storageImageDestination) CommitWithOptions(ctx context.Context, options
imgOptions.BigData = append(imgOptions.BigData, storage.ImageBigDataOption{
Key: key,
Data: signatures,
Digest: digest.Canonical.FromBytes(signatures),
Digest: supporteddigests.TmpDigestForNewObjects().FromBytes(signatures),
})
}

Expand Down Expand Up @@ -1643,7 +1644,7 @@ func (s *storageImageDestination) CommitWithOptions(ctx context.Context, options

// PutManifest writes the manifest to the destination.
func (s *storageImageDestination) PutManifest(ctx context.Context, manifestBlob []byte, instanceDigest *digest.Digest) error {
digest, err := manifest.Digest(manifestBlob)
digest, err := manifest.DigestWithAlgorithm(manifestBlob, supporteddigests.TmpDigestForNewObjects())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See elsewhere about lookups by manifest digest.


… but, also, see what PutSignaturesWithFormat does with the value. I’m not sure that actually makes a difference, the reader seems to ignore the entry for the top-level manifest digest, but that needs tracking down.

if err != nil {
return err
}
Expand Down
118 changes: 110 additions & 8 deletions image/storage/storage_dest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,76 +6,178 @@ import (
"github.com/opencontainers/go-digest"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
supporteddigests "go.podman.io/storage/pkg/supported-digests"
)

func TestLayerID(t *testing.T) {
blobDigest, err := digest.Parse("sha256:0000000000000000000000000000000000000000000000000000000000000000")
blobDigestSHA256, err := digest.Parse("sha256:0000000000000000000000000000000000000000000000000000000000000000")
require.NoError(t, err)
blobDigestSHA512, err := digest.Parse("sha512:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
require.NoError(t, err)

for _, c := range []struct {
algorithm digest.Algorithm
blobDigest digest.Digest
parentID string
identifiedByTOC bool
diffID string
tocDigest string
expected string
}{
{
algorithm: digest.SHA256,
blobDigest: blobDigestSHA256,
parentID: "",
identifiedByTOC: false,
diffID: "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
tocDigest: "",
expected: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
},
{
algorithm: digest.SHA256,
blobDigest: blobDigestSHA256,
parentID: "",
identifiedByTOC: false,
diffID: "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
expected: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
tocDigest: "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
},
{
algorithm: digest.SHA256,
blobDigest: blobDigestSHA256,
parentID: "",
identifiedByTOC: true,
diffID: "",
tocDigest: "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
expected: "07f60ddaf18a3d1fa18a71bf40f0b9889b473e26555d6fffdfbd72ba6a59469e",
expected: "sha256:07f60ddaf18a3d1fa18a71bf40f0b9889b473e26555d6fffdfbd72ba6a59469e",
},
{
algorithm: digest.SHA256,
blobDigest: blobDigestSHA256,
parentID: "",
identifiedByTOC: true,
diffID: "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
tocDigest: "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
expected: "07f60ddaf18a3d1fa18a71bf40f0b9889b473e26555d6fffdfbd72ba6a59469e",
expected: "sha256:07f60ddaf18a3d1fa18a71bf40f0b9889b473e26555d6fffdfbd72ba6a59469e",
},
{
algorithm: digest.SHA256,
blobDigest: blobDigestSHA256,
parentID: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
identifiedByTOC: false,
diffID: "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
tocDigest: "",
expected: "76f79efda453922cda1cecb6ec9e7cf9d86ea968c6dd199d4030dd00078a1686",
expected: "sha256:76f79efda453922cda1cecb6ec9e7cf9d86ea968c6dd199d4030dd00078a1686",
},
{
algorithm: digest.SHA256,
blobDigest: blobDigestSHA256,
parentID: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
identifiedByTOC: false,
diffID: "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
tocDigest: "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
expected: "76f79efda453922cda1cecb6ec9e7cf9d86ea968c6dd199d4030dd00078a1686",
expected: "sha256:76f79efda453922cda1cecb6ec9e7cf9d86ea968c6dd199d4030dd00078a1686",
},
{
algorithm: digest.SHA256,
blobDigest: blobDigestSHA256,
parentID: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
identifiedByTOC: true,
diffID: "",
tocDigest: "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
expected: "468becc3d25ee862f81fd728d229a2b2487cfc9b3e6cf3a4d0af8c3fdde0e7a9",
expected: "sha256:468becc3d25ee862f81fd728d229a2b2487cfc9b3e6cf3a4d0af8c3fdde0e7a9",
},
{
algorithm: digest.SHA256,
blobDigest: blobDigestSHA256,
parentID: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
identifiedByTOC: true,
diffID: "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
tocDigest: "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
expected: "468becc3d25ee862f81fd728d229a2b2487cfc9b3e6cf3a4d0af8c3fdde0e7a9",
expected: "sha256:468becc3d25ee862f81fd728d229a2b2487cfc9b3e6cf3a4d0af8c3fdde0e7a9",
},
{
algorithm: digest.SHA512,
blobDigest: blobDigestSHA512,
parentID: "",
identifiedByTOC: false,
diffID: "sha512:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
tocDigest: "",
expected: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
},
{
algorithm: digest.SHA512,
blobDigest: blobDigestSHA512,
parentID: "",
identifiedByTOC: false,
diffID: "sha512:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
tocDigest: "sha512:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
expected: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
},
{
algorithm: digest.SHA512,
blobDigest: blobDigestSHA512,
parentID: "",
identifiedByTOC: true,
diffID: "",
tocDigest: "sha512:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
expected: "sha512:129fa63234ab81f43f298346bb7159cc1c8fab6d8ae93f3438f8637f5079ea6dec6ea4740eb6bdbcae3ad1eb00af76011341b2f19fe590daf6f64f06e1302f52",
},
{
algorithm: digest.SHA512,
blobDigest: blobDigestSHA512,
parentID: "",
identifiedByTOC: true,
diffID: "sha512:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
tocDigest: "sha512:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
expected: "sha512:129fa63234ab81f43f298346bb7159cc1c8fab6d8ae93f3438f8637f5079ea6dec6ea4740eb6bdbcae3ad1eb00af76011341b2f19fe590daf6f64f06e1302f52",
},
{
algorithm: digest.SHA512,
blobDigest: blobDigestSHA512,
parentID: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
identifiedByTOC: false,
diffID: "sha512:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
tocDigest: "",
expected: "sha512:391623fa853a82d2795e8648f06b7c0eb880e345138dd6b9429132d9aa329ff1e6f7b76a3295b8b34d8682090056a798901b9b700e4059b1011d32b68a7bca21",
},
{
algorithm: digest.SHA512,
blobDigest: blobDigestSHA512,
parentID: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
identifiedByTOC: false,
diffID: "sha512:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
tocDigest: "sha512:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
expected: "sha512:391623fa853a82d2795e8648f06b7c0eb880e345138dd6b9429132d9aa329ff1e6f7b76a3295b8b34d8682090056a798901b9b700e4059b1011d32b68a7bca21",
},
{
algorithm: digest.SHA512,
blobDigest: blobDigestSHA512,
parentID: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
identifiedByTOC: true,
diffID: "",
tocDigest: "sha512:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
expected: "sha512:0263be0fd3b851ffe02c877574d1d2b19625e36888e63eec2682de2210b6bbaea262e04085843eda16b7014fd59db6aafa0628c2a02443111ae568ea545b112e",
},
{
algorithm: digest.SHA512,
blobDigest: blobDigestSHA512,
parentID: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
identifiedByTOC: true,
diffID: "sha512:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
tocDigest: "sha512:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
expected: "sha512:0263be0fd3b851ffe02c877574d1d2b19625e36888e63eec2682de2210b6bbaea262e04085843eda16b7014fd59db6aafa0628c2a02443111ae568ea545b112e",
},
} {
origAlg := supporteddigests.TmpDigestForNewObjects()
defer func() {
err := supporteddigests.TmpSetDigestForNewObjects(origAlg)
require.NoError(t, err)
}()

err := supporteddigests.TmpSetDigestForNewObjects(c.algorithm)
require.NoError(t, err)

var diffID, tocDigest digest.Digest
if c.diffID != "" {
diffID, err = digest.Parse(c.diffID)
Expand All @@ -98,7 +200,7 @@ func TestLayerID(t *testing.T) {
layerIdentifiedByTOC: c.identifiedByTOC,
diffID: diffID,
tocDigest: tocDigest,
blobDigest: blobDigest,
blobDigest: c.blobDigest,
})
assert.Equal(t, c.expected, res)
}
Expand Down
Loading