github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/chat/s3/sign.go (about)

     1  package s3
     2  
     3  import (
     4  	"log"
     5  	"sort"
     6  	"strings"
     7  )
     8  
     9  // ----------------------------------------------------------------------------
    10  // S3 signing (http://goo.gl/G1LrK)
    11  
    12  var s3ParamsToSign = map[string]bool{
    13  	"acl":                          true,
    14  	"location":                     true,
    15  	"logging":                      true,
    16  	"notification":                 true,
    17  	"partNumber":                   true,
    18  	"policy":                       true,
    19  	"requestPayment":               true,
    20  	"torrent":                      true,
    21  	"uploadId":                     true,
    22  	"uploads":                      true,
    23  	"versionId":                    true,
    24  	"versioning":                   true,
    25  	"versions":                     true,
    26  	"response-content-type":        true,
    27  	"response-content-language":    true,
    28  	"response-expires":             true,
    29  	"response-cache-control":       true,
    30  	"response-content-disposition": true,
    31  	"response-content-encoding":    true,
    32  	"website":                      true,
    33  	"delete":                       true,
    34  }
    35  
    36  type keySortableTupleList []keySortableTuple
    37  
    38  type keySortableTuple struct {
    39  	Key         string
    40  	TupleString string
    41  }
    42  
    43  func (l keySortableTupleList) StringSlice() []string {
    44  	slice := make([]string, len(l))
    45  	for i, v := range l {
    46  		slice[i] = v.TupleString
    47  	}
    48  	return slice
    49  }
    50  
    51  func (l keySortableTupleList) Len() int {
    52  	return len(l)
    53  }
    54  
    55  func (l keySortableTupleList) Less(i, j int) bool {
    56  	return l[i].Key < l[j].Key
    57  }
    58  
    59  func (l keySortableTupleList) Swap(i, j int) {
    60  	l[i], l[j] = l[j], l[i]
    61  }
    62  
    63  func (s *S3) sign(method, canonicalPath string, params, headers map[string][]string) error {
    64  	var md5, ctype, date, xamz string
    65  	var xamzDate bool
    66  	var sarray keySortableTupleList
    67  	for k, v := range headers {
    68  		k = strings.ToLower(k)
    69  		switch k {
    70  		case "content-md5":
    71  			md5 = v[0]
    72  		case "content-type":
    73  			ctype = v[0]
    74  		case "date":
    75  			if !xamzDate {
    76  				date = v[0]
    77  			}
    78  		default:
    79  			if strings.HasPrefix(k, "x-amz-") {
    80  				vall := strings.Join(v, ",")
    81  				sarray = append(sarray, keySortableTuple{k, k + ":" + vall})
    82  				if k == "x-amz-date" {
    83  					xamzDate = true
    84  					date = ""
    85  				}
    86  			}
    87  		}
    88  	}
    89  	if len(sarray) > 0 {
    90  		sort.Sort(sarray)
    91  		xamz = strings.Join(sarray.StringSlice(), "\n") + "\n"
    92  	}
    93  
    94  	expires := false
    95  	if v, ok := params["Expires"]; ok {
    96  		// Query string request authentication alternative.
    97  		expires = true
    98  		date = v[0]
    99  		params["AWSAccessKeyId"] = []string{s.AccessKey}
   100  	}
   101  
   102  	sarray = sarray[0:0]
   103  	for k, v := range params {
   104  		if s3ParamsToSign[k] {
   105  			for _, vi := range v {
   106  				if vi == "" {
   107  					sarray = append(sarray, keySortableTuple{k, k})
   108  				} else {
   109  					// "When signing you do not encode these values."
   110  					sarray = append(sarray, keySortableTuple{k, k + "=" + vi})
   111  				}
   112  			}
   113  		}
   114  	}
   115  	if len(sarray) > 0 {
   116  		sort.Sort(sarray)
   117  		canonicalPath = canonicalPath + "?" + strings.Join(sarray.StringSlice(), "&")
   118  	}
   119  
   120  	payload := method + "\n" + md5 + "\n" + ctype + "\n" + date + "\n" + xamz + canonicalPath
   121  
   122  	signature, err := s.Signer.Sign([]byte(payload))
   123  	if err != nil {
   124  		return err
   125  	}
   126  
   127  	if expires {
   128  		params["Signature"] = []string{string(signature)}
   129  	} else {
   130  		headers["Authorization"] = []string{"AWS " + s.AccessKey + ":" + string(signature)}
   131  	}
   132  	if debug {
   133  		log.Printf("Signature payload: %q", payload)
   134  		log.Printf("Signature: %q", signature)
   135  	}
   136  
   137  	return nil
   138  }