Start using the derived ephemeral session key for encrypting

This commit is contained in:
Sarah Jamie Lewis 2019-01-22 12:21:56 -08:00
parent 9a680cd257
commit a96f682e77
7 changed files with 80 additions and 21 deletions

View File

@ -25,4 +25,5 @@ type Channel struct {
SendMessage func([]byte)
CloseChannel 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" {
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
}
}

View File

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

View File

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

View File

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

View File

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

View File

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