Initial commit
This commit is contained in:
107
vendor/github.com/hyperhq/hyper-api/signature/sign.go
generated
vendored
Normal file
107
vendor/github.com/hyperhq/hyper-api/signature/sign.go
generated
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
package signature
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var b64 = base64.StdEncoding
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// S3 signing (http://goo.gl/G1LrK)
|
||||
|
||||
var s3ParamsToSign = map[string]bool{
|
||||
"acl": true,
|
||||
"location": true,
|
||||
"logging": true,
|
||||
"notification": true,
|
||||
"partNumber": true,
|
||||
"policy": true,
|
||||
"requestPayment": true,
|
||||
"torrent": true,
|
||||
"uploadId": true,
|
||||
"uploads": true,
|
||||
"versionId": true,
|
||||
"versioning": true,
|
||||
"versions": true,
|
||||
"response-content-type": true,
|
||||
"response-content-language": true,
|
||||
"response-expires": true,
|
||||
"response-cache-control": true,
|
||||
"response-content-disposition": true,
|
||||
"response-content-encoding": true,
|
||||
}
|
||||
|
||||
func makeSign(accessKey, secretKey string, req *http.Request) (string, error) {
|
||||
var md5, ctype, date, xamz string
|
||||
var xamzDate bool
|
||||
var sarray []string
|
||||
headers := req.Header
|
||||
req.ParseForm()
|
||||
params := req.Form
|
||||
method := req.Method
|
||||
canonicalPath := req.URL.EscapedPath()
|
||||
for k, v := range headers {
|
||||
k = strings.ToLower(k)
|
||||
switch k {
|
||||
case "content-md5":
|
||||
md5 = v[0]
|
||||
case "content-type":
|
||||
ctype = v[0]
|
||||
case "date":
|
||||
if !xamzDate {
|
||||
date = v[0]
|
||||
}
|
||||
default:
|
||||
if strings.HasPrefix(k, "x-amz-") {
|
||||
vall := strings.Join(v, ",")
|
||||
sarray = append(sarray, k+":"+vall)
|
||||
if k == "x-amz-date" {
|
||||
xamzDate = true
|
||||
date = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(sarray) > 0 {
|
||||
sort.StringSlice(sarray).Sort()
|
||||
xamz = strings.Join(sarray, "\n") + "\n"
|
||||
}
|
||||
|
||||
// expires := false
|
||||
if v, ok := params["Expires"]; ok {
|
||||
// Query string request authentication alternative.
|
||||
// expires = true
|
||||
date = v[0]
|
||||
params["HSCAccessKeyId"] = []string{accessKey}
|
||||
}
|
||||
|
||||
sarray = sarray[0:0]
|
||||
for k, v := range params {
|
||||
if s3ParamsToSign[k] {
|
||||
for _, vi := range v {
|
||||
if vi == "" {
|
||||
sarray = append(sarray, k)
|
||||
} else {
|
||||
// "When signing you do not encode these values."
|
||||
sarray = append(sarray, k+"="+vi)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(sarray) > 0 {
|
||||
sort.StringSlice(sarray).Sort()
|
||||
canonicalPath = canonicalPath + "?" + strings.Join(sarray, "&")
|
||||
}
|
||||
|
||||
payload := method + "\n" + md5 + "\n" + ctype + "\n" + date + "\n" + xamz + canonicalPath
|
||||
hash := hmac.New(sha1.New, []byte(secretKey))
|
||||
hash.Write([]byte(payload))
|
||||
signature := make([]byte, b64.EncodedLen(hash.Size()))
|
||||
b64.Encode(signature, hash.Sum(nil))
|
||||
return string(signature), nil
|
||||
}
|
||||
365
vendor/github.com/hyperhq/hyper-api/signature/sign4.go
generated
vendored
Normal file
365
vendor/github.com/hyperhq/hyper-api/signature/sign4.go
generated
vendored
Normal file
@@ -0,0 +1,365 @@
|
||||
/*
|
||||
Based on the AWS Signature Algorithm Sign4 http://docs.aws.amazon.com/general/latest/gr/sigv4_signing.html
|
||||
Based on the Implementation of https://github.com/smartystreets/go-aws-auth
|
||||
- Both Sign and Check
|
||||
- hostname of Hyper
|
||||
- change header X-AMZ- to X-Hyper-
|
||||
- changed normuri, treat // as /
|
||||
*/
|
||||
package signature
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
headerPrefix = "X-Hyper-"
|
||||
headerDate = "X-Hyper-Date"
|
||||
headerContentHash = "X-Hyper-Content-Sha256"
|
||||
headerAuthz = "Authorization"
|
||||
|
||||
metaAlgorithm = "HYPER-HMAC-SHA256"
|
||||
|
||||
keyPartsPrefix = "HYPER"
|
||||
keyPartsRequest = "hyper_request"
|
||||
|
||||
timeFormatV4 = "20060102T150405Z"
|
||||
|
||||
reqExpiration = 5 * time.Minute
|
||||
)
|
||||
|
||||
type AuthnHeader struct {
|
||||
Algorithm string
|
||||
AccessKey string
|
||||
Scope string
|
||||
SignedHeader string
|
||||
Signature string
|
||||
Date string
|
||||
}
|
||||
|
||||
func Signiture4(secretKey string, req *http.Request, header *AuthnHeader, region string) (bool, error) {
|
||||
meta := &metadata{
|
||||
algorithm: header.Algorithm,
|
||||
credentialScope: header.Scope,
|
||||
signedHeaders: header.SignedHeader,
|
||||
date: header.Date,
|
||||
region: region,
|
||||
service: "hyper",
|
||||
}
|
||||
|
||||
hashedCanonReq, ok := canonicalRequestV4FromMeta(req, meta)
|
||||
if !ok {
|
||||
return false, errors.New("payload check error")
|
||||
}
|
||||
|
||||
stringToSign := metaToSignV4(req, hashedCanonReq, meta)
|
||||
|
||||
signingKey := signingKeyV4(secretKey, meta.date, meta.region, meta.service)
|
||||
signature := signatureV4(signingKey, stringToSign)
|
||||
return signature == header.Signature, nil
|
||||
}
|
||||
|
||||
func Sign4(accessKey, secretKey string, req *http.Request, region string) *http.Request {
|
||||
|
||||
prepareRequestV4(req)
|
||||
meta := &metadata{}
|
||||
|
||||
// Task 1
|
||||
hashedCanonReq := hashedCanonicalRequestV4(req, meta)
|
||||
|
||||
// Task 2
|
||||
stringToSign := stringToSignV4(req, hashedCanonReq, meta, region)
|
||||
|
||||
// Task 3
|
||||
signingKey := signingKeyV4(secretKey, meta.date, meta.region, meta.service)
|
||||
signature := signatureV4(signingKey, stringToSign)
|
||||
|
||||
req.Header.Set(headerAuthz, buildAuthHeaderV4(accessKey, signature, meta))
|
||||
|
||||
return req
|
||||
}
|
||||
|
||||
// Build Request Steps
|
||||
func prepareRequestV4(request *http.Request) *http.Request {
|
||||
necessaryDefaults := map[string]string{
|
||||
"Content-Type": "application/json",
|
||||
headerDate: timestampV4(),
|
||||
}
|
||||
|
||||
for header, value := range necessaryDefaults {
|
||||
if request.Header.Get(header) == "" {
|
||||
request.Header.Set(header, value)
|
||||
}
|
||||
}
|
||||
|
||||
if request.URL.Path == "" {
|
||||
request.URL.Path += "/"
|
||||
}
|
||||
|
||||
return request
|
||||
}
|
||||
|
||||
func hashedCanonicalRequestV4(request *http.Request, meta *metadata) string {
|
||||
// TASK 1. http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
|
||||
|
||||
payload := readAndReplaceBody(request)
|
||||
payloadHash := hashSHA256(payload)
|
||||
request.Header.Set(headerContentHash, payloadHash)
|
||||
|
||||
// Set this in header values to make it appear in the range of headers to sign
|
||||
request.Header.Set("Host", request.URL.Host)
|
||||
|
||||
var sortedHeaderKeys []string
|
||||
for key := range request.Header {
|
||||
switch key {
|
||||
case "Content-Type", "Content-Md5", "Host":
|
||||
default:
|
||||
if !strings.HasPrefix(key, headerPrefix) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
sortedHeaderKeys = append(sortedHeaderKeys, strings.ToLower(key))
|
||||
}
|
||||
sort.Strings(sortedHeaderKeys)
|
||||
|
||||
var headersToSign string
|
||||
for _, key := range sortedHeaderKeys {
|
||||
value := strings.TrimSpace(request.Header.Get(key))
|
||||
if key == "host" {
|
||||
//Hyper(AWS) does not include port in signing request.
|
||||
if strings.Contains(value, ":") {
|
||||
split := strings.Split(value, ":")
|
||||
port := split[1]
|
||||
if port == "80" || port == "443" {
|
||||
value = split[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
headersToSign += key + ":" + value + "\n"
|
||||
}
|
||||
meta.signedHeaders = concat(";", sortedHeaderKeys...)
|
||||
canonicalRequest := concat("\n", request.Method, normuri(request.URL.Path), normquery(request.URL.Query()), headersToSign, meta.signedHeaders, payloadHash)
|
||||
|
||||
return hashSHA256([]byte(canonicalRequest))
|
||||
}
|
||||
|
||||
func canonicalRequestV4FromMeta(request *http.Request, meta *metadata) (string, bool) {
|
||||
payload := readPayload(request)
|
||||
payloadHash := hashSHA256(payload)
|
||||
if request.Header.Get(headerContentHash) != payloadHash {
|
||||
return "", false
|
||||
}
|
||||
var headersToSign string
|
||||
for _, hdr := range strings.Split(meta.signedHeaders, ";") {
|
||||
value := strings.TrimSpace(request.Header.Get(hdr))
|
||||
if hdr == "host" {
|
||||
//Hyper(AWS) does not include port in signing request.
|
||||
if strings.Contains(value, ":") {
|
||||
split := strings.Split(value, ":")
|
||||
port := split[1]
|
||||
if port == "80" || port == "443" {
|
||||
value = split[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
headersToSign += hdr + ":" + value + "\n"
|
||||
}
|
||||
canonicalRequest := concat("\n", request.Method, normuri(request.URL.Path), normquery(request.URL.Query()), headersToSign, meta.signedHeaders, payloadHash)
|
||||
return canonicalRequest, true
|
||||
}
|
||||
|
||||
func stringToSignV4(request *http.Request, hashedCanonReq string, meta *metadata, region string) string {
|
||||
// TASK 2. http://docs.aws.amazon.com/general/latest/gr/sigv4-create-string-to-sign.html
|
||||
|
||||
requestTs := request.Header.Get(headerDate)
|
||||
|
||||
meta.algorithm = metaAlgorithm
|
||||
meta.service, meta.region = serviceAndRegion(request.Host, region)
|
||||
meta.date = tsDateV4(requestTs)
|
||||
meta.credentialScope = concat("/", meta.date, meta.region, meta.service, keyPartsRequest)
|
||||
|
||||
return concat("\n", meta.algorithm, requestTs, meta.credentialScope, hashedCanonReq)
|
||||
}
|
||||
|
||||
func metaToSignV4(request *http.Request, hashedCanonReq string, meta *metadata) string {
|
||||
return concat("\n", meta.algorithm, request.Header.Get(headerDate), meta.credentialScope, hashedCanonReq)
|
||||
}
|
||||
|
||||
func signingKeyV4(secretKey, date, region, service string) []byte {
|
||||
kDate := hmacSHA256([]byte(keyPartsPrefix+secretKey), date)
|
||||
kRegion := hmacSHA256(kDate, region)
|
||||
kService := hmacSHA256(kRegion, service)
|
||||
kSigning := hmacSHA256(kService, keyPartsRequest)
|
||||
return kSigning
|
||||
}
|
||||
|
||||
func signatureV4(signingKey []byte, stringToSign string) string {
|
||||
// TASK 3. http://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html
|
||||
|
||||
return hex.EncodeToString(hmacSHA256(signingKey, stringToSign))
|
||||
}
|
||||
|
||||
func buildAuthHeaderV4(accessKey, signature string, meta *metadata) string {
|
||||
credential := accessKey + "/" + meta.credentialScope
|
||||
|
||||
return meta.algorithm +
|
||||
" Credential=" + credential +
|
||||
", SignedHeaders=" + meta.signedHeaders +
|
||||
", Signature=" + signature
|
||||
}
|
||||
|
||||
// Check Request Steps
|
||||
func validateExpire(req *http.Request) bool {
|
||||
dh := req.Header.Get(headerDate)
|
||||
if dh == "" {
|
||||
return false
|
||||
}
|
||||
date, err := time.ParseInLocation(timeFormatV4, dh, time.UTC)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if date.Add(reqExpiration).Before(time.Now().UTC()) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Details
|
||||
type metadata struct {
|
||||
algorithm string
|
||||
credentialScope string
|
||||
signedHeaders string
|
||||
date string
|
||||
region string
|
||||
service string
|
||||
}
|
||||
|
||||
func timestampV4() string {
|
||||
return time.Now().UTC().Format(timeFormatV4)
|
||||
}
|
||||
|
||||
func readAndReplaceBody(request *http.Request) []byte {
|
||||
if request.Body == nil {
|
||||
return []byte{}
|
||||
}
|
||||
payload, _ := ioutil.ReadAll(request.Body)
|
||||
request.Body = ioutil.NopCloser(bytes.NewReader(payload))
|
||||
return payload
|
||||
}
|
||||
|
||||
func readPayload(req *http.Request) []byte {
|
||||
if req.Body == nil {
|
||||
return []byte{}
|
||||
}
|
||||
payload, _ := ioutil.ReadAll(req.Body)
|
||||
return payload
|
||||
}
|
||||
|
||||
func hmacSHA256(key []byte, content string) []byte {
|
||||
mac := hmac.New(sha256.New, key)
|
||||
mac.Write([]byte(content))
|
||||
return mac.Sum(nil)
|
||||
}
|
||||
|
||||
func hashSHA256(content []byte) string {
|
||||
h := sha256.New()
|
||||
h.Write(content)
|
||||
return fmt.Sprintf("%x", h.Sum(nil))
|
||||
}
|
||||
|
||||
func concat(delim string, str ...string) string {
|
||||
return strings.Join(str, delim)
|
||||
}
|
||||
|
||||
func normuri(uri string) string {
|
||||
parts := []string{}
|
||||
for _, s := range strings.Split(uri, "/") {
|
||||
if s == "" {
|
||||
//bypass empty path segments
|
||||
continue
|
||||
}
|
||||
parts = append(parts, encodePathFrag(s))
|
||||
}
|
||||
return strings.Join(parts, "/")
|
||||
}
|
||||
|
||||
func encodePathFrag(s string) string {
|
||||
hexCount := 0
|
||||
for i := 0; i < len(s); i++ {
|
||||
c := s[i]
|
||||
if shouldEscape(c) {
|
||||
hexCount++
|
||||
}
|
||||
}
|
||||
t := make([]byte, len(s)+2*hexCount)
|
||||
j := 0
|
||||
for i := 0; i < len(s); i++ {
|
||||
c := s[i]
|
||||
if shouldEscape(c) {
|
||||
t[j] = '%'
|
||||
t[j+1] = "0123456789ABCDEF"[c>>4]
|
||||
t[j+2] = "0123456789ABCDEF"[c&15]
|
||||
j += 3
|
||||
} else {
|
||||
t[j] = c
|
||||
j++
|
||||
}
|
||||
}
|
||||
return string(t)
|
||||
}
|
||||
|
||||
func shouldEscape(c byte) bool {
|
||||
if 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' {
|
||||
return false
|
||||
}
|
||||
if '0' <= c && c <= '9' {
|
||||
return false
|
||||
}
|
||||
if c == '-' || c == '_' || c == '.' || c == '~' {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func normquery(v url.Values) string {
|
||||
queryString := v.Encode()
|
||||
|
||||
// Go encodes a space as '+' but Amazon requires '%20'. Luckily any '+' in the
|
||||
// original query string has been percent escaped so all '+' chars that are left
|
||||
// were originally spaces.
|
||||
|
||||
return strings.Replace(queryString, "+", "%20", -1)
|
||||
}
|
||||
|
||||
// serviceAndRegion parsers a hostname to find out which ones it is.
|
||||
func serviceAndRegion(host, r string) (service string, region string) {
|
||||
// These are the defaults if the hostname doesn't suggest something else
|
||||
region = r
|
||||
service = "hyper"
|
||||
|
||||
// region.hyper.sh
|
||||
if strings.HasSuffix(host, ".hyper.sh") {
|
||||
parts := strings.SplitN(host, ".", 2)
|
||||
if parts[1] == "hyper.sh" {
|
||||
region = parts[0]
|
||||
}
|
||||
}
|
||||
// no more service yet
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func tsDateV4(timestamp string) string {
|
||||
return timestamp[:8]
|
||||
}
|
||||
Reference in New Issue
Block a user