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