github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/internal/crypto/header_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 crypto
    19  
    20  import (
    21  	"encoding/base64"
    22  	"net/http"
    23  	"sort"
    24  	"testing"
    25  
    26  	xhttp "github.com/minio/minio/internal/http"
    27  )
    28  
    29  func TestIsRequested(t *testing.T) {
    30  	for i, test := range kmsIsRequestedTests {
    31  		_, got := IsRequested(test.Header)
    32  		if Requested(test.Header) != got {
    33  			// Test if result matches.
    34  			t.Errorf("Requested mismatch, want %v, got %v", Requested(test.Header), got)
    35  		}
    36  		got = got && S3KMS.IsRequested(test.Header)
    37  		if got != test.Expected {
    38  			t.Errorf("SSE-KMS: Test %d: Wanted %v but got %v", i, test.Expected, got)
    39  		}
    40  	}
    41  	for i, test := range s3IsRequestedTests {
    42  		_, got := IsRequested(test.Header)
    43  		if Requested(test.Header) != got {
    44  			// Test if result matches.
    45  			t.Errorf("Requested mismatch, want %v, got %v", Requested(test.Header), got)
    46  		}
    47  		got = got && S3.IsRequested(test.Header)
    48  		if got != test.Expected {
    49  			t.Errorf("SSE-S3: Test %d: Wanted %v but got %v", i, test.Expected, got)
    50  		}
    51  	}
    52  	for i, test := range ssecIsRequestedTests {
    53  		_, got := IsRequested(test.Header)
    54  		if Requested(test.Header) != got {
    55  			// Test if result matches.
    56  			t.Errorf("Requested mismatch, want %v, got %v", Requested(test.Header), got)
    57  		}
    58  		got = got && SSEC.IsRequested(test.Header)
    59  		if got != test.Expected {
    60  			t.Errorf("SSE-C: Test %d: Wanted %v but got %v", i, test.Expected, got)
    61  		}
    62  	}
    63  }
    64  
    65  var kmsIsRequestedTests = []struct {
    66  	Header   http.Header
    67  	Expected bool
    68  }{
    69  	{Header: http.Header{}, Expected: false},                                                                                     // 0
    70  	{Header: http.Header{"X-Amz-Server-Side-Encryption": []string{"aws:kms"}}, Expected: true},                                   // 1
    71  	{Header: http.Header{"X-Amz-Server-Side-Encryption-Aws-Kms-Key-Id": []string{"0839-9047947-844842874-481"}}, Expected: true}, // 2
    72  	{Header: http.Header{"X-Amz-Server-Side-Encryption-Context": []string{"7PpPLAK26ONlVUGOWlusfg=="}}, Expected: true},          // 3
    73  	{
    74  		Header: http.Header{
    75  			"X-Amz-Server-Side-Encryption":                []string{""},
    76  			"X-Amz-Server-Side-Encryption-Aws-Kms-Key-Id": []string{""},
    77  			"X-Amz-Server-Side-Encryption-Context":        []string{""},
    78  		},
    79  		Expected: true,
    80  	}, // 4
    81  	{
    82  		Header: http.Header{
    83  			"X-Amz-Server-Side-Encryption":                []string{"AES256"},
    84  			"X-Amz-Server-Side-Encryption-Aws-Kms-Key-Id": []string{""},
    85  		},
    86  		Expected: true,
    87  	}, // 5
    88  	{Header: http.Header{"X-Amz-Server-Side-Encryption": []string{"AES256"}}, Expected: false}, // 6
    89  }
    90  
    91  func TestKMSIsRequested(t *testing.T) {
    92  	for i, test := range kmsIsRequestedTests {
    93  		if got := S3KMS.IsRequested(test.Header); got != test.Expected {
    94  			t.Errorf("Test %d: Wanted %v but got %v", i, test.Expected, got)
    95  		}
    96  	}
    97  }
    98  
    99  var kmsParseHTTPTests = []struct {
   100  	Header     http.Header
   101  	ShouldFail bool
   102  }{
   103  	{Header: http.Header{}, ShouldFail: true},                                                     // 0
   104  	{Header: http.Header{"X-Amz-Server-Side-Encryption": []string{"aws:kms"}}, ShouldFail: false}, // 1
   105  	{Header: http.Header{
   106  		"X-Amz-Server-Side-Encryption":                []string{"aws:kms"},
   107  		"X-Amz-Server-Side-Encryption-Aws-Kms-Key-Id": []string{"s3-007-293847485-724784"},
   108  	}, ShouldFail: false}, // 2
   109  	{Header: http.Header{
   110  		"X-Amz-Server-Side-Encryption":                []string{"aws:kms"},
   111  		"X-Amz-Server-Side-Encryption-Aws-Kms-Key-Id": []string{"s3-007-293847485-724784"},
   112  		"X-Amz-Server-Side-Encryption-Context":        []string{base64.StdEncoding.EncodeToString([]byte("{}"))},
   113  	}, ShouldFail: false}, // 3
   114  	{Header: http.Header{
   115  		"X-Amz-Server-Side-Encryption":                []string{"aws:kms"},
   116  		"X-Amz-Server-Side-Encryption-Aws-Kms-Key-Id": []string{"s3-007-293847485-724784"},
   117  		"X-Amz-Server-Side-Encryption-Context":        []string{base64.StdEncoding.EncodeToString([]byte(`{"bucket": "some-bucket"}`))},
   118  	}, ShouldFail: false}, // 4
   119  	{Header: http.Header{
   120  		"X-Amz-Server-Side-Encryption":                []string{"aws:kms"},
   121  		"X-Amz-Server-Side-Encryption-Aws-Kms-Key-Id": []string{"s3-007-293847485-724784"},
   122  		"X-Amz-Server-Side-Encryption-Context":        []string{base64.StdEncoding.EncodeToString([]byte(`{"bucket": "some-bucket"}`))},
   123  	}, ShouldFail: false}, // 5
   124  	{Header: http.Header{
   125  		"X-Amz-Server-Side-Encryption":                []string{"AES256"},
   126  		"X-Amz-Server-Side-Encryption-Aws-Kms-Key-Id": []string{"s3-007-293847485-724784"},
   127  		"X-Amz-Server-Side-Encryption-Context":        []string{base64.StdEncoding.EncodeToString([]byte(`{"bucket": "some-bucket"}`))},
   128  	}, ShouldFail: true}, // 6
   129  	{Header: http.Header{
   130  		"X-Amz-Server-Side-Encryption":                []string{"aws:kms"},
   131  		"X-Amz-Server-Side-Encryption-Aws-Kms-Key-Id": []string{"s3-007-293847485-724784"},
   132  		"X-Amz-Server-Side-Encryption-Context":        []string{base64.StdEncoding.EncodeToString([]byte(`{"bucket": "some-bucket"`))}, // invalid JSON
   133  	}, ShouldFail: true}, // 7
   134  
   135  }
   136  
   137  func TestKMSParseHTTP(t *testing.T) {
   138  	for i, test := range kmsParseHTTPTests {
   139  		_, _, err := S3KMS.ParseHTTP(test.Header)
   140  		if err == nil && test.ShouldFail {
   141  			t.Errorf("Test %d: should fail but succeeded", i)
   142  		}
   143  		if err != nil && !test.ShouldFail {
   144  			t.Errorf("Test %d: should pass but failed with: %v", i, err)
   145  		}
   146  	}
   147  }
   148  
   149  var s3IsRequestedTests = []struct {
   150  	Header   http.Header
   151  	Expected bool
   152  }{
   153  	{Header: http.Header{"X-Amz-Server-Side-Encryption": []string{"AES256"}}, Expected: true},                // 0
   154  	{Header: http.Header{"X-Amz-Server-Side-Encryption": []string{"AES-256"}}, Expected: true},               // 1
   155  	{Header: http.Header{"X-Amz-Server-Side-Encryption": []string{""}}, Expected: true},                      // 2
   156  	{Header: http.Header{"X-Amz-Server-Side-Encryptio": []string{"AES256"}}, Expected: false},                // 3
   157  	{Header: http.Header{"X-Amz-Server-Side-Encryption": []string{xhttp.AmzEncryptionKMS}}, Expected: false}, // 4
   158  }
   159  
   160  func TestS3IsRequested(t *testing.T) {
   161  	for i, test := range s3IsRequestedTests {
   162  		if got := S3.IsRequested(test.Header); got != test.Expected {
   163  			t.Errorf("Test %d: Wanted %v but got %v", i, test.Expected, got)
   164  		}
   165  	}
   166  }
   167  
   168  var s3ParseTests = []struct {
   169  	Header      http.Header
   170  	ExpectedErr error
   171  }{
   172  	{Header: http.Header{"X-Amz-Server-Side-Encryption": []string{"AES256"}}, ExpectedErr: nil},                         // 0
   173  	{Header: http.Header{"X-Amz-Server-Side-Encryption": []string{"AES-256"}}, ExpectedErr: ErrInvalidEncryptionMethod}, // 1
   174  	{Header: http.Header{"X-Amz-Server-Side-Encryption": []string{""}}, ExpectedErr: ErrInvalidEncryptionMethod},        // 2
   175  	{Header: http.Header{"X-Amz-Server-Side-Encryptio": []string{"AES256"}}, ExpectedErr: ErrInvalidEncryptionMethod},   // 3
   176  }
   177  
   178  func TestS3Parse(t *testing.T) {
   179  	for i, test := range s3ParseTests {
   180  		if err := S3.ParseHTTP(test.Header); err != test.ExpectedErr {
   181  			t.Errorf("Test %d: Wanted '%v' but got '%v'", i, test.ExpectedErr, err)
   182  		}
   183  	}
   184  }
   185  
   186  var ssecIsRequestedTests = []struct {
   187  	Header   http.Header
   188  	Expected bool
   189  }{
   190  	{Header: http.Header{}, Expected: false}, // 0
   191  	{Header: http.Header{"X-Amz-Server-Side-Encryption-Customer-Algorithm": []string{"AES256"}}, Expected: true},                                 // 1
   192  	{Header: http.Header{"X-Amz-Server-Side-Encryption-Customer-Key": []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="}}, Expected: true}, // 2
   193  	{Header: http.Header{"X-Amz-Server-Side-Encryption-Customer-Key-Md5": []string{"7PpPLAK26ONlVUGOWlusfg=="}}, Expected: true},                 // 3
   194  	{
   195  		Header: http.Header{
   196  			"X-Amz-Server-Side-Encryption-Customer-Algorithm": []string{""},
   197  			"X-Amz-Server-Side-Encryption-Customer-Key":       []string{""},
   198  			"X-Amz-Server-Side-Encryption-Customer-Key-Md5":   []string{""},
   199  		},
   200  		Expected: true,
   201  	}, // 4
   202  	{
   203  		Header: http.Header{
   204  			"X-Amz-Server-Side-Encryption-Customer-Algorithm": []string{"AES256"},
   205  			"X-Amz-Server-Side-Encryption-Customer-Key":       []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
   206  			"X-Amz-Server-Side-Encryption-Customer-Key-Md5":   []string{"7PpPLAK26ONlVUGOWlusfg=="},
   207  		},
   208  		Expected: true,
   209  	}, // 5
   210  	{
   211  		Header: http.Header{
   212  			"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Algorithm": []string{"AES256"},
   213  			"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key":       []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
   214  			"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key-Md5":   []string{"7PpPLAK26ONlVUGOWlusfg=="},
   215  		},
   216  		Expected: false,
   217  	}, // 6
   218  }
   219  
   220  func TestSSECIsRequested(t *testing.T) {
   221  	for i, test := range ssecIsRequestedTests {
   222  		if got := SSEC.IsRequested(test.Header); got != test.Expected {
   223  			t.Errorf("Test %d: Wanted %v but got %v", i, test.Expected, got)
   224  		}
   225  	}
   226  }
   227  
   228  var ssecCopyIsRequestedTests = []struct {
   229  	Header   http.Header
   230  	Expected bool
   231  }{
   232  	{Header: http.Header{}, Expected: false}, // 0
   233  	{Header: http.Header{"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Algorithm": []string{"AES256"}}, Expected: true},                                 // 1
   234  	{Header: http.Header{"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key": []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="}}, Expected: true}, // 2
   235  	{Header: http.Header{"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key-Md5": []string{"7PpPLAK26ONlVUGOWlusfg=="}}, Expected: true},                 // 3
   236  	{
   237  		Header: http.Header{
   238  			"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Algorithm": []string{""},
   239  			"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key":       []string{""},
   240  			"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key-Md5":   []string{""},
   241  		},
   242  		Expected: true,
   243  	}, // 4
   244  	{
   245  		Header: http.Header{
   246  			"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Algorithm": []string{"AES256"},
   247  			"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key":       []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
   248  			"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key-Md5":   []string{"7PpPLAK26ONlVUGOWlusfg=="},
   249  		},
   250  		Expected: true,
   251  	}, // 5
   252  	{
   253  		Header: http.Header{
   254  			"X-Amz-Server-Side-Encryption-Customer-Algorithm": []string{"AES256"},
   255  			"X-Amz-Server-Side-Encryption-Customer-Key":       []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
   256  			"X-Amz-Server-Side-Encryption-Customer-Key-Md5":   []string{"7PpPLAK26ONlVUGOWlusfg=="},
   257  		},
   258  		Expected: false,
   259  	}, // 6
   260  }
   261  
   262  func TestSSECopyIsRequested(t *testing.T) {
   263  	for i, test := range ssecCopyIsRequestedTests {
   264  		if got := SSECopy.IsRequested(test.Header); got != test.Expected {
   265  			t.Errorf("Test %d: Wanted %v but got %v", i, test.Expected, got)
   266  		}
   267  	}
   268  }
   269  
   270  var ssecParseTests = []struct {
   271  	Header      http.Header
   272  	ExpectedErr error
   273  }{
   274  	{
   275  		Header: http.Header{
   276  			"X-Amz-Server-Side-Encryption-Customer-Algorithm": []string{"AES256"},
   277  			"X-Amz-Server-Side-Encryption-Customer-Key":       []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
   278  			"X-Amz-Server-Side-Encryption-Customer-Key-Md5":   []string{"7PpPLAK26ONlVUGOWlusfg=="},
   279  		},
   280  		ExpectedErr: nil, // 0
   281  	},
   282  	{
   283  		Header: http.Header{
   284  			"X-Amz-Server-Side-Encryption-Customer-Algorithm": []string{"AES-256"}, // invalid algorithm
   285  			"X-Amz-Server-Side-Encryption-Customer-Key":       []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
   286  			"X-Amz-Server-Side-Encryption-Customer-Key-Md5":   []string{"7PpPLAK26ONlVUGOWlusfg=="},
   287  		},
   288  		ExpectedErr: ErrInvalidCustomerAlgorithm, // 1
   289  	},
   290  	{
   291  		Header: http.Header{
   292  			"X-Amz-Server-Side-Encryption-Customer-Algorithm": []string{"AES256"},
   293  			"X-Amz-Server-Side-Encryption-Customer-Key":       []string{""}, // no client key
   294  			"X-Amz-Server-Side-Encryption-Customer-Key-Md5":   []string{"7PpPLAK26ONlVUGOWlusfg=="},
   295  		},
   296  		ExpectedErr: ErrMissingCustomerKey, // 2
   297  	},
   298  	{
   299  		Header: http.Header{
   300  			"X-Amz-Server-Side-Encryption-Customer-Algorithm": []string{"AES256"},
   301  			"X-Amz-Server-Side-Encryption-Customer-Key":       []string{"MzJieXRlc2xvbmdzZWNyZXRr.ZXltdXN0cHJvdmlkZWQ="}, // invalid key
   302  			"X-Amz-Server-Side-Encryption-Customer-Key-Md5":   []string{"7PpPLAK26ONlVUGOWlusfg=="},
   303  		},
   304  		ExpectedErr: ErrInvalidCustomerKey, // 3
   305  	},
   306  	{
   307  		Header: http.Header{
   308  			"X-Amz-Server-Side-Encryption-Customer-Algorithm": []string{"AES256"},
   309  			"X-Amz-Server-Side-Encryption-Customer-Key":       []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
   310  			"X-Amz-Server-Side-Encryption-Customer-Key-Md5":   []string{""}, // no key MD5
   311  		},
   312  		ExpectedErr: ErrMissingCustomerKeyMD5, // 4
   313  	},
   314  	{
   315  		Header: http.Header{
   316  			"X-Amz-Server-Side-Encryption-Customer-Algorithm": []string{"AES256"},
   317  			"X-Amz-Server-Side-Encryption-Customer-Key":       []string{"DzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="}, // wrong client key
   318  			"X-Amz-Server-Side-Encryption-Customer-Key-Md5":   []string{"7PpPLAK26ONlVUGOWlusfg=="},
   319  		},
   320  		ExpectedErr: ErrCustomerKeyMD5Mismatch, // 5
   321  	},
   322  	{
   323  		Header: http.Header{
   324  			"X-Amz-Server-Side-Encryption-Customer-Algorithm": []string{"AES256"},
   325  			"X-Amz-Server-Side-Encryption-Customer-Key":       []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
   326  			"X-Amz-Server-Side-Encryption-Customer-Key-Md5":   []string{".7PpPLAK26ONlVUGOWlusfg=="}, // wrong key MD5
   327  		},
   328  		ExpectedErr: ErrCustomerKeyMD5Mismatch, // 6
   329  	},
   330  }
   331  
   332  func TestSSECParse(t *testing.T) {
   333  	var zeroKey [32]byte
   334  	for i, test := range ssecParseTests {
   335  		key, err := SSEC.ParseHTTP(test.Header)
   336  		if err != test.ExpectedErr {
   337  			t.Errorf("Test %d: want error '%v' but got '%v'", i, test.ExpectedErr, err)
   338  		}
   339  
   340  		if err != nil && key != zeroKey {
   341  			t.Errorf("Test %d: parsing failed and client key is not zero key", i)
   342  		}
   343  		if err == nil && key == zeroKey {
   344  			t.Errorf("Test %d: parsed client key is zero key", i)
   345  		}
   346  	}
   347  }
   348  
   349  var ssecCopyParseTests = []struct {
   350  	Header      http.Header
   351  	ExpectedErr error
   352  }{
   353  	{
   354  		Header: http.Header{
   355  			"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Algorithm": []string{"AES256"},
   356  			"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key":       []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
   357  			"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key-Md5":   []string{"7PpPLAK26ONlVUGOWlusfg=="},
   358  		},
   359  		ExpectedErr: nil, // 0
   360  	},
   361  	{
   362  		Header: http.Header{
   363  			"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Algorithm": []string{"AES-256"}, // invalid algorithm
   364  			"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key":       []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
   365  			"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key-Md5":   []string{"7PpPLAK26ONlVUGOWlusfg=="},
   366  		},
   367  		ExpectedErr: ErrInvalidCustomerAlgorithm, // 1
   368  	},
   369  	{
   370  		Header: http.Header{
   371  			"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Algorithm": []string{"AES256"},
   372  			"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key":       []string{""}, // no client key
   373  			"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key-Md5":   []string{"7PpPLAK26ONlVUGOWlusfg=="},
   374  		},
   375  		ExpectedErr: ErrMissingCustomerKey, // 2
   376  	},
   377  	{
   378  		Header: http.Header{
   379  			"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Algorithm": []string{"AES256"},
   380  			"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key":       []string{"MzJieXRlc2xvbmdzZWNyZXRr.ZXltdXN0cHJvdmlkZWQ="}, // invalid key
   381  			"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key-Md5":   []string{"7PpPLAK26ONlVUGOWlusfg=="},
   382  		},
   383  		ExpectedErr: ErrInvalidCustomerKey, // 3
   384  	},
   385  	{
   386  		Header: http.Header{
   387  			"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Algorithm": []string{"AES256"},
   388  			"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key":       []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
   389  			"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key-Md5":   []string{""}, // no key MD5
   390  		},
   391  		ExpectedErr: ErrMissingCustomerKeyMD5, // 4
   392  	},
   393  	{
   394  		Header: http.Header{
   395  			"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Algorithm": []string{"AES256"},
   396  			"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key":       []string{"DzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="}, // wrong client key
   397  			"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key-Md5":   []string{"7PpPLAK26ONlVUGOWlusfg=="},
   398  		},
   399  		ExpectedErr: ErrCustomerKeyMD5Mismatch, // 5
   400  	},
   401  	{
   402  		Header: http.Header{
   403  			"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Algorithm": []string{"AES256"},
   404  			"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key":       []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
   405  			"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key-Md5":   []string{".7PpPLAK26ONlVUGOWlusfg=="}, // wrong key MD5
   406  		},
   407  		ExpectedErr: ErrCustomerKeyMD5Mismatch, // 6
   408  	},
   409  }
   410  
   411  func TestSSECopyParse(t *testing.T) {
   412  	var zeroKey [32]byte
   413  	for i, test := range ssecCopyParseTests {
   414  		key, err := SSECopy.ParseHTTP(test.Header)
   415  		if err != test.ExpectedErr {
   416  			t.Errorf("Test %d: want error '%v' but got '%v'", i, test.ExpectedErr, err)
   417  		}
   418  
   419  		if err != nil && key != zeroKey {
   420  			t.Errorf("Test %d: parsing failed and client key is not zero key", i)
   421  		}
   422  		if err == nil && key == zeroKey {
   423  			t.Errorf("Test %d: parsed client key is zero key", i)
   424  		}
   425  		if _, ok := test.Header[xhttp.AmzServerSideEncryptionCustomerKey]; ok {
   426  			t.Errorf("Test %d: client key is not removed from HTTP headers after parsing", i)
   427  		}
   428  	}
   429  }
   430  
   431  var removeSensitiveHeadersTests = []struct {
   432  	Header, ExpectedHeader http.Header
   433  }{
   434  	{
   435  		Header: http.Header{
   436  			xhttp.AmzServerSideEncryptionCustomerKey:     []string{""},
   437  			xhttp.AmzServerSideEncryptionCopyCustomerKey: []string{""},
   438  		},
   439  		ExpectedHeader: http.Header{},
   440  	},
   441  	{ // Standard SSE-C request headers
   442  		Header: http.Header{
   443  			xhttp.AmzServerSideEncryptionCustomerAlgorithm: []string{xhttp.AmzEncryptionAES},
   444  			xhttp.AmzServerSideEncryptionCustomerKey:       []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
   445  			xhttp.AmzServerSideEncryptionCustomerKeyMD5:    []string{"7PpPLAK26ONlVUGOWlusfg=="},
   446  		},
   447  		ExpectedHeader: http.Header{
   448  			xhttp.AmzServerSideEncryptionCustomerAlgorithm: []string{xhttp.AmzEncryptionAES},
   449  			xhttp.AmzServerSideEncryptionCustomerKeyMD5:    []string{"7PpPLAK26ONlVUGOWlusfg=="},
   450  		},
   451  	},
   452  	{ // Standard SSE-C + SSE-C-copy request headers
   453  		Header: http.Header{
   454  			xhttp.AmzServerSideEncryptionCustomerAlgorithm:  []string{xhttp.AmzEncryptionAES},
   455  			xhttp.AmzServerSideEncryptionCustomerKey:        []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
   456  			xhttp.AmzServerSideEncryptionCustomerKeyMD5:     []string{"7PpPLAK26ONlVUGOWlusfg=="},
   457  			xhttp.AmzServerSideEncryptionCopyCustomerKey:    []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
   458  			xhttp.AmzServerSideEncryptionCopyCustomerKeyMD5: []string{"7PpPLAK26ONlVUGOWlusfg=="},
   459  		},
   460  		ExpectedHeader: http.Header{
   461  			xhttp.AmzServerSideEncryptionCustomerAlgorithm:  []string{xhttp.AmzEncryptionAES},
   462  			xhttp.AmzServerSideEncryptionCustomerKeyMD5:     []string{"7PpPLAK26ONlVUGOWlusfg=="},
   463  			xhttp.AmzServerSideEncryptionCopyCustomerKeyMD5: []string{"7PpPLAK26ONlVUGOWlusfg=="},
   464  		},
   465  	},
   466  	{ // Standard SSE-C + metadata request headers
   467  		Header: http.Header{
   468  			xhttp.AmzServerSideEncryptionCustomerAlgorithm: []string{xhttp.AmzEncryptionAES},
   469  			xhttp.AmzServerSideEncryptionCustomerKey:       []string{"MzJieXRlc2xvbmdzZWNyZXRrZXltdXN0cHJvdmlkZWQ="},
   470  			xhttp.AmzServerSideEncryptionCustomerKeyMD5:    []string{"7PpPLAK26ONlVUGOWlusfg=="},
   471  			"X-Amz-Meta-Test-1":                            []string{"Test-1"},
   472  		},
   473  		ExpectedHeader: http.Header{
   474  			xhttp.AmzServerSideEncryptionCustomerAlgorithm: []string{xhttp.AmzEncryptionAES},
   475  			xhttp.AmzServerSideEncryptionCustomerKeyMD5:    []string{"7PpPLAK26ONlVUGOWlusfg=="},
   476  			"X-Amz-Meta-Test-1":                            []string{"Test-1"},
   477  		},
   478  	},
   479  	{ // https://github.com/google/security-research/security/advisories/GHSA-76wf-9vgp-pj7w
   480  		Header: http.Header{
   481  			"X-Amz-Meta-X-Amz-Unencrypted-Content-Md5":    []string{"value"},
   482  			"X-Amz-Meta-X-Amz-Unencrypted-Content-Length": []string{"value"},
   483  			"X-Amz-Meta-Test-1":                           []string{"Test-1"},
   484  		},
   485  		ExpectedHeader: http.Header{
   486  			"X-Amz-Meta-Test-1": []string{"Test-1"},
   487  		},
   488  	},
   489  }
   490  
   491  func TestRemoveSensitiveHeaders(t *testing.T) {
   492  	isEqual := func(x, y http.Header) bool {
   493  		if len(x) != len(y) {
   494  			return false
   495  		}
   496  		for k, v := range x {
   497  			u, ok := y[k]
   498  			if !ok || len(v) != len(u) {
   499  				return false
   500  			}
   501  			sort.Strings(v)
   502  			sort.Strings(u)
   503  			for j := range v {
   504  				if v[j] != u[j] {
   505  					return false
   506  				}
   507  			}
   508  		}
   509  		return true
   510  	}
   511  	areKeysEqual := func(h http.Header, metadata map[string]string) bool {
   512  		if len(h) != len(metadata) {
   513  			return false
   514  		}
   515  		for k := range h {
   516  			if _, ok := metadata[k]; !ok {
   517  				return false
   518  			}
   519  		}
   520  		return true
   521  	}
   522  
   523  	for i, test := range removeSensitiveHeadersTests {
   524  		metadata := make(map[string]string, len(test.Header))
   525  		for k := range test.Header {
   526  			metadata[k] = "" // set metadata key - we don't care about the value
   527  		}
   528  
   529  		RemoveSensitiveHeaders(test.Header)
   530  		if !isEqual(test.ExpectedHeader, test.Header) {
   531  			t.Errorf("Test %d: filtered headers do not match expected headers - got: %v , want: %v", i, test.Header, test.ExpectedHeader)
   532  		}
   533  		RemoveSensitiveEntries(metadata)
   534  		if !areKeysEqual(test.ExpectedHeader, metadata) {
   535  			t.Errorf("Test %d: filtered headers do not match expected headers - got: %v , want: %v", i, test.Header, test.ExpectedHeader)
   536  		}
   537  	}
   538  }