forked from dgrijalva/jwt-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ecdsa.go
173 lines (142 loc) · 4.66 KB
/
ecdsa.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
package jwt
import (
"crypto"
"crypto/ecdsa"
"crypto/rand"
"encoding/asn1"
"fmt"
"math/big"
)
// SigningMethodECDSA implements the ECDSA family of signing methods signing methods
// Expects *ecdsa.PrivateKey for signing and *ecdsa.PublicKey for verification
type SigningMethodECDSA struct {
Name string
Hash crypto.Hash
KeySize int
CurveBits int
}
// Mirrors the struct from crypto/ecdsa, we expect ecdsa.PrivateKey.Sign function to return this struct asn1 encoded
type ecdsaSignature struct {
R, S *big.Int
}
// Specific instances for EC256 and company
var (
SigningMethodES256 *SigningMethodECDSA
SigningMethodES384 *SigningMethodECDSA
SigningMethodES512 *SigningMethodECDSA
)
func init() {
// ES256
SigningMethodES256 = &SigningMethodECDSA{"ES256", crypto.SHA256, 32, 256}
RegisterSigningMethod(SigningMethodES256.Alg(), func() SigningMethod {
return SigningMethodES256
})
// ES384
SigningMethodES384 = &SigningMethodECDSA{"ES384", crypto.SHA384, 48, 384}
RegisterSigningMethod(SigningMethodES384.Alg(), func() SigningMethod {
return SigningMethodES384
})
// ES512
SigningMethodES512 = &SigningMethodECDSA{"ES512", crypto.SHA512, 66, 521}
RegisterSigningMethod(SigningMethodES512.Alg(), func() SigningMethod {
return SigningMethodES512
})
}
// Alg implements SigningMethod
func (m *SigningMethodECDSA) Alg() string {
return m.Name
}
// Verify implements the Verify method from SigningMethod
// For this verify method, key must be an ecdsa.PublicKey struct
func (m *SigningMethodECDSA) Verify(signingString, signature string, key interface{}) error {
var err error
// Decode the signature
var sig []byte
if sig, err = DecodeSegment(signature); err != nil {
return err
}
// Get the key
var ecdsaKey *ecdsa.PublicKey
var ok bool
switch k := key.(type) {
case *ecdsa.PublicKey:
ecdsaKey = k
case crypto.Signer:
pub := k.Public()
if ecdsaKey, ok = pub.(*ecdsa.PublicKey); !ok {
return &InvalidKeyError{Message: fmt.Sprintf("crypto.Signer returned an unexpected public key type: %T", pub)}
}
default:
return NewInvalidKeyTypeError("*ecdsa.PublicKey or crypto.Signer", key)
}
if len(sig) != 2*m.KeySize {
return &UnverfiableTokenError{Message: "signature length is invalid"}
}
r := big.NewInt(0).SetBytes(sig[:m.KeySize])
s := big.NewInt(0).SetBytes(sig[m.KeySize:])
// Create hasher
if !m.Hash.Available() {
return ErrHashUnavailable
}
hasher := m.Hash.New()
hasher.Write([]byte(signingString))
// Verify the signature
if verifystatus := ecdsa.Verify(ecdsaKey, hasher.Sum(nil), r, s); verifystatus == true {
return nil
}
return new(InvalidSignatureError)
}
// Sign implements the Sign method from SigningMethod
// For this signing method, key must be an ecdsa.PrivateKey struct
func (m *SigningMethodECDSA) Sign(signingString string, key interface{}) (string, error) {
var signer crypto.Signer
var pub *ecdsa.PublicKey
var ok bool
if signer, ok = key.(crypto.Signer); !ok {
return "", NewInvalidKeyTypeError("*ecdsa.PrivateKey or crypto.Signer", key)
}
//sanity check that the signer is an ecdsa signer
if pub, ok = signer.Public().(*ecdsa.PublicKey); !ok {
return "", &InvalidKeyError{Message: fmt.Sprintf("signer returned unexpected public key type: %T", pub)}
}
// Create the hasher
if !m.Hash.Available() {
return "", ErrHashUnavailable
}
hasher := m.Hash.New()
hasher.Write([]byte(signingString))
// Sign the string and return r, s
asn1Sig, err := signer.Sign(rand.Reader, hasher.Sum(nil), m.Hash)
if err != nil {
return "", err
}
//the ecdsa.PrivateKey Sign function returns an asn1 encoded signature which is not what we want
// so we unmarshal it to get r and s to encode as described in rfc7518 section-3.4
var ecdsaSig ecdsaSignature
rest, err := asn1.Unmarshal(asn1Sig, &ecdsaSig)
if err != nil {
return "", err
}
if len(rest) != 0 {
return "", &UnverfiableTokenError{Message: "unexpected extra bytes in ecda signature"}
}
curveBits := pub.Curve.Params().BitSize
if m.CurveBits != curveBits {
return "", &InvalidKeyError{Message: "CurveBits in public key don't match those in signing method"}
}
keyBytes := curveBits / 8
if curveBits%8 > 0 {
keyBytes++
}
// We serialize the output (r and s) into big-endian byte arrays and pad
// them with zeros on the left to make sure the sizes work out. Both arrays
// must be keyBytes long, and the output must be 2*keyBytes long.
rBytes := ecdsaSig.R.Bytes()
rBytesPadded := make([]byte, keyBytes)
copy(rBytesPadded[keyBytes-len(rBytes):], rBytes)
sBytes := ecdsaSig.S.Bytes()
sBytesPadded := make([]byte, keyBytes)
copy(sBytesPadded[keyBytes-len(sBytes):], sBytes)
out := append(rBytesPadded, sBytesPadded...)
return EncodeSegment(out), nil
}