mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-22 04:36:42 +00:00
147 lines
4.1 KiB
Go
147 lines
4.1 KiB
Go
package p2p
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/aes"
|
|
"crypto/cipher"
|
|
"crypto/hmac"
|
|
"crypto/sha256"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"hash"
|
|
"io"
|
|
|
|
// "github.com/ethereum/go-ethereum/crypto/sha256"
|
|
"github.com/ethereum/go-ethereum/ethutil"
|
|
"github.com/ethereum/go-ethereum/rlp"
|
|
)
|
|
|
|
/*
|
|
CryptoMsgRW implements MsgReadWriter a message read writer with encryption and authentication
|
|
it is initialised by cryptoId.NewSession() after a successful crypto handshake on the same IO
|
|
It uses the legacy devp2p packet structure (temporary)
|
|
*/
|
|
|
|
type CryptoMsgRW struct {
|
|
r io.Reader
|
|
w io.Writer
|
|
aesSecret, macSecret []byte
|
|
egressMac, ingressMac []byte
|
|
ingress, egress hash.Hash
|
|
stream cipher.Stream
|
|
}
|
|
|
|
func NewCryptoMsgRW(r io.Reader, w io.Writer, aesSecret, macSecret, egressMac, ingressMac []byte) (*CryptoMsgRW, error) {
|
|
self := &CryptoMsgRW{
|
|
r: r,
|
|
w: w,
|
|
aesSecret: aesSecret,
|
|
macSecret: macSecret,
|
|
egressMac: egressMac,
|
|
ingressMac: ingressMac,
|
|
}
|
|
block, err := aes.NewCipher(aesSecret)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
self.stream = cipher.NewCTR(block, macSecret[:aes.BlockSize])
|
|
self.egress = hmac.New(sha256.New, egressMac)
|
|
self.ingress = hmac.New(sha256.New, ingressMac)
|
|
return self, nil
|
|
}
|
|
|
|
func (self *CryptoMsgRW) Decrypt(plaintext, ciphertext []byte) (err error) {
|
|
self.stream.XORKeyStream(plaintext, ciphertext)
|
|
self.ingress.Write(plaintext)
|
|
return
|
|
}
|
|
|
|
func (self *CryptoMsgRW) Encrypt(ciphertext, plaintext []byte) (err error) {
|
|
self.stream.XORKeyStream(ciphertext, plaintext)
|
|
self.egress.Write(plaintext)
|
|
return
|
|
}
|
|
|
|
func (self *CryptoMsgRW) WriteMsg(msg Msg) (err error) {
|
|
// TODO: handle case when Size + len(code) + len(listhdr) overflows uint32
|
|
code := ethutil.Encode(uint32(msg.Code))
|
|
listhdr := makeListHeader(msg.Size + uint32(len(code)))
|
|
payloadLen := uint32(len(listhdr)) + uint32(len(code)) + msg.Size
|
|
|
|
start := make([]byte, 8)
|
|
copy(start, magicToken)
|
|
binary.BigEndian.PutUint32(start[4:], payloadLen)
|
|
if _, err = self.w.Write(start); err != nil {
|
|
return
|
|
}
|
|
listhdrLen := uint32(len(listhdr))
|
|
codeLen := uint32(len(code))
|
|
ciphertext := make([]byte, listhdrLen+codeLen+msg.Size)
|
|
plaintext := make([]byte, listhdrLen+codeLen+msg.Size)
|
|
copy(plaintext, listhdr)
|
|
copy(plaintext[listhdrLen:], code)
|
|
msg.Payload.Read(plaintext[listhdrLen+codeLen:])
|
|
self.Encrypt(ciphertext, plaintext)
|
|
fmt.Printf("ENCRYPT:\npt: %v\nct: %v\n", hexkey(plaintext), hexkey(ciphertext))
|
|
if _, err = self.w.Write(ciphertext); err != nil {
|
|
return
|
|
}
|
|
if _, err = self.w.Write(self.egress.Sum(nil)); err != nil {
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (self *CryptoMsgRW) ReadMsg() (msg Msg, err error) {
|
|
var size uint32
|
|
if size, err = self.readHeader(); err != nil {
|
|
err = newPeerError(errRead, "%v", err)
|
|
return
|
|
}
|
|
// authenticate size
|
|
var payload rlp.ByteReader
|
|
if payload, err = self.readPayload(size); err != nil {
|
|
err = newPeerError(errRead, "%v", err)
|
|
return
|
|
}
|
|
return NewMsgFromRLP(size, payload)
|
|
}
|
|
|
|
func (self *CryptoMsgRW) readHeader() (size uint32, err error) {
|
|
// read magic and payload size
|
|
start := make([]byte, 8)
|
|
if _, err = io.ReadFull(self.r, start); err != nil {
|
|
err = newPeerError(errRead, "%v", err)
|
|
return
|
|
}
|
|
if !bytes.HasPrefix(start, magicToken) {
|
|
err = newPeerError(errMagicTokenMismatch, "got %x, want %x", start[:4], magicToken)
|
|
return
|
|
}
|
|
// here we could deobfuscate and auth the header...
|
|
size = binary.BigEndian.Uint32(start[4:])
|
|
// here more header type metainfo...
|
|
return
|
|
}
|
|
|
|
func (self *CryptoMsgRW) readPayload(size uint32) (r rlp.ByteReader, err error) {
|
|
plaintext := make([]byte, size)
|
|
ciphertext := make([]byte, size)
|
|
self.r.Read(ciphertext)
|
|
self.Decrypt(plaintext, ciphertext)
|
|
fmt.Printf("DECRYPT:\npt: %v\nct: %v\n", hexkey(plaintext), hexkey(ciphertext))
|
|
mac := make([]byte, 32)
|
|
if _, err = self.r.Read(mac); err != nil {
|
|
err = newPeerError(errRead, "%v", err)
|
|
return
|
|
}
|
|
// var expectedMac = self.ingress.Sum(nil)
|
|
// if !hmac.Equal(expectedMac, mac) {
|
|
// err = newPeerError(errAuthentication, "ingress incorrect")
|
|
// return
|
|
// }
|
|
r = bytes.NewReader(plaintext)
|
|
// r = io.LimitReader(bytes.NewReader(plaintext), int64(size))
|
|
return
|
|
}
|