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  }