github.com/npaton/distribution@v2.3.1-rc.0+incompatible/digest/digester.go (about) 1 package digest 2 3 import ( 4 "crypto" 5 "fmt" 6 "hash" 7 "io" 8 ) 9 10 // Algorithm identifies and implementation of a digester by an identifier. 11 // Note the that this defines both the hash algorithm used and the string 12 // encoding. 13 type Algorithm string 14 15 // supported digest types 16 const ( 17 SHA256 Algorithm = "sha256" // sha256 with hex encoding 18 SHA384 Algorithm = "sha384" // sha384 with hex encoding 19 SHA512 Algorithm = "sha512" // sha512 with hex encoding 20 21 // Canonical is the primary digest algorithm used with the distribution 22 // project. Other digests may be used but this one is the primary storage 23 // digest. 24 Canonical = SHA256 25 ) 26 27 var ( 28 // TODO(stevvooe): Follow the pattern of the standard crypto package for 29 // registration of digests. Effectively, we are a registerable set and 30 // common symbol access. 31 32 // algorithms maps values to hash.Hash implementations. Other algorithms 33 // may be available but they cannot be calculated by the digest package. 34 algorithms = map[Algorithm]crypto.Hash{ 35 SHA256: crypto.SHA256, 36 SHA384: crypto.SHA384, 37 SHA512: crypto.SHA512, 38 } 39 ) 40 41 // Available returns true if the digest type is available for use. If this 42 // returns false, New and Hash will return nil. 43 func (a Algorithm) Available() bool { 44 h, ok := algorithms[a] 45 if !ok { 46 return false 47 } 48 49 // check availability of the hash, as well 50 return h.Available() 51 } 52 53 func (a Algorithm) String() string { 54 return string(a) 55 } 56 57 // Size returns number of bytes returned by the hash. 58 func (a Algorithm) Size() int { 59 h, ok := algorithms[a] 60 if !ok { 61 return 0 62 } 63 return h.Size() 64 } 65 66 // Set implemented to allow use of Algorithm as a command line flag. 67 func (a *Algorithm) Set(value string) error { 68 if value == "" { 69 *a = Canonical 70 } else { 71 // just do a type conversion, support is queried with Available. 72 *a = Algorithm(value) 73 } 74 75 return nil 76 } 77 78 // New returns a new digester for the specified algorithm. If the algorithm 79 // does not have a digester implementation, nil will be returned. This can be 80 // checked by calling Available before calling New. 81 func (a Algorithm) New() Digester { 82 return &digester{ 83 alg: a, 84 hash: a.Hash(), 85 } 86 } 87 88 // Hash returns a new hash as used by the algorithm. If not available, the 89 // method will panic. Check Algorithm.Available() before calling. 90 func (a Algorithm) Hash() hash.Hash { 91 if !a.Available() { 92 // NOTE(stevvooe): A missing hash is usually a programming error that 93 // must be resolved at compile time. We don't import in the digest 94 // package to allow users to choose their hash implementation (such as 95 // when using stevvooe/resumable or a hardware accelerated package). 96 // 97 // Applications that may want to resolve the hash at runtime should 98 // call Algorithm.Available before call Algorithm.Hash(). 99 panic(fmt.Sprintf("%v not available (make sure it is imported)", a)) 100 } 101 102 return algorithms[a].New() 103 } 104 105 // FromReader returns the digest of the reader using the algorithm. 106 func (a Algorithm) FromReader(rd io.Reader) (Digest, error) { 107 digester := a.New() 108 109 if _, err := io.Copy(digester.Hash(), rd); err != nil { 110 return "", err 111 } 112 113 return digester.Digest(), nil 114 } 115 116 // FromBytes digests the input and returns a Digest. 117 func (a Algorithm) FromBytes(p []byte) Digest { 118 digester := a.New() 119 120 if _, err := digester.Hash().Write(p); err != nil { 121 // Writes to a Hash should never fail. None of the existing 122 // hash implementations in the stdlib or hashes vendored 123 // here can return errors from Write. Having a panic in this 124 // condition instead of having FromBytes return an error value 125 // avoids unnecessary error handling paths in all callers. 126 panic("write to hash function returned error: " + err.Error()) 127 } 128 129 return digester.Digest() 130 } 131 132 // TODO(stevvooe): Allow resolution of verifiers using the digest type and 133 // this registration system. 134 135 // Digester calculates the digest of written data. Writes should go directly 136 // to the return value of Hash, while calling Digest will return the current 137 // value of the digest. 138 type Digester interface { 139 Hash() hash.Hash // provides direct access to underlying hash instance. 140 Digest() Digest 141 } 142 143 // digester provides a simple digester definition that embeds a hasher. 144 type digester struct { 145 alg Algorithm 146 hash hash.Hash 147 } 148 149 func (d *digester) Hash() hash.Hash { 150 return d.hash 151 } 152 153 func (d *digester) Digest() Digest { 154 return NewDigest(d.alg, d.hash) 155 }