1 package token
2
3 import (
4 "crypto"
5 "crypto/x509"
6 "encoding/base64"
7 "encoding/json"
8 "errors"
9 "fmt"
10 "strings"
11 "time"
12
13 "github.com/docker/libtrust"
14 log "github.com/sirupsen/logrus"
15
16 "github.com/docker/distribution/registry/auth"
17 )
18
19 const (
20
21
22 TokenSeparator = "."
23
24
25 Leeway = 60 * time.Second
26 )
27
28
29 var (
30 ErrMalformedToken = errors.New("malformed token")
31 ErrInvalidToken = errors.New("invalid token")
32 )
33
34
35 type ResourceActions struct {
36 Type string `json:"type"`
37 Class string `json:"class,omitempty"`
38 Name string `json:"name"`
39 Actions []string `json:"actions"`
40 }
41
42
43 type ClaimSet struct {
44
45 Issuer string `json:"iss"`
46 Subject string `json:"sub"`
47 Audience string `json:"aud"`
48 Expiration int64 `json:"exp"`
49 NotBefore int64 `json:"nbf"`
50 IssuedAt int64 `json:"iat"`
51 JWTID string `json:"jti"`
52
53
54 Access []*ResourceActions `json:"access"`
55 }
56
57
58 type Header struct {
59 Type string `json:"typ"`
60 SigningAlg string `json:"alg"`
61 KeyID string `json:"kid,omitempty"`
62 X5c []string `json:"x5c,omitempty"`
63 RawJWK *json.RawMessage `json:"jwk,omitempty"`
64 }
65
66
67 type Token struct {
68 Raw string
69 Header *Header
70 Claims *ClaimSet
71 Signature []byte
72 }
73
74
75
76 type VerifyOptions struct {
77 TrustedIssuers []string
78 AcceptedAudiences []string
79 Roots *x509.CertPool
80 TrustedKeys map[string]libtrust.PublicKey
81 }
82
83
84
85 func NewToken(rawToken string) (*Token, error) {
86 parts := strings.Split(rawToken, TokenSeparator)
87 if len(parts) != 3 {
88 return nil, ErrMalformedToken
89 }
90
91 var (
92 rawHeader, rawClaims = parts[0], parts[1]
93 headerJSON, claimsJSON []byte
94 err error
95 )
96
97 defer func() {
98 if err != nil {
99 log.Infof("error while unmarshalling raw token: %s", err)
100 }
101 }()
102
103 if headerJSON, err = joseBase64UrlDecode(rawHeader); err != nil {
104 err = fmt.Errorf("unable to decode header: %s", err)
105 return nil, ErrMalformedToken
106 }
107
108 if claimsJSON, err = joseBase64UrlDecode(rawClaims); err != nil {
109 err = fmt.Errorf("unable to decode claims: %s", err)
110 return nil, ErrMalformedToken
111 }
112
113 token := new(Token)
114 token.Header = new(Header)
115 token.Claims = new(ClaimSet)
116
117 token.Raw = strings.Join(parts[:2], TokenSeparator)
118 if token.Signature, err = joseBase64UrlDecode(parts[2]); err != nil {
119 err = fmt.Errorf("unable to decode signature: %s", err)
120 return nil, ErrMalformedToken
121 }
122
123 if err = json.Unmarshal(headerJSON, token.Header); err != nil {
124 return nil, ErrMalformedToken
125 }
126
127 if err = json.Unmarshal(claimsJSON, token.Claims); err != nil {
128 return nil, ErrMalformedToken
129 }
130
131 return token, nil
132 }
133
134
135
136 func (t *Token) Verify(verifyOpts VerifyOptions) error {
137
138 if !contains(verifyOpts.TrustedIssuers, t.Claims.Issuer) {
139 log.Infof("token from untrusted issuer: %q", t.Claims.Issuer)
140 return ErrInvalidToken
141 }
142
143
144 if !contains(verifyOpts.AcceptedAudiences, t.Claims.Audience) {
145 log.Infof("token intended for another audience: %q", t.Claims.Audience)
146 return ErrInvalidToken
147 }
148
149
150 currentTime := time.Now()
151
152 ExpWithLeeway := time.Unix(t.Claims.Expiration, 0).Add(Leeway)
153 if currentTime.After(ExpWithLeeway) {
154 log.Infof("token not to be used after %s - currently %s", ExpWithLeeway, currentTime)
155 return ErrInvalidToken
156 }
157
158 NotBeforeWithLeeway := time.Unix(t.Claims.NotBefore, 0).Add(-Leeway)
159 if currentTime.Before(NotBeforeWithLeeway) {
160 log.Infof("token not to be used before %s - currently %s", NotBeforeWithLeeway, currentTime)
161 return ErrInvalidToken
162 }
163
164
165 if len(t.Signature) == 0 {
166 log.Info("token has no signature")
167 return ErrInvalidToken
168 }
169
170
171 signingKey, err := t.VerifySigningKey(verifyOpts)
172 if err != nil {
173 log.Info(err)
174 return ErrInvalidToken
175 }
176
177
178 if err := signingKey.Verify(strings.NewReader(t.Raw), t.Header.SigningAlg, t.Signature); err != nil {
179 log.Infof("unable to verify token signature: %s", err)
180 return ErrInvalidToken
181 }
182
183 return nil
184 }
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199 func (t *Token) VerifySigningKey(verifyOpts VerifyOptions) (signingKey libtrust.PublicKey, err error) {
200
201 var (
202 x5c = t.Header.X5c
203 rawJWK = t.Header.RawJWK
204 keyID = t.Header.KeyID
205 )
206
207 switch {
208 case len(x5c) > 0:
209 signingKey, err = parseAndVerifyCertChain(x5c, verifyOpts.Roots)
210 case rawJWK != nil:
211 signingKey, err = parseAndVerifyRawJWK(rawJWK, verifyOpts)
212 case len(keyID) > 0:
213 signingKey = verifyOpts.TrustedKeys[keyID]
214 if signingKey == nil {
215 err = fmt.Errorf("token signed by untrusted key with ID: %q", keyID)
216 }
217 default:
218 err = errors.New("unable to get token signing key")
219 }
220
221 return
222 }
223
224 func parseAndVerifyCertChain(x5c []string, roots *x509.CertPool) (leafKey libtrust.PublicKey, err error) {
225 if len(x5c) == 0 {
226 return nil, errors.New("empty x509 certificate chain")
227 }
228
229
230 leafCertDer, err := base64.StdEncoding.DecodeString(x5c[0])
231 if err != nil {
232 return nil, fmt.Errorf("unable to decode leaf certificate: %s", err)
233 }
234
235
236 leafCert, err := x509.ParseCertificate(leafCertDer)
237 if err != nil {
238 return nil, fmt.Errorf("unable to parse leaf certificate: %s", err)
239 }
240
241
242 intermediates := x509.NewCertPool()
243 for i := 1; i < len(x5c); i++ {
244 intermediateCertDer, err := base64.StdEncoding.DecodeString(x5c[i])
245 if err != nil {
246 return nil, fmt.Errorf("unable to decode intermediate certificate: %s", err)
247 }
248
249 intermediateCert, err := x509.ParseCertificate(intermediateCertDer)
250 if err != nil {
251 return nil, fmt.Errorf("unable to parse intermediate certificate: %s", err)
252 }
253
254 intermediates.AddCert(intermediateCert)
255 }
256
257 verifyOpts := x509.VerifyOptions{
258 Intermediates: intermediates,
259 Roots: roots,
260 KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
261 }
262
263
264
265 if _, err = leafCert.Verify(verifyOpts); err != nil {
266 return nil, fmt.Errorf("unable to verify certificate chain: %s", err)
267 }
268
269
270 leafCryptoKey, ok := leafCert.PublicKey.(crypto.PublicKey)
271 if !ok {
272 return nil, errors.New("unable to get leaf cert public key value")
273 }
274
275 leafKey, err = libtrust.FromCryptoPublicKey(leafCryptoKey)
276 if err != nil {
277 return nil, fmt.Errorf("unable to make libtrust public key from leaf certificate: %s", err)
278 }
279
280 return
281 }
282
283 func parseAndVerifyRawJWK(rawJWK *json.RawMessage, verifyOpts VerifyOptions) (pubKey libtrust.PublicKey, err error) {
284 pubKey, err = libtrust.UnmarshalPublicKeyJWK([]byte(*rawJWK))
285 if err != nil {
286 return nil, fmt.Errorf("unable to decode raw JWK value: %s", err)
287 }
288
289
290 x5cVal, ok := pubKey.GetExtendedField("x5c").([]interface{})
291 if !ok {
292
293 if _, trusted := verifyOpts.TrustedKeys[pubKey.KeyID()]; !trusted {
294 return nil, errors.New("untrusted JWK with no certificate chain")
295 }
296
297
298 return
299 }
300
301
302 x5c := make([]string, len(x5cVal))
303 for i, val := range x5cVal {
304 certString, ok := val.(string)
305 if !ok || len(certString) == 0 {
306 return nil, errors.New("malformed certificate chain")
307 }
308 x5c[i] = certString
309 }
310
311
312
313 leafKey, err := parseAndVerifyCertChain(x5c, verifyOpts.Roots)
314 if err != nil {
315 return nil, fmt.Errorf("could not verify JWK certificate chain: %s", err)
316 }
317
318
319 if pubKey.KeyID() != leafKey.KeyID() {
320 return nil, errors.New("leaf certificate public key ID does not match JWK key ID")
321 }
322
323 return
324 }
325
326
327
328 func (t *Token) accessSet() accessSet {
329 if t.Claims == nil {
330 return nil
331 }
332
333 accessSet := make(accessSet, len(t.Claims.Access))
334
335 for _, resourceActions := range t.Claims.Access {
336 resource := auth.Resource{
337 Type: resourceActions.Type,
338 Name: resourceActions.Name,
339 }
340
341 set, exists := accessSet[resource]
342 if !exists {
343 set = newActionSet()
344 accessSet[resource] = set
345 }
346
347 for _, action := range resourceActions.Actions {
348 set.add(action)
349 }
350 }
351
352 return accessSet
353 }
354
355 func (t *Token) resources() []auth.Resource {
356 if t.Claims == nil {
357 return nil
358 }
359
360 resourceSet := map[auth.Resource]struct{}{}
361 for _, resourceActions := range t.Claims.Access {
362 resource := auth.Resource{
363 Type: resourceActions.Type,
364 Class: resourceActions.Class,
365 Name: resourceActions.Name,
366 }
367 resourceSet[resource] = struct{}{}
368 }
369
370 resources := make([]auth.Resource, 0, len(resourceSet))
371 for resource := range resourceSet {
372 resources = append(resources, resource)
373 }
374
375 return resources
376 }
377
378 func (t *Token) compactRaw() string {
379 return fmt.Sprintf("%s.%s", t.Raw, joseBase64UrlEncode(t.Signature))
380 }
381
View as plain text