github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/internal/hash/reader_test.go (about) 1 // Copyright (c) 2015-2021 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package hash 19 20 import ( 21 "bytes" 22 "context" 23 "encoding/base64" 24 "encoding/hex" 25 "fmt" 26 "io" 27 "testing" 28 29 "github.com/minio/minio/internal/ioutil" 30 ) 31 32 // Tests functions like Size(), MD5*(), SHA256*() 33 func TestHashReaderHelperMethods(t *testing.T) { 34 r, err := NewReader(context.Background(), bytes.NewReader([]byte("abcd")), 4, "e2fc714c4727ee9395f324cd2e7f331f", "88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031589", 4) 35 if err != nil { 36 t.Fatal(err) 37 } 38 _, err = io.Copy(io.Discard, r) 39 if err != nil { 40 t.Fatal(err) 41 } 42 md5sum := r.MD5Current() 43 if hex.EncodeToString(md5sum) != "e2fc714c4727ee9395f324cd2e7f331f" { 44 t.Errorf("Expected md5hex \"e2fc714c4727ee9395f324cd2e7f331f\", got %s", hex.EncodeToString(md5sum)) 45 } 46 if r.SHA256HexString() != "88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031589" { 47 t.Errorf("Expected sha256hex \"88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031589\", got %s", r.SHA256HexString()) 48 } 49 if base64.StdEncoding.EncodeToString(md5sum) != "4vxxTEcn7pOV8yTNLn8zHw==" { 50 t.Errorf("Expected md5base64 \"4vxxTEcn7pOV8yTNLn8zHw==\", got \"%s\"", base64.StdEncoding.EncodeToString(md5sum)) 51 } 52 if r.Size() != 4 { 53 t.Errorf("Expected size 4, got %d", r.Size()) 54 } 55 if r.ActualSize() != 4 { 56 t.Errorf("Expected size 4, got %d", r.ActualSize()) 57 } 58 expectedMD5, err := hex.DecodeString("e2fc714c4727ee9395f324cd2e7f331f") 59 if err != nil { 60 t.Fatal(err) 61 } 62 if !bytes.Equal(r.MD5Current(), expectedMD5) { 63 t.Errorf("Expected md5hex \"e2fc714c4727ee9395f324cd2e7f331f\", got %s", hex.EncodeToString(r.MD5Current())) 64 } 65 expectedSHA256, err := hex.DecodeString("88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031589") 66 if err != nil { 67 t.Fatal(err) 68 } 69 if !bytes.Equal(r.SHA256(), expectedSHA256) { 70 t.Errorf("Expected md5hex \"88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031589\", got %s", r.SHA256HexString()) 71 } 72 } 73 74 // Tests hash reader checksum verification. 75 func TestHashReaderVerification(t *testing.T) { 76 testCases := []struct { 77 desc string 78 src io.Reader 79 size int64 80 actualSize int64 81 md5hex, sha256hex string 82 err error 83 }{ 84 0: { 85 desc: "Success, no checksum verification provided.", 86 src: bytes.NewReader([]byte("abcd")), 87 size: 4, 88 actualSize: 4, 89 }, 90 { 91 desc: "Failure md5 mismatch.", 92 src: bytes.NewReader([]byte("abcd")), 93 size: 4, 94 actualSize: 4, 95 md5hex: "d41d8cd98f00b204e9800998ecf8427f", 96 err: BadDigest{ 97 "d41d8cd98f00b204e9800998ecf8427f", 98 "e2fc714c4727ee9395f324cd2e7f331f", 99 }, 100 }, 101 { 102 desc: "Failure sha256 mismatch.", 103 src: bytes.NewReader([]byte("abcd")), 104 size: 4, 105 actualSize: 4, 106 sha256hex: "88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031580", 107 err: SHA256Mismatch{ 108 "88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031580", 109 "88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031589", 110 }, 111 }, 112 { 113 desc: "Nested hash reader NewReader() should merge.", 114 src: mustReader(t, bytes.NewReader([]byte("abcd")), 4, "", "", 4), 115 size: 4, 116 actualSize: 4, 117 }, 118 { 119 desc: "Incorrect sha256, nested", 120 src: mustReader(t, bytes.NewReader([]byte("abcd")), 4, "", "", 4), 121 size: 4, 122 actualSize: 4, 123 sha256hex: "50d858e0985ecc7f60418aaf0cc5ab587f42c2570a884095a9e8ccacd0f6545c", 124 err: SHA256Mismatch{ 125 ExpectedSHA256: "50d858e0985ecc7f60418aaf0cc5ab587f42c2570a884095a9e8ccacd0f6545c", 126 CalculatedSHA256: "88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031589", 127 }, 128 }, 129 5: { 130 desc: "Correct sha256, nested", 131 src: mustReader(t, bytes.NewReader([]byte("abcd")), 4, "", "", 4), 132 size: 4, 133 actualSize: 4, 134 sha256hex: "88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031589", 135 }, 136 { 137 desc: "Correct sha256, nested, truncated", 138 src: mustReader(t, bytes.NewReader([]byte("abcd-more-stuff-to-be ignored")), 4, "", "", 4), 139 size: 4, 140 actualSize: -1, 141 sha256hex: "88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031589", 142 err: ioutil.ErrOverread, 143 }, 144 7: { 145 desc: "Correct sha256, nested, truncated, swapped", 146 src: mustReader(t, bytes.NewReader([]byte("abcd-more-stuff-to-be ignored")), 4, "", "", -1), 147 size: 4, 148 actualSize: -1, 149 sha256hex: "88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031589", 150 err: ioutil.ErrOverread, 151 }, 152 { 153 desc: "Incorrect MD5, nested", 154 src: mustReader(t, bytes.NewReader([]byte("abcd")), 4, "", "", 4), 155 size: 4, 156 actualSize: 4, 157 md5hex: "0773da587b322af3a8718cb418a715ce", 158 err: BadDigest{ 159 ExpectedMD5: "0773da587b322af3a8718cb418a715ce", 160 CalculatedMD5: "e2fc714c4727ee9395f324cd2e7f331f", 161 }, 162 }, 163 { 164 desc: "Correct sha256, truncated", 165 src: bytes.NewReader([]byte("abcd-morethan-4-bytes")), 166 size: 4, 167 actualSize: 4, 168 sha256hex: "88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031589", 169 err: ioutil.ErrOverread, 170 }, 171 { 172 desc: "Correct MD5, nested", 173 src: mustReader(t, bytes.NewReader([]byte("abcd")), 4, "", "", 4), 174 size: 4, 175 actualSize: 4, 176 md5hex: "e2fc714c4727ee9395f324cd2e7f331f", 177 }, 178 { 179 desc: "Correct MD5, truncated", 180 src: bytes.NewReader([]byte("abcd-morethan-4-bytes")), 181 size: 4, 182 actualSize: 4, 183 sha256hex: "", 184 md5hex: "e2fc714c4727ee9395f324cd2e7f331f", 185 err: ioutil.ErrOverread, 186 }, 187 { 188 desc: "Correct MD5, nested, truncated", 189 src: mustReader(t, bytes.NewReader([]byte("abcd-morestuff")), -1, "", "", -1), 190 size: 4, 191 actualSize: 4, 192 md5hex: "e2fc714c4727ee9395f324cd2e7f331f", 193 err: ioutil.ErrOverread, 194 }, 195 } 196 for i, testCase := range testCases { 197 t.Run(fmt.Sprintf("case-%d", i+1), func(t *testing.T) { 198 r, err := NewReader(context.Background(), testCase.src, testCase.size, testCase.md5hex, testCase.sha256hex, testCase.actualSize) 199 if err != nil { 200 t.Fatalf("Test %q: Initializing reader failed %s", testCase.desc, err) 201 } 202 _, err = io.Copy(io.Discard, r) 203 if err != nil { 204 if testCase.err == nil { 205 t.Errorf("Test %q; got unexpected error: %v", testCase.desc, err) 206 return 207 } 208 if err.Error() != testCase.err.Error() { 209 t.Errorf("Test %q: Expected error %s, got error %s", testCase.desc, testCase.err, err) 210 } 211 } 212 }) 213 } 214 } 215 216 func mustReader(t *testing.T, src io.Reader, size int64, md5Hex, sha256Hex string, actualSize int64) *Reader { 217 r, err := NewReader(context.Background(), src, size, md5Hex, sha256Hex, actualSize) 218 if err != nil { 219 t.Fatal(err) 220 } 221 return r 222 } 223 224 // Tests NewReader() constructor with invalid arguments. 225 func TestHashReaderInvalidArguments(t *testing.T) { 226 testCases := []struct { 227 desc string 228 src io.Reader 229 size int64 230 actualSize int64 231 md5hex, sha256hex string 232 success bool 233 }{ 234 { 235 desc: "Invalid md5sum NewReader() will fail.", 236 src: bytes.NewReader([]byte("abcd")), 237 size: 4, 238 actualSize: 4, 239 md5hex: "invalid-md5", 240 success: false, 241 }, 242 { 243 desc: "Invalid sha256 NewReader() will fail.", 244 src: bytes.NewReader([]byte("abcd")), 245 size: 4, 246 actualSize: 4, 247 sha256hex: "invalid-sha256", 248 success: false, 249 }, 250 { 251 desc: "Nested hash reader NewReader() should merge.", 252 src: mustReader(t, bytes.NewReader([]byte("abcd")), 4, "", "", 4), 253 size: 4, 254 actualSize: 4, 255 success: true, 256 }, 257 { 258 desc: "Mismatching sha256", 259 src: mustReader(t, bytes.NewReader([]byte("abcd")), 4, "", "88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031589", 4), 260 size: 4, 261 actualSize: 4, 262 sha256hex: "50d858e0985ecc7f60418aaf0cc5ab587f42c2570a884095a9e8ccacd0f6545c", 263 success: false, 264 }, 265 { 266 desc: "Correct sha256", 267 src: mustReader(t, bytes.NewReader([]byte("abcd")), 4, "", "88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031589", 4), 268 size: 4, 269 actualSize: 4, 270 sha256hex: "88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031589", 271 success: true, 272 }, 273 { 274 desc: "Mismatching MD5", 275 src: mustReader(t, bytes.NewReader([]byte("abcd")), 4, "e2fc714c4727ee9395f324cd2e7f331f", "", 4), 276 size: 4, 277 actualSize: 4, 278 md5hex: "0773da587b322af3a8718cb418a715ce", 279 success: false, 280 }, 281 { 282 desc: "Correct MD5", 283 src: mustReader(t, bytes.NewReader([]byte("abcd")), 4, "e2fc714c4727ee9395f324cd2e7f331f", "", 4), 284 size: 4, 285 actualSize: 4, 286 md5hex: "e2fc714c4727ee9395f324cd2e7f331f", 287 success: true, 288 }, 289 { 290 desc: "Nothing, all ok", 291 src: bytes.NewReader([]byte("abcd")), 292 size: 4, 293 actualSize: 4, 294 success: true, 295 }, 296 { 297 desc: "Nested, size mismatch", 298 src: mustReader(t, bytes.NewReader([]byte("abcd-morestuff")), 4, "", "", -1), 299 size: 2, 300 actualSize: -1, 301 success: false, 302 }, 303 } 304 305 for i, testCase := range testCases { 306 t.Run(fmt.Sprintf("case-%d", i+1), func(t *testing.T) { 307 _, err := NewReader(context.Background(), testCase.src, testCase.size, testCase.md5hex, testCase.sha256hex, testCase.actualSize) 308 if err != nil && testCase.success { 309 t.Errorf("Test %q: Expected success, but got error %s instead", testCase.desc, err) 310 } 311 if err == nil && !testCase.success { 312 t.Errorf("Test %q: Expected error, but got success", testCase.desc) 313 } 314 }) 315 } 316 }