-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathalgorithm.go
More file actions
217 lines (186 loc) · 4.63 KB
/
algorithm.go
File metadata and controls
217 lines (186 loc) · 4.63 KB
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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
package httpsig
import (
"crypto"
"crypto/ecdsa"
"crypto/hmac"
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/sha256"
"encoding/asn1"
"hash"
"math/big"
"strconv"
"github.com/pkg/errors"
)
// Algorithm represents the type of HTTP signature to use
type Algorithm int
// These are the available Algorithms to use
const (
Unknown Algorithm = iota
RSASHA1
RSASHA256
HMACSHA256
ECDSASHA256
)
func (a Algorithm) String() string {
switch a {
case RSASHA1:
return "rsa-sha1"
case RSASHA256:
return "rsa-sha256"
case HMACSHA256:
return "hmac-sha256"
case ECDSASHA256:
return "ecdsa-sha256"
}
return "Algorithm(" + strconv.Itoa(int(a)) + ")"
}
// ParseAlgorithm parses a string into an Algorithm
func ParseAlgorithm(val string) Algorithm {
switch val {
case RSASHA1.String():
return RSASHA1
case RSASHA256.String():
return RSASHA256
case HMACSHA256.String():
return HMACSHA256
case ECDSASHA256.String():
return ECDSASHA256
}
return Unknown
}
func rsaSign(key interface{}, hash crypto.Hash, hashed []byte) ([]byte, error) {
var priv *rsa.PrivateKey
switch k := key.(type) {
case rsa.PrivateKey:
priv = &k
case *rsa.PrivateKey:
priv = k
default:
return nil, errors.Errorf("invalid key type %T for RSA", key)
}
return rsa.SignPKCS1v15(rand.Reader, priv, hash, hashed)
}
func rsaVerify(key interface{}, hash crypto.Hash, hashed, sig []byte) error {
var pub *rsa.PublicKey
switch k := key.(type) {
case rsa.PrivateKey:
pub = &k.PublicKey
case *rsa.PrivateKey:
pub = &k.PublicKey
case rsa.PublicKey:
pub = &k
case *rsa.PublicKey:
pub = k
default:
return errors.Errorf("invalid key type %T for RSA", key)
}
return rsa.VerifyPKCS1v15(pub, hash, hashed, sig)
}
func hmacSign(h func() hash.Hash, key interface{}, data []byte) ([]byte, error) {
k, ok := key.([]byte)
if !ok {
return nil, errors.Errorf("invalid key type %T for HMAC", key)
}
hash := hmac.New(h, k)
if _, err := hash.Write(data); err != nil {
return nil, err
}
return hash.Sum(nil), nil
}
func hmacVerify(h func() hash.Hash, key interface{}, data, sig []byte) error {
k, ok := key.([]byte)
if !ok {
return errors.Errorf("invalid key type %T for HMAC", key)
}
hash := hmac.New(h, k)
if _, err := hash.Write(data); err != nil {
return err
}
if !hmac.Equal(hash.Sum(nil), sig) {
return errors.New("invalid hmac")
}
return nil
}
type ecdsaSignature struct {
R, S *big.Int
}
func ecdsaSign(key interface{}, hashed []byte) ([]byte, error) {
var priv *ecdsa.PrivateKey
switch k := key.(type) {
case ecdsa.PrivateKey:
priv = &k
case *ecdsa.PrivateKey:
priv = k
default:
return nil, errors.Errorf("invalid key type %T for ECDSA", key)
}
r, s, err := ecdsa.Sign(rand.Reader, priv, hashed[:])
if err != nil {
return nil, err
}
return asn1.Marshal(ecdsaSignature{r, s})
}
func ecdsaVerify(key interface{}, hashed, sig []byte) error {
var esig ecdsaSignature
if _, err := asn1.Unmarshal(sig, &esig); err != nil {
return err
}
var pub *ecdsa.PublicKey
switch k := key.(type) {
case ecdsa.PrivateKey:
pub = &k.PublicKey
case *ecdsa.PrivateKey:
pub = &k.PublicKey
case ecdsa.PublicKey:
pub = &k
case *ecdsa.PublicKey:
pub = k
default:
return errors.Errorf("invalid key type %T for ECDSA", key)
}
if !ecdsa.Verify(pub, hashed, esig.R, esig.S) {
return errors.New("invalid ecdsa signature")
}
return nil
}
// Sign signs the data with the provided key. key is expected to be an
// rsa.PrivateKey, []byte for HMAC or ecdsa.PrivateKey
func (a Algorithm) Sign(key interface{}, data []byte) ([]byte, error) {
switch a {
case RSASHA1:
hashed := sha1.Sum(data)
return rsaSign(key, crypto.SHA1, hashed[:])
case RSASHA256:
hashed := sha256.Sum256(data)
return rsaSign(key, crypto.SHA256, hashed[:])
case HMACSHA256:
return hmacSign(sha256.New, key, data)
case ECDSASHA256:
hashed := sha256.Sum256(data)
return ecdsaSign(key, hashed[:])
default:
return nil, errors.Errorf("unsupported algorithm %s", a)
}
}
// Verify verifies that data was properly signed by key. sig is the already
// signed data. key is expected to be an rsa.PublicKey, []byte for HMAC or
// ecdsa.PublicKey. PrivateKeys may also be used.
func (a Algorithm) Verify(key interface{}, data, sig []byte) error {
switch a {
case RSASHA1:
hashed := sha1.Sum(data)
return rsaVerify(key, crypto.SHA1, hashed[:], sig)
case RSASHA256:
hashed := sha256.Sum256(data)
return rsaVerify(key, crypto.SHA256, hashed[:], sig)
case HMACSHA256:
return hmacVerify(sha256.New, key, data, sig)
case ECDSASHA256:
hashed := sha256.Sum256(data)
return ecdsaVerify(key, hashed[:], sig)
default:
return errors.Errorf("unsupported algorithm %s", a)
}
}