package jwt import ( "crypto" "crypto/hmac" "crypto/rand" "crypto/rsa" "fmt" ) type hashLinkError struct { name string } func (e *hashLinkError) Error() string { return fmt.Sprintf("jws: Hash %s is not linked into binary", e.name) } // A Signer is able to crypto-sign a JWS object. type Signer interface { // Sign returns the signature of a binary payload. It should // returns the signature in binary format Sign([]byte) ([]byte, error) // Verify check if the signed binary paylod correspond to the // provided binary siganture. It assumes a binary format // signature. Verify(signed, signature []byte) error // Algorithm is returning a string idnetifying the algorithm used // as defined in RFC 7518. Algorithm() string } // A HMACSigner is a Signer using HMAC based algorithm type HMACSigner struct { name string hash crypto.Hash key []byte } // NewHMAC256Signer returs a HMACSigner using a SHA256 hash. func NewHMAC256Signer(key []byte) Signer { return &HMACSigner{ name: "HS256", hash: crypto.SHA256, key: key, } } // NewHMAC384Signer returs a HMACSigner using a SHA384 hash. func NewHMAC384Signer(key []byte) Signer { return &HMACSigner{ name: "HS256", hash: crypto.SHA384, key: key, } } // NewHMAC512Signer returs a HMACSigner using a SHA512 hash. func NewHMAC512Signer(key []byte) Signer { return &HMACSigner{ name: "HS256", hash: crypto.SHA384, key: key, } } // Algorithm return 'HSXXX' where XXX is either 256|384|512 depending // on the SHA Hash used. func (s *HMACSigner) Algorithm() string { return s.name } // Sign compute the binary signature for data. func (s *HMACSigner) Sign(data []byte) ([]byte, error) { if s.hash.Available() == false { return nil, &hashLinkError{s.name} } hasher := hmac.New(s.hash.New, s.key) hasher.Write(data) return hasher.Sum(nil), nil } // Verify returns an error if the binary provided signature does not // correspond to signed. It return nil if the signature is correct. func (s *HMACSigner) Verify(signed, signature []byte) error { expected, err := s.Sign(signed) if err != nil { return err } if hmac.Equal(signature, expected) == false { return fmt.Errorf("jws: Invalid %s signature", s.name) } return nil } // A RSASigner is a Signer using a PKS 1v15 algorithm. type RSASigner struct { name string hash crypto.Hash key *rsa.PrivateKey } // NewRSA256Signer returns a RSASigner using a SHA 256 hash. func NewRSA256Signer(key *rsa.PrivateKey) Signer { return &RSASigner{ name: "RS256", hash: crypto.SHA256, key: key, } } // NewRSA384Signer returns a RSASigner using a SHA 384 hash. func NewRSA384Signer(key *rsa.PrivateKey) Signer { return &RSASigner{ name: "RS384", hash: crypto.SHA384, key: key, } } // NewRSA512Signer returns a RSASigner using a SHA 512 hash. func NewRSA512Signer(key *rsa.PrivateKey) Signer { return &RSASigner{ name: "RS512", hash: crypto.SHA512, key: key, } } // Sign compute the binary signature for data. func (s *RSASigner) Sign(data []byte) ([]byte, error) { if s.hash.Available() == false { return nil, &hashLinkError{s.name} } hasher := s.hash.New() hasher.Write(data) res, err := rsa.SignPKCS1v15(rand.Reader, s.key, s.hash, hasher.Sum(nil)) if err != nil { return nil, err } return res, nil } // Verify returns an error if the binary provided signature does not // correspond to signed. It return nil if the signature is correct. func (s *RSASigner) Verify(signed, signature []byte) error { if s.hash.Available() == false { return &hashLinkError{s.name} } hasher := s.hash.New() hasher.Write(signed) return rsa.VerifyPKCS1v15(s.key.Public().(*rsa.PublicKey), s.hash, hasher.Sum(nil), signature) } // Algorithm return 'RSXXX' where XXX is either 256|384|512 depending // on the SHA Hash used. func (s *RSASigner) Algorithm() string { return s.name }