| 
									
										
										
										
											2016-02-22 09:57:05 +08:00
										 |  |  | /* | 
					
						
							|  |  |  |  * Minio 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. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-19 07:23:42 +08:00
										 |  |  | package cmd | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"net/url" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							|  |  |  | 	"time" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | // credentialHeader data type represents structured form of Credential
 | 
					
						
							| 
									
										
										
										
											2016-02-22 09:57:05 +08:00
										 |  |  | // string from authorization header.
 | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | type credentialHeader struct { | 
					
						
							|  |  |  | 	accessKey string | 
					
						
							|  |  |  | 	scope     struct { | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 		date    time.Time | 
					
						
							|  |  |  | 		region  string | 
					
						
							|  |  |  | 		service string | 
					
						
							|  |  |  | 		request string | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-07 05:09:09 +08:00
										 |  |  | // Return scope string.
 | 
					
						
							|  |  |  | func (c credentialHeader) getScope() string { | 
					
						
							|  |  |  | 	return strings.Join([]string{ | 
					
						
							|  |  |  | 		c.scope.date.Format(yyyymmdd), | 
					
						
							|  |  |  | 		c.scope.region, | 
					
						
							|  |  |  | 		c.scope.service, | 
					
						
							|  |  |  | 		c.scope.request, | 
					
						
							|  |  |  | 	}, "/") | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | // parse credentialHeader string into its structured form.
 | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | func parseCredentialHeader(credElement string) (credentialHeader, APIErrorCode) { | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	creds := strings.Split(strings.TrimSpace(credElement), "=") | 
					
						
							|  |  |  | 	if len(creds) != 2 { | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 		return credentialHeader{}, ErrMissingFields | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if creds[0] != "Credential" { | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 		return credentialHeader{}, ErrMissingCredTag | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	credElements := strings.Split(strings.TrimSpace(creds[1]), "/") | 
					
						
							|  |  |  | 	if len(credElements) != 5 { | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 		return credentialHeader{}, ErrCredMalformed | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-12-27 02:21:23 +08:00
										 |  |  | 	if !isAccessKeyValid(credElements[0]) { | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 		return credentialHeader{}, ErrInvalidAccessKeyID | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 	// Save access key id.
 | 
					
						
							|  |  |  | 	cred := credentialHeader{ | 
					
						
							|  |  |  | 		accessKey: credElements[0], | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	var e error | 
					
						
							|  |  |  | 	cred.scope.date, e = time.Parse(yyyymmdd, credElements[1]) | 
					
						
							|  |  |  | 	if e != nil { | 
					
						
							| 
									
										
										
										
											2016-08-10 00:13:15 +08:00
										 |  |  | 		return credentialHeader{}, ErrMalformedCredentialDate | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if credElements[2] == "" { | 
					
						
							| 
									
										
										
										
											2016-08-10 00:13:15 +08:00
										 |  |  | 		return credentialHeader{}, ErrMalformedCredentialRegion | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	cred.scope.region = credElements[2] | 
					
						
							|  |  |  | 	if credElements[3] != "s3" { | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 		return credentialHeader{}, ErrInvalidService | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	cred.scope.service = credElements[3] | 
					
						
							|  |  |  | 	if credElements[4] != "aws4_request" { | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 		return credentialHeader{}, ErrInvalidRequestVersion | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	cred.scope.request = credElements[4] | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 	return cred, ErrNone | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-07 03:47:16 +08:00
										 |  |  | // Parse signature from signature tag.
 | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | func parseSignature(signElement string) (string, APIErrorCode) { | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	signFields := strings.Split(strings.TrimSpace(signElement), "=") | 
					
						
							|  |  |  | 	if len(signFields) != 2 { | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 		return "", ErrMissingFields | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if signFields[0] != "Signature" { | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 		return "", ErrMissingSignTag | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-11-07 03:47:16 +08:00
										 |  |  | 	if signFields[1] == "" { | 
					
						
							|  |  |  | 		return "", ErrMissingFields | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	signature := signFields[1] | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 	return signature, ErrNone | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-07 03:47:16 +08:00
										 |  |  | // Parse slice of signed headers from signed headers tag.
 | 
					
						
							|  |  |  | func parseSignedHeader(signedHdrElement string) ([]string, APIErrorCode) { | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	signedHdrFields := strings.Split(strings.TrimSpace(signedHdrElement), "=") | 
					
						
							|  |  |  | 	if len(signedHdrFields) != 2 { | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 		return nil, ErrMissingFields | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if signedHdrFields[0] != "SignedHeaders" { | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 		return nil, ErrMissingSignHeadersTag | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-11-07 03:47:16 +08:00
										 |  |  | 	if signedHdrFields[1] == "" { | 
					
						
							|  |  |  | 		return nil, ErrMissingFields | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	signedHeaders := strings.Split(signedHdrFields[1], ";") | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 	return signedHeaders, ErrNone | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-22 09:57:05 +08:00
										 |  |  | // signValues data type represents structured form of AWS Signature V4 header.
 | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | type signValues struct { | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 	Credential    credentialHeader | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	SignedHeaders []string | 
					
						
							|  |  |  | 	Signature     string | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-22 09:57:05 +08:00
										 |  |  | // preSignValues data type represents structued form of AWS Signature V4 query string.
 | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | type preSignValues struct { | 
					
						
							|  |  |  | 	signValues | 
					
						
							|  |  |  | 	Date    time.Time | 
					
						
							|  |  |  | 	Expires time.Duration | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Parses signature version '4' query string of the following form.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //   querystring = X-Amz-Algorithm=algorithm
 | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | //   querystring += &X-Amz-Credential= urlencode(accessKey + '/' + credential_scope)
 | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | //   querystring += &X-Amz-Date=date
 | 
					
						
							|  |  |  | //   querystring += &X-Amz-Expires=timeout interval
 | 
					
						
							|  |  |  | //   querystring += &X-Amz-SignedHeaders=signed_headers
 | 
					
						
							|  |  |  | //   querystring += &X-Amz-Signature=signature
 | 
					
						
							| 
									
										
										
										
											2016-11-07 03:47:16 +08:00
										 |  |  | //
 | 
					
						
							| 
									
										
										
										
											2016-08-10 00:13:15 +08:00
										 |  |  | // verifies if any of the necessary query params are missing in the presigned request.
 | 
					
						
							|  |  |  | func doesV4PresignParamsExist(query url.Values) APIErrorCode { | 
					
						
							|  |  |  | 	v4PresignQueryParams := []string{"X-Amz-Algorithm", "X-Amz-Credential", "X-Amz-Signature", "X-Amz-Date", "X-Amz-SignedHeaders", "X-Amz-Expires"} | 
					
						
							|  |  |  | 	for _, v4PresignQueryParam := range v4PresignQueryParams { | 
					
						
							|  |  |  | 		if _, ok := query[v4PresignQueryParam]; !ok { | 
					
						
							|  |  |  | 			return ErrInvalidQueryParams | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return ErrNone | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-07 03:47:16 +08:00
										 |  |  | // Parses all the presigned signature values into separate elements.
 | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | func parsePreSignV4(query url.Values) (preSignValues, APIErrorCode) { | 
					
						
							| 
									
										
										
										
											2016-08-10 00:13:15 +08:00
										 |  |  | 	var err APIErrorCode | 
					
						
							|  |  |  | 	// verify whether the required query params exist.
 | 
					
						
							|  |  |  | 	err = doesV4PresignParamsExist(query) | 
					
						
							|  |  |  | 	if err != ErrNone { | 
					
						
							|  |  |  | 		return preSignValues{}, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	// Verify if the query algorithm is supported or not.
 | 
					
						
							|  |  |  | 	if query.Get("X-Amz-Algorithm") != signV4Algorithm { | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 		return preSignValues{}, ErrInvalidQuerySignatureAlgo | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Initialize signature version '4' structured header.
 | 
					
						
							|  |  |  | 	preSignV4Values := preSignValues{} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-22 09:57:05 +08:00
										 |  |  | 	// Save credential.
 | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 	preSignV4Values.Credential, err = parseCredentialHeader("Credential=" + query.Get("X-Amz-Credential")) | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 	if err != ErrNone { | 
					
						
							|  |  |  | 		return preSignValues{}, err | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var e error | 
					
						
							|  |  |  | 	// Save date in native time.Time.
 | 
					
						
							|  |  |  | 	preSignV4Values.Date, e = time.Parse(iso8601Format, query.Get("X-Amz-Date")) | 
					
						
							|  |  |  | 	if e != nil { | 
					
						
							| 
									
										
										
										
											2016-08-10 00:13:15 +08:00
										 |  |  | 		return preSignValues{}, ErrMalformedPresignedDate | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Save expires in native time.Duration.
 | 
					
						
							|  |  |  | 	preSignV4Values.Expires, e = time.ParseDuration(query.Get("X-Amz-Expires") + "s") | 
					
						
							|  |  |  | 	if e != nil { | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 		return preSignValues{}, ErrMalformedExpires | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-10 00:13:15 +08:00
										 |  |  | 	if preSignV4Values.Expires < 0 { | 
					
						
							|  |  |  | 		return preSignValues{}, ErrNegativeExpires | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	// Save signed headers.
 | 
					
						
							| 
									
										
										
										
											2016-11-07 03:47:16 +08:00
										 |  |  | 	preSignV4Values.SignedHeaders, err = parseSignedHeader("SignedHeaders=" + query.Get("X-Amz-SignedHeaders")) | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 	if err != ErrNone { | 
					
						
							|  |  |  | 		return preSignValues{}, err | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Save signature.
 | 
					
						
							| 
									
										
										
										
											2016-02-17 10:50:36 +08:00
										 |  |  | 	preSignV4Values.Signature, err = parseSignature("Signature=" + query.Get("X-Amz-Signature")) | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 	if err != ErrNone { | 
					
						
							|  |  |  | 		return preSignValues{}, err | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Return structed form of signature query string.
 | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 	return preSignV4Values, ErrNone | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Parses signature version '4' header of the following form.
 | 
					
						
							|  |  |  | //
 | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | //    Authorization: algorithm Credential=accessKeyID/credScope, \
 | 
					
						
							|  |  |  | //            SignedHeaders=signedHeaders, Signature=signature
 | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | //
 | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | func parseSignV4(v4Auth string) (signValues, APIErrorCode) { | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	// Replace all spaced strings, some clients can send spaced
 | 
					
						
							|  |  |  | 	// parameters and some won't. So we pro-actively remove any spaces
 | 
					
						
							|  |  |  | 	// to make parsing easier.
 | 
					
						
							|  |  |  | 	v4Auth = strings.Replace(v4Auth, " ", "", -1) | 
					
						
							|  |  |  | 	if v4Auth == "" { | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 		return signValues{}, ErrAuthHeaderEmpty | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Verify if the header algorithm is supported or not.
 | 
					
						
							|  |  |  | 	if !strings.HasPrefix(v4Auth, signV4Algorithm) { | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 		return signValues{}, ErrSignatureVersionNotSupported | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Strip off the Algorithm prefix.
 | 
					
						
							|  |  |  | 	v4Auth = strings.TrimPrefix(v4Auth, signV4Algorithm) | 
					
						
							|  |  |  | 	authFields := strings.Split(strings.TrimSpace(v4Auth), ",") | 
					
						
							|  |  |  | 	if len(authFields) != 3 { | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 		return signValues{}, ErrMissingFields | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Initialize signature version '4' structured header.
 | 
					
						
							|  |  |  | 	signV4Values := signValues{} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 	var err APIErrorCode | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	// Save credentail values.
 | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | 	signV4Values.Credential, err = parseCredentialHeader(authFields[0]) | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 	if err != ErrNone { | 
					
						
							|  |  |  | 		return signValues{}, err | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Save signed headers.
 | 
					
						
							| 
									
										
										
										
											2016-11-07 03:47:16 +08:00
										 |  |  | 	signV4Values.SignedHeaders, err = parseSignedHeader(authFields[1]) | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 	if err != ErrNone { | 
					
						
							|  |  |  | 		return signValues{}, err | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Save signature.
 | 
					
						
							|  |  |  | 	signV4Values.Signature, err = parseSignature(authFields[2]) | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 	if err != ErrNone { | 
					
						
							|  |  |  | 		return signValues{}, err | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Return the structure here.
 | 
					
						
							| 
									
										
										
										
											2016-03-31 11:04:51 +08:00
										 |  |  | 	return signV4Values, ErrNone | 
					
						
							| 
									
										
										
										
											2016-02-16 09:42:39 +08:00
										 |  |  | } |