package jwt

import (
	
	
	
)

// SigningMethodHMAC implements the HMAC-SHA family of signing methods.
// Expects key type of []byte for both signing and validation
type SigningMethodHMAC struct {
	Name string
	Hash crypto.Hash
}

// Specific instances for HS256 and company
var (
	SigningMethodHS256  *SigningMethodHMAC
	SigningMethodHS384  *SigningMethodHMAC
	SigningMethodHS512  *SigningMethodHMAC
	ErrSignatureInvalid = errors.New("signature is invalid")
)

func init() {
	// HS256
	SigningMethodHS256 = &SigningMethodHMAC{"HS256", crypto.SHA256}
	RegisterSigningMethod(SigningMethodHS256.Alg(), func() SigningMethod {
		return SigningMethodHS256
	})

	// HS384
	SigningMethodHS384 = &SigningMethodHMAC{"HS384", crypto.SHA384}
	RegisterSigningMethod(SigningMethodHS384.Alg(), func() SigningMethod {
		return SigningMethodHS384
	})

	// HS512
	SigningMethodHS512 = &SigningMethodHMAC{"HS512", crypto.SHA512}
	RegisterSigningMethod(SigningMethodHS512.Alg(), func() SigningMethod {
		return SigningMethodHS512
	})
}

func ( *SigningMethodHMAC) () string {
	return .Name
}

// Verify implements token verification for the SigningMethod. Returns nil if the signature is valid.
func ( *SigningMethodHMAC) (,  string,  interface{}) error {
	// Verify the key is the right type
	,  := .([]byte)
	if ! {
		return ErrInvalidKeyType
	}

	// Decode signature, for comparison
	,  := DecodeSegment()
	if  != nil {
		return 
	}

	// Can we use the specified hashing method?
	if !.Hash.Available() {
		return ErrHashUnavailable
	}

	// This signing method is symmetric, so we validate the signature
	// by reproducing the signature from the signing string and key, then
	// comparing that against the provided signature.
	 := hmac.New(.Hash.New, )
	.Write([]byte())
	if !hmac.Equal(, .Sum(nil)) {
		return ErrSignatureInvalid
	}

	// No validation errors.  Signature is good.
	return nil
}

// Sign implements token signing for the SigningMethod.
// Key must be []byte
func ( *SigningMethodHMAC) ( string,  interface{}) (string, error) {
	if ,  := .([]byte);  {
		if !.Hash.Available() {
			return "", ErrHashUnavailable
		}

		 := hmac.New(.Hash.New, )
		.Write([]byte())

		return EncodeSegment(.Sum(nil)), nil
	}

	return "", ErrInvalidKeyType
}