whisper: salt removed from AES encryption

This commit is contained in:
Vlad 2017-04-12 12:50:47 +02:00
parent f32b72ca5d
commit 8174fc7390
8 changed files with 34 additions and 88 deletions

View file

@ -58,8 +58,9 @@ func TestDBKey(t *testing.T) {
} }
func generateEnvelope(t *testing.T) *whisper.Envelope { func generateEnvelope(t *testing.T) *whisper.Envelope {
h := crypto.Keccak256Hash([]byte("test sample data"))
params := &whisper.MessageParams{ params := &whisper.MessageParams{
KeySym: []byte("test key"), KeySym: h[:],
Topic: whisper.TopicType{}, Topic: whisper.TopicType{},
Payload: []byte("test payload"), Payload: []byte("test payload"),
PoW: powRequirement, PoW: powRequirement,

View file

@ -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) { func BenchmarkEncryptionSym(b *testing.B) {
InitSingleTest() InitSingleTest()

View file

@ -52,7 +52,6 @@ const (
TopicLength = 4 TopicLength = 4
signatureLength = 65 signatureLength = 65
aesKeyLength = 32 aesKeyLength = 32
saltLength = 12
AESNonceMaxLength = 12 AESNonceMaxLength = 12
keyIdSize = 32 keyIdSize = 32

View file

@ -40,7 +40,6 @@ type Envelope struct {
Expiry uint32 Expiry uint32
TTL uint32 TTL uint32
Topic TopicType Topic TopicType
Salt []byte
AESNonce []byte AESNonce []byte
Data []byte Data []byte
EnvNonce uint64 EnvNonce uint64
@ -50,15 +49,25 @@ type Envelope struct {
// Don't access hash directly, use Hash() function instead. // 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 // NewEnvelope wraps a Whisper message with expiration and destination data
// included into an envelope for network forwarding. // 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{ env := Envelope{
Version: make([]byte, 1), Version: make([]byte, 1),
Expiry: uint32(time.Now().Add(time.Second * time.Duration(ttl)).Unix()), Expiry: uint32(time.Now().Add(time.Second * time.Duration(ttl)).Unix()),
TTL: ttl, TTL: ttl,
Topic: topic, Topic: topic,
Salt: salt,
AESNonce: aesNonce, AESNonce: aesNonce,
Data: msg.Raw, Data: msg.Raw,
EnvNonce: 0, EnvNonce: 0,
@ -126,10 +135,6 @@ func (e *Envelope) Seal(options *MessageParams) error {
return nil 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 { func (e *Envelope) PoW() float64 {
if e.pow == 0 { if e.pow == 0 {
e.calculatePoW(0) e.calculatePoW(0)
@ -159,12 +164,6 @@ func (e *Envelope) powToFirstBit(pow float64) int {
return int(bits) 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. // Hash returns the SHA3 hash of the envelope, calculating it if not yet done.
func (e *Envelope) Hash() common.Hash { func (e *Envelope) Hash() common.Hash {
if (e.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. // OpenSymmetric tries to decrypt an envelope, potentially encrypted with a particular key.
func (e *Envelope) OpenSymmetric(key []byte) (msg *ReceivedMessage, err error) { func (e *Envelope) OpenSymmetric(key []byte) (msg *ReceivedMessage, err error) {
msg = &ReceivedMessage{Raw: e.Data} msg = &ReceivedMessage{Raw: e.Data}
err = msg.decryptSymmetric(key, e.Salt, e.AESNonce) err = msg.decryptSymmetric(key, e.AESNonce)
if err != nil { if err != nil {
msg = nil msg = nil
} }

View file

@ -68,7 +68,7 @@ func generateFilter(t *testing.T, symmetric bool) (*Filter, error) {
f.Src = &key.PublicKey f.Src = &key.PublicKey
if symmetric { if symmetric {
f.KeySym = make([]byte, 12) f.KeySym = make([]byte, aesKeyLength)
mrand.Read(f.KeySym) mrand.Read(f.KeySym)
f.SymKeyHash = crypto.Keccak256Hash(f.KeySym) f.SymKeyHash = crypto.Keccak256Hash(f.KeySym)
} else { } else {

View file

@ -23,14 +23,12 @@ import (
"crypto/cipher" "crypto/cipher"
"crypto/ecdsa" "crypto/ecdsa"
crand "crypto/rand" crand "crypto/rand"
"crypto/sha256"
"errors" "errors"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/ecies" "github.com/ethereum/go-ethereum/crypto/ecies"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"golang.org/x/crypto/pbkdf2"
) )
// Options specifies the exact way a message should be wrapped into an Envelope. // 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 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. // NewMessage creates and initializes a non-signed, non-encrypted Whisper message.
func NewSentMessage(params *MessageParams) *SentMessage { func NewSentMessage(params *MessageParams) *SentMessage {
msg := 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. // encryptSymmetric encrypts a message with a topic key, using AES-GCM-256.
// nonce size should be 12 bytes (see cipher.gcmStandardNonceSize). // 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) { 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) block, err := aes.NewCipher(key)
_, err = crand.Read(salt)
if err != nil { if err != nil {
return nil, nil, err return 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
} }
aesgcm, err := cipher.NewGCM(block) aesgcm, err := cipher.NewGCM(block)
if err != nil { if err != nil {
return nil, nil, err return nil, err
} }
// never use more than 2^32 random nonces with a given key // never use more than 2^32 random nonces with a given key
nonce = make([]byte, aesgcm.NonceSize()) nonce = make([]byte, aesgcm.NonceSize())
_, err = crand.Read(nonce) _, err = crand.Read(nonce)
if err != nil { if err != nil {
return nil, nil, err return nil, err
} else if !validateSymmetricKey(nonce) { } 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) 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. // 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 return nil, err
} }
} }
var salt, nonce []byte var nonce []byte
if options.Dst != nil { if options.Dst != nil {
err = msg.encryptAsymmetric(options.Dst) err = msg.encryptAsymmetric(options.Dst)
} else if options.KeySym != nil { } else if options.KeySym != nil {
salt, nonce, err = msg.encryptSymmetric(options.KeySym) nonce, err = msg.encryptSymmetric(options.KeySym)
} else { } else {
err = errors.New("unable to encrypt the message: neither symmetric nor assymmetric key provided") 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 return nil, err
} }
envelope = NewEnvelope(options.TTL, options.Topic, salt, nonce, msg) envelope = NewEnvelope(options.TTL, options.Topic, nonce, msg)
err = envelope.Seal(options) err = envelope.Seal(options)
if err != nil { if err != nil {
return nil, err 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. // decryptSymmetric decrypts a message with a topic key, using AES-GCM-256.
// nonce size should be 12 bytes (see cipher.gcmStandardNonceSize). // nonce size should be 12 bytes (see cipher.gcmStandardNonceSize).
func (msg *ReceivedMessage) decryptSymmetric(key []byte, salt []byte, nonce []byte) error { func (msg *ReceivedMessage) decryptSymmetric(key []byte, nonce []byte) error {
derivedKey, err := DeriveOneTimeKey(key, salt, msg.EnvelopeVersion) block, err := aes.NewCipher(key)
if err != nil {
return err
}
block, err := aes.NewCipher(derivedKey)
if err != nil { if err != nil {
return err return err
} }

View file

@ -178,11 +178,9 @@ func TestMessageSeal(t *testing.T) {
msg := NewSentMessage(params) msg := NewSentMessage(params)
params.TTL = 1 params.TTL = 1
aesnonce := make([]byte, 12) aesnonce := make([]byte, 12)
salt := make([]byte, 12)
mrand.Read(aesnonce) 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 { if err != nil {
t.Fatalf("failed Wrap with seed %d: %s.", seed, err) t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
} }

View file

@ -262,24 +262,14 @@ func (w *Whisper) GetPrivateKey(id string) (*ecdsa.PrivateKey, error) {
// GenerateSymKey generates a random symmetric key and stores it under id, // 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. // which is then returned. Will be used in the future for session key exchange.
func (w *Whisper) GenerateSymKey() (string, error) { func (w *Whisper) GenerateSymKey() (string, error) {
const size = aesKeyLength * 2 key := make([]byte, aesKeyLength)
buf := make([]byte, size) _, err := crand.Read(key)
_, err := crand.Read(buf)
if err != nil { if err != nil {
return "", err return "", err
} else if !validateSymmetricKey(buf) { } else if !validateSymmetricKey(key) {
return "", fmt.Errorf("error in GenerateSymKey: crypto/rand failed to generate random data") 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() id, err := GenerateRandomID()
if err != nil { if err != nil {
return "", fmt.Errorf("failed to generate ID: %s", err) return "", fmt.Errorf("failed to generate ID: %s", err)
@ -291,7 +281,7 @@ func (w *Whisper) GenerateSymKey() (string, error) {
if w.symKeys[id] != nil { if w.symKeys[id] != nil {
return "", fmt.Errorf("failed to generate unique ID") return "", fmt.Errorf("failed to generate unique ID")
} }
w.symKeys[id] = derived w.symKeys[id] = key
return id, nil return id, nil
} }
@ -556,10 +546,6 @@ func (wh *Whisper) add(envelope *Envelope) (bool, error) {
return false, fmt.Errorf("oversized AESNonce [%x]", envelope.Hash()) 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 { if envelope.PoW() < wh.minPoW {
log.Debug("envelope with low PoW dropped", "PoW", envelope.PoW(), "hash", envelope.Hash().Hex()) log.Debug("envelope with low PoW dropped", "PoW", envelope.PoW(), "hash", envelope.Hash().Hex())
return false, nil // drop envelope without error return false, nil // drop envelope without error