@@ -17,19 +17,49 @@ package pss
17
17
18
18
import (
19
19
"context"
20
+ "crypto/aes"
21
+ "crypto/cipher"
20
22
"crypto/ecdsa"
23
+ crand "crypto/rand"
24
+ "errors"
25
+ "github.com/ethereum/go-ethereum/crypto/ecies"
26
+ "io"
27
+ mrand "math/rand"
28
+ "strconv"
21
29
22
30
ethCrypto "github.com/ethereum/go-ethereum/crypto"
23
31
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
24
32
)
25
33
34
+ // We need a singleton here?
26
35
var cryptoBackend defaultCryptoBackend
27
36
37
+ const (
38
+ aesNonceLength = 12 // in bytes; for more info please see cipher.gcmStandardNonceSize & aesgcm.NonceSize()
39
+ )
40
+
28
41
type CryptoBackend interface {
42
+ // Encrypt-Decrypt
43
+ EncryptAsymmetric (rawBytes []byte , key * ecdsa.PublicKey ) ([]byte , error )
44
+ DecryptAsymmetric (rawBytes []byte , key * ecdsa.PrivateKey ) ([]byte , error )
45
+ EncryptSymmetric (rawBytes []byte , key []byte ) ([]byte , error )
46
+ DecryptSymmetric (rawBytes []byte , key []byte ) (decrypted []byte , salt []byte , err error )
47
+
48
+ //Signing and hashing
49
+ Keccak256 (data ... []byte ) []byte
50
+ Sign (bytes []byte , key * ecdsa.PrivateKey ) ([]byte , error ) // Calculate hash and returns the signature
51
+ SigToPub (hash , sig []byte ) (* ecdsa.PublicKey , error ) // SigToPub returns the public key that created the given signature.
52
+
53
+ // Key store functions
29
54
GetSymKey (id string ) ([]byte , error )
30
55
GenerateSymKey () (string , error )
31
56
AddSymKeyDirect (bytes []byte ) (string , error )
57
+
58
+ // Key conversion
32
59
FromECDSAPub (pub * ecdsa.PublicKey ) []byte
60
+ ImportECDSAPublic (key * ecdsa.PublicKey ) * ecies.PublicKey // ecdsa pub key to ecies pub key
61
+
62
+ // Key serialization
33
63
UnmarshalPubkey (pub []byte ) (* ecdsa.PublicKey , error )
34
64
CompressPubkey (pubkey * ecdsa.PublicKey ) []byte
35
65
}
@@ -62,6 +92,88 @@ func NewCryptoUtils() CryptoUtils {
62
92
return & cryptoBackend
63
93
}
64
94
95
+ // === Encrypt-Decrypt ===
96
+
97
+ // encryptAsymmetric encrypts a message with a public key.
98
+ func (crypto * defaultCryptoBackend ) EncryptAsymmetric (rawBytes []byte , key * ecdsa.PublicKey ) ([]byte , error ) {
99
+ if ! validatePublicKey (key ) {
100
+ return nil , errors .New ("invalid public key provided for asymmetric encryption" )
101
+ }
102
+ encrypted , err := crypto .encrypt (crand .Reader , crypto .ImportECDSAPublic (key ), rawBytes , nil , nil )
103
+ if err == nil {
104
+ return encrypted , nil
105
+ }
106
+ return nil , err
107
+ }
108
+
109
+ func (crypto * defaultCryptoBackend ) DecryptAsymmetric (rawBytes []byte , key * ecdsa.PrivateKey ) ([]byte , error ) {
110
+ return ecies .ImportECDSA (key ).Decrypt (rawBytes , nil , nil )
111
+ }
112
+
113
+ func (crypto * defaultCryptoBackend ) EncryptSymmetric (rawBytes []byte , key []byte ) ([]byte , error ) {
114
+ if ! crypto .validateDataIntegrity (key , aesKeyLength ) {
115
+ return nil , errors .New ("invalid key provided for symmetric encryption, size: " + strconv .Itoa (len (key )))
116
+ }
117
+ block , err := aes .NewCipher (key )
118
+ if err != nil {
119
+ return nil , err
120
+ }
121
+ aesgcm , err := cipher .NewGCM (block )
122
+ if err != nil {
123
+ return nil , err
124
+ }
125
+ salt , err := crypto .generateSecureRandomData (aesNonceLength ) // never use more than 2^32 random nonces with a given key
126
+ if err != nil {
127
+ return nil , err
128
+ }
129
+ encrypted := aesgcm .Seal (nil , salt , rawBytes , nil )
130
+ encBytes := append (encrypted , salt ... )
131
+ return encBytes , nil
132
+ }
133
+
134
+ // decryptSymmetric decrypts a message with a topic key, using AES-GCM-256.
135
+ // nonce size should be 12 bytes (see cipher.gcmStandardNonceSize).
136
+ func (crypto * defaultCryptoBackend ) DecryptSymmetric (rawBytes []byte , key []byte ) (decrypted []byte , salt []byte , err error ) {
137
+ // symmetric messages are expected to contain the 12-byte nonce at the end of the payload
138
+ if len (rawBytes ) < aesNonceLength {
139
+ return nil , nil , errors .New ("missing salt or invalid payload in symmetric message" )
140
+ }
141
+ salt = rawBytes [len (rawBytes )- aesNonceLength :]
142
+
143
+ block , err := aes .NewCipher (key )
144
+ if err != nil {
145
+ return nil , nil , err
146
+ }
147
+ aesgcm , err := cipher .NewGCM (block )
148
+ if err != nil {
149
+ return nil , nil , err
150
+ }
151
+ decrypted , err = aesgcm .Open (nil , salt , rawBytes [:len (rawBytes )- aesNonceLength ], nil )
152
+ if err != nil {
153
+ return nil , nil , err
154
+ }
155
+ return
156
+ }
157
+
158
+ // === Signing and hashing ===
159
+
160
+ // Keccak256 calculates and returns the Keccak256 hash of the input data.
161
+ func (crypto * defaultCryptoBackend ) Keccak256 (data ... []byte ) []byte {
162
+ return ethCrypto .Keccak256 (data ... )
163
+ }
164
+
165
+ func (crypto * defaultCryptoBackend ) Sign (bytes []byte , key * ecdsa.PrivateKey ) ([]byte , error ) {
166
+ hash := crypto .Keccak256 (bytes )
167
+ signature , err := crypto .signWithHash (hash , key )
168
+ return signature , err
169
+ }
170
+
171
+ func (crypto * defaultCryptoBackend ) SigToPub (hash , sig []byte ) (* ecdsa.PublicKey , error ) {
172
+ return ethCrypto .SigToPub (hash , sig )
173
+ }
174
+
175
+ // === Key store functions ===
176
+
65
177
func (crypto * defaultCryptoBackend ) GetSymKey (id string ) ([]byte , error ) {
66
178
return crypto .whisper .GetSymKey (id )
67
179
}
@@ -74,21 +186,98 @@ func (crypto *defaultCryptoBackend) AddSymKeyDirect(bytes []byte) (string, error
74
186
return crypto .whisper .AddSymKeyDirect (bytes )
75
187
}
76
188
189
+ // === Key conversion ===
190
+
77
191
// FromECDSA exports a public key into a binary dump.
78
192
func (crypto * defaultCryptoBackend ) FromECDSAPub (pub * ecdsa.PublicKey ) []byte {
79
193
return ethCrypto .FromECDSAPub (pub )
80
194
}
81
195
82
- // CompressPubkey encodes a public key to the 33-byte compressed format.
83
- func (crypto * defaultCryptoBackend ) CompressPubkey (pubkey * ecdsa.PublicKey ) []byte {
84
- return ethCrypto .CompressPubkey (pubkey )
196
+ func (crypto * defaultCryptoBackend ) ImportECDSAPublic (key * ecdsa.PublicKey ) * ecies.PublicKey {
197
+ return ecies .ImportECDSAPublic (key )
85
198
}
86
199
200
+ // === Key serialization ===
201
+
87
202
// UnmarshalPubkey converts bytes to a secp256k1 public key.
88
203
func (crypto * defaultCryptoBackend ) UnmarshalPubkey (pub []byte ) (* ecdsa.PublicKey , error ) {
89
204
return ethCrypto .UnmarshalPubkey (pub )
90
205
}
91
206
207
+ // CompressPubkey encodes a public key to the 33-byte compressed format.
208
+ func (crypto * defaultCryptoBackend ) CompressPubkey (pubkey * ecdsa.PublicKey ) []byte {
209
+ return ethCrypto .CompressPubkey (pubkey )
210
+ }
211
+
212
+ // == private methods ==
213
+
214
+ // signWithHash calculates an ECDSA signature.
215
+ func (crypto * defaultCryptoBackend ) signWithHash (hash []byte , prv * ecdsa.PrivateKey ) (sig []byte , err error ) {
216
+ return ethCrypto .Sign (hash , prv )
217
+ }
218
+
219
+ func (crypto * defaultCryptoBackend ) encrypt (rand io.Reader , pub * ecies.PublicKey , m , s1 , s2 []byte ) (ct []byte , err error ) {
220
+ return ecies .Encrypt (rand , pub , m , s1 , s2 )
221
+ }
222
+
223
+ // generateSecureRandomData generates random data where extra security is required.
224
+ // The purpose of this function is to prevent some bugs in software or in hardware
225
+ // from delivering not-very-random data. This is especially useful for AES nonce,
226
+ // where true randomness does not really matter, but it is very important to have
227
+ // a unique nonce for every message.
228
+ func (crypto * defaultCryptoBackend ) generateSecureRandomData (length int ) ([]byte , error ) {
229
+ x := make ([]byte , length )
230
+ y := make ([]byte , length )
231
+ res := make ([]byte , length )
232
+
233
+ _ , err := crand .Read (x )
234
+ if err != nil {
235
+ return nil , err
236
+ } else if ! crypto .validateDataIntegrity (x , length ) {
237
+ return nil , errors .New ("crypto/rand failed to generate secure random data" )
238
+ }
239
+ _ , err = mrand .Read (y )
240
+ if err != nil {
241
+ return nil , err
242
+ } else if ! crypto .validateDataIntegrity (y , length ) {
243
+ return nil , errors .New ("math/rand failed to generate secure random data" )
244
+ }
245
+ for i := 0 ; i < length ; i ++ {
246
+ res [i ] = x [i ] ^ y [i ]
247
+ }
248
+ if ! crypto .validateDataIntegrity (res , length ) {
249
+ return nil , errors .New ("failed to generate secure random data" )
250
+ }
251
+ return res , nil
252
+ }
253
+
254
+ // validateDataIntegrity returns false if the data have the wrong or contains all zeros,
255
+ // which is the simplest and the most common bug.
256
+ func (crypto * defaultCryptoBackend ) validateDataIntegrity (k []byte , expectedSize int ) bool {
257
+ if len (k ) != expectedSize {
258
+ return false
259
+ }
260
+ if expectedSize > 3 && containsOnlyZeros (k ) {
261
+ return false
262
+ }
263
+ return true
264
+ }
265
+
266
+ // ValidatePublicKey checks the format of the given public key.
267
+ func validatePublicKey (k * ecdsa.PublicKey ) bool {
268
+ return k != nil && k .X != nil && k .Y != nil && k .X .Sign () != 0 && k .Y .Sign () != 0
269
+ }
270
+
271
+ // containsOnlyZeros checks if the data contain only zeros.
272
+ func containsOnlyZeros (data []byte ) bool {
273
+ for _ , b := range data {
274
+ if b != 0 {
275
+ return false
276
+ }
277
+ }
278
+ return true
279
+ }
280
+
92
281
// CryptoUtils
93
282
94
283
func (crypto * defaultCryptoBackend ) GenerateKey () (* ecdsa.PrivateKey , error ) {
0 commit comments