github.com/uber/kraken@v0.1.4/core/digest.go (about) 1 // Copyright (c) 2016-2019 Uber Technologies, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 package core 15 16 import ( 17 _ "crypto/sha256" // For computing digest. 18 "database/sql/driver" 19 "encoding/hex" 20 "encoding/json" 21 "errors" 22 "fmt" 23 "strings" 24 ) 25 26 const ( 27 // DigestEmptyTar is the sha256 digest of an empty tar file. 28 DigestEmptyTar = "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" 29 ) 30 31 // DigestList is a list of digests. 32 type DigestList []Digest 33 34 // Value marshals a list of digests and returns []byte as driver.Value. 35 func (l DigestList) Value() (driver.Value, error) { 36 b, err := json.Marshal(l) 37 if err != nil { 38 return driver.Value([]byte{}), err 39 } 40 return driver.Value(b), nil 41 } 42 43 // Scan unmarshals []byte to a list of Digest. 44 func (l *DigestList) Scan(src interface{}) error { 45 return json.Unmarshal(src.([]byte), l) 46 } 47 48 // Digest can be represented in a string like "<algorithm>:<hex_digest_string>" 49 // Example: 50 // sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 51 type Digest struct { 52 algo string 53 hex string 54 raw string 55 } 56 57 // NewSHA256DigestFromHex constructs a Digest from a sha256 in hexadecimal 58 // format. Returns error if hex is not a valid sha256. 59 func NewSHA256DigestFromHex(hex string) (Digest, error) { 60 if err := ValidateSHA256(hex); err != nil { 61 return Digest{}, fmt.Errorf("invalid sha256: %s", err) 62 } 63 return Digest{ 64 algo: SHA256, 65 hex: hex, 66 raw: fmt.Sprintf("%s:%s", SHA256, hex), 67 }, nil 68 } 69 70 // ParseSHA256Digest parses a raw "<algo>:<hex>" sha256 digest. Returns error if the 71 // algo is not sha256 or the hex is not a valid sha256. 72 func ParseSHA256Digest(raw string) (Digest, error) { 73 if raw == "" { 74 return Digest{}, errors.New("invalid digest: empty") 75 } 76 parts := strings.Split(raw, ":") 77 if len(parts) != 2 { 78 return Digest{}, errors.New("invalid digest: expected '<algo>:<hex>'") 79 } 80 algo := parts[0] 81 hex := parts[1] 82 if algo != SHA256 { 83 return Digest{}, errors.New("invalid digest algo: expected sha256") 84 } 85 if err := ValidateSHA256(hex); err != nil { 86 return Digest{}, fmt.Errorf("invalid sha256: %s", err) 87 } 88 return Digest{ 89 algo: algo, 90 hex: hex, 91 raw: raw, 92 }, nil 93 } 94 95 // Value marshals a digest and returns []byte as driver.Value. 96 func (d Digest) Value() (driver.Value, error) { 97 b, err := json.Marshal(d) 98 if err != nil { 99 return driver.Value([]byte{}), err 100 } 101 return driver.Value(b), nil 102 } 103 104 // Scan unmarshals []byte to a Digest. 105 func (d *Digest) Scan(src interface{}) error { 106 return json.Unmarshal(src.([]byte), d) 107 } 108 109 // UnmarshalJSON unmarshals "<algorithm>:<hex_digest_string>" to Digest. 110 func (d *Digest) UnmarshalJSON(str []byte) error { 111 var raw string 112 if err := json.Unmarshal(str, &raw); err != nil { 113 return err 114 } 115 digest, err := ParseSHA256Digest(raw) 116 if err != nil { 117 return err 118 } 119 *d = digest 120 return nil 121 } 122 123 // MarshalJSON unmarshals hexBytes to Digest. 124 func (d Digest) MarshalJSON() ([]byte, error) { 125 return json.Marshal(d.raw) 126 } 127 128 // String returns digest in string format like "<algorithm>:<hex_digest_string>". 129 func (d Digest) String() string { 130 return d.raw 131 } 132 133 // Algo returns the algo part of the digest. 134 // Example: 135 // sha256 136 func (d Digest) Algo() string { 137 return d.algo 138 } 139 140 // Hex returns the hex part of the digest. 141 // Example: 142 // e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 143 func (d Digest) Hex() string { 144 return d.hex 145 } 146 147 // ShardID returns the shard id of the digest. 148 func (d Digest) ShardID() string { 149 return d.hex[:4] 150 } 151 152 // ValidateSHA256 returns error if s is not a valid SHA256 hex digest. 153 func ValidateSHA256(s string) error { 154 if len(s) != 64 { 155 return fmt.Errorf("expected 64 characters, got %d from %q", len(s), s) 156 } 157 if _, err := hex.DecodeString(s); err != nil { 158 return fmt.Errorf("hex: %s", err) 159 } 160 return nil 161 }