digest: cleanup digester and verifier creation
Signed-off-by: Stephen J Day <stephen.day@docker.com>master
							parent
							
								
									ad80b3d492
								
							
						
					
					
						commit
						e37baed88e
					
				|  | @ -0,0 +1,140 @@ | |||
| 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) Digester() Digester { | ||||
| 	return &digester{ | ||||
| 		alg:  a, | ||||
| 		hash: a.Hash(), | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // New is deprecated. Use Algorithm.Digester.
 | ||||
| func (a Algorithm) New() Digester { | ||||
| 	return a.Digester() | ||||
| } | ||||
| 
 | ||||
| // 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() | ||||
| } | ||||
| 
 | ||||
| // FromString digests the string input and returns a Digest.
 | ||||
| func (a Algorithm) FromString(s string) Digest { | ||||
| 	return a.FromBytes([]byte(s)) | ||||
| } | ||||
|  | @ -123,6 +123,20 @@ func (d Digest) Algorithm() Algorithm { | |||
| 	return Algorithm(d[:d.sepIndex()]) | ||||
| } | ||||
| 
 | ||||
| // Verifier returns a writer object that can be used to verify a stream of
 | ||||
| // content against the digest. If the digest is invalid, an error will be
 | ||||
| // returned.
 | ||||
| func (d Digest) Verifier() (Verifier, error) { | ||||
| 	if err := d.Validate(); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return hashVerifier{ | ||||
| 		hash:   d.Algorithm().Hash(), | ||||
| 		digest: d, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| // 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 { | ||||
|  |  | |||
|  | @ -1,141 +1,6 @@ | |||
| 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() | ||||
| } | ||||
| 
 | ||||
| // FromString digests the string input and returns a Digest.
 | ||||
| func (a Algorithm) FromString(s string) Digest { | ||||
| 	return a.FromBytes([]byte(s)) | ||||
| } | ||||
| 
 | ||||
| // TODO(stevvooe): Allow resolution of verifiers using the digest type and
 | ||||
| // this registration system.
 | ||||
| import "hash" | ||||
| 
 | ||||
| // Digester calculates the digest of written data. Writes should go directly
 | ||||
| // to the return value of Hash, while calling Digest will return the current
 | ||||
|  |  | |||
|  | @ -17,8 +17,7 @@ type Verifier interface { | |||
| 	Verified() bool | ||||
| } | ||||
| 
 | ||||
| // NewDigestVerifier returns a verifier that compares the written bytes
 | ||||
| // against a passed in digest.
 | ||||
| // NewDigestVerifier is deprecated. Please use Digest.Verifier.
 | ||||
| func NewDigestVerifier(d Digest) (Verifier, error) { | ||||
| 	if err := d.Validate(); err != nil { | ||||
| 		return nil, err | ||||
|  |  | |||
|  | @ -552,7 +552,7 @@ func testBlobAPI(t *testing.T, env *testEnv, args blobArgs) *testEnv { | |||
| 	}) | ||||
| 
 | ||||
| 	// Verify the body
 | ||||
| 	verifier, err := digest.NewDigestVerifier(layerDigest) | ||||
| 	verifier, err := layerDigest.Verifier() | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("unexpected error getting digest verifier: %s", err) | ||||
| 	} | ||||
|  |  | |||
|  | @ -236,7 +236,7 @@ func (bw *blobWriter) validateBlob(ctx context.Context, desc distribution.Descri | |||
| 		if !verified { | ||||
| 			digester := digest.Canonical.New() | ||||
| 
 | ||||
| 			digestVerifier, err := digest.NewDigestVerifier(desc.Digest) | ||||
| 			digestVerifier, err := desc.Digest.Verifier() | ||||
| 			if err != nil { | ||||
| 				return distribution.Descriptor{}, err | ||||
| 			} | ||||
|  |  | |||
|  | @ -41,7 +41,7 @@ func TestSimpleRead(t *testing.T) { | |||
| 		t.Fatalf("error allocating file reader: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	verifier, err := digest.NewDigestVerifier(dgst) | ||||
| 	verifier, err := dgst.Verifier() | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("error getting digest verifier: %s", err) | ||||
| 	} | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue