checksum.go (5304B)
1 /* 2 * MinIO Go Library for Amazon S3 Compatible Cloud Storage 3 * Copyright 2015-2023 MinIO, Inc. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package minio 19 20 import ( 21 "crypto/sha1" 22 "crypto/sha256" 23 "encoding/base64" 24 "hash" 25 "hash/crc32" 26 "io" 27 "math/bits" 28 ) 29 30 // ChecksumType contains information about the checksum type. 31 type ChecksumType uint32 32 33 const ( 34 35 // ChecksumSHA256 indicates a SHA256 checksum. 36 ChecksumSHA256 ChecksumType = 1 << iota 37 // ChecksumSHA1 indicates a SHA-1 checksum. 38 ChecksumSHA1 39 // ChecksumCRC32 indicates a CRC32 checksum with IEEE table. 40 ChecksumCRC32 41 // ChecksumCRC32C indicates a CRC32 checksum with Castagnoli table. 42 ChecksumCRC32C 43 44 // Keep after all valid checksums 45 checksumLast 46 47 // checksumMask is a mask for valid checksum types. 48 checksumMask = checksumLast - 1 49 50 // ChecksumNone indicates no checksum. 51 ChecksumNone ChecksumType = 0 52 53 amzChecksumAlgo = "x-amz-checksum-algorithm" 54 amzChecksumCRC32 = "x-amz-checksum-crc32" 55 amzChecksumCRC32C = "x-amz-checksum-crc32c" 56 amzChecksumSHA1 = "x-amz-checksum-sha1" 57 amzChecksumSHA256 = "x-amz-checksum-sha256" 58 ) 59 60 // Is returns if c is all of t. 61 func (c ChecksumType) Is(t ChecksumType) bool { 62 return c&t == t 63 } 64 65 // Key returns the header key. 66 // returns empty string if invalid or none. 67 func (c ChecksumType) Key() string { 68 switch c & checksumMask { 69 case ChecksumCRC32: 70 return amzChecksumCRC32 71 case ChecksumCRC32C: 72 return amzChecksumCRC32C 73 case ChecksumSHA1: 74 return amzChecksumSHA1 75 case ChecksumSHA256: 76 return amzChecksumSHA256 77 } 78 return "" 79 } 80 81 // RawByteLen returns the size of the un-encoded checksum. 82 func (c ChecksumType) RawByteLen() int { 83 switch c & checksumMask { 84 case ChecksumCRC32, ChecksumCRC32C: 85 return 4 86 case ChecksumSHA1: 87 return sha1.Size 88 case ChecksumSHA256: 89 return sha256.Size 90 } 91 return 0 92 } 93 94 // Hasher returns a hasher corresponding to the checksum type. 95 // Returns nil if no checksum. 96 func (c ChecksumType) Hasher() hash.Hash { 97 switch c & checksumMask { 98 case ChecksumCRC32: 99 return crc32.NewIEEE() 100 case ChecksumCRC32C: 101 return crc32.New(crc32.MakeTable(crc32.Castagnoli)) 102 case ChecksumSHA1: 103 return sha1.New() 104 case ChecksumSHA256: 105 return sha256.New() 106 } 107 return nil 108 } 109 110 // IsSet returns whether the type is valid and known. 111 func (c ChecksumType) IsSet() bool { 112 return bits.OnesCount32(uint32(c)) == 1 113 } 114 115 // String returns the type as a string. 116 // CRC32, CRC32C, SHA1, and SHA256 for valid values. 117 // Empty string for unset and "<invalid>" if not valid. 118 func (c ChecksumType) String() string { 119 switch c & checksumMask { 120 case ChecksumCRC32: 121 return "CRC32" 122 case ChecksumCRC32C: 123 return "CRC32C" 124 case ChecksumSHA1: 125 return "SHA1" 126 case ChecksumSHA256: 127 return "SHA256" 128 case ChecksumNone: 129 return "" 130 } 131 return "<invalid>" 132 } 133 134 // ChecksumReader reads all of r and returns a checksum of type c. 135 // Returns any error that may have occurred while reading. 136 func (c ChecksumType) ChecksumReader(r io.Reader) (Checksum, error) { 137 h := c.Hasher() 138 if h == nil { 139 return Checksum{}, nil 140 } 141 _, err := io.Copy(h, r) 142 if err != nil { 143 return Checksum{}, err 144 } 145 return NewChecksum(c, h.Sum(nil)), nil 146 } 147 148 // ChecksumBytes returns a checksum of the content b with type c. 149 func (c ChecksumType) ChecksumBytes(b []byte) Checksum { 150 h := c.Hasher() 151 if h == nil { 152 return Checksum{} 153 } 154 n, err := h.Write(b) 155 if err != nil || n != len(b) { 156 // Shouldn't happen with these checksummers. 157 return Checksum{} 158 } 159 return NewChecksum(c, h.Sum(nil)) 160 } 161 162 // Checksum is a type and encoded value. 163 type Checksum struct { 164 Type ChecksumType 165 r []byte 166 } 167 168 // NewChecksum sets the checksum to the value of b, 169 // which is the raw hash output. 170 // If the length of c does not match t.RawByteLen, 171 // a checksum with ChecksumNone is returned. 172 func NewChecksum(t ChecksumType, b []byte) Checksum { 173 if t.IsSet() && len(b) == t.RawByteLen() { 174 return Checksum{Type: t, r: b} 175 } 176 return Checksum{} 177 } 178 179 // NewChecksumString sets the checksum to the value of s, 180 // which is the base 64 encoded raw hash output. 181 // If the length of c does not match t.RawByteLen, it is not added. 182 func NewChecksumString(t ChecksumType, s string) Checksum { 183 b, _ := base64.StdEncoding.DecodeString(s) 184 if t.IsSet() && len(b) == t.RawByteLen() { 185 return Checksum{Type: t, r: b} 186 } 187 return Checksum{} 188 } 189 190 // IsSet returns whether the checksum is valid and known. 191 func (c Checksum) IsSet() bool { 192 return c.Type.IsSet() && len(c.r) == c.Type.RawByteLen() 193 } 194 195 // Encoded returns the encoded value. 196 // Returns the empty string if not set or valid. 197 func (c Checksum) Encoded() string { 198 if !c.IsSet() { 199 return "" 200 } 201 return base64.StdEncoding.EncodeToString(c.r) 202 } 203 204 // Raw returns the raw checksum value if set. 205 func (c Checksum) Raw() []byte { 206 if !c.IsSet() { 207 return nil 208 } 209 return c.r 210 }