Refactor to a move event-driven library - incomplete
This commit is contained in:
parent
5b013a76c3
commit
93754f2916
|
@ -1,32 +1,31 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"github.com/s-rah/go-ricochet"
|
"github.com/s-rah/go-ricochet"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type EchoBotService struct {
|
||||||
|
goricochet.StandardRicochetService
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ebs * EchoBotService) OnAuthenticationResult(channelID int32, serverHostname string, result bool) {
|
||||||
|
if true {
|
||||||
|
ebs.Ricochet().OpenChatChannel(5)
|
||||||
|
ebs.Ricochet().SendMessage(5, "Hi I'm an echo bot, I echo what you say!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ebs * EchoBotService) OnChatMessage(channelID int32, serverHostname string, messageId int32, message string) {
|
||||||
|
ebs.Ricochet().AckChatMessage(channelID, messageId)
|
||||||
|
ebs.Ricochet().SendMessage(5, message)
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
ricochet := new(goricochet.Ricochet)
|
ricochetService := new(EchoBotService)
|
||||||
|
ricochetService.Init("./private_key", "kwke2hntvyfqm7dr")
|
||||||
// You will want to replace these values with your own test credentials
|
err := ricochetService.Ricochet().Connect("kwke2hntvyfqm7dr", "127.0.0.1:55555|jlq67qzo6s4yp3sp")
|
||||||
ricochet.Init("./private_key", true)
|
if err == nil {
|
||||||
ricochet.Connect("kwke2hntvyfqm7dr", "127.0.0.1:55555|jlq67qzo6s4yp3sp")
|
ricochetService.OnConnect("jlq67qzo6s4yp3sp")
|
||||||
|
ricochetService.Ricochet().ListenAndWait("jlq67qzo6s4yp3sp", ricochetService)
|
||||||
// Not needed past the initial run
|
|
||||||
// TODO need to wait for contact response before sending OpenChannel
|
|
||||||
// ricochet.SendContactRequest("EchoBot", "I'm an EchoBot")
|
|
||||||
|
|
||||||
go ricochet.ListenAndWait()
|
|
||||||
ricochet.OpenChatChannel(5)
|
|
||||||
time.Sleep(time.Second * 1)
|
|
||||||
ricochet.SendMessage(5, "Hi I'm an echo bot, I echo what you say!")
|
|
||||||
|
|
||||||
for true {
|
|
||||||
message,channel,_ := ricochet.Listen()
|
|
||||||
fmt.Print(channel, message)
|
|
||||||
if message != "" {
|
|
||||||
ricochet.SendMessage(5, message)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,18 @@ func (mb *MessageBuilder) OpenChatChannel(channelID int32) ([]byte, error) {
|
||||||
return proto.Marshal(pc)
|
return proto.Marshal(pc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AckOpenChannel constructs a message to acknowledge a previous open channel operation.
|
||||||
|
func (mb *MessageBuilder) AckOpenChannel(channelID int32, opened bool) ([]byte, error) {
|
||||||
|
cr := &Protocol_Data_Control.ChannelResult{
|
||||||
|
ChannelIdentifier: proto.Int32(channelID),
|
||||||
|
Opened: proto.Bool(opened),
|
||||||
|
}
|
||||||
|
pc := &Protocol_Data_Control.Packet{
|
||||||
|
ChannelResult: cr,
|
||||||
|
}
|
||||||
|
return proto.Marshal(pc)
|
||||||
|
}
|
||||||
|
|
||||||
// OpenContactRequestChannel contructs a message which will reuqest to open a channel for
|
// OpenContactRequestChannel contructs a message which will reuqest to open a channel for
|
||||||
// a contact request on the given channelID, with the given nick and message.
|
// a contact request on the given channelID, with the given nick and message.
|
||||||
func (mb *MessageBuilder) OpenContactRequestChannel(channelID int32, nick string, message string) ([]byte, error) {
|
func (mb *MessageBuilder) OpenContactRequestChannel(channelID int32, nick string, message string) ([]byte, error) {
|
||||||
|
@ -79,3 +91,15 @@ func (mb *MessageBuilder) ChatMessage(message string) ([]byte, error) {
|
||||||
}
|
}
|
||||||
return proto.Marshal(chatPacket)
|
return proto.Marshal(chatPacket)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AckChatMessage constructs a chat message acknowledgement.
|
||||||
|
func (mb *MessageBuilder) AckChatMessage(messageID int32) ([]byte, error) {
|
||||||
|
cr := &Protocol_Data_Chat.ChatAcknowledge{
|
||||||
|
MessageId: proto.Uint32(uint32(messageID)),
|
||||||
|
Accepted: proto.Bool(true),
|
||||||
|
}
|
||||||
|
pc := &Protocol_Data_Chat.Packet{
|
||||||
|
ChatAcknowledge: cr,
|
||||||
|
}
|
||||||
|
return proto.Marshal(pc)
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
package goricochet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
"github.com/s-rah/go-ricochet/auth"
|
||||||
|
"github.com/s-rah/go-ricochet/chat"
|
||||||
|
"github.com/s-rah/go-ricochet/control"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MessageDecoder struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conceptual Chat Message - we construct this to avoid polluting the
|
||||||
|
// the main ricochet code with protobuf cruft - and enable us to minimise the
|
||||||
|
// code that may break in the future.
|
||||||
|
type RicochetChatMessage struct {
|
||||||
|
Ack bool
|
||||||
|
MessageID int32
|
||||||
|
Message string
|
||||||
|
Accepted bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conceptual Control Message - we construct this to avoid polluting the
|
||||||
|
// the main ricochet code with protobuf cruft - and enable us to minimise the
|
||||||
|
// code that may break in the future.
|
||||||
|
type RicochetControlMessage struct {
|
||||||
|
Ack bool
|
||||||
|
Type string
|
||||||
|
ChannelID int32
|
||||||
|
Accepted bool
|
||||||
|
ClientCookie [16]byte
|
||||||
|
ServerCookie [16]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeAuthMessage
|
||||||
|
func (md *MessageDecoder) DecodeAuthMessage(data []byte) (bool, error) {
|
||||||
|
res := new(Protocol_Data_AuthHiddenService.Packet)
|
||||||
|
err := proto.Unmarshal(data[:], res)
|
||||||
|
if err != nil {
|
||||||
|
return false, errors.New("error unmarshalling control message type")
|
||||||
|
}
|
||||||
|
return res.GetResult().GetAccepted(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeControlMessage
|
||||||
|
func (md *MessageDecoder) DecodeControlMessage(data []byte) (*RicochetControlMessage, error) {
|
||||||
|
res := new(Protocol_Data_Control.Packet)
|
||||||
|
err := proto.Unmarshal(data[:], res)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("error unmarshalling control message type")
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.GetOpenChannel() != nil {
|
||||||
|
ricochetControlMessage := new(RicochetControlMessage)
|
||||||
|
ricochetControlMessage.Ack = false
|
||||||
|
|
||||||
|
if res.GetOpenChannel().GetChannelType() == "im.ricochet.auth.hidden-service" {
|
||||||
|
ricochetControlMessage.Type = "openauthchannel"
|
||||||
|
}
|
||||||
|
|
||||||
|
ricochetControlMessage.Type = "openchannel"
|
||||||
|
ricochetControlMessage.ChannelID = int32(res.GetOpenChannel().GetChannelIdentifier())
|
||||||
|
return ricochetControlMessage, nil
|
||||||
|
} else if res.GetChannelResult() != nil {
|
||||||
|
ricochetControlMessage := new(RicochetControlMessage)
|
||||||
|
ricochetControlMessage.Ack = true
|
||||||
|
ricochetControlMessage.ChannelID = int32(res.GetOpenChannel().GetChannelIdentifier())
|
||||||
|
|
||||||
|
serverCookie, err := proto.GetExtension(res.GetChannelResult(), Protocol_Data_AuthHiddenService.E_ServerCookie)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
ricochetControlMessage.Type = "openauthchannel"
|
||||||
|
copy(ricochetControlMessage.ServerCookie[:], serverCookie.([]byte))
|
||||||
|
} else {
|
||||||
|
ricochetControlMessage.Type = "openchannel"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return ricochetControlMessage, nil
|
||||||
|
}
|
||||||
|
return nil, errors.New("unknown control message type")
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeChatMessage takes a byte representing a data packet and returns a
|
||||||
|
// constructed RicochetControlMessage
|
||||||
|
func (md *MessageDecoder) DecodeChatMessage(data []byte) (*RicochetChatMessage, error) {
|
||||||
|
res := new(Protocol_Data_Chat.Packet)
|
||||||
|
err := proto.Unmarshal(data[:], res)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.GetChatMessage() != nil {
|
||||||
|
ricochetChatMessage := new(RicochetChatMessage)
|
||||||
|
ricochetChatMessage.Ack = false
|
||||||
|
ricochetChatMessage.MessageID = int32(res.GetChatMessage().GetMessageId())
|
||||||
|
ricochetChatMessage.Message = res.GetChatMessage().GetMessageText()
|
||||||
|
return ricochetChatMessage, nil
|
||||||
|
} else if res.GetChatAcknowledge != nil {
|
||||||
|
ricochetChatMessage := new(RicochetChatMessage)
|
||||||
|
ricochetChatMessage.Ack = true
|
||||||
|
ricochetChatMessage.MessageID = int32(res.GetChatAcknowledge().GetMessageId())
|
||||||
|
ricochetChatMessage.Accepted = res.GetChatAcknowledge().GetAccepted()
|
||||||
|
return ricochetChatMessage, nil
|
||||||
|
}
|
||||||
|
return nil, errors.New("chat message type not supported")
|
||||||
|
}
|
307
ricochet.go
307
ricochet.go
|
@ -1,44 +1,21 @@
|
||||||
package goricochet
|
package goricochet
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto"
|
|
||||||
"crypto/rsa"
|
|
||||||
"crypto/x509"
|
|
||||||
"encoding/asn1"
|
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"encoding/pem"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto"
|
||||||
"github.com/s-rah/go-ricochet/auth"
|
"github.com/s-rah/go-ricochet/auth"
|
||||||
"github.com/s-rah/go-ricochet/chat"
|
|
||||||
"github.com/s-rah/go-ricochet/control"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MessageType details the different kinds of messages used by Ricochet
|
|
||||||
type MessageType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
// CONTROL messages are those sent on channel 0
|
|
||||||
CONTROL MessageType = iota
|
|
||||||
// AUTH messages are those that deal with authentication
|
|
||||||
AUTH = iota
|
|
||||||
// DATA covers both chat and (later) file handling and other non-control messages.
|
|
||||||
DATA = iota
|
|
||||||
)
|
|
||||||
|
|
||||||
// Ricochet is a protocol to conducting anonymous IM.
|
// Ricochet is a protocol to conducting anonymous IM.
|
||||||
type Ricochet struct {
|
type Ricochet struct {
|
||||||
conn net.Conn
|
conn net.Conn
|
||||||
privateKey *rsa.PrivateKey
|
|
||||||
logger *log.Logger
|
logger *log.Logger
|
||||||
channelState map[int]int
|
|
||||||
channel chan RicochetMessage
|
|
||||||
known bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RicochetData is a structure containing the raw data and the channel it the
|
// RicochetData is a structure containing the raw data and the channel it the
|
||||||
|
@ -48,58 +25,14 @@ type RicochetData struct {
|
||||||
Data []byte
|
Data []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// RicochetMessage is a Wrapper Around Common Ricochet Protocol Strucutres
|
// Init sets up the Ricochet object.
|
||||||
type RicochetMessage struct {
|
func (r *Ricochet) Init(debugLog bool) {
|
||||||
Channel int32
|
|
||||||
ControlPacket *Protocol_Data_Control.Packet
|
|
||||||
DataPacket *Protocol_Data_Chat.Packet
|
|
||||||
AuthPacket *Protocol_Data_AuthHiddenService.Packet
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Ricochet) IsKnownContact() bool {
|
|
||||||
return r.known
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init sets up the Ricochet object. It takes in a filename of a hidden service
|
|
||||||
// private_key file so it can successfully authenticate itself with other
|
|
||||||
// clients.
|
|
||||||
func (r *Ricochet) Init(filename string, debugLog bool) {
|
|
||||||
|
|
||||||
if debugLog {
|
if debugLog {
|
||||||
r.logger = log.New(os.Stdout, "[Ricochet]: ", log.Ltime|log.Lmicroseconds)
|
r.logger = log.New(os.Stdout, "[Ricochet]: ", log.Ltime|log.Lmicroseconds)
|
||||||
} else {
|
} else {
|
||||||
r.logger = log.New(ioutil.Discard, "[Ricochet]: ", log.Ltime|log.Lmicroseconds)
|
r.logger = log.New(ioutil.Discard, "[Ricochet]: ", log.Ltime|log.Lmicroseconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
pemData, err := ioutil.ReadFile(filename)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
r.logger.Print("Error Reading Private Key: ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
block, _ := pem.Decode(pemData)
|
|
||||||
if block == nil || block.Type != "RSA PRIVATE KEY" {
|
|
||||||
r.logger.Print("No valid PEM data found")
|
|
||||||
}
|
|
||||||
|
|
||||||
r.privateKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
|
|
||||||
r.handleFatal(err, "Private key can't be decoded")
|
|
||||||
|
|
||||||
r.channelState = make(map[int]int)
|
|
||||||
r.channel = make(chan RicochetMessage)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Ricochet) StartService(server RicochetService, port string) {
|
|
||||||
// Listen
|
|
||||||
ln, _ := net.Listen("tcp", port)
|
|
||||||
conn, _ := ln.Accept()
|
|
||||||
go r.runService(conn, server)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Ricochet) runService(conn net.Conn, server RicochetService) {
|
|
||||||
// Negotiate Version
|
|
||||||
|
|
||||||
// Loop For Messages
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect sets up a ricochet connection between from and to which are
|
// Connect sets up a ricochet connection between from and to which are
|
||||||
|
@ -108,7 +41,6 @@ func (r *Ricochet) runService(conn net.Conn, server RicochetService) {
|
||||||
// be open and authenticated.
|
// be open and authenticated.
|
||||||
// To specify a local port using the format "127.0.0.1:[port]|ricochet-id".
|
// To specify a local port using the format "127.0.0.1:[port]|ricochet-id".
|
||||||
func (r *Ricochet) Connect(from string, to string) error {
|
func (r *Ricochet) Connect(from string, to string) error {
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
networkResolver := new(NetworkResolver)
|
networkResolver := new(NetworkResolver)
|
||||||
r.conn, to, err = networkResolver.Resolve(to)
|
r.conn, to, err = networkResolver.Resolve(to)
|
||||||
|
@ -117,46 +49,27 @@ func (r *Ricochet) Connect(from string, to string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
r.negotiateVersion()
|
return r.negotiateVersion()
|
||||||
|
}
|
||||||
authHandler := new(AuthenticationHandler)
|
|
||||||
clientCookie := authHandler.GenClientCookie()
|
|
||||||
|
|
||||||
|
// Authenticate opens an Authentication Channel and send a client cookie
|
||||||
|
func (r *Ricochet) Authenticate(channelID int32, clientCookie [16]byte) error {
|
||||||
messageBuilder := new(MessageBuilder)
|
messageBuilder := new(MessageBuilder)
|
||||||
data, err := messageBuilder.OpenAuthenticationChannel(1, clientCookie)
|
data, err := messageBuilder.OpenAuthenticationChannel(channelID, clientCookie)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("Cannot Marshal Open Channel Message")
|
return errors.New("Cannot Marshal Open Channel Message")
|
||||||
}
|
}
|
||||||
|
r.logger.Printf("Sending Open Channel with Auth Request (channel:%d)", channelID)
|
||||||
r.sendPacket(data, 0)
|
r.sendPacket(data, 0)
|
||||||
|
return nil
|
||||||
response, _ := r.getMessages()
|
|
||||||
openChannelResponse, _ := r.decodePacket(response[0], CONTROL)
|
|
||||||
r.logger.Print("Received Response: ", openChannelResponse)
|
|
||||||
channelResult := openChannelResponse.ControlPacket.GetChannelResult()
|
|
||||||
|
|
||||||
if channelResult.GetOpened() == true {
|
|
||||||
r.logger.Print("Channel Opened Successfully: ", channelResult.GetChannelIdentifier())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sCookie, _ := proto.GetExtension(channelResult, Protocol_Data_AuthHiddenService.E_ServerCookie)
|
// SendProof sends an authentication proof in response to a challenge.
|
||||||
authHandler.AddServerCookie(sCookie.([]byte))
|
func (r *Ricochet) SendProof(channelID int32, publickeyBytes []byte, signatureBytes []byte) error {
|
||||||
|
|
||||||
// DER Encode the Public Key
|
|
||||||
publickeybytes, err := asn1.Marshal(rsa.PublicKey{
|
|
||||||
N: r.privateKey.PublicKey.N,
|
|
||||||
E: r.privateKey.PublicKey.E,
|
|
||||||
})
|
|
||||||
|
|
||||||
signature, _ := rsa.SignPKCS1v15(nil, r.privateKey, crypto.SHA256, authHandler.GenChallenge(from, to))
|
|
||||||
|
|
||||||
signatureBytes := make([]byte, 128)
|
|
||||||
copy(signatureBytes[:], signature[:])
|
|
||||||
|
|
||||||
// Construct a Proof Message
|
// Construct a Proof Message
|
||||||
proof := &Protocol_Data_AuthHiddenService.Proof{
|
proof := &Protocol_Data_AuthHiddenService.Proof{
|
||||||
PublicKey: publickeybytes,
|
PublicKey: publickeyBytes,
|
||||||
Signature: signatureBytes,
|
Signature: signatureBytes,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,23 +78,14 @@ func (r *Ricochet) Connect(from string, to string) error {
|
||||||
Result: nil,
|
Result: nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err = proto.Marshal(ahsPacket)
|
data, err := proto.Marshal(ahsPacket)
|
||||||
r.sendPacket(data, 1)
|
|
||||||
|
|
||||||
response, err = r.getMessages()
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
resultResponse, _ := r.decodePacket(response[0], AUTH)
|
r.logger.Printf("Sending Proof Auth Request (channel:%d)", channelID)
|
||||||
r.logger.Print("Received Result: ", resultResponse)
|
r.sendPacket(data, channelID)
|
||||||
|
|
||||||
if resultResponse.AuthPacket.GetResult().GetAccepted() != true {
|
|
||||||
return errors.New("authorization failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
r.known = resultResponse.AuthPacket.GetResult().GetIsKnownContact()
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,6 +121,32 @@ func (r *Ricochet) SendContactRequest(channel int32, nick string, message string
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AckOpenChannel acknowledges a previously received open channel message
|
||||||
|
// Prerequisites:
|
||||||
|
// * Must have Previously issued a successful Connect()
|
||||||
|
func (r *Ricochet) AckOpenChannel(channel int32, result bool) error {
|
||||||
|
messageBuilder := new(MessageBuilder)
|
||||||
|
data, err := messageBuilder.AckOpenChannel(channel, result)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("Failed to serialize open channel ack")
|
||||||
|
}
|
||||||
|
r.sendPacket(data, 0)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AckChatMessage acknowledges a previously received chat message.
|
||||||
|
// Prerequisites:
|
||||||
|
// * Must have Previously issued a successful Connect()
|
||||||
|
func (r *Ricochet) AckChatMessage(channel int32, messageID int32) error {
|
||||||
|
messageBuilder := new(MessageBuilder)
|
||||||
|
data, err := messageBuilder.AckChatMessage(messageID)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("Failed to serialize chat message ack")
|
||||||
|
}
|
||||||
|
r.sendPacket(data, channel)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// SendMessage sends a Chat Message (message) to a give Channel (channel).
|
// SendMessage sends a Chat Message (message) to a give Channel (channel).
|
||||||
// Prerequisites:
|
// Prerequisites:
|
||||||
// * Must have previously issued a successful Connect()
|
// * Must have previously issued a successful Connect()
|
||||||
|
@ -267,141 +197,75 @@ func (r *Ricochet) sendPacket(data []byte, channel int32) {
|
||||||
header[2] = 0x00
|
header[2] = 0x00
|
||||||
header[3] = byte(channel)
|
header[3] = byte(channel)
|
||||||
copy(header[4:], data[:])
|
copy(header[4:], data[:])
|
||||||
|
|
||||||
fmt.Fprintf(r.conn, "%s", header)
|
fmt.Fprintf(r.conn, "%s", header)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Listen blocks and waits for a new message to arrive from the connected user
|
|
||||||
// once a message has arrived, it returns the message and the channel it occured
|
|
||||||
// on, else it returns an error.
|
|
||||||
// Prerequisites:
|
|
||||||
// * Must have previously issued a successful Connect()
|
|
||||||
// * Must have previously ran "go ricochet.ListenAndWait()"
|
|
||||||
func (r *Ricochet) Listen() (string, int32, error) {
|
|
||||||
var message RicochetMessage
|
|
||||||
message = <-r.channel
|
|
||||||
r.logger.Printf("Received Chat Message on Channel %d", message.Channel)
|
|
||||||
if message.DataPacket.GetChatMessage() == nil {
|
|
||||||
return "", 0, errors.New("Did not receive a chat message")
|
|
||||||
}
|
|
||||||
|
|
||||||
messageID := message.DataPacket.GetChatMessage().GetMessageId()
|
|
||||||
cr := &Protocol_Data_Chat.ChatAcknowledge{
|
|
||||||
MessageId: proto.Uint32(messageID),
|
|
||||||
Accepted: proto.Bool(true),
|
|
||||||
}
|
|
||||||
|
|
||||||
pc := &Protocol_Data_Chat.Packet{
|
|
||||||
ChatAcknowledge: cr,
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := proto.Marshal(pc)
|
|
||||||
if err != nil {
|
|
||||||
return "", 0, errors.New("Failed to serialize chat message")
|
|
||||||
}
|
|
||||||
|
|
||||||
r.sendPacket(data, message.Channel)
|
|
||||||
return message.DataPacket.GetChatMessage().GetMessageText(), message.Channel, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListenAndWait is intended to be a background thread listening for all messages
|
// ListenAndWait is intended to be a background thread listening for all messages
|
||||||
// a client will send, automaticall responding to some, and making the others available to
|
// a client will send, automaticall responding to some, and making the others available to
|
||||||
// Listen()
|
// Listen()
|
||||||
// Prerequisites:
|
// Prerequisites:
|
||||||
// * Must have previously issued a successful Connect()
|
// * Must have previously issued a successful Connect()
|
||||||
func (r *Ricochet) ListenAndWait() error {
|
func (r *Ricochet) ListenAndWait(serverHostname string, service RicochetService) error {
|
||||||
for true {
|
for true {
|
||||||
packets, err := r.getMessages()
|
packets, err := r.getMessages()
|
||||||
if err != nil {
|
r.handleFatal(err, "Error attempted to get new messages")
|
||||||
return errors.New("Error attempted to get new messages")
|
|
||||||
}
|
messageDecoder := new(MessageDecoder)
|
||||||
|
|
||||||
for _, packet := range packets {
|
for _, packet := range packets {
|
||||||
|
|
||||||
|
if len(packet.Data) == 0 {
|
||||||
|
r.logger.Printf("Closing Channel %d", packet.Channel)
|
||||||
|
service.OnChannelClose(packet.Channel, serverHostname)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
if packet.Channel == 0 {
|
if packet.Channel == 0 {
|
||||||
// This is a Control Channel Message
|
|
||||||
message, err := r.decodePacket(packet, CONTROL)
|
|
||||||
|
|
||||||
if err != nil {
|
message, err := messageDecoder.DecodeControlMessage(packet.Data)
|
||||||
r.logger.Printf("Failed to decode control packet, discarding")
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// Automatically accept new channels
|
|
||||||
if message.ControlPacket.GetOpenChannel() != nil {
|
|
||||||
// TODO Reject if already in use.
|
|
||||||
cr := &Protocol_Data_Control.ChannelResult{
|
|
||||||
ChannelIdentifier: proto.Int32(message.ControlPacket.GetOpenChannel().GetChannelIdentifier()),
|
|
||||||
Opened: proto.Bool(true),
|
|
||||||
}
|
|
||||||
|
|
||||||
pc := &Protocol_Data_Control.Packet{
|
|
||||||
ChannelResult: cr,
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := proto.Marshal(pc)
|
|
||||||
// TODO we should set up some kind of error channel.
|
|
||||||
r.handleFatal(err, "error marshalling control protocol")
|
|
||||||
|
|
||||||
r.logger.Printf("Client Opening Channel: %d\n", message.ControlPacket.GetOpenChannel().GetChannelIdentifier())
|
|
||||||
r.sendPacket(data, 0)
|
|
||||||
r.channelState[int(message.ControlPacket.GetOpenChannel().GetChannelIdentifier())] = 1
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if message.ControlPacket.GetChannelResult() != nil {
|
|
||||||
channelResult := message.ControlPacket.GetChannelResult()
|
|
||||||
if channelResult.GetOpened() == true {
|
|
||||||
r.logger.Print("Channel Opened Successfully: ", channelResult.GetChannelIdentifier())
|
|
||||||
r.channelState[int(message.ControlPacket.GetChannelResult().GetChannelIdentifier())] = 1
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
r.logger.Printf("Received Unknown Control Message\n")
|
|
||||||
|
|
||||||
} else if packet.Channel == 3 {
|
|
||||||
// Contact Request
|
|
||||||
r.logger.Printf("Received Unknown Message on Channel 3\n")
|
|
||||||
} else {
|
|
||||||
// At this point the only other expected type of message
|
|
||||||
// is a Chat Message
|
|
||||||
message, err := r.decodePacket(packet, DATA)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.logger.Printf("Failed to decode data packet, discarding")
|
r.logger.Printf("Failed to decode data packet, discarding")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
r.channel <- message
|
|
||||||
|
if message.Type == "openchannel" && message.Ack == false {
|
||||||
|
r.logger.Printf("new open channel request %d %s", message.ChannelID, serverHostname)
|
||||||
|
service.OnOpenChannelRequest(message.ChannelID, serverHostname)
|
||||||
|
} else if message.Type == "openchannel" && message.Ack == true {
|
||||||
|
r.logger.Printf("new open channel request ack %d %s", message.ChannelID, serverHostname)
|
||||||
|
service.OnOpenChannelRequestAck(message.ChannelID, serverHostname, message.Accepted)
|
||||||
|
} else if message.Type == "openauthchannel" && message.Ack == true {
|
||||||
|
r.logger.Printf("new authentication challenge %d %s", message.ChannelID, serverHostname)
|
||||||
|
service.OnAuthenticationChallenge(message.ChannelID, serverHostname, message.ServerCookie)
|
||||||
|
} else {
|
||||||
|
r.logger.Printf("Received Unknown Control Message\n", message)
|
||||||
|
}
|
||||||
|
} else if packet.Channel == 1 {
|
||||||
|
result, _ := messageDecoder.DecodeAuthMessage(packet.Data)
|
||||||
|
r.logger.Printf("newreceived auth result %d", packet.Channel)
|
||||||
|
service.OnAuthenticationResult(1, serverHostname, result)
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// At this point the only other expected type of message is a Chat Message
|
||||||
|
messageDecoder := new(MessageDecoder)
|
||||||
|
message, err := messageDecoder.DecodeChatMessage(packet.Data)
|
||||||
|
if err != nil {
|
||||||
|
r.logger.Printf("Failed to decode data packet, discarding on channel %d", packet.Channel)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if message.Ack == true {
|
||||||
|
service.OnChatMessageAck(packet.Channel, serverHostname, message.MessageID)
|
||||||
|
} else {
|
||||||
|
service.OnChatMessage(packet.Channel, serverHostname, message.MessageID, message.Message)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// decodePacket take a raw RicochetData message and decodes it based on a given MessageType
|
|
||||||
func (r *Ricochet) decodePacket(packet RicochetData, t MessageType) (rm RicochetMessage, err error) {
|
|
||||||
|
|
||||||
rm.Channel = packet.Channel
|
|
||||||
|
|
||||||
if t == CONTROL {
|
|
||||||
res := new(Protocol_Data_Control.Packet)
|
|
||||||
err = proto.Unmarshal(packet.Data[:], res)
|
|
||||||
rm.ControlPacket = res
|
|
||||||
} else if t == AUTH {
|
|
||||||
res := new(Protocol_Data_AuthHiddenService.Packet)
|
|
||||||
err = proto.Unmarshal(packet.Data[:], res)
|
|
||||||
rm.AuthPacket = res
|
|
||||||
} else if t == DATA {
|
|
||||||
res := new(Protocol_Data_Chat.Packet)
|
|
||||||
err = proto.Unmarshal(packet.Data[:], res)
|
|
||||||
rm.DataPacket = res
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return rm, errors.New("Error Unmarshalling Response")
|
|
||||||
}
|
|
||||||
return rm, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// getMessages returns an array of new messages received from the ricochet client
|
// getMessages returns an array of new messages received from the ricochet client
|
||||||
func (r *Ricochet) getMessages() ([]RicochetData, error) {
|
func (r *Ricochet) getMessages() ([]RicochetData, error) {
|
||||||
buf, err := r.recv()
|
buf, err := r.recv()
|
||||||
|
@ -432,6 +296,7 @@ func (r *Ricochet) getMessages() ([]RicochetData, error) {
|
||||||
finished = true
|
finished = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
r.logger.Printf("Got %d Packets", len(datas))
|
||||||
return datas, nil
|
return datas, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,16 @@
|
||||||
package goricochet
|
package goricochet
|
||||||
|
|
||||||
type RicochetService interface {
|
type RicochetService interface {
|
||||||
OnConnect(id string) error
|
OnConnect(serverHostname string)
|
||||||
OnContactRequest(id string) error
|
OnAuthenticationChallenge(channelID int32, serverHostname string, serverCookie [16]byte)
|
||||||
OnMessage(id string, message string, channel int) error
|
OnAuthenticationResult(channelID int32, serverHostname string, result bool)
|
||||||
|
|
||||||
|
OnOpenChannelRequest(channelID int32, serverHostname string)
|
||||||
|
OnOpenChannelRequestAck(channelID int32, serverHostname string, result bool)
|
||||||
|
OnChannelClose(channelID int32, serverHostname string)
|
||||||
|
|
||||||
|
OnContactRequest(channelID string, serverHostname string, nick string, message string)
|
||||||
|
|
||||||
|
OnChatMessage(channelID int32, serverHostname string, messageID int32, message string)
|
||||||
|
OnChatMessageAck(channelID int32, serverHostname string, messageID int32)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
package goricochet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/asn1"
|
||||||
|
//"encoding/binary"
|
||||||
|
"encoding/pem"
|
||||||
|
"io/ioutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StandardRicochetService struct {
|
||||||
|
ricochet *Ricochet
|
||||||
|
authHandler map[string]*AuthenticationHandler
|
||||||
|
privateKey *rsa.PrivateKey
|
||||||
|
hostname string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srs *StandardRicochetService) Init(filename string, hostname string) {
|
||||||
|
srs.ricochet = new(Ricochet)
|
||||||
|
srs.ricochet.Init(true)
|
||||||
|
srs.authHandler = make(map[string]*AuthenticationHandler)
|
||||||
|
srs.hostname = hostname
|
||||||
|
|
||||||
|
pemData, err := ioutil.ReadFile(filename)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
// r.logger.Print("Error Reading Private Key: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
block, _ := pem.Decode(pemData)
|
||||||
|
if block == nil || block.Type != "RSA PRIVATE KEY" {
|
||||||
|
// r.logger.Print("No valid PEM data found")
|
||||||
|
}
|
||||||
|
|
||||||
|
srs.privateKey, _ = x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srs *StandardRicochetService) OnConnect(serverHostname string) {
|
||||||
|
srs.authHandler[serverHostname] = new(AuthenticationHandler)
|
||||||
|
clientCookie := srs.authHandler[serverHostname].GenClientCookie()
|
||||||
|
srs.ricochet.Authenticate(1, clientCookie)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnAuthenticationChallenge constructs a valid authentication challenge to the serverCookie
|
||||||
|
func (srs *StandardRicochetService) OnAuthenticationChallenge(channelID int32, serverHostname string, serverCookie [16]byte) {
|
||||||
|
srs.authHandler[serverHostname].AddServerCookie(serverCookie[:])
|
||||||
|
|
||||||
|
// DER Encode the Public Key
|
||||||
|
publickeyBytes, _ := asn1.Marshal(rsa.PublicKey{
|
||||||
|
N: srs.privateKey.PublicKey.N,
|
||||||
|
E: srs.privateKey.PublicKey.E,
|
||||||
|
})
|
||||||
|
|
||||||
|
signature, _ := rsa.SignPKCS1v15(nil, srs.privateKey, crypto.SHA256, srs.authHandler[serverHostname].GenChallenge(srs.hostname, serverHostname))
|
||||||
|
// TODO Handle Errors
|
||||||
|
signatureBytes := make([]byte, 128)
|
||||||
|
copy(signatureBytes[:], signature[:])
|
||||||
|
srs.ricochet.SendProof(1, publickeyBytes, signatureBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srs *StandardRicochetService) Ricochet() *Ricochet {
|
||||||
|
return srs.ricochet
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srs *StandardRicochetService) OnAuthenticationResult(channelID int32, serverHostname string, result bool) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srs *StandardRicochetService) OnOpenChannelRequest(channelID int32, serverHostname string) {
|
||||||
|
srs.ricochet.AckOpenChannel(channelID, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srs *StandardRicochetService) OnOpenChannelRequestAck(channelID int32, serverHostname string, result bool) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srs *StandardRicochetService) OnChannelClose(channelID int32, serverHostname string) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srs *StandardRicochetService) OnContactRequest(channelID string, serverHostname string, nick string, message string) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srs *StandardRicochetService) OnChatMessage(channelID int32, serverHostname string, messageId int32, message string) {
|
||||||
|
srs.ricochet.AckChatMessage(channelID, messageId)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srs *StandardRicochetService) OnChatMessageAck(channelID int32, serverHostname string, messageId int32) {
|
||||||
|
}
|
Loading…
Reference in New Issue