Merge branch 'v1-encrypt' of openprivacy/libricochet-go into master

This commit is contained in:
erinn 2019-01-23 19:38:13 +00:00 committed by Gogs
commit 8f00e26b81
7 changed files with 80 additions and 21 deletions

View File

@ -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)
} }

View File

@ -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
} }
} }

View File

@ -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)

View File

@ -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.

View File

@ -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

View File

@ -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)

View File

@ -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
} }