-
Notifications
You must be signed in to change notification settings - Fork 0
/
credentials.go
132 lines (110 loc) · 3.45 KB
/
credentials.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
package gcs
import (
"bytes"
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"io"
)
func ParseCredentialsFromJSON(raw []byte, options ...ResolverOption) (Credentials, error) {
if bytes.HasPrefix(raw, []byte("Bearer")) {
return Credentials{BearerToken: string(raw)}, nil
}
parsed, err := unmarshalClientCredentials(raw)
if err != nil {
return Credentials{}, err
}
user := parsed.ClientIdentity()
if len(user.RefreshToken) == 0 {
return NewCredentials(parsed.ServiceAccount())
}
resolver := newTokenResolver(options...)
accessToken, err := resolver.AccessToken(user)
if err != nil {
return Credentials{}, err
}
bearerToken := fmt.Sprintf("%s %s", accessToken.Type, accessToken.Value)
return Credentials{BearerToken: bearerToken}, nil
}
/* ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// */
type clientSecrets struct {
ClientID string `json:"client_id"`
ClientSecret string `json:"client_secret"`
RefreshToken string `json:"refresh_token"`
ClientEmail string `json:"client_email"`
PrivateKeyPEM string `json:"private_key"`
}
func unmarshalClientCredentials(raw []byte) (result clientSecrets, err error) {
if err = json.Unmarshal(raw, &result); err != nil {
return clientSecrets{}, ErrMalformedJSON
}
return result, err
}
func (this clientSecrets) ClientIdentity() ClientIdentity {
return ClientIdentity{
ID: this.ClientID,
Secret: this.ClientSecret,
RefreshToken: this.RefreshToken,
}
}
func (this clientSecrets) ServiceAccount() (string, []byte) {
return this.ClientEmail, []byte(this.PrivateKeyPEM)
}
/* ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// */
type Credentials struct {
BearerToken string
AccessID string
PrivateKey PrivateKey
}
func NewCredentials(accessID string, privateKey []byte) (Credentials, error) {
if parsed, err := newPrivateKey(privateKey); err != nil {
return Credentials{}, err
} else {
return Credentials{AccessID: accessID, PrivateKey: parsed}, nil
}
}
/* ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// */
type PrivateKey struct {
inner *rsa.PrivateKey
random io.Reader
}
func newPrivateKey(raw []byte) (PrivateKey, error) {
if parsed, err := tryReadPrivateKey(raw); err != nil {
return PrivateKey{}, ErrMalformedPrivateKey
} else {
return PrivateKey{inner: parsed, random: rand.Reader}, err
}
}
func tryReadPrivateKey(key []byte) (*rsa.PrivateKey, error) {
if decoded, _ := pem.Decode(key); decoded != nil {
key = decoded.Bytes
}
return tryParsePKCS8(key)
}
func tryParsePKCS8(key []byte) (*rsa.PrivateKey, error) {
if parsed, err := x509.ParsePKCS8PrivateKey(key); err != nil {
return nil, ErrMalformedPrivateKey
} else if parsedRSA, ok := parsed.(*rsa.PrivateKey); !ok {
return nil, ErrUnsupportedPrivateKey // e.g. ecdsa.PrivateKey
} else {
return parsedRSA, nil
}
}
func (this *PrivateKey) Sign(raw []byte) ([]byte, error) {
if this.inner == nil {
return nil, nil // no private key to sign with
}
sum := sha256.Sum256(raw)
return rsa.SignPKCS1v15(this.random, this.inner, crypto.SHA256, sum[:])
}
var (
ErrMalformedPrivateKey = errors.New("malformed private key")
ErrUnsupportedPrivateKey = errors.New("unsupported private key type")
ErrMalformedJSON = errors.New("malformed JSON")
)