p2p/rlpx: reduce allocation and syscalls (#22899)

This change significantly improves the performance of RLPx message reads
and writes. In the previous implementation, reading and writing of
message frames performed multiple reads and writes on the underlying
network connection, and allocated a new []byte buffer for every read.

In the new implementation, reads and writes re-use buffers, and perform
much fewer system calls on the underlying connection. This doubles the
theoretically achievable throughput on a single connection, as shown by
the benchmark result:

    name             old speed      new speed       delta
    Throughput-8     70.3MB/s ± 0%  155.4MB/s ± 0%  +121.11%  (p=0.000 n=9+8)

The change also removes support for the legacy, pre-EIP-8 handshake encoding.
As of May 2021, no actively maintained client sends this format.
This commit is contained in:
Felix Lange
2021-05-27 10:19:13 +02:00
committed by GitHub
parent 2e7714f864
commit 7194c847b6
7 changed files with 480 additions and 229 deletions

View File

@ -48,19 +48,45 @@ import (
// This type is not generally safe for concurrent use, but reading and writing of messages
// may happen concurrently after the handshake.
type Conn struct {
dialDest *ecdsa.PublicKey
conn net.Conn
handshake *handshakeState
snappy bool
dialDest *ecdsa.PublicKey
conn net.Conn
session *sessionState
// These are the buffers for snappy compression.
// Compression is enabled if they are non-nil.
snappyReadBuffer []byte
snappyWriteBuffer []byte
}
type handshakeState struct {
// sessionState contains the session keys.
type sessionState struct {
enc cipher.Stream
dec cipher.Stream
macCipher cipher.Block
egressMAC hash.Hash
ingressMAC hash.Hash
egressMAC hashMAC
ingressMAC hashMAC
rbuf readBuffer
wbuf writeBuffer
}
// hashMAC holds the state of the RLPx v4 MAC contraption.
type hashMAC struct {
cipher cipher.Block
hash hash.Hash
aesBuffer [16]byte
hashBuffer [32]byte
seedBuffer [32]byte
}
func newHashMAC(cipher cipher.Block, h hash.Hash) hashMAC {
m := hashMAC{cipher: cipher, hash: h}
if cipher.BlockSize() != len(m.aesBuffer) {
panic(fmt.Errorf("invalid MAC cipher block size %d", cipher.BlockSize()))
}
if h.Size() != len(m.hashBuffer) {
panic(fmt.Errorf("invalid MAC digest size %d", h.Size()))
}
return m
}
// NewConn wraps the given network connection. If dialDest is non-nil, the connection
@ -76,7 +102,13 @@ func NewConn(conn net.Conn, dialDest *ecdsa.PublicKey) *Conn {
// after the devp2p Hello message exchange when the negotiated version indicates that
// compression is available on both ends of the connection.
func (c *Conn) SetSnappy(snappy bool) {
c.snappy = snappy
if snappy {
c.snappyReadBuffer = []byte{}
c.snappyWriteBuffer = []byte{}
} else {
c.snappyReadBuffer = nil
c.snappyWriteBuffer = nil
}
}
// SetReadDeadline sets the deadline for all future read operations.
@ -95,12 +127,13 @@ func (c *Conn) SetDeadline(time time.Time) error {
}
// Read reads a message from the connection.
// The returned data buffer is valid until the next call to Read.
func (c *Conn) Read() (code uint64, data []byte, wireSize int, err error) {
if c.handshake == nil {
if c.session == nil {
panic("can't ReadMsg before handshake")
}
frame, err := c.handshake.readFrame(c.conn)
frame, err := c.session.readFrame(c.conn)
if err != nil {
return 0, nil, 0, err
}
@ -111,7 +144,7 @@ func (c *Conn) Read() (code uint64, data []byte, wireSize int, err error) {
wireSize = len(data)
// If snappy is enabled, verify and decompress message.
if c.snappy {
if c.snappyReadBuffer != nil {
var actualSize int
actualSize, err = snappy.DecodedLen(data)
if err != nil {
@ -120,51 +153,55 @@ func (c *Conn) Read() (code uint64, data []byte, wireSize int, err error) {
if actualSize > maxUint24 {
return code, nil, 0, errPlainMessageTooLarge
}
data, err = snappy.Decode(nil, data)
c.snappyReadBuffer = growslice(c.snappyReadBuffer, actualSize)
data, err = snappy.Decode(c.snappyReadBuffer, data)
}
return code, data, wireSize, err
}
func (h *handshakeState) readFrame(conn io.Reader) ([]byte, error) {
// read the header
headbuf := make([]byte, 32)
if _, err := io.ReadFull(conn, headbuf); err != nil {
func (h *sessionState) readFrame(conn io.Reader) ([]byte, error) {
h.rbuf.reset()
// Read the frame header.
header, err := h.rbuf.read(conn, 32)
if err != nil {
return nil, err
}
// verify header mac
shouldMAC := updateMAC(h.ingressMAC, h.macCipher, headbuf[:16])
if !hmac.Equal(shouldMAC, headbuf[16:]) {
// Verify header MAC.
wantHeaderMAC := h.ingressMAC.computeHeader(header[:16])
if !hmac.Equal(wantHeaderMAC, header[16:]) {
return nil, errors.New("bad header MAC")
}
h.dec.XORKeyStream(headbuf[:16], headbuf[:16]) // first half is now decrypted
fsize := readInt24(headbuf)
// ignore protocol type for now
// read the frame content
var rsize = fsize // frame size rounded up to 16 byte boundary
// Decrypt the frame header to get the frame size.
h.dec.XORKeyStream(header[:16], header[:16])
fsize := readUint24(header[:16])
// Frame size rounded up to 16 byte boundary for padding.
rsize := fsize
if padding := fsize % 16; padding > 0 {
rsize += 16 - padding
}
framebuf := make([]byte, rsize)
if _, err := io.ReadFull(conn, framebuf); err != nil {
// Read the frame content.
frame, err := h.rbuf.read(conn, int(rsize))
if err != nil {
return nil, err
}
// read and validate frame MAC. we can re-use headbuf for that.
h.ingressMAC.Write(framebuf)
fmacseed := h.ingressMAC.Sum(nil)
if _, err := io.ReadFull(conn, headbuf[:16]); err != nil {
// Validate frame MAC.
frameMAC, err := h.rbuf.read(conn, 16)
if err != nil {
return nil, err
}
shouldMAC = updateMAC(h.ingressMAC, h.macCipher, fmacseed)
if !hmac.Equal(shouldMAC, headbuf[:16]) {
wantFrameMAC := h.ingressMAC.computeFrame(frame)
if !hmac.Equal(wantFrameMAC, frameMAC) {
return nil, errors.New("bad frame MAC")
}
// decrypt frame content
h.dec.XORKeyStream(framebuf, framebuf)
return framebuf[:fsize], nil
// Decrypt the frame data.
h.dec.XORKeyStream(frame, frame)
return frame[:fsize], nil
}
// Write writes a message to the connection.
@ -172,83 +209,90 @@ func (h *handshakeState) readFrame(conn io.Reader) ([]byte, error) {
// Write returns the written size of the message data. This may be less than or equal to
// len(data) depending on whether snappy compression is enabled.
func (c *Conn) Write(code uint64, data []byte) (uint32, error) {
if c.handshake == nil {
if c.session == nil {
panic("can't WriteMsg before handshake")
}
if len(data) > maxUint24 {
return 0, errPlainMessageTooLarge
}
if c.snappy {
data = snappy.Encode(nil, data)
if c.snappyWriteBuffer != nil {
// Ensure the buffer has sufficient size.
// Package snappy will allocate its own buffer if the provided
// one is smaller than MaxEncodedLen.
c.snappyWriteBuffer = growslice(c.snappyWriteBuffer, snappy.MaxEncodedLen(len(data)))
data = snappy.Encode(c.snappyWriteBuffer, data)
}
wireSize := uint32(len(data))
err := c.handshake.writeFrame(c.conn, code, data)
err := c.session.writeFrame(c.conn, code, data)
return wireSize, err
}
func (h *handshakeState) writeFrame(conn io.Writer, code uint64, data []byte) error {
ptype, _ := rlp.EncodeToBytes(code)
func (h *sessionState) writeFrame(conn io.Writer, code uint64, data []byte) error {
h.wbuf.reset()
// write header
headbuf := make([]byte, 32)
fsize := len(ptype) + len(data)
// Write header.
fsize := rlp.IntSize(code) + len(data)
if fsize > maxUint24 {
return errPlainMessageTooLarge
}
putInt24(uint32(fsize), headbuf)
copy(headbuf[3:], zeroHeader)
h.enc.XORKeyStream(headbuf[:16], headbuf[:16]) // first half is now encrypted
header := h.wbuf.appendZero(16)
putUint24(uint32(fsize), header)
copy(header[3:], zeroHeader)
h.enc.XORKeyStream(header, header)
// write header MAC
copy(headbuf[16:], updateMAC(h.egressMAC, h.macCipher, headbuf[:16]))
if _, err := conn.Write(headbuf); err != nil {
return err
}
// Write header MAC.
h.wbuf.Write(h.egressMAC.computeHeader(header))
// write encrypted frame, updating the egress MAC hash with
// the data written to conn.
tee := cipher.StreamWriter{S: h.enc, W: io.MultiWriter(conn, h.egressMAC)}
if _, err := tee.Write(ptype); err != nil {
return err
}
if _, err := tee.Write(data); err != nil {
return err
}
// Encode and encrypt the frame data.
offset := len(h.wbuf.data)
h.wbuf.data = rlp.AppendUint64(h.wbuf.data, code)
h.wbuf.Write(data)
if padding := fsize % 16; padding > 0 {
if _, err := tee.Write(zero16[:16-padding]); err != nil {
return err
}
h.wbuf.appendZero(16 - padding)
}
framedata := h.wbuf.data[offset:]
h.enc.XORKeyStream(framedata, framedata)
// write frame MAC. egress MAC hash is up to date because
// frame content was written to it as well.
fmacseed := h.egressMAC.Sum(nil)
mac := updateMAC(h.egressMAC, h.macCipher, fmacseed)
_, err := conn.Write(mac)
// Write frame MAC.
h.wbuf.Write(h.egressMAC.computeFrame(framedata))
_, err := conn.Write(h.wbuf.data)
return err
}
func readInt24(b []byte) uint32 {
return uint32(b[2]) | uint32(b[1])<<8 | uint32(b[0])<<16
// computeHeader computes the MAC of a frame header.
func (m *hashMAC) computeHeader(header []byte) []byte {
sum1 := m.hash.Sum(m.hashBuffer[:0])
return m.compute(sum1, header)
}
func putInt24(v uint32, b []byte) {
b[0] = byte(v >> 16)
b[1] = byte(v >> 8)
b[2] = byte(v)
// computeFrame computes the MAC of framedata.
func (m *hashMAC) computeFrame(framedata []byte) []byte {
m.hash.Write(framedata)
seed := m.hash.Sum(m.seedBuffer[:0])
return m.compute(seed, seed[:16])
}
// updateMAC reseeds the given hash with encrypted seed.
// it returns the first 16 bytes of the hash sum after seeding.
func updateMAC(mac hash.Hash, block cipher.Block, seed []byte) []byte {
aesbuf := make([]byte, aes.BlockSize)
block.Encrypt(aesbuf, mac.Sum(nil))
for i := range aesbuf {
aesbuf[i] ^= seed[i]
// compute computes the MAC of a 16-byte 'seed'.
//
// To do this, it encrypts the current value of the hash state, then XORs the ciphertext
// with seed. The obtained value is written back into the hash state and hash output is
// taken again. The first 16 bytes of the resulting sum are the MAC value.
//
// This MAC construction is a horrible, legacy thing.
func (m *hashMAC) compute(sum1, seed []byte) []byte {
if len(seed) != len(m.aesBuffer) {
panic("invalid MAC seed")
}
mac.Write(aesbuf)
return mac.Sum(nil)[:16]
m.cipher.Encrypt(m.aesBuffer[:], sum1)
for i := range m.aesBuffer {
m.aesBuffer[i] ^= seed[i]
}
m.hash.Write(m.aesBuffer[:])
sum2 := m.hash.Sum(m.hashBuffer[:0])
return sum2[:16]
}
// Handshake performs the handshake. This must be called before any data is written
@ -257,23 +301,26 @@ func (c *Conn) Handshake(prv *ecdsa.PrivateKey) (*ecdsa.PublicKey, error) {
var (
sec Secrets
err error
h handshakeState
)
if c.dialDest != nil {
sec, err = initiatorEncHandshake(c.conn, prv, c.dialDest)
sec, err = h.runInitiator(c.conn, prv, c.dialDest)
} else {
sec, err = receiverEncHandshake(c.conn, prv)
sec, err = h.runRecipient(c.conn, prv)
}
if err != nil {
return nil, err
}
c.InitWithSecrets(sec)
c.session.rbuf = h.rbuf
c.session.wbuf = h.wbuf
return sec.remote, err
}
// InitWithSecrets injects connection secrets as if a handshake had
// been performed. This cannot be called after the handshake.
func (c *Conn) InitWithSecrets(sec Secrets) {
if c.handshake != nil {
if c.session != nil {
panic("can't handshake twice")
}
macc, err := aes.NewCipher(sec.MAC)
@ -287,12 +334,11 @@ func (c *Conn) InitWithSecrets(sec Secrets) {
// we use an all-zeroes IV for AES because the key used
// for encryption is ephemeral.
iv := make([]byte, encc.BlockSize())
c.handshake = &handshakeState{
c.session = &sessionState{
enc: cipher.NewCTR(encc, iv),
dec: cipher.NewCTR(encc, iv),
macCipher: macc,
egressMAC: sec.EgressMAC,
ingressMAC: sec.IngressMAC,
egressMAC: newHashMAC(macc, sec.EgressMAC),
ingressMAC: newHashMAC(macc, sec.IngressMAC),
}
}
@ -303,28 +349,18 @@ func (c *Conn) Close() error {
// Constants for the handshake.
const (
maxUint24 = int(^uint32(0) >> 8)
sskLen = 16 // ecies.MaxSharedKeyLength(pubKey) / 2
sigLen = crypto.SignatureLength // elliptic S256
pubLen = 64 // 512 bit pubkey in uncompressed representation without format byte
shaLen = 32 // hash length (for nonce etc)
authMsgLen = sigLen + shaLen + pubLen + shaLen + 1
authRespLen = pubLen + shaLen + 1
eciesOverhead = 65 /* pubkey */ + 16 /* IV */ + 32 /* MAC */
encAuthMsgLen = authMsgLen + eciesOverhead // size of encrypted pre-EIP-8 initiator handshake
encAuthRespLen = authRespLen + eciesOverhead // size of encrypted pre-EIP-8 handshake reply
)
var (
// this is used in place of actual frame header data.
// TODO: replace this when Msg contains the protocol type code.
zeroHeader = []byte{0xC2, 0x80, 0x80}
// sixteen zero bytes
zero16 = make([]byte, 16)
// errPlainMessageTooLarge is returned if a decompressed message length exceeds
// the allowed 24 bits (i.e. length >= 16MB).
@ -338,19 +374,20 @@ type Secrets struct {
remote *ecdsa.PublicKey
}
// encHandshake contains the state of the encryption handshake.
type encHandshake struct {
// handshakeState contains the state of the encryption handshake.
type handshakeState struct {
initiator bool
remote *ecies.PublicKey // remote-pubk
initNonce, respNonce []byte // nonce
randomPrivKey *ecies.PrivateKey // ecdhe-random
remoteRandomPub *ecies.PublicKey // ecdhe-random-pubk
rbuf readBuffer
wbuf writeBuffer
}
// RLPx v4 handshake auth (defined in EIP-8).
type authMsgV4 struct {
gotPlain bool // whether read packet had plain format.
Signature [sigLen]byte
InitiatorPubkey [pubLen]byte
Nonce [shaLen]byte
@ -370,17 +407,16 @@ type authRespV4 struct {
Rest []rlp.RawValue `rlp:"tail"`
}
// receiverEncHandshake negotiates a session token on conn.
// runRecipient negotiates a session token on conn.
// it should be called on the listening side of the connection.
//
// prv is the local client's private key.
func receiverEncHandshake(conn io.ReadWriter, prv *ecdsa.PrivateKey) (s Secrets, err error) {
func (h *handshakeState) runRecipient(conn io.ReadWriter, prv *ecdsa.PrivateKey) (s Secrets, err error) {
authMsg := new(authMsgV4)
authPacket, err := readHandshakeMsg(authMsg, encAuthMsgLen, prv, conn)
authPacket, err := h.readMsg(authMsg, prv, conn)
if err != nil {
return s, err
}
h := new(encHandshake)
if err := h.handleAuthMsg(authMsg, prv); err != nil {
return s, err
}
@ -389,22 +425,18 @@ func receiverEncHandshake(conn io.ReadWriter, prv *ecdsa.PrivateKey) (s Secrets,
if err != nil {
return s, err
}
var authRespPacket []byte
if authMsg.gotPlain {
authRespPacket, err = authRespMsg.sealPlain(h)
} else {
authRespPacket, err = sealEIP8(authRespMsg, h)
}
authRespPacket, err := h.sealEIP8(authRespMsg)
if err != nil {
return s, err
}
if _, err = conn.Write(authRespPacket); err != nil {
return s, err
}
return h.secrets(authPacket, authRespPacket)
}
func (h *encHandshake) handleAuthMsg(msg *authMsgV4, prv *ecdsa.PrivateKey) error {
func (h *handshakeState) handleAuthMsg(msg *authMsgV4, prv *ecdsa.PrivateKey) error {
// Import the remote identity.
rpub, err := importPublicKey(msg.InitiatorPubkey[:])
if err != nil {
@ -438,7 +470,7 @@ func (h *encHandshake) handleAuthMsg(msg *authMsgV4, prv *ecdsa.PrivateKey) erro
// secrets is called after the handshake is completed.
// It extracts the connection secrets from the handshake values.
func (h *encHandshake) secrets(auth, authResp []byte) (Secrets, error) {
func (h *handshakeState) secrets(auth, authResp []byte) (Secrets, error) {
ecdheSecret, err := h.randomPrivKey.GenerateShared(h.remoteRandomPub, sskLen, sskLen)
if err != nil {
return Secrets{}, err
@ -471,21 +503,23 @@ func (h *encHandshake) secrets(auth, authResp []byte) (Secrets, error) {
// staticSharedSecret returns the static shared secret, the result
// of key agreement between the local and remote static node key.
func (h *encHandshake) staticSharedSecret(prv *ecdsa.PrivateKey) ([]byte, error) {
func (h *handshakeState) staticSharedSecret(prv *ecdsa.PrivateKey) ([]byte, error) {
return ecies.ImportECDSA(prv).GenerateShared(h.remote, sskLen, sskLen)
}
// initiatorEncHandshake negotiates a session token on conn.
// runInitiator negotiates a session token on conn.
// it should be called on the dialing side of the connection.
//
// prv is the local client's private key.
func initiatorEncHandshake(conn io.ReadWriter, prv *ecdsa.PrivateKey, remote *ecdsa.PublicKey) (s Secrets, err error) {
h := &encHandshake{initiator: true, remote: ecies.ImportECDSAPublic(remote)}
func (h *handshakeState) runInitiator(conn io.ReadWriter, prv *ecdsa.PrivateKey, remote *ecdsa.PublicKey) (s Secrets, err error) {
h.initiator = true
h.remote = ecies.ImportECDSAPublic(remote)
authMsg, err := h.makeAuthMsg(prv)
if err != nil {
return s, err
}
authPacket, err := sealEIP8(authMsg, h)
authPacket, err := h.sealEIP8(authMsg)
if err != nil {
return s, err
}
@ -495,18 +529,19 @@ func initiatorEncHandshake(conn io.ReadWriter, prv *ecdsa.PrivateKey, remote *ec
}
authRespMsg := new(authRespV4)
authRespPacket, err := readHandshakeMsg(authRespMsg, encAuthRespLen, prv, conn)
authRespPacket, err := h.readMsg(authRespMsg, prv, conn)
if err != nil {
return s, err
}
if err := h.handleAuthResp(authRespMsg); err != nil {
return s, err
}
return h.secrets(authPacket, authRespPacket)
}
// makeAuthMsg creates the initiator handshake message.
func (h *encHandshake) makeAuthMsg(prv *ecdsa.PrivateKey) (*authMsgV4, error) {
func (h *handshakeState) makeAuthMsg(prv *ecdsa.PrivateKey) (*authMsgV4, error) {
// Generate random initiator nonce.
h.initNonce = make([]byte, shaLen)
_, err := rand.Read(h.initNonce)
@ -538,13 +573,13 @@ func (h *encHandshake) makeAuthMsg(prv *ecdsa.PrivateKey) (*authMsgV4, error) {
return msg, nil
}
func (h *encHandshake) handleAuthResp(msg *authRespV4) (err error) {
func (h *handshakeState) handleAuthResp(msg *authRespV4) (err error) {
h.respNonce = msg.Nonce[:]
h.remoteRandomPub, err = importPublicKey(msg.RandomPubkey[:])
return err
}
func (h *encHandshake) makeAuthResp() (msg *authRespV4, err error) {
func (h *handshakeState) makeAuthResp() (msg *authRespV4, err error) {
// Generate random nonce.
h.respNonce = make([]byte, shaLen)
if _, err = rand.Read(h.respNonce); err != nil {
@ -558,79 +593,51 @@ func (h *encHandshake) makeAuthResp() (msg *authRespV4, err error) {
return msg, nil
}
func (msg *authMsgV4) decodePlain(input []byte) {
n := copy(msg.Signature[:], input)
n += shaLen // skip sha3(initiator-ephemeral-pubk)
n += copy(msg.InitiatorPubkey[:], input[n:])
copy(msg.Nonce[:], input[n:])
msg.Version = 4
msg.gotPlain = true
}
// readMsg reads an encrypted handshake message, decoding it into msg.
func (h *handshakeState) readMsg(msg interface{}, prv *ecdsa.PrivateKey, r io.Reader) ([]byte, error) {
h.rbuf.reset()
h.rbuf.grow(512)
func (msg *authRespV4) sealPlain(hs *encHandshake) ([]byte, error) {
buf := make([]byte, authRespLen)
n := copy(buf, msg.RandomPubkey[:])
copy(buf[n:], msg.Nonce[:])
return ecies.Encrypt(rand.Reader, hs.remote, buf, nil, nil)
}
func (msg *authRespV4) decodePlain(input []byte) {
n := copy(msg.RandomPubkey[:], input)
copy(msg.Nonce[:], input[n:])
msg.Version = 4
}
var padSpace = make([]byte, 300)
func sealEIP8(msg interface{}, h *encHandshake) ([]byte, error) {
buf := new(bytes.Buffer)
if err := rlp.Encode(buf, msg); err != nil {
// Read the size prefix.
prefix, err := h.rbuf.read(r, 2)
if err != nil {
return nil, err
}
// pad with random amount of data. the amount needs to be at least 100 bytes to make
// the message distinguishable from pre-EIP-8 handshakes.
pad := padSpace[:mrand.Intn(len(padSpace)-100)+100]
buf.Write(pad)
prefix := make([]byte, 2)
binary.BigEndian.PutUint16(prefix, uint16(buf.Len()+eciesOverhead))
enc, err := ecies.Encrypt(rand.Reader, h.remote, buf.Bytes(), nil, prefix)
return append(prefix, enc...), err
}
type plainDecoder interface {
decodePlain([]byte)
}
func readHandshakeMsg(msg plainDecoder, plainSize int, prv *ecdsa.PrivateKey, r io.Reader) ([]byte, error) {
buf := make([]byte, plainSize)
if _, err := io.ReadFull(r, buf); err != nil {
return buf, err
}
// Attempt decoding pre-EIP-8 "plain" format.
key := ecies.ImportECDSA(prv)
if dec, err := key.Decrypt(buf, nil, nil); err == nil {
msg.decodePlain(dec)
return buf, nil
}
// Could be EIP-8 format, try that.
prefix := buf[:2]
size := binary.BigEndian.Uint16(prefix)
if size < uint16(plainSize) {
return buf, fmt.Errorf("size underflow, need at least %d bytes", plainSize)
}
buf = append(buf, make([]byte, size-uint16(plainSize)+2)...)
if _, err := io.ReadFull(r, buf[plainSize:]); err != nil {
return buf, err
}
dec, err := key.Decrypt(buf[2:], nil, prefix)
// Read the handshake packet.
packet, err := h.rbuf.read(r, int(size))
if err != nil {
return buf, err
return nil, err
}
dec, err := ecies.ImportECDSA(prv).Decrypt(packet, nil, prefix)
if err != nil {
return nil, err
}
// Can't use rlp.DecodeBytes here because it rejects
// trailing data (forward-compatibility).
s := rlp.NewStream(bytes.NewReader(dec), 0)
return buf, s.Decode(msg)
err = s.Decode(msg)
return h.rbuf.data[:len(prefix)+len(packet)], err
}
// sealEIP8 encrypts a handshake message.
func (h *handshakeState) sealEIP8(msg interface{}) ([]byte, error) {
h.wbuf.reset()
// Write the message plaintext.
if err := rlp.Encode(&h.wbuf, msg); err != nil {
return nil, err
}
// Pad with random amount of data. the amount needs to be at least 100 bytes to make
// the message distinguishable from pre-EIP-8 handshakes.
h.wbuf.appendZero(mrand.Intn(100) + 100)
prefix := make([]byte, 2)
binary.BigEndian.PutUint16(prefix, uint16(len(h.wbuf.data)+eciesOverhead))
enc, err := ecies.Encrypt(rand.Reader, h.remote, h.wbuf.data, nil, prefix)
return append(prefix, enc...), err
}
// importPublicKey unmarshals 512 bit public keys.