/
crypto.go
283 lines (251 loc) · 9.42 KB
/
crypto.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
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
package gopaque
import (
"bytes"
"crypto/cipher"
"fmt"
"io"
"golang.org/x/crypto/argon2"
"crypto/aes"
"crypto/hmac"
"go.dedis.ch/kyber/v3"
"go.dedis.ch/kyber/v3/sign/schnorr"
"go.dedis.ch/kyber/v3/suites"
"go.dedis.ch/kyber/v3/util/key"
"go.dedis.ch/kyber/v3/util/random"
"golang.org/x/crypto/hkdf"
)
// KyberSuiteDefault is the recommended default kyber.Suite impl. This uses the
// Ed25519 suite which, based on reading, should satisfy the prime-order
// requirement.
var KyberSuiteDefault = suites.MustFind("Ed25519")
// CryptoDefault is the recommended default Crypto impl.
var CryptoDefault Crypto = CryptoStandardDefault
// Crypto is the abstraction of all needed features for OPAQUE.
type Crypto interface {
suites.Suite
KeyGenerator
PointHasher
KeyDeriver
AuthEncrypter
Signer
}
// KeyGenerator generates keys from a given stream or reader. It shares the
// NewKey signature with go.dedis.ch/kyber/util/key.Generator.
type KeyGenerator interface {
// NewKey creates a new key from the given stream. If the stream is nil, the
// underlying crypto random stream is used.
NewKey(stream cipher.Stream) kyber.Scalar
// NewKeyFromReader creates a new key for the given reader. This simply
// wraps a io.Reader into a cipher.Stream and calls NewKey. If the reader is
// nil, the crypto random stream is used.
NewKeyFromReader(r io.Reader) kyber.Scalar
}
// PointHasher provides HashToPoint to create points from the hash of messages.
type PointHasher interface {
// HashToPoint hashes the given msg to a point.
HashToPoint(msg []byte) kyber.Point
}
// KeyDeriver provides DeriveKey to create deterministic keys from other keys.
type KeyDeriver interface {
// DeriveKey creates a deterministic private key for the given parent
// private key and an "info" discriminator.
DeriveKey(priv kyber.Scalar, info []byte) kyber.Scalar
}
// AuthEncrypter provides authenticated encryption/decryption that satisfies
// OPAQUE requirements.
type AuthEncrypter interface {
// AuthEncrypt performs an encryption of the given plain bytes
// authenticated with the given key. It satisfies the OPAQUE requirement
// for "key committing" meaning it's infeasible to have the same result
// able to be decrypted successfully by different keys.
AuthEncrypt(priv kyber.Scalar, plain []byte) ([]byte, error)
// AuthDecrypt decrypts what was encrypted with AuthEncrypt with the same
// key.
AuthDecrypt(priv kyber.Scalar, enc []byte) ([]byte, error)
}
// Signer supports signing and verification.
type Signer interface {
// Sign signs the given msg with the given priv key.
Sign(priv kyber.Scalar, msg []byte) ([]byte, error)
// Verify verifies the given sig for the given msg was signed by the private
// key for the given pub key. If verification fails, the error is non-nil.
Verify(pub kyber.Point, msg, sig []byte) error
}
// CryptoStandardDefault is the recommended default CryptoStandard impl.
var CryptoStandardDefault = &CryptoStandard{
Suite: KyberSuiteDefault,
KeyDeriver: DeriveKeyHKDF,
Signer: &SignerSchnorr{KyberSuiteDefault},
}
// CryptoStandard implements Crypto with a given suite + key deriver + signer
// and using basic implementations of other features.
type CryptoStandard struct {
suites.Suite
KeyDeriver func(c Crypto, priv kyber.Scalar, info []byte) kyber.Scalar
Signer
}
// NewKey implements KeyGenerator.NewKey.
func (c *CryptoStandard) NewKey(stream cipher.Stream) kyber.Scalar {
if stream == nil {
stream = c.RandomStream()
}
if g, _ := c.Suite.(key.Generator); g != nil {
return g.NewKey(stream)
}
return c.Scalar().Pick(stream)
}
// NewKeyFromReader implements KeyGenerator.NewKeyFromReader.
func (c *CryptoStandard) NewKeyFromReader(r io.Reader) kyber.Scalar {
if r == nil {
return c.NewKey(nil)
}
// Implements a particular utility to turn a io.Reader into a cipher.Stream from kyber library
return c.NewKey(random.New(r))
}
// HashToPoint implements PointHasher.HashToPoint.
func (c *CryptoStandard) HashToPoint(msg []byte) kyber.Point {
// TODO: Since functionality was removed in https://github.com/dedis/kyber/pull/352, we just copied the BLS
// code but we need to reintroduce proper elligator or something when it's back. Note, this uses LE
// internally.
h := c.Hash()
h.Write(msg)
x := c.Scalar().SetBytes(h.Sum(nil))
return c.Point().Mul(x, nil)
}
// DeriveKey implements KeyDeriver.DeriveKey. It just defers to the KeyDeriver
// function.
func (c *CryptoStandard) DeriveKey(priv kyber.Scalar, info []byte) kyber.Scalar {
return c.KeyDeriver(c, priv, info)
}
// AuthEncrypt implements Crypto.AuthEncrypt.
func (c *CryptoStandard) AuthEncrypt(priv kyber.Scalar, plain []byte) ([]byte, error) {
// TODO: can an alternative be nacl secretbox instead?
// We need two deterministic keys for the parent key, one for AES and one for MAC
encKey, macKey := c.DeriveKey(priv, []byte("encKey")), c.DeriveKey(priv, []byte("macKey"))
// Encrypt first
encBytes, err := c.aesCBCEncrypt(encKey, plain)
if err != nil {
return nil, err
}
mac := hmac.New(c.Hash, toBytes(macKey))
mac.Write(encBytes)
macBytes := mac.Sum(nil)
// Just put the MAC at the end
return append(encBytes, macBytes...), nil
}
func (c *CryptoStandard) aesCBCEncrypt(priv kyber.Scalar, plain []byte) ([]byte, error) {
// We need to pad w/ repeated bytes of pad amount, and if none are needed we do a whole block of it
padAmount := byte(aes.BlockSize - (len(plain) % aes.BlockSize))
if padAmount == 0 {
padAmount = aes.BlockSize
}
padded := make([]byte, len(plain)+int(padAmount))
copy(padded, plain)
for i := len(plain); i < len(padded); i++ {
padded[i] = padAmount
}
// Just use the first 32 bytes for the key if it's more than 32
keyBytes := toBytes(priv)
if len(keyBytes) > 32 {
keyBytes = keyBytes[:32]
}
block, err := aes.NewCipher(keyBytes[:32])
if err != nil {
return nil, err
}
// Includes IV
enc := make([]byte, aes.BlockSize+len(padded))
random.Bytes(enc[:aes.BlockSize], c.RandomStream())
mode := cipher.NewCBCEncrypter(block, enc[:aes.BlockSize])
mode.CryptBlocks(enc[aes.BlockSize:], padded)
return enc, nil
}
// AuthDecrypt implements Crypto.AuthDecrypt.
func (c *CryptoStandard) AuthDecrypt(priv kyber.Scalar, enc []byte) ([]byte, error) {
// Build the same two keys for AES and MAC
encKey, macKey := c.DeriveKey(priv, []byte("encKey")), c.DeriveKey(priv, []byte("macKey"))
macSize := c.Hash().Size()
encBytes, macBytes := enc[:len(enc)-macSize], enc[len(enc)-macSize:]
// First check the mac
mac := hmac.New(c.Hash, toBytes(macKey))
mac.Write(encBytes)
if !hmac.Equal(mac.Sum(nil), macBytes) {
return nil, fmt.Errorf("MAC mismatch")
}
// Now just decrypt
return c.aesCBCDecrypt(encKey, encBytes)
}
func (c *CryptoStandard) aesCBCDecrypt(priv kyber.Scalar, enc []byte) ([]byte, error) {
// IV is first block
if len(enc) < aes.BlockSize || len(enc)%aes.BlockSize != 0 {
return nil, fmt.Errorf("Invalid enc size")
}
// Just use the first 32 bytes for the key
// Just use the first 32 bytes for the key if it's more than 32
keyBytes := toBytes(priv)
if len(keyBytes) > 32 {
keyBytes = keyBytes[:32]
}
block, err := aes.NewCipher(keyBytes)
if err != nil {
return nil, err
}
decPadded := make([]byte, len(enc[aes.BlockSize:]))
mode := cipher.NewCBCDecrypter(block, enc[:aes.BlockSize])
mode.CryptBlocks(decPadded, enc[aes.BlockSize:])
// Validate it is padded with the bytes representing the pad amount
padAmount := decPadded[len(decPadded)-1]
if padAmount == 0 || padAmount > aes.BlockSize {
return nil, fmt.Errorf("Pad validation fail")
}
for i := 1; i <= int(padAmount); i++ {
if decPadded[len(decPadded)-i] != padAmount {
return nil, fmt.Errorf("Pad validation fail")
}
}
return decPadded[:len(decPadded)-int(padAmount)], nil
}
// DeriveKeyHKDF builds a key for the given parent key and info using
// HKDF (RFC 5869). This function can be set as the CryptoStandard.KeyDeriver
// field.
func DeriveKeyHKDF(c Crypto, priv kyber.Scalar, info []byte) kyber.Scalar {
hkdfR := hkdf.New(c.Hash, toBytes(priv), nil, info)
return c.NewKeyFromReader(hkdfR)
}
// DeriveKeyArgon holds parameters to use for Argon-based key derivation. See
// the DeriveKey method for more information.
type DeriveKeyArgon struct {
Time uint32
Memory uint32
Threads uint8
}
// DeriveKeyArgonDefault is the recommended DeriveKeyArgon impl.
var DeriveKeyArgonDefault = &DeriveKeyArgon{
Time: 1,
Memory: 64 * 1024,
Threads: 4,
}
// DeriveKey creates a hash of info and uses that as the Argon salt value w/ the
// key's private bytes as the Argon password to deterministically derive a seed.
// That seed is then used in NewKeyFromReader.
func (d *DeriveKeyArgon) DeriveKey(c Crypto, priv kyber.Scalar, info []byte) kyber.Scalar {
// Build a argon2 hash with the private part of the master key as the password, the hashed info
// as the salt, and the other argon params as given. Then use that hash as the input for a key.
// I.e. key-gen(argon2(P=priv, S=hash(info), ...))
h := c.Hash()
h.Write(info)
argonHash := argon2.IDKey(toBytes(priv), h.Sum(nil), d.Time, d.Memory, d.Threads, uint32(c.ScalarLen()))
return c.NewKeyFromReader(bytes.NewReader(argonHash))
}
// SignerSchnorr implement Signer for Schnorr signatures.
type SignerSchnorr struct {
schnorr.Suite
}
// Sign implements Signer.Sign.
func (s *SignerSchnorr) Sign(priv kyber.Scalar, msg []byte) ([]byte, error) {
return schnorr.Sign(s, priv, msg)
}
// Verify implements Signer.Verify.
func (s *SignerSchnorr) Verify(pub kyber.Point, msg, sig []byte) error {
return schnorr.Verify(s, pub, msg, sig)
}