github.com/npaton/distribution@v2.3.1-rc.0+incompatible/digest/digest.go (about) 1 package digest 2 3 import ( 4 "fmt" 5 "hash" 6 "io" 7 "regexp" 8 "strings" 9 ) 10 11 const ( 12 // DigestSha256EmptyTar is the canonical sha256 digest of empty data 13 DigestSha256EmptyTar = "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" 14 ) 15 16 // Digest allows simple protection of hex formatted digest strings, prefixed 17 // by their algorithm. Strings of type Digest have some guarantee of being in 18 // the correct format and it provides quick access to the components of a 19 // digest string. 20 // 21 // The following is an example of the contents of Digest types: 22 // 23 // sha256:7173b809ca12ec5dee4506cd86be934c4596dd234ee82c0662eac04a8c2c71dc 24 // 25 // This allows to abstract the digest behind this type and work only in those 26 // terms. 27 type Digest string 28 29 // NewDigest returns a Digest from alg and a hash.Hash object. 30 func NewDigest(alg Algorithm, h hash.Hash) Digest { 31 return NewDigestFromBytes(alg, h.Sum(nil)) 32 } 33 34 // NewDigestFromBytes returns a new digest from the byte contents of p. 35 // Typically, this can come from hash.Hash.Sum(...) or xxx.SumXXX(...) 36 // functions. This is also useful for rebuilding digests from binary 37 // serializations. 38 func NewDigestFromBytes(alg Algorithm, p []byte) Digest { 39 return Digest(fmt.Sprintf("%s:%x", alg, p)) 40 } 41 42 // NewDigestFromHex returns a Digest from alg and a the hex encoded digest. 43 func NewDigestFromHex(alg, hex string) Digest { 44 return Digest(fmt.Sprintf("%s:%s", alg, hex)) 45 } 46 47 // DigestRegexp matches valid digest types. 48 var DigestRegexp = regexp.MustCompile(`[a-zA-Z0-9-_+.]+:[a-fA-F0-9]+`) 49 50 // DigestRegexpAnchored matches valid digest types, anchored to the start and end of the match. 51 var DigestRegexpAnchored = regexp.MustCompile(`^` + DigestRegexp.String() + `$`) 52 53 var ( 54 // ErrDigestInvalidFormat returned when digest format invalid. 55 ErrDigestInvalidFormat = fmt.Errorf("invalid checksum digest format") 56 57 // ErrDigestInvalidLength returned when digest has invalid length. 58 ErrDigestInvalidLength = fmt.Errorf("invalid checksum digest length") 59 60 // ErrDigestUnsupported returned when the digest algorithm is unsupported. 61 ErrDigestUnsupported = fmt.Errorf("unsupported digest algorithm") 62 ) 63 64 // ParseDigest parses s and returns the validated digest object. An error will 65 // be returned if the format is invalid. 66 func ParseDigest(s string) (Digest, error) { 67 d := Digest(s) 68 69 return d, d.Validate() 70 } 71 72 // FromReader returns the most valid digest for the underlying content using 73 // the canonical digest algorithm. 74 func FromReader(rd io.Reader) (Digest, error) { 75 return Canonical.FromReader(rd) 76 } 77 78 // FromBytes digests the input and returns a Digest. 79 func FromBytes(p []byte) Digest { 80 return Canonical.FromBytes(p) 81 } 82 83 // Validate checks that the contents of d is a valid digest, returning an 84 // error if not. 85 func (d Digest) Validate() error { 86 s := string(d) 87 88 if !DigestRegexpAnchored.MatchString(s) { 89 return ErrDigestInvalidFormat 90 } 91 92 i := strings.Index(s, ":") 93 if i < 0 { 94 return ErrDigestInvalidFormat 95 } 96 97 // case: "sha256:" with no hex. 98 if i+1 == len(s) { 99 return ErrDigestInvalidFormat 100 } 101 102 switch algorithm := Algorithm(s[:i]); algorithm { 103 case SHA256, SHA384, SHA512: 104 if algorithm.Size()*2 != len(s[i+1:]) { 105 return ErrDigestInvalidLength 106 } 107 break 108 default: 109 return ErrDigestUnsupported 110 } 111 112 return nil 113 } 114 115 // Algorithm returns the algorithm portion of the digest. This will panic if 116 // the underlying digest is not in a valid format. 117 func (d Digest) Algorithm() Algorithm { 118 return Algorithm(d[:d.sepIndex()]) 119 } 120 121 // Hex returns the hex digest portion of the digest. This will panic if the 122 // underlying digest is not in a valid format. 123 func (d Digest) Hex() string { 124 return string(d[d.sepIndex()+1:]) 125 } 126 127 func (d Digest) String() string { 128 return string(d) 129 } 130 131 func (d Digest) sepIndex() int { 132 i := strings.Index(string(d), ":") 133 134 if i < 0 { 135 panic("could not find ':' in digest: " + d) 136 } 137 138 return i 139 }