forked from openshift/client-go
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
1,304 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
package digest | ||
|
||
import ( | ||
"fmt" | ||
"hash" | ||
"io" | ||
"regexp" | ||
"strings" | ||
) | ||
|
||
const ( | ||
// DigestSha256EmptyTar is the canonical sha256 digest of empty data | ||
DigestSha256EmptyTar = "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" | ||
) | ||
|
||
// Digest allows simple protection of hex formatted digest strings, prefixed | ||
// by their algorithm. Strings of type Digest have some guarantee of being in | ||
// the correct format and it provides quick access to the components of a | ||
// digest string. | ||
// | ||
// The following is an example of the contents of Digest types: | ||
// | ||
// sha256:7173b809ca12ec5dee4506cd86be934c4596dd234ee82c0662eac04a8c2c71dc | ||
// | ||
// This allows to abstract the digest behind this type and work only in those | ||
// terms. | ||
type Digest string | ||
|
||
// NewDigest returns a Digest from alg and a hash.Hash object. | ||
func NewDigest(alg Algorithm, h hash.Hash) Digest { | ||
return NewDigestFromBytes(alg, h.Sum(nil)) | ||
} | ||
|
||
// NewDigestFromBytes returns a new digest from the byte contents of p. | ||
// Typically, this can come from hash.Hash.Sum(...) or xxx.SumXXX(...) | ||
// 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)) | ||
} | ||
|
||
// NewDigestFromHex returns a Digest from alg and a the hex encoded digest. | ||
func NewDigestFromHex(alg, hex string) Digest { | ||
return Digest(fmt.Sprintf("%s:%s", alg, hex)) | ||
} | ||
|
||
// DigestRegexp matches valid digest types. | ||
var DigestRegexp = regexp.MustCompile(`[a-zA-Z0-9-_+.]+:[a-fA-F0-9]+`) | ||
|
||
// DigestRegexpAnchored matches valid digest types, anchored to the start and end of the match. | ||
var DigestRegexpAnchored = regexp.MustCompile(`^` + DigestRegexp.String() + `$`) | ||
|
||
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") | ||
) | ||
|
||
// ParseDigest parses s and returns the validated digest object. An error will | ||
// be returned if the format is invalid. | ||
func ParseDigest(s string) (Digest, error) { | ||
d := Digest(s) | ||
|
||
return d, d.Validate() | ||
} | ||
|
||
// FromReader returns the most valid digest for the underlying content using | ||
// the canonical digest algorithm. | ||
func FromReader(rd io.Reader) (Digest, error) { | ||
return Canonical.FromReader(rd) | ||
} | ||
|
||
// FromBytes digests the input and returns a Digest. | ||
func FromBytes(p []byte) Digest { | ||
return Canonical.FromBytes(p) | ||
} | ||
|
||
// Validate checks that the contents of d is a valid digest, returning an | ||
// error if not. | ||
func (d Digest) Validate() error { | ||
s := string(d) | ||
|
||
if !DigestRegexpAnchored.MatchString(s) { | ||
return ErrDigestInvalidFormat | ||
} | ||
|
||
i := strings.Index(s, ":") | ||
if i < 0 { | ||
return ErrDigestInvalidFormat | ||
} | ||
|
||
// case: "sha256:" with no hex. | ||
if i+1 == len(s) { | ||
return ErrDigestInvalidFormat | ||
} | ||
|
||
switch algorithm := Algorithm(s[:i]); algorithm { | ||
case SHA256, SHA384, SHA512: | ||
if algorithm.Size()*2 != len(s[i+1:]) { | ||
return ErrDigestInvalidLength | ||
} | ||
default: | ||
return ErrDigestUnsupported | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// Algorithm returns the algorithm portion of the digest. This will panic if | ||
// the underlying digest is not in a valid format. | ||
func (d Digest) Algorithm() Algorithm { | ||
return Algorithm(d[:d.sepIndex()]) | ||
} | ||
|
||
// Hex returns the hex digest portion of the digest. This will panic if the | ||
// underlying digest is not in a valid format. | ||
func (d Digest) Hex() string { | ||
return string(d[d.sepIndex()+1:]) | ||
} | ||
|
||
func (d Digest) String() string { | ||
return string(d) | ||
} | ||
|
||
func (d Digest) sepIndex() int { | ||
i := strings.Index(string(d), ":") | ||
|
||
if i < 0 { | ||
panic("could not find ':' in digest: " + d) | ||
} | ||
|
||
return i | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
package digest | ||
|
||
import ( | ||
"crypto" | ||
"fmt" | ||
"hash" | ||
"io" | ||
) | ||
|
||
// Algorithm identifies and implementation of a digester by an identifier. | ||
// Note the that this defines both the hash algorithm used and the string | ||
// encoding. | ||
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 | ||
|
||
// Canonical is the primary digest algorithm used with the distribution | ||
// project. Other digests may be used but this one is the primary storage | ||
// digest. | ||
Canonical = SHA256 | ||
) | ||
|
||
var ( | ||
// TODO(stevvooe): Follow the pattern of the standard crypto package for | ||
// registration of digests. Effectively, we are a registerable set and | ||
// common symbol access. | ||
|
||
// algorithms maps values to hash.Hash implementations. Other algorithms | ||
// may be available but they cannot be calculated by the digest package. | ||
algorithms = map[Algorithm]crypto.Hash{ | ||
SHA256: crypto.SHA256, | ||
SHA384: crypto.SHA384, | ||
SHA512: crypto.SHA512, | ||
} | ||
) | ||
|
||
// Available returns true if the digest type is available for use. If this | ||
// returns false, New and Hash will return nil. | ||
func (a Algorithm) Available() bool { | ||
h, ok := algorithms[a] | ||
if !ok { | ||
return false | ||
} | ||
|
||
// check availability of the hash, as well | ||
return h.Available() | ||
} | ||
|
||
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 == "" { | ||
*a = Canonical | ||
} else { | ||
// just do a type conversion, support is queried with Available. | ||
*a = Algorithm(value) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// New returns a new digester for the specified algorithm. If the algorithm | ||
// does not have a digester implementation, nil will be returned. This can be | ||
// checked by calling Available before calling New. | ||
func (a Algorithm) New() Digester { | ||
return &digester{ | ||
alg: a, | ||
hash: a.Hash(), | ||
} | ||
} | ||
|
||
// Hash returns a new hash as used by the algorithm. If not available, the | ||
// method will panic. Check Algorithm.Available() before calling. | ||
func (a Algorithm) Hash() hash.Hash { | ||
if !a.Available() { | ||
// NOTE(stevvooe): A missing hash is usually a programming error that | ||
// must be resolved at compile time. We don't import in the digest | ||
// package to allow users to choose their hash implementation (such as | ||
// when using stevvooe/resumable or a hardware accelerated package). | ||
// | ||
// Applications that may want to resolve the hash at runtime should | ||
// call Algorithm.Available before call Algorithm.Hash(). | ||
panic(fmt.Sprintf("%v not available (make sure it is imported)", a)) | ||
} | ||
|
||
return algorithms[a].New() | ||
} | ||
|
||
// FromReader returns the digest of the reader using the algorithm. | ||
func (a Algorithm) FromReader(rd io.Reader) (Digest, error) { | ||
digester := a.New() | ||
|
||
if _, err := io.Copy(digester.Hash(), rd); err != nil { | ||
return "", err | ||
} | ||
|
||
return digester.Digest(), nil | ||
} | ||
|
||
// FromBytes digests the input and returns a Digest. | ||
func (a Algorithm) FromBytes(p []byte) Digest { | ||
digester := a.New() | ||
|
||
if _, err := digester.Hash().Write(p); err != nil { | ||
// Writes to a Hash should never fail. None of the existing | ||
// hash implementations in the stdlib or hashes vendored | ||
// here can return errors from Write. Having a panic in this | ||
// condition instead of having FromBytes return an error value | ||
// avoids unnecessary error handling paths in all callers. | ||
panic("write to hash function returned error: " + err.Error()) | ||
} | ||
|
||
return digester.Digest() | ||
} | ||
|
||
// TODO(stevvooe): Allow resolution of verifiers using the digest type and | ||
// this registration system. | ||
|
||
// Digester calculates the digest of written data. Writes should go directly | ||
// to the return value of Hash, while calling Digest will return the current | ||
// value of the digest. | ||
type Digester interface { | ||
Hash() hash.Hash // provides direct access to underlying hash instance. | ||
Digest() Digest | ||
} | ||
|
||
// digester provides a simple digester definition that embeds a hasher. | ||
type digester struct { | ||
alg Algorithm | ||
hash hash.Hash | ||
} | ||
|
||
func (d *digester) Hash() hash.Hash { | ||
return d.hash | ||
} | ||
|
||
func (d *digester) Digest() Digest { | ||
return NewDigest(d.alg, d.hash) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
// digest is a copy from "github.com/docker/distribution/digest" that is kept because we want to avoid the godep, | ||
// this package has no non-standard dependencies, and if it changes lots of other docker registry stuff breaks. | ||
// Don't try this at home! | ||
// Changes here require sign-off from openshift/api-reviewers and they will be rejected. | ||
package digest |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
// reference is a copy from "github.com/docker/distribution/reference" that is kept because we want to avoid the godep, | ||
// this package has no non-standard dependencies, and if it changes lots of other docker registry stuff breaks. | ||
// Don't try this at home! | ||
// Changes here require sign-off from openshift/api-reviewers and they will be rejected. | ||
package reference |
Oops, something went wrong.