@ -25,6 +25,7 @@ import (
crand "crypto/rand"
"encoding/binary"
"errors"
mrand "math/rand"
"strconv"
"github.com/ethereum/go-ethereum/common"
@ -55,7 +56,7 @@ type sentMessage struct {
}
// ReceivedMessage represents a data packet to be received through the
// Whisper protocol.
// Whisper protocol and successfully decrypted .
type ReceivedMessage struct {
Raw [ ] byte
@ -71,7 +72,7 @@ type ReceivedMessage struct {
Dst * ecdsa . PublicKey // Message recipient (identity used to decode the message)
Topic TopicType
SymKeyHash common . Hash // The Keccak256Hash of the key, associated with the Topic
SymKeyHash common . Hash // The Keccak256Hash of the key
EnvelopeHash common . Hash // Message envelope hash to act as a unique id
}
@ -89,81 +90,60 @@ func (msg *ReceivedMessage) isAsymmetricEncryption() bool {
// NewSentMessage creates and initializes a non-signed, non-encrypted Whisper message.
func newSentMessage ( params * MessageParams ) ( * sentMessage , error ) {
const payloadSizeFieldMaxSize = 4
msg := sentMessage { }
msg . Raw = make ( [ ] byte , 1 , len ( params . Payload ) + len ( params . Padding ) + signatureLength + padSizeLimit )
msg . Raw = make ( [ ] byte , 1 ,
flagsLength + payloadSizeFieldMaxSize + 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 {
return nil , err
}
msg . addPayloadSizeField ( params . Payload )
msg . Raw = append ( msg . Raw , params . Payload ... )
return & msg , nil
err := msg . appendPadding ( params )
return & msg , err
}
// 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
// addPayloadSizeField appends the auxiliary field containing the size of payload
func ( msg * sentMessage ) addPayloadSizeField ( payload [ ] byte ) {
fieldSize := getSizeOfPayloadSizeField ( payload )
field := make ( [ ] byte , 4 )
binary . LittleEndian . PutUint32 ( field , uint32 ( len ( payload ) ) )
field = field [ : fieldSize ]
msg . Raw = append ( msg . Raw , field ... )
msg . Raw [ 0 ] |= byte ( fieldSize )
}
// 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
// getSizeOfPayloadSizeField returns the number of bytes necessary to encode the size of payload
func getSizeOfPayloadSizeField ( payload [ ] byte ) int {
s := 1
for i := len ( payload ) ; i >= 256 ; i /= 256 {
s ++
}
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) .
// appendPadding appends the padding specified in params .
// If no padding is provided in params, then random padding is generated .
func ( msg * sentMessage ) appendPadding ( params * MessageParams ) error {
rawSize := len ( params . Payload ) + 1
if params . Src != nil {
rawSize += signatureLength
if len ( params . Padding ) != 0 {
// padding data was provided by the Dapp, just use it as is
msg . Raw = append ( msg . Raw , params . Padding ... )
return nil
}
if params . KeySym != nil {
rawSize += AESNonceLength
rawSize := flagsLength + getSizeOfPayloadSizeField ( params . Payload ) + len ( params . Payload )
if params . Src != nil {
rawSize += signatureLength
}
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 , totalPadSize )
_ , err := crand . Read ( buf [ 1 : ] )
paddingSize := padSizeLimit - odd
pad := make ( [ ] byte , paddingSize )
_ , err := crand . Read ( pad )
if err != nil {
return err
}
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
if ! validateDataIntegrity ( pad , paddingSize ) {
return errors . New ( "failed to generate random padding of size " + strconv . Itoa ( paddingSize ) )
}
msg . Raw = append ( msg . Raw , pad ... )
return nil
}
@ -176,11 +156,11 @@ func (msg *sentMessage) sign(key *ecdsa.PrivateKey) error {
return nil
}
msg . Raw [ 0 ] |= signatureFlag
msg . Raw [ 0 ] |= signatureFlag // it is important to set this flag before signing
hash := crypto . Keccak256 ( msg . Raw )
signature , err := crypto . Sign ( hash , key )
if err != nil {
msg . Raw [ 0 ] &= ^ signatureFlag // clear the flag
msg . Raw [ 0 ] &= ( 0xFF ^ signatureFlag ) // clear the flag
return err
}
msg . Raw = append ( msg . Raw , signature ... )
@ -202,10 +182,9 @@ 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 ) ( err error ) {
if ! validateSymmetricKe y ( key ) {
return errors . New ( "invalid key provided for symmetric encryption" )
if ! validateDataIntegrit y ( key , aesKeyLength ) {
return errors . New ( "invalid key provided for symmetric encryption, size: " + strconv . Itoa ( len ( key ) ) )
}
block , err := aes . NewCipher ( key )
if err != nil {
return err
@ -214,20 +193,46 @@ func (msg *sentMessage) encryptSymmetric(key []byte) (err error) {
if err != nil {
return err
}
// never use more than 2^32 random nonces with a given key
salt := make ( [ ] byte , aesgcm . NonceSize ( ) )
_ , err = crand . Read ( salt )
salt , err := generateSecureRandomData ( aesNonceLength ) // never use more than 2^32 random nonces with a given key
if err != nil {
return err
} else if ! validateSymmetricKey ( salt ) {
return errors . New ( "crypto/rand failed to generate salt" )
}
msg . Raw = append ( aesgcm . Seal ( nil , salt , msg . Raw , nil ) , salt ... )
encrypted := aesgcm . Seal ( nil , salt , msg . Raw , nil )
msg . Raw = append ( encrypted , salt ... )
return nil
}
// generateSecureRandomData generates random data where extra security is required.
// The purpose of this function is to prevent some bugs in software or in hardware
// from delivering not-very-random data. This is especially useful for AES nonce,
// where true randomness does not really matter, but it is very important to have
// a unique nonce for every message.
func generateSecureRandomData ( length int ) ( [ ] byte , error ) {
x := make ( [ ] byte , length )
y := make ( [ ] byte , length )
res := make ( [ ] byte , length )
_ , err := crand . Read ( x )
if err != nil {
return nil , err
} else if ! validateDataIntegrity ( x , length ) {
return nil , errors . New ( "crypto/rand failed to generate secure random data" )
}
_ , err = mrand . Read ( y )
if err != nil {
return nil , err
} else if ! validateDataIntegrity ( y , length ) {
return nil , errors . New ( "math/rand failed to generate secure random data" )
}
for i := 0 ; i < length ; i ++ {
res [ i ] = x [ i ] ^ y [ i ]
}
if ! validateDataIntegrity ( res , length ) {
return nil , errors . New ( "failed to generate secure random data" )
}
return res , nil
}
// Wrap bundles the message into an Envelope to transmit over the network.
func ( msg * sentMessage ) Wrap ( options * MessageParams ) ( envelope * Envelope , err error ) {
if options . TTL == 0 {
@ -259,12 +264,11 @@ 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 ) error {
// In v6, symmetric messages are expected to contain the 12-byte
// "salt" at the end of the payload.
if len ( msg . Raw ) < AESNonceLength {
// symmetric messages are expected to contain the 12-byte nonce 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 ) - AES NonceLength: ]
salt := msg . Raw [ len ( msg . Raw ) - aes NonceLength: ]
block , err := aes . NewCipher ( key )
if err != nil {
@ -274,11 +278,7 @@ func (msg *ReceivedMessage) decryptSymmetric(key []byte) error {
if err != nil {
return err
}
if len ( salt ) != aesgcm . NonceSize ( ) {
log . Error ( "decrypting the message" , "AES salt size" , len ( salt ) )
return errors . New ( "wrong AES salt size" )
}
decrypted , err := aesgcm . Open ( nil , salt , msg . Raw [ : len ( msg . Raw ) - AESNonceLength ] , nil )
decrypted , err := aesgcm . Open ( nil , salt , msg . Raw [ : len ( msg . Raw ) - aesNonceLength ] , nil )
if err != nil {
return err
}
@ -296,8 +296,8 @@ func (msg *ReceivedMessage) decryptAsymmetric(key *ecdsa.PrivateKey) error {
return err
}
// Validate checks the validity and extracts the fields in case of success
func ( msg * ReceivedMessage ) Validate ( ) bool {
// ValidateAndParse checks the messag e validity and extracts the fields in case of success.
func ( msg * ReceivedMessage ) ValidateAndParse ( ) bool {
end := len ( msg . Raw )
if end < 1 {
return false
@ -308,40 +308,30 @@ func (msg *ReceivedMessage) Validate() bool {
if end <= 1 {
return false
}
msg . Signature = msg . Raw [ end : ]
msg . Signature = msg . Raw [ end : end + signatureLength ]
msg . Src = msg . SigToPubKey ( )
if msg . Src == nil {
return false
}
}
padSize , ok := msg . extractPadding ( end )
if ! ok {
beg := 1
payloadSize := 0
sizeOfPayloadSizeField := int ( msg . Raw [ 0 ] & SizeMask ) // number of bytes indicating the size of payload
if sizeOfPayloadSizeField != 0 {
payloadSize = int ( bytesToUintLittleEndian ( msg . Raw [ beg : beg + sizeOfPayloadSizeField ] ) )
if payloadSize + 1 > end {
return false
}
beg += sizeOfPayloadSizeField
msg . Payload = msg . Raw [ beg : beg + payloadSize ]
}
msg . Payload = msg . Raw [ 1 + padSize : end ]
beg += payloadSize
msg . Padding = msg . Raw [ beg : end ]
return true
}
// extractPadding extracts the padding from raw message.
// although we don't support sending messages with padding size
// exceeding 255 bytes, such messages are perfectly valid, and
// can be successfully decrypted.
func ( msg * ReceivedMessage ) extractPadding ( end int ) ( int , bool ) {
paddingSize := 0
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 {
return 0 , false
}
msg . Padding = msg . Raw [ 1 + sz : 1 + paddingSize ]
}
return paddingSize , true
}
// SigToPubKey returns the public key associated to the message's
// signature.
func ( msg * ReceivedMessage ) SigToPubKey ( ) * ecdsa . PublicKey {
@ -355,7 +345,7 @@ func (msg *ReceivedMessage) SigToPubKey() *ecdsa.PublicKey {
return pub
}
// hash calculates the SHA3 checksum of the message flags, payload and padding.
// hash calculates the SHA3 checksum of the message flags, payload size field, payload and padding.
func ( msg * ReceivedMessage ) hash ( ) [ ] byte {
if isMessageSigned ( msg . Raw [ 0 ] ) {
sz := len ( msg . Raw ) - signatureLength