mirror of
https://source.quilibrium.com/quilibrium/ceremonyclient.git
synced 2024-11-14 04:05:17 +00:00
195 lines
4.4 KiB
Go
195 lines
4.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"
|
|||
|
"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 *crypto.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
|
|||
|
}
|
|||
|
|
|||
|
func NewPublicP2PChannel(
|
|||
|
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...,
|
|||
|
)
|
|||
|
|
|||
|
channel := &PublicP2PChannel{
|
|||
|
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 *crypto.DoubleRatchetParticipant
|
|||
|
if initiator {
|
|||
|
sendingEphemeralPrivateKey := curve.Scalar.Random(
|
|||
|
rand.Reader,
|
|||
|
)
|
|||
|
x3dh := crypto.SenderX3DH(
|
|||
|
sendingIdentityPrivateKey,
|
|||
|
sendingSignedPrePrivateKey,
|
|||
|
receivingIdentityKey,
|
|||
|
receivingSignedPreKey,
|
|||
|
96,
|
|||
|
)
|
|||
|
participant, err = crypto.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 := crypto.SenderX3DH(
|
|||
|
sendingIdentityPrivateKey,
|
|||
|
sendingSignedPrePrivateKey,
|
|||
|
receivingIdentityKey,
|
|||
|
receivingSignedPreKey,
|
|||
|
96,
|
|||
|
)
|
|||
|
participant, err = crypto.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")
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
channel.participant = participant
|
|||
|
|
|||
|
pubSub.Subscribe(
|
|||
|
sendFilter,
|
|||
|
func(message *pb.Message) error { return nil },
|
|||
|
true,
|
|||
|
)
|
|||
|
|
|||
|
pubSub.Subscribe(
|
|||
|
receiveFilter,
|
|||
|
channel.handleReceive,
|
|||
|
true,
|
|||
|
)
|
|||
|
|
|||
|
return channel, 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")
|
|||
|
}
|
|||
|
|
|||
|
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")
|
|||
|
}
|
|||
|
|
|||
|
func (c *PublicP2PChannel) Receive() ([]byte, error) {
|
|||
|
c.receiverSeqNo++
|
|||
|
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")
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func (c *PublicP2PChannel) Close() {
|
|||
|
c.pubSub.Unsubscribe(c.sendFilter, true)
|
|||
|
c.pubSub.Unsubscribe(c.receiveFilter, true)
|
|||
|
}
|