-
-
Notifications
You must be signed in to change notification settings - Fork 4.1k
/
signature.go
203 lines (171 loc) · 4.45 KB
/
signature.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
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
// Copyright (C) 2015 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.
// Package signature provides simple methods to create and verify signatures
// in PEM format.
package signature
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"encoding/asn1"
"encoding/pem"
"errors"
"fmt"
"io"
"math/big"
"github.com/syncthing/syncthing/lib/sha256"
)
// GenerateKeys returns a new key pair, with the private and public key
// encoded in PEM format.
func GenerateKeys() (privKey []byte, pubKey []byte, err error) {
// Generate a new key pair
key, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
if err != nil {
return nil, nil, err
}
// Marshal the private key
bs, err := x509.MarshalECPrivateKey(key)
if err != nil {
return nil, nil, err
}
// Encode it in PEM format
privKey = pem.EncodeToMemory(&pem.Block{
Type: "EC PRIVATE KEY",
Bytes: bs,
})
// Marshal the public key
bs, err = x509.MarshalPKIXPublicKey(&key.PublicKey)
if err != nil {
return nil, nil, err
}
// Encode it in PEM format
pubKey = pem.EncodeToMemory(&pem.Block{
Type: "EC PUBLIC KEY",
Bytes: bs,
})
return
}
// Sign computes the hash of data and signs it with the private key, returning
// a signature in PEM format.
func Sign(privKeyPEM []byte, data io.Reader) ([]byte, error) {
// Parse the private key
key, err := loadPrivateKey(privKeyPEM)
if err != nil {
return nil, err
}
// Hash the reader data
hash, err := hashReader(data)
if err != nil {
return nil, err
}
// Sign the hash
r, s, err := ecdsa.Sign(rand.Reader, key, hash)
if err != nil {
return nil, err
}
// Marshal the signature using ASN.1
sig, err := marshalSignature(r, s)
if err != nil {
return nil, err
}
// Encode it in a PEM block
bs := pem.EncodeToMemory(&pem.Block{
Type: "SIGNATURE",
Bytes: sig,
})
return bs, nil
}
// Verify computes the hash of data and compares it to the signature using the
// given public key. Returns nil if the signature is correct.
func Verify(pubKeyPEM []byte, signature []byte, data io.Reader) error {
// Parse the public key
key, err := loadPublicKey(pubKeyPEM)
if err != nil {
return err
}
// Parse the signature
block, _ := pem.Decode(signature)
if block == nil || block.Bytes == nil {
return errors.New("unsupported signature format")
}
r, s, err := unmarshalSignature(block.Bytes)
if err != nil {
return err
}
// Compute the hash of the data
hash, err := hashReader(data)
if err != nil {
return err
}
// Verify the signature
if !ecdsa.Verify(key, hash, r, s) {
return errors.New("incorrect signature")
}
return nil
}
// hashReader returns the SHA256 hash of the reader
func hashReader(r io.Reader) ([]byte, error) {
h := sha256.New()
if _, err := io.Copy(h, r); err != nil {
return nil, err
}
hash := []byte(fmt.Sprintf("%x", h.Sum(nil)))
return hash, nil
}
// loadPrivateKey returns the ECDSA private key structure for the given PEM
// data.
func loadPrivateKey(bs []byte) (*ecdsa.PrivateKey, error) {
block, _ := pem.Decode(bs)
return x509.ParseECPrivateKey(block.Bytes)
}
// loadPublicKey returns the ECDSA public key structure for the given PEM
// data.
func loadPublicKey(bs []byte) (*ecdsa.PublicKey, error) {
// Decode and parse the public key PEM block
block, _ := pem.Decode(bs)
if block == nil || block.Bytes == nil {
return nil, errors.New("unsupported public key format")
}
intf, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return nil, err
}
// It should be an ECDSA public key
pk, ok := intf.(*ecdsa.PublicKey)
if !ok {
return nil, errors.New("unsupported public key format")
}
return pk, nil
}
// A wrapper around the signature integers so that we can marshal and
// unmarshal them.
type signature struct {
R, S *big.Int
}
// marhalSignature returns ASN.1 encoded bytes for the given integers,
// suitable for PEM encoding.
func marshalSignature(r, s *big.Int) ([]byte, error) {
sig := signature{
R: r,
S: s,
}
bs, err := asn1.Marshal(sig)
if err != nil {
return nil, err
}
return bs, nil
}
// unmarshalSignature returns the R and S integers from the given ASN.1
// encoded signature.
func unmarshalSignature(sig []byte) (r *big.Int, s *big.Int, err error) {
var ts signature
_, err = asn1.Unmarshal(sig, &ts)
if err != nil {
return nil, nil, err
}
return ts.R, ts.S, nil
}