-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcryptokey.go
189 lines (157 loc) · 4.75 KB
/
cryptokey.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
// Package cryptokey is an encapsulation for cryptographic keys in Go.
package cryptokey
import (
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rand"
"errors"
"fmt"
"math/big"
"github.com/cyphrme/coze"
)
// CryptoKey is a generalization of a singing or encryption cryptographic key:
// public, private, or a key pair.
type CryptoKey struct {
Alg coze.SEAlg
Public crypto.PublicKey
Private crypto.PrivateKey
}
// NewCryptoKey generates a new CryptoKey.
func NewCryptoKey(alg coze.SEAlg) (ck *CryptoKey, err error) {
var cryptoKey = new(CryptoKey)
cryptoKey.Alg = alg
switch coze.SigAlg(alg) {
case coze.Ed25519, coze.Ed25519ph:
// Note: Go's ed25519.PrivateKey is the seed || public key.
cryptoKey.Public, cryptoKey.Private, err = ed25519.GenerateKey(rand.Reader)
if err != nil {
return nil, err
}
return cryptoKey, nil
case coze.ES224, coze.ES256, coze.ES384, coze.ES512:
keyPair, err := ecdsa.GenerateKey(alg.Curve().EllipticCurve(), rand.Reader)
if err != nil {
return nil, err
}
cryptoKey.Public = keyPair.PublicKey
cryptoKey.Private = keyPair
return cryptoKey, nil
default:
return nil, errors.New("coze.NewCryptoKey: Unknown Alg")
}
}
// Sign signs a precalculated digest. On error, returns zero bytes. Digest's
// length must match c.Alg.Hash().Size().
func (c CryptoKey) Sign(digest []byte) (sig []byte, err error) {
if len(digest) != c.Alg.Hash().Size() {
return nil, errors.New(fmt.Sprintf("coze.enum: digest length does not match alg.hash.size. Len: %d, Alg: %s.", len(digest), c.Alg.String()))
}
switch c.Alg.SigAlg() {
default:
return nil, errors.New("coze.CryptoKey.Sign: Unknown Alg")
case coze.ES224, coze.ES256, coze.ES384, coze.ES512:
v, ok := c.Private.(*ecdsa.PrivateKey)
if !ok {
return nil, errors.New("Not a valid ECDSA private key.")
}
// Note: ECDSA Sig is always R || S of a fixed size with left padding. For
// example, ES256 should always have a 64 byte signature.
r, s, err := ecdsa.Sign(rand.Reader, v, digest)
if err != nil {
return nil, err
}
return coze.PadInts(r, s, c.Alg.SigAlg().SigSize()), nil
case coze.Ed25519, coze.Ed25519ph:
v, ok := c.Private.(ed25519.PrivateKey)
if !ok {
return nil, errors.New("Not a valid EdDSA private key")
}
return ed25519.Sign(v, digest), nil
}
}
// Verify verifies that a signature is valid with a given public CryptoKey
// and digest. `digest` should be the digest of the original msg to verify.
func (c CryptoKey) Verify(digest, sig []byte) (valid bool) {
if len(sig) == 0 || len(digest) == 0 {
return false
}
switch c.Alg.SigAlg() {
default:
return false
case coze.ES224, coze.ES256, coze.ES384, coze.ES512:
var size = c.Alg.SigAlg().SigSize() / 2
r := big.NewInt(0).SetBytes(sig[:size])
s := big.NewInt(0).SetBytes(sig[size:])
v, ok := c.Public.(ecdsa.PublicKey)
if !ok {
return false
}
return ecdsa.Verify(&v, digest, r, s)
case coze.Ed25519, coze.Ed25519ph:
v, ok := c.Public.(ed25519.PublicKey)
if !ok {
return false
}
return ed25519.Verify(v, digest, sig)
}
}
// SignMsg signs a pre-hash msg. Message is hashed before signing according to
// `alg`. On error, returns zero bytes.
func (c CryptoKey) SignMsg(msg []byte) (sig []byte, err error) {
return c.Sign(coze.Hash(c.Alg.Hash(), msg))
}
// Verify verifies that a signature with a given public CryptoKey and
// signed message.
func (c CryptoKey) VerifyMsg(msg, sig []byte) (valid bool) {
return c.Verify(coze.Hash(c.Alg.Hash(), msg), sig)
}
// ToCryptoKey takes a Coze Key and returns a crypto key.
func ToCryptoKey(cozekey *coze.Key) (ck *CryptoKey, err error) {
if len(cozekey.X) == 0 {
return nil, errors.New("coze: invalid CozeKey")
}
switch cozekey.Alg.SigAlg().Genus() {
default:
return nil, errors.New("unsupported alg: " + cozekey.Alg.String())
case coze.Ecdsa:
return ecDSACozeKeyToCryptoKey(cozekey), nil
case coze.Eddsa:
return edDSACozeKeyToCryptoKey(cozekey), nil
}
}
func edDSACozeKeyToCryptoKey(ck *coze.Key) (key *CryptoKey) {
key = new(CryptoKey)
key.Alg = ck.Alg
key.Public = crypto.PublicKey(ck.X)
b := make([]coze.B64, 64)
d := append(b, ck.D, ck.X)
key.Private = crypto.PublicKey(d)
return key
}
// ecdsaCozeKeyToCryptoKey converts a Coze Key, public or private, to a
// CryptoKey.
func ecDSACozeKeyToCryptoKey(ck *coze.Key) (key *CryptoKey) {
key = new(CryptoKey)
key.Alg = ck.Alg
half := ck.Alg.XSize() / 2
x := new(big.Int).SetBytes(ck.X[:half])
y := new(big.Int).SetBytes(ck.X[half:])
ec := ecdsa.PublicKey{
Curve: ck.Alg.Curve().EllipticCurve(),
X: x,
Y: y,
}
key.Public = crypto.PublicKey(ec)
// Private
if len(ck.D) == 0 {
return key
}
d := new(big.Int).SetBytes(ck.D)
var private = ecdsa.PrivateKey{
PublicKey: ec,
D: d,
}
key.Private = &private
return key
}