ceremonyclient/go-libp2p/core/sec/insecure/insecure.go
2023-08-20 23:07:43 -05:00

234 lines
6.4 KiB
Go

// Package insecure provides an insecure, unencrypted implementation of the SecureConn and SecureTransport interfaces.
//
// Recommended only for testing and other non-production usage.
package insecure
import (
"context"
"fmt"
"io"
"net"
ci "github.com/libp2p/go-libp2p/core/crypto"
"github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/protocol"
"github.com/libp2p/go-libp2p/core/sec"
"github.com/libp2p/go-libp2p/core/sec/insecure/pb"
"github.com/libp2p/go-msgio"
"google.golang.org/protobuf/proto"
)
//go:generate protoc --proto_path=$PWD:$PWD/../../.. --go_out=. --go_opt=Mpb/plaintext.proto=./pb pb/plaintext.proto
// ID is the multistream-select protocol ID that should be used when identifying
// this security transport.
const ID = "/plaintext/2.0.0"
// Transport is a no-op stream security transport. It provides no
// security and simply mocks the security methods. Identity methods
// return the local peer's ID and private key, and whatever the remote
// peer presents as their ID and public key.
// No authentication of the remote identity is performed.
type Transport struct {
id peer.ID
key ci.PrivKey
protocolID protocol.ID
}
var _ sec.SecureTransport = &Transport{}
// NewWithIdentity constructs a new insecure transport. The public key is sent to
// remote peers. No security is provided.
func NewWithIdentity(protocolID protocol.ID, id peer.ID, key ci.PrivKey) *Transport {
return &Transport{
protocolID: protocolID,
id: id,
key: key,
}
}
// LocalPeer returns the transport's local peer ID.
func (t *Transport) LocalPeer() peer.ID {
return t.id
}
// SecureInbound *pretends to secure* an inbound connection to the given peer.
// It sends the local peer's ID and public key, and receives the same from the remote peer.
// No validation is performed as to the authenticity or ownership of the provided public key,
// and the key exchange provides no security.
//
// SecureInbound may fail if the remote peer sends an ID and public key that are inconsistent
// with each other, or if a network error occurs during the ID exchange.
func (t *Transport) SecureInbound(_ context.Context, insecure net.Conn, p peer.ID) (sec.SecureConn, error) {
conn := &Conn{
Conn: insecure,
local: t.id,
localPubKey: t.key.GetPublic(),
}
if err := conn.runHandshakeSync(); err != nil {
return nil, err
}
if p != "" && p != conn.remote {
return nil, fmt.Errorf("remote peer sent unexpected peer ID. expected=%s received=%s", p, conn.remote)
}
return conn, nil
}
// SecureOutbound *pretends to secure* an outbound connection to the given peer.
// It sends the local peer's ID and public key, and receives the same from the remote peer.
// No validation is performed as to the authenticity or ownership of the provided public key,
// and the key exchange provides no security.
//
// SecureOutbound may fail if the remote peer sends an ID and public key that are inconsistent
// with each other, or if the ID sent by the remote peer does not match the one dialed. It may
// also fail if a network error occurs during the ID exchange.
func (t *Transport) SecureOutbound(_ context.Context, insecure net.Conn, p peer.ID) (sec.SecureConn, error) {
conn := &Conn{
Conn: insecure,
local: t.id,
localPubKey: t.key.GetPublic(),
}
if err := conn.runHandshakeSync(); err != nil {
return nil, err
}
if p != conn.remote {
return nil, fmt.Errorf("remote peer sent unexpected peer ID. expected=%s received=%s",
p, conn.remote)
}
return conn, nil
}
func (t *Transport) ID() protocol.ID { return t.protocolID }
// Conn is the connection type returned by the insecure transport.
type Conn struct {
net.Conn
local, remote peer.ID
localPubKey, remotePubKey ci.PubKey
}
func makeExchangeMessage(pubkey ci.PubKey) (*pb.Exchange, error) {
keyMsg, err := ci.PublicKeyToProto(pubkey)
if err != nil {
return nil, err
}
id, err := peer.IDFromPublicKey(pubkey)
if err != nil {
return nil, err
}
return &pb.Exchange{
Id: []byte(id),
Pubkey: keyMsg,
}, nil
}
func (ic *Conn) runHandshakeSync() error {
// If we were initialized without keys, behave as in plaintext/1.0.0 (do nothing)
if ic.localPubKey == nil {
return nil
}
// Generate an Exchange message
msg, err := makeExchangeMessage(ic.localPubKey)
if err != nil {
return err
}
// Send our Exchange and read theirs
remoteMsg, err := readWriteMsg(ic.Conn, msg)
if err != nil {
return err
}
// Pull remote ID and public key from message
remotePubkey, err := ci.PublicKeyFromProto(remoteMsg.Pubkey)
if err != nil {
return err
}
remoteID, err := peer.IDFromBytes(remoteMsg.Id)
if err != nil {
return err
}
// Validate that ID matches public key
if !remoteID.MatchesPublicKey(remotePubkey) {
calculatedID, _ := peer.IDFromPublicKey(remotePubkey)
return fmt.Errorf("remote peer id does not match public key. id=%s calculated_id=%s",
remoteID, calculatedID)
}
// Add remote ID and key to conn state
ic.remotePubKey = remotePubkey
ic.remote = remoteID
return nil
}
// read and write a message at the same time.
func readWriteMsg(rw io.ReadWriter, out *pb.Exchange) (*pb.Exchange, error) {
const maxMessageSize = 1 << 16
outBytes, err := proto.Marshal(out)
if err != nil {
return nil, err
}
wresult := make(chan error)
go func() {
w := msgio.NewVarintWriter(rw)
wresult <- w.WriteMsg(outBytes)
}()
r := msgio.NewVarintReaderSize(rw, maxMessageSize)
b, err1 := r.ReadMsg()
// Always wait for the read to finish.
err2 := <-wresult
if err1 != nil {
return nil, err1
}
if err2 != nil {
r.ReleaseMsg(b)
return nil, err2
}
inMsg := new(pb.Exchange)
err = proto.Unmarshal(b, inMsg)
return inMsg, err
}
// LocalPeer returns the local peer ID.
func (ic *Conn) LocalPeer() peer.ID {
return ic.local
}
// RemotePeer returns the remote peer ID if we initiated the dial. Otherwise, it
// returns "" (because this connection isn't actually secure).
func (ic *Conn) RemotePeer() peer.ID {
return ic.remote
}
// RemotePublicKey returns whatever public key was given by the remote peer.
// Note that no verification of ownership is done, as this connection is not secure.
func (ic *Conn) RemotePublicKey() ci.PubKey {
return ic.remotePubKey
}
// ConnState returns the security connection's state information.
func (ic *Conn) ConnState() network.ConnectionState {
return network.ConnectionState{}
}
var _ sec.SecureTransport = (*Transport)(nil)
var _ sec.SecureConn = (*Conn)(nil)