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
  49  // the signature is valid. Key must be []byte.
  50  //
  51  // Note it is not advised to provide a []byte which was converted from a 'human
  52  // readable' string using a subset of ASCII characters. To maximize entropy, you
  53  // should ideally be providing a []byte key which was produced from a
  54  // cryptographically random source, e.g. crypto/rand. Additional information
  55  // about this, and why we intentionally are not supporting string as a key can
  56  // be found on our usage guide
  57  // https://golang-jwt.github.io/jwt/usage/signing_methods/#signing-methods-and-key-types.
  58  func (m *SigningMethodHMAC) Verify(signingString string, sig []byte, key any) error {
  59  	// Verify the key is the right type
  60  	keyBytes, ok := key.([]byte)
  61  	if !ok {
  62  		return newError("HMAC verify expects []byte", ErrInvalidKeyType)
  63  	}
  64  
  65  	// Can we use the specified hashing method?
  66  	if !m.Hash.Available() {
  67  		return ErrHashUnavailable
  68  	}
  69  
  70  	// This signing method is symmetric, so we validate the signature
  71  	// by reproducing the signature from the signing string and key, then
  72  	// comparing that against the provided signature.
  73  	hasher := hmac.New(m.Hash.New, keyBytes)
  74  	hasher.Write([]byte(signingString))
  75  	if !hmac.Equal(sig, hasher.Sum(nil)) {
  76  		return ErrSignatureInvalid
  77  	}
  78  
  79  	// No validation errors.  Signature is good.
  80  	return nil
  81  }
  82  
  83  // Sign implements token signing for the SigningMethod. Key must be []byte.
  84  //
  85  // Note it is not advised to provide a []byte which was converted from a 'human
  86  // readable' string using a subset of ASCII characters. To maximize entropy, you
  87  // should ideally be providing a []byte key which was produced from a
  88  // cryptographically random source, e.g. crypto/rand. Additional information
  89  // about this, and why we intentionally are not supporting string as a key can
  90  // be found on our usage guide https://golang-jwt.github.io/jwt/usage/signing_methods/.
  91  func (m *SigningMethodHMAC) Sign(signingString string, key any) ([]byte, error) {
  92  	if keyBytes, ok := key.([]byte); ok {
  93  		if !m.Hash.Available() {
  94  			return nil, ErrHashUnavailable
  95  		}
  96  
  97  		hasher := hmac.New(m.Hash.New, keyBytes)
  98  		hasher.Write([]byte(signingString))
  99  
 100  		return hasher.Sum(nil), nil
 101  	}
 102  
 103  	return nil, newError("HMAC sign expects []byte", ErrInvalidKeyType)
 104  }
 105