mirror of
https://source.quilibrium.com/quilibrium/ceremonyclient.git
synced 2024-11-20 15:15:18 +00:00
239 lines
5.4 KiB
Go
239 lines
5.4 KiB
Go
package p2p
|
||
|
||
import (
|
||
"crypto/rand"
|
||
"encoding/binary"
|
||
"sync"
|
||
"time"
|
||
|
||
"github.com/pkg/errors"
|
||
"google.golang.org/protobuf/proto"
|
||
"source.quilibrium.com/quilibrium/monorepo/go-libp2p-blossomsub/pb"
|
||
"source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/core/curves"
|
||
"source.quilibrium.com/quilibrium/monorepo/node/crypto/channel"
|
||
"source.quilibrium.com/quilibrium/monorepo/node/keys"
|
||
"source.quilibrium.com/quilibrium/monorepo/node/protobufs"
|
||
)
|
||
|
||
// A simplified P2P channel – the pair of actors communicating is public
|
||
// knowledge, even though the data itself is encrypted.
|
||
type PublicP2PChannel struct {
|
||
participant *channel.DoubleRatchetParticipant
|
||
sendMap map[uint64][]byte
|
||
receiveMap map[uint64][]byte
|
||
pubSub PubSub
|
||
sendFilter []byte
|
||
receiveFilter []byte
|
||
initiator bool
|
||
senderSeqNo uint64
|
||
receiverSeqNo uint64
|
||
receiveChan chan []byte
|
||
receiveMx sync.Mutex
|
||
publicChannelClient PublicChannelClient
|
||
}
|
||
|
||
type PublicChannelClient interface {
|
||
Send(m *protobufs.P2PChannelEnvelope) error
|
||
Recv() (*protobufs.P2PChannelEnvelope, error)
|
||
}
|
||
|
||
func NewPublicP2PChannel(
|
||
publicChannelClient PublicChannelClient,
|
||
senderIdentifier, receiverIdentifier []byte,
|
||
initiator bool,
|
||
sendingIdentityPrivateKey curves.Scalar,
|
||
sendingSignedPrePrivateKey curves.Scalar,
|
||
receivingIdentityKey curves.Point,
|
||
receivingSignedPreKey curves.Point,
|
||
curve *curves.Curve,
|
||
keyManager keys.KeyManager,
|
||
pubSub PubSub,
|
||
) (*PublicP2PChannel, error) {
|
||
sendFilter := append(
|
||
append([]byte{}, senderIdentifier...),
|
||
receiverIdentifier...,
|
||
)
|
||
receiveFilter := append(
|
||
append([]byte{}, receiverIdentifier...),
|
||
senderIdentifier...,
|
||
)
|
||
|
||
ch := &PublicP2PChannel{
|
||
publicChannelClient: publicChannelClient,
|
||
sendMap: map[uint64][]byte{},
|
||
receiveMap: map[uint64][]byte{},
|
||
initiator: initiator,
|
||
sendFilter: sendFilter,
|
||
receiveFilter: receiveFilter,
|
||
pubSub: pubSub,
|
||
senderSeqNo: 0,
|
||
receiverSeqNo: 0,
|
||
receiveChan: make(chan []byte),
|
||
}
|
||
|
||
var err error
|
||
var participant *channel.DoubleRatchetParticipant
|
||
if initiator {
|
||
sendingEphemeralPrivateKey := curve.Scalar.Random(
|
||
rand.Reader,
|
||
)
|
||
x3dh := channel.SenderX3DH(
|
||
sendingIdentityPrivateKey,
|
||
sendingSignedPrePrivateKey,
|
||
receivingIdentityKey,
|
||
receivingSignedPreKey,
|
||
96,
|
||
)
|
||
participant, err = channel.NewDoubleRatchetParticipant(
|
||
x3dh[:32],
|
||
x3dh[32:64],
|
||
x3dh[64:],
|
||
true,
|
||
sendingEphemeralPrivateKey,
|
||
receivingSignedPreKey,
|
||
curve,
|
||
keyManager,
|
||
)
|
||
if err != nil {
|
||
return nil, errors.Wrap(err, "new public p2p channel")
|
||
}
|
||
} else {
|
||
x3dh := channel.SenderX3DH(
|
||
sendingIdentityPrivateKey,
|
||
sendingSignedPrePrivateKey,
|
||
receivingIdentityKey,
|
||
receivingSignedPreKey,
|
||
96,
|
||
)
|
||
participant, err = channel.NewDoubleRatchetParticipant(
|
||
x3dh[:32],
|
||
x3dh[32:64],
|
||
x3dh[64:],
|
||
false,
|
||
sendingSignedPrePrivateKey,
|
||
nil,
|
||
curve,
|
||
keyManager,
|
||
)
|
||
if err != nil {
|
||
return nil, errors.Wrap(err, "new public p2p channel")
|
||
}
|
||
}
|
||
|
||
ch.participant = participant
|
||
if publicChannelClient == nil {
|
||
pubSub.Subscribe(
|
||
sendFilter,
|
||
func(message *pb.Message) error { return nil },
|
||
true,
|
||
)
|
||
|
||
pubSub.Subscribe(
|
||
receiveFilter,
|
||
ch.handleReceive,
|
||
true,
|
||
)
|
||
}
|
||
|
||
return ch, nil
|
||
}
|
||
|
||
func (c *PublicP2PChannel) handleReceive(message *pb.Message) error {
|
||
envelope := &protobufs.P2PChannelEnvelope{}
|
||
if err := proto.Unmarshal(message.Data, envelope); err != nil {
|
||
return errors.Wrap(err, "handle receive")
|
||
}
|
||
|
||
c.receiveMx.Lock()
|
||
rawData, err := c.participant.RatchetDecrypt(envelope)
|
||
c.receiveMx.Unlock()
|
||
if err != nil {
|
||
return errors.Wrap(err, "handle receive")
|
||
}
|
||
|
||
seqNo := binary.BigEndian.Uint64(rawData[:8])
|
||
|
||
if seqNo == c.receiverSeqNo {
|
||
c.receiveChan <- rawData[8:]
|
||
} else {
|
||
c.receiveMx.Lock()
|
||
c.receiveMap[seqNo] = rawData[8:]
|
||
c.receiveMx.Unlock()
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func (c *PublicP2PChannel) Send(message []byte) error {
|
||
c.senderSeqNo++
|
||
message = append(
|
||
binary.BigEndian.AppendUint64(nil, c.senderSeqNo),
|
||
message...,
|
||
)
|
||
|
||
envelope, err := c.participant.RatchetEncrypt(message)
|
||
if err != nil {
|
||
return errors.Wrap(err, "send")
|
||
}
|
||
|
||
if c.publicChannelClient == nil {
|
||
rawBytes, err := proto.Marshal(envelope)
|
||
if err != nil {
|
||
return errors.Wrap(err, "send")
|
||
}
|
||
|
||
c.sendMap[c.senderSeqNo] = rawBytes
|
||
return errors.Wrap(
|
||
c.pubSub.PublishToBitmask(c.sendFilter, rawBytes),
|
||
"send",
|
||
)
|
||
} else {
|
||
return errors.Wrap(
|
||
c.publicChannelClient.Send(envelope),
|
||
"send",
|
||
)
|
||
}
|
||
}
|
||
|
||
func (c *PublicP2PChannel) Receive() ([]byte, error) {
|
||
c.receiverSeqNo++
|
||
if c.publicChannelClient == nil {
|
||
after := time.After(20 * time.Second)
|
||
select {
|
||
case msg := <-c.receiveChan:
|
||
return msg, nil
|
||
case <-after:
|
||
return nil, errors.Wrap(errors.New("timed out"), "receive")
|
||
}
|
||
} else {
|
||
msg, err := c.publicChannelClient.Recv()
|
||
if err != nil {
|
||
return nil, errors.Wrap(err, "receive")
|
||
}
|
||
|
||
rawData, err := c.participant.RatchetDecrypt(msg)
|
||
if err != nil {
|
||
return nil, errors.Wrap(err, "receive")
|
||
}
|
||
|
||
seqNo := binary.BigEndian.Uint64(rawData[:8])
|
||
|
||
if seqNo == c.receiverSeqNo {
|
||
return rawData[8:], nil
|
||
} else {
|
||
c.receiveMx.Lock()
|
||
c.receiveMap[seqNo] = rawData[8:]
|
||
c.receiveMx.Unlock()
|
||
}
|
||
|
||
return nil, nil
|
||
}
|
||
}
|
||
|
||
func (c *PublicP2PChannel) Close() {
|
||
if c.publicChannelClient == nil {
|
||
c.pubSub.Unsubscribe(c.sendFilter, true)
|
||
c.pubSub.Unsubscribe(c.receiveFilter, true)
|
||
}
|
||
}
|