From a5921b5743a506f26a01bfe2a890f0576c12d4dd Mon Sep 17 00:00:00 2001 From: Krishnan Parthasarathi Date: Wed, 12 Oct 2016 12:16:51 +0530 Subject: [PATCH] Use same timestamp for all chunks in chunked signature (#2908) --- cmd/object-handlers_test.go | 34 +++++++++++++++++++--- cmd/test-utils_test.go | 56 ++++++++++++++++++++++++++----------- 2 files changed, 70 insertions(+), 20 deletions(-) diff --git a/cmd/object-handlers_test.go b/cmd/object-handlers_test.go index c6a3d232e..f3a3b4137 100644 --- a/cmd/object-handlers_test.go +++ b/cmd/object-handlers_test.go @@ -228,6 +228,7 @@ func testAPIPutObjectStreamSigV4Handler(obj ObjectLayer, instanceType, bucketNam malformedEncoding unexpectedEOF signatureMismatch + chunkDateMismatch ) // byte data for PutObject. @@ -364,16 +365,41 @@ func testAPIPutObjectStreamSigV4Handler(obj ObjectLayer, instanceType, bucketNam shouldPass: false, fault: signatureMismatch, }, + // Test case - 9 + // Different date (timestamps) used in seed signature calculation + // and chunks signature calculation. + { + bucketName: bucketName, + objectName: objectName, + data: oneKData, + dataLen: 1024, + chunkSize: 1024, + expectedContent: []byte{}, + expectedRespStatus: http.StatusForbidden, + accessKey: credentials.AccessKeyID, + secretKey: credentials.SecretAccessKey, + shouldPass: false, + fault: chunkDateMismatch, + }, } // Iterating over the cases, fetching the object validating the response. for i, testCase := range testCases { // initialize HTTP NewRecorder, this records any mutations to response writer inside the handler. rec := httptest.NewRecorder() // construct HTTP request for Put Object end point. - req, err := newTestStreamingSignedRequest("PUT", - getPutObjectURL("", testCase.bucketName, testCase.objectName), - int64(testCase.dataLen), testCase.chunkSize, bytes.NewReader(testCase.data), - testCase.accessKey, testCase.secretKey) + var req *http.Request + if testCase.fault == chunkDateMismatch { + req, err = newTestStreamingSignedBadChunkDateRequest("PUT", + getPutObjectURL("", testCase.bucketName, testCase.objectName), + int64(testCase.dataLen), testCase.chunkSize, bytes.NewReader(testCase.data), + testCase.accessKey, testCase.secretKey) + + } else { + req, err = newTestStreamingSignedRequest("PUT", + getPutObjectURL("", testCase.bucketName, testCase.objectName), + int64(testCase.dataLen), testCase.chunkSize, bytes.NewReader(testCase.data), + testCase.accessKey, testCase.secretKey) + } if err != nil { t.Fatalf("Test %d: Failed to create HTTP request for Put Object: %v", i+1, err) } diff --git a/cmd/test-utils_test.go b/cmd/test-utils_test.go index 5732cef5a..f659e254a 100644 --- a/cmd/test-utils_test.go +++ b/cmd/test-utils_test.go @@ -399,14 +399,13 @@ func malformChunkSizeSigV4(req *http.Request, badSize int64) (*http.Request, err } // Sign given request using Signature V4. -func signStreamingRequest(req *http.Request, accessKey, secretKey string) (string, error) { +func signStreamingRequest(req *http.Request, accessKey, secretKey string, currTime time.Time) (string, error) { // Get hashed payload. hashedPayload := req.Header.Get("x-amz-content-sha256") if hashedPayload == "" { return "", fmt.Errorf("Invalid hashed payload.") } - currTime := time.Now().UTC() // Set x-amz-date. req.Header.Set("x-amz-date", currTime.Format(iso8601Format)) @@ -540,20 +539,10 @@ func newTestStreamingRequest(method, urlStr string, dataLength, chunkSize int64, return req, nil } -// Returns new HTTP request object signed with streaming signature v4. -func newTestStreamingSignedRequest(method, urlStr string, contentLength, chunkSize int64, body io.ReadSeeker, accessKey, secretKey string) (*http.Request, error) { - req, err := newTestStreamingRequest(method, urlStr, contentLength, chunkSize, body) - if err != nil { - return nil, err - } - - signature, err := signStreamingRequest(req, accessKey, secretKey) - if err != nil { - return nil, err - } +func assembleStreamingChunks(req *http.Request, body io.ReadSeeker, chunkSize int64, + secretKey, signature string, currTime time.Time) (*http.Request, error) { regionStr := serverConfig.GetRegion() - var stream []byte var buffer []byte body.Seek(0, 0) @@ -564,7 +553,6 @@ func newTestStreamingSignedRequest(method, urlStr string, contentLength, chunkSi return nil, err } - currTime := time.Now().UTC() // Get scope. scope := strings.Join([]string{ currTime.Format(yyyymmdd), @@ -596,11 +584,47 @@ func newTestStreamingSignedRequest(method, urlStr string, contentLength, chunkSi } } - req.Body = ioutil.NopCloser(bytes.NewReader(stream)) return req, nil } +func newTestStreamingSignedBadChunkDateRequest(method, urlStr string, contentLength, chunkSize int64, body io.ReadSeeker, accessKey, secretKey string) (*http.Request, error) { + req, err := newTestStreamingRequest(method, urlStr, contentLength, chunkSize, body) + if err != nil { + return nil, err + } + + currTime := time.Now().UTC() + fmt.Println("now: ", currTime) + signature, err := signStreamingRequest(req, accessKey, secretKey, currTime) + if err != nil { + return nil, err + } + + // skew the time between the chunk signature calculation and seed signature. + currTime = currTime.Add(1 * time.Second) + fmt.Println("later: ", currTime) + req, err = assembleStreamingChunks(req, body, chunkSize, secretKey, signature, currTime) + return req, nil +} + +// Returns new HTTP request object signed with streaming signature v4. +func newTestStreamingSignedRequest(method, urlStr string, contentLength, chunkSize int64, body io.ReadSeeker, accessKey, secretKey string) (*http.Request, error) { + req, err := newTestStreamingRequest(method, urlStr, contentLength, chunkSize, body) + if err != nil { + return nil, err + } + + currTime := time.Now().UTC() + signature, err := signStreamingRequest(req, accessKey, secretKey, currTime) + if err != nil { + return nil, err + } + + req, err = assembleStreamingChunks(req, body, chunkSize, secretKey, signature, currTime) + return req, nil +} + // Replaces any occurring '/' in string, into its encoded representation. func percentEncodeSlash(s string) string { return strings.Replace(s, "/", "%2F", -1)