storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/jwt/parser.go (about)

     1  /*
     2   * MinIO Cloud Storage, (C) 2020 MinIO, Inc.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package jwt
    18  
    19  // This file is a re-implementation of the original code here with some
    20  // additional allocation tweaks reproduced using GODEBUG=allocfreetrace=1
    21  // original file https://github.com/dgrijalva/jwt-go/blob/master/parser.go
    22  // borrowed under MIT License https://github.com/dgrijalva/jwt-go/blob/master/LICENSE
    23  
    24  import (
    25  	"crypto"
    26  	"crypto/hmac"
    27  	"encoding/base64"
    28  	"encoding/json"
    29  	"fmt"
    30  	"strings"
    31  	"sync"
    32  	"time"
    33  
    34  	jwtgo "github.com/dgrijalva/jwt-go"
    35  	jsoniter "github.com/json-iterator/go"
    36  )
    37  
    38  // SigningMethodHMAC - Implements the HMAC-SHA family of signing methods signing methods
    39  // Expects key type of []byte for both signing and validation
    40  type SigningMethodHMAC struct {
    41  	Name string
    42  	Hash crypto.Hash
    43  }
    44  
    45  // Specific instances for HS256, HS384, HS512
    46  var (
    47  	SigningMethodHS256 *SigningMethodHMAC
    48  	SigningMethodHS384 *SigningMethodHMAC
    49  	SigningMethodHS512 *SigningMethodHMAC
    50  )
    51  
    52  var (
    53  	base64BufPool sync.Pool
    54  	hmacSigners   []*SigningMethodHMAC
    55  )
    56  
    57  func init() {
    58  	base64BufPool = sync.Pool{
    59  		New: func() interface{} {
    60  			buf := make([]byte, 8192)
    61  			return &buf
    62  		},
    63  	}
    64  
    65  	hmacSigners = []*SigningMethodHMAC{
    66  		{"HS256", crypto.SHA256},
    67  		{"HS384", crypto.SHA384},
    68  		{"HS512", crypto.SHA512},
    69  	}
    70  }
    71  
    72  // StandardClaims are basically standard claims with "accessKey"
    73  type StandardClaims struct {
    74  	AccessKey string `json:"accessKey,omitempty"`
    75  	jwtgo.StandardClaims
    76  }
    77  
    78  // MapClaims - implements custom unmarshaller
    79  type MapClaims struct {
    80  	AccessKey string `json:"accessKey,omitempty"`
    81  	jwtgo.MapClaims
    82  }
    83  
    84  // GetAccessKey will return the access key.
    85  // If nil an empty string will be returned.
    86  func (c *MapClaims) GetAccessKey() string {
    87  	if c == nil {
    88  		return ""
    89  	}
    90  	return c.AccessKey
    91  }
    92  
    93  // NewStandardClaims - initializes standard claims
    94  func NewStandardClaims() *StandardClaims {
    95  	return &StandardClaims{}
    96  }
    97  
    98  // SetIssuer sets issuer for these claims
    99  func (c *StandardClaims) SetIssuer(issuer string) {
   100  	c.Issuer = issuer
   101  }
   102  
   103  // SetAudience sets audience for these claims
   104  func (c *StandardClaims) SetAudience(aud string) {
   105  	c.Audience = aud
   106  }
   107  
   108  // SetExpiry sets expiry in unix epoch secs
   109  func (c *StandardClaims) SetExpiry(t time.Time) {
   110  	c.ExpiresAt = t.Unix()
   111  }
   112  
   113  // SetAccessKey sets access key as jwt subject and custom
   114  // "accessKey" field.
   115  func (c *StandardClaims) SetAccessKey(accessKey string) {
   116  	c.Subject = accessKey
   117  	c.AccessKey = accessKey
   118  }
   119  
   120  // Valid - implements https://godoc.org/github.com/dgrijalva/jwt-go#Claims compatible
   121  // claims interface, additionally validates "accessKey" fields.
   122  func (c *StandardClaims) Valid() error {
   123  	if err := c.StandardClaims.Valid(); err != nil {
   124  		return err
   125  	}
   126  
   127  	if c.AccessKey == "" && c.Subject == "" {
   128  		return jwtgo.NewValidationError("accessKey/sub missing",
   129  			jwtgo.ValidationErrorClaimsInvalid)
   130  	}
   131  
   132  	return nil
   133  }
   134  
   135  // NewMapClaims - Initializes a new map claims
   136  func NewMapClaims() *MapClaims {
   137  	return &MapClaims{MapClaims: jwtgo.MapClaims{}}
   138  }
   139  
   140  // Lookup returns the value and if the key is found.
   141  func (c *MapClaims) Lookup(key string) (value string, ok bool) {
   142  	if c == nil {
   143  		return "", false
   144  	}
   145  	var vinterface interface{}
   146  	vinterface, ok = c.MapClaims[key]
   147  	if ok {
   148  		value, ok = vinterface.(string)
   149  	}
   150  	return
   151  }
   152  
   153  // SetExpiry sets expiry in unix epoch secs
   154  func (c *MapClaims) SetExpiry(t time.Time) {
   155  	c.MapClaims["exp"] = t.Unix()
   156  }
   157  
   158  // SetAccessKey sets access key as jwt subject and custom
   159  // "accessKey" field.
   160  func (c *MapClaims) SetAccessKey(accessKey string) {
   161  	c.MapClaims["sub"] = accessKey
   162  	c.MapClaims["accessKey"] = accessKey
   163  }
   164  
   165  // Valid - implements https://godoc.org/github.com/dgrijalva/jwt-go#Claims compatible
   166  // claims interface, additionally validates "accessKey" fields.
   167  func (c *MapClaims) Valid() error {
   168  	if err := c.MapClaims.Valid(); err != nil {
   169  		return err
   170  	}
   171  
   172  	if c.AccessKey == "" {
   173  		return jwtgo.NewValidationError("accessKey/sub missing",
   174  			jwtgo.ValidationErrorClaimsInvalid)
   175  	}
   176  
   177  	return nil
   178  }
   179  
   180  // Map returns underlying low-level map claims.
   181  func (c *MapClaims) Map() map[string]interface{} {
   182  	if c == nil {
   183  		return nil
   184  	}
   185  	return c.MapClaims
   186  }
   187  
   188  // MarshalJSON marshals the MapClaims struct
   189  func (c *MapClaims) MarshalJSON() ([]byte, error) {
   190  	return json.Marshal(c.MapClaims)
   191  }
   192  
   193  // ParseWithStandardClaims - parse the token string, valid methods.
   194  func ParseWithStandardClaims(tokenStr string, claims *StandardClaims, key []byte) error {
   195  	// Key is not provided.
   196  	if key == nil {
   197  		// keyFunc was not provided, return error.
   198  		return jwtgo.NewValidationError("no key was provided.", jwtgo.ValidationErrorUnverifiable)
   199  	}
   200  
   201  	bufp := base64BufPool.Get().(*[]byte)
   202  	defer base64BufPool.Put(bufp)
   203  
   204  	signer, err := ParseUnverifiedStandardClaims(tokenStr, claims, *bufp)
   205  	if err != nil {
   206  		return err
   207  	}
   208  
   209  	i := strings.LastIndex(tokenStr, ".")
   210  	if i < 0 {
   211  		return jwtgo.ErrSignatureInvalid
   212  	}
   213  
   214  	n, err := base64Decode(tokenStr[i+1:], *bufp)
   215  	if err != nil {
   216  		return err
   217  	}
   218  
   219  	hasher := hmac.New(signer.Hash.New, key)
   220  	hasher.Write([]byte(tokenStr[:i]))
   221  	if !hmac.Equal((*bufp)[:n], hasher.Sum(nil)) {
   222  		return jwtgo.ErrSignatureInvalid
   223  	}
   224  
   225  	if claims.AccessKey == "" && claims.Subject == "" {
   226  		return jwtgo.NewValidationError("accessKey/sub missing",
   227  			jwtgo.ValidationErrorClaimsInvalid)
   228  	}
   229  
   230  	// Signature is valid, lets validate the claims for
   231  	// other fields such as expiry etc.
   232  	return claims.Valid()
   233  }
   234  
   235  // https://tools.ietf.org/html/rfc7519#page-11
   236  type jwtHeader struct {
   237  	Algorithm string `json:"alg"`
   238  	Type      string `json:"typ"`
   239  }
   240  
   241  // ParseUnverifiedStandardClaims - WARNING: Don't use this method unless you know what you're doing
   242  //
   243  // This method parses the token but doesn't validate the signature. It's only
   244  // ever useful in cases where you know the signature is valid (because it has
   245  // been checked previously in the stack) and you want to extract values from
   246  // it.
   247  func ParseUnverifiedStandardClaims(tokenString string, claims *StandardClaims, buf []byte) (*SigningMethodHMAC, error) {
   248  	if strings.Count(tokenString, ".") != 2 {
   249  		return nil, jwtgo.ErrSignatureInvalid
   250  	}
   251  
   252  	i := strings.Index(tokenString, ".")
   253  	j := strings.LastIndex(tokenString, ".")
   254  
   255  	n, err := base64Decode(tokenString[:i], buf)
   256  	if err != nil {
   257  		return nil, &jwtgo.ValidationError{Inner: err, Errors: jwtgo.ValidationErrorMalformed}
   258  	}
   259  
   260  	var header = jwtHeader{}
   261  	var json = jsoniter.ConfigCompatibleWithStandardLibrary
   262  	if err = json.Unmarshal(buf[:n], &header); err != nil {
   263  		return nil, &jwtgo.ValidationError{Inner: err, Errors: jwtgo.ValidationErrorMalformed}
   264  	}
   265  
   266  	n, err = base64Decode(tokenString[i+1:j], buf)
   267  	if err != nil {
   268  		return nil, &jwtgo.ValidationError{Inner: err, Errors: jwtgo.ValidationErrorMalformed}
   269  	}
   270  
   271  	if err = json.Unmarshal(buf[:n], claims); err != nil {
   272  		return nil, &jwtgo.ValidationError{Inner: err, Errors: jwtgo.ValidationErrorMalformed}
   273  	}
   274  
   275  	for _, signer := range hmacSigners {
   276  		if header.Algorithm == signer.Name {
   277  			return signer, nil
   278  		}
   279  	}
   280  
   281  	return nil, jwtgo.NewValidationError(fmt.Sprintf("signing method (%s) is unavailable.", header.Algorithm),
   282  		jwtgo.ValidationErrorUnverifiable)
   283  }
   284  
   285  // ParseWithClaims - parse the token string, valid methods.
   286  func ParseWithClaims(tokenStr string, claims *MapClaims, fn func(*MapClaims) ([]byte, error)) error {
   287  	// Key lookup function has to be provided.
   288  	if fn == nil {
   289  		// keyFunc was not provided, return error.
   290  		return jwtgo.NewValidationError("no Keyfunc was provided.", jwtgo.ValidationErrorUnverifiable)
   291  	}
   292  
   293  	bufp := base64BufPool.Get().(*[]byte)
   294  	defer base64BufPool.Put(bufp)
   295  
   296  	signer, err := ParseUnverifiedMapClaims(tokenStr, claims, *bufp)
   297  	if err != nil {
   298  		return err
   299  	}
   300  
   301  	i := strings.LastIndex(tokenStr, ".")
   302  	if i < 0 {
   303  		return jwtgo.ErrSignatureInvalid
   304  	}
   305  
   306  	n, err := base64Decode(tokenStr[i+1:], *bufp)
   307  	if err != nil {
   308  		return err
   309  	}
   310  
   311  	var ok bool
   312  	claims.AccessKey, ok = claims.Lookup("accessKey")
   313  	if !ok {
   314  		claims.AccessKey, ok = claims.Lookup("sub")
   315  		if !ok {
   316  			return jwtgo.NewValidationError("accessKey/sub missing",
   317  				jwtgo.ValidationErrorClaimsInvalid)
   318  		}
   319  	}
   320  
   321  	// Lookup key from claims, claims may not be valid and may return
   322  	// invalid key which is okay as the signature verification will fail.
   323  	key, err := fn(claims)
   324  	if err != nil {
   325  		return err
   326  	}
   327  
   328  	hasher := hmac.New(signer.Hash.New, key)
   329  	hasher.Write([]byte(tokenStr[:i]))
   330  	if !hmac.Equal((*bufp)[:n], hasher.Sum(nil)) {
   331  		return jwtgo.ErrSignatureInvalid
   332  	}
   333  
   334  	// Signature is valid, lets validate the claims for
   335  	// other fields such as expiry etc.
   336  	return claims.Valid()
   337  }
   338  
   339  // base64Decode returns the bytes represented by the base64 string s.
   340  func base64Decode(s string, buf []byte) (int, error) {
   341  	return base64.RawURLEncoding.Decode(buf, []byte(s))
   342  }
   343  
   344  // ParseUnverifiedMapClaims - WARNING: Don't use this method unless you know what you're doing
   345  //
   346  // This method parses the token but doesn't validate the signature. It's only
   347  // ever useful in cases where you know the signature is valid (because it has
   348  // been checked previously in the stack) and you want to extract values from
   349  // it.
   350  func ParseUnverifiedMapClaims(tokenString string, claims *MapClaims, buf []byte) (*SigningMethodHMAC, error) {
   351  	if strings.Count(tokenString, ".") != 2 {
   352  		return nil, jwtgo.ErrSignatureInvalid
   353  	}
   354  
   355  	i := strings.Index(tokenString, ".")
   356  	j := strings.LastIndex(tokenString, ".")
   357  
   358  	n, err := base64Decode(tokenString[:i], buf)
   359  	if err != nil {
   360  		return nil, &jwtgo.ValidationError{Inner: err, Errors: jwtgo.ValidationErrorMalformed}
   361  	}
   362  
   363  	var header = jwtHeader{}
   364  	var json = jsoniter.ConfigCompatibleWithStandardLibrary
   365  	if err = json.Unmarshal(buf[:n], &header); err != nil {
   366  		return nil, &jwtgo.ValidationError{Inner: err, Errors: jwtgo.ValidationErrorMalformed}
   367  	}
   368  
   369  	n, err = base64Decode(tokenString[i+1:j], buf)
   370  	if err != nil {
   371  		return nil, &jwtgo.ValidationError{Inner: err, Errors: jwtgo.ValidationErrorMalformed}
   372  	}
   373  
   374  	if err = json.Unmarshal(buf[:n], &claims.MapClaims); err != nil {
   375  		return nil, &jwtgo.ValidationError{Inner: err, Errors: jwtgo.ValidationErrorMalformed}
   376  	}
   377  
   378  	for _, signer := range hmacSigners {
   379  		if header.Algorithm == signer.Name {
   380  			return signer, nil
   381  		}
   382  	}
   383  
   384  	return nil, jwtgo.NewValidationError(fmt.Sprintf("signing method (%s) is unavailable.", header.Algorithm),
   385  		jwtgo.ValidationErrorUnverifiable)
   386  }