github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/cmd/encryption-v1_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 cmd
    19  
    20  import (
    21  	"bytes"
    22  	"encoding/base64"
    23  	"net/http"
    24  	"testing"
    25  
    26  	humanize "github.com/dustin/go-humanize"
    27  	"github.com/minio/minio-go/v7/pkg/encrypt"
    28  	"github.com/minio/minio/internal/crypto"
    29  	xhttp "github.com/minio/minio/internal/http"
    30  	"github.com/minio/sio"
    31  )
    32  
    33  var encryptRequestTests = []struct {
    34  	header   map[string]string
    35  	metadata map[string]string
    36  }{
    37  	{
    38  		header: map[string]string{
    39  			xhttp.AmzServerSideEncryptionCustomerAlgorithm: "AES256",
    40  			xhttp.AmzServerSideEncryptionCustomerKey:       "XAm0dRrJsEsyPb1UuFNezv1bl9hxuYsgUVC/MUctE2k=",
    41  			xhttp.AmzServerSideEncryptionCustomerKeyMD5:    "bY4wkxQejw9mUJfo72k53A==",
    42  		},
    43  		metadata: map[string]string{},
    44  	},
    45  	{
    46  		header: map[string]string{
    47  			xhttp.AmzServerSideEncryptionCustomerAlgorithm: "AES256",
    48  			xhttp.AmzServerSideEncryptionCustomerKey:       "XAm0dRrJsEsyPb1UuFNezv1bl9hxuYsgUVC/MUctE2k=",
    49  			xhttp.AmzServerSideEncryptionCustomerKeyMD5:    "bY4wkxQejw9mUJfo72k53A==",
    50  		},
    51  		metadata: map[string]string{
    52  			xhttp.AmzServerSideEncryptionCustomerKey: "XAm0dRrJsEsyPb1UuFNezv1bl9hxuYsgUVC/MUctE2k=",
    53  		},
    54  	},
    55  }
    56  
    57  func TestEncryptRequest(t *testing.T) {
    58  	defer func(flag bool) { globalIsTLS = flag }(globalIsTLS)
    59  	globalIsTLS = true
    60  	for i, test := range encryptRequestTests {
    61  		content := bytes.NewReader(make([]byte, 64))
    62  		req := &http.Request{Header: http.Header{}}
    63  		for k, v := range test.header {
    64  			req.Header.Set(k, v)
    65  		}
    66  		_, _, err := EncryptRequest(content, req, "bucket", "object", test.metadata)
    67  		if err != nil {
    68  			t.Fatalf("Test %d: Failed to encrypt request: %v", i, err)
    69  		}
    70  		if kdf, ok := test.metadata[crypto.MetaAlgorithm]; !ok {
    71  			t.Errorf("Test %d: ServerSideEncryptionKDF must be part of metadata: %v", i, kdf)
    72  		}
    73  		if iv, ok := test.metadata[crypto.MetaIV]; !ok {
    74  			t.Errorf("Test %d: crypto.SSEIV must be part of metadata: %v", i, iv)
    75  		}
    76  		if mac, ok := test.metadata[crypto.MetaSealedKeySSEC]; !ok {
    77  			t.Errorf("Test %d: ServerSideEncryptionKeyMAC must be part of metadata: %v", i, mac)
    78  		}
    79  	}
    80  }
    81  
    82  var decryptObjectMetaTests = []struct {
    83  	info    ObjectInfo
    84  	request *http.Request
    85  	expErr  error
    86  }{
    87  	{
    88  		info:    ObjectInfo{Size: 100},
    89  		request: &http.Request{Header: http.Header{}},
    90  		expErr:  nil,
    91  	},
    92  	{
    93  		info:    ObjectInfo{Size: 100, UserDefined: map[string]string{crypto.MetaAlgorithm: crypto.InsecureSealAlgorithm}},
    94  		request: &http.Request{Header: http.Header{xhttp.AmzServerSideEncryption: []string{xhttp.AmzEncryptionAES}}},
    95  		expErr:  nil,
    96  	},
    97  	{
    98  		info:    ObjectInfo{Size: 0, UserDefined: map[string]string{crypto.MetaAlgorithm: crypto.InsecureSealAlgorithm}},
    99  		request: &http.Request{Header: http.Header{xhttp.AmzServerSideEncryption: []string{xhttp.AmzEncryptionAES}}},
   100  		expErr:  nil,
   101  	},
   102  	{
   103  		info:    ObjectInfo{Size: 100, UserDefined: map[string]string{crypto.MetaSealedKeySSEC: "EAAfAAAAAAD7v1hQq3PFRUHsItalxmrJqrOq6FwnbXNarxOOpb8jTWONPPKyM3Gfjkjyj6NCf+aB/VpHCLCTBA=="}},
   104  		request: &http.Request{Header: http.Header{}},
   105  		expErr:  errEncryptedObject,
   106  	},
   107  	{
   108  		info:    ObjectInfo{Size: 100, UserDefined: map[string]string{}},
   109  		request: &http.Request{Method: http.MethodGet, Header: http.Header{xhttp.AmzServerSideEncryptionCustomerAlgorithm: []string{xhttp.AmzEncryptionAES}}},
   110  		expErr:  errInvalidEncryptionParameters,
   111  	},
   112  	{
   113  		info:    ObjectInfo{Size: 100, UserDefined: map[string]string{}},
   114  		request: &http.Request{Method: http.MethodHead, Header: http.Header{xhttp.AmzServerSideEncryptionCustomerAlgorithm: []string{xhttp.AmzEncryptionAES}}},
   115  		expErr:  errInvalidEncryptionParameters,
   116  	},
   117  	{
   118  		info:    ObjectInfo{Size: 31, UserDefined: map[string]string{crypto.MetaAlgorithm: crypto.InsecureSealAlgorithm}},
   119  		request: &http.Request{Header: http.Header{xhttp.AmzServerSideEncryptionCustomerAlgorithm: []string{xhttp.AmzEncryptionAES}}},
   120  		expErr:  errObjectTampered,
   121  	},
   122  }
   123  
   124  func TestDecryptObjectInfo(t *testing.T) {
   125  	for i, test := range decryptObjectMetaTests {
   126  		if encrypted, err := DecryptObjectInfo(&test.info, test.request); err != test.expErr {
   127  			t.Errorf("Test %d: Decryption returned wrong error code: got %d , want %d", i, err, test.expErr)
   128  		} else if _, enc := crypto.IsEncrypted(test.info.UserDefined); encrypted && enc != encrypted {
   129  			t.Errorf("Test %d: Decryption thinks object is encrypted but it is not", i)
   130  		} else if !encrypted && enc != encrypted {
   131  			t.Errorf("Test %d: Decryption thinks object is not encrypted but it is", i)
   132  		}
   133  	}
   134  }
   135  
   136  var decryptETagTests = []struct {
   137  	ObjectKey  crypto.ObjectKey
   138  	ObjectInfo ObjectInfo
   139  	ShouldFail bool
   140  	ETag       string
   141  }{
   142  	{
   143  		ObjectKey:  [32]byte{},
   144  		ObjectInfo: ObjectInfo{ETag: "20000f00f27834c9a2654927546df57f9e998187496394d4ee80f3d9978f85f3c7d81f72600cdbe03d80dc5a13d69354"},
   145  		ETag:       "8ad3fe6b84bf38489e95c701c84355b6",
   146  	},
   147  	{
   148  		ObjectKey:  [32]byte{},
   149  		ObjectInfo: ObjectInfo{ETag: "20000f00f27834c9a2654927546df57f9e998187496394d4ee80f3d9978f85f3c7d81f72600cdbe03d80dc5a13d6935"},
   150  		ETag:       "",
   151  		ShouldFail: true, // ETag is not a valid hex value
   152  	},
   153  	{
   154  		ObjectKey:  [32]byte{},
   155  		ObjectInfo: ObjectInfo{ETag: "00000f00f27834c9a2654927546df57f9e998187496394d4ee80f3d9978f85f3c7d81f72600cdbe03d80dc5a13d69354"},
   156  		ETag:       "",
   157  		ShouldFail: true, // modified ETag
   158  	},
   159  
   160  	// Special tests for ETags that end with a '-x'
   161  	{
   162  		ObjectKey:  [32]byte{},
   163  		ObjectInfo: ObjectInfo{ETag: "916516b396f0f4d4f2a0e7177557bec4-1"},
   164  		ETag:       "916516b396f0f4d4f2a0e7177557bec4-1",
   165  	},
   166  	{
   167  		ObjectKey:  [32]byte{},
   168  		ObjectInfo: ObjectInfo{ETag: "916516b396f0f4d4f2a0e7177557bec4-738"},
   169  		ETag:       "916516b396f0f4d4f2a0e7177557bec4-738",
   170  	},
   171  	{
   172  		ObjectKey:  [32]byte{},
   173  		ObjectInfo: ObjectInfo{ETag: "916516b396f0f4d4f2a0e7177557bec4-Q"},
   174  		ETag:       "",
   175  		ShouldFail: true, // Q is not a number
   176  	},
   177  	{
   178  		ObjectKey:  [32]byte{},
   179  		ObjectInfo: ObjectInfo{ETag: "16516b396f0f4d4f2a0e7177557bec4-1"},
   180  		ETag:       "",
   181  		ShouldFail: true, // ETag prefix is not a valid hex value
   182  	},
   183  	{
   184  		ObjectKey:  [32]byte{},
   185  		ObjectInfo: ObjectInfo{ETag: "16516b396f0f4d4f2a0e7177557bec4-1-2"},
   186  		ETag:       "",
   187  		ShouldFail: true, // ETag contains multiple: -
   188  	},
   189  }
   190  
   191  func TestDecryptETag(t *testing.T) {
   192  	for i, test := range decryptETagTests {
   193  		etag, err := DecryptETag(test.ObjectKey, test.ObjectInfo)
   194  		if err != nil && !test.ShouldFail {
   195  			t.Fatalf("Test %d: should succeed but failed: %v", i, err)
   196  		}
   197  		if err == nil && test.ShouldFail {
   198  			t.Fatalf("Test %d: should fail but succeeded", i)
   199  		}
   200  		if err == nil {
   201  			if etag != test.ETag {
   202  				t.Fatalf("Test %d: ETag mismatch: got %s - want %s", i, etag, test.ETag)
   203  			}
   204  		}
   205  	}
   206  }
   207  
   208  // Tests for issue reproduced when getting the right encrypted
   209  // offset of the object.
   210  func TestGetDecryptedRange_Issue50(t *testing.T) {
   211  	rs, err := parseRequestRangeSpec("bytes=594870256-594870263")
   212  	if err != nil {
   213  		t.Fatal(err)
   214  	}
   215  
   216  	objInfo := ObjectInfo{
   217  		Bucket: "bucket",
   218  		Name:   "object",
   219  		Size:   595160760,
   220  		UserDefined: map[string]string{
   221  			crypto.MetaMultipart:                   "",
   222  			crypto.MetaIV:                          "HTexa=",
   223  			crypto.MetaAlgorithm:                   "DAREv2-HMAC-SHA256",
   224  			crypto.MetaSealedKeySSEC:               "IAA8PGAA==",
   225  			ReservedMetadataPrefix + "actual-size": "594870264",
   226  			"content-type":                         "application/octet-stream",
   227  			"etag":                                 "166b1545b4c1535294ee0686678bea8c-2",
   228  		},
   229  		Parts: []ObjectPartInfo{
   230  			{
   231  				Number:     1,
   232  				Size:       297580380,
   233  				ActualSize: 297435132,
   234  			},
   235  			{
   236  				Number:     2,
   237  				Size:       297580380,
   238  				ActualSize: 297435132,
   239  			},
   240  		},
   241  	}
   242  
   243  	encOff, encLength, skipLen, seqNumber, partStart, err := objInfo.GetDecryptedRange(rs)
   244  	if err != nil {
   245  		t.Fatalf("Test: failed %s", err)
   246  	}
   247  	if encOff != 595127964 {
   248  		t.Fatalf("Test: expected %d, got %d", 595127964, encOff)
   249  	}
   250  	if encLength != 32796 {
   251  		t.Fatalf("Test: expected %d, got %d", 32796, encLength)
   252  	}
   253  	if skipLen != 32756 {
   254  		t.Fatalf("Test: expected %d, got %d", 32756, skipLen)
   255  	}
   256  	if seqNumber != 4538 {
   257  		t.Fatalf("Test: expected %d, got %d", 4538, seqNumber)
   258  	}
   259  	if partStart != 1 {
   260  		t.Fatalf("Test: expected %d, got %d", 1, partStart)
   261  	}
   262  }
   263  
   264  func TestGetDecryptedRange(t *testing.T) {
   265  	var (
   266  		pkgSz     = int64(64) * humanize.KiByte
   267  		minPartSz = int64(5) * humanize.MiByte
   268  		maxPartSz = int64(5) * humanize.GiByte
   269  
   270  		getEncSize = func(s int64) int64 {
   271  			v, _ := sio.EncryptedSize(uint64(s))
   272  			return int64(v)
   273  		}
   274  		udMap = func(isMulti bool) map[string]string {
   275  			m := map[string]string{
   276  				crypto.MetaAlgorithm: crypto.InsecureSealAlgorithm,
   277  				crypto.MetaMultipart: "1",
   278  			}
   279  			if !isMulti {
   280  				delete(m, crypto.MetaMultipart)
   281  			}
   282  			return m
   283  		}
   284  	)
   285  
   286  	// Single part object tests
   287  
   288  	mkSPObj := func(s int64) ObjectInfo {
   289  		return ObjectInfo{
   290  			Size:        getEncSize(s),
   291  			UserDefined: udMap(false),
   292  		}
   293  	}
   294  
   295  	testSP := []struct {
   296  		decSz int64
   297  		oi    ObjectInfo
   298  	}{
   299  		{0, mkSPObj(0)},
   300  		{1, mkSPObj(1)},
   301  		{pkgSz - 1, mkSPObj(pkgSz - 1)},
   302  		{pkgSz, mkSPObj(pkgSz)},
   303  		{2*pkgSz - 1, mkSPObj(2*pkgSz - 1)},
   304  		{minPartSz, mkSPObj(minPartSz)},
   305  		{maxPartSz, mkSPObj(maxPartSz)},
   306  	}
   307  
   308  	for i, test := range testSP {
   309  		{
   310  			// nil range
   311  			o, l, skip, sn, ps, err := test.oi.GetDecryptedRange(nil)
   312  			if err != nil {
   313  				t.Errorf("Case %d: unexpected err: %v", i, err)
   314  			}
   315  			if skip != 0 || sn != 0 || ps != 0 || o != 0 || l != getEncSize(test.decSz) {
   316  				t.Errorf("Case %d: test failed: %d %d %d %d %d", i, o, l, skip, sn, ps)
   317  			}
   318  		}
   319  
   320  		if test.decSz >= 10 {
   321  			// first 10 bytes
   322  			o, l, skip, sn, ps, err := test.oi.GetDecryptedRange(&HTTPRangeSpec{false, 0, 9})
   323  			if err != nil {
   324  				t.Errorf("Case %d: unexpected err: %v", i, err)
   325  			}
   326  			rLen := pkgSz + 32
   327  			if test.decSz < pkgSz {
   328  				rLen = test.decSz + 32
   329  			}
   330  			if skip != 0 || sn != 0 || ps != 0 || o != 0 || l != rLen {
   331  				t.Errorf("Case %d: test failed: %d %d %d %d %d", i, o, l, skip, sn, ps)
   332  			}
   333  		}
   334  
   335  		kb32 := int64(32) * humanize.KiByte
   336  		if test.decSz >= (64+32)*humanize.KiByte {
   337  			// Skip the first 32Kib, and read the next 64Kib
   338  			o, l, skip, sn, ps, err := test.oi.GetDecryptedRange(&HTTPRangeSpec{false, kb32, 3*kb32 - 1})
   339  			if err != nil {
   340  				t.Errorf("Case %d: unexpected err: %v", i, err)
   341  			}
   342  			rLen := (pkgSz + 32) * 2
   343  			if test.decSz < 2*pkgSz {
   344  				rLen = (pkgSz + 32) + (test.decSz - pkgSz + 32)
   345  			}
   346  			if skip != kb32 || sn != 0 || ps != 0 || o != 0 || l != rLen {
   347  				t.Errorf("Case %d: test failed: %d %d %d %d %d", i, o, l, skip, sn, ps)
   348  			}
   349  		}
   350  
   351  		if test.decSz >= (64*2+32)*humanize.KiByte {
   352  			// Skip the first 96Kib and read the next 64Kib
   353  			o, l, skip, sn, ps, err := test.oi.GetDecryptedRange(&HTTPRangeSpec{false, 3 * kb32, 5*kb32 - 1})
   354  			if err != nil {
   355  				t.Errorf("Case %d: unexpected err: %v", i, err)
   356  			}
   357  			rLen := (pkgSz + 32) * 2
   358  			if test.decSz-pkgSz < 2*pkgSz {
   359  				rLen = (pkgSz + 32) + (test.decSz - pkgSz + 32*2)
   360  			}
   361  			if skip != kb32 || sn != 1 || ps != 0 || o != pkgSz+32 || l != rLen {
   362  				t.Errorf("Case %d: test failed: %d %d %d %d %d", i, o, l, skip, sn, ps)
   363  			}
   364  		}
   365  
   366  	}
   367  
   368  	// Multipart object tests
   369  	var (
   370  		// make a multipart object-info given part sizes
   371  		mkMPObj = func(sizes []int64) ObjectInfo {
   372  			r := make([]ObjectPartInfo, len(sizes))
   373  			sum := int64(0)
   374  			for i, s := range sizes {
   375  				r[i].Number = i
   376  				r[i].Size = getEncSize(s)
   377  				sum += r[i].Size
   378  			}
   379  			return ObjectInfo{
   380  				Size:        sum,
   381  				UserDefined: udMap(true),
   382  				Parts:       r,
   383  			}
   384  		}
   385  		// Simple useful utilities
   386  		repeat = func(k int64, n int) []int64 {
   387  			a := []int64{}
   388  			for i := 0; i < n; i++ {
   389  				a = append(a, k)
   390  			}
   391  			return a
   392  		}
   393  		lsum = func(s []int64) int64 {
   394  			sum := int64(0)
   395  			for _, i := range s {
   396  				if i < 0 {
   397  					return -1
   398  				}
   399  				sum += i
   400  			}
   401  			return sum
   402  		}
   403  		esum = func(oi ObjectInfo) int64 {
   404  			sum := int64(0)
   405  			for _, i := range oi.Parts {
   406  				sum += i.Size
   407  			}
   408  			return sum
   409  		}
   410  	)
   411  
   412  	s1 := []int64{5487701, 5487799, 3}
   413  	s2 := repeat(5487701, 5)
   414  	s3 := repeat(maxPartSz, 10000)
   415  	testMPs := []struct {
   416  		decSizes []int64
   417  		oi       ObjectInfo
   418  	}{
   419  		{s1, mkMPObj(s1)},
   420  		{s2, mkMPObj(s2)},
   421  		{s3, mkMPObj(s3)},
   422  	}
   423  
   424  	// This function is a reference (re-)implementation of
   425  	// decrypted range computation, written solely for the purpose
   426  	// of the unit tests.
   427  	//
   428  	// `s` gives the decrypted part sizes, and the other
   429  	// parameters describe the desired read segment. When
   430  	// `isFromEnd` is true, `skipLen` argument is ignored.
   431  	decryptedRangeRef := func(s []int64, skipLen, readLen int64, isFromEnd bool) (o, l, skip int64, sn uint32, ps int) {
   432  		oSize := lsum(s)
   433  		if isFromEnd {
   434  			skipLen = oSize - readLen
   435  		}
   436  		if skipLen < 0 || readLen < 0 || oSize < 0 || skipLen+readLen > oSize {
   437  			t.Fatalf("Impossible read specified: %d %d %d", skipLen, readLen, oSize)
   438  		}
   439  
   440  		var cumulativeSum, cumulativeEncSum int64
   441  		toRead := readLen
   442  		readStart := false
   443  		for i, v := range s {
   444  			partOffset := int64(0)
   445  			partDarePkgOffset := int64(0)
   446  			if !readStart && cumulativeSum+v > skipLen {
   447  				// Read starts at the current part
   448  				readStart = true
   449  
   450  				partOffset = skipLen - cumulativeSum
   451  
   452  				// All return values except `l` are
   453  				// calculated here.
   454  				sn = uint32(partOffset / pkgSz)
   455  				skip = partOffset % pkgSz
   456  				ps = i
   457  				o = cumulativeEncSum + int64(sn)*(pkgSz+32)
   458  
   459  				partDarePkgOffset = partOffset - skip
   460  			}
   461  			if readStart {
   462  				currentPartBytes := v - partOffset
   463  				currentPartDareBytes := v - partDarePkgOffset
   464  				if currentPartBytes < toRead {
   465  					toRead -= currentPartBytes
   466  					l += getEncSize(currentPartDareBytes)
   467  				} else {
   468  					// current part has the last
   469  					// byte required
   470  					lbPartOffset := partOffset + toRead - 1
   471  
   472  					// round up the lbPartOffset
   473  					// to the end of the
   474  					// corresponding DARE package
   475  					lbPkgEndOffset := lbPartOffset - (lbPartOffset % pkgSz) + pkgSz
   476  					if lbPkgEndOffset > v {
   477  						lbPkgEndOffset = v
   478  					}
   479  					bytesToDrop := v - lbPkgEndOffset
   480  
   481  					// Last segment to update `l`
   482  					l += getEncSize(currentPartDareBytes - bytesToDrop)
   483  					break
   484  				}
   485  			}
   486  
   487  			cumulativeSum += v
   488  			cumulativeEncSum += getEncSize(v)
   489  		}
   490  		return
   491  	}
   492  
   493  	for i, test := range testMPs {
   494  		{
   495  			// nil range
   496  			o, l, skip, sn, ps, err := test.oi.GetDecryptedRange(nil)
   497  			if err != nil {
   498  				t.Errorf("Case %d: unexpected err: %v", i, err)
   499  			}
   500  			if o != 0 || l != esum(test.oi) || skip != 0 || sn != 0 || ps != 0 {
   501  				t.Errorf("Case %d: test failed: %d %d %d %d %d", i, o, l, skip, sn, ps)
   502  			}
   503  		}
   504  
   505  		// Skip 1Mib and read 1Mib (in the decrypted object)
   506  		//
   507  		// The check below ensures the object is large enough
   508  		// for the read.
   509  		if lsum(test.decSizes) >= 2*humanize.MiByte {
   510  			skipLen, readLen := int64(1)*humanize.MiByte, int64(1)*humanize.MiByte
   511  			o, l, skip, sn, ps, err := test.oi.GetDecryptedRange(&HTTPRangeSpec{false, skipLen, skipLen + readLen - 1})
   512  			if err != nil {
   513  				t.Errorf("Case %d: unexpected err: %v", i, err)
   514  			}
   515  
   516  			oRef, lRef, skipRef, snRef, psRef := decryptedRangeRef(test.decSizes, skipLen, readLen, false)
   517  			if o != oRef || l != lRef || skip != skipRef || sn != snRef || ps != psRef {
   518  				t.Errorf("Case %d: test failed: %d %d %d %d %d (Ref: %d %d %d %d %d)",
   519  					i, o, l, skip, sn, ps, oRef, lRef, skipRef, snRef, psRef)
   520  			}
   521  		}
   522  
   523  		// Read the last 6Mib+1 bytes of the (decrypted)
   524  		// object
   525  		//
   526  		// The check below ensures the object is large enough
   527  		// for the read.
   528  		readLen := int64(6)*humanize.MiByte + 1
   529  		if lsum(test.decSizes) >= readLen {
   530  			o, l, skip, sn, ps, err := test.oi.GetDecryptedRange(&HTTPRangeSpec{true, -readLen, -1})
   531  			if err != nil {
   532  				t.Errorf("Case %d: unexpected err: %v", i, err)
   533  			}
   534  
   535  			oRef, lRef, skipRef, snRef, psRef := decryptedRangeRef(test.decSizes, 0, readLen, true)
   536  			if o != oRef || l != lRef || skip != skipRef || sn != snRef || ps != psRef {
   537  				t.Errorf("Case %d: test failed: %d %d %d %d %d (Ref: %d %d %d %d %d)",
   538  					i, o, l, skip, sn, ps, oRef, lRef, skipRef, snRef, psRef)
   539  			}
   540  		}
   541  
   542  	}
   543  }
   544  
   545  var getDefaultOptsTests = []struct {
   546  	headers        http.Header
   547  	copySource     bool
   548  	metadata       map[string]string
   549  	encryptionType encrypt.Type
   550  	err            error
   551  }{
   552  	{
   553  		headers: http.Header{
   554  			xhttp.AmzServerSideEncryptionCustomerAlgorithm: []string{"AES256"},
   555  			xhttp.AmzServerSideEncryptionCustomerKey:       []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
   556  			xhttp.AmzServerSideEncryptionCustomerKeyMD5:    []string{"7PpPLAK26ONlVUGOWlusfg=="},
   557  		},
   558  		copySource:     false,
   559  		metadata:       nil,
   560  		encryptionType: encrypt.SSEC,
   561  		err:            nil,
   562  	}, // 0
   563  	{
   564  		headers: http.Header{
   565  			xhttp.AmzServerSideEncryptionCustomerAlgorithm: []string{"AES256"},
   566  			xhttp.AmzServerSideEncryptionCustomerKey:       []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
   567  			xhttp.AmzServerSideEncryptionCustomerKeyMD5:    []string{"7PpPLAK26ONlVUGOWlusfg=="},
   568  		},
   569  		copySource:     true,
   570  		metadata:       nil,
   571  		encryptionType: "",
   572  		err:            nil,
   573  	}, // 1
   574  	{
   575  		headers: http.Header{
   576  			xhttp.AmzServerSideEncryptionCustomerAlgorithm: []string{"AES256"},
   577  			xhttp.AmzServerSideEncryptionCustomerKey:       []string{"Mz"},
   578  			xhttp.AmzServerSideEncryptionCustomerKeyMD5:    []string{"7PpPLAK26ONlVUGOWlusfg=="},
   579  		},
   580  		copySource:     false,
   581  		metadata:       nil,
   582  		encryptionType: "",
   583  		err:            crypto.ErrInvalidCustomerKey,
   584  	}, // 2
   585  	{
   586  		headers:        http.Header{xhttp.AmzServerSideEncryption: []string{"AES256"}},
   587  		copySource:     false,
   588  		metadata:       nil,
   589  		encryptionType: encrypt.S3,
   590  		err:            nil,
   591  	}, // 3
   592  	{
   593  		headers:    http.Header{},
   594  		copySource: false,
   595  		metadata: map[string]string{
   596  			crypto.MetaSealedKeyS3:       base64.StdEncoding.EncodeToString(make([]byte, 64)),
   597  			crypto.MetaKeyID:             "kms-key",
   598  			crypto.MetaDataEncryptionKey: "m-key",
   599  		},
   600  		encryptionType: encrypt.S3,
   601  		err:            nil,
   602  	}, // 4
   603  	{
   604  		headers:    http.Header{},
   605  		copySource: true,
   606  		metadata: map[string]string{
   607  			crypto.MetaSealedKeyS3:       base64.StdEncoding.EncodeToString(make([]byte, 64)),
   608  			crypto.MetaKeyID:             "kms-key",
   609  			crypto.MetaDataEncryptionKey: "m-key",
   610  		},
   611  		encryptionType: "",
   612  		err:            nil,
   613  	}, // 5
   614  	{
   615  		headers: http.Header{
   616  			xhttp.AmzServerSideEncryptionCopyCustomerAlgorithm: []string{"AES256"},
   617  			xhttp.AmzServerSideEncryptionCopyCustomerKey:       []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
   618  			xhttp.AmzServerSideEncryptionCopyCustomerKeyMD5:    []string{"7PpPLAK26ONlVUGOWlusfg=="},
   619  		},
   620  		copySource:     true,
   621  		metadata:       nil,
   622  		encryptionType: encrypt.SSEC,
   623  		err:            nil,
   624  	}, // 6
   625  	{
   626  		headers: http.Header{
   627  			xhttp.AmzServerSideEncryptionCopyCustomerAlgorithm: []string{"AES256"},
   628  			xhttp.AmzServerSideEncryptionCopyCustomerKey:       []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
   629  			xhttp.AmzServerSideEncryptionCopyCustomerKeyMD5:    []string{"7PpPLAK26ONlVUGOWlusfg=="},
   630  		},
   631  		copySource:     false,
   632  		metadata:       nil,
   633  		encryptionType: "",
   634  		err:            nil,
   635  	}, // 7
   636  }
   637  
   638  func TestGetDefaultOpts(t *testing.T) {
   639  	for i, test := range getDefaultOptsTests {
   640  		opts, err := getDefaultOpts(test.headers, test.copySource, test.metadata)
   641  		if test.err != err {
   642  			t.Errorf("Case %d: expected err: %v , actual err: %v", i, test.err, err)
   643  		}
   644  		if err == nil {
   645  			if opts.ServerSideEncryption == nil && test.encryptionType != "" {
   646  				t.Errorf("Case %d: expected opts to be of %v encryption type", i, test.encryptionType)
   647  			}
   648  			if opts.ServerSideEncryption != nil && test.encryptionType != opts.ServerSideEncryption.Type() {
   649  				t.Errorf("Case %d: expected opts to have encryption type %v but was %v ", i, test.encryptionType, opts.ServerSideEncryption.Type())
   650  			}
   651  		}
   652  	}
   653  }