whisper: padding format updated

This commit is contained in:
Vlad 2017-04-13 17:38:09 +02:00
parent 8174fc7390
commit 631da856a5
4 changed files with 88 additions and 33 deletions

View file

@ -49,17 +49,16 @@ const (
paddingMask = byte(3) paddingMask = byte(3)
signatureFlag = byte(4) signatureFlag = byte(4)
TopicLength = 4 TopicLength = 4
signatureLength = 65 signatureLength = 65
aesKeyLength = 32 aesKeyLength = 32
AESNonceMaxLength = 12 AESNonceLength = 12
keyIdSize = 32 keyIdSize = 32
DefaultMaxMessageLength = 1024 * 1024 DefaultMaxMessageLength = 1024 * 1024
DefaultMinimumPoW = 1.0 // todo: review after testing. DefaultMinimumPoW = 1.0 // todo: review after testing.
padSizeLimitLower = 128 // it can not be less - we don't want to reveal the absence of signature padSizeLimit = 256 // just an arbitrary number, could be changed without breaking the protocol (must not exceed 2^24)
padSizeLimitUpper = 256 // just an arbitrary number, could be changed without losing compatibility
messageQueueLimit = 1024 messageQueueLimit = 1024
expirationCycle = time.Second expirationCycle = time.Second

View file

@ -23,6 +23,7 @@ import (
"crypto/cipher" "crypto/cipher"
"crypto/ecdsa" "crypto/ecdsa"
crand "crypto/rand" crand "crypto/rand"
"encoding/binary"
"errors" "errors"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -87,7 +88,7 @@ func (msg *ReceivedMessage) isAsymmetricEncryption() bool {
// 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{}
msg.Raw = make([]byte, 1, len(params.Payload)+len(params.Payload)+signatureLength+padSizeLimitUpper) msg.Raw = make([]byte, 1, len(params.Payload)+len(params.Payload)+signatureLength+padSizeLimit)
msg.Raw[0] = 0 // set all the flags to zero msg.Raw[0] = 0 // set all the flags to zero
err := msg.appendPadding(params) err := msg.appendPadding(params)
if err != nil { if err != nil {
@ -98,35 +99,77 @@ func NewSentMessage(params *MessageParams) *SentMessage {
return &msg return &msg
} }
// getSizeOfLength returns the number of bytes necessary to encode the entire size padding (including these bytes)
func getSizeOfLength(b []byte) (int, error) {
// prefer clarity over efficiency
var sizeOfLength int
len := len(b)
// first iteration
if len < 256 {
sizeOfLength = 1
} else if len < 256*256 {
sizeOfLength = 2
} else if len < 256*256*256 {
sizeOfLength = 3
} else {
return 0, errors.New("oversized padding parameter")
}
// second iteration
total := len + sizeOfLength
if total < 256 {
sizeOfLength = 1
} else if total < 256*256 {
sizeOfLength = 2
} else if total < 256*256*256 {
sizeOfLength = 3
} else {
return 0, errors.New("oversized padding parameter")
}
return sizeOfLength, nil
}
// appendPadding appends the pseudorandom padding bytes and sets the padding flag. // 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). // The last byte contains the size of padding (thus, its size must not exceed 256).
func (msg *SentMessage) appendPadding(params *MessageParams) error { func (msg *SentMessage) appendPadding(params *MessageParams) error {
total := len(params.Payload) + 1 rawSize := len(params.Payload) + 1
if params.Src != nil { if params.Src != nil {
total += signatureLength rawSize += signatureLength
} }
padChunk := padSizeLimitUpper odd := rawSize % padSizeLimit
if total <= padSizeLimitLower {
padChunk = padSizeLimitLower if len(params.Padding) != 0 {
} padSize := len(params.Padding)
odd := total % padChunk padLengthSize, err := getSizeOfLength(params.Padding)
if odd > 0 { if err != nil {
padSize := padChunk - odd return err
if padSize > 255 { }
// this algorithm is only valid if padSizeLimitUpper <= 256. totalPadSize := padSize + padLengthSize
// if padSizeLimitUpper will ever change, please fix the algorithm buf := make([]byte, 8)
// (for more information see ReceivedMessage.extractPadding() function). 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") panic("please fix the padding algorithm before releasing new version")
} }
buf := make([]byte, padSize) buf := make([]byte, totalPadSize)
_, err := crand.Read(buf[1:]) _, err := crand.Read(buf[1:])
if err != nil { if err != nil {
return err return err
} }
buf[0] = byte(padSize) if !validateSymmetricKey(buf) {
if params.Padding != nil { return errors.New("failed to generate random padding")
copy(buf[1:], params.Padding)
} }
buf[0] = byte(totalPadSize)
msg.Raw = append(msg.Raw, buf...) msg.Raw = append(msg.Raw, buf...)
msg.Raw[0] |= byte(0x1) // number of bytes indicating the padding size msg.Raw[0] |= byte(0x1) // number of bytes indicating the padding size
} }
@ -292,7 +335,8 @@ func (msg *ReceivedMessage) Validate() bool {
// can be successfully decrypted. // can be successfully decrypted.
func (msg *ReceivedMessage) extractPadding(end int) (int, bool) { func (msg *ReceivedMessage) extractPadding(end int) (int, bool) {
paddingSize := 0 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 { if sz != 0 {
paddingSize = int(bytesToUintLittleEndian(msg.Raw[1 : 1+sz])) paddingSize = int(bytesToUintLittleEndian(msg.Raw[1 : 1+sz]))
if paddingSize < sz || paddingSize+1 > end { if paddingSize < sz || paddingSize+1 > end {

View file

@ -42,7 +42,7 @@ func generateMessageParams() (*MessageParams, error) {
p.WorkTime = 1 p.WorkTime = 1
p.TTL = uint32(mrand.Intn(1024)) p.TTL = uint32(mrand.Intn(1024))
p.Payload = make([]byte, sz) p.Payload = make([]byte, sz)
p.Padding = make([]byte, padSizeLimitUpper) p.Padding = make([]byte, padSizeLimit)
p.KeySym = make([]byte, aesKeyLength) p.KeySym = make([]byte, aesKeyLength)
var b int var b int
@ -289,21 +289,29 @@ func TestEncryptWithZeroKey(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
} }
msg := NewSentMessage(params) msg := NewSentMessage(params)
params.KeySym = make([]byte, aesKeyLength) params.KeySym = make([]byte, aesKeyLength)
_, err = msg.Wrap(params) _, err = msg.Wrap(params)
if err == nil { if err == nil {
t.Fatalf("wrapped with zero key, seed: %d.", seed) t.Fatalf("wrapped with zero key, seed: %d.", seed)
} }
params, err = generateMessageParams()
if err != nil {
t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
}
msg = NewSentMessage(params)
params.KeySym = make([]byte, 0) params.KeySym = make([]byte, 0)
_, err = msg.Wrap(params) _, err = msg.Wrap(params)
if err == nil { if err == nil {
t.Fatalf("wrapped with empty key, seed: %d.", seed) t.Fatalf("wrapped with empty key, seed: %d.", seed)
} }
params, err = generateMessageParams()
if err != nil {
t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
}
msg = NewSentMessage(params)
params.KeySym = nil params.KeySym = nil
_, err = msg.Wrap(params) _, err = msg.Wrap(params)
if err == nil { if err == nil {

View file

@ -385,6 +385,9 @@ func (w *Whisper) Unsubscribe(id string) error {
// network in the coming cycles. // network in the coming cycles.
func (w *Whisper) Send(envelope *Envelope) error { func (w *Whisper) Send(envelope *Envelope) error {
ok, err := w.add(envelope) ok, err := w.add(envelope)
if err != nil {
return err
}
if !ok { if !ok {
return fmt.Errorf("failed to add envelope") return fmt.Errorf("failed to add envelope")
} }
@ -540,10 +543,11 @@ func (wh *Whisper) add(envelope *Envelope) (bool, error) {
return false, fmt.Errorf("oversized version [%x]", envelope.Hash()) return false, fmt.Errorf("oversized version [%x]", envelope.Hash())
} }
if len(envelope.AESNonce) > AESNonceMaxLength { aesNonceSize := len(envelope.AESNonce)
// the standard AES GSM nonce size is 12, if aesNonceSize != 0 && aesNonceSize != AESNonceLength {
// but const gcmStandardNonceSize cannot be accessed directly // the standard AES GCM nonce size is 12 bytes,
return false, fmt.Errorf("oversized AESNonce [%x]", envelope.Hash()) // but constant gcmStandardNonceSize cannot be accessed (not exported)
return false, fmt.Errorf("wrong size of AESNonce: %d bytes [env: %x]", aesNonceSize, envelope.Hash())
} }
if envelope.PoW() < wh.minPoW { if envelope.PoW() < wh.minPoW {