diff --git a/digest/digest.go b/digest/digest.go index a02212163..ae581f159 100644 --- a/digest/digest.go +++ b/digest/digest.go @@ -58,6 +58,9 @@ var ( // ErrDigestInvalidFormat returned when digest format invalid. ErrDigestInvalidFormat = fmt.Errorf("invalid checksum digest format") + // ErrDigestInvalidLength returned when digest has invalid length. + ErrDigestInvalidLength = fmt.Errorf("invalid checksum digest length") + // ErrDigestUnsupported returned when the digest algorithm is unsupported. ErrDigestUnsupported = fmt.Errorf("unsupported digest algorithm") ) @@ -126,8 +129,11 @@ func (d Digest) Validate() error { return ErrDigestInvalidFormat } - switch Algorithm(s[:i]) { + switch algorithm := Algorithm(s[:i]); algorithm { case SHA256, SHA384, SHA512: + if algorithm.Size()*2 != len(s[i+1:]) { + return ErrDigestInvalidLength + } break default: return ErrDigestUnsupported diff --git a/digest/digest_test.go b/digest/digest_test.go index 41c8bee83..94c6175f4 100644 --- a/digest/digest_test.go +++ b/digest/digest_test.go @@ -53,6 +53,16 @@ func TestParseDigest(t *testing.T) { input: "sha256:d41d8cd98f00b204e9800m98ecf8427e", err: ErrDigestInvalidFormat, }, + { + // too short + input: "sha256:abcdef0123456789", + err: ErrDigestInvalidLength, + }, + { + // too short (from different algorithm) + input: "sha512:abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789", + err: ErrDigestInvalidLength, + }, { input: "foo:d41d8cd98f00b204e9800998ecf8427e", err: ErrDigestUnsupported, diff --git a/digest/digester.go b/digest/digester.go index 4f03e189b..9a10539c6 100644 --- a/digest/digester.go +++ b/digest/digester.go @@ -54,6 +54,15 @@ func (a Algorithm) String() string { return string(a) } +// Size returns number of bytes returned by the hash. +func (a Algorithm) Size() int { + h, ok := algorithms[a] + if !ok { + return 0 + } + return h.Size() +} + // Set implemented to allow use of Algorithm as a command line flag. func (a *Algorithm) Set(value string) error { if value == "" { diff --git a/digest/set_test.go b/digest/set_test.go index 0c0f650d2..e9dab8795 100644 --- a/digest/set_test.go +++ b/digest/set_test.go @@ -15,14 +15,14 @@ func assertEqualDigests(t *testing.T, d1, d2 Digest) { func TestLookup(t *testing.T) { digests := []Digest{ - "sha256:12345", - "sha256:1234", - "sha256:12346", - "sha256:54321", - "sha256:65431", - "sha256:64321", - "sha256:65421", - "sha256:65321", + "sha256:1234511111111111111111111111111111111111111111111111111111111111", + "sha256:1234111111111111111111111111111111111111111111111111111111111111", + "sha256:1234611111111111111111111111111111111111111111111111111111111111", + "sha256:5432111111111111111111111111111111111111111111111111111111111111", + "sha256:6543111111111111111111111111111111111111111111111111111111111111", + "sha256:6432111111111111111111111111111111111111111111111111111111111111", + "sha256:6542111111111111111111111111111111111111111111111111111111111111", + "sha256:6532111111111111111111111111111111111111111111111111111111111111", } dset := NewSet() @@ -55,10 +55,12 @@ func TestLookup(t *testing.T) { } dgst, err = dset.Lookup("sha256:1234") - if err != nil { + if err == nil { + t.Fatal("Expected ambiguous error looking up: sha256:1234") + } + if err != ErrDigestAmbiguous { t.Fatal(err) } - assertEqualDigests(t, dgst, digests[1]) dgst, err = dset.Lookup("sha256:12345") if err != nil { @@ -87,14 +89,14 @@ func TestLookup(t *testing.T) { func TestAddDuplication(t *testing.T) { digests := []Digest{ - "sha256:1234", - "sha256:12345", - "sha256:12346", - "sha256:54321", - "sha256:65431", - "sha512:65431", - "sha512:65421", - "sha512:65321", + "sha256:1234111111111111111111111111111111111111111111111111111111111111", + "sha256:1234511111111111111111111111111111111111111111111111111111111111", + "sha256:1234611111111111111111111111111111111111111111111111111111111111", + "sha256:5432111111111111111111111111111111111111111111111111111111111111", + "sha256:6543111111111111111111111111111111111111111111111111111111111111", + "sha512:65431111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", + "sha512:65421111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", + "sha512:65321111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", } dset := NewSet() @@ -108,7 +110,7 @@ func TestAddDuplication(t *testing.T) { t.Fatal("Invalid dset size") } - if err := dset.Add(Digest("sha256:12345")); err != nil { + if err := dset.Add(Digest("sha256:1234511111111111111111111111111111111111111111111111111111111111")); err != nil { t.Fatal(err) } @@ -116,7 +118,7 @@ func TestAddDuplication(t *testing.T) { t.Fatal("Duplicate digest insert allowed") } - if err := dset.Add(Digest("sha384:12345")); err != nil { + if err := dset.Add(Digest("sha384:123451111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111")); err != nil { t.Fatal(err) } @@ -193,14 +195,14 @@ func assertEqualShort(t *testing.T, actual, expected string) { func TestShortCodeTable(t *testing.T) { digests := []Digest{ - "sha256:1234", - "sha256:12345", - "sha256:12346", - "sha256:54321", - "sha256:65431", - "sha256:64321", - "sha256:65421", - "sha256:65321", + "sha256:1234111111111111111111111111111111111111111111111111111111111111", + "sha256:1234511111111111111111111111111111111111111111111111111111111111", + "sha256:1234611111111111111111111111111111111111111111111111111111111111", + "sha256:5432111111111111111111111111111111111111111111111111111111111111", + "sha256:6543111111111111111111111111111111111111111111111111111111111111", + "sha256:6432111111111111111111111111111111111111111111111111111111111111", + "sha256:6542111111111111111111111111111111111111111111111111111111111111", + "sha256:6532111111111111111111111111111111111111111111111111111111111111", } dset := NewSet() @@ -215,10 +217,9 @@ func TestShortCodeTable(t *testing.T) { if len(dump) < len(digests) { t.Fatalf("Error unexpected size: %d, expecting %d", len(dump), len(digests)) } - - assertEqualShort(t, dump[digests[0]], "sha256:1234") - assertEqualShort(t, dump[digests[1]], "sha256:12345") - assertEqualShort(t, dump[digests[2]], "sha256:12346") + assertEqualShort(t, dump[digests[0]], "12341") + assertEqualShort(t, dump[digests[1]], "12345") + assertEqualShort(t, dump[digests[2]], "12346") assertEqualShort(t, dump[digests[3]], "54") assertEqualShort(t, dump[digests[4]], "6543") assertEqualShort(t, dump[digests[5]], "64") diff --git a/reference/reference.go b/reference/reference.go index 3a5d36c2d..7b2cc2e9a 100644 --- a/reference/reference.go +++ b/reference/reference.go @@ -7,7 +7,7 @@ // // // repository.go // repository := hostname ['/' component]+ -// hostname := hostcomponent [':' port-number] +// hostname := hostcomponent [':' port-number] // component := subcomponent [separator subcomponent]* // subcomponent := alpha-numeric ['-'* alpha-numeric]* // hostcomponent := [hostpart '.']* hostpart @@ -24,7 +24,7 @@ // digest-algorithm := digest-algorithm-component [ digest-algorithm-separator digest-algorithm-component ] // digest-algorithm-separator := /[+.-_]/ // digest-algorithm-component := /[A-Za-z][A-Za-z0-9]*/ -// digest-hex := /[0-9a-fA-F]{32,}/ ; Atleast 128 bit digest value +// digest-hex := /[0-9a-fA-F]{32,}/ ; At least 128 bit digest value package reference import ( diff --git a/reference/reference_test.go b/reference/reference_test.go index 8e1ac1f36..cde1a7a2e 100644 --- a/reference/reference_test.go +++ b/reference/reference_test.go @@ -87,6 +87,10 @@ func TestReferenceParse(t *testing.T) { input: "@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", err: ErrReferenceInvalidFormat, }, + { + input: "repo@sha256:ffffffffffffffffffffffffffffffffff", + err: digest.ErrDigestInvalidLength, + }, { input: "validname@invaliddigest:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", err: digest.ErrDigestUnsupported, @@ -129,11 +133,11 @@ func TestReferenceParse(t *testing.T) { tag: "xn--n3h.com", }, { - input: "xn--7o8h.com/myimage:xn--7o8h.com@sha512:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", // 🐳.com in punycode + input: "xn--7o8h.com/myimage:xn--7o8h.com@sha512:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", // 🐳.com in punycode hostname: "xn--7o8h.com", repository: "xn--7o8h.com/myimage", tag: "xn--7o8h.com", - digest: "sha512:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + digest: "sha512:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", }, { input: "foo_bar.com:8080", @@ -343,9 +347,9 @@ func TestSerialization(t *testing.T) { }, { description: "name with digest", - input: "other.com/named@sha256:1234567890098765432112345667890098765", + input: "other.com/named@sha256:1234567890098765432112345667890098765432112345667890098765432112", name: "other.com/named", - digest: "sha256:1234567890098765432112345667890098765", + digest: "sha256:1234567890098765432112345667890098765432112345667890098765432112", }, } for _, testcase := range testcases { diff --git a/registry/storage/cache/cachecheck/suite.go b/registry/storage/cache/cachecheck/suite.go index ed0f95fd9..423909538 100644 --- a/registry/storage/cache/cachecheck/suite.go +++ b/registry/storage/cache/cachecheck/suite.go @@ -20,7 +20,7 @@ func CheckBlobDescriptorCache(t *testing.T, provider cache.BlobDescriptorCachePr } func checkBlobDescriptorCacheEmptyRepository(t *testing.T, ctx context.Context, provider cache.BlobDescriptorCacheProvider) { - if _, err := provider.Stat(ctx, "sha384:abc"); err != distribution.ErrBlobUnknown { + if _, err := provider.Stat(ctx, "sha384:abc111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"); err != distribution.ErrBlobUnknown { t.Fatalf("expected unknown blob error with empty store: %v", err) } @@ -41,7 +41,7 @@ func checkBlobDescriptorCacheEmptyRepository(t *testing.T, ctx context.Context, t.Fatalf("expected error with invalid digest: %v", err) } - if err := cache.SetDescriptor(ctx, "sha384:abc", distribution.Descriptor{ + if err := cache.SetDescriptor(ctx, "sha384:abc111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", distribution.Descriptor{ Digest: "", Size: 10, MediaType: "application/octet-stream"}); err == nil { @@ -52,15 +52,15 @@ func checkBlobDescriptorCacheEmptyRepository(t *testing.T, ctx context.Context, t.Fatalf("expected error checking for cache item with empty digest: %v", err) } - if _, err := cache.Stat(ctx, "sha384:abc"); err != distribution.ErrBlobUnknown { + if _, err := cache.Stat(ctx, "sha384:abc111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"); err != distribution.ErrBlobUnknown { t.Fatalf("expected unknown blob error with empty repo: %v", err) } } func checkBlobDescriptorCacheSetAndRead(t *testing.T, ctx context.Context, provider cache.BlobDescriptorCacheProvider) { - localDigest := digest.Digest("sha384:abc") + localDigest := digest.Digest("sha384:abc111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111") expected := distribution.Descriptor{ - Digest: "sha256:abc", + Digest: "sha256:abc1111111111111111111111111111111111111111111111111111111111111", Size: 10, MediaType: "application/octet-stream"} diff --git a/registry/storage/manifeststore_test.go b/registry/storage/manifeststore_test.go index 51370e173..928ce219b 100644 --- a/registry/storage/manifeststore_test.go +++ b/registry/storage/manifeststore_test.go @@ -385,15 +385,15 @@ func TestLinkPathFuncs(t *testing.T) { }{ { repo: "foo/bar", - digest: "sha256:deadbeaf", + digest: "sha256:deadbeaf98fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", linkPathFn: blobLinkPath, - expected: "/docker/registry/v2/repositories/foo/bar/_layers/sha256/deadbeaf/link", + expected: "/docker/registry/v2/repositories/foo/bar/_layers/sha256/deadbeaf98fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855/link", }, { repo: "foo/bar", - digest: "sha256:deadbeaf", + digest: "sha256:deadbeaf98fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", linkPathFn: manifestRevisionLinkPath, - expected: "/docker/registry/v2/repositories/foo/bar/_manifests/revisions/sha256/deadbeaf/link", + expected: "/docker/registry/v2/repositories/foo/bar/_manifests/revisions/sha256/deadbeaf98fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855/link", }, } { p, err := testcase.linkPathFn(testcase.repo, testcase.digest) diff --git a/registry/storage/paths_test.go b/registry/storage/paths_test.go index 9e91a3fa6..238e2f377 100644 --- a/registry/storage/paths_test.go +++ b/registry/storage/paths_test.go @@ -15,31 +15,31 @@ func TestPathMapper(t *testing.T) { { spec: manifestRevisionPathSpec{ name: "foo/bar", - revision: "sha256:abcdef0123456789", + revision: "sha256:abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789", }, - expected: "/docker/registry/v2/repositories/foo/bar/_manifests/revisions/sha256/abcdef0123456789", + expected: "/docker/registry/v2/repositories/foo/bar/_manifests/revisions/sha256/abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789", }, { spec: manifestRevisionLinkPathSpec{ name: "foo/bar", - revision: "sha256:abcdef0123456789", + revision: "sha256:abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789", }, - expected: "/docker/registry/v2/repositories/foo/bar/_manifests/revisions/sha256/abcdef0123456789/link", + expected: "/docker/registry/v2/repositories/foo/bar/_manifests/revisions/sha256/abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789/link", }, { spec: manifestSignatureLinkPathSpec{ name: "foo/bar", - revision: "sha256:abcdef0123456789", - signature: "sha256:abcdef0123456789", + revision: "sha256:abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789", + signature: "sha256:abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789", }, - expected: "/docker/registry/v2/repositories/foo/bar/_manifests/revisions/sha256/abcdef0123456789/signatures/sha256/abcdef0123456789/link", + expected: "/docker/registry/v2/repositories/foo/bar/_manifests/revisions/sha256/abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789/signatures/sha256/abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789/link", }, { spec: manifestSignaturesPathSpec{ name: "foo/bar", - revision: "sha256:abcdef0123456789", + revision: "sha256:abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789", }, - expected: "/docker/registry/v2/repositories/foo/bar/_manifests/revisions/sha256/abcdef0123456789/signatures", + expected: "/docker/registry/v2/repositories/foo/bar/_manifests/revisions/sha256/abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789/signatures", }, { spec: manifestTagsPathSpec{ @@ -72,17 +72,17 @@ func TestPathMapper(t *testing.T) { spec: manifestTagIndexEntryPathSpec{ name: "foo/bar", tag: "thetag", - revision: "sha256:abcdef0123456789", + revision: "sha256:abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789", }, - expected: "/docker/registry/v2/repositories/foo/bar/_manifests/tags/thetag/index/sha256/abcdef0123456789", + expected: "/docker/registry/v2/repositories/foo/bar/_manifests/tags/thetag/index/sha256/abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789", }, { spec: manifestTagIndexEntryLinkPathSpec{ name: "foo/bar", tag: "thetag", - revision: "sha256:abcdef0123456789", + revision: "sha256:abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789", }, - expected: "/docker/registry/v2/repositories/foo/bar/_manifests/tags/thetag/index/sha256/abcdef0123456789/link", + expected: "/docker/registry/v2/repositories/foo/bar/_manifests/tags/thetag/index/sha256/abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789/link", }, { spec: layerLinkPathSpec{ @@ -93,15 +93,15 @@ func TestPathMapper(t *testing.T) { }, { spec: blobDataPathSpec{ - digest: digest.Digest("tarsum.dev+sha512:abcdefabcdefabcdef908909909"), + digest: digest.Digest("tarsum.dev+sha512:abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"), }, - expected: "/docker/registry/v2/blobs/tarsum/dev/sha512/ab/abcdefabcdefabcdef908909909/data", + expected: "/docker/registry/v2/blobs/tarsum/dev/sha512/ab/abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789/data", }, { spec: blobDataPathSpec{ - digest: digest.Digest("tarsum.v1+sha256:abcdefabcdefabcdef908909909"), + digest: digest.Digest("tarsum.v1+sha256:abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"), }, - expected: "/docker/registry/v2/blobs/tarsum/v1/sha256/ab/abcdefabcdefabcdef908909909/data", + expected: "/docker/registry/v2/blobs/tarsum/v1/sha256/ab/abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789/data", }, {