hmac.go raw

   1  package jwt
   2  
   3  import (
   4  	"crypto"
   5  	"crypto/hmac"
   6  	"errors"
   7  )
   8  
   9  // SigningMethodHMAC implements the HMAC-SHA family of signing methods.
  10  // Expects key type of []byte for both signing and validation
  11  type SigningMethodHMAC struct {
  12  	Name string
  13  	Hash crypto.Hash
  14  }
  15  
  16  // Specific instances for HS256 and company
  17  var (
  18  	SigningMethodHS256  *SigningMethodHMAC
  19  	SigningMethodHS384  *SigningMethodHMAC
  20  	SigningMethodHS512  *SigningMethodHMAC
  21  	ErrSignatureInvalid = errors.New("signature is invalid")
  22  )
  23  
  24  func init() {
  25  	// HS256
  26  	SigningMethodHS256 = &SigningMethodHMAC{"HS256", crypto.SHA256}
  27  	RegisterSigningMethod(SigningMethodHS256.Alg(), func() SigningMethod {
  28  		return SigningMethodHS256
  29  	})
  30  
  31  	// HS384
  32  	SigningMethodHS384 = &SigningMethodHMAC{"HS384", crypto.SHA384}
  33  	RegisterSigningMethod(SigningMethodHS384.Alg(), func() SigningMethod {
  34  		return SigningMethodHS384
  35  	})
  36  
  37  	// HS512
  38  	SigningMethodHS512 = &SigningMethodHMAC{"HS512", crypto.SHA512}
  39  	RegisterSigningMethod(SigningMethodHS512.Alg(), func() SigningMethod {
  40  		return SigningMethodHS512
  41  	})
  42  }
  43  
  44  func (m *SigningMethodHMAC) Alg() string {
  45  	return m.Name
  46  }
  47  
  48  // Verify implements token verification for the SigningMethod. Returns nil if the signature is valid.
  49  func (m *SigningMethodHMAC) Verify(signingString, signature string, key interface{}) error {
  50  	// Verify the key is the right type
  51  	keyBytes, ok := key.([]byte)
  52  	if !ok {
  53  		return ErrInvalidKeyType
  54  	}
  55  
  56  	// Decode signature, for comparison
  57  	sig, err := DecodeSegment(signature)
  58  	if err != nil {
  59  		return err
  60  	}
  61  
  62  	// Can we use the specified hashing method?
  63  	if !m.Hash.Available() {
  64  		return ErrHashUnavailable
  65  	}
  66  
  67  	// This signing method is symmetric, so we validate the signature
  68  	// by reproducing the signature from the signing string and key, then
  69  	// comparing that against the provided signature.
  70  	hasher := hmac.New(m.Hash.New, keyBytes)
  71  	hasher.Write([]byte(signingString))
  72  	if !hmac.Equal(sig, hasher.Sum(nil)) {
  73  		return ErrSignatureInvalid
  74  	}
  75  
  76  	// No validation errors.  Signature is good.
  77  	return nil
  78  }
  79  
  80  // Sign implements token signing for the SigningMethod.
  81  // Key must be []byte
  82  func (m *SigningMethodHMAC) Sign(signingString string, key interface{}) (string, error) {
  83  	if keyBytes, ok := key.([]byte); ok {
  84  		if !m.Hash.Available() {
  85  			return "", ErrHashUnavailable
  86  		}
  87  
  88  		hasher := hmac.New(m.Hash.New, keyBytes)
  89  		hasher.Write([]byte(signingString))
  90  
  91  		return EncodeSegment(hasher.Sum(nil)), nil
  92  	}
  93  
  94  	return "", ErrInvalidKeyType
  95  }
  96