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)
|
||||
CloseChannel func()
|
||||
DelegateAuthorization func()
|
||||
DelegateEncryption func([32]byte)
|
||||
}
|
||||
|
|
|
@ -152,11 +152,13 @@ func (ah *Server3DHAuthChannel) Packet(data []byte) {
|
|||
if ok && string(decrypted) == "Hello World" {
|
||||
allowed, known := ah.ServerAuthValid(utils.GetTorV3Hostname(ah.clientPubKey), ah.clientPubKey)
|
||||
ah.channel.DelegateAuthorization()
|
||||
ah.channel.DelegateEncryption(key)
|
||||
log.Debugf("3DH Session Decrypted OK. Authenticating Connection!")
|
||||
messageBuilder := new(utils.MessageBuilder)
|
||||
result := messageBuilder.AuthResult3DH(allowed, known)
|
||||
ah.channel.SendMessage(result)
|
||||
ah.channel.CloseChannel()
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ func TestServer3DHAuthChannel(t *testing.T) {
|
|||
cc.ID = 1
|
||||
closed := false
|
||||
cc.CloseChannel = func() { closed = true }
|
||||
cc.DelegateEncryption = func([32]byte) {}
|
||||
clientChannel := new(outbound.Client3DHAuthChannel)
|
||||
pub, priv, _ := ed25519.GenerateKey(rand.Reader)
|
||||
cid := identity.InitializeV3("", &priv, &pub)
|
||||
|
@ -75,6 +76,7 @@ func TestServer3DHAuthChannelReject(t *testing.T) {
|
|||
cc := new(channels.Channel)
|
||||
cc.ID = 1
|
||||
cc.CloseChannel = func() {}
|
||||
cc.DelegateEncryption = func([32]byte) {}
|
||||
clientChannel := new(outbound.Client3DHAuthChannel)
|
||||
pub, priv, _ := ed25519.GenerateKey(rand.Reader)
|
||||
cid := identity.InitializeV3("", &priv, &pub)
|
||||
|
|
|
@ -27,6 +27,7 @@ type Client3DHAuthChannel struct {
|
|||
serverPubKey, serverEphemeralPublicKey, clientEphemeralPublicKey ed25519.PublicKey
|
||||
clientEphemeralPrivateKey ed25519.PrivateKey
|
||||
channel *channels.Channel
|
||||
key [32]byte
|
||||
}
|
||||
|
||||
// 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)
|
||||
var key [32]byte
|
||||
copy(key[:], pkey[:])
|
||||
encrypted := secretbox.Seal(nonce[:], []byte("Hello World"), &nonce, &key)
|
||||
|
||||
copy(ah.key[:], pkey[:])
|
||||
encrypted := secretbox.Seal(nonce[:], []byte("Hello World"), &nonce, &ah.key)
|
||||
messageBuilder := new(utils.MessageBuilder)
|
||||
proof := messageBuilder.Proof3DH(encrypted)
|
||||
ah.channel.SendMessage(proof)
|
||||
ah.channel.DelegateEncryption(ah.key)
|
||||
}
|
||||
|
||||
// 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.channelManager.RemoveChannel(channel.ID)
|
||||
}
|
||||
channel.DelegateEncryption = func(key [32]byte) {
|
||||
rc.SetEncryptionKey(key)
|
||||
}
|
||||
return channel, nil
|
||||
}
|
||||
return nil, err
|
||||
|
|
|
@ -8,7 +8,6 @@ import (
|
|||
"git.openprivacy.ca/openprivacy/libricochet-go/identity"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/log"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/utils"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
@ -75,9 +74,9 @@ func (bot *ChatEchoBot) ChatMessage(messageID uint32, when time.Time, message st
|
|||
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)
|
||||
rai.Connection.Do(func() error {
|
||||
return rai.Connection.Do(func() error {
|
||||
|
||||
log.Infof("Finding Chat Channel")
|
||||
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) {
|
||||
log.SetLevel(log.LevelInfo)
|
||||
log.SetLevel(log.LevelDebug)
|
||||
startGoRoutines := runtime.NumGoroutine()
|
||||
|
||||
acn, err := connectivity.StartTor(".", "")
|
||||
|
@ -177,11 +176,11 @@ func TestApplicationIntegration(t *testing.T) {
|
|||
if err != nil {
|
||||
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...")
|
||||
// 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")
|
||||
if err != nil {
|
||||
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)
|
||||
return err
|
||||
})
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
fmt.Println("Alice sending message to Bob...")
|
||||
SendMessage(alicei, "Hello Bob!")
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("Error dialing from Alice to Bob: %v", err)
|
||||
os.Exit(1)
|
||||
t.Errorf("Error Opening a Channel: %v", err)
|
||||
}
|
||||
|
||||
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)
|
||||
|
|
|
@ -2,7 +2,11 @@ package utils
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/log"
|
||||
"golang.org/x/crypto/nacl/secretbox"
|
||||
"io"
|
||||
)
|
||||
|
||||
|
@ -21,7 +25,7 @@ type RicochetData struct {
|
|||
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.
|
||||
func (rd RicochetData) Equals(other RicochetData) bool {
|
||||
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
|
||||
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
|
||||
|
@ -52,6 +67,17 @@ func (rn *RicochetNetwork) SendRicochetPacket(dst io.Writer, channel int32, data
|
|||
binary.BigEndian.PutUint16(packet[2:4], uint16(channel))
|
||||
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); {
|
||||
n, err := dst.Write(packet[pos:])
|
||||
if err != nil {
|
||||
|
@ -59,16 +85,18 @@ func (rn *RicochetNetwork) SendRicochetPacket(dst io.Writer, channel int32, data
|
|||
}
|
||||
pos += n
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RecvRicochetPacket returns the next packet from reader as a RicochetData
|
||||
// structure, or an error.
|
||||
func (rn *RicochetNetwork) RecvRicochetPacket(reader io.Reader) (RicochetData, error) {
|
||||
|
||||
packet := RicochetData{}
|
||||
|
||||
// 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 {
|
||||
return packet, err
|
||||
}
|
||||
|
@ -78,12 +106,30 @@ func (rn *RicochetNetwork) RecvRicochetPacket(reader io.Reader) (RicochetData, e
|
|||
return packet, InvalidPacketLengthError
|
||||
}
|
||||
|
||||
packet.Channel = int32(binary.BigEndian.Uint16(header[2:4]))
|
||||
packet.Data = make([]byte, size-4)
|
||||
|
||||
if _, err := io.ReadAtLeast(reader, packet.Data, len(packet.Data)); err != nil {
|
||||
packetBytes := make([]byte, size-2)
|
||||
_, err := io.ReadAtLeast(reader, packetBytes, size-2)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
|
Reference in New Issue