Files
narco/jwt/signer.go
Alexandre Tuleu 00c5853b45 Adds JWS encode and decode
It also adds interfaces Signer and HS256 and RS256 implementation.
2015-08-18 16:11:56 +02:00

165 lines
3.8 KiB
Go

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
}