github.com/lusis/distribution@v2.0.1+incompatible/registry/auth/token/token.go (about)

     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  	log "github.com/Sirupsen/logrus"
    14  	"github.com/docker/libtrust"
    15  
    16  	"github.com/docker/distribution/registry/auth"
    17  )
    18  
    19  const (
    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  )
    24  
    25  // Errors used by token parsing and verification.
    26  var (
    27  	ErrMalformedToken = errors.New("malformed token")
    28  	ErrInvalidToken   = errors.New("invalid token")
    29  )
    30  
    31  // ResourceActions stores allowed actions on a named and typed resource.
    32  type ResourceActions struct {
    33  	Type    string   `json:"type"`
    34  	Name    string   `json:"name"`
    35  	Actions []string `json:"actions"`
    36  }
    37  
    38  // ClaimSet describes the main section of a JSON Web Token.
    39  type ClaimSet struct {
    40  	// Public claims
    41  	Issuer     string `json:"iss"`
    42  	Subject    string `json:"sub"`
    43  	Audience   string `json:"aud"`
    44  	Expiration int64  `json:"exp"`
    45  	NotBefore  int64  `json:"nbf"`
    46  	IssuedAt   int64  `json:"iat"`
    47  	JWTID      string `json:"jti"`
    48  
    49  	// Private claims
    50  	Access []*ResourceActions `json:"access"`
    51  }
    52  
    53  // Header describes the header section of a JSON Web Token.
    54  type Header struct {
    55  	Type       string          `json:"typ"`
    56  	SigningAlg string          `json:"alg"`
    57  	KeyID      string          `json:"kid,omitempty"`
    58  	X5c        []string        `json:"x5c,omitempty"`
    59  	RawJWK     json.RawMessage `json:"jwk,omitempty"`
    60  }
    61  
    62  // Token describes a JSON Web Token.
    63  type Token struct {
    64  	Raw       string
    65  	Header    *Header
    66  	Claims    *ClaimSet
    67  	Signature []byte
    68  }
    69  
    70  // VerifyOptions is used to specify
    71  // options when verifying a JSON Web Token.
    72  type VerifyOptions struct {
    73  	TrustedIssuers    []string
    74  	AcceptedAudiences []string
    75  	Roots             *x509.CertPool
    76  	TrustedKeys       map[string]libtrust.PublicKey
    77  }
    78  
    79  // NewToken parses the given raw token string
    80  // and constructs an unverified JSON Web Token.
    81  func NewToken(rawToken string) (*Token, error) {
    82  	parts := strings.Split(rawToken, TokenSeparator)
    83  	if len(parts) != 3 {
    84  		return nil, ErrMalformedToken
    85  	}
    86  
    87  	var (
    88  		rawHeader, rawClaims   = parts[0], parts[1]
    89  		headerJSON, claimsJSON []byte
    90  		err                    error
    91  	)
    92  
    93  	defer func() {
    94  		if err != nil {
    95  			log.Errorf("error while unmarshalling raw token: %s", err)
    96  		}
    97  	}()
    98  
    99  	if headerJSON, err = joseBase64UrlDecode(rawHeader); err != nil {
   100  		err = fmt.Errorf("unable to decode header: %s", err)
   101  		return nil, ErrMalformedToken
   102  	}
   103  
   104  	if claimsJSON, err = joseBase64UrlDecode(rawClaims); err != nil {
   105  		err = fmt.Errorf("unable to decode claims: %s", err)
   106  		return nil, ErrMalformedToken
   107  	}
   108  
   109  	token := new(Token)
   110  	token.Header = new(Header)
   111  	token.Claims = new(ClaimSet)
   112  
   113  	token.Raw = strings.Join(parts[:2], TokenSeparator)
   114  	if token.Signature, err = joseBase64UrlDecode(parts[2]); err != nil {
   115  		err = fmt.Errorf("unable to decode signature: %s", err)
   116  		return nil, ErrMalformedToken
   117  	}
   118  
   119  	if err = json.Unmarshal(headerJSON, token.Header); err != nil {
   120  		return nil, ErrMalformedToken
   121  	}
   122  
   123  	if err = json.Unmarshal(claimsJSON, token.Claims); err != nil {
   124  		return nil, ErrMalformedToken
   125  	}
   126  
   127  	return token, nil
   128  }
   129  
   130  // Verify attempts to verify this token using the given options.
   131  // Returns a nil error if the token is valid.
   132  func (t *Token) Verify(verifyOpts VerifyOptions) error {
   133  	// Verify that the Issuer claim is a trusted authority.
   134  	if !contains(verifyOpts.TrustedIssuers, t.Claims.Issuer) {
   135  		log.Errorf("token from untrusted issuer: %q", t.Claims.Issuer)
   136  		return ErrInvalidToken
   137  	}
   138  
   139  	// Verify that the Audience claim is allowed.
   140  	if !contains(verifyOpts.AcceptedAudiences, t.Claims.Audience) {
   141  		log.Errorf("token intended for another audience: %q", t.Claims.Audience)
   142  		return ErrInvalidToken
   143  	}
   144  
   145  	// Verify that the token is currently usable and not expired.
   146  	currentUnixTime := time.Now().Unix()
   147  	if !(t.Claims.NotBefore <= currentUnixTime && currentUnixTime <= t.Claims.Expiration) {
   148  		log.Errorf("token not to be used before %d or after %d - currently %d", t.Claims.NotBefore, t.Claims.Expiration, currentUnixTime)
   149  		return ErrInvalidToken
   150  	}
   151  
   152  	// Verify the token signature.
   153  	if len(t.Signature) == 0 {
   154  		log.Error("token has no signature")
   155  		return ErrInvalidToken
   156  	}
   157  
   158  	// Verify that the signing key is trusted.
   159  	signingKey, err := t.VerifySigningKey(verifyOpts)
   160  	if err != nil {
   161  		log.Error(err)
   162  		return ErrInvalidToken
   163  	}
   164  
   165  	// Finally, verify the signature of the token using the key which signed it.
   166  	if err := signingKey.Verify(strings.NewReader(t.Raw), t.Header.SigningAlg, t.Signature); err != nil {
   167  		log.Errorf("unable to verify token signature: %s", err)
   168  		return ErrInvalidToken
   169  	}
   170  
   171  	return nil
   172  }
   173  
   174  // VerifySigningKey attempts to get the key which was used to sign this token.
   175  // The token header should contain either of these 3 fields:
   176  //      `x5c` - The x509 certificate chain for the signing key. Needs to be
   177  //              verified.
   178  //      `jwk` - The JSON Web Key representation of the signing key.
   179  //              May contain its own `x5c` field which needs to be verified.
   180  //      `kid` - The unique identifier for the key. This library interprets it
   181  //              as a libtrust fingerprint. The key itself can be looked up in
   182  //              the trustedKeys field of the given verify options.
   183  // Each of these methods are tried in that order of preference until the
   184  // signing key is found or an error is returned.
   185  func (t *Token) VerifySigningKey(verifyOpts VerifyOptions) (signingKey libtrust.PublicKey, err error) {
   186  	// First attempt to get an x509 certificate chain from the header.
   187  	var (
   188  		x5c    = t.Header.X5c
   189  		rawJWK = t.Header.RawJWK
   190  		keyID  = t.Header.KeyID
   191  	)
   192  
   193  	switch {
   194  	case len(x5c) > 0:
   195  		signingKey, err = parseAndVerifyCertChain(x5c, verifyOpts.Roots)
   196  	case len(rawJWK) > 0:
   197  		signingKey, err = parseAndVerifyRawJWK(rawJWK, verifyOpts)
   198  	case len(keyID) > 0:
   199  		signingKey = verifyOpts.TrustedKeys[keyID]
   200  		if signingKey == nil {
   201  			err = fmt.Errorf("token signed by untrusted key with ID: %q", keyID)
   202  		}
   203  	default:
   204  		err = errors.New("unable to get token signing key")
   205  	}
   206  
   207  	return
   208  }
   209  
   210  func parseAndVerifyCertChain(x5c []string, roots *x509.CertPool) (leafKey libtrust.PublicKey, err error) {
   211  	if len(x5c) == 0 {
   212  		return nil, errors.New("empty x509 certificate chain")
   213  	}
   214  
   215  	// Ensure the first element is encoded correctly.
   216  	leafCertDer, err := base64.StdEncoding.DecodeString(x5c[0])
   217  	if err != nil {
   218  		return nil, fmt.Errorf("unable to decode leaf certificate: %s", err)
   219  	}
   220  
   221  	// And that it is a valid x509 certificate.
   222  	leafCert, err := x509.ParseCertificate(leafCertDer)
   223  	if err != nil {
   224  		return nil, fmt.Errorf("unable to parse leaf certificate: %s", err)
   225  	}
   226  
   227  	// The rest of the certificate chain are intermediate certificates.
   228  	intermediates := x509.NewCertPool()
   229  	for i := 1; i < len(x5c); i++ {
   230  		intermediateCertDer, err := base64.StdEncoding.DecodeString(x5c[i])
   231  		if err != nil {
   232  			return nil, fmt.Errorf("unable to decode intermediate certificate: %s", err)
   233  		}
   234  
   235  		intermediateCert, err := x509.ParseCertificate(intermediateCertDer)
   236  		if err != nil {
   237  			return nil, fmt.Errorf("unable to parse intermediate certificate: %s", err)
   238  		}
   239  
   240  		intermediates.AddCert(intermediateCert)
   241  	}
   242  
   243  	verifyOpts := x509.VerifyOptions{
   244  		Intermediates: intermediates,
   245  		Roots:         roots,
   246  		KeyUsages:     []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
   247  	}
   248  
   249  	// TODO: this call returns certificate chains which we ignore for now, but
   250  	// we should check them for revocations if we have the ability later.
   251  	if _, err = leafCert.Verify(verifyOpts); err != nil {
   252  		return nil, fmt.Errorf("unable to verify certificate chain: %s", err)
   253  	}
   254  
   255  	// Get the public key from the leaf certificate.
   256  	leafCryptoKey, ok := leafCert.PublicKey.(crypto.PublicKey)
   257  	if !ok {
   258  		return nil, errors.New("unable to get leaf cert public key value")
   259  	}
   260  
   261  	leafKey, err = libtrust.FromCryptoPublicKey(leafCryptoKey)
   262  	if err != nil {
   263  		return nil, fmt.Errorf("unable to make libtrust public key from leaf certificate: %s", err)
   264  	}
   265  
   266  	return
   267  }
   268  
   269  func parseAndVerifyRawJWK(rawJWK json.RawMessage, verifyOpts VerifyOptions) (pubKey libtrust.PublicKey, err error) {
   270  	pubKey, err = libtrust.UnmarshalPublicKeyJWK([]byte(rawJWK))
   271  	if err != nil {
   272  		return nil, fmt.Errorf("unable to decode raw JWK value: %s", err)
   273  	}
   274  
   275  	// Check to see if the key includes a certificate chain.
   276  	x5cVal, ok := pubKey.GetExtendedField("x5c").([]interface{})
   277  	if !ok {
   278  		// The JWK should be one of the trusted root keys.
   279  		if _, trusted := verifyOpts.TrustedKeys[pubKey.KeyID()]; !trusted {
   280  			return nil, errors.New("untrusted JWK with no certificate chain")
   281  		}
   282  
   283  		// The JWK is one of the trusted keys.
   284  		return
   285  	}
   286  
   287  	// Ensure each item in the chain is of the correct type.
   288  	x5c := make([]string, len(x5cVal))
   289  	for i, val := range x5cVal {
   290  		certString, ok := val.(string)
   291  		if !ok || len(certString) == 0 {
   292  			return nil, errors.New("malformed certificate chain")
   293  		}
   294  		x5c[i] = certString
   295  	}
   296  
   297  	// Ensure that the x509 certificate chain can
   298  	// be verified up to one of our trusted roots.
   299  	leafKey, err := parseAndVerifyCertChain(x5c, verifyOpts.Roots)
   300  	if err != nil {
   301  		return nil, fmt.Errorf("could not verify JWK certificate chain: %s", err)
   302  	}
   303  
   304  	// Verify that the public key in the leaf cert *is* the signing key.
   305  	if pubKey.KeyID() != leafKey.KeyID() {
   306  		return nil, errors.New("leaf certificate public key ID does not match JWK key ID")
   307  	}
   308  
   309  	return
   310  }
   311  
   312  // accessSet returns a set of actions available for the resource
   313  // actions listed in the `access` section of this token.
   314  func (t *Token) accessSet() accessSet {
   315  	if t.Claims == nil {
   316  		return nil
   317  	}
   318  
   319  	accessSet := make(accessSet, len(t.Claims.Access))
   320  
   321  	for _, resourceActions := range t.Claims.Access {
   322  		resource := auth.Resource{
   323  			Type: resourceActions.Type,
   324  			Name: resourceActions.Name,
   325  		}
   326  
   327  		set, exists := accessSet[resource]
   328  		if !exists {
   329  			set = newActionSet()
   330  			accessSet[resource] = set
   331  		}
   332  
   333  		for _, action := range resourceActions.Actions {
   334  			set.add(action)
   335  		}
   336  	}
   337  
   338  	return accessSet
   339  }
   340  
   341  func (t *Token) compactRaw() string {
   342  	return fmt.Sprintf("%s.%s", t.Raw, joseBase64UrlEncode(t.Signature))
   343  }