Merge pull request #4064 from thaJeztah/2.8_backport_nodigestset

[release/2.8] digestset: deprecate package in favor of go-digest/digestset
This commit is contained in:
Milos Gajdos 2023-09-22 14:35:45 +01:00 committed by GitHub
commit 2b76378843
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 247 additions and 440 deletions

51
digestset/deprecated.go Normal file
View file

@ -0,0 +1,51 @@
package digestset
import (
"github.com/opencontainers/go-digest"
"github.com/opencontainers/go-digest/digestset"
)
// ErrDigestNotFound is used when a matching digest
// could not be found in a set.
//
// Deprecated: use [digestset.ErrDigestNotFound].
var ErrDigestNotFound = digestset.ErrDigestNotFound
// ErrDigestAmbiguous is used when multiple digests
// are found in a set. None of the matching digests
// should be considered valid matches.
//
// Deprecated: use [digestset.ErrDigestAmbiguous].
var ErrDigestAmbiguous = digestset.ErrDigestAmbiguous
// Set is used to hold a unique set of digests which
// may be easily referenced by a string
// representation of the digest as well as short representation.
// The uniqueness of the short representation is based on other
// digests in the set. If digests are omitted from this set,
// collisions in a larger set may not be detected, therefore it
// is important to always do short representation lookups on
// the complete set of digests. To mitigate collisions, an
// appropriately long short code should be used.
//
// Deprecated: use [digestset.Set].
type Set = digestset.Set
// NewSet creates an empty set of digests
// which may have digests added.
//
// Deprecated: use [digestset.NewSet].
func NewSet() *digestset.Set {
return digestset.NewSet()
}
// ShortCodeTable returns a map of Digest to unique short codes. The
// length represents the minimum value, the maximum length may be the
// entire value of digest if uniqueness cannot be achieved without the
// full value. This function will attempt to make short codes as short
// as possible to be unique.
//
// Deprecated: use [digestset.ShortCodeTable].
func ShortCodeTable(dst *digestset.Set, length int) map[digest.Digest]string {
return digestset.ShortCodeTable(dst, length)
}

View file

@ -1,371 +0,0 @@
package digestset
import (
"crypto/sha256"
_ "crypto/sha512"
"encoding/binary"
"math/rand"
"testing"
digest "github.com/opencontainers/go-digest"
)
func assertEqualDigests(t *testing.T, d1, d2 digest.Digest) {
if d1 != d2 {
t.Fatalf("Digests do not match:\n\tActual: %s\n\tExpected: %s", d1, d2)
}
}
func TestLookup(t *testing.T) {
digests := []digest.Digest{
"sha256:1234511111111111111111111111111111111111111111111111111111111111",
"sha256:1234111111111111111111111111111111111111111111111111111111111111",
"sha256:1234611111111111111111111111111111111111111111111111111111111111",
"sha256:5432111111111111111111111111111111111111111111111111111111111111",
"sha256:6543111111111111111111111111111111111111111111111111111111111111",
"sha256:6432111111111111111111111111111111111111111111111111111111111111",
"sha256:6542111111111111111111111111111111111111111111111111111111111111",
"sha256:6532111111111111111111111111111111111111111111111111111111111111",
}
dset := NewSet()
for i := range digests {
if err := dset.Add(digests[i]); err != nil {
t.Fatal(err)
}
}
dgst, err := dset.Lookup("54")
if err != nil {
t.Fatal(err)
}
assertEqualDigests(t, dgst, digests[3])
_, err = dset.Lookup("1234")
if err == nil {
t.Fatal("Expected ambiguous error looking up: 1234")
}
if err != ErrDigestAmbiguous {
t.Fatal(err)
}
_, err = dset.Lookup("9876")
if err == nil {
t.Fatal("Expected not found error looking up: 9876")
}
if err != ErrDigestNotFound {
t.Fatal(err)
}
_, err = dset.Lookup("sha256:1234")
if err == nil {
t.Fatal("Expected ambiguous error looking up: sha256:1234")
}
if err != ErrDigestAmbiguous {
t.Fatal(err)
}
dgst, err = dset.Lookup("sha256:12345")
if err != nil {
t.Fatal(err)
}
assertEqualDigests(t, dgst, digests[0])
dgst, err = dset.Lookup("sha256:12346")
if err != nil {
t.Fatal(err)
}
assertEqualDigests(t, dgst, digests[2])
dgst, err = dset.Lookup("12346")
if err != nil {
t.Fatal(err)
}
assertEqualDigests(t, dgst, digests[2])
dgst, err = dset.Lookup("12345")
if err != nil {
t.Fatal(err)
}
assertEqualDigests(t, dgst, digests[0])
}
func TestAddDuplication(t *testing.T) {
digests := []digest.Digest{
"sha256:1234111111111111111111111111111111111111111111111111111111111111",
"sha256:1234511111111111111111111111111111111111111111111111111111111111",
"sha256:1234611111111111111111111111111111111111111111111111111111111111",
"sha256:5432111111111111111111111111111111111111111111111111111111111111",
"sha256:6543111111111111111111111111111111111111111111111111111111111111",
"sha512:65431111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111",
"sha512:65421111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111",
"sha512:65321111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111",
}
dset := NewSet()
for i := range digests {
if err := dset.Add(digests[i]); err != nil {
t.Fatal(err)
}
}
if len(dset.entries) != 8 {
t.Fatal("Invalid dset size")
}
if err := dset.Add(digest.Digest("sha256:1234511111111111111111111111111111111111111111111111111111111111")); err != nil {
t.Fatal(err)
}
if len(dset.entries) != 8 {
t.Fatal("Duplicate digest insert should not increase entries size")
}
if err := dset.Add(digest.Digest("sha384:123451111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111")); err != nil {
t.Fatal(err)
}
if len(dset.entries) != 9 {
t.Fatal("Insert with different algorithm should be allowed")
}
}
func TestRemove(t *testing.T) {
digests, err := createDigests(10)
if err != nil {
t.Fatal(err)
}
dset := NewSet()
for i := range digests {
if err := dset.Add(digests[i]); err != nil {
t.Fatal(err)
}
}
dgst, err := dset.Lookup(digests[0].String())
if err != nil {
t.Fatal(err)
}
if dgst != digests[0] {
t.Fatalf("Unexpected digest value:\n\tExpected: %s\n\tActual: %s", digests[0], dgst)
}
if err := dset.Remove(digests[0]); err != nil {
t.Fatal(err)
}
if _, err := dset.Lookup(digests[0].String()); err != ErrDigestNotFound {
t.Fatalf("Expected error %v when looking up removed digest, got %v", ErrDigestNotFound, err)
}
}
func TestAll(t *testing.T) {
digests, err := createDigests(100)
if err != nil {
t.Fatal(err)
}
dset := NewSet()
for i := range digests {
if err := dset.Add(digests[i]); err != nil {
t.Fatal(err)
}
}
all := map[digest.Digest]struct{}{}
for _, dgst := range dset.All() {
all[dgst] = struct{}{}
}
if len(all) != len(digests) {
t.Fatalf("Unexpected number of unique digests found:\n\tExpected: %d\n\tActual: %d", len(digests), len(all))
}
for i, dgst := range digests {
if _, ok := all[dgst]; !ok {
t.Fatalf("Missing element at position %d: %s", i, dgst)
}
}
}
func assertEqualShort(t *testing.T, actual, expected string) {
if actual != expected {
t.Fatalf("Unexpected short value:\n\tExpected: %s\n\tActual: %s", expected, actual)
}
}
func TestShortCodeTable(t *testing.T) {
digests := []digest.Digest{
"sha256:1234111111111111111111111111111111111111111111111111111111111111",
"sha256:1234511111111111111111111111111111111111111111111111111111111111",
"sha256:1234611111111111111111111111111111111111111111111111111111111111",
"sha256:5432111111111111111111111111111111111111111111111111111111111111",
"sha256:6543111111111111111111111111111111111111111111111111111111111111",
"sha256:6432111111111111111111111111111111111111111111111111111111111111",
"sha256:6542111111111111111111111111111111111111111111111111111111111111",
"sha256:6532111111111111111111111111111111111111111111111111111111111111",
}
dset := NewSet()
for i := range digests {
if err := dset.Add(digests[i]); err != nil {
t.Fatal(err)
}
}
dump := ShortCodeTable(dset, 2)
if len(dump) < len(digests) {
t.Fatalf("Error unexpected size: %d, expecting %d", len(dump), len(digests))
}
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")
assertEqualShort(t, dump[digests[6]], "6542")
assertEqualShort(t, dump[digests[7]], "653")
}
func createDigests(count int) ([]digest.Digest, error) {
r := rand.New(rand.NewSource(25823))
digests := make([]digest.Digest, count)
for i := range digests {
h := sha256.New()
if err := binary.Write(h, binary.BigEndian, r.Int63()); err != nil {
return nil, err
}
digests[i] = digest.NewDigest("sha256", h)
}
return digests, nil
}
func benchAddNTable(b *testing.B, n int) {
digests, err := createDigests(n)
if err != nil {
b.Fatal(err)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
dset := &Set{entries: digestEntries(make([]*digestEntry, 0, n))}
for j := range digests {
if err = dset.Add(digests[j]); err != nil {
b.Fatal(err)
}
}
}
}
func benchLookupNTable(b *testing.B, n int, shortLen int) {
digests, err := createDigests(n)
if err != nil {
b.Fatal(err)
}
dset := &Set{entries: digestEntries(make([]*digestEntry, 0, n))}
for i := range digests {
if err := dset.Add(digests[i]); err != nil {
b.Fatal(err)
}
}
shorts := make([]string, 0, n)
for _, short := range ShortCodeTable(dset, shortLen) {
shorts = append(shorts, short)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
if _, err = dset.Lookup(shorts[i%n]); err != nil {
b.Fatal(err)
}
}
}
func benchRemoveNTable(b *testing.B, n int) {
digests, err := createDigests(n)
if err != nil {
b.Fatal(err)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
dset := &Set{entries: digestEntries(make([]*digestEntry, 0, n))}
b.StopTimer()
for j := range digests {
if err = dset.Add(digests[j]); err != nil {
b.Fatal(err)
}
}
b.StartTimer()
for j := range digests {
if err = dset.Remove(digests[j]); err != nil {
b.Fatal(err)
}
}
}
}
func benchShortCodeNTable(b *testing.B, n int, shortLen int) {
digests, err := createDigests(n)
if err != nil {
b.Fatal(err)
}
dset := &Set{entries: digestEntries(make([]*digestEntry, 0, n))}
for i := range digests {
if err := dset.Add(digests[i]); err != nil {
b.Fatal(err)
}
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
ShortCodeTable(dset, shortLen)
}
}
func BenchmarkAdd10(b *testing.B) {
benchAddNTable(b, 10)
}
func BenchmarkAdd100(b *testing.B) {
benchAddNTable(b, 100)
}
func BenchmarkAdd1000(b *testing.B) {
benchAddNTable(b, 1000)
}
func BenchmarkRemove10(b *testing.B) {
benchRemoveNTable(b, 10)
}
func BenchmarkRemove100(b *testing.B) {
benchRemoveNTable(b, 100)
}
func BenchmarkRemove1000(b *testing.B) {
benchRemoveNTable(b, 1000)
}
func BenchmarkLookup10(b *testing.B) {
benchLookupNTable(b, 10, 12)
}
func BenchmarkLookup100(b *testing.B) {
benchLookupNTable(b, 100, 12)
}
func BenchmarkLookup1000(b *testing.B) {
benchLookupNTable(b, 1000, 12)
}
func BenchmarkShortCode10(b *testing.B) {
benchShortCodeNTable(b, 10, 12)
}
func BenchmarkShortCode100(b *testing.B) {
benchShortCodeNTable(b, 100, 12)
}
func BenchmarkShortCode1000(b *testing.B) {
benchShortCodeNTable(b, 1000, 12)
}

View file

@ -4,8 +4,8 @@ import (
"regexp"
"github.com/distribution/reference"
"github.com/docker/distribution/digestset"
"github.com/opencontainers/go-digest"
"github.com/opencontainers/go-digest/digestset"
)
// ParseNormalizedNamed parses a string into a named reference

View file

@ -48,5 +48,5 @@ gopkg.in/check.v1 64131543e7896d5bcc6bd5a76287eb75ea96c673
gopkg.in/square/go-jose.v1 40d457b439244b546f023d056628e5184136899b
gopkg.in/yaml.v2 v2.2.1
rsc.io/letsencrypt e770c10b0f1a64775ae91d240407ce00d1a5bdeb https://github.com/dmcgowan/letsencrypt.git
github.com/opencontainers/go-digest a6d0ee40d4207ea02364bd3b9e8e77b9159ba1eb
github.com/opencontainers/go-digest ea51bea511f75cfa3ef6098cc253c5c3609b037a # v1.0.0
github.com/opencontainers/image-spec 67d2d5658fe0476ab9bf414cec164077ebff3920 # v1.0.2

View file

@ -176,6 +176,7 @@
END OF TERMS AND CONDITIONS
Copyright 2019, 2020 OCI Contributors
Copyright 2016 Docker, Inc.
Licensed under the Apache License, Version 2.0 (the "License");

View file

@ -1,27 +1,23 @@
# go-digest
[![GoDoc](https://godoc.org/github.com/docker/go-digest?status.svg)](https://godoc.org/github.com/docker/go-digest) [![Go Report Card](https://goreportcard.com/badge/github.com/docker/go-digest)](https://goreportcard.com/report/github.com/docker/go-digest) [![Build Status](https://travis-ci.org/docker/go-digest.svg?branch=master)](https://travis-ci.org/docker/go-digest)
[![GoDoc](https://godoc.org/github.com/opencontainers/go-digest?status.svg)](https://godoc.org/github.com/opencontainers/go-digest) [![Go Report Card](https://goreportcard.com/badge/github.com/opencontainers/go-digest)](https://goreportcard.com/report/github.com/opencontainers/go-digest) [![Build Status](https://travis-ci.org/opencontainers/go-digest.svg?branch=master)](https://travis-ci.org/opencontainers/go-digest)
Common digest package used across the container ecosystem.
Please see the [godoc](https://godoc.org/github.com/docker/go-digest) for more information.
Please see the [godoc](https://godoc.org/github.com/opencontainers/go-digest) for more information.
# What is a digest?
A digest is just a hash.
A digest is just a [hash](https://en.wikipedia.org/wiki/Hash_function).
The most common use case for a digest is to create a content
identifier for use in [Content Addressable Storage](https://en.wikipedia.org/wiki/Content-addressable_storage)
systems:
The most common use case for a digest is to create a content identifier for use in [Content Addressable Storage](https://en.wikipedia.org/wiki/Content-addressable_storage) systems:
```go
id := digest.FromBytes([]byte("my content"))
```
In the example above, the id can be used to uniquely identify
the byte slice "my content". This allows two disparate applications
to agree on a verifiable identifier without having to trust one
another.
In the example above, the id can be used to uniquely identify the byte slice "my content".
This allows two disparate applications to agree on a verifiable identifier without having to trust one another.
An identifying digest can be verified, as follows:
@ -31,8 +27,7 @@ if id != digest.FromBytes([]byte("my content")) {
}
```
A `Verifier` type can be used to handle cases where an `io.Reader`
makes more sense:
A `Verifier` type can be used to handle cases where an `io.Reader` makes more sense:
```go
rd := getContent()
@ -44,61 +39,58 @@ if !verifier.Verified() {
}
```
Using [Merkle DAGs](https://en.wikipedia.org/wiki/Merkle_tree), this
can power a rich, safe, content distribution system.
Using [Merkle DAGs](https://en.wikipedia.org/wiki/Merkle_tree), this can power a rich, safe, content distribution system.
# Usage
While the [godoc](https://godoc.org/github.com/docker/go-digest) is
considered the best resource, a few important items need to be called
out when using this package.
While the [godoc](https://godoc.org/github.com/opencontainers/go-digest) is considered the best resource, a few important items need to be called out when using this package.
1. Make sure to import the hash implementations into your application
or the package will panic. You should have something like the
following in the main (or other entrypoint) of your application:
1. Make sure to import the hash implementations into your application or the package will panic.
You should have something like the following in the main (or other entrypoint) of your application:
```go
import (
_ "crypto/sha256"
_ "crypto/sha512"
_ "crypto/sha512"
)
```
This may seem inconvenient but it allows you replace the hash
implementations with others, such as https://github.com/stevvooe/resumable.
2. Even though `digest.Digest` may be assemable as a string, _always_
verify your input with `digest.Parse` or use `Digest.Validate`
when accepting untrusted input. While there are measures to
avoid common problems, this will ensure you have valid digests
in the rest of your application.
2. Even though `digest.Digest` may be assemblable as a string, _always_ verify your input with `digest.Parse` or use `Digest.Validate` when accepting untrusted input.
While there are measures to avoid common problems, this will ensure you have valid digests in the rest of your application.
3. While alternative encodings of hash values (digests) are possible (for example, base64), this package deals exclusively with hex-encoded digests.
# Stability
The Go API, at this stage, is considered stable, unless otherwise noted.
As always, before using a package export, read the [godoc](https://godoc.org/github.com/docker/go-digest).
As always, before using a package export, read the [godoc](https://godoc.org/github.com/opencontainers/go-digest).
# Contributing
This package is considered fairly complete. It has been in production
in thousands (millions?) of deployments and is fairly battle-hardened.
New additions will be met with skepticism. If you think there is a
missing feature, please file a bug clearly describing the problem and
the alternatives you tried before submitting a PR.
This package is considered fairly complete.
It has been in production in thousands (millions?) of deployments and is fairly battle-hardened.
New additions will be met with skepticism.
If you think there is a missing feature, please file a bug clearly describing the problem and the alternatives you tried before submitting a PR.
# Reporting security issues
## Code of Conduct
The maintainers take security seriously. If you discover a security
issue, please bring it to their attention right away!
Participation in the OpenContainers community is governed by [OpenContainer's Code of Conduct][code-of-conduct].
Please DO NOT file a public issue, instead send your report privately
to security@docker.com.
## Security
Security reports are greatly appreciated and we will publicly thank you
for it. We also like to send gifts—if you're into Docker schwag, make
sure to let us know. We currently do not offer a paid security bounty
program, but are not ruling it out in the future.
If you find an issue, please follow the [security][security] protocol to report it.
# Copyright and license
Copyright © 2016 Docker, Inc. All rights reserved, except as follows. Code is released under the [Apache 2.0 license](LICENSE.code). This `README.md` file and the [`CONTRIBUTING.md`](CONTRIBUTING.md) file are licensed under the Creative Commons Attribution 4.0 International License under the terms and conditions set forth in the file [`LICENSE.docs`](LICENSE.docs). You may obtain a duplicate copy of the same license, titled CC BY-SA 4.0, at http://creativecommons.org/licenses/by-sa/4.0/.
Copyright © 2019, 2020 OCI Contributors
Copyright © 2016 Docker, Inc.
All rights reserved, except as follows.
Code is released under the [Apache 2.0 license](LICENSE).
This `README.md` file and the [`CONTRIBUTING.md`](CONTRIBUTING.md) file are licensed under the Creative Commons Attribution 4.0 International License under the terms and conditions set forth in the file [`LICENSE.docs`](LICENSE.docs).
You may obtain a duplicate copy of the same license, titled CC BY-SA 4.0, at http://creativecommons.org/licenses/by-sa/4.0/.
[security]: https://github.com/opencontainers/org/blob/master/security
[code-of-conduct]: https://github.com/opencontainers/org/blob/master/CODE_OF_CONDUCT.md

View file

@ -1,3 +1,18 @@
// Copyright 2019, 2020 OCI Contributors
// Copyright 2017 Docker, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package digest
import (
@ -5,6 +20,7 @@ import (
"fmt"
"hash"
"io"
"regexp"
)
// Algorithm identifies and implementation of a digester by an identifier.
@ -14,9 +30,9 @@ type Algorithm string
// supported digest types
const (
SHA256 Algorithm = "sha256" // sha256 with hex encoding
SHA384 Algorithm = "sha384" // sha384 with hex encoding
SHA512 Algorithm = "sha512" // sha512 with hex encoding
SHA256 Algorithm = "sha256" // sha256 with hex encoding (lower case only)
SHA384 Algorithm = "sha384" // sha384 with hex encoding (lower case only)
SHA512 Algorithm = "sha512" // sha512 with hex encoding (lower case only)
// Canonical is the primary digest algorithm used with the distribution
// project. Other digests may be used but this one is the primary storage
@ -36,6 +52,14 @@ var (
SHA384: crypto.SHA384,
SHA512: crypto.SHA512,
}
// anchoredEncodedRegexps contains anchored regular expressions for hex-encoded digests.
// Note that /A-F/ disallowed.
anchoredEncodedRegexps = map[Algorithm]*regexp.Regexp{
SHA256: regexp.MustCompile(`^[a-f0-9]{64}$`),
SHA384: regexp.MustCompile(`^[a-f0-9]{96}$`),
SHA512: regexp.MustCompile(`^[a-f0-9]{128}$`),
}
)
// Available returns true if the digest type is available for use. If this
@ -111,6 +135,14 @@ func (a Algorithm) Hash() hash.Hash {
return algorithms[a].New()
}
// Encode encodes the raw bytes of a digest, typically from a hash.Hash, into
// the encoded portion of the digest.
func (a Algorithm) Encode(d []byte) string {
// TODO(stevvooe): Currently, all algorithms use a hex encoding. When we
// add support for back registration, we can modify this accordingly.
return fmt.Sprintf("%x", d)
}
// FromReader returns the digest of the reader using the algorithm.
func (a Algorithm) FromReader(rd io.Reader) (Digest, error) {
digester := a.Digester()
@ -142,3 +174,20 @@ func (a Algorithm) FromBytes(p []byte) Digest {
func (a Algorithm) FromString(s string) Digest {
return a.FromBytes([]byte(s))
}
// Validate validates the encoded portion string
func (a Algorithm) Validate(encoded string) error {
r, ok := anchoredEncodedRegexps[a]
if !ok {
return ErrDigestUnsupported
}
// Digests much always be hex-encoded, ensuring that their hex portion will
// always be size*2
if a.Size()*2 != len(encoded) {
return ErrDigestInvalidLength
}
if r.MatchString(encoded) {
return nil
}
return ErrDigestInvalidFormat
}

View file

@ -1,3 +1,18 @@
// Copyright 2019, 2020 OCI Contributors
// Copyright 2017 Docker, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package digest
import (
@ -31,16 +46,21 @@ func NewDigest(alg Algorithm, h hash.Hash) Digest {
// functions. This is also useful for rebuilding digests from binary
// serializations.
func NewDigestFromBytes(alg Algorithm, p []byte) Digest {
return Digest(fmt.Sprintf("%s:%x", alg, p))
return NewDigestFromEncoded(alg, alg.Encode(p))
}
// NewDigestFromHex returns a Digest from alg and a the hex encoded digest.
// NewDigestFromHex is deprecated. Please use NewDigestFromEncoded.
func NewDigestFromHex(alg, hex string) Digest {
return Digest(fmt.Sprintf("%s:%s", alg, hex))
return NewDigestFromEncoded(Algorithm(alg), hex)
}
// NewDigestFromEncoded returns a Digest from alg and the encoded digest.
func NewDigestFromEncoded(alg Algorithm, encoded string) Digest {
return Digest(fmt.Sprintf("%s:%s", alg, encoded))
}
// DigestRegexp matches valid digest types.
var DigestRegexp = regexp.MustCompile(`[a-zA-Z0-9-_+.]+:[a-fA-F0-9]+`)
var DigestRegexp = regexp.MustCompile(`[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+`)
// DigestRegexpAnchored matches valid digest types, anchored to the start and end of the match.
var DigestRegexpAnchored = regexp.MustCompile(`^` + DigestRegexp.String() + `$`)
@ -82,26 +102,18 @@ func FromString(s string) Digest {
// error if not.
func (d Digest) Validate() error {
s := string(d)
i := strings.Index(s, ":")
// validate i then run through regexp
if i < 0 || i+1 == len(s) || !DigestRegexpAnchored.MatchString(s) {
if i <= 0 || i+1 == len(s) {
return ErrDigestInvalidFormat
}
algorithm := Algorithm(s[:i])
algorithm, encoded := Algorithm(s[:i]), s[i+1:]
if !algorithm.Available() {
if !DigestRegexpAnchored.MatchString(s) {
return ErrDigestInvalidFormat
}
return ErrDigestUnsupported
}
// Digests much always be hex-encoded, ensuring that their hex portion will
// always be size*2
if algorithm.Size()*2 != len(s[i+1:]) {
return ErrDigestInvalidLength
}
return nil
return algorithm.Validate(encoded)
}
// Algorithm returns the algorithm portion of the digest. This will panic if
@ -119,12 +131,17 @@ func (d Digest) Verifier() Verifier {
}
}
// Hex returns the hex digest portion of the digest. This will panic if the
// Encoded returns the encoded portion of the digest. This will panic if the
// underlying digest is not in a valid format.
func (d Digest) Hex() string {
func (d Digest) Encoded() string {
return string(d[d.sepIndex()+1:])
}
// Hex is deprecated. Please use Digest.Encoded.
func (d Digest) Hex() string {
return d.Encoded()
}
func (d Digest) String() string {
return string(d)
}

View file

@ -1,3 +1,18 @@
// Copyright 2019, 2020 OCI Contributors
// Copyright 2017 Docker, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package digest
import "hash"

View file

@ -1,3 +1,18 @@
// Copyright 2020, 2020 OCI Contributors
// Copyright 2017 Docker, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package digestset
import (

View file

@ -1,3 +1,18 @@
// Copyright 2019, 2020 OCI Contributors
// Copyright 2017 Docker, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package digest provides a generalized type to opaquely represent message
// digests and their operations within the registry. The Digest type is
// designed to serve as a flexible identifier in a content-addressable system.
@ -15,8 +30,13 @@
//
// sha256:7173b809ca12ec5dee4506cd86be934c4596dd234ee82c0662eac04a8c2c71dc
//
// In this case, the string "sha256" is the algorithm and the hex bytes are
// the "digest".
// The "algorithm" portion defines both the hashing algorithm used to calculate
// the digest and the encoding of the resulting digest, which defaults to "hex"
// if not otherwise specified. Currently, all supported algorithms have their
// digests encoded in hex strings.
//
// In the example above, the string "sha256" is the algorithm and the hex bytes
// are the "digest".
//
// Because the Digest type is simply a string, once a valid Digest is
// obtained, comparisons are cheap, quick and simple to express with the

3
vendor/github.com/opencontainers/go-digest/go.mod generated vendored Normal file
View file

@ -0,0 +1,3 @@
module github.com/opencontainers/go-digest
go 1.13

View file

@ -1,3 +1,18 @@
// Copyright 2019, 2020 OCI Contributors
// Copyright 2017 Docker, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package digest
import (