diff options
author | gluk256 <gluk256@users.noreply.github.com> | 2017-04-27 03:05:48 +0800 |
---|---|---|
committer | Felix Lange <fjl@users.noreply.github.com> | 2017-04-27 03:05:48 +0800 |
commit | 95f0bd0acf301bf8415747c4ff050e8a4dfdc864 (patch) | |
tree | 13edad970fb30a8e12151b2ec1a443b0ad970d2b /whisper/whisperv5/message.go | |
parent | 8dce4c283dda3a8e10aa30dadab05a8c0dd9e19d (diff) | |
download | go-tangerine-95f0bd0acf301bf8415747c4ff050e8a4dfdc864.tar go-tangerine-95f0bd0acf301bf8415747c4ff050e8a4dfdc864.tar.gz go-tangerine-95f0bd0acf301bf8415747c4ff050e8a4dfdc864.tar.bz2 go-tangerine-95f0bd0acf301bf8415747c4ff050e8a4dfdc864.tar.lz go-tangerine-95f0bd0acf301bf8415747c4ff050e8a4dfdc864.tar.xz go-tangerine-95f0bd0acf301bf8415747c4ff050e8a4dfdc864.tar.zst go-tangerine-95f0bd0acf301bf8415747c4ff050e8a4dfdc864.zip |
whisper: message format refactoring (#14335)
* whisper: salt removed from AES encryption
* whisper: padding format updated
* whisper: padding test added
* whisper: padding refactored, tests fixed
* whisper: padding test updated
* whisper: wnode bugfix
* whisper: send/receive protocol updated
* whisper: minor update
* whisper: bugfix in test
* whisper: updated parameter names and comments
* whisper: functions renamed
* whisper: minor refactoring
Diffstat (limited to 'whisper/whisperv5/message.go')
-rw-r--r-- | whisper/whisperv5/message.go | 133 |
1 files changed, 66 insertions, 67 deletions
diff --git a/whisper/whisperv5/message.go b/whisper/whisperv5/message.go index 9b9c389a6..4ef469b51 100644 --- a/whisper/whisperv5/message.go +++ b/whisper/whisperv5/message.go @@ -23,14 +23,14 @@ import ( "crypto/cipher" "crypto/ecdsa" crand "crypto/rand" - "crypto/sha256" + "encoding/binary" "errors" + "strconv" "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,58 +86,76 @@ 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 { +func NewSentMessage(params *MessageParams) (*SentMessage, error) { msg := SentMessage{} - msg.Raw = make([]byte, 1, len(params.Payload)+len(params.Payload)+signatureLength+padSizeLimitUpper) + msg.Raw = make([]byte, 1, len(params.Payload)+len(params.Padding)+signatureLength+padSizeLimit) msg.Raw[0] = 0 // set all the flags to zero err := msg.appendPadding(params) if err != nil { - log.Error("failed to create NewSentMessage", "err", err) - return nil + return nil, err } msg.Raw = append(msg.Raw, params.Payload...) - return &msg + return &msg, nil +} + +// getSizeOfLength returns the number of bytes necessary to encode the entire size padding (including these bytes) +func getSizeOfLength(b []byte) (sz int, err error) { + sz = intSize(len(b)) // first iteration + sz = intSize(len(b) + sz) // second iteration + if sz > 3 { + err = errors.New("oversized padding parameter") + } + return sz, err +} + +// sizeOfIntSize returns minimal number of bytes necessary to encode an integer value +func intSize(i int) (s int) { + for s = 1; i >= 256; s++ { + i /= 256 + } + return s } // appendPadding appends the pseudorandom padding bytes and sets the padding flag. // The last byte contains the size of padding (thus, its size must not exceed 256). func (msg *SentMessage) appendPadding(params *MessageParams) error { - total := len(params.Payload) + 1 + rawSize := len(params.Payload) + 1 if params.Src != nil { - total += signatureLength + rawSize += signatureLength } - padChunk := padSizeLimitUpper - if total <= padSizeLimitLower { - padChunk = padSizeLimitLower - } - odd := total % padChunk - if odd > 0 { - padSize := padChunk - odd - if padSize > 255 { - // this algorithm is only valid if padSizeLimitUpper <= 256. - // if padSizeLimitUpper will ever change, please fix the algorithm - // (for more information see ReceivedMessage.extractPadding() function). + odd := rawSize % padSizeLimit + + if len(params.Padding) != 0 { + padSize := len(params.Padding) + padLengthSize, err := getSizeOfLength(params.Padding) + if err != nil { + return err + } + totalPadSize := padSize + padLengthSize + buf := make([]byte, 8) + binary.LittleEndian.PutUint32(buf, uint32(totalPadSize)) + buf = buf[:padLengthSize] + msg.Raw = append(msg.Raw, buf...) + msg.Raw = append(msg.Raw, params.Padding...) + msg.Raw[0] |= byte(padLengthSize) // number of bytes indicating the padding size + } else if odd != 0 { + totalPadSize := padSizeLimit - odd + if totalPadSize > 255 { + // this algorithm is only valid if padSizeLimit < 256. + // if padSizeLimit will ever change, please fix the algorithm + // (please see also ReceivedMessage.extractPadding() function). panic("please fix the padding algorithm before releasing new version") } - buf := make([]byte, padSize) + buf := make([]byte, totalPadSize) _, err := crand.Read(buf[1:]) if err != nil { return err } - buf[0] = byte(padSize) - if params.Padding != nil { - copy(buf[1:], params.Padding) + if totalPadSize > 6 && !validateSymmetricKey(buf) { + return errors.New("failed to generate random padding of size " + strconv.Itoa(totalPadSize)) } + buf[0] = byte(totalPadSize) msg.Raw = append(msg.Raw, buf...) msg.Raw[0] |= byte(0x1) // number of bytes indicating the padding size } @@ -178,46 +196,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") - } - - salt = make([]byte, saltLength) - _, err = crand.Read(salt) - if err != nil { - return nil, nil, err - } else if !validateSymmetricKey(salt) { - return nil, nil, errors.New("crypto/rand failed to generate salt") + return nil, errors.New("invalid key provided for symmetric encryption") } - 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) + block, err := aes.NewCipher(key) 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 +234,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 +247,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 +257,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 } @@ -323,7 +321,8 @@ func (msg *ReceivedMessage) Validate() bool { // can be successfully decrypted. func (msg *ReceivedMessage) extractPadding(end int) (int, bool) { paddingSize := 0 - sz := int(msg.Raw[0] & paddingMask) // number of bytes containing the entire size of padding, could be zero + sz := int(msg.Raw[0] & paddingMask) // number of bytes indicating the entire size of padding (including these bytes) + // could be zero -- it means no padding if sz != 0 { paddingSize = int(bytesToUintLittleEndian(msg.Raw[1 : 1+sz])) if paddingSize < sz || paddingSize+1 > end { |