1package token
2
3import (
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
19const (
20	// TokenSeparator is the value which separates the header, claims, and
21	// signature in the compact serialization of a JSON Web Token.
22	TokenSeparator = "."
23	// Leeway is the Duration that will be added to NBF and EXP claim
24	// checks to account for clock skew as per https://tools.ietf.org/html/rfc7519#section-4.1.5
25	Leeway = 60 * time.Second
26)
27
28// Errors used by token parsing and verification.
29var (
30	ErrMalformedToken = errors.New("malformed token")
31	ErrInvalidToken   = errors.New("invalid token")
32)
33
34// ResourceActions stores allowed actions on a named and typed resource.
35type 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// ClaimSet describes the main section of a JSON Web Token.
43type ClaimSet struct {
44	// Public claims
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	// Private claims
54	Access []*ResourceActions `json:"access"`
55}
56
57// Header describes the header section of a JSON Web Token.
58type 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// Token describes a JSON Web Token.
67type Token struct {
68	Raw       string
69	Header    *Header
70	Claims    *ClaimSet
71	Signature []byte
72}
73
74// VerifyOptions is used to specify
75// options when verifying a JSON Web Token.
76type VerifyOptions struct {
77	TrustedIssuers    []string
78	AcceptedAudiences []string
79	Roots             *x509.CertPool
80	TrustedKeys       map[string]libtrust.PublicKey
81}
82
83// NewToken parses the given raw token string
84// and constructs an unverified JSON Web Token.
85func 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// Verify attempts to verify this token using the given options.
135// Returns a nil error if the token is valid.
136func (t *Token) Verify(verifyOpts VerifyOptions) error {
137	// Verify that the Issuer claim is a trusted authority.
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	// Verify that the Audience claim is allowed.
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	// Verify that the token is currently usable and not expired.
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	// Verify the token signature.
165	if len(t.Signature) == 0 {
166		log.Info("token has no signature")
167		return ErrInvalidToken
168	}
169
170	// Verify that the signing key is trusted.
171	signingKey, err := t.VerifySigningKey(verifyOpts)
172	if err != nil {
173		log.Info(err)
174		return ErrInvalidToken
175	}
176
177	// Finally, verify the signature of the token using the key which signed it.
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// VerifySigningKey attempts to get the key which was used to sign this token.
187// The token header should contain either of these 3 fields:
188//      `x5c` - The x509 certificate chain for the signing key. Needs to be
189//              verified.
190//      `jwk` - The JSON Web Key representation of the signing key.
191//              May contain its own `x5c` field which needs to be verified.
192//      `kid` - The unique identifier for the key. This library interprets it
193//              as a libtrust fingerprint. The key itself can be looked up in
194//              the trustedKeys field of the given verify options.
195// Each of these methods are tried in that order of preference until the
196// signing key is found or an error is returned.
197func (t *Token) VerifySigningKey(verifyOpts VerifyOptions) (signingKey libtrust.PublicKey, err error) {
198	// First attempt to get an x509 certificate chain from the header.
199	var (
200		x5c    = t.Header.X5c
201		rawJWK = t.Header.RawJWK
202		keyID  = t.Header.KeyID
203	)
204
205	switch {
206	case len(x5c) > 0:
207		signingKey, err = parseAndVerifyCertChain(x5c, verifyOpts.Roots)
208	case rawJWK != nil:
209		signingKey, err = parseAndVerifyRawJWK(rawJWK, verifyOpts)
210	case len(keyID) > 0:
211		signingKey = verifyOpts.TrustedKeys[keyID]
212		if signingKey == nil {
213			err = fmt.Errorf("token signed by untrusted key with ID: %q", keyID)
214		}
215	default:
216		err = errors.New("unable to get token signing key")
217	}
218
219	return
220}
221
222func parseAndVerifyCertChain(x5c []string, roots *x509.CertPool) (leafKey libtrust.PublicKey, err error) {
223	if len(x5c) == 0 {
224		return nil, errors.New("empty x509 certificate chain")
225	}
226
227	// Ensure the first element is encoded correctly.
228	leafCertDer, err := base64.StdEncoding.DecodeString(x5c[0])
229	if err != nil {
230		return nil, fmt.Errorf("unable to decode leaf certificate: %s", err)
231	}
232
233	// And that it is a valid x509 certificate.
234	leafCert, err := x509.ParseCertificate(leafCertDer)
235	if err != nil {
236		return nil, fmt.Errorf("unable to parse leaf certificate: %s", err)
237	}
238
239	// The rest of the certificate chain are intermediate certificates.
240	intermediates := x509.NewCertPool()
241	for i := 1; i < len(x5c); i++ {
242		intermediateCertDer, err := base64.StdEncoding.DecodeString(x5c[i])
243		if err != nil {
244			return nil, fmt.Errorf("unable to decode intermediate certificate: %s", err)
245		}
246
247		intermediateCert, err := x509.ParseCertificate(intermediateCertDer)
248		if err != nil {
249			return nil, fmt.Errorf("unable to parse intermediate certificate: %s", err)
250		}
251
252		intermediates.AddCert(intermediateCert)
253	}
254
255	verifyOpts := x509.VerifyOptions{
256		Intermediates: intermediates,
257		Roots:         roots,
258		KeyUsages:     []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
259	}
260
261	// TODO: this call returns certificate chains which we ignore for now, but
262	// we should check them for revocations if we have the ability later.
263	if _, err = leafCert.Verify(verifyOpts); err != nil {
264		return nil, fmt.Errorf("unable to verify certificate chain: %s", err)
265	}
266
267	// Get the public key from the leaf certificate.
268	leafCryptoKey, ok := leafCert.PublicKey.(crypto.PublicKey)
269	if !ok {
270		return nil, errors.New("unable to get leaf cert public key value")
271	}
272
273	leafKey, err = libtrust.FromCryptoPublicKey(leafCryptoKey)
274	if err != nil {
275		return nil, fmt.Errorf("unable to make libtrust public key from leaf certificate: %s", err)
276	}
277
278	return
279}
280
281func parseAndVerifyRawJWK(rawJWK *json.RawMessage, verifyOpts VerifyOptions) (pubKey libtrust.PublicKey, err error) {
282	pubKey, err = libtrust.UnmarshalPublicKeyJWK([]byte(*rawJWK))
283	if err != nil {
284		return nil, fmt.Errorf("unable to decode raw JWK value: %s", err)
285	}
286
287	// Check to see if the key includes a certificate chain.
288	x5cVal, ok := pubKey.GetExtendedField("x5c").([]interface{})
289	if !ok {
290		// The JWK should be one of the trusted root keys.
291		if _, trusted := verifyOpts.TrustedKeys[pubKey.KeyID()]; !trusted {
292			return nil, errors.New("untrusted JWK with no certificate chain")
293		}
294
295		// The JWK is one of the trusted keys.
296		return
297	}
298
299	// Ensure each item in the chain is of the correct type.
300	x5c := make([]string, len(x5cVal))
301	for i, val := range x5cVal {
302		certString, ok := val.(string)
303		if !ok || len(certString) == 0 {
304			return nil, errors.New("malformed certificate chain")
305		}
306		x5c[i] = certString
307	}
308
309	// Ensure that the x509 certificate chain can
310	// be verified up to one of our trusted roots.
311	leafKey, err := parseAndVerifyCertChain(x5c, verifyOpts.Roots)
312	if err != nil {
313		return nil, fmt.Errorf("could not verify JWK certificate chain: %s", err)
314	}
315
316	// Verify that the public key in the leaf cert *is* the signing key.
317	if pubKey.KeyID() != leafKey.KeyID() {
318		return nil, errors.New("leaf certificate public key ID does not match JWK key ID")
319	}
320
321	return
322}
323
324// accessSet returns a set of actions available for the resource
325// actions listed in the `access` section of this token.
326func (t *Token) accessSet() accessSet {
327	if t.Claims == nil {
328		return nil
329	}
330
331	accessSet := make(accessSet, len(t.Claims.Access))
332
333	for _, resourceActions := range t.Claims.Access {
334		resource := auth.Resource{
335			Type: resourceActions.Type,
336			Name: resourceActions.Name,
337		}
338
339		set, exists := accessSet[resource]
340		if !exists {
341			set = newActionSet()
342			accessSet[resource] = set
343		}
344
345		for _, action := range resourceActions.Actions {
346			set.add(action)
347		}
348	}
349
350	return accessSet
351}
352
353func (t *Token) resources() []auth.Resource {
354	if t.Claims == nil {
355		return nil
356	}
357
358	resourceSet := map[auth.Resource]struct{}{}
359	for _, resourceActions := range t.Claims.Access {
360		resource := auth.Resource{
361			Type:  resourceActions.Type,
362			Class: resourceActions.Class,
363			Name:  resourceActions.Name,
364		}
365		resourceSet[resource] = struct{}{}
366	}
367
368	resources := make([]auth.Resource, 0, len(resourceSet))
369	for resource := range resourceSet {
370		resources = append(resources, resource)
371	}
372
373	return resources
374}
375
376func (t *Token) compactRaw() string {
377	return fmt.Sprintf("%s.%s", t.Raw, joseBase64UrlEncode(t.Signature))
378}
379