diff options
Diffstat (limited to 'vendor/golang.org/x/oauth2/jws')
-rw-r--r-- | vendor/golang.org/x/oauth2/jws/jws.go | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/vendor/golang.org/x/oauth2/jws/jws.go b/vendor/golang.org/x/oauth2/jws/jws.go new file mode 100644 index 0000000..683d2d2 --- /dev/null +++ b/vendor/golang.org/x/oauth2/jws/jws.go | |||
@@ -0,0 +1,182 @@ | |||
1 | // Copyright 2014 The Go Authors. All rights reserved. | ||
2 | // Use of this source code is governed by a BSD-style | ||
3 | // license that can be found in the LICENSE file. | ||
4 | |||
5 | // Package jws provides a partial implementation | ||
6 | // of JSON Web Signature encoding and decoding. | ||
7 | // It exists to support the golang.org/x/oauth2 package. | ||
8 | // | ||
9 | // See RFC 7515. | ||
10 | // | ||
11 | // Deprecated: this package is not intended for public use and might be | ||
12 | // removed in the future. It exists for internal use only. | ||
13 | // Please switch to another JWS package or copy this package into your own | ||
14 | // source tree. | ||
15 | package jws // import "golang.org/x/oauth2/jws" | ||
16 | |||
17 | import ( | ||
18 | "bytes" | ||
19 | "crypto" | ||
20 | "crypto/rand" | ||
21 | "crypto/rsa" | ||
22 | "crypto/sha256" | ||
23 | "encoding/base64" | ||
24 | "encoding/json" | ||
25 | "errors" | ||
26 | "fmt" | ||
27 | "strings" | ||
28 | "time" | ||
29 | ) | ||
30 | |||
31 | // ClaimSet contains information about the JWT signature including the | ||
32 | // permissions being requested (scopes), the target of the token, the issuer, | ||
33 | // the time the token was issued, and the lifetime of the token. | ||
34 | type ClaimSet struct { | ||
35 | Iss string `json:"iss"` // email address of the client_id of the application making the access token request | ||
36 | Scope string `json:"scope,omitempty"` // space-delimited list of the permissions the application requests | ||
37 | Aud string `json:"aud"` // descriptor of the intended target of the assertion (Optional). | ||
38 | Exp int64 `json:"exp"` // the expiration time of the assertion (seconds since Unix epoch) | ||
39 | Iat int64 `json:"iat"` // the time the assertion was issued (seconds since Unix epoch) | ||
40 | Typ string `json:"typ,omitempty"` // token type (Optional). | ||
41 | |||
42 | // Email for which the application is requesting delegated access (Optional). | ||
43 | Sub string `json:"sub,omitempty"` | ||
44 | |||
45 | // The old name of Sub. Client keeps setting Prn to be | ||
46 | // complaint with legacy OAuth 2.0 providers. (Optional) | ||
47 | Prn string `json:"prn,omitempty"` | ||
48 | |||
49 | // See http://tools.ietf.org/html/draft-jones-json-web-token-10#section-4.3 | ||
50 | // This array is marshalled using custom code (see (c *ClaimSet) encode()). | ||
51 | PrivateClaims map[string]interface{} `json:"-"` | ||
52 | } | ||
53 | |||
54 | func (c *ClaimSet) encode() (string, error) { | ||
55 | // Reverting time back for machines whose time is not perfectly in sync. | ||
56 | // If client machine's time is in the future according | ||
57 | // to Google servers, an access token will not be issued. | ||
58 | now := time.Now().Add(-10 * time.Second) | ||
59 | if c.Iat == 0 { | ||
60 | c.Iat = now.Unix() | ||
61 | } | ||
62 | if c.Exp == 0 { | ||
63 | c.Exp = now.Add(time.Hour).Unix() | ||
64 | } | ||
65 | if c.Exp < c.Iat { | ||
66 | return "", fmt.Errorf("jws: invalid Exp = %v; must be later than Iat = %v", c.Exp, c.Iat) | ||
67 | } | ||
68 | |||
69 | b, err := json.Marshal(c) | ||
70 | if err != nil { | ||
71 | return "", err | ||
72 | } | ||
73 | |||
74 | if len(c.PrivateClaims) == 0 { | ||
75 | return base64.RawURLEncoding.EncodeToString(b), nil | ||
76 | } | ||
77 | |||
78 | // Marshal private claim set and then append it to b. | ||
79 | prv, err := json.Marshal(c.PrivateClaims) | ||
80 | if err != nil { | ||
81 | return "", fmt.Errorf("jws: invalid map of private claims %v", c.PrivateClaims) | ||
82 | } | ||
83 | |||
84 | // Concatenate public and private claim JSON objects. | ||
85 | if !bytes.HasSuffix(b, []byte{'}'}) { | ||
86 | return "", fmt.Errorf("jws: invalid JSON %s", b) | ||
87 | } | ||
88 | if !bytes.HasPrefix(prv, []byte{'{'}) { | ||
89 | return "", fmt.Errorf("jws: invalid JSON %s", prv) | ||
90 | } | ||
91 | b[len(b)-1] = ',' // Replace closing curly brace with a comma. | ||
92 | b = append(b, prv[1:]...) // Append private claims. | ||
93 | return base64.RawURLEncoding.EncodeToString(b), nil | ||
94 | } | ||
95 | |||
96 | // Header represents the header for the signed JWS payloads. | ||
97 | type Header struct { | ||
98 | // The algorithm used for signature. | ||
99 | Algorithm string `json:"alg"` | ||
100 | |||
101 | // Represents the token type. | ||
102 | Typ string `json:"typ"` | ||
103 | |||
104 | // The optional hint of which key is being used. | ||
105 | KeyID string `json:"kid,omitempty"` | ||
106 | } | ||
107 | |||
108 | func (h *Header) encode() (string, error) { | ||
109 | b, err := json.Marshal(h) | ||
110 | if err != nil { | ||
111 | return "", err | ||
112 | } | ||
113 | return base64.RawURLEncoding.EncodeToString(b), nil | ||
114 | } | ||
115 | |||
116 | // Decode decodes a claim set from a JWS payload. | ||
117 | func Decode(payload string) (*ClaimSet, error) { | ||
118 | // decode returned id token to get expiry | ||
119 | s := strings.Split(payload, ".") | ||
120 | if len(s) < 2 { | ||
121 | // TODO(jbd): Provide more context about the error. | ||
122 | return nil, errors.New("jws: invalid token received") | ||
123 | } | ||
124 | decoded, err := base64.RawURLEncoding.DecodeString(s[1]) | ||
125 | if err != nil { | ||
126 | return nil, err | ||
127 | } | ||
128 | c := &ClaimSet{} | ||
129 | err = json.NewDecoder(bytes.NewBuffer(decoded)).Decode(c) | ||
130 | return c, err | ||
131 | } | ||
132 | |||
133 | // Signer returns a signature for the given data. | ||
134 | type Signer func(data []byte) (sig []byte, err error) | ||
135 | |||
136 | // EncodeWithSigner encodes a header and claim set with the provided signer. | ||
137 | func EncodeWithSigner(header *Header, c *ClaimSet, sg Signer) (string, error) { | ||
138 | head, err := header.encode() | ||
139 | if err != nil { | ||
140 | return "", err | ||
141 | } | ||
142 | cs, err := c.encode() | ||
143 | if err != nil { | ||
144 | return "", err | ||
145 | } | ||
146 | ss := fmt.Sprintf("%s.%s", head, cs) | ||
147 | sig, err := sg([]byte(ss)) | ||
148 | if err != nil { | ||
149 | return "", err | ||
150 | } | ||
151 | return fmt.Sprintf("%s.%s", ss, base64.RawURLEncoding.EncodeToString(sig)), nil | ||
152 | } | ||
153 | |||
154 | // Encode encodes a signed JWS with provided header and claim set. | ||
155 | // This invokes EncodeWithSigner using crypto/rsa.SignPKCS1v15 with the given RSA private key. | ||
156 | func Encode(header *Header, c *ClaimSet, key *rsa.PrivateKey) (string, error) { | ||
157 | sg := func(data []byte) (sig []byte, err error) { | ||
158 | h := sha256.New() | ||
159 | h.Write(data) | ||
160 | return rsa.SignPKCS1v15(rand.Reader, key, crypto.SHA256, h.Sum(nil)) | ||
161 | } | ||
162 | return EncodeWithSigner(header, c, sg) | ||
163 | } | ||
164 | |||
165 | // Verify tests whether the provided JWT token's signature was produced by the private key | ||
166 | // associated with the supplied public key. | ||
167 | func Verify(token string, key *rsa.PublicKey) error { | ||
168 | parts := strings.Split(token, ".") | ||
169 | if len(parts) != 3 { | ||
170 | return errors.New("jws: invalid token received, token must have 3 parts") | ||
171 | } | ||
172 | |||
173 | signedContent := parts[0] + "." + parts[1] | ||
174 | signatureString, err := base64.RawURLEncoding.DecodeString(parts[2]) | ||
175 | if err != nil { | ||
176 | return err | ||
177 | } | ||
178 | |||
179 | h := sha256.New() | ||
180 | h.Write([]byte(signedContent)) | ||
181 | return rsa.VerifyPKCS1v15(key, crypto.SHA256, h.Sum(nil), []byte(signatureString)) | ||
182 | } | ||