forked from openprivacy/libricochet-go
Merge branch 'v1-encrypt' of openprivacy/libricochet-go into master
This commit is contained in:
commit
8f00e26b81
|
@ -25,4 +25,5 @@ type Channel struct {
|
||||||
SendMessage func([]byte)
|
SendMessage func([]byte)
|
||||||
CloseChannel func()
|
CloseChannel func()
|
||||||
DelegateAuthorization func()
|
DelegateAuthorization func()
|
||||||
|
DelegateEncryption func([32]byte)
|
||||||
}
|
}
|
||||||
|
|
|
@ -152,11 +152,13 @@ func (ah *Server3DHAuthChannel) Packet(data []byte) {
|
||||||
if ok && string(decrypted) == "Hello World" {
|
if ok && string(decrypted) == "Hello World" {
|
||||||
allowed, known := ah.ServerAuthValid(utils.GetTorV3Hostname(ah.clientPubKey), ah.clientPubKey)
|
allowed, known := ah.ServerAuthValid(utils.GetTorV3Hostname(ah.clientPubKey), ah.clientPubKey)
|
||||||
ah.channel.DelegateAuthorization()
|
ah.channel.DelegateAuthorization()
|
||||||
|
ah.channel.DelegateEncryption(key)
|
||||||
log.Debugf("3DH Session Decrypted OK. Authenticating Connection!")
|
log.Debugf("3DH Session Decrypted OK. Authenticating Connection!")
|
||||||
messageBuilder := new(utils.MessageBuilder)
|
messageBuilder := new(utils.MessageBuilder)
|
||||||
result := messageBuilder.AuthResult3DH(allowed, known)
|
result := messageBuilder.AuthResult3DH(allowed, known)
|
||||||
ah.channel.SendMessage(result)
|
ah.channel.SendMessage(result)
|
||||||
ah.channel.CloseChannel()
|
ah.channel.CloseChannel()
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ func TestServer3DHAuthChannel(t *testing.T) {
|
||||||
cc.ID = 1
|
cc.ID = 1
|
||||||
closed := false
|
closed := false
|
||||||
cc.CloseChannel = func() { closed = true }
|
cc.CloseChannel = func() { closed = true }
|
||||||
|
cc.DelegateEncryption = func([32]byte) {}
|
||||||
clientChannel := new(outbound.Client3DHAuthChannel)
|
clientChannel := new(outbound.Client3DHAuthChannel)
|
||||||
pub, priv, _ := ed25519.GenerateKey(rand.Reader)
|
pub, priv, _ := ed25519.GenerateKey(rand.Reader)
|
||||||
cid := identity.InitializeV3("", &priv, &pub)
|
cid := identity.InitializeV3("", &priv, &pub)
|
||||||
|
@ -75,6 +76,7 @@ func TestServer3DHAuthChannelReject(t *testing.T) {
|
||||||
cc := new(channels.Channel)
|
cc := new(channels.Channel)
|
||||||
cc.ID = 1
|
cc.ID = 1
|
||||||
cc.CloseChannel = func() {}
|
cc.CloseChannel = func() {}
|
||||||
|
cc.DelegateEncryption = func([32]byte) {}
|
||||||
clientChannel := new(outbound.Client3DHAuthChannel)
|
clientChannel := new(outbound.Client3DHAuthChannel)
|
||||||
pub, priv, _ := ed25519.GenerateKey(rand.Reader)
|
pub, priv, _ := ed25519.GenerateKey(rand.Reader)
|
||||||
cid := identity.InitializeV3("", &priv, &pub)
|
cid := identity.InitializeV3("", &priv, &pub)
|
||||||
|
|
|
@ -27,6 +27,7 @@ type Client3DHAuthChannel struct {
|
||||||
serverPubKey, serverEphemeralPublicKey, clientEphemeralPublicKey ed25519.PublicKey
|
serverPubKey, serverEphemeralPublicKey, clientEphemeralPublicKey ed25519.PublicKey
|
||||||
clientEphemeralPrivateKey ed25519.PrivateKey
|
clientEphemeralPrivateKey ed25519.PrivateKey
|
||||||
channel *channels.Channel
|
channel *channels.Channel
|
||||||
|
key [32]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// Type returns the type string for this channel, e.g. "im.ricochet.chat".
|
// Type returns the type string for this channel, e.g. "im.ricochet.chat".
|
||||||
|
@ -135,12 +136,13 @@ func (ah *Client3DHAuthChannel) OpenOutboundResult(err error, crm *Protocol_Data
|
||||||
}
|
}
|
||||||
|
|
||||||
pkey := pbkdf2.Key(secret[:], secret[:], 4096, 32, sha3.New512)
|
pkey := pbkdf2.Key(secret[:], secret[:], 4096, 32, sha3.New512)
|
||||||
var key [32]byte
|
|
||||||
copy(key[:], pkey[:])
|
copy(ah.key[:], pkey[:])
|
||||||
encrypted := secretbox.Seal(nonce[:], []byte("Hello World"), &nonce, &key)
|
encrypted := secretbox.Seal(nonce[:], []byte("Hello World"), &nonce, &ah.key)
|
||||||
messageBuilder := new(utils.MessageBuilder)
|
messageBuilder := new(utils.MessageBuilder)
|
||||||
proof := messageBuilder.Proof3DH(encrypted)
|
proof := messageBuilder.Proof3DH(encrypted)
|
||||||
ah.channel.SendMessage(proof)
|
ah.channel.SendMessage(proof)
|
||||||
|
ah.channel.DelegateEncryption(ah.key)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Packet is called for each raw packet received on this channel.
|
// Packet is called for each raw packet received on this channel.
|
||||||
|
|
|
@ -229,6 +229,9 @@ func (rc *Connection) buildChannel(handler channels.Handler, openChannelFunc fun
|
||||||
rc.SendRicochetPacket(rc.conn, channel.ID, []byte{})
|
rc.SendRicochetPacket(rc.conn, channel.ID, []byte{})
|
||||||
rc.channelManager.RemoveChannel(channel.ID)
|
rc.channelManager.RemoveChannel(channel.ID)
|
||||||
}
|
}
|
||||||
|
channel.DelegateEncryption = func(key [32]byte) {
|
||||||
|
rc.SetEncryptionKey(key)
|
||||||
|
}
|
||||||
return channel, nil
|
return channel, nil
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -8,7 +8,6 @@ import (
|
||||||
"git.openprivacy.ca/openprivacy/libricochet-go/identity"
|
"git.openprivacy.ca/openprivacy/libricochet-go/identity"
|
||||||
"git.openprivacy.ca/openprivacy/libricochet-go/log"
|
"git.openprivacy.ca/openprivacy/libricochet-go/log"
|
||||||
"git.openprivacy.ca/openprivacy/libricochet-go/utils"
|
"git.openprivacy.ca/openprivacy/libricochet-go/utils"
|
||||||
"os"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -75,9 +74,9 @@ func (bot *ChatEchoBot) ChatMessage(messageID uint32, when time.Time, message st
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func SendMessage(rai *application.Instance, message string) {
|
func SendMessage(rai *application.Instance, message string) error {
|
||||||
log.Infof("SendMessage(to: %v, %v)\n", rai.RemoteHostname, message)
|
log.Infof("SendMessage(to: %v, %v)\n", rai.RemoteHostname, message)
|
||||||
rai.Connection.Do(func() error {
|
return rai.Connection.Do(func() error {
|
||||||
|
|
||||||
log.Infof("Finding Chat Channel")
|
log.Infof("Finding Chat Channel")
|
||||||
channel := rai.Connection.Channel("im.ricochet.chat", channels.Outbound)
|
channel := rai.Connection.Channel("im.ricochet.chat", channels.Outbound)
|
||||||
|
@ -99,7 +98,7 @@ func (bot *ChatEchoBot) ChatMessageAck(messageID uint32, accepted bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestApplicationIntegration(t *testing.T) {
|
func TestApplicationIntegration(t *testing.T) {
|
||||||
log.SetLevel(log.LevelInfo)
|
log.SetLevel(log.LevelDebug)
|
||||||
startGoRoutines := runtime.NumGoroutine()
|
startGoRoutines := runtime.NumGoroutine()
|
||||||
|
|
||||||
acn, err := connectivity.StartTor(".", "")
|
acn, err := connectivity.StartTor(".", "")
|
||||||
|
@ -177,11 +176,11 @@ func TestApplicationIntegration(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error Alice connecting to Bob: %v", err)
|
t.Fatalf("Error Alice connecting to Bob: %v", err)
|
||||||
}
|
}
|
||||||
time.Sleep(10 * time.Second)
|
time.Sleep(30 * time.Second)
|
||||||
|
|
||||||
fmt.Println("Alice request open chat channel...")
|
fmt.Println("Alice request open chat channel...")
|
||||||
// TODO: opening a channel should be easier?
|
// TODO: opening a channel should be easier?
|
||||||
alicei.Connection.Do(func() error {
|
err = alicei.Connection.Do(func() error {
|
||||||
handler, err := alicei.OnOpenChannelRequest("im.ricochet.chat")
|
handler, err := alicei.OnOpenChannelRequest("im.ricochet.chat")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Infof("Could not get chat handler!\n")
|
log.Infof("Could not get chat handler!\n")
|
||||||
|
@ -190,14 +189,18 @@ func TestApplicationIntegration(t *testing.T) {
|
||||||
_, err = alicei.Connection.RequestOpenChannel("im.ricochet.chat", handler)
|
_, err = alicei.Connection.RequestOpenChannel("im.ricochet.chat", handler)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
time.Sleep(5 * time.Second)
|
|
||||||
|
|
||||||
fmt.Println("Alice sending message to Bob...")
|
|
||||||
SendMessage(alicei, "Hello Bob!")
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error dialing from Alice to Bob: %v", err)
|
t.Errorf("Error Opening a Channel: %v", err)
|
||||||
os.Exit(1)
|
}
|
||||||
|
|
||||||
|
time.Sleep(30 * time.Second)
|
||||||
|
|
||||||
|
fmt.Println("Alice sending message to Bob...")
|
||||||
|
err = SendMessage(alicei, "Hello Bob!")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Error dialing from Alice to Bob: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
time.Sleep(10 * time.Second)
|
time.Sleep(10 * time.Second)
|
||||||
|
|
|
@ -2,7 +2,11 @@ package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/rand"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"git.openprivacy.ca/openprivacy/libricochet-go/log"
|
||||||
|
"golang.org/x/crypto/nacl/secretbox"
|
||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -21,7 +25,7 @@ type RicochetData struct {
|
||||||
Data []byte
|
Data []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
//Equals compares a RicochetData object to another and returns true if contain
|
// Equals compares a RicochetData object to another and returns true if contain
|
||||||
// the same data.
|
// the same data.
|
||||||
func (rd RicochetData) Equals(other RicochetData) bool {
|
func (rd RicochetData) Equals(other RicochetData) bool {
|
||||||
return rd.Channel == other.Channel && bytes.Equal(rd.Data, other.Data)
|
return rd.Channel == other.Channel && bytes.Equal(rd.Data, other.Data)
|
||||||
|
@ -36,6 +40,17 @@ type RicochetNetworkInterface interface {
|
||||||
|
|
||||||
// RicochetNetwork is a concrete implementation of the RicochetNetworkInterface
|
// RicochetNetwork is a concrete implementation of the RicochetNetworkInterface
|
||||||
type RicochetNetwork struct {
|
type RicochetNetwork struct {
|
||||||
|
// Derived ephemeral session key for connection
|
||||||
|
key [32]byte
|
||||||
|
encrypt bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetEncryptionKey sets the ephemeral encryption key for this session.
|
||||||
|
func (rn *RicochetNetwork) SetEncryptionKey(key [32]byte) {
|
||||||
|
log.Debugf("turning on ephemeral session encryption for connection")
|
||||||
|
copy(rn.key[:], key[:])
|
||||||
|
|
||||||
|
rn.encrypt = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendRicochetPacket places the data into a structure needed for the client to
|
// SendRicochetPacket places the data into a structure needed for the client to
|
||||||
|
@ -52,6 +67,17 @@ func (rn *RicochetNetwork) SendRicochetPacket(dst io.Writer, channel int32, data
|
||||||
binary.BigEndian.PutUint16(packet[2:4], uint16(channel))
|
binary.BigEndian.PutUint16(packet[2:4], uint16(channel))
|
||||||
copy(packet[4:], data[:])
|
copy(packet[4:], data[:])
|
||||||
|
|
||||||
|
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...)
|
||||||
|
}
|
||||||
|
|
||||||
for pos := 0; pos < len(packet); {
|
for pos := 0; pos < len(packet); {
|
||||||
n, err := dst.Write(packet[pos:])
|
n, err := dst.Write(packet[pos:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -59,16 +85,18 @@ func (rn *RicochetNetwork) SendRicochetPacket(dst io.Writer, channel int32, data
|
||||||
}
|
}
|
||||||
pos += n
|
pos += n
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RecvRicochetPacket returns the next packet from reader as a RicochetData
|
// RecvRicochetPacket returns the next packet from reader as a RicochetData
|
||||||
// structure, or an error.
|
// structure, or an error.
|
||||||
func (rn *RicochetNetwork) RecvRicochetPacket(reader io.Reader) (RicochetData, error) {
|
func (rn *RicochetNetwork) RecvRicochetPacket(reader io.Reader) (RicochetData, error) {
|
||||||
|
|
||||||
packet := RicochetData{}
|
packet := RicochetData{}
|
||||||
|
|
||||||
// Read the four-byte header to get packet length
|
// Read the four-byte header to get packet length
|
||||||
header := make([]byte, 4)
|
header := make([]byte, 2)
|
||||||
if _, err := io.ReadAtLeast(reader, header, len(header)); err != nil {
|
if _, err := io.ReadAtLeast(reader, header, len(header)); err != nil {
|
||||||
return packet, err
|
return packet, err
|
||||||
}
|
}
|
||||||
|
@ -78,12 +106,30 @@ func (rn *RicochetNetwork) RecvRicochetPacket(reader io.Reader) (RicochetData, e
|
||||||
return packet, InvalidPacketLengthError
|
return packet, InvalidPacketLengthError
|
||||||
}
|
}
|
||||||
|
|
||||||
packet.Channel = int32(binary.BigEndian.Uint16(header[2:4]))
|
packetBytes := make([]byte, size-2)
|
||||||
packet.Data = make([]byte, size-4)
|
_, err := io.ReadAtLeast(reader, packetBytes, size-2)
|
||||||
|
if err != nil {
|
||||||
if _, err := io.ReadAtLeast(reader, packet.Data, len(packet.Data)); err != nil {
|
|
||||||
return packet, err
|
return packet, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
packet.Channel = int32(binary.BigEndian.Uint16(packetBytes[0:2]))
|
||||||
|
packet.Data = make([]byte, len(packetBytes)-2)
|
||||||
|
copy(packet.Data[:], packetBytes[2:])
|
||||||
return packet, nil
|
return packet, nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue