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 }