Эх сурвалжийг харах

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
gluk256 8 жил өмнө
parent
commit
95f0bd0acf

+ 17 - 14
cmd/wnode/main.go

@@ -65,7 +65,7 @@ var (
 	pub        *ecdsa.PublicKey
 	asymKey    *ecdsa.PrivateKey
 	nodeid     *ecdsa.PrivateKey
-	topic      []byte
+	topic      whisper.TopicType
 	asymKeyID  string
 	filterID   string
 	symPass    string
@@ -84,7 +84,7 @@ var (
 	testMode       = flag.Bool("test", false, "use of predefined parameters for diagnostics")
 	echoMode       = flag.Bool("echo", false, "echo mode: prints some arguments for diagnostics")
 
-	argVerbosity = flag.Int("verbosity", int(log.LvlWarn), "log verbosity level")
+	argVerbosity = flag.Int("verbosity", int(log.LvlError), "log verbosity level")
 	argTTL       = flag.Uint("ttl", 30, "time-to-live for messages in seconds")
 	argWorkTime  = flag.Uint("work", 5, "work time in seconds")
 	argMaxSize   = flag.Int("maxsize", whisper.DefaultMaxMessageLength, "max size of message")
@@ -129,7 +129,7 @@ func processArgs() {
 		if err != nil {
 			utils.Fatalf("Failed to parse the topic: %s", err)
 		}
-		topic = x
+		topic = whisper.BytesToTopic(x)
 	}
 
 	if *asymmetricMode && len(*argPub) > 0 {
@@ -307,7 +307,11 @@ func configureNode() {
 	if *asymmetricMode {
 		if len(*argPub) == 0 {
 			s := scanLine("Please enter the peer's public key: ")
-			pub = crypto.ToECDSAPub(common.FromHex(s))
+			b := common.FromHex(s)
+			if b == nil {
+				utils.Fatalf("Error: can not convert hexadecimal string")
+			}
+			pub = crypto.ToECDSAPub(b)
 			if !isKeyValid(pub) {
 				utils.Fatalf("Error: invalid public key")
 			}
@@ -354,7 +358,7 @@ func configureNode() {
 	filter := whisper.Filter{
 		KeySym:   symKey,
 		KeyAsym:  asymKey,
-		Topics:   [][]byte{topic},
+		Topics:   [][]byte{topic[:]},
 		AllowP2P: p2pAccept,
 	}
 	filterID, err = shh.Subscribe(&filter)
@@ -365,7 +369,7 @@ func configureNode() {
 }
 
 func generateTopic(password []byte) {
-	x := pbkdf2.Key(password, password, 8196, 128, sha512.New)
+	x := pbkdf2.Key(password, password, 4096, 128, sha512.New)
 	for i := 0; i < len(x); i++ {
 		topic[i%whisper.TopicLength] ^= x[i]
 	}
@@ -485,16 +489,15 @@ func sendMsg(payload []byte) common.Hash {
 		Dst:      pub,
 		KeySym:   symKey,
 		Payload:  payload,
-		Topic:    whisper.BytesToTopic(topic),
+		Topic:    topic,
 		TTL:      uint32(*argTTL),
 		PoW:      *argPoW,
 		WorkTime: uint32(*argWorkTime),
 	}
 
-	msg := whisper.NewSentMessage(&params)
-	if msg == nil {
-		fmt.Printf("failed to create new message (OS level error)")
-		os.Exit(0)
+	msg, err := whisper.NewSentMessage(&params)
+	if err != nil {
+		utils.Fatalf("failed to create new message: %s", err)
 	}
 	envelope, err := msg.Wrap(&params)
 	if err != nil {
@@ -624,9 +627,9 @@ func requestExpiredMessagesLoop() {
 		params.Src = nodeid
 		params.WorkTime = 5
 
-		msg := whisper.NewSentMessage(&params)
-		if msg == nil {
-			utils.Fatalf("failed to create new message (OS level error)")
+		msg, err := whisper.NewSentMessage(&params)
+		if err != nil {
+			utils.Fatalf("failed to create new message: %s", err)
 		}
 		env, err := msg.Wrap(&params)
 		if err != nil {

+ 10 - 3
whisper/mailserver/server_test.go

@@ -58,15 +58,19 @@ 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,
 		WorkTime: 2,
 	}
 
-	msg := whisper.NewSentMessage(params)
+	msg, err := whisper.NewSentMessage(params)
+	if err != nil {
+		t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
+	}
 	env, err := msg.Wrap(params)
 	if err != nil {
 		t.Fatalf("failed to wrap with seed %d: %s.", seed, err)
@@ -188,7 +192,10 @@ func createRequest(t *testing.T, p *ServerTestParams) *whisper.Envelope {
 		Src:      p.key,
 	}
 
-	msg := whisper.NewSentMessage(params)
+	msg, err := whisper.NewSentMessage(params)
+	if err != nil {
+		t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
+	}
 	env, err := msg.Wrap(params)
 	if err != nil {
 		t.Fatalf("failed to wrap with seed %d: %s.", seed, err)

+ 32 - 15
whisper/whisperv5/api.go

@@ -214,7 +214,6 @@ func (api *PublicWhisperAPI) Subscribe(args WhisperFilterArgs) (string, error) {
 	}
 
 	filter := Filter{
-		Src:      crypto.ToECDSAPub(common.FromHex(args.SignedWith)),
 		PoW:      args.MinPoW,
 		Messages: make(map[common.Hash]*ReceivedMessage),
 		AllowP2P: args.AllowP2P,
@@ -233,6 +232,11 @@ func (api *PublicWhisperAPI) Subscribe(args WhisperFilterArgs) (string, error) {
 	}
 
 	if len(args.SignedWith) > 0 {
+		sb := common.FromHex(args.SignedWith)
+		if sb == nil {
+			return "", errors.New("subscribe: SignedWith parameter is invalid")
+		}
+		filter.Src = crypto.ToECDSAPub(sb)
 		if !ValidatePublicKey(filter.Src) {
 			return "", errors.New("subscribe: invalid 'SignedWith' field")
 		}
@@ -269,9 +273,10 @@ func (api *PublicWhisperAPI) Unsubscribe(id string) {
 	api.whisper.Unsubscribe(id)
 }
 
-// GetSubscriptionMessages retrieves all the new messages matched by a filter since the last retrieval.
-func (api *PublicWhisperAPI) GetSubscriptionMessages(filterId string) []*WhisperMessage {
-	f := api.whisper.GetFilter(filterId)
+// GetSubscriptionMessages retrieves all the new messages matched by the corresponding
+// subscription filter since the last retrieval.
+func (api *PublicWhisperAPI) GetNewSubscriptionMessages(id string) []*WhisperMessage {
+	f := api.whisper.GetFilter(id)
 	if f != nil {
 		newMail := f.Retrieve()
 		return toWhisperMessages(newMail)
@@ -279,10 +284,10 @@ func (api *PublicWhisperAPI) GetSubscriptionMessages(filterId string) []*Whisper
 	return toWhisperMessages(nil)
 }
 
-// GetMessages retrieves all the floating messages that match a specific filter.
+// GetMessages retrieves all the floating messages that match a specific subscription filter.
 // It is likely to be called once per session, right after Subscribe call.
-func (api *PublicWhisperAPI) GetMessages(filterId string) []*WhisperMessage {
-	all := api.whisper.Messages(filterId)
+func (api *PublicWhisperAPI) GetFloatingMessages(id string) []*WhisperMessage {
+	all := api.whisper.Messages(id)
 	return toWhisperMessages(all)
 }
 
@@ -345,7 +350,11 @@ func (api *PublicWhisperAPI) Post(args PostArgs) error {
 			return errors.New("post: topic is missing for symmetric encryption")
 		}
 	} else if args.Type == "asym" {
-		params.Dst = crypto.ToECDSAPub(common.FromHex(args.Key))
+		kb := common.FromHex(args.Key)
+		if kb == nil {
+			return errors.New("post: public key for asymmetric encryption is invalid")
+		}
+		params.Dst = crypto.ToECDSAPub(kb)
 		if !ValidatePublicKey(params.Dst) {
 			return errors.New("post: public key for asymmetric encryption is invalid")
 		}
@@ -354,9 +363,9 @@ func (api *PublicWhisperAPI) Post(args PostArgs) error {
 	}
 
 	// encrypt and send
-	message := NewSentMessage(&params)
-	if message == nil {
-		return errors.New("post: failed create new message, probably due to failed rand function (OS level)")
+	message, err := NewSentMessage(&params)
+	if err != nil {
+		return err
 	}
 	envelope, err := message.Wrap(&params)
 	if err != nil {
@@ -383,7 +392,7 @@ type PostArgs struct {
 	Type       string        `json:"type"`       // "sym"/"asym" (symmetric or asymmetric)
 	TTL        uint32        `json:"ttl"`        // time-to-live in seconds
 	SignWith   string        `json:"signWith"`   // id of the signing key
-	Key        string        `json:"key"`        // id of encryption key
+	Key        string        `json:"key"`        // key id (in case of sym) or public key (in case of asym)
 	Topic      hexutil.Bytes `json:"topic"`      // topic (4 bytes)
 	Padding    hexutil.Bytes `json:"padding"`    // optional padding bytes
 	Payload    hexutil.Bytes `json:"payload"`    // payload to be encrypted
@@ -474,7 +483,6 @@ type WhisperMessage struct {
 // NewWhisperMessage converts an internal message into an API version.
 func NewWhisperMessage(message *ReceivedMessage) *WhisperMessage {
 	msg := WhisperMessage{
-		Topic:     common.ToHex(message.Topic[:]),
 		Payload:   common.ToHex(message.Payload),
 		Padding:   common.ToHex(message.Padding),
 		Timestamp: message.Sent,
@@ -483,11 +491,20 @@ func NewWhisperMessage(message *ReceivedMessage) *WhisperMessage {
 		Hash:      common.ToHex(message.EnvelopeHash.Bytes()),
 	}
 
+	if len(message.Topic) == TopicLength {
+		msg.Topic = common.ToHex(message.Topic[:])
+	}
 	if message.Dst != nil {
-		msg.Dst = common.ToHex(crypto.FromECDSAPub(message.Dst))
+		b := crypto.FromECDSAPub(message.Dst)
+		if b != nil {
+			msg.Dst = common.ToHex(b)
+		}
 	}
 	if isMessageSigned(message.Raw[0]) {
-		msg.Src = common.ToHex(crypto.FromECDSAPub(message.SigToPubKey()))
+		b := crypto.FromECDSAPub(message.SigToPubKey())
+		if b != nil {
+			msg.Src = common.ToHex(b)
+		}
 	}
 	return &msg
 }

+ 4 - 4
whisper/whisperv5/api_test.go

@@ -43,7 +43,7 @@ func TestBasic(t *testing.T) {
 		t.Fatalf("wrong version: %d.", ver)
 	}
 
-	mail := api.GetSubscriptionMessages("non-existent-id")
+	mail := api.GetNewSubscriptionMessages("non-existent-id")
 	if len(mail) != 0 {
 		t.Fatalf("failed GetFilterChanges: premature result")
 	}
@@ -282,7 +282,7 @@ func waitForMessages(api *PublicWhisperAPI, id string, target int) []*WhisperMes
 	// timeout: 2 seconds
 	result := make([]*WhisperMessage, 0, target)
 	for i := 0; i < 100; i++ {
-		mail := api.GetSubscriptionMessages(id)
+		mail := api.GetNewSubscriptionMessages(id)
 		if len(mail) > 0 {
 			for _, m := range mail {
 				result = append(result, m)
@@ -448,7 +448,7 @@ func TestIntegrationSym(t *testing.T) {
 	f.Topics = make([][]byte, 2)
 	f.Topics[0] = topics[0][:]
 	f.Topics[1] = topics[1][:]
-	f.MinPoW = 0.324
+	f.MinPoW = DefaultMinimumPoW / 2
 	f.SignedWith = sigPubKey.String()
 	f.AllowP2P = false
 
@@ -546,7 +546,7 @@ func TestIntegrationSymWithFilter(t *testing.T) {
 	f.Topics = make([][]byte, 2)
 	f.Topics[0] = topics[0][:]
 	f.Topics[1] = topics[1][:]
-	f.MinPoW = 0.324
+	f.MinPoW = DefaultMinimumPoW / 2
 	f.SignedWith = sigPubKey.String()
 	f.AllowP2P = false
 

+ 7 - 13
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()
 
@@ -43,7 +37,7 @@ func BenchmarkEncryptionSym(b *testing.B) {
 	}
 
 	for i := 0; i < b.N; i++ {
-		msg := NewSentMessage(params)
+		msg, _ := NewSentMessage(params)
 		_, err := msg.Wrap(params)
 		if err != nil {
 			b.Errorf("failed Wrap with seed %d: %s.", seed, err)
@@ -68,7 +62,7 @@ func BenchmarkEncryptionAsym(b *testing.B) {
 	params.Dst = &key.PublicKey
 
 	for i := 0; i < b.N; i++ {
-		msg := NewSentMessage(params)
+		msg, _ := NewSentMessage(params)
 		_, err := msg.Wrap(params)
 		if err != nil {
 			b.Fatalf("failed Wrap with seed %d: %s.", seed, err)
@@ -83,7 +77,7 @@ func BenchmarkDecryptionSymValid(b *testing.B) {
 	if err != nil {
 		b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
 	}
-	msg := NewSentMessage(params)
+	msg, _ := NewSentMessage(params)
 	env, err := msg.Wrap(params)
 	if err != nil {
 		b.Fatalf("failed Wrap with seed %d: %s.", seed, err)
@@ -105,7 +99,7 @@ func BenchmarkDecryptionSymInvalid(b *testing.B) {
 	if err != nil {
 		b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
 	}
-	msg := NewSentMessage(params)
+	msg, _ := NewSentMessage(params)
 	env, err := msg.Wrap(params)
 	if err != nil {
 		b.Fatalf("failed Wrap with seed %d: %s.", seed, err)
@@ -134,7 +128,7 @@ func BenchmarkDecryptionAsymValid(b *testing.B) {
 	f := Filter{KeyAsym: key}
 	params.KeySym = nil
 	params.Dst = &key.PublicKey
-	msg := NewSentMessage(params)
+	msg, _ := NewSentMessage(params)
 	env, err := msg.Wrap(params)
 	if err != nil {
 		b.Fatalf("failed Wrap with seed %d: %s.", seed, err)
@@ -161,7 +155,7 @@ func BenchmarkDecryptionAsymInvalid(b *testing.B) {
 	}
 	params.KeySym = nil
 	params.Dst = &key.PublicKey
-	msg := NewSentMessage(params)
+	msg, _ := NewSentMessage(params)
 	env, err := msg.Wrap(params)
 	if err != nil {
 		b.Fatalf("failed Wrap with seed %d: %s.", seed, err)
@@ -203,7 +197,7 @@ func BenchmarkPoW(b *testing.B) {
 
 	for i := 0; i < b.N; i++ {
 		increment(params.Payload)
-		msg := NewSentMessage(params)
+		msg, _ := NewSentMessage(params)
 		_, err := msg.Wrap(params)
 		if err != nil {
 			b.Fatalf("failed Wrap with seed %d: %s.", seed, err)

+ 7 - 9
whisper/whisperv5/doc.go

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

+ 13 - 14
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
 	}

+ 29 - 10
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 {
@@ -179,7 +179,10 @@ func TestMatchEnvelope(t *testing.T) {
 	params.Topic[0] = 0xFF // ensure mismatch
 
 	// mismatch with pseudo-random data
-	msg := NewSentMessage(params)
+	msg, err := NewSentMessage(params)
+	if err != nil {
+		t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
+	}
 	env, err := msg.Wrap(params)
 	if err != nil {
 		t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
@@ -197,7 +200,10 @@ func TestMatchEnvelope(t *testing.T) {
 	i := mrand.Int() % 4
 	fsym.Topics[i] = params.Topic[:]
 	fasym.Topics[i] = params.Topic[:]
-	msg = NewSentMessage(params)
+	msg, err = NewSentMessage(params)
+	if err != nil {
+		t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
+	}
 	env, err = msg.Wrap(params)
 	if err != nil {
 		t.Fatalf("failed Wrap() with seed %d: %s.", seed, err)
@@ -245,7 +251,10 @@ func TestMatchEnvelope(t *testing.T) {
 	}
 	params.KeySym = nil
 	params.Dst = &key.PublicKey
-	msg = NewSentMessage(params)
+	msg, err = NewSentMessage(params)
+	if err != nil {
+		t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
+	}
 	env, err = msg.Wrap(params)
 	if err != nil {
 		t.Fatalf("failed Wrap() with seed %d: %s.", seed, err)
@@ -323,12 +332,14 @@ func TestMatchMessageSym(t *testing.T) {
 	params.KeySym = f.KeySym
 	params.Topic = BytesToTopic(f.Topics[index])
 
-	sentMessage := NewSentMessage(params)
+	sentMessage, err := NewSentMessage(params)
+	if err != nil {
+		t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
+	}
 	env, err := sentMessage.Wrap(params)
 	if err != nil {
 		t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
 	}
-
 	msg := env.Open(f)
 	if msg == nil {
 		t.Fatalf("failed Open with seed %d.", seed)
@@ -419,12 +430,14 @@ func TestMatchMessageAsym(t *testing.T) {
 	keySymOrig := params.KeySym
 	params.KeySym = nil
 
-	sentMessage := NewSentMessage(params)
+	sentMessage, err := NewSentMessage(params)
+	if err != nil {
+		t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
+	}
 	env, err := sentMessage.Wrap(params)
 	if err != nil {
 		t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
 	}
-
 	msg := env.Open(f)
 	if msg == nil {
 		t.Fatalf("failed to open with seed %d.", seed)
@@ -506,7 +519,10 @@ func generateCompatibeEnvelope(t *testing.T, f *Filter) *Envelope {
 
 	params.KeySym = f.KeySym
 	params.Topic = BytesToTopic(f.Topics[2])
-	sentMessage := NewSentMessage(params)
+	sentMessage, err := NewSentMessage(params)
+	if err != nil {
+		t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
+	}
 	env, err := sentMessage.Wrap(params)
 	if err != nil {
 		t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
@@ -678,7 +694,10 @@ func TestVariableTopics(t *testing.T) {
 	if err != nil {
 		t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
 	}
-	msg := NewSentMessage(params)
+	msg, err := NewSentMessage(params)
+	if err != nil {
+		t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
+	}
 	env, err := msg.Wrap(params)
 	if err != nil {
 		t.Fatalf("failed Wrap with seed %d: %s.", seed, err)

+ 66 - 67
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 {

+ 107 - 33
whisper/whisperv5/message_test.go

@@ -31,9 +31,9 @@ func copyFromBuf(dst []byte, src []byte, beg int) int {
 }
 
 func generateMessageParams() (*MessageParams, error) {
-	// set all the parameters except p.Dst
+	// set all the parameters except p.Dst and p.Padding
 
-	buf := make([]byte, 1024)
+	buf := make([]byte, 4)
 	mrand.Read(buf)
 	sz := mrand.Intn(400)
 
@@ -42,14 +42,10 @@ func generateMessageParams() (*MessageParams, error) {
 	p.WorkTime = 1
 	p.TTL = uint32(mrand.Intn(1024))
 	p.Payload = make([]byte, sz)
-	p.Padding = make([]byte, padSizeLimitUpper)
 	p.KeySym = make([]byte, aesKeyLength)
-
-	var b int
-	b = copyFromBuf(p.Payload, buf, b)
-	b = copyFromBuf(p.Padding, buf, b)
-	b = copyFromBuf(p.KeySym, buf, b)
-	p.Topic = BytesToTopic(buf[b:])
+	mrand.Read(p.Payload)
+	mrand.Read(p.KeySym)
+	p.Topic = BytesToTopic(buf)
 
 	var err error
 	p.Src, err = crypto.GenerateKey()
@@ -77,11 +73,12 @@ func singleMessageTest(t *testing.T, symmetric bool) {
 	}
 
 	text := make([]byte, 0, 512)
-	steg := make([]byte, 0, 512)
 	text = append(text, params.Payload...)
-	steg = append(steg, params.Padding...)
 
-	msg := NewSentMessage(params)
+	msg, err := NewSentMessage(params)
+	if err != nil {
+		t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
+	}
 	env, err := msg.Wrap(params)
 	if err != nil {
 		t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
@@ -102,10 +99,6 @@ func singleMessageTest(t *testing.T, symmetric bool) {
 		t.Fatalf("failed to validate with seed %d.", seed)
 	}
 
-	padsz := len(decrypted.Padding)
-	if !bytes.Equal(steg[:padsz], decrypted.Padding) {
-		t.Fatalf("failed with seed %d: compare padding.", seed)
-	}
 	if !bytes.Equal(text, decrypted.Payload) {
 		t.Fatalf("failed with seed %d: compare payload.", seed)
 	}
@@ -140,7 +133,10 @@ func TestMessageWrap(t *testing.T) {
 		t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
 	}
 
-	msg := NewSentMessage(params)
+	msg, err := NewSentMessage(params)
+	if err != nil {
+		t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
+	}
 	params.TTL = 1
 	params.WorkTime = 12
 	params.PoW = target
@@ -155,7 +151,10 @@ func TestMessageWrap(t *testing.T) {
 	}
 
 	// set PoW target too high, expect error
-	msg2 := NewSentMessage(params)
+	msg2, err := NewSentMessage(params)
+	if err != nil {
+		t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
+	}
 	params.TTL = 1000000
 	params.WorkTime = 1
 	params.PoW = 10000000.0
@@ -175,14 +174,15 @@ func TestMessageSeal(t *testing.T) {
 		t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
 	}
 
-	msg := NewSentMessage(params)
+	msg, err := NewSentMessage(params)
+	if err != nil {
+		t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
+	}
 	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)
 	}
@@ -236,11 +236,12 @@ func singleEnvelopeOpenTest(t *testing.T, symmetric bool) {
 	}
 
 	text := make([]byte, 0, 512)
-	steg := make([]byte, 0, 512)
 	text = append(text, params.Payload...)
-	steg = append(steg, params.Padding...)
 
-	msg := NewSentMessage(params)
+	msg, err := NewSentMessage(params)
+	if err != nil {
+		t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
+	}
 	env, err := msg.Wrap(params)
 	if err != nil {
 		t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
@@ -252,10 +253,6 @@ func singleEnvelopeOpenTest(t *testing.T, symmetric bool) {
 		t.Fatalf("failed to open with seed %d.", seed)
 	}
 
-	padsz := len(decrypted.Padding)
-	if !bytes.Equal(steg[:padsz], decrypted.Padding) {
-		t.Fatalf("failed with seed %d: compare padding.", seed)
-	}
 	if !bytes.Equal(text, decrypted.Payload) {
 		t.Fatalf("failed with seed %d: compare payload.", seed)
 	}
@@ -291,21 +288,38 @@ func TestEncryptWithZeroKey(t *testing.T) {
 	if err != nil {
 		t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
 	}
-
-	msg := NewSentMessage(params)
-
+	msg, err := NewSentMessage(params)
+	if err != nil {
+		t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
+	}
 	params.KeySym = make([]byte, aesKeyLength)
 	_, err = msg.Wrap(params)
 	if err == nil {
 		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, err = NewSentMessage(params)
+	if err != nil {
+		t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
+	}
 	params.KeySym = make([]byte, 0)
 	_, err = msg.Wrap(params)
 	if err == nil {
 		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, err = NewSentMessage(params)
+	if err != nil {
+		t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
+	}
 	params.KeySym = nil
 	_, err = msg.Wrap(params)
 	if err == nil {
@@ -320,7 +334,10 @@ func TestRlpEncode(t *testing.T) {
 	if err != nil {
 		t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
 	}
-	msg := NewSentMessage(params)
+	msg, err := NewSentMessage(params)
+	if err != nil {
+		t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
+	}
 	env, err := msg.Wrap(params)
 	if err != nil {
 		t.Fatalf("wrapped with zero key, seed: %d.", seed)
@@ -344,3 +361,60 @@ func TestRlpEncode(t *testing.T) {
 		t.Fatalf("Hashes are not equal: %x vs. %x", he, hd)
 	}
 }
+
+func singlePaddingTest(t *testing.T, padSize int) {
+	params, err := generateMessageParams()
+	if err != nil {
+		t.Fatalf("failed generateMessageParams with seed %d and sz=%d: %s.", seed, padSize, err)
+	}
+	params.Padding = make([]byte, padSize)
+	params.PoW = 0.0000000001
+	pad := make([]byte, padSize)
+	_, err = mrand.Read(pad)
+	if err != nil {
+		t.Fatalf("padding is not generated (seed %d): %s", seed, err)
+	}
+	n := copy(params.Padding, pad)
+	if n != padSize {
+		t.Fatalf("padding is not copied (seed %d): %s", seed, err)
+	}
+	msg, err := NewSentMessage(params)
+	if err != nil {
+		t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
+	}
+	env, err := msg.Wrap(params)
+	if err != nil {
+		t.Fatalf("failed to wrap, seed: %d and sz=%d.", seed, padSize)
+	}
+	f := Filter{KeySym: params.KeySym}
+	decrypted := env.Open(&f)
+	if decrypted == nil {
+		t.Fatalf("failed to open, seed and sz=%d: %d.", seed, padSize)
+	}
+	if !bytes.Equal(pad, decrypted.Padding) {
+		t.Fatalf("padding is not retireved as expected with seed %d and sz=%d:\n[%x]\n[%x].", seed, padSize, pad, decrypted.Padding)
+	}
+}
+
+func TestPadding(t *testing.T) {
+	InitSingleTest()
+
+	for i := 1; i < 260; i++ {
+		singlePaddingTest(t, i)
+	}
+
+	lim := 256 * 256
+	for i := lim - 5; i < lim+2; i++ {
+		singlePaddingTest(t, i)
+	}
+
+	for i := 0; i < 256; i++ {
+		n := mrand.Intn(256*254) + 256
+		singlePaddingTest(t, n)
+	}
+
+	for i := 0; i < 256; i++ {
+		n := mrand.Intn(256*1024) + 256*256
+		singlePaddingTest(t, n)
+	}
+}

+ 10 - 11
whisper/whisperv5/peer.go

@@ -149,23 +149,22 @@ func (peer *Peer) expire() {
 // broadcast iterates over the collection of envelopes and transmits yet unknown
 // ones over the network.
 func (p *Peer) broadcast() error {
-	// Fetch the envelopes and collect the unknown ones
+	var cnt int
 	envelopes := p.host.Envelopes()
-	transmit := make([]*Envelope, 0, len(envelopes))
 	for _, envelope := range envelopes {
 		if !p.marked(envelope) {
-			transmit = append(transmit, envelope)
-			p.mark(envelope)
+			err := p2p.Send(p.ws, messagesCode, envelope)
+			if err != nil {
+				return err
+			} else {
+				p.mark(envelope)
+				cnt++
+			}
 		}
 	}
-	if len(transmit) == 0 {
-		return nil
-	}
-	// Transmit the unknown batch (potentially empty)
-	if err := p2p.Send(p.ws, messagesCode, transmit); err != nil {
-		return err
+	if cnt > 0 {
+		log.Trace("broadcast", "num. messages", cnt)
 	}
-	log.Trace("broadcast", "num. messages", len(transmit))
 	return nil
 }
 

+ 8 - 2
whisper/whisperv5/peer_test.go

@@ -265,7 +265,10 @@ func sendMsg(t *testing.T, expected bool, id int) {
 		opt.Payload = opt.Payload[1:]
 	}
 
-	msg := NewSentMessage(&opt)
+	msg, err := NewSentMessage(&opt)
+	if err != nil {
+		t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
+	}
 	envelope, err := msg.Wrap(&opt)
 	if err != nil {
 		t.Fatalf("failed to seal message: %s", err)
@@ -286,7 +289,10 @@ func TestPeerBasic(t *testing.T) {
 	}
 
 	params.PoW = 0.001
-	msg := NewSentMessage(params)
+	msg, err := NewSentMessage(params)
+	if err != nil {
+		t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
+	}
 	env, err := msg.Wrap(params)
 	if err != nil {
 		t.Fatalf("failed Wrap with seed %d.", seed)

+ 21 - 34
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
 }
 
@@ -395,6 +385,9 @@ func (w *Whisper) Unsubscribe(id string) error {
 // network in the coming cycles.
 func (w *Whisper) Send(envelope *Envelope) error {
 	ok, err := w.add(envelope)
+	if err != nil {
+		return err
+	}
 	if !ok {
 		return fmt.Errorf("failed to add envelope")
 	}
@@ -469,21 +462,18 @@ func (wh *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error {
 			log.Warn("unxepected status message received", "peer", p.peer.ID())
 		case messagesCode:
 			// decode the contained envelopes
-			var envelopes []*Envelope
-			if err := packet.Decode(&envelopes); err != nil {
+			var envelope Envelope
+			if err := packet.Decode(&envelope); err != nil {
 				log.Warn("failed to decode envelope, peer will be disconnected", "peer", p.peer.ID(), "err", err)
 				return errors.New("invalid envelope")
 			}
-			// inject all envelopes into the internal pool
-			for _, envelope := range envelopes {
-				cached, err := wh.add(envelope)
-				if err != nil {
-					log.Warn("bad envelope received, peer will be disconnected", "peer", p.peer.ID(), "err", err)
-					return errors.New("invalid envelope")
-				}
-				if cached {
-					p.mark(envelope)
-				}
+			cached, err := wh.add(&envelope)
+			if err != nil {
+				log.Warn("bad envelope received, peer will be disconnected", "peer", p.peer.ID(), "err", err)
+				return errors.New("invalid envelope")
+			}
+			if cached {
+				p.mark(&envelope)
 			}
 		case p2pCode:
 			// peer-to-peer message, sent directly to peer bypassing PoW checks, etc.
@@ -550,14 +540,11 @@ func (wh *Whisper) add(envelope *Envelope) (bool, error) {
 		return false, fmt.Errorf("oversized version [%x]", envelope.Hash())
 	}
 
-	if len(envelope.AESNonce) > AESNonceMaxLength {
-		// the standard AES GSM nonce size is 12,
-		// but const gcmStandardNonceSize cannot be accessed directly
-		return false, fmt.Errorf("oversized AESNonce [%x]", envelope.Hash())
-	}
-
-	if len(envelope.Salt) > saltLength {
-		return false, fmt.Errorf("oversized salt [%x]", envelope.Hash())
+	aesNonceSize := len(envelope.AESNonce)
+	if aesNonceSize != 0 && aesNonceSize != AESNonceLength {
+		// the standard AES GCM nonce size is 12 bytes,
+		// 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 {

+ 12 - 3
whisper/whisperv5/whisper_test.go

@@ -455,7 +455,10 @@ func TestExpiry(t *testing.T) {
 	}
 
 	params.TTL = 1
-	msg := NewSentMessage(params)
+	msg, err := NewSentMessage(params)
+	if err != nil {
+		t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
+	}
 	env, err := msg.Wrap(params)
 	if err != nil {
 		t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
@@ -515,7 +518,10 @@ func TestCustomization(t *testing.T) {
 	params.Topic = BytesToTopic(f.Topics[2])
 	params.PoW = smallPoW
 	params.TTL = 3600 * 24 // one day
-	msg := NewSentMessage(params)
+	msg, err := NewSentMessage(params)
+	if err != nil {
+		t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
+	}
 	env, err := msg.Wrap(params)
 	if err != nil {
 		t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
@@ -533,7 +539,10 @@ func TestCustomization(t *testing.T) {
 	}
 
 	params.TTL++
-	msg = NewSentMessage(params)
+	msg, err = NewSentMessage(params)
+	if err != nil {
+		t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
+	}
 	env, err = msg.Wrap(params)
 	if err != nil {
 		t.Fatalf("failed Wrap with seed %d: %s.", seed, err)