Skip to content

Commit 7826765

Browse files
committed
Distilling whisper message and envelope code into message.go
1 parent 506ab97 commit 7826765

File tree

5 files changed

+407
-77
lines changed

5 files changed

+407
-77
lines changed

pss/crypto.go

Lines changed: 192 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,49 @@ package pss
1717

1818
import (
1919
"context"
20+
"crypto/aes"
21+
"crypto/cipher"
2022
"crypto/ecdsa"
23+
crand "crypto/rand"
24+
"errors"
25+
"github.com/ethereum/go-ethereum/crypto/ecies"
26+
"io"
27+
mrand "math/rand"
28+
"strconv"
2129

2230
ethCrypto "github.com/ethereum/go-ethereum/crypto"
2331
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
2432
)
2533

34+
// We need a singleton here?
2635
var cryptoBackend defaultCryptoBackend
2736

37+
const (
38+
aesNonceLength = 12 // in bytes; for more info please see cipher.gcmStandardNonceSize & aesgcm.NonceSize()
39+
)
40+
2841
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
2954
GetSymKey(id string) ([]byte, error)
3055
GenerateSymKey() (string, error)
3156
AddSymKeyDirect(bytes []byte) (string, error)
57+
58+
// Key conversion
3259
FromECDSAPub(pub *ecdsa.PublicKey) []byte
60+
ImportECDSAPublic(key *ecdsa.PublicKey) *ecies.PublicKey // ecdsa pub key to ecies pub key
61+
62+
// Key serialization
3363
UnmarshalPubkey(pub []byte) (*ecdsa.PublicKey, error)
3464
CompressPubkey(pubkey *ecdsa.PublicKey) []byte
3565
}
@@ -62,6 +92,88 @@ func NewCryptoUtils() CryptoUtils {
6292
return &cryptoBackend
6393
}
6494

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+
65177
func (crypto *defaultCryptoBackend) GetSymKey(id string) ([]byte, error) {
66178
return crypto.whisper.GetSymKey(id)
67179
}
@@ -74,21 +186,98 @@ func (crypto *defaultCryptoBackend) AddSymKeyDirect(bytes []byte) (string, error
74186
return crypto.whisper.AddSymKeyDirect(bytes)
75187
}
76188

189+
// === Key conversion ===
190+
77191
// FromECDSA exports a public key into a binary dump.
78192
func (crypto *defaultCryptoBackend) FromECDSAPub(pub *ecdsa.PublicKey) []byte {
79193
return ethCrypto.FromECDSAPub(pub)
80194
}
81195

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)
85198
}
86199

200+
// === Key serialization ===
201+
87202
// UnmarshalPubkey converts bytes to a secp256k1 public key.
88203
func (crypto *defaultCryptoBackend) UnmarshalPubkey(pub []byte) (*ecdsa.PublicKey, error) {
89204
return ethCrypto.UnmarshalPubkey(pub)
90205
}
91206

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+
92281
// CryptoUtils
93282

94283
func (crypto *defaultCryptoBackend) GenerateKey() (*ecdsa.PrivateKey, error) {

pss/keystore.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ func (ks *KeyStore) processSym(envelope *envelope) (*receivedMessage, string, Ps
155155
if err != nil {
156156
continue
157157
}
158-
recvmsg, err := envelope.openSymmetric(symkey)
158+
recvmsg, err := envelope.openSymmetric(symkey, ks.Crypto)
159159
if err != nil {
160160
continue
161161
}
@@ -183,7 +183,7 @@ func (ks *KeyStore) processSym(envelope *envelope) (*receivedMessage, string, Ps
183183
func (p *Pss) processAsym(envelope *envelope) (*receivedMessage, string, PssAddress, error) {
184184
metrics.GetOrRegisterCounter("pss.process.asym", nil).Inc(1)
185185

186-
recvmsg, err := envelope.openAsymmetric(p.privateKey)
186+
recvmsg, err := envelope.openAsymmetric(p.privateKey, p.Crypto)
187187
if err != nil {
188188
return nil, "", nil, fmt.Errorf("could not decrypt message: %s", err)
189189
}

0 commit comments

Comments
 (0)