1package jwt
2
3import (
4	"crypto"
5	"crypto/ecdsa"
6	"crypto/rand"
7	"errors"
8	"math/big"
9)
10
11var (
12	// Sadly this is missing from crypto/ecdsa compared to crypto/rsa
13	ErrECDSAVerification = errors.New("crypto/ecdsa: verification error")
14)
15
16// Implements the ECDSA family of signing methods signing methods
17// Expects *ecdsa.PrivateKey for signing and *ecdsa.PublicKey for verification
18type SigningMethodECDSA struct {
19	Name      string
20	Hash      crypto.Hash
21	KeySize   int
22	CurveBits int
23}
24
25// Specific instances for EC256 and company
26var (
27	SigningMethodES256 *SigningMethodECDSA
28	SigningMethodES384 *SigningMethodECDSA
29	SigningMethodES512 *SigningMethodECDSA
30)
31
32func init() {
33	// ES256
34	SigningMethodES256 = &SigningMethodECDSA{"ES256", crypto.SHA256, 32, 256}
35	RegisterSigningMethod(SigningMethodES256.Alg(), func() SigningMethod {
36		return SigningMethodES256
37	})
38
39	// ES384
40	SigningMethodES384 = &SigningMethodECDSA{"ES384", crypto.SHA384, 48, 384}
41	RegisterSigningMethod(SigningMethodES384.Alg(), func() SigningMethod {
42		return SigningMethodES384
43	})
44
45	// ES512
46	SigningMethodES512 = &SigningMethodECDSA{"ES512", crypto.SHA512, 66, 521}
47	RegisterSigningMethod(SigningMethodES512.Alg(), func() SigningMethod {
48		return SigningMethodES512
49	})
50}
51
52func (m *SigningMethodECDSA) Alg() string {
53	return m.Name
54}
55
56// Implements the Verify method from SigningMethod
57// For this verify method, key must be an ecdsa.PublicKey struct
58func (m *SigningMethodECDSA) Verify(signingString, signature string, key interface{}) error {
59	var err error
60
61	// Decode the signature
62	var sig []byte
63	if sig, err = DecodeSegment(signature); err != nil {
64		return err
65	}
66
67	// Get the key
68	var ecdsaKey *ecdsa.PublicKey
69	switch k := key.(type) {
70	case *ecdsa.PublicKey:
71		ecdsaKey = k
72	default:
73		return ErrInvalidKeyType
74	}
75
76	if len(sig) != 2*m.KeySize {
77		return ErrECDSAVerification
78	}
79
80	r := big.NewInt(0).SetBytes(sig[:m.KeySize])
81	s := big.NewInt(0).SetBytes(sig[m.KeySize:])
82
83	// Create hasher
84	if !m.Hash.Available() {
85		return ErrHashUnavailable
86	}
87	hasher := m.Hash.New()
88	hasher.Write([]byte(signingString))
89
90	// Verify the signature
91	if verifystatus := ecdsa.Verify(ecdsaKey, hasher.Sum(nil), r, s); verifystatus == true {
92		return nil
93	} else {
94		return ErrECDSAVerification
95	}
96}
97
98// Implements the Sign method from SigningMethod
99// For this signing method, key must be an ecdsa.PrivateKey struct
100func (m *SigningMethodECDSA) Sign(signingString string, key interface{}) (string, error) {
101	// Get the key
102	var ecdsaKey *ecdsa.PrivateKey
103	switch k := key.(type) {
104	case *ecdsa.PrivateKey:
105		ecdsaKey = k
106	default:
107		return "", ErrInvalidKeyType
108	}
109
110	// Create the hasher
111	if !m.Hash.Available() {
112		return "", ErrHashUnavailable
113	}
114
115	hasher := m.Hash.New()
116	hasher.Write([]byte(signingString))
117
118	// Sign the string and return r, s
119	if r, s, err := ecdsa.Sign(rand.Reader, ecdsaKey, hasher.Sum(nil)); err == nil {
120		curveBits := ecdsaKey.Curve.Params().BitSize
121
122		if m.CurveBits != curveBits {
123			return "", ErrInvalidKey
124		}
125
126		keyBytes := curveBits / 8
127		if curveBits%8 > 0 {
128			keyBytes += 1
129		}
130
131		// We serialize the outpus (r and s) into big-endian byte arrays and pad
132		// them with zeros on the left to make sure the sizes work out. Both arrays
133		// must be keyBytes long, and the output must be 2*keyBytes long.
134		rBytes := r.Bytes()
135		rBytesPadded := make([]byte, keyBytes)
136		copy(rBytesPadded[keyBytes-len(rBytes):], rBytes)
137
138		sBytes := s.Bytes()
139		sBytesPadded := make([]byte, keyBytes)
140		copy(sBytesPadded[keyBytes-len(sBytes):], sBytes)
141
142		out := append(rBytesPadded, sBytesPadded...)
143
144		return EncodeSegment(out), nil
145	} else {
146		return "", err
147	}
148}
149