-
Notifications
You must be signed in to change notification settings - Fork 343
/
ecdsa.go
109 lines (95 loc) · 3.07 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
/*
Copyright 2021 CodeNotary, Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package signer
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
"crypto/x509"
"encoding/asn1"
"encoding/pem"
"errors"
"io"
"io/ioutil"
"math/big"
)
type signer struct {
rand io.Reader
privateKey *ecdsa.PrivateKey
}
type ecdsaSignature struct {
R *big.Int
S *big.Int
}
// NewSigner returns a signer object. It requires a private key file path. To generate a valid key use openssl tool. Ex: openssl ecparam -name prime256v1 -genkey -noout -out ec.key
func NewSigner(privateKeyPath string) (Signer, error) {
privateKeyBytes, err := ioutil.ReadFile(privateKeyPath)
if err != nil {
return nil, err
}
privateKeyBlock, _ := pem.Decode(privateKeyBytes)
if privateKeyBlock == nil {
return nil, errors.New("no ecdsa key found in provided signing key file")
}
privateKey, err := x509.ParseECPrivateKey(privateKeyBlock.Bytes)
if err != nil {
return nil, err
}
return signer{rand: rand.Reader, privateKey: privateKey}, nil
}
// NewSignerFromPKey returns a signer from a io.Reader and a *ecdsa.PrivateKey.
func NewSignerFromPKey(r io.Reader, pk *ecdsa.PrivateKey) Signer {
return signer{rand: r, privateKey: pk}
}
// sign sign a payload and returns asn1 marshal byte sequence
func (sig signer) Sign(payload []byte) ([]byte, []byte, error) {
hash := sha256.Sum256(payload)
r, s, err := ecdsa.Sign(sig.rand, sig.privateKey, hash[:])
if err != nil {
return nil, nil, err
}
sigToMarshal := ecdsaSignature{R: r, S: s}
m, _ := asn1.Marshal(sigToMarshal)
pk := sig.privateKey.PublicKey
p := elliptic.Marshal(pk.Curve, pk.X, pk.Y)
return m, p, nil
}
func UnmarshalKey(publicKey []byte) *ecdsa.PublicKey {
x, y := elliptic.Unmarshal(elliptic.P256(), publicKey)
return &ecdsa.PublicKey{Curve: elliptic.P256(), X: x, Y: y}
}
// verify verifies a signed payload
func Verify(payload []byte, signature []byte, publicKey *ecdsa.PublicKey) (bool, error) {
hash := sha256.Sum256(payload)
es := ecdsaSignature{}
if _, err := asn1.Unmarshal(signature, &es); err != nil {
return false, err
}
return ecdsa.Verify(publicKey, hash[:], es.R, es.S), nil
}
func ParsePublicKeyFile(filePath string) (*ecdsa.PublicKey, error) {
publicKeyBytes, err := ioutil.ReadFile(filePath)
if err != nil {
return nil, err
}
publicKeyBlock, _ := pem.Decode(publicKeyBytes)
if publicKeyBlock == nil {
return nil, errors.New("no ecdsa key found in provided public key file")
}
cert, err := x509.ParsePKIXPublicKey(publicKeyBlock.Bytes)
if err != nil {
return nil, err
}
return cert.(*ecdsa.PublicKey), nil
}