diff --git a/api-presigned.go b/api-presigned.go index 200f33e9b..471d5fcce 100644 --- a/api-presigned.go +++ b/api-presigned.go @@ -20,6 +20,8 @@ import ( "errors" "net/url" "time" + + "github.com/minio/minio-go/pkg/s3signer" ) // supportedGetReqParams - supported request parameters for GET presigned request. @@ -133,7 +135,7 @@ func (c Client) PresignedPostPolicy(p *PostPolicy) (u *url.URL, formData map[str p.formData["AWSAccessKeyId"] = c.accessKeyID } // Sign the policy. - p.formData["signature"] = postPresignSignatureV2(policyBase64, c.secretAccessKey) + p.formData["signature"] = s3signer.PostPresignSignatureV2(policyBase64, c.secretAccessKey) return u, p.formData, nil } @@ -156,7 +158,7 @@ func (c Client) PresignedPostPolicy(p *PostPolicy) (u *url.URL, formData map[str } // Add a credential policy. - credential := getCredential(c.accessKeyID, location, t) + credential := s3signer.GetCredential(c.accessKeyID, location, t) if err = p.addNewPolicy(policyCondition{ matchType: "eq", condition: "$x-amz-credential", @@ -172,6 +174,6 @@ func (c Client) PresignedPostPolicy(p *PostPolicy) (u *url.URL, formData map[str p.formData["x-amz-algorithm"] = signV4Algorithm p.formData["x-amz-credential"] = credential p.formData["x-amz-date"] = t.Format(iso8601DateFormat) - p.formData["x-amz-signature"] = postPresignSignatureV4(policyBase64, t, c.secretAccessKey, location) + p.formData["x-amz-signature"] = s3signer.PostPresignSignatureV4(policyBase64, t, c.secretAccessKey, location) return u, p.formData, nil } diff --git a/api-put-bucket.go b/api-put-bucket.go index 3c9f438ef..870f8a8e6 100644 --- a/api-put-bucket.go +++ b/api-put-bucket.go @@ -28,6 +28,7 @@ import ( "net/url" "github.com/minio/minio-go/pkg/policy" + "github.com/minio/minio-go/pkg/s3signer" ) /// Bucket operations @@ -133,9 +134,9 @@ func (c Client) makeBucketRequest(bucketName string, location string) (*http.Req if c.signature.isV4() { // Signature calculated for MakeBucket request should be for 'us-east-1', // regardless of the bucket's location constraint. - req = signV4(*req, c.accessKeyID, c.secretAccessKey, "us-east-1") + req = s3signer.SignV4(*req, c.accessKeyID, c.secretAccessKey, "us-east-1") } else if c.signature.isV2() { - req = signV2(*req, c.accessKeyID, c.secretAccessKey) + req = s3signer.SignV2(*req, c.accessKeyID, c.secretAccessKey) } // Return signed request. diff --git a/api-put-bucket_test.go b/api-put-bucket_test.go index a1899fbe2..e6d4164a7 100644 --- a/api-put-bucket_test.go +++ b/api-put-bucket_test.go @@ -26,6 +26,8 @@ import ( "net/http" "net/url" "testing" + + "github.com/minio/minio-go/pkg/s3signer" ) // Tests validate http request formulated for creation of bucket. @@ -78,9 +80,9 @@ func TestMakeBucketRequest(t *testing.T) { if c.signature.isV4() { // Signature calculated for MakeBucket request should be for 'us-east-1', // regardless of the bucket's location constraint. - req = signV4(*req, c.accessKeyID, c.secretAccessKey, "us-east-1") + req = s3signer.SignV4(*req, c.accessKeyID, c.secretAccessKey, "us-east-1") } else if c.signature.isV2() { - req = signV2(*req, c.accessKeyID, c.secretAccessKey) + req = s3signer.SignV2(*req, c.accessKeyID, c.secretAccessKey) } // Return signed request. diff --git a/api.go b/api.go index 564ff15c0..5d2d0d82c 100644 --- a/api.go +++ b/api.go @@ -33,6 +33,8 @@ import ( "strings" "sync" "time" + + "github.com/minio/minio-go/pkg/s3signer" ) // Client implements Amazon S3 compatible methods. @@ -580,10 +582,10 @@ func (c Client) newRequest(method string, metadata requestMetadata) (req *http.R } if c.signature.isV2() { // Presign URL with signature v2. - req = preSignV2(*req, c.accessKeyID, c.secretAccessKey, metadata.expires) + req = s3signer.PreSignV2(*req, c.accessKeyID, c.secretAccessKey, metadata.expires) } else { // Presign URL with signature v4. - req = preSignV4(*req, c.accessKeyID, c.secretAccessKey, location, metadata.expires) + req = s3signer.PreSignV4(*req, c.accessKeyID, c.secretAccessKey, location, metadata.expires) } return req, nil } @@ -640,10 +642,10 @@ func (c Client) newRequest(method string, metadata requestMetadata) (req *http.R if !c.anonymous { if c.signature.isV2() { // Add signature version '2' authorization header. - req = signV2(*req, c.accessKeyID, c.secretAccessKey) + req = s3signer.SignV2(*req, c.accessKeyID, c.secretAccessKey) } else if c.signature.isV4() { // Add signature version '4' authorization header. - req = signV4(*req, c.accessKeyID, c.secretAccessKey, location) + req = s3signer.SignV4(*req, c.accessKeyID, c.secretAccessKey, location) } } diff --git a/api_unit_test.go b/api_unit_test.go index 817a8c2c7..c1db0df5d 100644 --- a/api_unit_test.go +++ b/api_unit_test.go @@ -18,11 +18,9 @@ package minio import ( "bytes" - "fmt" "io" "io/ioutil" "net/http" - "net/url" "os" "strings" "testing" @@ -202,49 +200,6 @@ func TestTempFile(t *testing.T) { } } -// Tests url encoding. -func TestEncodeURL2Path(t *testing.T) { - type urlStrings struct { - objName string - encodedObjName string - } - - bucketName := "bucketName" - want := []urlStrings{ - { - objName: "本語", - encodedObjName: "%E6%9C%AC%E8%AA%9E", - }, - { - objName: "本語.1", - encodedObjName: "%E6%9C%AC%E8%AA%9E.1", - }, - { - objName: ">123>3123123", - encodedObjName: "%3E123%3E3123123", - }, - { - objName: "test 1 2.txt", - encodedObjName: "test%201%202.txt", - }, - { - objName: "test++ 1.txt", - encodedObjName: "test%2B%2B%201.txt", - }, - } - - for _, o := range want { - u, err := url.Parse(fmt.Sprintf("https://%s.s3.amazonaws.com/%s", bucketName, o.objName)) - if err != nil { - t.Fatal("Error:", err) - } - urlPath := "/" + bucketName + "/" + o.encodedObjName - if urlPath != encodeURL2Path(u) { - t.Fatal("Error") - } - } -} - // Tests error response structure. func TestErrorResponse(t *testing.T) { var err error @@ -270,53 +225,6 @@ func TestErrorResponse(t *testing.T) { } } -// Tests signature calculation. -func TestSignatureCalculation(t *testing.T) { - req, err := http.NewRequest("GET", "https://s3.amazonaws.com", nil) - if err != nil { - t.Fatal("Error:", err) - } - req = signV4(*req, "", "", "us-east-1") - if req.Header.Get("Authorization") != "" { - t.Fatal("Error: anonymous credentials should not have Authorization header.") - } - - req = preSignV4(*req, "", "", "us-east-1", 0) - if strings.Contains(req.URL.RawQuery, "X-Amz-Signature") { - t.Fatal("Error: anonymous credentials should not have Signature query resource.") - } - - req = signV2(*req, "", "") - if req.Header.Get("Authorization") != "" { - t.Fatal("Error: anonymous credentials should not have Authorization header.") - } - - req = preSignV2(*req, "", "", 0) - if strings.Contains(req.URL.RawQuery, "Signature") { - t.Fatal("Error: anonymous credentials should not have Signature query resource.") - } - - req = signV4(*req, "ACCESS-KEY", "SECRET-KEY", "us-east-1") - if req.Header.Get("Authorization") == "" { - t.Fatal("Error: normal credentials should have Authorization header.") - } - - req = preSignV4(*req, "ACCESS-KEY", "SECRET-KEY", "us-east-1", 0) - if !strings.Contains(req.URL.RawQuery, "X-Amz-Signature") { - t.Fatal("Error: normal credentials should have Signature query resource.") - } - - req = signV2(*req, "ACCESS-KEY", "SECRET-KEY") - if req.Header.Get("Authorization") == "" { - t.Fatal("Error: normal credentials should have Authorization header.") - } - - req = preSignV2(*req, "ACCESS-KEY", "SECRET-KEY", 0) - if !strings.Contains(req.URL.RawQuery, "Signature") { - t.Fatal("Error: normal credentials should not have Signature query resource.") - } -} - // Tests signature type. func TestSignatureType(t *testing.T) { clnt := Client{} diff --git a/bucket-cache.go b/bucket-cache.go index 4ad106959..d56c4c65e 100644 --- a/bucket-cache.go +++ b/bucket-cache.go @@ -23,6 +23,8 @@ import ( "path" "strings" "sync" + + "github.com/minio/minio-go/pkg/s3signer" ) // bucketLocationCache - Provides simple mechanism to hold bucket @@ -189,9 +191,9 @@ func (c Client) getBucketLocationRequest(bucketName string) (*http.Request, erro // Sign the request. if c.signature.isV4() { - req = signV4(*req, c.accessKeyID, c.secretAccessKey, "us-east-1") + req = s3signer.SignV4(*req, c.accessKeyID, c.secretAccessKey, "us-east-1") } else if c.signature.isV2() { - req = signV2(*req, c.accessKeyID, c.secretAccessKey) + req = s3signer.SignV2(*req, c.accessKeyID, c.secretAccessKey) } return req, nil } diff --git a/bucket-cache_test.go b/bucket-cache_test.go index 81cfbc097..59ead29be 100644 --- a/bucket-cache_test.go +++ b/bucket-cache_test.go @@ -26,6 +26,8 @@ import ( "path" "reflect" "testing" + + "github.com/minio/minio-go/pkg/s3signer" ) // Test validates `newBucketLocationCache`. @@ -93,9 +95,9 @@ func TestGetBucketLocationRequest(t *testing.T) { // Sign the request. if c.signature.isV4() { - req = signV4(*req, c.accessKeyID, c.secretAccessKey, "us-east-1") + req = s3signer.SignV4(*req, c.accessKeyID, c.secretAccessKey, "us-east-1") } else if c.signature.isV2() { - req = signV2(*req, c.accessKeyID, c.secretAccessKey) + req = s3signer.SignV2(*req, c.accessKeyID, c.secretAccessKey) } return req, nil diff --git a/constants.go b/constants.go index 779ed8c7a..057c3eef4 100644 --- a/constants.go +++ b/constants.go @@ -44,3 +44,9 @@ const optimalReadBufferSize = 1024 * 1024 * 5 // unsignedPayload - value to be set to X-Amz-Content-Sha256 header when // we don't want to sign the request payload const unsignedPayload = "UNSIGNED-PAYLOAD" + +// Signature related constants. +const ( + signV4Algorithm = "AWS4-HMAC-SHA256" + iso8601DateFormat = "20060102T150405Z" +) diff --git a/request-signature-v2.go b/pkg/s3signer/request-signature-v2.go similarity index 95% rename from request-signature-v2.go rename to pkg/s3signer/request-signature-v2.go index b9f248253..90aca9558 100644 --- a/request-signature-v2.go +++ b/pkg/s3signer/request-signature-v2.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package minio +package s3signer import ( "bytes" @@ -58,9 +58,9 @@ func encodeURL2Path(u *url.URL) (path string) { return } -// preSignV2 - presign the request in following style. +// PreSignV2 - presign the request in following style. // https://${S3_BUCKET}.s3.amazonaws.com/${S3_OBJECT}?AWSAccessKeyId=${S3_ACCESS_KEY}&Expires=${TIMESTAMP}&Signature=${SIGNATURE}. -func preSignV2(req http.Request, accessKeyID, secretAccessKey string, expires int64) *http.Request { +func PreSignV2(req http.Request, accessKeyID, secretAccessKey string, expires int64) *http.Request { // Presign is not needed for anonymous credentials. if accessKeyID == "" || secretAccessKey == "" { return &req @@ -104,9 +104,9 @@ func preSignV2(req http.Request, accessKeyID, secretAccessKey string, expires in return &req } -// postPresignSignatureV2 - presigned signature for PostPolicy +// PostPresignSignatureV2 - presigned signature for PostPolicy // request. -func postPresignSignatureV2(policyBase64, secretAccessKey string) string { +func PostPresignSignatureV2(policyBase64, secretAccessKey string) string { hm := hmac.New(sha1.New, []byte(secretAccessKey)) hm.Write([]byte(policyBase64)) signature := base64.StdEncoding.EncodeToString(hm.Sum(nil)) @@ -129,8 +129,8 @@ func postPresignSignatureV2(policyBase64, secretAccessKey string) string { // // CanonicalizedProtocolHeaders = -// signV2 sign the request before Do() (AWS Signature Version 2). -func signV2(req http.Request, accessKeyID, secretAccessKey string) *http.Request { +// SignV2 sign the request before Do() (AWS Signature Version 2). +func SignV2(req http.Request, accessKeyID, secretAccessKey string) *http.Request { // Signature calculation is not needed for anonymous credentials. if accessKeyID == "" || secretAccessKey == "" { return &req diff --git a/request-signature-v2_test.go b/pkg/s3signer/request-signature-v2_test.go similarity index 98% rename from request-signature-v2_test.go rename to pkg/s3signer/request-signature-v2_test.go index 6d861fb81..3c0e0ecea 100644 --- a/request-signature-v2_test.go +++ b/pkg/s3signer/request-signature-v2_test.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package minio +package s3signer import ( "sort" diff --git a/request-signature-v4.go b/pkg/s3signer/request-signature-v4.go similarity index 93% rename from request-signature-v4.go rename to pkg/s3signer/request-signature-v4.go index 2be3808d6..b18f5a755 100644 --- a/request-signature-v4.go +++ b/pkg/s3signer/request-signature-v4.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package minio +package s3signer import ( "bytes" @@ -101,8 +101,8 @@ func getScope(location string, t time.Time) string { return scope } -// getCredential generate a credential string. -func getCredential(accessKeyID, location string, t time.Time) string { +// GetCredential generate a credential string. +func GetCredential(accessKeyID, location string, t time.Time) string { scope := getScope(location, t) return accessKeyID + "/" + scope } @@ -202,9 +202,9 @@ func getStringToSignV4(t time.Time, location, canonicalRequest string) string { return stringToSign } -// preSignV4 presign the request, in accordance with +// PreSignV4 presign the request, in accordance with // http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html. -func preSignV4(req http.Request, accessKeyID, secretAccessKey, location string, expires int64) *http.Request { +func PreSignV4(req http.Request, accessKeyID, secretAccessKey, location string, expires int64) *http.Request { // Presign is not needed for anonymous credentials. if accessKeyID == "" || secretAccessKey == "" { return &req @@ -214,7 +214,7 @@ func preSignV4(req http.Request, accessKeyID, secretAccessKey, location string, t := time.Now().UTC() // Get credential string. - credential := getCredential(accessKeyID, location, t) + credential := GetCredential(accessKeyID, location, t) // Get all signed headers. signedHeaders := getSignedHeaders(req) @@ -246,9 +246,9 @@ func preSignV4(req http.Request, accessKeyID, secretAccessKey, location string, return &req } -// postPresignSignatureV4 - presigned signature for PostPolicy +// PostPresignSignatureV4 - presigned signature for PostPolicy // requests. -func postPresignSignatureV4(policyBase64 string, t time.Time, secretAccessKey, location string) string { +func PostPresignSignatureV4(policyBase64 string, t time.Time, secretAccessKey, location string) string { // Get signining key. signingkey := getSigningKey(secretAccessKey, location, t) // Calculate signature. @@ -256,9 +256,9 @@ func postPresignSignatureV4(policyBase64 string, t time.Time, secretAccessKey, l return signature } -// signV4 sign the request before Do(), in accordance with +// SignV4 sign the request before Do(), in accordance with // http://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html. -func signV4(req http.Request, accessKeyID, secretAccessKey, location string) *http.Request { +func SignV4(req http.Request, accessKeyID, secretAccessKey, location string) *http.Request { // Signature calculation is not needed for anonymous credentials. if accessKeyID == "" || secretAccessKey == "" { return &req @@ -280,7 +280,7 @@ func signV4(req http.Request, accessKeyID, secretAccessKey, location string) *ht signingKey := getSigningKey(secretAccessKey, location, t) // Get credential string. - credential := getCredential(accessKeyID, location, t) + credential := GetCredential(accessKeyID, location, t) // Get all signed headers. signedHeaders := getSignedHeaders(req) diff --git a/pkg/s3signer/request-signature_test.go b/pkg/s3signer/request-signature_test.go new file mode 100644 index 000000000..6f5ba1895 --- /dev/null +++ b/pkg/s3signer/request-signature_test.go @@ -0,0 +1,70 @@ +/* + * Minio Go Library for Amazon S3 Compatible Cloud Storage (C) 2015, 2016 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package s3signer + +import ( + "net/http" + "strings" + "testing" +) + +// Tests signature calculation. +func TestSignatureCalculation(t *testing.T) { + req, err := http.NewRequest("GET", "https://s3.amazonaws.com", nil) + if err != nil { + t.Fatal("Error:", err) + } + req = SignV4(*req, "", "", "us-east-1") + if req.Header.Get("Authorization") != "" { + t.Fatal("Error: anonymous credentials should not have Authorization header.") + } + + req = PreSignV4(*req, "", "", "us-east-1", 0) + if strings.Contains(req.URL.RawQuery, "X-Amz-Signature") { + t.Fatal("Error: anonymous credentials should not have Signature query resource.") + } + + req = SignV2(*req, "", "") + if req.Header.Get("Authorization") != "" { + t.Fatal("Error: anonymous credentials should not have Authorization header.") + } + + req = PreSignV2(*req, "", "", 0) + if strings.Contains(req.URL.RawQuery, "Signature") { + t.Fatal("Error: anonymous credentials should not have Signature query resource.") + } + + req = SignV4(*req, "ACCESS-KEY", "SECRET-KEY", "us-east-1") + if req.Header.Get("Authorization") == "" { + t.Fatal("Error: normal credentials should have Authorization header.") + } + + req = PreSignV4(*req, "ACCESS-KEY", "SECRET-KEY", "us-east-1", 0) + if !strings.Contains(req.URL.RawQuery, "X-Amz-Signature") { + t.Fatal("Error: normal credentials should have Signature query resource.") + } + + req = SignV2(*req, "ACCESS-KEY", "SECRET-KEY") + if req.Header.Get("Authorization") == "" { + t.Fatal("Error: normal credentials should have Authorization header.") + } + + req = PreSignV2(*req, "ACCESS-KEY", "SECRET-KEY", 0) + if !strings.Contains(req.URL.RawQuery, "Signature") { + t.Fatal("Error: normal credentials should not have Signature query resource.") + } +} diff --git a/pkg/s3signer/utils.go b/pkg/s3signer/utils.go new file mode 100644 index 000000000..ac58a0063 --- /dev/null +++ b/pkg/s3signer/utils.go @@ -0,0 +1,118 @@ +/* + * Minio Go Library for Amazon S3 Compatible Cloud Storage (C) 2015 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package s3signer + +import ( + "bytes" + "crypto/hmac" + "crypto/sha256" + "encoding/hex" + "net/url" + "regexp" + "sort" + "strings" + "unicode/utf8" +) + +// unsignedPayload - value to be set to X-Amz-Content-Sha256 header when +const unsignedPayload = "UNSIGNED-PAYLOAD" + +// sum256 calculate sha256 sum for an input byte array. +func sum256(data []byte) []byte { + hash := sha256.New() + hash.Write(data) + return hash.Sum(nil) +} + +// sumHMAC calculate hmac between two input byte array. +func sumHMAC(key []byte, data []byte) []byte { + hash := hmac.New(sha256.New, key) + hash.Write(data) + return hash.Sum(nil) +} + +//expects ascii encoded strings - from output of urlEncodePath +func percentEncodeSlash(s string) string { + return strings.Replace(s, "/", "%2F", -1) +} + +// queryEncode - encodes query values in their URL encoded form. In +// addition to the percent encoding performed by urlEncodePath() used +// here, it also percent encodes '/' (forward slash) +func queryEncode(v url.Values) string { + if v == nil { + return "" + } + var buf bytes.Buffer + keys := make([]string, 0, len(v)) + for k := range v { + keys = append(keys, k) + } + sort.Strings(keys) + for _, k := range keys { + vs := v[k] + prefix := percentEncodeSlash(urlEncodePath(k)) + "=" + for _, v := range vs { + if buf.Len() > 0 { + buf.WriteByte('&') + } + buf.WriteString(prefix) + buf.WriteString(percentEncodeSlash(urlEncodePath(v))) + } + } + return buf.String() +} + +// urlEncodePath encode the strings from UTF-8 byte representations to HTML hex escape sequences +// +// This is necessary since regular url.Parse() and url.Encode() functions do not support UTF-8 +// non english characters cannot be parsed due to the nature in which url.Encode() is written +// +// This function on the other hand is a direct replacement for url.Encode() technique to support +// pretty much every UTF-8 character. +func urlEncodePath(pathName string) string { + // if object matches reserved string, no need to encode them + reservedNames := regexp.MustCompile("^[a-zA-Z0-9-_.~/]+$") + if reservedNames.MatchString(pathName) { + return pathName + } + var encodedPathname string + for _, s := range pathName { + if 'A' <= s && s <= 'Z' || 'a' <= s && s <= 'z' || '0' <= s && s <= '9' { // §2.3 Unreserved characters (mark) + encodedPathname = encodedPathname + string(s) + continue + } + switch s { + case '-', '_', '.', '~', '/': // §2.3 Unreserved characters (mark) + encodedPathname = encodedPathname + string(s) + continue + default: + len := utf8.RuneLen(s) + if len < 0 { + // if utf8 cannot convert return the same string as is + return pathName + } + u := make([]byte, len) + utf8.EncodeRune(u, s) + for _, r := range u { + hex := hex.EncodeToString([]byte{r}) + encodedPathname = encodedPathname + "%" + strings.ToUpper(hex) + } + } + } + return encodedPathname +} diff --git a/pkg/s3signer/utils_test.go b/pkg/s3signer/utils_test.go new file mode 100644 index 000000000..b266e42a1 --- /dev/null +++ b/pkg/s3signer/utils_test.go @@ -0,0 +1,66 @@ +/* + * Minio Go Library for Amazon S3 Compatible Cloud Storage (C) 2015 Minio, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package s3signer + +import ( + "fmt" + "net/url" + "testing" +) + +// Tests url encoding. +func TestEncodeURL2Path(t *testing.T) { + type urlStrings struct { + objName string + encodedObjName string + } + + bucketName := "bucketName" + want := []urlStrings{ + { + objName: "本語", + encodedObjName: "%E6%9C%AC%E8%AA%9E", + }, + { + objName: "本語.1", + encodedObjName: "%E6%9C%AC%E8%AA%9E.1", + }, + { + objName: ">123>3123123", + encodedObjName: "%3E123%3E3123123", + }, + { + objName: "test 1 2.txt", + encodedObjName: "test%201%202.txt", + }, + { + objName: "test++ 1.txt", + encodedObjName: "test%2B%2B%201.txt", + }, + } + + for _, o := range want { + u, err := url.Parse(fmt.Sprintf("https://%s.s3.amazonaws.com/%s", bucketName, o.objName)) + if err != nil { + t.Fatal("Error:", err) + } + urlPath := "/" + bucketName + "/" + o.encodedObjName + if urlPath != encodeURL2Path(u) { + t.Fatal("Error") + } + } +} diff --git a/utils.go b/utils.go index 2208d3603..b0ef6fd41 100644 --- a/utils.go +++ b/utils.go @@ -18,7 +18,6 @@ package minio import ( "bytes" - "crypto/hmac" "crypto/md5" "crypto/sha256" "encoding/hex" @@ -55,13 +54,6 @@ func sumMD5(data []byte) []byte { return hash.Sum(nil) } -// sumHMAC calculate hmac between two input byte array. -func sumHMAC(key []byte, data []byte) []byte { - hash := hmac.New(sha256.New, key) - hash.Write(data) - return hash.Sum(nil) -} - // getEndpointURL - construct a new endpoint. func getEndpointURL(endpoint string, secure bool) (*url.URL, error) { if strings.Contains(endpoint, ":") {