-
Notifications
You must be signed in to change notification settings - Fork 282
/
signer.go
158 lines (136 loc) · 3.58 KB
/
signer.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
// Copyright 2016 Yahoo Inc.
// Licensed under the terms of the Apache version 2.0 license. See LICENSE file for terms.
package zmssvctoken
import (
"crypto"
"crypto/ecdsa"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/asn1"
"encoding/pem"
"fmt"
"math/big"
"strings"
)
var hash = crypto.SHA256
// Signer signs a string and returns the signature.
type Signer interface {
Sign(input string) (string, error)
}
// Verifier verifies the signature for a string.
type Verifier interface {
Verify(input, signature string) error
}
// hashString hashes the input string using the
// standard hash algo
func hashString(input string) ([]byte, error) {
h := hash.New()
_, err := h.Write([]byte(input))
if err != nil {
return nil, err
}
return h.Sum(nil), nil
}
// NewSigner creates an instance of Signer using the given private key (ECDSA or RSA).
func NewSigner(privateKeyPEM []byte) (Signer, error) {
block, _ := pem.Decode(privateKeyPEM)
if block == nil {
return nil, fmt.Errorf("Unable to load private key")
}
switch block.Type {
case "EC PRIVATE KEY":
key, err := x509.ParseECPrivateKey(block.Bytes)
if err != nil {
return nil, err
}
return &sign{key: key}, nil
case "RSA PRIVATE KEY":
key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
return &sign{key: key}, nil
default:
return nil, fmt.Errorf("Unsupported private key type: %s", block.Type)
}
}
type sign struct {
key crypto.Signer
}
// Sign signs the given input string using the internal key.
func (s *sign) Sign(input string) (string, error) {
hashed, err := hashString(input)
if err != nil {
return "", err
}
signed, err := s.key.Sign(rand.Reader, hashed, hash)
if err != nil {
return "", err
}
return new(YBase64).EncodeToString(signed), nil
}
type internalVerifier interface {
Verify(hashed []byte, sig []byte) error
}
type rsaVerify struct {
key *rsa.PublicKey
}
// Verify verifies the signature of the input using the RSA public key.
func (r *rsaVerify) Verify(hashed []byte, sig []byte) error {
return rsa.VerifyPKCS1v15(r.key, hash, hashed, sig)
}
type ecdsaVerify struct {
key *ecdsa.PublicKey
}
// Verify verifies the signature of the input using the ECDSA public key.
func (e *ecdsaVerify) Verify(hashed []byte, sig []byte) error {
var s struct {
R, S *big.Int
}
_, err := asn1.Unmarshal(sig, &s)
if err != nil {
return fmt.Errorf("Unable to unmarshal ECDSA sig, %v", err)
}
if ok := ecdsa.Verify(e.key, hashed, s.R, s.S); !ok {
return fmt.Errorf("Invalid ECDSA signature")
}
return nil
}
// NewVerifier creates an instance of Verifier using the given public key.
func NewVerifier(publicKeyPEM []byte) (Verifier, error) {
block, _ := pem.Decode(publicKeyPEM)
if block == nil {
return nil, fmt.Errorf("Unable to load public key")
}
if !strings.HasSuffix(block.Type, "PUBLIC KEY") {
return nil, fmt.Errorf("Invalid public key type: %s", block.Type)
}
xkey, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return nil, err
}
switch key := xkey.(type) {
case *rsa.PublicKey:
return &verify{iv: &rsaVerify{key: key}}, nil
case *ecdsa.PublicKey:
return &verify{iv: &ecdsaVerify{key: key}}, nil
default:
return nil, fmt.Errorf("Unsupported key type, not RSA or ECDSA")
}
}
type verify struct {
iv internalVerifier
}
// Verify verifies the ybase64-encoded signature of the input.
func (v *verify) Verify(input, signature string) error {
sigBytes, err := new(YBase64).DecodeString(signature)
if err != nil {
return err
}
hashed, err := hashString(input)
if err != nil {
return err
}
return v.iv.Verify(hashed, sigBytes)
}