Skip to content

Commit

Permalink
chore: export p2p package errors (cometbft#1901)
Browse files Browse the repository at this point in the history
This PR Contributes to: cometbft#1140

Changes: 
 - Exports errors for `p2p` package

---------

Co-authored-by: Andy Nogueira <[email protected]>
  • Loading branch information
akaladarshi and andynog authored Mar 18, 2024
1 parent a24eaeb commit c3f7ea3
Show file tree
Hide file tree
Showing 15 changed files with 370 additions and 95 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- `[p2p]` Export p2p package errors ([\#1901](https://github.com/cometbft/cometbft/pull/1901)) (contributes to [\#1140](https://github.com/cometbft/cometbft/issues/1140))
7 changes: 6 additions & 1 deletion p2p/conn/connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -852,6 +852,10 @@ func (ch *Channel) nextPacketMsg() tmp2p.PacketMsg {
func (ch *Channel) writePacketMsgTo(w io.Writer) (n int, err error) {
packet := ch.nextPacketMsg()
n, err = protoio.NewDelimitedWriter(w).WriteMsg(mustWrapPacket(&packet))
if err != nil {
err = ErrPacketWrite{Source: err}
}

atomic.AddInt64(&ch.recentlySent, int64(n))
return n, err
}
Expand All @@ -863,8 +867,9 @@ func (ch *Channel) recvPacketMsg(packet tmp2p.PacketMsg) ([]byte, error) {
ch.Logger.Debug("Read PacketMsg", "conn", ch.conn, "packet", packet)
recvCap, recvReceived := ch.desc.RecvMessageCapacity, len(ch.recving)+len(packet.Data)
if recvCap < recvReceived {
return nil, fmt.Errorf("received message exceeds available capacity: %v < %v", recvCap, recvReceived)
return nil, ErrPacketTooBig{Max: recvCap, Received: recvReceived}
}

ch.recving = append(ch.recving, packet.Data...)
if packet.EOF {
msgBytes := ch.recving
Expand Down
64 changes: 64 additions & 0 deletions p2p/conn/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package conn

import (
"errors"
"fmt"
)

var (
ErrInvalidSecretConnKeySend = errors.New("send invalid secret connection key")
ErrInvalidSecretConnKeyRecv = errors.New("invalid receive SecretConnection Key")
ErrChallengeVerification = errors.New("challenge verification failed")
)

// ErrPacketWrite Packet error when writing.
type ErrPacketWrite struct {
Source error
}

func (e ErrPacketWrite) Error() string {
return fmt.Sprintf("failed to write packet message: %v", e.Source)
}

func (e ErrPacketWrite) Unwrap() error {
return e.Source
}

type ErrUnexpectedPubKeyType struct {
Expected string
Got string
}

func (e ErrUnexpectedPubKeyType) Error() string {
return fmt.Sprintf("expected pubkey type %s, got %s", e.Expected, e.Got)
}

type ErrDecryptFrame struct {
Source error
}

func (e ErrDecryptFrame) Error() string {
return fmt.Sprintf("SecretConnection: failed to decrypt the frame: %v", e.Source)
}

func (e ErrDecryptFrame) Unwrap() error {
return e.Source
}

type ErrPacketTooBig struct {
Received int
Max int
}

func (e ErrPacketTooBig) Error() string {
return fmt.Sprintf("received message exceeds available capacity (max: %d, got: %d)", e.Max, e.Received)
}

type ErrChunkTooBig struct {
Received int
Max int
}

func (e ErrChunkTooBig) Error() string {
return fmt.Sprintf("chunk too big (max: %d, got %d)", e.Max, e.Received)
}
22 changes: 10 additions & 12 deletions p2p/conn/evil_secret_connection_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,26 +244,24 @@ func (c *evilConn) signChallenge() []byte {
// MakeSecretConnection errors at different stages.
func TestMakeSecretConnection(t *testing.T) {
testCases := []struct {
name string
conn *evilConn
errMsg string
name string
conn *evilConn
checkError func(error) bool // Function to check if the error matches the expectation
}{
{"refuse to share ethimeral key", newEvilConn(false, false, false, false), "EOF"},
{"share bad ethimeral key", newEvilConn(true, true, false, false), "wrong wireType"},
{"refuse to share auth signature", newEvilConn(true, false, false, false), "EOF"},
{"share bad auth signature", newEvilConn(true, false, true, true), "failed to decrypt SecretConnection"},
{"all good", newEvilConn(true, false, true, false), ""},
{"refuse to share ethimeral key", newEvilConn(false, false, false, false), func(err error) bool { return err == io.EOF }},
{"share bad ethimeral key", newEvilConn(true, true, false, false), func(err error) bool { return assert.Contains(t, err.Error(), "wrong wireType") }},
{"refuse to share auth signature", newEvilConn(true, false, false, false), func(err error) bool { return err == io.EOF }},
{"share bad auth signature", newEvilConn(true, false, true, true), func(err error) bool { return errors.As(err, &ErrDecryptFrame{}) }},
{"all good", newEvilConn(true, false, true, false), func(err error) bool { return err == nil }},
}

for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
privKey := ed25519.GenPrivKey()
_, err := MakeSecretConnection(tc.conn, privKey)
if tc.errMsg != "" {
if assert.Error(t, err) { //nolint:testifylint // require.Error doesn't work with the conditional here
assert.Contains(t, err.Error(), tc.errMsg)
}
if tc.checkError != nil {
assert.True(t, tc.checkError(err))
} else {
require.NoError(t, err)
}
Expand Down
30 changes: 20 additions & 10 deletions p2p/conn/secret_connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"crypto/sha256"
"encoding/binary"
"errors"
"fmt"
"io"
"math"
"net"
Expand Down Expand Up @@ -46,8 +45,7 @@ const (
)

var (
ErrSmallOrderRemotePubKey = errors.New("detected low order point from remote peer")

ErrSmallOrderRemotePubKey = errors.New("detected low order point from remote peer")
secretConnKeyAndChallengeGen = []byte("TENDERMINT_SECRET_CONNECTION_KEY_AND_CHALLENGE_GEN")
)

Expand Down Expand Up @@ -133,11 +131,12 @@ func MakeSecretConnection(conn io.ReadWriteCloser, locPrivKey crypto.PrivKey) (*

sendAead, err := chacha20poly1305.New(sendSecret[:])
if err != nil {
return nil, errors.New("invalid send SecretConnection Key")
return nil, ErrInvalidSecretConnKeySend
}

recvAead, err := chacha20poly1305.New(recvSecret[:])
if err != nil {
return nil, errors.New("invalid receive SecretConnection Key")
return nil, ErrInvalidSecretConnKeyRecv
}

sc := &SecretConnection{
Expand All @@ -162,11 +161,16 @@ func MakeSecretConnection(conn io.ReadWriteCloser, locPrivKey crypto.PrivKey) (*
}

remPubKey, remSignature := authSigMsg.Key, authSigMsg.Sig
// Usage in your function
if _, ok := remPubKey.(ed25519.PubKey); !ok {
return nil, fmt.Errorf("expected ed25519 pubkey, got %T", remPubKey)
return nil, ErrUnexpectedPubKeyType{
Expected: ed25519.KeyType,
Got: remPubKey.Type(),
}
}

if !remPubKey.VerifySignature(challenge[:], remSignature) {
return nil, errors.New("challenge verification failed")
return nil, ErrChallengeVerification
}

// We've authorized.
Expand Down Expand Up @@ -214,6 +218,7 @@ func (sc *SecretConnection) Write(data []byte) (n int, err error) {
if err != nil {
return err
}

n += len(chunk)
return nil
}(); err != nil {
Expand Down Expand Up @@ -249,17 +254,22 @@ func (sc *SecretConnection) Read(data []byte) (n int, err error) {
defer pool.Put(frame)
_, err = sc.recvAead.Open(frame[:0], sc.recvNonce[:], sealedFrame, nil)
if err != nil {
return n, fmt.Errorf("failed to decrypt SecretConnection: %w", err)
return n, ErrDecryptFrame{Source: err}
}

incrNonce(sc.recvNonce)
// end decryption

// copy checkLength worth into data,
// set recvBuffer to the rest.
chunkLength := binary.LittleEndian.Uint32(frame) // read the first four bytes
if chunkLength > dataMaxSize {
return 0, errors.New("chunkLength is greater than dataMaxSize")
return 0, ErrChunkTooBig{
Received: int(chunkLength),
Max: dataMaxSize,
}
}

chunk := frame[dataLenSize : dataLenSize+chunkLength]
n = copy(data, chunk)
if n < len(chunk) {
Expand Down Expand Up @@ -289,7 +299,7 @@ func genEphKeys() (ephPub, ephPriv *[32]byte) {
// see: https://github.com/dalek-cryptography/x25519-dalek/blob/34676d336049df2bba763cc076a75e47ae1f170f/src/x25519.rs#L56-L74
ephPub, ephPriv, err = box.GenerateKey(crand.Reader)
if err != nil {
panic("Could not generate ephemeral key-pair")
panic("failed to generate ephemeral key-pair")
}
return ephPub, ephPriv
}
Expand Down
3 changes: 2 additions & 1 deletion p2p/conn/secret_connection_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package conn
import (
"bufio"
"encoding/hex"
"errors"
"flag"
"fmt"
"io"
Expand Down Expand Up @@ -155,7 +156,7 @@ func TestSecretConnectionReadWrite(t *testing.T) {
readBuffer := make([]byte, dataMaxSize)
for {
n, err := nodeSecretConn.Read(readBuffer)
if err == io.EOF {
if errors.Is(err, io.EOF) {
if err := nodeConn.PipeReader.Close(); err != nil {
t.Error(err)
return nil, true, err
Expand Down
Loading

0 comments on commit c3f7ea3

Please sign in to comment.