diff --git a/pss/api.go b/pss/api.go index b5a0145d5e..9370b4a720 100644 --- a/pss/api.go +++ b/pss/api.go @@ -117,13 +117,13 @@ func (pssapi *API) BaseAddr() (PssAddress, error) { // Retrieves the node's public key in hex form func (pssapi *API) GetPublicKey() (keybytes hexutil.Bytes) { key := pssapi.Pss.PublicKey() - keybytes = pssapi.Pss.Crypto.FromECDSAPub(key) + keybytes = pssapi.Pss.Crypto.SerializePublicKey(key) return keybytes } // Set Public key to associate with a particular Pss peer func (pssapi *API) SetPeerPublicKey(pubkey hexutil.Bytes, topic Topic, addr PssAddress) error { - pk, err := pssapi.Pss.Crypto.UnmarshalPubkey(pubkey) + pk, err := pssapi.Pss.Crypto.UnmarshalPublicKey(pubkey) if err != nil { return fmt.Errorf("Cannot unmarshal pubkey: %x", pubkey) } diff --git a/pss/client/client_test.go b/pss/client/client_test.go index f53c516f1c..62cf51b604 100644 --- a/pss/client/client_test.go +++ b/pss/client/client_test.go @@ -25,6 +25,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common/hexutil" + ethCrypto "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" @@ -45,7 +46,6 @@ type protoCtrl struct { } var ( - cryptoUtils pss.CryptoUtils // custom logging psslogmain log.Logger pssprotocols map[string]*protoCtrl @@ -62,8 +62,6 @@ func init() { psslogmain = log.New("psslog", "*") - cryptoUtils = pss.NewCryptoUtils() - pssprotocols = make(map[string]*protoCtrl) } @@ -231,13 +229,7 @@ func newServices() adapters.Services { } return adapters.Services{ "pss": func(ctx *adapters.ServiceContext) (node.Service, error) { - ctxlocal, cancel := context.WithTimeout(context.Background(), time.Second) - defer cancel() - keys, err := cryptoUtils.NewKeyPair(ctxlocal) - if err != nil { - return nil, err - } - privkey, err := cryptoUtils.GetPrivateKey(keys) + privkey, err := ethCrypto.GenerateKey() if err != nil { return nil, err } diff --git a/pss/crypto.go b/pss/crypto.go deleted file mode 100644 index 4951249f52..0000000000 --- a/pss/crypto.go +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2019 The Swarm Authors -// This file is part of the Swarm library. -// -// The Swarm library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The Swarm library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the Swarm library. If not, see . -package pss - -import ( - "context" - "crypto/ecdsa" - - ethCrypto "github.com/ethereum/go-ethereum/crypto" - whisper "github.com/ethereum/go-ethereum/whisper/whisperv6" -) - -var cryptoBackend defaultCryptoBackend - -type CryptoBackend interface { - GetSymKey(id string) ([]byte, error) - GenerateSymKey() (string, error) - AddSymKeyDirect(bytes []byte) (string, error) - FromECDSAPub(pub *ecdsa.PublicKey) []byte - UnmarshalPubkey(pub []byte) (*ecdsa.PublicKey, error) - CompressPubkey(pubkey *ecdsa.PublicKey) []byte -} - -//Used only in tests -type CryptoUtils interface { - GenerateKey() (*ecdsa.PrivateKey, error) - NewKeyPair(ctx context.Context) (string, error) - GetPrivateKey(id string) (*ecdsa.PrivateKey, error) -} - -type defaultCryptoBackend struct { - whisper *whisper.Whisper - wapi *whisper.PublicWhisperAPI -} - -func NewCryptoBackend() CryptoBackend { - w := whisper.New(&whisper.DefaultConfig) - cryptoBackend = defaultCryptoBackend{ - whisper: w, - wapi: whisper.NewPublicWhisperAPI(w), - } - return &cryptoBackend -} - -func NewCryptoUtils() CryptoUtils { - if cryptoBackend.whisper == nil { - NewCryptoBackend() - } - return &cryptoBackend -} - -func (crypto *defaultCryptoBackend) GetSymKey(id string) ([]byte, error) { - return crypto.whisper.GetSymKey(id) -} - -func (crypto *defaultCryptoBackend) GenerateSymKey() (string, error) { - return crypto.whisper.GenerateSymKey() -} - -func (crypto *defaultCryptoBackend) AddSymKeyDirect(bytes []byte) (string, error) { - return crypto.whisper.AddSymKeyDirect(bytes) -} - -// FromECDSA exports a public key into a binary dump. -func (crypto *defaultCryptoBackend) FromECDSAPub(pub *ecdsa.PublicKey) []byte { - return ethCrypto.FromECDSAPub(pub) -} - -// CompressPubkey encodes a public key to the 33-byte compressed format. -func (crypto *defaultCryptoBackend) CompressPubkey(pubkey *ecdsa.PublicKey) []byte { - return ethCrypto.CompressPubkey(pubkey) -} - -// UnmarshalPubkey converts bytes to a secp256k1 public key. -func (crypto *defaultCryptoBackend) UnmarshalPubkey(pub []byte) (*ecdsa.PublicKey, error) { - return ethCrypto.UnmarshalPubkey(pub) -} - -// CryptoUtils - -func (crypto *defaultCryptoBackend) GenerateKey() (*ecdsa.PrivateKey, error) { - return ethCrypto.GenerateKey() -} - -func (crypto *defaultCryptoBackend) NewKeyPair(ctx context.Context) (string, error) { - return crypto.wapi.NewKeyPair(ctx) -} - -func (crypto *defaultCryptoBackend) GetPrivateKey(id string) (*ecdsa.PrivateKey, error) { - return crypto.whisper.GetPrivateKey(id) -} diff --git a/pss/crypto/crypto.go b/pss/crypto/crypto.go new file mode 100644 index 0000000000..81982d7f2e --- /dev/null +++ b/pss/crypto/crypto.go @@ -0,0 +1,608 @@ +// Copyright 2019 The Swarm Authors +// This file is part of the Swarm library. +// +// The Swarm library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The Swarm library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the Swarm library. If not, see . +package crypto + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/ecdsa" + crand "crypto/rand" + "encoding/binary" + "errors" + "fmt" + mrand "math/rand" + "strconv" + "sync" + + "github.com/ethereum/go-ethereum/common" + ethCrypto "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/crypto/ecies" + "github.com/ethersphere/swarm/log" +) + +var createPadding = true + +const ( + aesNonceLength = 12 // in bytes; for more info please see cipher.gcmStandardNonceSize & aesgcm.NonceSize() + keyIDSize = 32 // in bytes + + flagsLength = 1 + payloadSizeFieldMaxSize = 4 + signatureLength = 65 // in bytes + padSizeLimit = 256 // just an arbitrary number, could be changed without breaking the protocol + SizeMask = byte(3) // mask used to extract the size of payload size field from the flags + signatureFlag = byte(4) + aesKeyLength = 32 // in bytes + + defaultPaddingByteSize = 16 +) + +// Config params to wrap and encrypt a message. +// For asymmetric encryption Receiver is needed. +// For symmetric, SymmetricKey is needed. Sender is not mandatory but used to sign the message in both schemes. +type WrapParams struct { + Sender *ecdsa.PrivateKey // Private key of sender used for signature + Receiver *ecdsa.PublicKey // Public key of receiver for encryption + SymmetricKey []byte // Symmetric key for encryption +} + +// Config params to unwrap and decrypt a message. +// For asymmetric encryption Receiver is needed. +// For symmetric, SymmetricKey is needed. Sender is not mandatory but used to sign the message in both schemes. +type UnwrapParams struct { + Sender *ecdsa.PublicKey // Public key of sender used for signature validation + Receiver *ecdsa.PrivateKey // Private key of receiver for decryption + SymmetricKey []byte // Symmetric key for decryption +} + +// Contains a successfully decrypted message prior to parsing and validating +type ReceivedMessage interface { + GetPayload() ([]byte, error) + GetSender() *ecdsa.PublicKey +} + +// Crypto contains methods from Message and KeyStore +type Crypto interface { + Message + KeyStore +} + +// Message contains methods for wrapping(encrypting) and unwrapping(decrypting) messages +type Message interface { + Wrap(plaintext []byte, params *WrapParams) (data []byte, err error) + UnWrap(ciphertext []byte, unwrapParams *UnwrapParams) (ReceivedMessage, error) +} + +// KeyStore contains key manipulation methods +type KeyStore interface { + + // Symmetric key management + GetSymmetricKey(id string) ([]byte, error) + GenerateSymmetricKey() (string, error) + AddSymmetricKey(bytes []byte) (string, error) + + // Key serialization + SerializePublicKey(pub *ecdsa.PublicKey) []byte + UnmarshalPublicKey(pub []byte) (*ecdsa.PublicKey, error) + CompressPublicKey(pub *ecdsa.PublicKey) []byte +} + +var ( + errInvalidPubkey = errors.New("invalid public key provided for asymmetric encryption") + errInvalidSymkey = errors.New("invalid key provided for symmetric encryption") + errMissingSaltOrInvalidPayload = errors.New("missing salt or invalid payload in symmetric message") + errSecureRandomData = errors.New("failed to generate secure random data") + errNoKey = errors.New("unable to encrypt the message: neither symmetric nor asymmetric key provided") + + // Validation and Parse errors + errEmptyMessage = errors.New("empty message") + errEmptySignature = errors.New("empty expected signature") + errIncorrectSignature = errors.New("incorrect signature") + errIncorrectSize = errors.New("incorrect payload size") +) + +type defaultCryptoBackend struct { + symKeys map[string][]byte // Symmetric key storage + keyMu sync.RWMutex // Mutex associated with key storage +} + +// receivedMessage represents a data packet to be received +// and successfully decrypted. +type receivedMessage struct { + Payload []byte // Parsed and validated message content + Raw []byte // Unparsed but decrypted message content + Signature []byte // Signature from the sender + Salt []byte // Protect against plaintext attacks + Padding []byte // Padding applied + + Sender *ecdsa.PublicKey // Source public key used for signing the message + + validateOnce sync.Once + validateError error + + crypto *defaultCryptoBackend +} + +// Returns the sender public key of the message +func (msg *receivedMessage) GetSender() *ecdsa.PublicKey { + msg.validateOnce.Do( + func() { + msg.validateError = msg.validateAndParse() + }) + return msg.Sender +} + +// GetPayload validate and parse the payload of the message. +func (msg *receivedMessage) GetPayload() ([]byte, error) { + msg.validateOnce.Do( + func() { + msg.validateError = msg.validateAndParse() + }) + return msg.Payload, msg.validateError +} + +// validateAndParse checks that the format and the signature are correct. It also set Payload as the parsed message +func (msg *receivedMessage) validateAndParse() error { + end := len(msg.Raw) + if end == 0 { + return errEmptyMessage + } + if isMessageSigned(msg.Raw[0]) { + end -= signatureLength + if end <= 1 { + return errEmptySignature + } + msg.Signature = msg.Raw[end : end+signatureLength] + sz := len(msg.Raw) - signatureLength + pub, err := msg.crypto.sigToPub(msg.Raw[:sz], msg.Signature) + if err != nil { + log.Error("failed to recover public key from signature", "err", err) + return errIncorrectSignature + } else { + msg.Sender = pub + } + } + + beg := 1 + payloadSize := 0 + sizeOfPayloadSizeField := int(msg.Raw[0] & SizeMask) // number of bytes indicating the size of payload + if sizeOfPayloadSizeField != 0 { + log.Warn("Size of payload field", "size", sizeOfPayloadSizeField) + payloadSize = int(bytesToUintLittleEndian(msg.Raw[beg : beg+sizeOfPayloadSizeField])) + if payloadSize+1 > end { + return errIncorrectSize + } + beg += sizeOfPayloadSizeField + msg.Payload = msg.Raw[beg : beg+payloadSize] + } + + beg += payloadSize + msg.Padding = msg.Raw[beg:end] + return nil +} + +func newReceivedMessage(decrypted []byte, salt []byte, crypto *defaultCryptoBackend) *receivedMessage { + return &receivedMessage{ + Raw: decrypted, + Salt: salt, + crypto: crypto, + validateOnce: sync.Once{}, + } +} + +// Return the default implementation of Crypto +func New() *defaultCryptoBackend { + return newDefaultCryptoBackend() + +} + +func newDefaultCryptoBackend() *defaultCryptoBackend { + return &defaultCryptoBackend{ + symKeys: make(map[string][]byte), + } +} + +// == Message Crypto == + +// Wrap creates a message adding signature, padding and other control fields and then it is encrypted using params +func (crypto *defaultCryptoBackend) Wrap(plaintext []byte, params *WrapParams) (data []byte, err error) { + var padding []byte + if createPadding { + padding, err = generateSecureRandomData(defaultPaddingByteSize) + if err != nil { + return + } + } else { + padding = make([]byte, 0) + } + // Message structure is flags+PayloadSize+Payload+padding+signature + rawBytes := make([]byte, 1, + flagsLength+payloadSizeFieldMaxSize+len(plaintext)+len(padding)+signatureLength+padSizeLimit) + // set flags byte + rawBytes[0] = 0 // set all the flags to zero + // add payloadSizeField + rawBytes = crypto.addPayloadSizeField(rawBytes, plaintext) + // add payload + rawBytes = append(rawBytes, plaintext...) + // add padding + rawBytes = append(rawBytes, padding...) + // sign + if params.Sender != nil { + if rawBytes, err = crypto.sign(rawBytes, params.Sender); err != nil { + return + } + } + // encrypt + if params.Receiver != nil { + rawBytes, err = crypto.encryptAsymmetric(rawBytes, params.Receiver) + } else if params.SymmetricKey != nil { + rawBytes, err = crypto.encryptSymmetric(rawBytes, params.SymmetricKey) + } else { + err = errNoKey + } + if err != nil { + return + } + + data = rawBytes + return +} + +// Unwrap decrypts and compose a received message ready to be parsed and validated. +// It selects symmetric/asymmetric crypto depending on unwrapParams +func (crypto *defaultCryptoBackend) UnWrap(ciphertext []byte, unwrapParams *UnwrapParams) (ReceivedMessage, error) { + if unwrapParams.SymmetricKey != nil { + return crypto.unWrapSymmetric(ciphertext, unwrapParams.SymmetricKey) + } else if unwrapParams.Receiver != nil { + return crypto.unWrapAsymmetric(ciphertext, unwrapParams.Receiver) + } else { + return nil, errNoKey + } +} + +func (crypto *defaultCryptoBackend) unWrapSymmetric(ciphertext, key []byte) (ReceivedMessage, error) { + decrypted, salt, err := crypto.decryptSymmetric(ciphertext, key) + if err != nil { + return nil, err + } + msg := newReceivedMessage(decrypted, salt, crypto) + return msg, err +} + +func (crypto *defaultCryptoBackend) unWrapAsymmetric(ciphertext []byte, key *ecdsa.PrivateKey) (ReceivedMessage, error) { + plaintext, err := crypto.decryptAsymmetric(ciphertext, key) + switch err { + case nil: + message := newReceivedMessage(plaintext, nil, crypto) + return message, nil + case ecies.ErrInvalidPublicKey: // addressed to somebody else + return nil, err + default: + return nil, fmt.Errorf("unable to open envelope, decrypt failed: %v", err) + } +} + +func (crypto *defaultCryptoBackend) addPayloadSizeField(rawBytes []byte, payload []byte) []byte { + fieldSize := getSizeOfPayloadSizeField(payload) + field := make([]byte, 4) + binary.LittleEndian.PutUint32(field, uint32(len(payload))) + field = field[:fieldSize] + rawBytes = append(rawBytes, field...) + rawBytes[0] |= byte(fieldSize) + return rawBytes +} + +// pad appends the padding specified in params. +func (crypto *defaultCryptoBackend) pad(rawBytes, payload []byte, signed bool) ([]byte, error) { + rawSize := flagsLength + getSizeOfPayloadSizeField(payload) + len(payload) + if signed { + rawSize += signatureLength + } + odd := rawSize % padSizeLimit + paddingSize := padSizeLimit - odd + pad := make([]byte, paddingSize) + _, err := crand.Read(pad) + if err != nil { + return nil, err + } + + if len(pad) != paddingSize || containsOnlyZeros(pad) { + return nil, errors.New("failed to generate random padding of size " + strconv.Itoa(paddingSize)) + } + rawBytes = append(rawBytes, pad...) + return rawBytes, nil +} + +// sign calculates and sets the cryptographic signature for the message, +// also setting the sign flag. +func (crypto *defaultCryptoBackend) sign(rawBytes []byte, key *ecdsa.PrivateKey) ([]byte, error) { + if isMessageSigned(rawBytes[0]) { + // this should not happen, but no reason to panic + log.Error("failed to sign the message: already signed") + return rawBytes, nil + } + rawBytes[0] |= signatureFlag // it is important to set this flag before signing + + hash := ethCrypto.Keccak256(rawBytes) + signature, err := crypto.signHash(hash, key) + + if err != nil { + rawBytes[0] &= (0xFF ^ signatureFlag) // clear the flag + return nil, err + } + rawBytes = append(rawBytes, signature...) + return rawBytes, nil +} + +func isMessageSigned(flags byte) bool { + return (flags & signatureFlag) != 0 +} + +// === Key store functions === + +// GetSymmetricKey retrieves symmetric key by id from the store +func (crypto *defaultCryptoBackend) GetSymmetricKey(id string) ([]byte, error) { + crypto.keyMu.RLock() + defer crypto.keyMu.RUnlock() + if crypto.symKeys[id] != nil { + return crypto.symKeys[id], nil + } + return nil, fmt.Errorf("non-existent key ID") +} + +// GenerateSymmetricKey creates a new symmetric, stores it and return its id +func (crypto *defaultCryptoBackend) GenerateSymmetricKey() (string, error) { + key, err := generateSecureRandomData(aesKeyLength) + if err != nil { + return "", err + } else if !validateDataIntegrity(key, aesKeyLength) { + return "", fmt.Errorf("error in GenerateSymmetricKey: crypto/rand failed to generate random data") + } + + id, err := generateRandomKeyID() + if err != nil { + return "", fmt.Errorf("failed to generate ID: %s", err) + } + + crypto.keyMu.Lock() + defer crypto.keyMu.Unlock() + + if crypto.symKeys[id] != nil { + return "", fmt.Errorf("failed to generate unique ID") + } + crypto.symKeys[id] = key + return id, nil +} + +// Add a symmetric key to the store generating an id and returning it +func (crypto *defaultCryptoBackend) AddSymmetricKey(key []byte) (string, error) { + if len(key) != aesKeyLength { + return "", fmt.Errorf("wrong key size: %d", len(key)) + } + + id, err := generateRandomKeyID() + if err != nil { + return "", fmt.Errorf("failed to generate ID: %s", err) + } + + crypto.keyMu.Lock() + defer crypto.keyMu.Unlock() + + if crypto.symKeys[id] != nil { + return "", fmt.Errorf("failed to generate unique ID") + } + crypto.symKeys[id] = key + return id, nil +} + +// === Key conversion === + +// FromECDSA exports a public key into a binary dump. +func (crypto *defaultCryptoBackend) SerializePublicKey(pub *ecdsa.PublicKey) []byte { + return ethCrypto.FromECDSAPub(pub) +} + +// === Key serialization === + +// UnmarshalPublicKey converts bytes to a secp256k1 public key. +func (crypto *defaultCryptoBackend) UnmarshalPublicKey(pub []byte) (*ecdsa.PublicKey, error) { + return ethCrypto.UnmarshalPubkey(pub) +} + +// CompressPublicKey encodes a public key to the 33-byte compressed format. +func (crypto *defaultCryptoBackend) CompressPublicKey(pubkey *ecdsa.PublicKey) []byte { + return ethCrypto.CompressPubkey(pubkey) +} + +// == private methods == + +// === Encrypt-Decrypt === + +// decryptSymmetric decrypts a message with a topic key, using AES-GCM-256. +// nonce size should be 12 bytes (see cipher.gcmStandardNonceSize). +func (crypto *defaultCryptoBackend) decryptSymmetric(rawBytes []byte, key []byte) (decrypted []byte, salt []byte, err error) { + // symmetric messages are expected to contain the 12-byte nonce at the end of the payload + if len(rawBytes) < aesNonceLength { + return nil, nil, errMissingSaltOrInvalidPayload + } + salt = rawBytes[len(rawBytes)-aesNonceLength:] + + block, err := aes.NewCipher(key) + if err != nil { + return nil, nil, err + } + aesgcm, err := cipher.NewGCM(block) + if err != nil { + return nil, nil, err + } + decrypted, err = aesgcm.Open(nil, salt, rawBytes[:len(rawBytes)-aesNonceLength], nil) + if err != nil { + return nil, nil, err + } + return +} + +// encryptAsymmetric encrypts a message with a public key. +func (crypto *defaultCryptoBackend) encryptAsymmetric(rawBytes []byte, key *ecdsa.PublicKey) ([]byte, error) { + if !validatePublicKey(key) { + return nil, errInvalidPubkey + } + encrypted, err := ecies.Encrypt(crand.Reader, crypto.fromECDSAtoECIESPublic(key), rawBytes, nil, nil) + if err == nil { + return encrypted, nil + } + return nil, err +} + +func (crypto *defaultCryptoBackend) decryptAsymmetric(rawBytes []byte, key *ecdsa.PrivateKey) ([]byte, error) { + return ecies.ImportECDSA(key).Decrypt(rawBytes, nil, nil) +} + +func (crypto *defaultCryptoBackend) encryptSymmetric(rawBytes []byte, key []byte) ([]byte, error) { + if !validateDataIntegrity(key, aesKeyLength) { + return nil, errInvalidSymkey + } + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + aesgcm, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + salt, err := generateSecureRandomData(aesNonceLength) // never use more than 2^32 random nonces with a given key + if err != nil { + return nil, err + } + encrypted := aesgcm.Seal(nil, salt, rawBytes, nil) + encBytes := append(encrypted, salt...) + return encBytes, nil +} + +// signHash calculates an ECDSA signature. +func (crypto *defaultCryptoBackend) signHash(hash []byte, prv *ecdsa.PrivateKey) (sig []byte, err error) { + return ethCrypto.Sign(hash, prv) +} + +// sigToPub obtains public key from the signed message and the signature +func (crypto *defaultCryptoBackend) sigToPub(signed, sig []byte) (*ecdsa.PublicKey, error) { + defer func() { recover() }() // in case of invalid signature + hash := ethCrypto.Keccak256(signed) + return ethCrypto.SigToPub(hash, sig) +} + +// fromECDSAtoECIESPublic converts a ecdsa public key to the format needed by the crypti/ecies package +func (crypto *defaultCryptoBackend) fromECDSAtoECIESPublic(key *ecdsa.PublicKey) *ecies.PublicKey { + return ecies.ImportECDSAPublic(key) +} + +// generateRandomKeyID generates a random string, which is then returned to be used as a key id +func generateRandomKeyID() (id string, err error) { + buf, err := generateSecureRandomData(keyIDSize) + if err != nil { + return "", err + } + if !validateDataIntegrity(buf, keyIDSize) { + return "", fmt.Errorf("error in generateRandomKeyID: crypto/rand failed to generate random data") + } + id = common.Bytes2Hex(buf) + return id, err +} + +// 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, errSecureRandomData + } + _, err = mrand.Read(y) + if err != nil { + return nil, err + } else if !validateDataIntegrity(y, length) { + return nil, errSecureRandomData + } + for i := 0; i < length; i++ { + res[i] = x[i] ^ y[i] + } + if !validateDataIntegrity(res, length) { + return nil, errSecureRandomData + } + return res, nil +} + +// validateDataIntegrity returns false if the data have the wrong size or contains all zeros, +// which is the simplest and the most common bug. +func validateDataIntegrity(k []byte, expectedSize int) bool { + if len(k) != expectedSize { + return false + } + if expectedSize > 3 && containsOnlyZeros(k) { + return false + } + return true +} + +// validatePrivateKey checks the format of the given private key. +func validatePrivateKey(k *ecdsa.PrivateKey) bool { + if k == nil || k.D == nil || k.D.Sign() == 0 { + return false + } + return validatePublicKey(&k.PublicKey) +} + +// ValidatePublicKey checks the format of the given public key. +func validatePublicKey(k *ecdsa.PublicKey) bool { + return k != nil && k.X != nil && k.Y != nil && k.X.Sign() != 0 && k.Y.Sign() != 0 +} + +// containsOnlyZeros checks if the data contain only zeros. +func containsOnlyZeros(data []byte) bool { + for _, b := range data { + if b != 0 { + return false + } + } + return true +} + +// bytesToUintLittleEndian converts the slice to 64-bit unsigned integer. +func bytesToUintLittleEndian(b []byte) (res uint64) { + mul := uint64(1) + for i := 0; i < len(b); i++ { + res += uint64(b[i]) * mul + mul *= 256 + } + return res +} + +// 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 +} diff --git a/pss/forwarding_test.go b/pss/forwarding_test.go index c79da8e9b9..af7d126271 100644 --- a/pss/forwarding_test.go +++ b/pss/forwarding_test.go @@ -6,6 +6,7 @@ import ( "testing" "time" + ethCrypto "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethersphere/swarm/network" @@ -24,8 +25,6 @@ type testCase struct { errors string } -var crypto CryptoUtils = NewCryptoUtils() - // the purpose of this test is to see that pss.forward() function correctly // selects the peers for message forwarding, depending on the message address // and kademlia constellation. @@ -324,7 +323,7 @@ func addPeers(kad *network.Kademlia, addresses []pot.Address) { } func createPss(t *testing.T, kad *network.Kademlia) *Pss { - privKey, err := crypto.GenerateKey() + privKey, err := ethCrypto.GenerateKey() pssp := NewParams().WithPrivateKey(privKey) ps, err := New(kad, pssp) if err != nil { @@ -351,9 +350,7 @@ func newTestMsg(addr []byte) *PssMsg { msg := newPssMsg(&msgParams{}) msg.To = addr[:] msg.Expire = uint32(time.Now().Add(time.Second * 60).Unix()) - msg.Payload = &envelope{ - Topic: [4]byte{}, - Data: []byte("i have nothing to hide"), - } + msg.Topic = [4]byte{} + msg.Payload = []byte("i have nothing to hide") return msg } diff --git a/pss/handshake.go b/pss/handshake.go index da28f7cf4a..62401f246f 100644 --- a/pss/handshake.go +++ b/pss/handshake.go @@ -324,7 +324,7 @@ func (ctl *HandshakeController) registerSymKeyUse(symkeyid string) error { } symKey.count++ - receiver := common.ToHex(ctl.pss.Crypto.FromECDSAPub(ctl.pss.PublicKey())) + receiver := common.ToHex(ctl.pss.Crypto.SerializePublicKey(ctl.pss.PublicKey())) log.Trace("increment symkey recv use", "symsymkeyid", symkeyid, "count", symKey.count, "limit", symKey.limit, "receiver", receiver) return nil diff --git a/pss/keystore.go b/pss/keystore.go index f32fbf2116..4598b238f2 100644 --- a/pss/keystore.go +++ b/pss/keystore.go @@ -25,10 +25,11 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/metrics" "github.com/ethersphere/swarm/log" + "github.com/ethersphere/swarm/pss/crypto" ) type KeyStore struct { - Crypto CryptoBackend // key and encryption crypto + Crypto crypto.Crypto // key and encryption crypto mx sync.RWMutex pubKeyPool map[string]map[Topic]*peer // mapping of hex public keys to peer address by topic. symKeyPool map[string]map[Topic]*peer // mapping of symkeyids to peer address by topic. @@ -38,7 +39,7 @@ type KeyStore struct { func loadKeyStore() *KeyStore { return &KeyStore{ - Crypto: NewCryptoBackend(), + Crypto: crypto.New(), pubKeyPool: make(map[string]map[Topic]*peer), symKeyPool: make(map[string]map[Topic]*peer), symKeyDecryptCache: make([]*string, defaultSymKeyCacheCapacity), @@ -82,7 +83,7 @@ func (ks *KeyStore) SetPeerPublicKey(pubkey *ecdsa.PublicKey, topic Topic, addre if err := validateAddress(address); err != nil { return err } - pubkeybytes := ks.Crypto.FromECDSAPub(pubkey) + pubkeybytes := ks.Crypto.SerializePublicKey(pubkey) if len(pubkeybytes) == 0 { return fmt.Errorf("invalid public key: %v", pubkey) } @@ -142,63 +143,71 @@ func (ks *KeyStore) getPeerAddress(keyid string, topic Topic) (PssAddress, error } // Attempt to decrypt, validate and unpack a symmetrically encrypted message. -// If successful, returns the unpacked receivedMessage struct -// encapsulating the decrypted message, and the crypto backend id +// If successful, returns the payload of the message and the id // of the symmetric key used to decrypt the message. -// It fails if decryption of the message fails or if the message is corrupted. -func (ks *KeyStore) processSym(envelope *envelope) (*receivedMessage, string, PssAddress, error) { +// It fails if decryption of the message fails or if the message is corrupted/not valid. +func (ks *KeyStore) processSym(pssMsg *PssMsg) ([]byte, string, PssAddress, error) { metrics.GetOrRegisterCounter("pss.process.sym", nil).Inc(1) for i := ks.symKeyDecryptCacheCursor; i > ks.symKeyDecryptCacheCursor-cap(ks.symKeyDecryptCache) && i > 0; i-- { symkeyid := ks.symKeyDecryptCache[i%cap(ks.symKeyDecryptCache)] - symkey, err := ks.Crypto.GetSymKey(*symkeyid) + symkey, err := ks.Crypto.GetSymmetricKey(*symkeyid) if err != nil { continue } - recvmsg, err := envelope.openSymmetric(symkey) + unwrapParams := &crypto.UnwrapParams{ + SymmetricKey: symkey, + } + recvmsg, err := ks.Crypto.UnWrap(pssMsg.Payload, unwrapParams) if err != nil { continue } - if !recvmsg.validateAndParse() { - return nil, "", nil, errors.New("symmetrically encrypted message has invalid signature or is corrupt") + payload, validateError := recvmsg.GetPayload() + if validateError != nil { + return nil, "", nil, validateError } + var from PssAddress ks.mx.RLock() - if ks.symKeyPool[*symkeyid][envelope.Topic] != nil { - from = ks.symKeyPool[*symkeyid][envelope.Topic].address + if ks.symKeyPool[*symkeyid][pssMsg.Topic] != nil { + from = ks.symKeyPool[*symkeyid][pssMsg.Topic].address } ks.mx.RUnlock() ks.symKeyDecryptCacheCursor++ ks.symKeyDecryptCache[ks.symKeyDecryptCacheCursor%cap(ks.symKeyDecryptCache)] = symkeyid - return recvmsg, *symkeyid, from, nil + return payload, *symkeyid, from, nil } return nil, "", nil, errors.New("could not decrypt message") } // Attempt to decrypt, validate and unpack an asymmetrically encrypted message. -// If successful, returns the unpacked receivedMessage struct -// encapsulating the decrypted message, and the byte representation of +// If successful, returns the payload of the message and the hex representation of // the public key used to decrypt the message. // It fails if decryption of message fails, or if the message is corrupted. -func (p *Pss) processAsym(envelope *envelope) (*receivedMessage, string, PssAddress, error) { +func (p *Pss) processAsym(pssMsg *PssMsg) ([]byte, string, PssAddress, error) { metrics.GetOrRegisterCounter("pss.process.asym", nil).Inc(1) - recvmsg, err := envelope.openAsymmetric(p.privateKey) + unwrapParams := &crypto.UnwrapParams{ + Receiver: p.privateKey, + } + recvmsg, err := p.Crypto.UnWrap(pssMsg.Payload, unwrapParams) if err != nil { return nil, "", nil, fmt.Errorf("could not decrypt message: %s", err) } - // check signature (if signed), strip padding - if !recvmsg.validateAndParse() { - return nil, "", nil, errors.New("invalid message") + + payload, validateError := recvmsg.GetPayload() + if validateError != nil { + return nil, "", nil, validateError } - pubkeyid := common.ToHex(p.Crypto.FromECDSAPub(recvmsg.Src)) + + pubkeyid := common.ToHex(p.Crypto.SerializePublicKey(recvmsg.GetSender())) var from PssAddress p.mx.RLock() - if p.pubKeyPool[pubkeyid][envelope.Topic] != nil { - from = p.pubKeyPool[pubkeyid][envelope.Topic].address + if p.pubKeyPool[pubkeyid][pssMsg.Topic] != nil { + from = p.pubKeyPool[pubkeyid][pssMsg.Topic].address } p.mx.RUnlock() - return recvmsg, pubkeyid, from, nil + return payload, pubkeyid, from, nil } // Symkey garbage collection @@ -237,7 +246,7 @@ func (p *Pss) cleanKeys() (count int) { // Automatically generate a new symkey for a topic and address hint func (ks *KeyStore) GenerateSymmetricKey(topic Topic, address PssAddress, addToCache bool) (string, error) { - keyid, err := ks.Crypto.GenerateSymKey() + keyid, err := ks.Crypto.GenerateSymmetricKey() if err == nil { ks.addSymmetricKeyToPool(keyid, topic, address, addToCache, false) } @@ -247,7 +256,7 @@ func (ks *KeyStore) GenerateSymmetricKey(topic Topic, address PssAddress, addToC // Returns a symmetric key byte sequence stored in the crypto backend by its unique id. // Passes on the error value from the crypto backend. func (ks *KeyStore) GetSymmetricKey(symkeyid string) ([]byte, error) { - return ks.Crypto.GetSymKey(symkeyid) + return ks.Crypto.GetSymmetricKey(symkeyid) } // Links a peer symmetric key (arbitrary byte sequence) to a topic. @@ -269,7 +278,7 @@ func (ks *KeyStore) SetSymmetricKey(key []byte, topic Topic, address PssAddress, } func (ks *KeyStore) setSymmetricKey(key []byte, topic Topic, address PssAddress, addtocache bool, protected bool) (string, error) { - keyid, err := ks.Crypto.AddSymKeyDirect(key) + keyid, err := ks.Crypto.AddSymmetricKey(key) if err == nil { ks.addSymmetricKeyToPool(keyid, topic, address, addtocache, protected) } diff --git a/pss/message.go b/pss/message.go deleted file mode 100644 index 10a5d2dc7b..0000000000 --- a/pss/message.go +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright 2019 The Swarm Authors -// This file is part of the Swarm library. -// -// The Swarm library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The Swarm library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the Swarm library. If not, see . - -// Contains all types and code related to messages and envelopes. -// Currently backed by whisperv6 -package pss - -import ( - "crypto/ecdsa" - - whisper "github.com/ethereum/go-ethereum/whisper/whisperv6" -) - -func toTopic(b []byte) (t Topic) { - return Topic(whisper.BytesToTopic(b)) -} - -type envelope struct { - Topic Topic - Data []byte - Expiry uint32 -} - -// == envelope == - -// newSentEnvelope creates and initializes a non-signed, non-encrypted Whisper message -// and then wrap it and encrypt it. It performs what it used to be two function calls: -// msg, e1 := NewSentMessage and env, e := msg.Wrap -func newSentEnvelope(params *messageParams) (*envelope, error) { - whisperParams := toWhisperParams(params) - - message, e := whisper.NewSentMessage(whisperParams) - if e != nil { - return nil, e - } - - whisperEnvelope, e := message.Wrap(whisperParams) - if e != nil { - return nil, e - } - - return toPssEnvelope(whisperEnvelope), nil -} - -// openSymmetric tries to decrypt an envelope, potentially encrypted with a particular key. -func (e *envelope) openSymmetric(key []byte) (*receivedMessage, error) { - whisperEnvelope := toWhisperEnvelope(e) - whisperMsg, err := whisperEnvelope.OpenSymmetric(key) - if err != nil { - return nil, err - } - msg := toReceivedMessage(whisperMsg) - return msg, nil -} - -// openAsymmetric tries to decrypt an envelope, potentially encrypted with a particular key. -func (e *envelope) openAsymmetric(key *ecdsa.PrivateKey) (*receivedMessage, error) { - whisperEnvelope := toWhisperEnvelope(e) - whisperMsg, err := whisperEnvelope.OpenAsymmetric(key) - if err != nil { - return nil, err - } - - msg := toReceivedMessage(whisperMsg) - return msg, nil -} - -// == received message == - -// receivedMessage represents a data packet to be received -// and successfully decrypted. -type receivedMessage struct { - Payload []byte - Raw []byte - Signature []byte - Salt []byte - Padding []byte - - Src *ecdsa.PublicKey - Dst *ecdsa.PublicKey -} - -// validateAndParse checks the message validity and extracts the fields in case of success. -func (msg *receivedMessage) validateAndParse() bool { - whisperRecvMsg := &whisper.ReceivedMessage{ - Raw: msg.Raw, - } - - success := whisperRecvMsg.ValidateAndParse() - if success { - msg.Signature = whisperRecvMsg.Signature - msg.Src = whisperRecvMsg.Src - msg.Payload = whisperRecvMsg.Payload - msg.Padding = whisperRecvMsg.Padding - } - return success -} - -type messageParams struct { - Src *ecdsa.PrivateKey - Dst *ecdsa.PublicKey - KeySym []byte - Topic Topic - Payload []byte - Padding []byte -} - -// == Conversion functions to/from whisper == - -func toReceivedMessage(whisperMsg *whisper.ReceivedMessage) *receivedMessage { - return &receivedMessage{ - Payload: whisperMsg.Payload, - Raw: whisperMsg.Raw, - Signature: whisperMsg.Signature, - Salt: whisperMsg.Salt, - Src: whisperMsg.Src, - Dst: whisperMsg.Dst, - } -} - -func toWhisperEnvelope(e *envelope) *whisper.Envelope { - whisperEnvelope := &whisper.Envelope{ - Expiry: e.Expiry, - TTL: defaultWhisperTTL, - Topic: whisper.TopicType(e.Topic), - Data: e.Data, - Nonce: 0, - } - return whisperEnvelope -} - -func toPssEnvelope(whisperEnvelope *whisper.Envelope) *envelope { - return &envelope{ - Topic: Topic(whisperEnvelope.Topic), - Data: whisperEnvelope.Data, - Expiry: whisperEnvelope.Expiry, - } -} - -func toWhisperParams(params *messageParams) *whisper.MessageParams { - whisperParams := &whisper.MessageParams{ - TTL: defaultWhisperTTL, - Src: params.Src, - Dst: params.Dst, - KeySym: params.KeySym, - Topic: whisper.TopicType(params.Topic), - WorkTime: defaultWhisperWorkTime, - PoW: defaultWhisperPoW, - Payload: params.Payload, - Padding: params.Padding, - } - return whisperParams -} diff --git a/pss/notify/notify.go b/pss/notify/notify.go index fa97fb2d24..d51489794f 100644 --- a/pss/notify/notify.go +++ b/pss/notify/notify.go @@ -138,7 +138,7 @@ func (c *Controller) Subscribe(name string, pubkey *ecdsa.PublicKey, address pss defer c.mu.Unlock() msg := NewMsg(MsgCodeStart, name, c.pss.BaseAddr()) c.pss.SetPeerPublicKey(pubkey, controlTopic, address) - pubkeyId := hexutil.Encode(c.pss.Crypto.FromECDSAPub(pubkey)) + pubkeyId := hexutil.Encode(c.pss.Crypto.SerializePublicKey(pubkey)) smsg, err := rlp.EncodeToBytes(msg) if err != nil { return err @@ -289,7 +289,7 @@ func (c *Controller) handleStartMsg(msg *Msg, keyid string) (err error) { if err != nil { return err } - pubkey, err := c.pss.Crypto.UnmarshalPubkey(keyidbytes) + pubkey, err := c.pss.Crypto.UnmarshalPublicKey(keyidbytes) if err != nil { return err } diff --git a/pss/notify/notify_test.go b/pss/notify/notify_test.go index 30bdd43bc6..e0ae3b6370 100644 --- a/pss/notify/notify_test.go +++ b/pss/notify/notify_test.go @@ -8,7 +8,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common/hexutil" - + ethCrypto "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p/enode" @@ -16,21 +16,20 @@ import ( "github.com/ethereum/go-ethereum/p2p/simulations/adapters" "github.com/ethersphere/swarm/network" "github.com/ethersphere/swarm/pss" + "github.com/ethersphere/swarm/pss/crypto" "github.com/ethersphere/swarm/state" "github.com/ethersphere/swarm/testutil" ) var ( - psses map[string]*pss.Pss - cryptoUtils pss.CryptoUtils - crypto pss.CryptoBackend + psses map[string]*pss.Pss + crypt crypto.Crypto ) func init() { testutil.Init() - cryptoUtils = pss.NewCryptoUtils() - crypto = pss.NewCryptoBackend() + crypt = crypto.New() psses = make(map[string]*pss.Pss) } @@ -131,7 +130,7 @@ func TestStart(t *testing.T) { if err != nil { t.Fatal(err) } - pubkey, err := crypto.UnmarshalPubkey(pubkeybytes) + pubkey, err := crypt.UnmarshalPublicKey(pubkeybytes) if err != nil { t.Fatal(err) } @@ -221,13 +220,7 @@ func newServices(allowRaw bool) adapters.Services { } return adapters.Services{ "pss": func(ctx *adapters.ServiceContext) (node.Service, error) { - ctxlocal, cancel := context.WithTimeout(context.Background(), time.Second) - defer cancel() - keys, err := cryptoUtils.NewKeyPair(ctxlocal) - if err != nil { - return nil, err - } - privkey, err := cryptoUtils.GetPrivateKey(keys) + privkey, err := ethCrypto.GenerateKey() if err != nil { return nil, err } @@ -239,7 +232,7 @@ func newServices(allowRaw bool) adapters.Services { if err != nil { return nil, err } - psses[hexutil.Encode(crypto.FromECDSAPub(&privkey.PublicKey))] = ps + psses[hexutil.Encode(crypt.SerializePublicKey(&privkey.PublicKey))] = ps return ps, nil }, "bzz": func(ctx *adapters.ServiceContext) (node.Service, error) { diff --git a/pss/prox_test.go b/pss/prox_test.go index 65482976fb..dd8d160582 100644 --- a/pss/prox_test.go +++ b/pss/prox_test.go @@ -11,6 +11,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + ethCrypto "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" @@ -426,11 +427,8 @@ func newProxServices(td *testData, allowRaw bool, handlerContextFuncs map[Topic] // execadapter does not exec init() initTest() - // create keys in cryptoUtils and set up the pss object - ctxlocal, cancel := context.WithTimeout(context.Background(), time.Second*3) - defer cancel() - keys, err := cryptoUtils.NewKeyPair(ctxlocal) - privkey, err := cryptoUtils.GetPrivateKey(keys) + // create keys and set up the pss object + privkey, err := ethCrypto.GenerateKey() pssp := NewParams().WithPrivateKey(privkey) pssp.AllowRaw = allowRaw bzzPrivateKey, err := simulation.BzzPrivateKeyFromConfig(ctx.Config) diff --git a/pss/pss.go b/pss/pss.go index d8177216e7..fe04f56943 100644 --- a/pss/pss.go +++ b/pss/pss.go @@ -20,7 +20,7 @@ import ( "bytes" "context" "crypto/ecdsa" - "crypto/rand" + "encoding/hex" "errors" "fmt" "hash" @@ -36,18 +36,16 @@ import ( "github.com/ethersphere/swarm/network" "github.com/ethersphere/swarm/p2p/protocols" "github.com/ethersphere/swarm/pot" + "github.com/ethersphere/swarm/pss/crypto" "github.com/ethersphere/swarm/storage" "golang.org/x/crypto/sha3" ) const ( - defaultPaddingByteSize = 16 defaultMsgTTL = time.Second * 120 defaultDigestCacheTTL = time.Second * 10 defaultSymKeyCacheCapacity = 512 digestLength = 32 // byte length of digest used for pss cache (currently same as swarm chunk hash) - defaultWhisperWorkTime = 3 - defaultWhisperPoW = 0.0000000001 defaultMaxMsgSize = 1024 * 1024 defaultCleanInterval = time.Second * 60 * 10 defaultOutboxCapacity = 100000 @@ -212,13 +210,12 @@ type Pss struct { peers map[string]*protocols.Peer // keep track of all peers sitting on the pssmsg routing layer peersMu sync.RWMutex - fwdCache map[digest]cacheEntry // checksum of unique fields from pssmsg mapped to expiry, cache to determine whether to drop msg - fwdCacheMu sync.RWMutex - cacheTTL time.Duration // how long to keep messages in fwdCache (not implemented) - msgTTL time.Duration - paddingByteSize int - capstring string - outbox outbox + fwdCache map[digest]cacheEntry // checksum of unique fields from pssmsg mapped to expiry, cache to determine whether to drop msg + fwdCacheMu sync.RWMutex + cacheTTL time.Duration // how long to keep messages in fwdCache (not implemented) + msgTTL time.Duration + capstring string + outbox outbox // message handling handlers map[Topic]map[*handler]bool // topic and version based pss payload handlers. See pss.Handle() @@ -232,7 +229,7 @@ type Pss struct { } func (p *Pss) String() string { - return fmt.Sprintf("pss: addr %x, pubkey %v", p.BaseAddr(), common.ToHex(p.Crypto.FromECDSAPub(&p.privateKey.PublicKey))) + return fmt.Sprintf("pss: addr %x, pubkey %v", p.BaseAddr(), hex.EncodeToString(p.Crypto.SerializePublicKey(&p.privateKey.PublicKey))) } // Creates a new Pss instance. @@ -254,12 +251,11 @@ func New(k *network.Kademlia, params *Params) (*Pss, error) { privateKey: params.privateKey, quitC: make(chan struct{}), - peers: make(map[string]*protocols.Peer), - fwdCache: make(map[digest]cacheEntry), - cacheTTL: params.CacheTTL, - msgTTL: params.MsgTTL, - paddingByteSize: defaultPaddingByteSize, - capstring: c.String(), + peers: make(map[string]*protocols.Peer), + fwdCache: make(map[digest]cacheEntry), + cacheTTL: params.CacheTTL, + msgTTL: params.MsgTTL, + capstring: c.String(), handlers: make(map[Topic]map[*handler]bool), topicHandlerCaps: make(map[Topic]*handlerCaps), @@ -306,7 +302,7 @@ func (p *Pss) Start(srv *p2p.Server) error { go p.outbox.processOutbox() log.Info("Started Pss") - log.Info("Loaded EC keys", "pubkey", common.ToHex(p.Crypto.FromECDSAPub(p.PublicKey())), "secp256", common.ToHex(p.Crypto.CompressPubkey(p.PublicKey()))) + log.Info("Loaded EC keys", "pubkey", hex.EncodeToString(p.Crypto.SerializePublicKey(p.PublicKey())), "secp256", hex.EncodeToString(p.Crypto.CompressPublicKey(p.PublicKey()))) return nil } @@ -471,19 +467,19 @@ func (p *Pss) handle(ctx context.Context, msg interface{}) error { if !ok { return fmt.Errorf("invalid message type. Expected *PssMsg, got %T", msg) } - log.Trace("handler", "self", label(p.Kademlia.BaseAddr()), "topic", label(pssmsg.Payload.Topic[:])) + log.Trace("handler", "self", label(p.Kademlia.BaseAddr()), "topic", label(pssmsg.Topic[:])) if int64(pssmsg.Expire) < time.Now().Unix() { metrics.GetOrRegisterCounter("pss.expire", nil).Inc(1) - log.Warn("pss filtered expired message", "from", common.ToHex(p.Kademlia.BaseAddr()), "to", common.ToHex(pssmsg.To)) + log.Warn("pss filtered expired message", "from", hex.EncodeToString(p.Kademlia.BaseAddr()), "to", hex.EncodeToString(pssmsg.To)) return nil } if p.checkFwdCache(pssmsg) { - log.Trace("pss relay block-cache match (process)", "from", common.ToHex(p.Kademlia.BaseAddr()), "to", (common.ToHex(pssmsg.To))) + log.Trace("pss relay block-cache match (process)", "from", hex.EncodeToString(p.Kademlia.BaseAddr()), "to", (hex.EncodeToString(pssmsg.To))) return nil } p.addFwdCache(pssmsg) - psstopic := pssmsg.Payload.Topic + psstopic := pssmsg.Topic // raw is simplest handler contingency to check, so check that first var isRaw bool @@ -507,11 +503,11 @@ func (p *Pss) handle(ctx context.Context, msg interface{}) error { } isRecipient := p.isSelfPossibleRecipient(pssmsg, isProx) if !isRecipient { - log.Trace("pss msg forwarding ===>", "pss", common.ToHex(p.BaseAddr()), "prox", isProx) + log.Trace("pss msg forwarding ===>", "pss", hex.EncodeToString(p.BaseAddr()), "prox", isProx) return p.enqueue(pssmsg) } - log.Trace("pss msg processing <===", "pss", common.ToHex(p.BaseAddr()), "prox", isProx, "raw", isRaw, "topic", label(pssmsg.Payload.Topic[:])) + log.Trace("pss msg processing <===", "pss", hex.EncodeToString(p.BaseAddr()), "prox", isProx, "raw", isRaw, "topic", label(pssmsg.Topic[:])) if err := p.process(pssmsg, isRaw, isProx); err != nil { qerr := p.enqueue(pssmsg) if qerr != nil { @@ -528,18 +524,16 @@ func (p *Pss) process(pssmsg *PssMsg, raw bool, prox bool) error { defer metrics.GetOrRegisterResettingTimer("pss.process", nil).UpdateSince(time.Now()) var err error - var recvmsg *receivedMessage var payload []byte var from PssAddress var asymmetric bool var keyid string - var keyFunc func(envelope *envelope) (*receivedMessage, string, PssAddress, error) + var keyFunc func(pssMsg *PssMsg) ([]byte, string, PssAddress, error) - envelope := pssmsg.Payload - psstopic := envelope.Topic + psstopic := pssmsg.Topic if raw { - payload = pssmsg.Payload.Data + payload = pssmsg.Payload } else { if pssmsg.isSym() { keyFunc = p.processSym @@ -548,11 +542,10 @@ func (p *Pss) process(pssmsg *PssMsg, raw bool, prox bool) error { keyFunc = p.processAsym } - recvmsg, keyid, from, err = keyFunc(envelope) + payload, keyid, from, err = keyFunc(pssmsg) if err != nil { - return errors.New("Decryption failed") + return errors.New("decryption failed") } - payload = recvmsg.Payload } if len(pssmsg.To) < addressLength || prox { @@ -643,15 +636,12 @@ func (p *Pss) SendRaw(address PssAddress, topic Topic, msg []byte) error { pssMsgParams := &msgParams{ raw: true, } - payload := &envelope{ - Data: msg, - Topic: topic, - } pssMsg := newPssMsg(pssMsgParams) pssMsg.To = address pssMsg.Expire = uint32(time.Now().Add(p.msgTTL).Unix()) - pssMsg.Payload = payload + pssMsg.Payload = msg + pssMsg.Topic = topic p.addFwdCache(pssMsg) @@ -677,7 +667,7 @@ func (p *Pss) SendSym(symkeyid string, topic Topic, msg []byte) error { // // Fails if the key id does not match any in of the stored public keys func (p *Pss) SendAsym(pubkeyid string, topic Topic, msg []byte) error { - if _, err := p.Crypto.UnmarshalPubkey(common.FromHex(pubkeyid)); err != nil { + if _, err := p.Crypto.UnmarshalPublicKey(common.FromHex(pubkeyid)); err != nil { return fmt.Errorf("Cannot unmarshal pubkey: %x", pubkeyid) } psp, ok := p.getPeerPub(pubkeyid, topic) @@ -697,34 +687,24 @@ func (p *Pss) send(to []byte, topic Topic, msg []byte, asymmetric bool, key []by if key == nil || bytes.Equal(key, []byte{}) { return fmt.Errorf("Zero length key passed to pss send") } - padding := make([]byte, p.paddingByteSize) - c, err := rand.Read(padding) - if err != nil { - return err - } else if c < p.paddingByteSize { - return fmt.Errorf("invalid padding length: %d", c) - } - mparams := &messageParams{ - Src: p.privateKey, - Topic: topic, - Payload: msg, - Padding: padding, + wrapParams := &crypto.WrapParams{ + Sender: p.privateKey, } if asymmetric { - pk, err := p.Crypto.UnmarshalPubkey(key) + pk, err := p.Crypto.UnmarshalPublicKey(key) if err != nil { return fmt.Errorf("Cannot unmarshal pubkey: %x", key) } - mparams.Dst = pk + wrapParams.Receiver = pk } else { - mparams.KeySym = key + wrapParams.SymmetricKey = key } // set up outgoing message container, which does encryption and envelope wrapping - envelope, err := newSentEnvelope(mparams) + envelope, err := p.Crypto.Wrap(msg, wrapParams) if err != nil { return fmt.Errorf("failed to perform message encapsulation and encryption: %v", err) } - log.Trace("pssmsg wrap done", "env", envelope, "mparams payload", common.ToHex(mparams.Payload), "to", common.ToHex(to), "asym", asymmetric, "key", common.ToHex(key)) + log.Trace("pssmsg wrap done", "env", envelope, "mparams payload", hex.EncodeToString(msg), "to", hex.EncodeToString(to), "asym", asymmetric, "key", hex.EncodeToString(key)) // prepare for devp2p transport pssMsgParams := &msgParams{ @@ -734,6 +714,7 @@ func (p *Pss) send(to []byte, topic Topic, msg []byte, asymmetric bool, key []by pssMsg.To = to pssMsg.Expire = uint32(time.Now().Add(p.msgTTL).Unix()) pssMsg.Payload = envelope + pssMsg.Topic = topic return p.enqueue(pssMsg) } diff --git a/pss/pss_test.go b/pss/pss_test.go index 2ca5e997b3..ab67d23228 100644 --- a/pss/pss_test.go +++ b/pss/pss_test.go @@ -33,6 +33,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + ethCrypto "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" @@ -43,13 +44,13 @@ import ( "github.com/ethersphere/swarm/network/simulation" "github.com/ethersphere/swarm/p2p/protocols" "github.com/ethersphere/swarm/pot" + "github.com/ethersphere/swarm/pss/crypto" "github.com/ethersphere/swarm/state" "github.com/ethersphere/swarm/testutil" ) var ( initOnce = sync.Once{} - cryptoUtils CryptoUtils psslogmain log.Logger pssprotocols map[string]*protoCtrl useHandshake bool @@ -69,8 +70,6 @@ func initTest() { func() { psslogmain = log.New("psslog", "*") - cryptoUtils = NewCryptoUtils() - pssprotocols = make(map[string]*protoCtrl) }, ) @@ -146,10 +145,7 @@ func TestMsgParams(t *testing.T) { func TestCache(t *testing.T) { var err error to, _ := hex.DecodeString("08090a0b0c0d0e0f1011121314150001020304050607161718191a1b1c1d1e1f") - ctx, cancel := context.WithTimeout(context.Background(), time.Second) - defer cancel() - keys, err := cryptoUtils.NewKeyPair(ctx) - privkey, err := cryptoUtils.GetPrivateKey(keys) + privkey, err := ethCrypto.GenerateKey() if err != nil { t.Fatal(err) } @@ -159,28 +155,27 @@ func TestCache(t *testing.T) { data := []byte("foo") datatwo := []byte("bar") datathree := []byte("baz") - mparams := &messageParams{ - Src: privkey, - Dst: &privkey.PublicKey, - Topic: PingTopic, - Payload: data, + wparams := &crypto.WrapParams{ + Sender: privkey, + Receiver: &privkey.PublicKey, } - env, err := newSentEnvelope(mparams) + env, err := ps.Crypto.Wrap(data, wparams) msg := &PssMsg{ Payload: env, To: to, + Topic: PingTopic, } - mparams.Payload = datatwo - envtwo, err := newSentEnvelope(mparams) + envtwo, err := ps.Crypto.Wrap(datatwo, wparams) msgtwo := &PssMsg{ Payload: envtwo, To: to, + Topic: PingTopic, } - mparams.Payload = datathree - envthree, err := newSentEnvelope(mparams) + envthree, err := ps.Crypto.Wrap(datathree, wparams) msgthree := &PssMsg{ Payload: envthree, To: to, + Topic: PingTopic, } digestone := ps.msgDigest(msg) @@ -241,13 +236,10 @@ func TestAddressMatch(t *testing.T) { remoteaddr := []byte("feedbeef") kadparams := network.NewKadParams() kad := network.NewKademlia(localaddr, kadparams) - ctx, cancel := context.WithTimeout(context.Background(), time.Second) - defer cancel() - keys, err := cryptoUtils.NewKeyPair(ctx) + privkey, err := ethCrypto.GenerateKey() if err != nil { t.Fatalf("Could not generate private key: %v", err) } - privkey, err := cryptoUtils.GetPrivateKey(keys) pssp := NewParams().WithPrivateKey(privkey) ps, err := New(kad, pssp) if err != nil { @@ -301,7 +293,7 @@ func TestAddressMatchProx(t *testing.T) { peerCount := nnPeerCount + 2 // set up pss - privKey, err := cryptoUtils.GenerateKey() + privKey, err := ethCrypto.GenerateKey() pssp := NewParams().WithPrivateKey(privKey) ps, err := New(kad, pssp) // enqueue method now is blocking, so we need always somebody processing the outbox @@ -412,10 +404,8 @@ func TestAddressMatchProx(t *testing.T) { pssMsg := newPssMsg(&msgParams{raw: true}) pssMsg.To = remoteAddr pssMsg.Expire = uint32(time.Now().Unix() + 4200) - pssMsg.Payload = &envelope{ - Topic: topic, - Data: data[:], - } + pssMsg.Payload = data[:] + pssMsg.Topic = topic log.Trace("withprox addrs", "local", localAddr, "remote", remoteAddr) ps.handle(context.TODO(), pssMsg) @@ -443,10 +433,8 @@ func TestAddressMatchProx(t *testing.T) { pssMsg := newPssMsg(&msgParams{raw: true}) pssMsg.To = remoteAddr pssMsg.Expire = uint32(time.Now().Unix() + 4200) - pssMsg.Payload = &envelope{ - Topic: topic, - Data: data[:], - } + pssMsg.Payload = data[:] + pssMsg.Topic = topic log.Trace("withprox addrs", "local", localAddr, "remote", remoteAddr) ps.handle(context.TODO(), pssMsg) @@ -467,10 +455,8 @@ func TestAddressMatchProx(t *testing.T) { pssMsg := newPssMsg(&msgParams{raw: true}) pssMsg.To = remoteAddr pssMsg.Expire = uint32(time.Now().Unix() + 4200) - pssMsg.Payload = &envelope{ - Topic: topic, - Data: []byte(remotePotAddr.String()), - } + pssMsg.Payload = []byte(remotePotAddr.String()) + pssMsg.Topic = topic log.Trace("noprox addrs", "local", localAddr, "remote", remoteAddr) ps.handle(context.TODO(), pssMsg) @@ -483,7 +469,7 @@ func TestAddressMatchProx(t *testing.T) { func TestMessageOutbox(t *testing.T) { // setup - privkey, err := cryptoUtils.GenerateKey() + privkey, err := ethCrypto.GenerateKey() if err != nil { t.Fatal(err.Error()) } @@ -558,7 +544,7 @@ func TestMessageOutbox(t *testing.T) { func TestOutboxFull(t *testing.T) { // setup - privkey, err := crypto.GenerateKey() + privkey, err := ethCrypto.GenerateKey() if err != nil { t.Fatal(err.Error()) } @@ -605,23 +591,11 @@ func TestOutboxFull(t *testing.T) { // set and generate pubkeys and symkeys func TestKeys(t *testing.T) { // make our key and init pss with it - ctx, cancel := context.WithTimeout(context.Background(), time.Second) - defer cancel() - ourkeys, err := cryptoUtils.NewKeyPair(ctx) - if err != nil { - t.Fatalf("create 'our' key fail") - } - ctx, cancel2 := context.WithTimeout(context.Background(), time.Second) - defer cancel2() - theirkeys, err := cryptoUtils.NewKeyPair(ctx) - if err != nil { - t.Fatalf("create 'their' key fail") - } - ourprivkey, err := cryptoUtils.GetPrivateKey(ourkeys) + ourprivkey, err := ethCrypto.GenerateKey() if err != nil { t.Fatalf("failed to retrieve 'our' private key") } - theirprivkey, err := cryptoUtils.GetPrivateKey(theirkeys) + theirprivkey, err := ethCrypto.GenerateKey() if err != nil { t.Fatalf("failed to retrieve 'their' private key") } @@ -646,11 +620,11 @@ func TestKeys(t *testing.T) { } // get the key back from crypto backend, check that it's still the same - outkeyback, err := ps.Crypto.GetSymKey(outkeyid) + outkeyback, err := ps.Crypto.GetSymmetricKey(outkeyid) if err != nil { t.Fatalf(err.Error()) } - inkey, err := ps.Crypto.GetSymKey(inkeyid) + inkey, err := ps.Crypto.GetSymmetricKey(inkeyid) if err != nil { t.Fatalf(err.Error()) } @@ -671,7 +645,7 @@ func TestKeys(t *testing.T) { // check that we can retrieve previously added public key entires per topic and peer func TestGetPublickeyEntries(t *testing.T) { - privkey, err := cryptoUtils.GenerateKey() + privkey, err := ethCrypto.GenerateKey() if err != nil { t.Fatal(err) } @@ -684,11 +658,11 @@ func TestGetPublickeyEntries(t *testing.T) { topicaddr[Topic{0x2a}] = peeraddr[:16] topicaddr[Topic{0x02, 0x9a}] = []byte{} - remoteprivkey, err := cryptoUtils.GenerateKey() + remoteprivkey, err := ethCrypto.GenerateKey() if err != nil { t.Fatal(err) } - remotepubkeybytes := ps.Crypto.FromECDSAPub(&remoteprivkey.PublicKey) + remotepubkeybytes := ps.Crypto.SerializePublicKey(&remoteprivkey.PublicKey) remotepubkeyhex := common.ToHex(remotepubkeybytes) pssapi := NewAPI(ps) @@ -732,7 +706,7 @@ OUTER: func TestPeerCapabilityMismatch(t *testing.T) { // create privkey for forwarder node - privkey, err := cryptoUtils.GenerateKey() + privkey, err := ethCrypto.GenerateKey() if err != nil { t.Fatal(err) } @@ -777,7 +751,7 @@ func TestPeerCapabilityMismatch(t *testing.T) { pssmsg := &PssMsg{ To: []byte{}, Expire: uint32(time.Now().Add(time.Second).Unix()), - Payload: &envelope{}, + Payload: nil, } ps := newTestPss(privkey, kad, nil) defer ps.Stop() @@ -792,7 +766,7 @@ func TestPeerCapabilityMismatch(t *testing.T) { func TestRawAllow(t *testing.T) { // set up pss like so many times before - privKey, err := cryptoUtils.GenerateKey() + privKey, err := ethCrypto.GenerateKey() if err != nil { t.Fatal(err) } @@ -822,9 +796,8 @@ func TestRawAllow(t *testing.T) { }) pssMsg.To = baseAddr.OAddr pssMsg.Expire = uint32(time.Now().Unix() + 4200) - pssMsg.Payload = &envelope{ - Topic: topic, - } + pssMsg.Topic = topic + pssMsg.Payload = nil ps.handle(context.TODO(), pssMsg) if receives > 0 { t.Fatalf("Expected handler not to be executed with raw cap off") @@ -840,7 +813,7 @@ func TestRawAllow(t *testing.T) { deregRawHandler := ps.Register(&topic, hndlrRaw) // should work now - pssMsg.Payload.Data = []byte("Raw Deal") + pssMsg.Payload = []byte("Raw Deal") ps.handle(context.TODO(), pssMsg) if receives == 0 { t.Fatalf("Expected handler to be executed with raw cap on") @@ -851,7 +824,7 @@ func TestRawAllow(t *testing.T) { deregRawHandler() // check that raw messages fail again - pssMsg.Payload.Data = []byte("Raw Trump") + pssMsg.Payload = []byte("Raw Trump") ps.handle(context.TODO(), pssMsg) if receives != prevReceives { t.Fatalf("Expected handler not to be executed when raw handler is retracted") @@ -1516,10 +1489,7 @@ func benchmarkSymKeySend(b *testing.B) { if err != nil { b.Fatalf("benchmark called with invalid msgsize param '%s': %v", msgsizestring[1], err) } - ctx, cancel := context.WithTimeout(context.Background(), time.Second) - defer cancel() - keys, err := cryptoUtils.NewKeyPair(ctx) - privkey, err := cryptoUtils.GetPrivateKey(keys) + privkey, err := ethCrypto.GenerateKey() ps := newTestPss(privkey, nil, nil) defer ps.Stop() msg := make([]byte, msgsize) @@ -1531,7 +1501,7 @@ func benchmarkSymKeySend(b *testing.B) { if err != nil { b.Fatalf("could not generate symkey: %v", err) } - symkey, err := ps.Crypto.GetSymKey(symkeyid) + symkey, err := ps.Crypto.GetSymmetricKey(symkeyid) if err != nil { b.Fatalf("could not retrieve symkey: %v", err) } @@ -1561,10 +1531,7 @@ func benchmarkAsymKeySend(b *testing.B) { if err != nil { b.Fatalf("benchmark called with invalid msgsize param '%s': %v", msgsizestring[1], err) } - ctx, cancel := context.WithTimeout(context.Background(), time.Second) - defer cancel() - keys, err := cryptoUtils.NewKeyPair(ctx) - privkey, err := cryptoUtils.GetPrivateKey(keys) + privkey, err := ethCrypto.GenerateKey() ps := newTestPss(privkey, nil, nil) defer ps.Stop() msg := make([]byte, msgsize) @@ -1575,7 +1542,7 @@ func benchmarkAsymKeySend(b *testing.B) { ps.SetPeerPublicKey(&privkey.PublicKey, topic, to) b.ResetTimer() for i := 0; i < b.N; i++ { - ps.SendAsym(common.ToHex(ps.Crypto.FromECDSAPub(&privkey.PublicKey)), topic, msg) + ps.SendAsym(common.ToHex(ps.Crypto.SerializePublicKey(&privkey.PublicKey)), topic, msg) } } func BenchmarkSymkeyBruteforceChangeaddr(b *testing.B) { @@ -1609,10 +1576,7 @@ func benchmarkSymkeyBruteforceChangeaddr(b *testing.B) { } pssmsgs := make([]*PssMsg, 0, keycount) var keyid string - ctx, cancel := context.WithTimeout(context.Background(), time.Second) - defer cancel() - keys, err := cryptoUtils.NewKeyPair(ctx) - privkey, err := cryptoUtils.GetPrivateKey(keys) + privkey, err := ethCrypto.GenerateKey() if cachesize > 0 { ps = newTestPss(privkey, nil, &Params{SymKeyCacheCapacity: int(cachesize)}) } else { @@ -1627,17 +1591,14 @@ func benchmarkSymkeyBruteforceChangeaddr(b *testing.B) { if err != nil { b.Fatalf("cant generate symkey #%d: %v", i, err) } - symkey, err := ps.Crypto.GetSymKey(keyid) + symkey, err := ps.Crypto.GetSymmetricKey(keyid) if err != nil { b.Fatalf("could not retrieve symkey %s: %v", keyid, err) } - mparams := &messageParams{ - KeySym: symkey, - Topic: topic, - Payload: []byte("xyzzy"), - Padding: []byte("1234567890abcdef"), + wparams := &crypto.WrapParams{ + SymmetricKey: symkey, } - env, err := newSentEnvelope(mparams) + payload, err := ps.Crypto.Wrap([]byte("xyzzy"), wparams) if err != nil { b.Fatalf("could not generate envelope: %v", err) } @@ -1646,7 +1607,8 @@ func benchmarkSymkeyBruteforceChangeaddr(b *testing.B) { }) pssmsgs = append(pssmsgs, &PssMsg{ To: to, - Payload: env, + Topic: topic, + Payload: payload, }) } b.ResetTimer() @@ -1687,10 +1649,7 @@ func benchmarkSymkeyBruteforceSameaddr(b *testing.B) { } } addr := make([]PssAddress, keycount) - ctx, cancel := context.WithTimeout(context.Background(), time.Second) - defer cancel() - keys, err := cryptoUtils.NewKeyPair(ctx) - privkey, err := cryptoUtils.GetPrivateKey(keys) + privkey, err := ethCrypto.GenerateKey() if cachesize > 0 { ps = newTestPss(privkey, nil, &Params{SymKeyCacheCapacity: int(cachesize)}) } else { @@ -1706,17 +1665,14 @@ func benchmarkSymkeyBruteforceSameaddr(b *testing.B) { } } - symkey, err := ps.Crypto.GetSymKey(keyid) + symkey, err := ps.Crypto.GetSymmetricKey(keyid) if err != nil { b.Fatalf("could not retrieve symkey %s: %v", keyid, err) } - mparams := &messageParams{ - KeySym: symkey, - Topic: topic, - Payload: []byte("xyzzy"), - Padding: []byte("1234567890abcdef"), + wparams := &crypto.WrapParams{ + SymmetricKey: symkey, } - env, err := newSentEnvelope(mparams) + payload, err := ps.Crypto.Wrap([]byte("xyzzy"), wparams) if err != nil { b.Fatalf("could not generate envelope: %v", err) } @@ -1725,7 +1681,8 @@ func benchmarkSymkeyBruteforceSameaddr(b *testing.B) { }) pssmsg := &PssMsg{ To: addr[len(addr)-1][:], - Payload: env, + Topic: topic, + Payload: payload, } for i := 0; i < b.N; i++ { if err := ps.process(pssmsg, false, false); err != nil { @@ -1740,10 +1697,8 @@ func testRandomMessage() *PssMsg { msg := newPssMsg(&msgParams{}) msg.To = addr msg.Expire = uint32(time.Now().Add(time.Second * 60).Unix()) - msg.Payload = &envelope{ - Topic: [4]byte{}, - Data: []byte{0x66, 0x6f, 0x6f}, - } + msg.Topic = [4]byte{} + msg.Payload = []byte{0x66, 0x6f, 0x6f} return msg } @@ -1818,10 +1773,7 @@ func newServices(allowRaw bool) map[string]simulation.ServiceFunc { // execadapter does not exec init() initTest() - ctxlocal, cancel := context.WithTimeout(context.Background(), time.Second) - defer cancel() - keys, err := cryptoUtils.NewKeyPair(ctxlocal) - privkey, err := cryptoUtils.GetPrivateKey(keys) + privkey, err := ethCrypto.GenerateKey() pssp := NewParams().WithPrivateKey(privkey) pssp.AllowRaw = allowRaw bzzPrivateKey, err := simulation.BzzPrivateKeyFromConfig(ctx.Config) diff --git a/pss/types.go b/pss/types.go index 6241d10a54..185b7c18b4 100644 --- a/pss/types.go +++ b/pss/types.go @@ -29,10 +29,6 @@ import ( "github.com/ethersphere/swarm/storage" ) -const ( - defaultWhisperTTL = 6000 -) - const ( pssControlSym = 1 pssControlRaw = 1 << 1 @@ -135,7 +131,8 @@ type PssMsg struct { To []byte Control []byte Expire uint32 - Payload *envelope + Topic Topic + Payload []byte } func newPssMsg(param *msgParams) *PssMsg { @@ -158,9 +155,11 @@ func (msg *PssMsg) isSym() bool { func (msg *PssMsg) serialize() []byte { rlpdata, _ := rlp.EncodeToBytes(struct { To []byte - Payload *envelope + Topic Topic + Payload []byte }{ To: msg.To, + Topic: msg.Topic, Payload: msg.Payload, }) return rlpdata @@ -168,7 +167,7 @@ func (msg *PssMsg) serialize() []byte { // String representation of PssMsg func (msg *PssMsg) String() string { - return fmt.Sprintf("PssMsg: Recipient: %x", common.ToHex(msg.To)) + return fmt.Sprintf("PssMsg: Recipient: %x, Topic: %v", common.ToHex(msg.To), msg.Topic.String()) } // Signature for a message handler function for a PssMsg @@ -228,3 +227,16 @@ func BytesToTopic(b []byte) Topic { topicHashFunc.Write(b) return toTopic(topicHashFunc.Sum(nil)) } + +// toTopic converts from the byte array representation of a topic +// into the Topic type. +func toTopic(b []byte) (t Topic) { + sz := TopicLength + if x := len(b); x < TopicLength { + sz = x + } + for i := 0; i < sz; i++ { + t[i] = b[i] + } + return t +}