From 8174fc7390308ab7d59babc61f18224eaad33107 Mon Sep 17 00:00:00 2001 From: Vlad Date: Wed, 12 Apr 2017 12:50:47 +0200 Subject: [PATCH] whisper: salt removed from AES encryption --- whisper/mailserver/server_test.go | 3 +- whisper/whisperv5/benchmarks_test.go | 6 --- whisper/whisperv5/doc.go | 1 - whisper/whisperv5/envelope.go | 27 +++++++------ whisper/whisperv5/filter_test.go | 2 +- whisper/whisperv5/message.go | 57 +++++++--------------------- whisper/whisperv5/message_test.go | 4 +- whisper/whisperv5/whisper.go | 22 ++--------- 8 files changed, 34 insertions(+), 88 deletions(-) diff --git a/whisper/mailserver/server_test.go b/whisper/mailserver/server_test.go index ffdff31915..5c4c08f44f 100644 --- a/whisper/mailserver/server_test.go +++ b/whisper/mailserver/server_test.go @@ -58,8 +58,9 @@ func TestDBKey(t *testing.T) { } func generateEnvelope(t *testing.T) *whisper.Envelope { + h := crypto.Keccak256Hash([]byte("test sample data")) params := &whisper.MessageParams{ - KeySym: []byte("test key"), + KeySym: h[:], Topic: whisper.TopicType{}, Payload: []byte("test payload"), PoW: powRequirement, diff --git a/whisper/whisperv5/benchmarks_test.go b/whisper/whisperv5/benchmarks_test.go index 417b2881b8..fe5d454c02 100644 --- a/whisper/whisperv5/benchmarks_test.go +++ b/whisper/whisperv5/benchmarks_test.go @@ -28,12 +28,6 @@ func BenchmarkDeriveKeyMaterial(b *testing.B) { } } -func BenchmarkDeriveOneTimeKey(b *testing.B) { - for i := 0; i < b.N; i++ { - DeriveOneTimeKey([]byte("test value 1"), []byte("test value 2"), 0) - } -} - func BenchmarkEncryptionSym(b *testing.B) { InitSingleTest() diff --git a/whisper/whisperv5/doc.go b/whisper/whisperv5/doc.go index d60868f670..ed1d4e4b47 100644 --- a/whisper/whisperv5/doc.go +++ b/whisper/whisperv5/doc.go @@ -52,7 +52,6 @@ const ( TopicLength = 4 signatureLength = 65 aesKeyLength = 32 - saltLength = 12 AESNonceMaxLength = 12 keyIdSize = 32 diff --git a/whisper/whisperv5/envelope.go b/whisper/whisperv5/envelope.go index dffa7b2862..d95fcab750 100644 --- a/whisper/whisperv5/envelope.go +++ b/whisper/whisperv5/envelope.go @@ -40,7 +40,6 @@ type Envelope struct { Expiry uint32 TTL uint32 Topic TopicType - Salt []byte AESNonce []byte Data []byte EnvNonce uint64 @@ -50,15 +49,25 @@ type Envelope struct { // Don't access hash directly, use Hash() function instead. } +// size returns the size of envelope as it is sent (i.e. public fields only) +func (e *Envelope) size() int { + return 20 + len(e.Version) + len(e.AESNonce) + len(e.Data) +} + +// rlpWithoutNonce returns the RLP encoded envelope contents, except the nonce. +func (e *Envelope) rlpWithoutNonce() []byte { + res, _ := rlp.EncodeToBytes([]interface{}{e.Version, e.Expiry, e.TTL, e.Topic, e.AESNonce, e.Data}) + return res +} + // NewEnvelope wraps a Whisper message with expiration and destination data // included into an envelope for network forwarding. -func NewEnvelope(ttl uint32, topic TopicType, salt []byte, aesNonce []byte, msg *SentMessage) *Envelope { +func NewEnvelope(ttl uint32, topic TopicType, aesNonce []byte, msg *SentMessage) *Envelope { env := Envelope{ Version: make([]byte, 1), Expiry: uint32(time.Now().Add(time.Second * time.Duration(ttl)).Unix()), TTL: ttl, Topic: topic, - Salt: salt, AESNonce: aesNonce, Data: msg.Raw, EnvNonce: 0, @@ -126,10 +135,6 @@ func (e *Envelope) Seal(options *MessageParams) error { return nil } -func (e *Envelope) size() int { - return len(e.Data) + len(e.Version) + len(e.AESNonce) + len(e.Salt) + 20 -} - func (e *Envelope) PoW() float64 { if e.pow == 0 { e.calculatePoW(0) @@ -159,12 +164,6 @@ func (e *Envelope) powToFirstBit(pow float64) int { return int(bits) } -// rlpWithoutNonce returns the RLP encoded envelope contents, except the nonce. -func (e *Envelope) rlpWithoutNonce() []byte { - res, _ := rlp.EncodeToBytes([]interface{}{e.Expiry, e.TTL, e.Topic, e.Salt, e.AESNonce, e.Data}) - return res -} - // Hash returns the SHA3 hash of the envelope, calculating it if not yet done. func (e *Envelope) Hash() common.Hash { if (e.hash == common.Hash{}) { @@ -210,7 +209,7 @@ func (e *Envelope) OpenAsymmetric(key *ecdsa.PrivateKey) (*ReceivedMessage, erro // OpenSymmetric tries to decrypt an envelope, potentially encrypted with a particular key. func (e *Envelope) OpenSymmetric(key []byte) (msg *ReceivedMessage, err error) { msg = &ReceivedMessage{Raw: e.Data} - err = msg.decryptSymmetric(key, e.Salt, e.AESNonce) + err = msg.decryptSymmetric(key, e.AESNonce) if err != nil { msg = nil } diff --git a/whisper/whisperv5/filter_test.go b/whisper/whisperv5/filter_test.go index ae21d17396..1f990566ef 100644 --- a/whisper/whisperv5/filter_test.go +++ b/whisper/whisperv5/filter_test.go @@ -68,7 +68,7 @@ func generateFilter(t *testing.T, symmetric bool) (*Filter, error) { f.Src = &key.PublicKey if symmetric { - f.KeySym = make([]byte, 12) + f.KeySym = make([]byte, aesKeyLength) mrand.Read(f.KeySym) f.SymKeyHash = crypto.Keccak256Hash(f.KeySym) } else { diff --git a/whisper/whisperv5/message.go b/whisper/whisperv5/message.go index 9b9c389a6a..a34ea028e3 100644 --- a/whisper/whisperv5/message.go +++ b/whisper/whisperv5/message.go @@ -23,14 +23,12 @@ import ( "crypto/cipher" "crypto/ecdsa" crand "crypto/rand" - "crypto/sha256" "errors" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto/ecies" "github.com/ethereum/go-ethereum/log" - "golang.org/x/crypto/pbkdf2" ) // Options specifies the exact way a message should be wrapped into an Envelope. @@ -86,15 +84,6 @@ func (msg *ReceivedMessage) isAsymmetricEncryption() bool { return msg.Dst != nil } -func DeriveOneTimeKey(key []byte, salt []byte, version uint64) ([]byte, error) { - if version == 0 { - derivedKey := pbkdf2.Key(key, salt, 8, aesKeyLength, sha256.New) - return derivedKey, nil - } else { - return nil, unknownVersionError(version) - } -} - // NewMessage creates and initializes a non-signed, non-encrypted Whisper message. func NewSentMessage(params *MessageParams) *SentMessage { msg := SentMessage{} @@ -178,46 +167,31 @@ func (msg *SentMessage) encryptAsymmetric(key *ecdsa.PublicKey) error { // encryptSymmetric encrypts a message with a topic key, using AES-GCM-256. // nonce size should be 12 bytes (see cipher.gcmStandardNonceSize). -func (msg *SentMessage) encryptSymmetric(key []byte) (salt []byte, nonce []byte, err error) { +func (msg *SentMessage) encryptSymmetric(key []byte) (nonce []byte, err error) { if !validateSymmetricKey(key) { - return nil, nil, errors.New("invalid key provided for symmetric encryption") + return nil, errors.New("invalid key provided for symmetric encryption") } - salt = make([]byte, saltLength) - _, err = crand.Read(salt) + block, err := aes.NewCipher(key) if err != nil { - return nil, nil, err - } else if !validateSymmetricKey(salt) { - return nil, nil, errors.New("crypto/rand failed to generate salt") - } - - derivedKey, err := DeriveOneTimeKey(key, salt, EnvelopeVersion) - if err != nil { - return nil, nil, err - } - if !validateSymmetricKey(derivedKey) { - return nil, nil, errors.New("failed to derive one-time key") - } - block, err := aes.NewCipher(derivedKey) - if err != nil { - return nil, nil, err + return nil, err } aesgcm, err := cipher.NewGCM(block) if err != nil { - return nil, nil, err + return nil, err } // never use more than 2^32 random nonces with a given key nonce = make([]byte, aesgcm.NonceSize()) _, err = crand.Read(nonce) if err != nil { - return nil, nil, err + return nil, err } else if !validateSymmetricKey(nonce) { - return nil, nil, errors.New("crypto/rand failed to generate nonce") + return nil, errors.New("crypto/rand failed to generate nonce") } msg.Raw = aesgcm.Seal(nil, nonce, msg.Raw, nil) - return salt, nonce, nil + return nonce, nil } // Wrap bundles the message into an Envelope to transmit over the network. @@ -231,11 +205,11 @@ func (msg *SentMessage) Wrap(options *MessageParams) (envelope *Envelope, err er return nil, err } } - var salt, nonce []byte + var nonce []byte if options.Dst != nil { err = msg.encryptAsymmetric(options.Dst) } else if options.KeySym != nil { - salt, nonce, err = msg.encryptSymmetric(options.KeySym) + nonce, err = msg.encryptSymmetric(options.KeySym) } else { err = errors.New("unable to encrypt the message: neither symmetric nor assymmetric key provided") } @@ -244,7 +218,7 @@ func (msg *SentMessage) Wrap(options *MessageParams) (envelope *Envelope, err er return nil, err } - envelope = NewEnvelope(options.TTL, options.Topic, salt, nonce, msg) + envelope = NewEnvelope(options.TTL, options.Topic, nonce, msg) err = envelope.Seal(options) if err != nil { return nil, err @@ -254,13 +228,8 @@ func (msg *SentMessage) Wrap(options *MessageParams) (envelope *Envelope, err er // decryptSymmetric decrypts a message with a topic key, using AES-GCM-256. // nonce size should be 12 bytes (see cipher.gcmStandardNonceSize). -func (msg *ReceivedMessage) decryptSymmetric(key []byte, salt []byte, nonce []byte) error { - derivedKey, err := DeriveOneTimeKey(key, salt, msg.EnvelopeVersion) - if err != nil { - return err - } - - block, err := aes.NewCipher(derivedKey) +func (msg *ReceivedMessage) decryptSymmetric(key []byte, nonce []byte) error { + block, err := aes.NewCipher(key) if err != nil { return err } diff --git a/whisper/whisperv5/message_test.go b/whisper/whisperv5/message_test.go index 1ed7250d3a..0f805ee5b9 100644 --- a/whisper/whisperv5/message_test.go +++ b/whisper/whisperv5/message_test.go @@ -178,11 +178,9 @@ func TestMessageSeal(t *testing.T) { msg := NewSentMessage(params) params.TTL = 1 aesnonce := make([]byte, 12) - salt := make([]byte, 12) mrand.Read(aesnonce) - mrand.Read(salt) - env := NewEnvelope(params.TTL, params.Topic, salt, aesnonce, msg) + env := NewEnvelope(params.TTL, params.Topic, aesnonce, msg) if err != nil { t.Fatalf("failed Wrap with seed %d: %s.", seed, err) } diff --git a/whisper/whisperv5/whisper.go b/whisper/whisperv5/whisper.go index c4d5d04a74..af24e6c31c 100644 --- a/whisper/whisperv5/whisper.go +++ b/whisper/whisperv5/whisper.go @@ -262,24 +262,14 @@ func (w *Whisper) GetPrivateKey(id string) (*ecdsa.PrivateKey, error) { // GenerateSymKey generates a random symmetric key and stores it under id, // which is then returned. Will be used in the future for session key exchange. func (w *Whisper) GenerateSymKey() (string, error) { - const size = aesKeyLength * 2 - buf := make([]byte, size) - _, err := crand.Read(buf) + key := make([]byte, aesKeyLength) + _, err := crand.Read(key) if err != nil { return "", err - } else if !validateSymmetricKey(buf) { + } else if !validateSymmetricKey(key) { return "", fmt.Errorf("error in GenerateSymKey: crypto/rand failed to generate random data") } - key := buf[:aesKeyLength] - salt := buf[aesKeyLength:] - derived, err := DeriveOneTimeKey(key, salt, EnvelopeVersion) - if err != nil { - return "", err - } else if !validateSymmetricKey(derived) { - return "", fmt.Errorf("failed to derive valid key") - } - id, err := GenerateRandomID() if err != nil { return "", fmt.Errorf("failed to generate ID: %s", err) @@ -291,7 +281,7 @@ func (w *Whisper) GenerateSymKey() (string, error) { if w.symKeys[id] != nil { return "", fmt.Errorf("failed to generate unique ID") } - w.symKeys[id] = derived + w.symKeys[id] = key return id, nil } @@ -556,10 +546,6 @@ func (wh *Whisper) add(envelope *Envelope) (bool, error) { return false, fmt.Errorf("oversized AESNonce [%x]", envelope.Hash()) } - if len(envelope.Salt) > saltLength { - return false, fmt.Errorf("oversized salt [%x]", envelope.Hash()) - } - if envelope.PoW() < wh.minPoW { log.Debug("envelope with low PoW dropped", "PoW", envelope.PoW(), "hash", envelope.Hash().Hex()) return false, nil // drop envelope without error