blob: a3c44801d5e6fa9473592541ac1e70f29a65fecf [file] [log] [blame]
David K. Bainbridge215e0242017-09-05 23:18:24 -07001package digest
2
3import (
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.
13type Algorithm string
14
15// supported digest types
16const (
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
27var (
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, Digester and Hash will return nil.
43func (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
53func (a Algorithm) String() string {
54 return string(a)
55}
56
57// Size returns number of bytes returned by the hash.
58func (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.
67func (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 if !a.Available() {
76 return ErrDigestUnsupported
77 }
78
79 return nil
80}
81
82// Digester returns a new digester for the specified algorithm. If the algorithm
83// does not have a digester implementation, nil will be returned. This can be
84// checked by calling Available before calling Digester.
85func (a Algorithm) Digester() Digester {
86 return &digester{
87 alg: a,
88 hash: a.Hash(),
89 }
90}
91
92// Hash returns a new hash as used by the algorithm. If not available, the
93// method will panic. Check Algorithm.Available() before calling.
94func (a Algorithm) Hash() hash.Hash {
95 if !a.Available() {
96 // Empty algorithm string is invalid
97 if a == "" {
98 panic(fmt.Sprintf("empty digest algorithm, validate before calling Algorithm.Hash()"))
99 }
100
101 // NOTE(stevvooe): A missing hash is usually a programming error that
102 // must be resolved at compile time. We don't import in the digest
103 // package to allow users to choose their hash implementation (such as
104 // when using stevvooe/resumable or a hardware accelerated package).
105 //
106 // Applications that may want to resolve the hash at runtime should
107 // call Algorithm.Available before call Algorithm.Hash().
108 panic(fmt.Sprintf("%v not available (make sure it is imported)", a))
109 }
110
111 return algorithms[a].New()
112}
113
114// FromReader returns the digest of the reader using the algorithm.
115func (a Algorithm) FromReader(rd io.Reader) (Digest, error) {
116 digester := a.Digester()
117
118 if _, err := io.Copy(digester.Hash(), rd); err != nil {
119 return "", err
120 }
121
122 return digester.Digest(), nil
123}
124
125// FromBytes digests the input and returns a Digest.
126func (a Algorithm) FromBytes(p []byte) Digest {
127 digester := a.Digester()
128
129 if _, err := digester.Hash().Write(p); err != nil {
130 // Writes to a Hash should never fail. None of the existing
131 // hash implementations in the stdlib or hashes vendored
132 // here can return errors from Write. Having a panic in this
133 // condition instead of having FromBytes return an error value
134 // avoids unnecessary error handling paths in all callers.
135 panic("write to hash function returned error: " + err.Error())
136 }
137
138 return digester.Digest()
139}
140
141// FromString digests the string input and returns a Digest.
142func (a Algorithm) FromString(s string) Digest {
143 return a.FromBytes([]byte(s))
144}