diff --git a/api/handler/put_test.go b/api/handler/put_test.go index f64123f..c004eb4 100644 --- a/api/handler/put_test.go +++ b/api/handler/put_test.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "crypto/md5" + "crypto/tls" "encoding/base64" "encoding/hex" "encoding/json" @@ -634,6 +635,186 @@ func TestPutObjectWithContentLanguage(t *testing.T) { require.Equal(t, expectedContentLanguage, w.Header().Get(api.ContentLanguage)) } +func TestFormEncryptionParamsBase(t *testing.T) { + hc := prepareHandlerContext(t) + + userSecret := "test1customer2secret3with32char4" + expectedEncKey := []byte(userSecret) + emptyEncKey := []byte(nil) + + validAlgo := "AES256" + validKey := "dGVzdDFjdXN0b21lcjJzZWNyZXQzd2l0aDMyY2hhcjQ=" + validMD5 := "zcQmPqFhtJaxkOIg5tXm9g==" + + invalidAlgo := "TTT111" + invalidKeyBase64 := "dGVzdDFjdXN0b21lcjJzZWNyZXQzd2l0aDMyY2hhcjQ" + invalidKeySize := "dGVzdDFjdXN0b21lcjJzZWNyZXQzd2l0aA==" + invalidMD5Base64 := "zcQmPqFhtJaxkOIg5tXm9g" + invalidMD5 := "zcQmPqPhtJaxkOIg5tXm9g==" + + for _, tc := range []struct { + name string + algo string + key string + md5 string + tlsTermination string + reqWithoutTLS bool + reqWithoutSSE bool + isCopySource bool + err error + }{ + { + name: "valid requst copy source", + algo: validAlgo, + key: validKey, + md5: validMD5, + isCopySource: true, + }, + { + name: "valid request with TLS", + algo: validAlgo, + key: validKey, + md5: validMD5, + }, + { + name: "valid request without TLS and valid termination header", + algo: validAlgo, + key: validKey, + md5: validMD5, + tlsTermination: "true", + reqWithoutTLS: true, + }, + { + name: "request without tls and termination header", + algo: validAlgo, + key: validKey, + md5: validMD5, + reqWithoutTLS: true, + err: apierr.GetAPIError(apierr.ErrInsecureSSECustomerRequest), + }, + { + name: "request without tls and invalid header", + algo: validAlgo, + key: validKey, + md5: validMD5, + tlsTermination: "invalid", + reqWithoutTLS: true, + err: apierr.GetAPIError(apierr.ErrInsecureSSECustomerRequest), + }, + { + name: "missing SSE customer algorithm", + key: validKey, + md5: validMD5, + err: apierr.GetAPIError(apierr.ErrMissingSSECustomerAlgorithm), + }, + { + name: "missing SSE customer key", + algo: validAlgo, + md5: validMD5, + err: apierr.GetAPIError(apierr.ErrMissingSSECustomerKey), + }, + { + name: "invalid encryption algorithm", + algo: invalidAlgo, + key: validKey, + md5: validMD5, + err: apierr.GetAPIError(apierr.ErrInvalidEncryptionAlgorithm), + }, + { + name: "invalid base64 SSE customer key", + algo: validAlgo, + key: invalidKeyBase64, + md5: validMD5, + err: apierr.GetAPIError(apierr.ErrInvalidSSECustomerKey), + }, + { + name: "invalid base64 SSE customer parameters", + algo: validAlgo, + key: invalidKeyBase64, + md5: validMD5, + isCopySource: true, + err: apierr.GetAPIError(apierr.ErrInvalidSSECustomerParameters), + }, + { + name: "invalid size of custom key", + algo: validAlgo, + key: invalidKeySize, + md5: validMD5, + err: apierr.GetAPIError(apierr.ErrInvalidSSECustomerKey), + }, + { + name: "invalid size of custom key - copy source", + algo: validAlgo, + key: invalidKeySize, + md5: validMD5, + isCopySource: true, + err: apierr.GetAPIError(apierr.ErrInvalidSSECustomerParameters), + }, + { + name: "invalid base64 key md5 of customer", + algo: validAlgo, + key: validKey, + md5: invalidMD5Base64, + err: apierr.GetAPIError(apierr.ErrSSECustomerKeyMD5Mismatch), + }, + { + name: "invalid md5 sum key of customer", + algo: validAlgo, + key: validKey, + md5: invalidMD5, + err: apierr.GetAPIError(apierr.ErrSSECustomerKeyMD5Mismatch), + }, + { + name: "request without sse", + reqWithoutSSE: true, + }, + } { + t.Run(tc.name, func(t *testing.T) { + r := prepareRequestForEnctyption(tc.algo, tc.key, tc.md5, tc.tlsTermination, tc.reqWithoutTLS, tc.reqWithoutSSE, tc.isCopySource) + + enc, err := hc.h.formEncryptionParamsBase(r, tc.isCopySource) + if tc.err != nil { + require.ErrorIs(t, tc.err, err) + return + } + + require.NoError(t, err) + + if tc.reqWithoutSSE { + require.Equal(t, emptyEncKey, enc.Key()) + } else { + require.Equal(t, expectedEncKey, enc.Key()) + } + }) + } +} + +func prepareRequestForEnctyption(algo, key, md5, tlsTermination string, reqWithoutTLS, reqWithoutSSE, isCopySource bool) *http.Request { + r := httptest.NewRequest(http.MethodPost, "/", nil) + + if !reqWithoutTLS { + r.TLS = &tls.ConnectionState{} + } + + if !reqWithoutSSE { + if isCopySource { + r.Header.Set(api.AmzCopySourceServerSideEncryptionCustomerAlgorithm, algo) + r.Header.Set(api.AmzCopySourceServerSideEncryptionCustomerKey, key) + r.Header.Set(api.AmzCopySourceServerSideEncryptionCustomerKeyMD5, md5) + } else { + r.Header.Set(api.AmzServerSideEncryptionCustomerAlgorithm, algo) + r.Header.Set(api.AmzServerSideEncryptionCustomerKey, key) + r.Header.Set(api.AmzServerSideEncryptionCustomerKeyMD5, md5) + } + } + + if tlsTermination != "" { + r.Header.Set("X-Frostfs-TLS-Termination", tlsTermination) + } + + return r +} + func postObjectBase(hc *handlerContext, ns, bktName, key, filename, content string) *httptest.ResponseRecorder { policy := "eyJleHBpcmF0aW9uIjogIjIwMjUtMTItMDFUMTI6MDA6MDAuMDAwWiIsImNvbmRpdGlvbnMiOiBbCiBbInN0YXJ0cy13aXRoIiwgIiR4LWFtei1jcmVkZW50aWFsIiwgIiJdLAogWyJzdGFydHMtd2l0aCIsICIkeC1hbXotZGF0ZSIsICIiXSwKIFsic3RhcnRzLXdpdGgiLCAiJGtleSIsICIiXQpdfQ=="