mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-22 04:36:42 +00:00
add encryption/authentication with test
This commit is contained in:
parent
cd235b24c5
commit
a4b4f680a1
2 changed files with 229 additions and 0 deletions
147
p2p/encryption.go
Normal file
147
p2p/encryption.go
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
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
|
||||
}
|
||||
82
p2p/encryption_test.go
Normal file
82
p2p/encryption_test.go
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
package p2p
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
// "fmt"
|
||||
"io"
|
||||
"net"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func randomKey(i int) (key []byte) {
|
||||
key = make([]byte, i)
|
||||
if _, err := io.ReadFull(rand.Reader, key); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func TestEncryption(t *testing.T) {
|
||||
var args [][]byte = make([][]byte, 4)
|
||||
for i, _ := range args {
|
||||
args[i] = randomKey(32)
|
||||
}
|
||||
var pubkey = randomKey(64)
|
||||
|
||||
var caps []interface{}
|
||||
for _, p := range []Cap{Cap{"bzz", 0}, Cap{"shh", 1}, Cap{"eth", 2}} {
|
||||
caps = append(caps, p)
|
||||
}
|
||||
|
||||
var msg0 = NewMsg(handshakeMsg,
|
||||
baseProtocolVersion,
|
||||
"ethersphere",
|
||||
caps,
|
||||
3301,
|
||||
pubkey,
|
||||
)
|
||||
|
||||
var hs handshake
|
||||
|
||||
conn0, conn1 := net.Pipe()
|
||||
rw0, err := NewCryptoMsgRW(bufio.NewReader(conn0), conn0, args[0], args[1], args[2], args[3])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
messenger0 := NewMessenger(rw0)
|
||||
|
||||
rw1, err := NewCryptoMsgRW(bufio.NewReader(conn1), conn1, args[0], args[1], args[2], args[3])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
messenger1 := NewMessenger(rw1)
|
||||
|
||||
messenger0.WriteC() <- msg0
|
||||
|
||||
messenger1.ReadNextC() <- true
|
||||
|
||||
var msg1 Msg
|
||||
select {
|
||||
case msg1 = <-messenger1.ReadC():
|
||||
|
||||
case err = <-messenger0.ErrorC():
|
||||
t.Errorf("unexpected error on initiator%v", err)
|
||||
|
||||
case err = <-messenger1.ErrorC():
|
||||
t.Errorf("unexpected error on receiver %v", err)
|
||||
}
|
||||
|
||||
if err = msg1.Decode(&hs); err != nil {
|
||||
t.Errorf("rlp decoding error: %v", err)
|
||||
}
|
||||
|
||||
if //!bytes.Equal(hs.ListenPort, 3301) ||
|
||||
hs.ID != "ethersphere" ||
|
||||
len(hs.Caps) != 3 ||
|
||||
!bytes.Equal(hs.PublicKey(), pubkey) {
|
||||
t.Errorf("mismatch")
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in a new issue