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 {
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,

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) {
InitSingleTest()

View file

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

View file

@ -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
}

View file

@ -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 {

View file

@ -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
}

View file

@ -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)
}

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,
// 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