Skip to content

Commit 63e8928

Browse files
committed
[sha512]: image: enable sha512 support
Use storage/pkg/supported-digests to get and set digest based on user input. Signed-off-by: Lokesh Mandvekar <[email protected]>
1 parent 94df9c1 commit 63e8928

File tree

12 files changed

+163
-42
lines changed

12 files changed

+163
-42
lines changed

image/copy/digesting_reader_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ func TestDigestingReaderRead(t *testing.T) {
3434
{[]byte(""), "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"},
3535
{[]byte("abc"), "sha256:ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"},
3636
{make([]byte, 65537), "sha256:3266304f31be278d06c3bd3eb9aa3e00c59bedec0a890de466568b0b90b0e01f"},
37+
// SHA512 test cases
38+
{[]byte(""), "sha512:cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"},
39+
{[]byte("abc"), "sha512:ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"},
40+
{make([]byte, 65537), "sha512:490821004e5a6025fe335a11f6c27b0f73cae0434bd9d2e5ac7aee3370bd421718cad7d8fbfd5f39153b6ca3b05faede68f5d6e462eeaf143bb034791ceb72ab"},
3741
}
3842
// Valid input
3943
for _, c := range cases {

image/copy/single.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"go.podman.io/image/v5/transports"
2929
"go.podman.io/image/v5/types"
3030
chunkedToc "go.podman.io/storage/pkg/chunked/toc"
31+
supportedDigests "go.podman.io/storage/pkg/supported-digests"
3132
)
3233

3334
// imageCopier tracks state specific to a single image (possibly an item of a manifest list)
@@ -987,7 +988,9 @@ func computeDiffID(stream io.Reader, decompressor compressiontypes.DecompressorF
987988
stream = s
988989
}
989990

990-
return digest.Canonical.FromReader(stream)
991+
// Use the configured digest algorithm
992+
algorithm := supportedDigests.Get()
993+
return algorithm.FromReader(stream)
991994
}
992995

993996
// algorithmsByNames returns slice of Algorithms from a sequence of Algorithm Names

image/copy/single_test.go

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"go.podman.io/image/v5/pkg/compression"
1818
compressiontypes "go.podman.io/image/v5/pkg/compression/types"
1919
"go.podman.io/image/v5/types"
20+
supportedDigests "go.podman.io/storage/pkg/supported-digests"
2021
)
2122

2223
func TestUpdatedBlobInfoFromReuse(t *testing.T) {
@@ -110,13 +111,33 @@ func goDiffIDComputationGoroutineWithTimeout(layerStream io.ReadCloser, decompre
110111
}
111112

112113
func TestDiffIDComputationGoroutine(t *testing.T) {
114+
// Test with SHA256 (default)
113115
stream, err := os.Open("fixtures/Hello.uncompressed")
114116
require.NoError(t, err)
115117
res := goDiffIDComputationGoroutineWithTimeout(stream, nil)
116118
require.NotNil(t, res)
117119
assert.NoError(t, res.err)
118120
assert.Equal(t, "sha256:185f8db32271fe25f561a6fc938b2e264306ec304eda518007d1764826381969", res.digest.String())
119121

122+
// Test with SHA512
123+
stream2, err := os.Open("fixtures/Hello.uncompressed")
124+
require.NoError(t, err)
125+
defer stream2.Close()
126+
127+
// Save original algorithm and set SHA512
128+
originalAlgorithm := supportedDigests.Get()
129+
defer func() {
130+
err := supportedDigests.Set(originalAlgorithm)
131+
require.NoError(t, err)
132+
}()
133+
err = supportedDigests.Set(digest.SHA512)
134+
require.NoError(t, err)
135+
136+
res2 := goDiffIDComputationGoroutineWithTimeout(stream2, nil)
137+
require.NotNil(t, res2)
138+
assert.NoError(t, res2.err)
139+
assert.Equal(t, "sha512:3615f80c9d293ed7402687f94b22d58e529b8cc7916f8fac7fddf7fbd5af4cf777d3d795a7a00a16bf7e7f3fb9561ee9baae480da9fe7a18769e71886b03f315", res2.digest.String())
140+
120141
// Error reading input
121142
reader, writer := io.Pipe()
122143
err = writer.CloseWithError(errors.New("Expected error reading input in diffIDComputationGoroutine"))
@@ -130,32 +151,60 @@ func TestComputeDiffID(t *testing.T) {
130151
for _, c := range []struct {
131152
filename string
132153
decompressor compressiontypes.DecompressorFunc
154+
algorithm digest.Algorithm
133155
result digest.Digest
134156
}{
135-
{"fixtures/Hello.uncompressed", nil, "sha256:185f8db32271fe25f561a6fc938b2e264306ec304eda518007d1764826381969"},
136-
{"fixtures/Hello.gz", nil, "sha256:0bd4409dcd76476a263b8f3221b4ce04eb4686dec40bfdcc2e86a7403de13609"},
137-
{"fixtures/Hello.gz", compression.GzipDecompressor, "sha256:185f8db32271fe25f561a6fc938b2e264306ec304eda518007d1764826381969"},
138-
{"fixtures/Hello.zst", nil, "sha256:361a8e0372ad438a0316eb39a290318364c10b60d0a7e55b40aa3eafafc55238"},
139-
{"fixtures/Hello.zst", compression.ZstdDecompressor, "sha256:185f8db32271fe25f561a6fc938b2e264306ec304eda518007d1764826381969"},
157+
// SHA256 test cases (default)
158+
{"fixtures/Hello.uncompressed", nil, digest.SHA256, "sha256:185f8db32271fe25f561a6fc938b2e264306ec304eda518007d1764826381969"},
159+
{"fixtures/Hello.gz", nil, digest.SHA256, "sha256:0bd4409dcd76476a263b8f3221b4ce04eb4686dec40bfdcc2e86a7403de13609"},
160+
{"fixtures/Hello.gz", compression.GzipDecompressor, digest.SHA256, "sha256:185f8db32271fe25f561a6fc938b2e264306ec304eda518007d1764826381969"},
161+
{"fixtures/Hello.zst", nil, digest.SHA256, "sha256:361a8e0372ad438a0316eb39a290318364c10b60d0a7e55b40aa3eafafc55238"},
162+
{"fixtures/Hello.zst", compression.ZstdDecompressor, digest.SHA256, "sha256:185f8db32271fe25f561a6fc938b2e264306ec304eda518007d1764826381969"},
163+
// SHA512 test cases
164+
{"fixtures/Hello.uncompressed", nil, digest.SHA512, "sha512:3615f80c9d293ed7402687f94b22d58e529b8cc7916f8fac7fddf7fbd5af4cf777d3d795a7a00a16bf7e7f3fb9561ee9baae480da9fe7a18769e71886b03f315"},
165+
{"fixtures/Hello.gz", nil, digest.SHA512, "sha512:8ee9be48dfc6274f65199847cd18ff4711f00329c5063b17cd128ba45ea1b9cea2479db0266cc1f4a3902874fdd7306f9c8a615347c0603b893fc75184fcb627"},
166+
{"fixtures/Hello.gz", compression.GzipDecompressor, digest.SHA512, "sha512:3615f80c9d293ed7402687f94b22d58e529b8cc7916f8fac7fddf7fbd5af4cf777d3d795a7a00a16bf7e7f3fb9561ee9baae480da9fe7a18769e71886b03f315"},
167+
{"fixtures/Hello.zst", nil, digest.SHA512, "sha512:e4ddd61689ce9d1cdd49e11dc8dc89ca064bdb09e85b9df56658560b8207647a78b95d04c3f5f2fb31abf13e1822f0d19307df18a3fdf88f58ef24a50e71a1ae"},
168+
{"fixtures/Hello.zst", compression.ZstdDecompressor, digest.SHA512, "sha512:3615f80c9d293ed7402687f94b22d58e529b8cc7916f8fac7fddf7fbd5af4cf777d3d795a7a00a16bf7e7f3fb9561ee9baae480da9fe7a18769e71886b03f315"},
140169
} {
141170
stream, err := os.Open(c.filename)
142171
require.NoError(t, err, c.filename)
143172
defer stream.Close()
144173

174+
// Save original algorithm and set the desired one
175+
originalAlgorithm := supportedDigests.Get()
176+
err = supportedDigests.Set(c.algorithm)
177+
require.NoError(t, err)
178+
179+
// Test the digest computation directly without ImageDestination
145180
diffID, err := computeDiffID(stream, c.decompressor)
181+
182+
// Restore the original algorithm
183+
err = supportedDigests.Set(originalAlgorithm)
184+
require.NoError(t, err)
185+
146186
require.NoError(t, err, c.filename)
147187
assert.Equal(t, c.result, diffID)
148188
}
149189

150190
// Error initializing decompression
151-
_, err := computeDiffID(bytes.NewReader([]byte{}), compression.GzipDecompressor)
191+
originalAlgorithm := supportedDigests.Get()
192+
err := supportedDigests.Set(digest.SHA256)
193+
require.NoError(t, err)
194+
_, err = computeDiffID(bytes.NewReader([]byte{}), compression.GzipDecompressor)
152195
assert.Error(t, err)
196+
err = supportedDigests.Set(originalAlgorithm)
197+
require.NoError(t, err)
153198

154199
// Error reading input
155200
reader, writer := io.Pipe()
156201
defer reader.Close()
157202
err = writer.CloseWithError(errors.New("Expected error reading input in computeDiffID"))
158203
require.NoError(t, err)
204+
err = supportedDigests.Set(digest.SHA256)
205+
require.NoError(t, err)
159206
_, err = computeDiffID(reader, nil)
160207
assert.Error(t, err)
208+
err = supportedDigests.Set(originalAlgorithm)
209+
require.NoError(t, err)
161210
}

image/internal/image/docker_schema2.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,12 @@ func (m *manifestSchema2) ConfigBlob(ctx context.Context) ([]byte, error) {
110110
if err != nil {
111111
return nil, err
112112
}
113-
computedDigest := digest.FromBytes(blob)
114-
if computedDigest != m.m.ConfigDescriptor.Digest {
115-
return nil, fmt.Errorf("Download config.json digest %s does not match expected %s", computedDigest, m.m.ConfigDescriptor.Digest)
113+
// Use the same algorithm as the expected digest
114+
expectedDigest := m.m.ConfigDescriptor.Digest
115+
algorithm := expectedDigest.Algorithm()
116+
computedDigest := algorithm.FromBytes(blob)
117+
if computedDigest != expectedDigest {
118+
return nil, fmt.Errorf("Download config.json digest %s does not match expected %s", computedDigest, expectedDigest)
116119
}
117120
m.configBlob = blob
118121
}

image/internal/image/oci.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
"slices"
99

1010
ociencspec "github.com/containers/ocicrypt/spec"
11-
"github.com/opencontainers/go-digest"
1211
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
1312
"go.podman.io/image/v5/docker/reference"
1413
"go.podman.io/image/v5/internal/iolimits"
@@ -74,9 +73,12 @@ func (m *manifestOCI1) ConfigBlob(ctx context.Context) ([]byte, error) {
7473
if err != nil {
7574
return nil, err
7675
}
77-
computedDigest := digest.FromBytes(blob)
78-
if computedDigest != m.m.Config.Digest {
79-
return nil, fmt.Errorf("Download config.json digest %s does not match expected %s", computedDigest, m.m.Config.Digest)
76+
// Use the same algorithm as the expected digest
77+
expectedDigest := m.m.Config.Digest
78+
algorithm := expectedDigest.Algorithm()
79+
computedDigest := algorithm.FromBytes(blob)
80+
if computedDigest != expectedDigest {
81+
return nil, fmt.Errorf("Download config.json digest %s does not match expected %s", computedDigest, expectedDigest)
8082
}
8183
m.configBlob = blob
8284
}

image/internal/manifest/manifest_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,26 @@ func TestDigest(t *testing.T) {
7171
actualDigest, err := Digest([]byte{})
7272
require.NoError(t, err)
7373
assert.Equal(t, digest.Digest(digestSha256EmptyTar), actualDigest)
74+
75+
// Test SHA512 digest computation
76+
testData := []byte("test manifest data")
77+
78+
// Test SHA256 digest
79+
sha256Digest := digest.SHA256.FromBytes(testData)
80+
actualDigest, err = Digest(testData)
81+
require.NoError(t, err)
82+
assert.Equal(t, sha256Digest, actualDigest)
83+
84+
// Test that we can compute SHA512 digest directly
85+
sha512Digest := digest.SHA512.FromBytes(testData)
86+
assert.NotEqual(t, sha256Digest, sha512Digest)
87+
assert.Equal(t, "sha512", sha512Digest.Algorithm().String())
88+
assert.Equal(t, "sha256", sha256Digest.Algorithm().String())
89+
90+
// Test empty data with SHA512
91+
emptySha512Digest := digest.SHA512.FromBytes([]byte{})
92+
emptySha256Digest := digest.SHA256.FromBytes([]byte{})
93+
assert.NotEqual(t, emptySha256Digest, emptySha512Digest)
7494
}
7595

7696
func TestMatchesDigest(t *testing.T) {
@@ -87,6 +107,9 @@ func TestMatchesDigest(t *testing.T) {
87107
{"v2s1.manifest.json", TestDockerV2S2ManifestDigest, false},
88108
// Unrecognized algorithm
89109
{"v2s2.manifest.json", digest.Digest("md5:2872f31c5c1f62a694fbd20c1e85257c"), false},
110+
// SHA512 test cases
111+
{"v2s2.manifest.json", digest.SHA512.FromBytes([]byte("test")), false},
112+
{"v2s1.manifest.json", digest.SHA512.FromBytes([]byte("test")), false},
90113
// Mangled format
91114
{"v2s2.manifest.json", digest.Digest(TestDockerV2S2ManifestDigest.String() + "abc"), false},
92115
{"v2s2.manifest.json", digest.Digest(TestDockerV2S2ManifestDigest.String()[:20]), false},

image/internal/putblobdigest/put_blob_digest.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55

66
"github.com/opencontainers/go-digest"
77
"go.podman.io/image/v5/types"
8+
supportedDigests "go.podman.io/storage/pkg/supported-digests"
89
)
910

1011
// Digester computes a digest of the provided stream, if not known yet.
@@ -13,15 +14,15 @@ type Digester struct {
1314
digester digest.Digester // Or nil
1415
}
1516

16-
// newDigester initiates computation of a digest.Canonical digest of stream,
17+
// newDigester initiates computation of a digest using the configured algorithm of stream,
1718
// if !validDigest; otherwise it just records knownDigest to be returned later.
1819
// The caller MUST use the returned stream instead of the original value.
1920
func newDigester(stream io.Reader, knownDigest digest.Digest, validDigest bool) (Digester, io.Reader) {
2021
if validDigest {
2122
return Digester{knownDigest: knownDigest}, stream
2223
} else {
2324
res := Digester{
24-
digester: digest.Canonical.Digester(),
25+
digester: supportedDigests.Get().Digester(),
2526
}
2627
stream = io.TeeReader(stream, res.digester.Hash())
2728
return res, stream
@@ -37,13 +38,14 @@ func DigestIfUnknown(stream io.Reader, blobInfo types.BlobInfo) (Digester, io.Re
3738
return newDigester(stream, d, d != "")
3839
}
3940

40-
// DigestIfCanonicalUnknown initiates computation of a digest.Canonical digest of stream,
41-
// if a digest.Canonical digest is not supplied in the provided blobInfo;
41+
// DigestIfCanonicalUnknown initiates computation of a digest using the configured algorithm of stream,
42+
// if a digest with the configured algorithm is not supplied in the provided blobInfo;
4243
// otherwise blobInfo.Digest will be used.
4344
// The caller MUST use the returned stream instead of the original value.
4445
func DigestIfCanonicalUnknown(stream io.Reader, blobInfo types.BlobInfo) (Digester, io.Reader) {
4546
d := blobInfo.Digest
46-
return newDigester(stream, d, d != "" && d.Algorithm() == digest.Canonical)
47+
configuredAlgorithm := supportedDigests.Get()
48+
return newDigester(stream, d, d != "" && d.Algorithm() == configuredAlgorithm)
4749
}
4850

4951
// Digest() returns a digest value possibly computed by Digester.

image/internal/putblobdigest/put_blob_digest_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ func TestDigestIfUnknown(t *testing.T) {
4040
computesDigest: false,
4141
expectedDigest: digest.Digest("sha256:uninspected-value"),
4242
},
43+
{
44+
inputDigest: digest.Digest("sha512:uninspected-value"),
45+
computesDigest: false,
46+
expectedDigest: digest.Digest("sha512:uninspected-value"),
47+
},
4348
{
4449
inputDigest: digest.Digest("unknown-algorithm:uninspected-value"),
4550
computesDigest: false,
@@ -60,6 +65,11 @@ func TestDigestIfCanonicalUnknown(t *testing.T) {
6065
computesDigest: false,
6166
expectedDigest: digest.Digest("sha256:uninspected-value"),
6267
},
68+
{
69+
inputDigest: digest.Digest("sha512:uninspected-value"),
70+
computesDigest: true,
71+
expectedDigest: digest.Canonical.FromBytes(testData),
72+
},
6373
{
6474
inputDigest: digest.Digest("unknown-algorithm:uninspected-value"),
6575
computesDigest: true,

image/pkg/blobcache/dest.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"go.podman.io/image/v5/types"
2222
"go.podman.io/storage/pkg/archive"
2323
"go.podman.io/storage/pkg/ioutils"
24+
supportedDigests "go.podman.io/storage/pkg/supported-digests"
2425
)
2526

2627
type blobCacheDestination struct {
@@ -92,7 +93,7 @@ func (d *blobCacheDestination) saveStream(wg *sync.WaitGroup, decompressReader i
9293
}
9394
}()
9495

95-
digester := digest.Canonical.Digester()
96+
digester := supportedDigests.Get().Digester()
9697
if err := func() error { // A scope for defer
9798
defer tempFile.Close()
9899

0 commit comments

Comments
 (0)