mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-17 01:13:45 +00:00
whisper: remove AESNonce from envelope in v6
The aes nonce is now part of the payload of a symmetric message. Since there is no futher available indication whether a message is symmetric or not, functions isSymmetric and isAsymmetric have been removed and a symmetric topic match will be attempted if the filter is symmetric. Accordingly, other checks have been removed at the top level, because they can no longer be performed until it is known for a fact that the message uses symmetric encryption.
This commit is contained in:
parent
b5874273ce
commit
0d31e257e0
6 changed files with 45 additions and 55 deletions
|
|
@ -40,7 +40,6 @@ type Envelope struct {
|
||||||
Expiry uint32
|
Expiry uint32
|
||||||
TTL uint32
|
TTL uint32
|
||||||
Topic TopicType
|
Topic TopicType
|
||||||
AESNonce []byte
|
|
||||||
Data []byte
|
Data []byte
|
||||||
Nonce uint64
|
Nonce uint64
|
||||||
|
|
||||||
|
|
@ -51,12 +50,12 @@ type Envelope struct {
|
||||||
|
|
||||||
// size returns the size of envelope as it is sent (i.e. public fields only)
|
// size returns the size of envelope as it is sent (i.e. public fields only)
|
||||||
func (e *Envelope) size() int {
|
func (e *Envelope) size() int {
|
||||||
return 20 + len(e.Version) + len(e.AESNonce) + len(e.Data)
|
return 20 + len(e.Version) + len(e.Data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// rlpWithoutNonce returns the RLP encoded envelope contents, except the nonce.
|
// rlpWithoutNonce returns the RLP encoded envelope contents, except the nonce.
|
||||||
func (e *Envelope) rlpWithoutNonce() []byte {
|
func (e *Envelope) rlpWithoutNonce() []byte {
|
||||||
res, _ := rlp.EncodeToBytes([]interface{}{e.Version, e.Expiry, e.TTL, e.Topic, e.AESNonce, e.Data})
|
res, _ := rlp.EncodeToBytes([]interface{}{e.Version, e.Expiry, e.TTL, e.Topic, e.Data})
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -82,14 +81,6 @@ func NewEnvelope(ttl uint32, topic TopicType, aesNonce []byte, msg *sentMessage)
|
||||||
return &env
|
return &env
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Envelope) IsSymmetric() bool {
|
|
||||||
return len(e.AESNonce) > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Envelope) isAsymmetric() bool {
|
|
||||||
return !e.IsSymmetric()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Envelope) Ver() uint64 {
|
func (e *Envelope) Ver() uint64 {
|
||||||
return bytesToUintLittleEndian(e.Version)
|
return bytesToUintLittleEndian(e.Version)
|
||||||
}
|
}
|
||||||
|
|
@ -209,7 +200,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.AESNonce)
|
err = msg.decryptSymmetric(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
msg = nil
|
msg = nil
|
||||||
}
|
}
|
||||||
|
|
@ -218,12 +209,17 @@ func (e *Envelope) OpenSymmetric(key []byte) (msg *ReceivedMessage, err error) {
|
||||||
|
|
||||||
// Open tries to decrypt an envelope, and populates the message fields in case of success.
|
// Open tries to decrypt an envelope, and populates the message fields in case of success.
|
||||||
func (e *Envelope) Open(watcher *Filter) (msg *ReceivedMessage) {
|
func (e *Envelope) Open(watcher *Filter) (msg *ReceivedMessage) {
|
||||||
if e.isAsymmetric() {
|
// if the filter has some asymmetric key provided, it will
|
||||||
|
// attempt to open it asymmetrically. If this fails, then
|
||||||
|
// attempt to open it symmetrically.
|
||||||
|
if watcher.KeyAsym != nil {
|
||||||
msg, _ = e.OpenAsymmetric(watcher.KeyAsym)
|
msg, _ = e.OpenAsymmetric(watcher.KeyAsym)
|
||||||
if msg != nil {
|
if msg != nil {
|
||||||
msg.Dst = &watcher.KeyAsym.PublicKey
|
msg.Dst = &watcher.KeyAsym.PublicKey
|
||||||
}
|
}
|
||||||
} else if e.IsSymmetric() {
|
}
|
||||||
|
|
||||||
|
if msg == nil {
|
||||||
msg, _ = e.OpenSymmetric(watcher.KeySym)
|
msg, _ = e.OpenSymmetric(watcher.KeySym)
|
||||||
if msg != nil {
|
if msg != nil {
|
||||||
msg.SymKeyHash = crypto.Keccak256Hash(watcher.KeySym)
|
msg.SymKeyHash = crypto.Keccak256Hash(watcher.KeySym)
|
||||||
|
|
|
||||||
|
|
@ -175,6 +175,9 @@ func (f *Filter) Retrieve() (all []*ReceivedMessage) {
|
||||||
return all
|
return all
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MatchMessage checks if the filter matches an already decrypted
|
||||||
|
// message (i.e. a Message that has already been handled by
|
||||||
|
// MatchEnvelope when checked by a previous filter)
|
||||||
func (f *Filter) MatchMessage(msg *ReceivedMessage) bool {
|
func (f *Filter) MatchMessage(msg *ReceivedMessage) bool {
|
||||||
if f.PoW > 0 && msg.PoW < f.PoW {
|
if f.PoW > 0 && msg.PoW < f.PoW {
|
||||||
return false
|
return false
|
||||||
|
|
@ -188,17 +191,15 @@ func (f *Filter) MatchMessage(msg *ReceivedMessage) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MatchEvelope checks if it's worth decrypting the message. If
|
||||||
|
// it returns `true`, client code is expected to attempt decrypting
|
||||||
|
// the message and subsequently call MatchMessage.
|
||||||
func (f *Filter) MatchEnvelope(envelope *Envelope) bool {
|
func (f *Filter) MatchEnvelope(envelope *Envelope) bool {
|
||||||
if f.PoW > 0 && envelope.pow < f.PoW {
|
if f.PoW > 0 && envelope.pow < f.PoW {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if f.expectsAsymmetricEncryption() && envelope.isAsymmetric() {
|
|
||||||
return f.MatchTopic(envelope.Topic)
|
return f.MatchTopic(envelope.Topic)
|
||||||
} else if f.expectsSymmetricEncryption() && envelope.IsSymmetric() {
|
|
||||||
return f.MatchTopic(envelope.Topic)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Filter) MatchTopic(topic TopicType) bool {
|
func (f *Filter) MatchTopic(topic TopicType) bool {
|
||||||
|
|
|
||||||
|
|
@ -312,12 +312,6 @@ func TestMatchEnvelope(t *testing.T) {
|
||||||
t.Fatalf("failed MatchEnvelope() symmetric with seed %d.", seed)
|
t.Fatalf("failed MatchEnvelope() symmetric with seed %d.", seed)
|
||||||
}
|
}
|
||||||
|
|
||||||
// asymmetric + matching topic: mismatch
|
|
||||||
match = fasym.MatchEnvelope(env)
|
|
||||||
if match {
|
|
||||||
t.Fatalf("failed MatchEnvelope() asymmetric with seed %d.", seed)
|
|
||||||
}
|
|
||||||
|
|
||||||
// symmetric + matching topic + insufficient PoW: mismatch
|
// symmetric + matching topic + insufficient PoW: mismatch
|
||||||
fsym.PoW = env.PoW() + 1.0
|
fsym.PoW = env.PoW() + 1.0
|
||||||
match = fsym.MatchEnvelope(env)
|
match = fsym.MatchEnvelope(env)
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,7 @@ type ReceivedMessage struct {
|
||||||
Payload []byte
|
Payload []byte
|
||||||
Padding []byte
|
Padding []byte
|
||||||
Signature []byte
|
Signature []byte
|
||||||
|
Salt []byte
|
||||||
|
|
||||||
PoW float64 // Proof of work as described in the Whisper spec
|
PoW float64 // Proof of work as described in the Whisper spec
|
||||||
Sent uint32 // Time when the message was posted into the network
|
Sent uint32 // Time when the message was posted into the network
|
||||||
|
|
@ -196,31 +197,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) (nonce []byte, err error) {
|
func (msg *sentMessage) encryptSymmetric(key []byte) (err error) {
|
||||||
if !validateSymmetricKey(key) {
|
if !validateSymmetricKey(key) {
|
||||||
return nil, errors.New("invalid key provided for symmetric encryption")
|
return errors.New("invalid key provided for symmetric encryption")
|
||||||
}
|
}
|
||||||
|
|
||||||
block, err := aes.NewCipher(key)
|
block, err := aes.NewCipher(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
aesgcm, err := cipher.NewGCM(block)
|
aesgcm, err := cipher.NewGCM(block)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return 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())
|
salt := make([]byte, aesgcm.NonceSize())
|
||||||
_, err = crand.Read(nonce)
|
_, err = crand.Read(salt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
} else if !validateSymmetricKey(nonce) {
|
} else if !validateSymmetricKey(salt) {
|
||||||
return nil, errors.New("crypto/rand failed to generate nonce")
|
return errors.New("crypto/rand failed to generate salt")
|
||||||
}
|
}
|
||||||
|
|
||||||
msg.Raw = aesgcm.Seal(nil, nonce, msg.Raw, nil)
|
msg.Raw = append(aesgcm.Seal(nil, salt, msg.Raw, nil), salt...)
|
||||||
return nonce, nil
|
return 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.
|
||||||
|
|
@ -233,11 +234,10 @@ func (msg *sentMessage) Wrap(options *MessageParams) (envelope *Envelope, err er
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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 {
|
||||||
nonce, err = msg.encryptSymmetric(options.KeySym)
|
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")
|
||||||
}
|
}
|
||||||
|
|
@ -245,7 +245,7 @@ func (msg *sentMessage) Wrap(options *MessageParams) (envelope *Envelope, err er
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
envelope = NewEnvelope(options.TTL, options.Topic, nonce, msg)
|
envelope = NewEnvelope(options.TTL, options.Topic, msg)
|
||||||
if err = envelope.Seal(options); err != nil {
|
if err = envelope.Seal(options); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -254,7 +254,14 @@ 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, nonce []byte) error {
|
func (msg *ReceivedMessage) decryptSymmetric(key []byte) error {
|
||||||
|
// In v6, symmetric messages are expected to contain the 12-byte
|
||||||
|
// "salt" at the end of the payload.
|
||||||
|
if len(msg.Raw) < AESNonceLength {
|
||||||
|
return errors.New("missing salt or invalid payload in symmetric message")
|
||||||
|
}
|
||||||
|
salt := msg.Raw[len(msg.Raw)-AESNonceLength:]
|
||||||
|
|
||||||
block, err := aes.NewCipher(key)
|
block, err := aes.NewCipher(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -263,15 +270,16 @@ func (msg *ReceivedMessage) decryptSymmetric(key []byte, nonce []byte) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if len(nonce) != aesgcm.NonceSize() {
|
if len(salt) != aesgcm.NonceSize() {
|
||||||
log.Error("decrypting the message", "AES nonce size", len(nonce))
|
log.Error("decrypting the message", "AES salt size", len(salt))
|
||||||
return errors.New("wrong AES nonce size")
|
return errors.New("wrong AES salt size")
|
||||||
}
|
}
|
||||||
decrypted, err := aesgcm.Open(nil, nonce, msg.Raw, nil)
|
decrypted, err := aesgcm.Open(nil, salt, msg.Raw[:len(msg.Raw)-AESNonceLength], nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
msg.Raw = decrypted
|
msg.Raw = decrypted
|
||||||
|
msg.Salt = salt
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -174,10 +174,8 @@ func TestMessageSeal(t *testing.T) {
|
||||||
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
||||||
}
|
}
|
||||||
params.TTL = 1
|
params.TTL = 1
|
||||||
aesnonce := make([]byte, 12)
|
|
||||||
mrand.Read(aesnonce)
|
|
||||||
|
|
||||||
env := NewEnvelope(params.TTL, params.Topic, aesnonce, msg)
|
env := NewEnvelope(params.TTL, params.Topic, 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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -591,13 +591,6 @@ 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())
|
||||||
}
|
}
|
||||||
|
|
||||||
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() {
|
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
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue