algorithm.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. // Copyright 2019, 2020 OCI Contributors
  2. // Copyright 2017 Docker, Inc.
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. //
  8. // https://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS,
  12. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. package digest
  16. import (
  17. "crypto"
  18. "fmt"
  19. "hash"
  20. "io"
  21. "regexp"
  22. )
  23. // Algorithm identifies and implementation of a digester by an identifier.
  24. // Note the that this defines both the hash algorithm used and the string
  25. // encoding.
  26. type Algorithm string
  27. // supported digest types
  28. const (
  29. SHA256 Algorithm = "sha256" // sha256 with hex encoding (lower case only)
  30. SHA384 Algorithm = "sha384" // sha384 with hex encoding (lower case only)
  31. SHA512 Algorithm = "sha512" // sha512 with hex encoding (lower case only)
  32. // Canonical is the primary digest algorithm used with the distribution
  33. // project. Other digests may be used but this one is the primary storage
  34. // digest.
  35. Canonical = SHA256
  36. )
  37. var (
  38. // TODO(stevvooe): Follow the pattern of the standard crypto package for
  39. // registration of digests. Effectively, we are a registerable set and
  40. // common symbol access.
  41. // algorithms maps values to hash.Hash implementations. Other algorithms
  42. // may be available but they cannot be calculated by the digest package.
  43. algorithms = map[Algorithm]crypto.Hash{
  44. SHA256: crypto.SHA256,
  45. SHA384: crypto.SHA384,
  46. SHA512: crypto.SHA512,
  47. }
  48. // anchoredEncodedRegexps contains anchored regular expressions for hex-encoded digests.
  49. // Note that /A-F/ disallowed.
  50. anchoredEncodedRegexps = map[Algorithm]*regexp.Regexp{
  51. SHA256: regexp.MustCompile(`^[a-f0-9]{64}$`),
  52. SHA384: regexp.MustCompile(`^[a-f0-9]{96}$`),
  53. SHA512: regexp.MustCompile(`^[a-f0-9]{128}$`),
  54. }
  55. )
  56. // Available returns true if the digest type is available for use. If this
  57. // returns false, Digester and Hash will return nil.
  58. func (a Algorithm) Available() bool {
  59. h, ok := algorithms[a]
  60. if !ok {
  61. return false
  62. }
  63. // check availability of the hash, as well
  64. return h.Available()
  65. }
  66. func (a Algorithm) String() string {
  67. return string(a)
  68. }
  69. // Size returns number of bytes returned by the hash.
  70. func (a Algorithm) Size() int {
  71. h, ok := algorithms[a]
  72. if !ok {
  73. return 0
  74. }
  75. return h.Size()
  76. }
  77. // Set implemented to allow use of Algorithm as a command line flag.
  78. func (a *Algorithm) Set(value string) error {
  79. if value == "" {
  80. *a = Canonical
  81. } else {
  82. // just do a type conversion, support is queried with Available.
  83. *a = Algorithm(value)
  84. }
  85. if !a.Available() {
  86. return ErrDigestUnsupported
  87. }
  88. return nil
  89. }
  90. // Digester returns a new digester for the specified algorithm. If the algorithm
  91. // does not have a digester implementation, nil will be returned. This can be
  92. // checked by calling Available before calling Digester.
  93. func (a Algorithm) Digester() Digester {
  94. return &digester{
  95. alg: a,
  96. hash: a.Hash(),
  97. }
  98. }
  99. // Hash returns a new hash as used by the algorithm. If not available, the
  100. // method will panic. Check Algorithm.Available() before calling.
  101. func (a Algorithm) Hash() hash.Hash {
  102. if !a.Available() {
  103. // Empty algorithm string is invalid
  104. if a == "" {
  105. panic(fmt.Sprintf("empty digest algorithm, validate before calling Algorithm.Hash()"))
  106. }
  107. // NOTE(stevvooe): A missing hash is usually a programming error that
  108. // must be resolved at compile time. We don't import in the digest
  109. // package to allow users to choose their hash implementation (such as
  110. // when using stevvooe/resumable or a hardware accelerated package).
  111. //
  112. // Applications that may want to resolve the hash at runtime should
  113. // call Algorithm.Available before call Algorithm.Hash().
  114. panic(fmt.Sprintf("%v not available (make sure it is imported)", a))
  115. }
  116. return algorithms[a].New()
  117. }
  118. // Encode encodes the raw bytes of a digest, typically from a hash.Hash, into
  119. // the encoded portion of the digest.
  120. func (a Algorithm) Encode(d []byte) string {
  121. // TODO(stevvooe): Currently, all algorithms use a hex encoding. When we
  122. // add support for back registration, we can modify this accordingly.
  123. return fmt.Sprintf("%x", d)
  124. }
  125. // FromReader returns the digest of the reader using the algorithm.
  126. func (a Algorithm) FromReader(rd io.Reader) (Digest, error) {
  127. digester := a.Digester()
  128. if _, err := io.Copy(digester.Hash(), rd); err != nil {
  129. return "", err
  130. }
  131. return digester.Digest(), nil
  132. }
  133. // FromBytes digests the input and returns a Digest.
  134. func (a Algorithm) FromBytes(p []byte) Digest {
  135. digester := a.Digester()
  136. if _, err := digester.Hash().Write(p); err != nil {
  137. // Writes to a Hash should never fail. None of the existing
  138. // hash implementations in the stdlib or hashes vendored
  139. // here can return errors from Write. Having a panic in this
  140. // condition instead of having FromBytes return an error value
  141. // avoids unnecessary error handling paths in all callers.
  142. panic("write to hash function returned error: " + err.Error())
  143. }
  144. return digester.Digest()
  145. }
  146. // FromString digests the string input and returns a Digest.
  147. func (a Algorithm) FromString(s string) Digest {
  148. return a.FromBytes([]byte(s))
  149. }
  150. // Validate validates the encoded portion string
  151. func (a Algorithm) Validate(encoded string) error {
  152. r, ok := anchoredEncodedRegexps[a]
  153. if !ok {
  154. return ErrDigestUnsupported
  155. }
  156. // Digests much always be hex-encoded, ensuring that their hex portion will
  157. // always be size*2
  158. if a.Size()*2 != len(encoded) {
  159. return ErrDigestInvalidLength
  160. }
  161. if r.MatchString(encoded) {
  162. return nil
  163. }
  164. return ErrDigestInvalidFormat
  165. }