blob: f977381240e394435fcc524f66a5f35cf5890582 [file] [log] [blame]
khenaidooab1f7bd2019-11-14 14:00:27 -05001package 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}