-
Notifications
You must be signed in to change notification settings - Fork 6
/
auth.go
132 lines (116 loc) · 3.45 KB
/
auth.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
//
// gosign - Go HTTP signing library for the Joyent Public Cloud and Joyent Manta
//
//
// Copyright (c) 2013 Joyent Inc.
//
// Written by Daniele Stroppa <[email protected]>
//
package auth
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"fmt"
"net/http"
"net/url"
"strings"
)
const (
// Authorization Headers
SdcSignature = "Signature keyId=\"/%s/keys/%s\",algorithm=\"%s\" %s"
MantaSignature = "Signature keyId=\"/%s/keys/%s\",algorithm=\"%s\",signature=\"%s\""
)
type Endpoint struct {
URL string
}
type Auth struct {
User string
PrivateKey PrivateKey
Algorithm string
}
type Credentials struct {
UserAuthentication *Auth
SdcKeyId string
SdcEndpoint Endpoint
MantaKeyId string
MantaEndpoint Endpoint
}
type PrivateKey struct {
key *rsa.PrivateKey
}
// NewAuth creates a new Auth.
func NewAuth(user, privateKey, algorithm string) (*Auth, error) {
block, _ := pem.Decode([]byte(privateKey))
if block == nil {
return nil, fmt.Errorf("invalid private key data: %s", privateKey)
}
rsakey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, fmt.Errorf("An error occurred while parsing the key: %s", err)
}
return &Auth{user, PrivateKey{rsakey}, algorithm}, nil
}
// The CreateAuthorizationHeader returns the Authorization header for the give request.
func CreateAuthorizationHeader(headers http.Header, credentials *Credentials, isMantaRequest bool) (string, error) {
if isMantaRequest {
signature, err := GetSignature(credentials.UserAuthentication, "date: "+headers.Get("Date"))
if err != nil {
return "", err
}
return fmt.Sprintf(MantaSignature, credentials.UserAuthentication.User, credentials.MantaKeyId,
credentials.UserAuthentication.Algorithm, signature), nil
}
signature, err := GetSignature(credentials.UserAuthentication, headers.Get("Date"))
if err != nil {
return "", err
}
return fmt.Sprintf(SdcSignature, credentials.UserAuthentication.User, credentials.SdcKeyId,
credentials.UserAuthentication.Algorithm, signature), nil
}
// The GetSignature method signs the specified key according to http://apidocs.joyent.com/cloudapi/#issuing-requests
// and http://apidocs.joyent.com/manta/api.html#authentication.
func GetSignature(auth *Auth, signing string) (string, error) {
hashFunc := getHashFunction(auth.Algorithm)
hash := hashFunc.New()
hash.Write([]byte(signing))
digest := hash.Sum(nil)
signed, err := rsa.SignPKCS1v15(rand.Reader, auth.PrivateKey.key, hashFunc, digest)
if err != nil {
return "", fmt.Errorf("An error occurred while signing the key: %s", err)
}
return base64.StdEncoding.EncodeToString(signed), nil
}
// Helper method to get the Hash function based on the algorithm
func getHashFunction(algorithm string) (hashFunc crypto.Hash) {
switch strings.ToLower(algorithm) {
case "rsa-sha1":
hashFunc = crypto.SHA1
case "rsa-sha224", "rsa-sha256":
hashFunc = crypto.SHA256
case "rsa-sha384", "rsa-sha512":
hashFunc = crypto.SHA512
default:
hashFunc = crypto.SHA256
}
return
}
func (cred *Credentials) Region() string {
parsedUrl, err := url.Parse(cred.SdcEndpoint.URL)
if err != nil {
// Bogus URL - no region.
return ""
}
if strings.HasPrefix(parsedUrl.Host, "localhost") || strings.HasPrefix(parsedUrl.Host, "127.0.0.1") {
return "some-region"
}
host := parsedUrl.Host
firstDotIdx := strings.Index(host, ".")
if firstDotIdx >= 0 {
return host[:firstDotIdx]
}
return host
}