Refactor to a move event-driven library - incomplete
This commit is contained in:
parent
5b013a76c3
commit
93754f2916
|
@ -1,32 +1,31 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"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() {
|
||||
ricochet := new(goricochet.Ricochet)
|
||||
|
||||
// You will want to replace these values with your own test credentials
|
||||
ricochet.Init("./private_key", true)
|
||||
ricochet.Connect("kwke2hntvyfqm7dr", "127.0.0.1:55555|jlq67qzo6s4yp3sp")
|
||||
|
||||
// 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)
|
||||
}
|
||||
ricochetService := new(EchoBotService)
|
||||
ricochetService.Init("./private_key", "kwke2hntvyfqm7dr")
|
||||
err := ricochetService.Ricochet().Connect("kwke2hntvyfqm7dr", "127.0.0.1:55555|jlq67qzo6s4yp3sp")
|
||||
if err == nil {
|
||||
ricochetService.OnConnect("jlq67qzo6s4yp3sp")
|
||||
ricochetService.Ricochet().ListenAndWait("jlq67qzo6s4yp3sp", ricochetService)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,18 @@ func (mb *MessageBuilder) OpenChatChannel(channelID int32) ([]byte, error) {
|
|||
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
|
||||
// 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) {
|
||||
|
@ -79,3 +91,15 @@ func (mb *MessageBuilder) ChatMessage(message string) ([]byte, error) {
|
|||
}
|
||||
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
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/asn1"
|
||||
"encoding/binary"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"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"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"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.
|
||||
type Ricochet struct {
|
||||
conn net.Conn
|
||||
privateKey *rsa.PrivateKey
|
||||
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
|
||||
|
@ -48,58 +25,14 @@ type RicochetData struct {
|
|||
Data []byte
|
||||
}
|
||||
|
||||
// RicochetMessage is a Wrapper Around Common Ricochet Protocol Strucutres
|
||||
type RicochetMessage struct {
|
||||
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) {
|
||||
// Init sets up the Ricochet object.
|
||||
func (r *Ricochet) Init(debugLog bool) {
|
||||
|
||||
if debugLog {
|
||||
r.logger = log.New(os.Stdout, "[Ricochet]: ", log.Ltime|log.Lmicroseconds)
|
||||
} else {
|
||||
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
|
||||
|
@ -108,7 +41,6 @@ func (r *Ricochet) runService(conn net.Conn, server RicochetService) {
|
|||
// be open and authenticated.
|
||||
// To specify a local port using the format "127.0.0.1:[port]|ricochet-id".
|
||||
func (r *Ricochet) Connect(from string, to string) error {
|
||||
|
||||
var err error
|
||||
networkResolver := new(NetworkResolver)
|
||||
r.conn, to, err = networkResolver.Resolve(to)
|
||||
|
@ -117,46 +49,27 @@ func (r *Ricochet) Connect(from string, to string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
r.negotiateVersion()
|
||||
|
||||
authHandler := new(AuthenticationHandler)
|
||||
clientCookie := authHandler.GenClientCookie()
|
||||
return r.negotiateVersion()
|
||||
}
|
||||
|
||||
// Authenticate opens an Authentication Channel and send a client cookie
|
||||
func (r *Ricochet) Authenticate(channelID int32, clientCookie [16]byte) error {
|
||||
messageBuilder := new(MessageBuilder)
|
||||
data, err := messageBuilder.OpenAuthenticationChannel(1, clientCookie)
|
||||
data, err := messageBuilder.OpenAuthenticationChannel(channelID, clientCookie)
|
||||
|
||||
if err != nil {
|
||||
return errors.New("Cannot Marshal Open Channel Message")
|
||||
}
|
||||
|
||||
r.logger.Printf("Sending Open Channel with Auth Request (channel:%d)", channelID)
|
||||
r.sendPacket(data, 0)
|
||||
|
||||
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())
|
||||
return nil
|
||||
}
|
||||
|
||||
sCookie, _ := proto.GetExtension(channelResult, Protocol_Data_AuthHiddenService.E_ServerCookie)
|
||||
authHandler.AddServerCookie(sCookie.([]byte))
|
||||
|
||||
// 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[:])
|
||||
|
||||
// SendProof sends an authentication proof in response to a challenge.
|
||||
func (r *Ricochet) SendProof(channelID int32, publickeyBytes []byte, signatureBytes []byte) error {
|
||||
// Construct a Proof Message
|
||||
proof := &Protocol_Data_AuthHiddenService.Proof{
|
||||
PublicKey: publickeybytes,
|
||||
PublicKey: publickeyBytes,
|
||||
Signature: signatureBytes,
|
||||
}
|
||||
|
||||
|
@ -165,23 +78,14 @@ func (r *Ricochet) Connect(from string, to string) error {
|
|||
Result: nil,
|
||||
}
|
||||
|
||||
data, err = proto.Marshal(ahsPacket)
|
||||
r.sendPacket(data, 1)
|
||||
|
||||
response, err = r.getMessages()
|
||||
data, err := proto.Marshal(ahsPacket)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resultResponse, _ := r.decodePacket(response[0], AUTH)
|
||||
r.logger.Print("Received Result: ", resultResponse)
|
||||
|
||||
if resultResponse.AuthPacket.GetResult().GetAccepted() != true {
|
||||
return errors.New("authorization failed")
|
||||
}
|
||||
|
||||
r.known = resultResponse.AuthPacket.GetResult().GetIsKnownContact()
|
||||
r.logger.Printf("Sending Proof Auth Request (channel:%d)", channelID)
|
||||
r.sendPacket(data, channelID)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -217,6 +121,32 @@ func (r *Ricochet) SendContactRequest(channel int32, nick string, message string
|
|||
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).
|
||||
// Prerequisites:
|
||||
// * Must have previously issued a successful Connect()
|
||||
|
@ -267,141 +197,75 @@ func (r *Ricochet) sendPacket(data []byte, channel int32) {
|
|||
header[2] = 0x00
|
||||
header[3] = byte(channel)
|
||||
copy(header[4:], data[:])
|
||||
|
||||
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
|
||||
// a client will send, automaticall responding to some, and making the others available to
|
||||
// Listen()
|
||||
// Prerequisites:
|
||||
// * Must have previously issued a successful Connect()
|
||||
func (r *Ricochet) ListenAndWait() error {
|
||||
func (r *Ricochet) ListenAndWait(serverHostname string, service RicochetService) error {
|
||||
for true {
|
||||
packets, err := r.getMessages()
|
||||
if err != nil {
|
||||
return errors.New("Error attempted to get new messages")
|
||||
}
|
||||
r.handleFatal(err, "Error attempted to get new messages")
|
||||
|
||||
messageDecoder := new(MessageDecoder)
|
||||
|
||||
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 {
|
||||
// This is a Control Channel Message
|
||||
message, err := r.decodePacket(packet, CONTROL)
|
||||
|
||||
if err != nil {
|
||||
r.logger.Printf("Failed to decode control packet, discarding")
|
||||
break
|
||||
}
|
||||
message, err := messageDecoder.DecodeControlMessage(packet.Data)
|
||||
|
||||
// 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 {
|
||||
r.logger.Printf("Failed to decode data packet, discarding")
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
func (r *Ricochet) getMessages() ([]RicochetData, error) {
|
||||
buf, err := r.recv()
|
||||
|
@ -432,6 +296,7 @@ func (r *Ricochet) getMessages() ([]RicochetData, error) {
|
|||
finished = true
|
||||
}
|
||||
}
|
||||
r.logger.Printf("Got %d Packets", len(datas))
|
||||
return datas, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,16 @@
|
|||
package goricochet
|
||||
|
||||
type RicochetService interface {
|
||||
OnConnect(id string) error
|
||||
OnContactRequest(id string) error
|
||||
OnMessage(id string, message string, channel int) error
|
||||
OnConnect(serverHostname string)
|
||||
OnAuthenticationChallenge(channelID int32, serverHostname string, serverCookie [16]byte)
|
||||
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