2016-06-27 01:56:23 +00:00
|
|
|
package utils
|
|
|
|
|
|
|
|
import (
|
2016-10-02 22:39:08 +00:00
|
|
|
"bytes"
|
2019-01-22 20:21:56 +00:00
|
|
|
"crypto/rand"
|
2016-06-27 01:56:23 +00:00
|
|
|
"encoding/binary"
|
2019-01-22 20:21:56 +00:00
|
|
|
"errors"
|
2020-02-10 19:30:32 +00:00
|
|
|
"git.openprivacy.ca/openprivacy/log"
|
2019-01-22 20:21:56 +00:00
|
|
|
"golang.org/x/crypto/nacl/secretbox"
|
2016-10-02 22:39:08 +00:00
|
|
|
"io"
|
2019-11-08 00:11:14 +00:00
|
|
|
"sync"
|
2016-06-27 01:56:23 +00:00
|
|
|
)
|
|
|
|
|
2017-07-04 18:29:11 +00:00
|
|
|
const (
|
2018-01-08 00:51:46 +00:00
|
|
|
// InvalidPacketLengthError is returned whenever ricochet receives a packet too small or too large to conform to the spec.
|
2017-07-04 18:29:11 +00:00
|
|
|
InvalidPacketLengthError = Error("InvalidPacketLengthError")
|
2018-01-08 00:51:46 +00:00
|
|
|
|
2018-01-05 22:16:52 +00:00
|
|
|
// InvalidChannelIDError channels must be between 0 and 65535
|
2018-01-08 00:51:46 +00:00
|
|
|
InvalidChannelIDError = Error("InvalidChannelIDError")
|
2017-07-04 18:29:11 +00:00
|
|
|
)
|
|
|
|
|
2016-06-27 01:56:23 +00:00
|
|
|
// RicochetData is a structure containing the raw data and the channel it the
|
|
|
|
// message originated on.
|
|
|
|
type RicochetData struct {
|
|
|
|
Channel int32
|
|
|
|
Data []byte
|
|
|
|
}
|
|
|
|
|
2019-01-22 20:21:56 +00:00
|
|
|
// Equals compares a RicochetData object to another and returns true if contain
|
2016-11-08 23:04:11 +00:00
|
|
|
// the same data.
|
2016-10-02 22:39:08 +00:00
|
|
|
func (rd RicochetData) Equals(other RicochetData) bool {
|
|
|
|
return rd.Channel == other.Channel && bytes.Equal(rd.Data, other.Data)
|
|
|
|
}
|
|
|
|
|
2016-06-27 01:56:23 +00:00
|
|
|
// RicochetNetworkInterface abstract operations that interact with ricochet's
|
|
|
|
// packet layer.
|
|
|
|
type RicochetNetworkInterface interface {
|
2016-10-02 22:39:08 +00:00
|
|
|
SendRicochetPacket(dst io.Writer, channel int32, data []byte) error
|
|
|
|
RecvRicochetPacket(reader io.Reader) (RicochetData, error)
|
2016-06-27 01:56:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// RicochetNetwork is a concrete implementation of the RicochetNetworkInterface
|
|
|
|
type RicochetNetwork struct {
|
2019-01-22 20:21:56 +00:00
|
|
|
// Derived ephemeral session key for connection
|
|
|
|
key [32]byte
|
|
|
|
encrypt bool
|
2019-11-08 00:11:14 +00:00
|
|
|
lock sync.Mutex
|
2019-01-22 20:21:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// SetEncryptionKey sets the ephemeral encryption key for this session.
|
|
|
|
func (rn *RicochetNetwork) SetEncryptionKey(key [32]byte) {
|
2019-11-08 00:11:14 +00:00
|
|
|
rn.lock.Lock()
|
|
|
|
defer rn.lock.Unlock()
|
2019-01-22 20:21:56 +00:00
|
|
|
log.Debugf("turning on ephemeral session encryption for connection")
|
|
|
|
copy(rn.key[:], key[:])
|
|
|
|
|
|
|
|
rn.encrypt = true
|
2016-06-27 01:56:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// SendRicochetPacket places the data into a structure needed for the client to
|
|
|
|
// decode the packet and writes the packet to the network.
|
2016-10-02 22:39:08 +00:00
|
|
|
func (rn *RicochetNetwork) SendRicochetPacket(dst io.Writer, channel int32, data []byte) error {
|
|
|
|
packet := make([]byte, 4+len(data))
|
|
|
|
if len(packet) > 65535 {
|
2017-07-04 18:29:11 +00:00
|
|
|
return InvalidPacketLengthError
|
2016-06-27 01:56:23 +00:00
|
|
|
}
|
2016-10-02 22:39:08 +00:00
|
|
|
binary.BigEndian.PutUint16(packet[0:2], uint16(len(packet)))
|
|
|
|
if channel < 0 || channel > 65535 {
|
2017-07-04 18:29:11 +00:00
|
|
|
return InvalidChannelIDError
|
2016-10-02 22:39:08 +00:00
|
|
|
}
|
|
|
|
binary.BigEndian.PutUint16(packet[2:4], uint16(channel))
|
|
|
|
copy(packet[4:], data[:])
|
2016-06-27 01:56:23 +00:00
|
|
|
|
2019-11-08 00:11:14 +00:00
|
|
|
rn.lock.Lock()
|
2019-01-22 20:21:56 +00:00
|
|
|
if rn.encrypt {
|
|
|
|
var nonce [24]byte
|
|
|
|
if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil {
|
|
|
|
panic(err)
|
|
|
|
|
|
|
|
}
|
|
|
|
encrypted := secretbox.Seal(nonce[:], packet[2:], &nonce, &rn.key)
|
|
|
|
binary.BigEndian.PutUint16(packet[0:2], uint16(len(encrypted)+2))
|
|
|
|
packet = append(packet[0:2], encrypted...)
|
|
|
|
}
|
2019-11-08 00:11:14 +00:00
|
|
|
rn.lock.Unlock()
|
2019-01-22 20:21:56 +00:00
|
|
|
|
2016-10-02 22:39:08 +00:00
|
|
|
for pos := 0; pos < len(packet); {
|
|
|
|
n, err := dst.Write(packet[pos:])
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2016-06-27 01:56:23 +00:00
|
|
|
}
|
2016-10-02 22:39:08 +00:00
|
|
|
pos += n
|
|
|
|
}
|
2019-01-22 20:21:56 +00:00
|
|
|
|
2016-10-02 22:39:08 +00:00
|
|
|
return nil
|
|
|
|
}
|
2016-06-27 01:56:23 +00:00
|
|
|
|
2016-10-02 22:39:08 +00:00
|
|
|
// RecvRicochetPacket returns the next packet from reader as a RicochetData
|
|
|
|
// structure, or an error.
|
|
|
|
func (rn *RicochetNetwork) RecvRicochetPacket(reader io.Reader) (RicochetData, error) {
|
2019-01-22 20:21:56 +00:00
|
|
|
|
2016-10-02 22:39:08 +00:00
|
|
|
packet := RicochetData{}
|
2016-06-27 01:56:23 +00:00
|
|
|
|
2016-10-02 22:39:08 +00:00
|
|
|
// Read the four-byte header to get packet length
|
2019-01-22 20:21:56 +00:00
|
|
|
header := make([]byte, 2)
|
2016-10-02 22:39:08 +00:00
|
|
|
if _, err := io.ReadAtLeast(reader, header, len(header)); err != nil {
|
|
|
|
return packet, err
|
|
|
|
}
|
2016-06-27 01:56:23 +00:00
|
|
|
|
2016-10-02 22:39:08 +00:00
|
|
|
size := int(binary.BigEndian.Uint16(header[0:2]))
|
|
|
|
if size < 4 {
|
2017-07-04 18:29:11 +00:00
|
|
|
return packet, InvalidPacketLengthError
|
2016-10-02 22:39:08 +00:00
|
|
|
}
|
2016-06-27 01:56:23 +00:00
|
|
|
|
2019-01-22 20:21:56 +00:00
|
|
|
packetBytes := make([]byte, size-2)
|
|
|
|
_, err := io.ReadAtLeast(reader, packetBytes, size-2)
|
|
|
|
if err != nil {
|
2016-10-02 22:39:08 +00:00
|
|
|
return packet, err
|
2016-06-27 01:56:23 +00:00
|
|
|
}
|
2016-10-02 22:39:08 +00:00
|
|
|
|
2019-11-08 00:11:14 +00:00
|
|
|
rn.lock.Lock()
|
2019-01-22 20:21:56 +00:00
|
|
|
if rn.encrypt {
|
|
|
|
var decryptNonce [24]byte
|
|
|
|
if len(packetBytes) > 24 {
|
|
|
|
copy(decryptNonce[:], packetBytes[:24])
|
|
|
|
decrypted, ok := secretbox.Open(nil, packetBytes[24:], &decryptNonce, &rn.key)
|
|
|
|
if ok {
|
|
|
|
packetBytes = decrypted
|
|
|
|
} else {
|
|
|
|
return packet, errors.New("failed to decrypt encrypted ricochet packet")
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
return packet, errors.New("ciphertext length was too short")
|
|
|
|
}
|
|
|
|
}
|
2019-11-08 00:11:14 +00:00
|
|
|
rn.lock.Unlock()
|
2019-01-22 20:21:56 +00:00
|
|
|
|
|
|
|
packet.Channel = int32(binary.BigEndian.Uint16(packetBytes[0:2]))
|
|
|
|
packet.Data = make([]byte, len(packetBytes)-2)
|
|
|
|
copy(packet.Data[:], packetBytes[2:])
|
2016-10-02 22:39:08 +00:00
|
|
|
return packet, nil
|
2016-06-27 01:56:23 +00:00
|
|
|
}
|