Update go-ricochet to new API (plus fixes)
go-ricochet upstream is in the midst of a massive API rewrite, moving towards something that is more flexible and robust. It's a work in progress, but already better than what we were on before. This updates the vendored go-ricochet to a fork off of the latest master, including a series of patches that were needed to fix or add behavior needed by ricochet-go. All of these are submitted upstream, and the goal is to point back there as soon as possible. Current upstream is https://github.com/special/go-ricochet-protocol on the api-rework-fixes branch. The go-ricochet/vendor folder was manually deleted during the import, because it vendors only dependencies that are also vendored by this repository.
This commit is contained in:
		
							parent
							
								
									48aab2536a
								
							
						
					
					
						commit
						9ae0eac4f3
					
				| 
						 | 
				
			
			@ -25,8 +25,8 @@ SOFTWARE.
 | 
			
		|||
 | 
			
		||||
--------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
Autogenerated protobuf code was generated using the proto file from Ricochet. 
 | 
			
		||||
They are covered under the following license. 
 | 
			
		||||
Autogenerated protobuf code was generated using the proto file from Ricochet.
 | 
			
		||||
They are covered under the following license.
 | 
			
		||||
 | 
			
		||||
              Ricochet - https://ricochet.im/
 | 
			
		||||
Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
 | 
			
		||||
| 
						 | 
				
			
			@ -61,10 +61,4 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		|||
 | 
			
		||||
--------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
The go-ricochet logo is based on an image by Olga Shalakhina 
 | 
			
		||||
<osshalakhina@gmail.com> who in turn modified the original gopher images made by 
 | 
			
		||||
Renee French.  The image is licensed under Creative Commons 3.0 Attributions.
 | 
			
		||||
 | 
			
		||||
--------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
go-ricochet is not affiliated with or endorsed by Ricochet.im or the Tor Project.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										19
									
								
								vendor/github.com/s-rah/go-ricochet/application/acceptallcontactmanager.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							
							
						
						
									
										19
									
								
								vendor/github.com/s-rah/go-ricochet/application/acceptallcontactmanager.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							| 
						 | 
				
			
			@ -0,0 +1,19 @@
 | 
			
		|||
package application
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/rsa"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// AcceptAllContactManager implements the contact manager interface an presumes
 | 
			
		||||
// all connections are allowed.
 | 
			
		||||
type AcceptAllContactManager struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LookupContact returns that a contact is known and allowed to communicate for all cases.
 | 
			
		||||
func (aacm *AcceptAllContactManager) LookupContact(hostname string, publicKey rsa.PublicKey) (allowed, known bool) {
 | 
			
		||||
	return true, true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (aacm *AcceptAllContactManager) ContactRequest(name string, message string) string {
 | 
			
		||||
	return "Accepted"
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,140 @@
 | 
			
		|||
package application
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/rsa"
 | 
			
		||||
	"github.com/s-rah/go-ricochet"
 | 
			
		||||
	"github.com/s-rah/go-ricochet/channels"
 | 
			
		||||
	"github.com/s-rah/go-ricochet/connection"
 | 
			
		||||
	"log"
 | 
			
		||||
	"net"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// RicochetApplication bundles many useful constructs that are
 | 
			
		||||
// likely standard in a ricochet application
 | 
			
		||||
type RicochetApplication struct {
 | 
			
		||||
	contactManager        ContactManagerInterface
 | 
			
		||||
	privateKey            *rsa.PrivateKey
 | 
			
		||||
	chatMessageHandler    func(*RicochetApplicationInstance, uint32, time.Time, string)
 | 
			
		||||
	chatMessageAckHandler func(*RicochetApplicationInstance, uint32)
 | 
			
		||||
	l       net.Listener
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type RicochetApplicationInstance struct {
 | 
			
		||||
	connection.AutoConnectionHandler
 | 
			
		||||
	connection            *connection.Connection
 | 
			
		||||
	RemoteHostname        string
 | 
			
		||||
	ChatMessageHandler    func(*RicochetApplicationInstance, uint32, time.Time, string)
 | 
			
		||||
	ChatMessageAckHandler func(*RicochetApplicationInstance, uint32)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (rai *RicochetApplicationInstance) ContactRequest(name string, message string) string {
 | 
			
		||||
	return "Accepted"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (rai *RicochetApplicationInstance) ContactRequestRejected() {
 | 
			
		||||
}
 | 
			
		||||
func (rai *RicochetApplicationInstance) ContactRequestAccepted() {
 | 
			
		||||
}
 | 
			
		||||
func (rai *RicochetApplicationInstance) ContactRequestError() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (rai *RicochetApplicationInstance) SendChatMessage(message string) {
 | 
			
		||||
	rai.connection.Do(func() error {
 | 
			
		||||
		// Technically this errors afte the second time but we can ignore it.
 | 
			
		||||
		rai.connection.RequestOpenChannel("im.ricochet.chat", rai)
 | 
			
		||||
 | 
			
		||||
		channel := rai.connection.Channel("im.ricochet.chat", channels.Outbound)
 | 
			
		||||
		if channel != nil {
 | 
			
		||||
			chatchannel, ok := channel.Handler.(*channels.ChatChannel)
 | 
			
		||||
			if ok {
 | 
			
		||||
				chatchannel.SendMessage(message)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (rai *RicochetApplicationInstance) ChatMessage(messageID uint32, when time.Time, message string) bool {
 | 
			
		||||
	go rai.ChatMessageHandler(rai, messageID, when, message)
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (rai *RicochetApplicationInstance) ChatMessageAck(messageID uint32) {
 | 
			
		||||
	rai.ChatMessageAckHandler(rai, messageID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ra *RicochetApplication) Init(pk *rsa.PrivateKey, cm ContactManagerInterface) {
 | 
			
		||||
	ra.privateKey = pk
 | 
			
		||||
	ra.contactManager = cm
 | 
			
		||||
	ra.chatMessageHandler = func(*RicochetApplicationInstance, uint32, time.Time, string) {}
 | 
			
		||||
	ra.chatMessageAckHandler = func(*RicochetApplicationInstance, uint32) {}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ra *RicochetApplication) OnChatMessage(call func(*RicochetApplicationInstance, uint32, time.Time, string)) {
 | 
			
		||||
	ra.chatMessageHandler = call
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ra *RicochetApplication) OnChatMessageAck(call func(*RicochetApplicationInstance, uint32)) {
 | 
			
		||||
	ra.chatMessageAckHandler = call
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ra *RicochetApplication) handleConnection(conn net.Conn) {
 | 
			
		||||
	rc, err := goricochet.NegotiateVersionInbound(conn)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Printf("There was an error")
 | 
			
		||||
		conn.Close()
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ich := connection.HandleInboundConnection(rc)
 | 
			
		||||
 | 
			
		||||
	err = ich.ProcessAuthAsServer(ra.privateKey, ra.contactManager.LookupContact)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Printf("There was an error")
 | 
			
		||||
		conn.Close()
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rai := new(RicochetApplicationInstance)
 | 
			
		||||
	rai.Init(ra.privateKey, "")
 | 
			
		||||
	rai.RemoteHostname = rc.RemoteHostname
 | 
			
		||||
	rai.connection = rc
 | 
			
		||||
	rai.ChatMessageHandler = ra.chatMessageHandler
 | 
			
		||||
	rai.ChatMessageAckHandler = ra.chatMessageAckHandler
 | 
			
		||||
 | 
			
		||||
	rai.RegisterChannelHandler("im.ricochet.contact.request", func() channels.Handler {
 | 
			
		||||
		contact := new(channels.ContactRequestChannel)
 | 
			
		||||
		contact.Handler = rai
 | 
			
		||||
		return contact
 | 
			
		||||
	})
 | 
			
		||||
	rai.RegisterChannelHandler("im.ricochet.chat", func() channels.Handler {
 | 
			
		||||
		chat := new(channels.ChatChannel)
 | 
			
		||||
		chat.Handler = rai
 | 
			
		||||
		return chat
 | 
			
		||||
	})
 | 
			
		||||
	rc.Process(rai)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ra *RicochetApplication) Shutdown () {
 | 
			
		||||
        log.Printf("Closing")
 | 
			
		||||
        ra.l.Close()
 | 
			
		||||
        log.Printf("Closed")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ra *RicochetApplication) Run(l net.Listener) {
 | 
			
		||||
	if ra.privateKey == nil || ra.contactManager == nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	ra.l = l
 | 
			
		||||
        var err error
 | 
			
		||||
	for err == nil {
 | 
			
		||||
		conn, err := ra.l.Accept()
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			go ra.handleConnection(conn)
 | 
			
		||||
		} else {
 | 
			
		||||
		        log.Printf("Closing")
 | 
			
		||||
		        return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								vendor/github.com/s-rah/go-ricochet/application/contactmanagerinterface.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							
							
						
						
									
										11
									
								
								vendor/github.com/s-rah/go-ricochet/application/contactmanagerinterface.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							| 
						 | 
				
			
			@ -0,0 +1,11 @@
 | 
			
		|||
package application
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/rsa"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ContactManagerInterface provides a mechanism for autonous applications
 | 
			
		||||
// to make decisions on what connections to accept or reject.
 | 
			
		||||
type ContactManagerInterface interface {
 | 
			
		||||
	LookupContact(hostname string, publicKey rsa.PublicKey) (allowed, known bool)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										31
									
								
								vendor/github.com/s-rah/go-ricochet/application/examples/echobot/main.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							
							
						
						
									
										31
									
								
								vendor/github.com/s-rah/go-ricochet/application/examples/echobot/main.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							| 
						 | 
				
			
			@ -0,0 +1,31 @@
 | 
			
		|||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/s-rah/go-ricochet/application"
 | 
			
		||||
	"github.com/s-rah/go-ricochet/utils"
 | 
			
		||||
	"log"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	echobot := new(application.RicochetApplication)
 | 
			
		||||
	pk, err := utils.LoadPrivateKeyFromFile("./testing/private_key")
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatalf("error reading private key file: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	l, err := application.SetupOnion("127.0.0.1:9051", "", pk, 9878)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatalf("error setting up onion service: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	echobot.Init(pk, new(application.AcceptAllContactManager))
 | 
			
		||||
	echobot.OnChatMessage(func(rai *application.RicochetApplicationInstance, id uint32, timestamp time.Time, message string) {
 | 
			
		||||
		log.Printf("message from %v - %v", rai.RemoteHostname, message)
 | 
			
		||||
		rai.SendChatMessage(message)
 | 
			
		||||
	})
 | 
			
		||||
	log.Printf("echobot listening on %s", l.Addr().String())
 | 
			
		||||
	echobot.Run(l)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,27 @@
 | 
			
		|||
package application
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/rsa"
 | 
			
		||||
	"github.com/yawning/bulb"
 | 
			
		||||
	"net"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// "127.0.0.1:9051" "tcp4"
 | 
			
		||||
// "/var/run/tor/control" "unix"
 | 
			
		||||
func SetupOnion(torControlAddress string, torControlSocketType string, authentication string, pk *rsa.PrivateKey, onionport uint16) (net.Listener, error) {
 | 
			
		||||
	c, err := bulb.Dial(torControlSocketType, torControlAddress)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := c.Authenticate(authentication); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cfg := &bulb.NewOnionConfig{
 | 
			
		||||
		DiscardPK:  true,
 | 
			
		||||
		PrivateKey: pk,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return c.NewListener(cfg, onionport)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,57 +0,0 @@
 | 
			
		|||
package goricochet
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/hmac"
 | 
			
		||||
	"crypto/rand"
 | 
			
		||||
	"crypto/sha256"
 | 
			
		||||
	"io"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// AuthenticationHandler manages the state required for the AuthHiddenService
 | 
			
		||||
// authentication scheme for ricochet.
 | 
			
		||||
type AuthenticationHandler struct {
 | 
			
		||||
	clientCookie [16]byte
 | 
			
		||||
	serverCookie [16]byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddClientCookie adds a client cookie to the state.
 | 
			
		||||
func (ah *AuthenticationHandler) AddClientCookie(cookie []byte) {
 | 
			
		||||
	copy(ah.clientCookie[:], cookie[:16])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddServerCookie adds a server cookie to the state.
 | 
			
		||||
func (ah *AuthenticationHandler) AddServerCookie(cookie []byte) {
 | 
			
		||||
	copy(ah.serverCookie[:], cookie[:16])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GenRandom generates a random 16byte cookie string.
 | 
			
		||||
func (ah *AuthenticationHandler) GenRandom() [16]byte {
 | 
			
		||||
	var cookie [16]byte
 | 
			
		||||
	io.ReadFull(rand.Reader, cookie[:])
 | 
			
		||||
	return cookie
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GenClientCookie generates and adds a client cookie to the state.
 | 
			
		||||
func (ah *AuthenticationHandler) GenClientCookie() [16]byte {
 | 
			
		||||
	ah.clientCookie = ah.GenRandom()
 | 
			
		||||
	return ah.clientCookie
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GenServerCookie generates and adds a server cookie to the state.
 | 
			
		||||
func (ah *AuthenticationHandler) GenServerCookie() [16]byte {
 | 
			
		||||
	ah.serverCookie = ah.GenRandom()
 | 
			
		||||
	return ah.serverCookie
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GenChallenge constructs the challenge parameter for the AuthHiddenService session.
 | 
			
		||||
// The challenge is the a Sha256HMAC(clientHostname+serverHostname, key=clientCookie+serverCookie)
 | 
			
		||||
func (ah *AuthenticationHandler) GenChallenge(clientHostname string, serverHostname string) []byte {
 | 
			
		||||
	key := make([]byte, 32)
 | 
			
		||||
	copy(key[0:16], ah.clientCookie[:])
 | 
			
		||||
	copy(key[16:], ah.serverCookie[:])
 | 
			
		||||
	value := []byte(clientHostname + serverHostname)
 | 
			
		||||
	mac := hmac.New(sha256.New, key)
 | 
			
		||||
	mac.Write(value)
 | 
			
		||||
	hmac := mac.Sum(nil)
 | 
			
		||||
	return hmac
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,28 @@
 | 
			
		|||
package channels
 | 
			
		||||
 | 
			
		||||
// Direction indicated whether we or the remote peer opened the channel
 | 
			
		||||
type Direction int
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// Inbound indcates the channel was opened by the remote peer
 | 
			
		||||
	Inbound Direction = iota
 | 
			
		||||
	// Outbound indicated the channel was opened by us
 | 
			
		||||
	Outbound
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Channel holds the state of a channel on an open connection
 | 
			
		||||
type Channel struct {
 | 
			
		||||
	ID int32
 | 
			
		||||
 | 
			
		||||
	Type           string
 | 
			
		||||
	Direction      Direction
 | 
			
		||||
	Handler        Handler
 | 
			
		||||
	Pending        bool
 | 
			
		||||
	ServerHostname string
 | 
			
		||||
	ClientHostname string
 | 
			
		||||
 | 
			
		||||
	// Functions for updating the underlying Connection
 | 
			
		||||
	SendMessage           func([]byte)
 | 
			
		||||
	CloseChannel          func()
 | 
			
		||||
	DelegateAuthorization func()
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,146 @@
 | 
			
		|||
package channels
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/rand"
 | 
			
		||||
	"github.com/golang/protobuf/proto"
 | 
			
		||||
	"github.com/s-rah/go-ricochet/utils"
 | 
			
		||||
	"github.com/s-rah/go-ricochet/wire/chat"
 | 
			
		||||
	"github.com/s-rah/go-ricochet/wire/control"
 | 
			
		||||
	"math"
 | 
			
		||||
	"math/big"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ChatChannel implements the ChannelHandler interface for a channel of
 | 
			
		||||
// type "im.ricochet.chat". The channel may be inbound or outbound.
 | 
			
		||||
//
 | 
			
		||||
// ChatChannel implements protocol-level sanity and state validation, but
 | 
			
		||||
// does not handle or acknowledge chat messages. The application must provide
 | 
			
		||||
// a ChatChannelHandler implementation to handle chat events.
 | 
			
		||||
type ChatChannel struct {
 | 
			
		||||
	// Methods of Handler are called for chat events on this channel
 | 
			
		||||
	Handler       ChatChannelHandler
 | 
			
		||||
	channel       *Channel
 | 
			
		||||
	lastMessageID uint32
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ChatChannelHandler is implemented by an application type to receive
 | 
			
		||||
// events from a ChatChannel.
 | 
			
		||||
//
 | 
			
		||||
// Note that ChatChannelHandler is composable with other interfaces, including
 | 
			
		||||
// ConnectionHandler; there is no need to use a distinct type as a
 | 
			
		||||
// ChatChannelHandler.
 | 
			
		||||
type ChatChannelHandler interface {
 | 
			
		||||
	// ChatMessage is called when a chat message is received. Return true to acknowledge
 | 
			
		||||
	// the message successfully, and false to NACK and refuse the message.
 | 
			
		||||
	ChatMessage(messageID uint32, when time.Time, message string) bool
 | 
			
		||||
	// ChatMessageAck is called when an acknowledgement of a sent message is received.
 | 
			
		||||
	ChatMessageAck(messageID uint32)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SendMessage sends a given message using this channe
 | 
			
		||||
func (cc *ChatChannel) SendMessage(message string) {
 | 
			
		||||
	messageBuilder := new(utils.MessageBuilder)
 | 
			
		||||
	//TODO Implement Chat Number
 | 
			
		||||
	data := messageBuilder.ChatMessage(message, cc.lastMessageID)
 | 
			
		||||
	cc.lastMessageID++
 | 
			
		||||
	cc.channel.SendMessage(data)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Acknowledge indicates the given messageID was received
 | 
			
		||||
func (cc *ChatChannel) Acknowledge(messageID uint32) {
 | 
			
		||||
	messageBuilder := new(utils.MessageBuilder)
 | 
			
		||||
	cc.channel.SendMessage(messageBuilder.AckChatMessage(messageID))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Type returns the type string for this channel, e.g. "im.ricochet.chat".
 | 
			
		||||
func (cc *ChatChannel) Type() string {
 | 
			
		||||
	return "im.ricochet.chat"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Closed is called when the channel is closed for any reason.
 | 
			
		||||
func (cc *ChatChannel) Closed(err error) {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OnlyClientCanOpen  - for chat channels any side can open
 | 
			
		||||
func (cc *ChatChannel) OnlyClientCanOpen() bool {
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Singleton - for chat channels there can only be one instance per direction
 | 
			
		||||
func (cc *ChatChannel) Singleton() bool {
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Bidirectional - for chat channels are not bidrectional
 | 
			
		||||
func (cc *ChatChannel) Bidirectional() bool {
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RequiresAuthentication - chat channels require hidden service auth
 | 
			
		||||
func (cc *ChatChannel) RequiresAuthentication() string {
 | 
			
		||||
	return "im.ricochet.auth.hidden-service"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OpenInbound is the first method called for an inbound channel request.
 | 
			
		||||
// If an error is returned, the channel is rejected. If a RawMessage is
 | 
			
		||||
// returned, it will be sent as the ChannelResult message.
 | 
			
		||||
func (cc *ChatChannel) OpenInbound(channel *Channel, raw *Protocol_Data_Control.OpenChannel) ([]byte, error) {
 | 
			
		||||
	cc.channel = channel
 | 
			
		||||
	id, err := rand.Int(rand.Reader, big.NewInt(math.MaxUint32))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	cc.lastMessageID = uint32(id.Uint64())
 | 
			
		||||
	cc.channel.Pending = false
 | 
			
		||||
	messageBuilder := new(utils.MessageBuilder)
 | 
			
		||||
	return messageBuilder.AckOpenChannel(channel.ID), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OpenOutbound is the first method called for an outbound channel request.
 | 
			
		||||
// If an error is returned, the channel is not opened. If a RawMessage is
 | 
			
		||||
// returned, it will be sent as the OpenChannel message.
 | 
			
		||||
func (cc *ChatChannel) OpenOutbound(channel *Channel) ([]byte, error) {
 | 
			
		||||
	cc.channel = channel
 | 
			
		||||
	id, err := rand.Int(rand.Reader, big.NewInt(math.MaxUint32))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	cc.lastMessageID = uint32(id.Uint64())
 | 
			
		||||
	messageBuilder := new(utils.MessageBuilder)
 | 
			
		||||
	return messageBuilder.OpenChannel(channel.ID, cc.Type()), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OpenOutboundResult is called when a response is received for an
 | 
			
		||||
// outbound OpenChannel request. If `err` is non-nil, the channel was
 | 
			
		||||
// rejected and Closed will be called immediately afterwards. `raw`
 | 
			
		||||
// contains the raw protocol message including any extension data.
 | 
			
		||||
func (cc *ChatChannel) OpenOutboundResult(err error, crm *Protocol_Data_Control.ChannelResult) {
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		if crm.GetOpened() {
 | 
			
		||||
			cc.channel.Pending = false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Packet is called for each raw packet received on this channel.
 | 
			
		||||
func (cc *ChatChannel) Packet(data []byte) {
 | 
			
		||||
	if !cc.channel.Pending {
 | 
			
		||||
		res := new(Protocol_Data_Chat.Packet)
 | 
			
		||||
		err := proto.Unmarshal(data, res)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			if res.GetChatMessage() != nil {
 | 
			
		||||
				ack := cc.Handler.ChatMessage(res.GetChatMessage().GetMessageId(), time.Now(), res.GetChatMessage().GetMessageText())
 | 
			
		||||
				if ack {
 | 
			
		||||
					cc.Acknowledge(res.GetChatMessage().GetMessageId())
 | 
			
		||||
				} else {
 | 
			
		||||
					//XXX
 | 
			
		||||
				}
 | 
			
		||||
			} else if res.GetChatAcknowledge() != nil {
 | 
			
		||||
				cc.Handler.ChatMessageAck(res.GetChatMessage().GetMessageId())
 | 
			
		||||
			}
 | 
			
		||||
			// XXX?
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										162
									
								
								vendor/github.com/s-rah/go-ricochet/channels/contactrequestchannel.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							
							
						
						
									
										162
									
								
								vendor/github.com/s-rah/go-ricochet/channels/contactrequestchannel.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							| 
						 | 
				
			
			@ -0,0 +1,162 @@
 | 
			
		|||
package channels
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/golang/protobuf/proto"
 | 
			
		||||
	"github.com/s-rah/go-ricochet/utils"
 | 
			
		||||
	"github.com/s-rah/go-ricochet/wire/contact"
 | 
			
		||||
	"github.com/s-rah/go-ricochet/wire/control"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Defining Versions
 | 
			
		||||
const (
 | 
			
		||||
	InvalidContactNameError    = utils.Error("InvalidContactNameError")
 | 
			
		||||
	InvalidContactMessageError = utils.Error("InvalidContactMessageError")
 | 
			
		||||
	InvalidContactRequestError = utils.Error("InvalidContactRequestError")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ContactRequestChannel implements the ChannelHandler interface for a channel of
 | 
			
		||||
// type "im.ricochet.contact.request". The channel may be inbound or outbound.
 | 
			
		||||
type ContactRequestChannel struct {
 | 
			
		||||
	// Methods of Handler are called for chat events on this channel
 | 
			
		||||
	Handler ContactRequestChannelHandler
 | 
			
		||||
	channel *Channel
 | 
			
		||||
 | 
			
		||||
	// Properties of the request
 | 
			
		||||
	Name    string
 | 
			
		||||
	Message string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ContactRequestChannelHandler is implemented by an application type to receive
 | 
			
		||||
// events from a ContactRequestChannel.
 | 
			
		||||
//
 | 
			
		||||
// Note that ContactRequestChannelHandler is composable with other interfaces, including
 | 
			
		||||
// ConnectionHandler; there is no need to use a distinct type as a
 | 
			
		||||
// ContactRequestChannelHandler.
 | 
			
		||||
type ContactRequestChannelHandler interface {
 | 
			
		||||
	ContactRequest(name string, message string) string
 | 
			
		||||
	ContactRequestRejected()
 | 
			
		||||
	ContactRequestAccepted()
 | 
			
		||||
	ContactRequestError()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OnlyClientCanOpen - only clients can open contact requests
 | 
			
		||||
func (crc *ContactRequestChannel) OnlyClientCanOpen() bool {
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Singleton - only one contact request can be opened per side
 | 
			
		||||
func (crc *ContactRequestChannel) Singleton() bool {
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Bidirectional - only clients can send messages
 | 
			
		||||
func (crc *ContactRequestChannel) Bidirectional() bool {
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RequiresAuthentication - contact requests require hidden service auth
 | 
			
		||||
func (crc *ContactRequestChannel) RequiresAuthentication() string {
 | 
			
		||||
	return "im.ricochet.auth.hidden-service"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Type returns the type string for this channel, e.g. "im.ricochet.chat".
 | 
			
		||||
func (crc *ContactRequestChannel) Type() string {
 | 
			
		||||
	return "im.ricochet.contact.request"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Closed is called when the channel is closed for any reason.
 | 
			
		||||
func (crc *ContactRequestChannel) Closed(err error) {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OpenInbound is the first method called for an inbound channel request.
 | 
			
		||||
// If an error is returned, the channel is rejected. If a RawMessage is
 | 
			
		||||
// returned, it will be sent as the ChannelResult message.
 | 
			
		||||
func (crc *ContactRequestChannel) OpenInbound(channel *Channel, oc *Protocol_Data_Control.OpenChannel) ([]byte, error) {
 | 
			
		||||
	crc.channel = channel
 | 
			
		||||
	contactRequestI, err := proto.GetExtension(oc, Protocol_Data_ContactRequest.E_ContactRequest)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		contactRequest, check := contactRequestI.(*Protocol_Data_ContactRequest.ContactRequest)
 | 
			
		||||
		if check {
 | 
			
		||||
 | 
			
		||||
			if len(contactRequest.GetNickname()) > int(Protocol_Data_ContactRequest.Limits_NicknameMaxCharacters) {
 | 
			
		||||
				// Violation of the Protocol
 | 
			
		||||
				return nil, InvalidContactNameError
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if len(contactRequest.GetMessageText()) > int(Protocol_Data_ContactRequest.Limits_MessageMaxCharacters) {
 | 
			
		||||
				// Violation of the Protocol
 | 
			
		||||
				return nil, InvalidContactMessageError
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			crc.Name = contactRequest.GetNickname()
 | 
			
		||||
			crc.Message = contactRequest.GetMessageText()
 | 
			
		||||
			result := crc.Handler.ContactRequest(contactRequest.GetNickname(), contactRequest.GetMessageText())
 | 
			
		||||
			messageBuilder := new(utils.MessageBuilder)
 | 
			
		||||
			return messageBuilder.ReplyToContactRequestOnResponse(channel.ID, result), nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil, InvalidContactRequestError
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OpenOutbound is the first method called for an outbound channel request.
 | 
			
		||||
// If an error is returned, the channel is not opened. If a RawMessage is
 | 
			
		||||
// returned, it will be sent as the OpenChannel message.
 | 
			
		||||
func (crc *ContactRequestChannel) OpenOutbound(channel *Channel) ([]byte, error) {
 | 
			
		||||
	crc.channel = channel
 | 
			
		||||
	messageBuilder := new(utils.MessageBuilder)
 | 
			
		||||
	return messageBuilder.OpenContactRequestChannel(channel.ID, crc.Name, crc.Message), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OpenOutboundResult is called when a response is received for an
 | 
			
		||||
// outbound OpenChannel request. If `err` is non-nil, the channel was
 | 
			
		||||
// rejected and Closed will be called immediately afterwards. `raw`
 | 
			
		||||
// contains the raw protocol message including any extension data.
 | 
			
		||||
func (crc *ContactRequestChannel) OpenOutboundResult(err error, crm *Protocol_Data_Control.ChannelResult) {
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		if crm.GetOpened() {
 | 
			
		||||
			responseI, err := proto.GetExtension(crm, Protocol_Data_ContactRequest.E_Response)
 | 
			
		||||
			if err == nil {
 | 
			
		||||
				response, check := responseI.(*Protocol_Data_ContactRequest.Response)
 | 
			
		||||
				if check {
 | 
			
		||||
					crc.handleStatus(response.GetStatus().String())
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	crc.channel.SendMessage([]byte{})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (crc *ContactRequestChannel) SendResponse(status string) {
 | 
			
		||||
	messageBuilder := new(utils.MessageBuilder)
 | 
			
		||||
	crc.channel.SendMessage(messageBuilder.ReplyToContactRequest(crc.channel.ID, status))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (crc *ContactRequestChannel) handleStatus(status string) {
 | 
			
		||||
	switch status {
 | 
			
		||||
	case "Accepted":
 | 
			
		||||
		crc.Handler.ContactRequestAccepted()
 | 
			
		||||
	case "Pending":
 | 
			
		||||
		break
 | 
			
		||||
	case "Rejected":
 | 
			
		||||
		crc.Handler.ContactRequestRejected()
 | 
			
		||||
		break
 | 
			
		||||
	case "Error":
 | 
			
		||||
		crc.Handler.ContactRequestError()
 | 
			
		||||
		break
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Packet is called for each raw packet received on this channel.
 | 
			
		||||
func (crc *ContactRequestChannel) Packet(data []byte) {
 | 
			
		||||
	if !crc.channel.Pending {
 | 
			
		||||
		response := new(Protocol_Data_ContactRequest.Response)
 | 
			
		||||
		err := proto.Unmarshal(data, response)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			crc.handleStatus(response.GetStatus().String())
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	crc.channel.SendMessage([]byte{})
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,51 @@
 | 
			
		|||
package channels
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/s-rah/go-ricochet/wire/control"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Handler reacts to low-level events on a protocol channel. There
 | 
			
		||||
// should be a unique instance of a ChannelHandler type per channel.
 | 
			
		||||
//
 | 
			
		||||
// Applications generally don't need to implement ChannelHandler directly;
 | 
			
		||||
// instead, use the built-in implementations for common channel types, and
 | 
			
		||||
// their individual callback interfaces. ChannelHandler is useful when
 | 
			
		||||
// implementing new channel types, or modifying low level default behavior.
 | 
			
		||||
type Handler interface {
 | 
			
		||||
	// Type returns the type string for this channel, e.g. "im.ricochet.chat".
 | 
			
		||||
	Type() string
 | 
			
		||||
 | 
			
		||||
	// Closed is called when the channel is closed for any reason.
 | 
			
		||||
	Closed(err error)
 | 
			
		||||
 | 
			
		||||
	// OnlyClientCanOpen indicates if only a client can open a given channel
 | 
			
		||||
	OnlyClientCanOpen() bool
 | 
			
		||||
 | 
			
		||||
	// Singleton indicates if a channel can only have one instance per direction
 | 
			
		||||
	Singleton() bool
 | 
			
		||||
 | 
			
		||||
	// Bidirectional indicates if messages can be send by either side
 | 
			
		||||
	Bidirectional() bool
 | 
			
		||||
 | 
			
		||||
	// RequiresAuthentication describes what authentication is needed for the channel
 | 
			
		||||
	RequiresAuthentication() string
 | 
			
		||||
 | 
			
		||||
	// OpenInbound is the first method called for an inbound channel request.
 | 
			
		||||
	// If an error is returned, the channel is rejected. If a RawMessage is
 | 
			
		||||
	// returned, it will be sent as the ChannelResult message.
 | 
			
		||||
	OpenInbound(channel *Channel, raw *Protocol_Data_Control.OpenChannel) ([]byte, error)
 | 
			
		||||
 | 
			
		||||
	// OpenOutbound is the first method called for an outbound channel request.
 | 
			
		||||
	// If an error is returned, the channel is not opened. If a RawMessage is
 | 
			
		||||
	// returned, it will be sent as the OpenChannel message.
 | 
			
		||||
	OpenOutbound(channel *Channel) ([]byte, error)
 | 
			
		||||
 | 
			
		||||
	// OpenOutboundResult is called when a response is received for an
 | 
			
		||||
	// outbound OpenChannel request. If `err` is non-nil, the channel was
 | 
			
		||||
	// rejected and Closed will be called immediately afterwards. `raw`
 | 
			
		||||
	// contains the raw protocol message including any extension data.
 | 
			
		||||
	OpenOutboundResult(err error, raw *Protocol_Data_Control.ChannelResult)
 | 
			
		||||
 | 
			
		||||
	// Packet is called for each raw packet received on this channel.
 | 
			
		||||
	Packet(data []byte)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										260
									
								
								vendor/github.com/s-rah/go-ricochet/channels/hiddenserviceauthchannel.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							
							
						
						
									
										260
									
								
								vendor/github.com/s-rah/go-ricochet/channels/hiddenserviceauthchannel.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							| 
						 | 
				
			
			@ -0,0 +1,260 @@
 | 
			
		|||
package channels
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto"
 | 
			
		||||
	"crypto/hmac"
 | 
			
		||||
	"crypto/rand"
 | 
			
		||||
	"crypto/rsa"
 | 
			
		||||
	"crypto/sha256"
 | 
			
		||||
	"encoding/asn1"
 | 
			
		||||
	"github.com/golang/protobuf/proto"
 | 
			
		||||
	"github.com/s-rah/go-ricochet/utils"
 | 
			
		||||
	"github.com/s-rah/go-ricochet/wire/auth"
 | 
			
		||||
	"github.com/s-rah/go-ricochet/wire/control"
 | 
			
		||||
	"io"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	InvalidClientCookieError = utils.Error("InvalidClientCookieError")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// HiddenServiceAuthChannel wraps implementation of im.ricochet.auth.hidden-service"
 | 
			
		||||
type HiddenServiceAuthChannel struct {
 | 
			
		||||
	// PrivateKey must be set for client-side authentication channels
 | 
			
		||||
	PrivateKey *rsa.PrivateKey
 | 
			
		||||
	// Server Hostname must be set for client-side authentication channels
 | 
			
		||||
	ServerHostname string
 | 
			
		||||
 | 
			
		||||
	// Callbacks
 | 
			
		||||
	ClientAuthResult  func(accepted, isKnownContact bool)
 | 
			
		||||
	ServerAuthValid   func(hostname string, publicKey rsa.PublicKey) (allowed, known bool)
 | 
			
		||||
	ServerAuthInvalid func(err error)
 | 
			
		||||
 | 
			
		||||
	// Internal state
 | 
			
		||||
	clientCookie, serverCookie [16]byte
 | 
			
		||||
	channel                    *Channel
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Type returns the type string for this channel, e.g. "im.ricochet.chat".
 | 
			
		||||
func (ah *HiddenServiceAuthChannel) Type() string {
 | 
			
		||||
	return "im.ricochet.auth.hidden-service"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Singleton Returns whether or not the given channel type is a singleton
 | 
			
		||||
func (ah *HiddenServiceAuthChannel) Singleton() bool {
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OnlyClientCanOpen ...
 | 
			
		||||
func (ah *HiddenServiceAuthChannel) OnlyClientCanOpen() bool {
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Bidirectional Returns whether or not the given channel allows anyone to send messages
 | 
			
		||||
func (ah *HiddenServiceAuthChannel) Bidirectional() bool {
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RequiresAuthentication Returns whether or not the given channel type requires authentication
 | 
			
		||||
func (ah *HiddenServiceAuthChannel) RequiresAuthentication() string {
 | 
			
		||||
	return "none"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Closed is called when the channel is closed for any reason.
 | 
			
		||||
func (ah *HiddenServiceAuthChannel) Closed(err error) {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OpenInbound is the first method called for an inbound channel request.
 | 
			
		||||
// If an error is returned, the channel is rejected. If a RawMessage is
 | 
			
		||||
// returned, it will be sent as the ChannelResult message.
 | 
			
		||||
// Remote -> [Open Authentication Channel] -> Local
 | 
			
		||||
func (ah *HiddenServiceAuthChannel) OpenInbound(channel *Channel, oc *Protocol_Data_Control.OpenChannel) ([]byte, error) {
 | 
			
		||||
 | 
			
		||||
	if ah.PrivateKey == nil {
 | 
			
		||||
		return nil, utils.PrivateKeyNotSetError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ah.channel = channel
 | 
			
		||||
	clientCookie, _ := proto.GetExtension(oc, Protocol_Data_AuthHiddenService.E_ClientCookie)
 | 
			
		||||
	if len(clientCookie.([]byte)[:]) != 16 {
 | 
			
		||||
		// reutrn without opening channel.
 | 
			
		||||
		return nil, InvalidClientCookieError
 | 
			
		||||
	}
 | 
			
		||||
	ah.AddClientCookie(clientCookie.([]byte)[:])
 | 
			
		||||
	messageBuilder := new(utils.MessageBuilder)
 | 
			
		||||
	channel.Pending = false
 | 
			
		||||
	return messageBuilder.ConfirmAuthChannel(ah.channel.ID, ah.GenServerCookie()), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OpenOutbound is the first method called for an outbound channel request.
 | 
			
		||||
// If an error is returned, the channel is not opened. If a RawMessage is
 | 
			
		||||
// returned, it will be sent as the OpenChannel message.
 | 
			
		||||
// Local -> [Open Authentication Channel] -> Remote
 | 
			
		||||
func (ah *HiddenServiceAuthChannel) OpenOutbound(channel *Channel) ([]byte, error) {
 | 
			
		||||
 | 
			
		||||
	if ah.PrivateKey == nil {
 | 
			
		||||
		return nil, utils.PrivateKeyNotSetError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ah.channel = channel
 | 
			
		||||
	messageBuilder := new(utils.MessageBuilder)
 | 
			
		||||
	return messageBuilder.OpenAuthenticationChannel(ah.channel.ID, ah.GenClientCookie()), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OpenOutboundResult is called when a response is received for an
 | 
			
		||||
// outbound OpenChannel request. If `err` is non-nil, the channel was
 | 
			
		||||
// rejected and Closed will be called immediately afterwards. `raw`
 | 
			
		||||
// contains the raw protocol message including any extension data.
 | 
			
		||||
// Input: Remote -> [ChannelResult] -> {Client}
 | 
			
		||||
// Output: {Client} -> [Proof] -> Remote
 | 
			
		||||
func (ah *HiddenServiceAuthChannel) OpenOutboundResult(err error, crm *Protocol_Data_Control.ChannelResult) {
 | 
			
		||||
 | 
			
		||||
	if err == nil {
 | 
			
		||||
 | 
			
		||||
		if crm.GetOpened() {
 | 
			
		||||
			serverCookie, _ := proto.GetExtension(crm, Protocol_Data_AuthHiddenService.E_ServerCookie)
 | 
			
		||||
 | 
			
		||||
			if len(serverCookie.([]byte)[:]) != 16 {
 | 
			
		||||
				ah.channel.SendMessage([]byte{})
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			ah.AddServerCookie(serverCookie.([]byte)[:])
 | 
			
		||||
 | 
			
		||||
			publicKeyBytes, _ := asn1.Marshal(rsa.PublicKey{
 | 
			
		||||
				N: ah.PrivateKey.PublicKey.N,
 | 
			
		||||
				E: ah.PrivateKey.PublicKey.E,
 | 
			
		||||
			})
 | 
			
		||||
 | 
			
		||||
			clientHostname := utils.GetTorHostname(publicKeyBytes)
 | 
			
		||||
			challenge := ah.GenChallenge(clientHostname, ah.ServerHostname)
 | 
			
		||||
 | 
			
		||||
			signature, err := rsa.SignPKCS1v15(nil, ah.PrivateKey, crypto.SHA256, challenge)
 | 
			
		||||
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				ah.channel.SendMessage([]byte{})
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			messageBuilder := new(utils.MessageBuilder)
 | 
			
		||||
			proof := messageBuilder.Proof(publicKeyBytes, signature)
 | 
			
		||||
			ah.channel.SendMessage(proof)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Packet is called for each raw packet received on this channel.
 | 
			
		||||
// Input: Remote -> [Proof] -> Client
 | 
			
		||||
// OR
 | 
			
		||||
// Input: Remote -> [Result] -> Client
 | 
			
		||||
func (ah *HiddenServiceAuthChannel) Packet(data []byte) {
 | 
			
		||||
	res := new(Protocol_Data_AuthHiddenService.Packet)
 | 
			
		||||
	err := proto.Unmarshal(data[:], res)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ah.channel.CloseChannel()
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if res.GetProof() != nil && ah.channel.Direction == Inbound {
 | 
			
		||||
		provisionalClientHostname := utils.GetTorHostname(res.GetProof().GetPublicKey())
 | 
			
		||||
 | 
			
		||||
		publicKeyBytes, err := asn1.Marshal(rsa.PublicKey{
 | 
			
		||||
			N: ah.PrivateKey.PublicKey.N,
 | 
			
		||||
			E: ah.PrivateKey.PublicKey.E,
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			ah.ServerAuthInvalid(err)
 | 
			
		||||
			ah.channel.SendMessage([]byte{})
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		serverHostname := utils.GetTorHostname(publicKeyBytes)
 | 
			
		||||
 | 
			
		||||
		publicKey := rsa.PublicKey{}
 | 
			
		||||
		_, err = asn1.Unmarshal(res.GetProof().GetPublicKey(), &publicKey)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			ah.ServerAuthInvalid(err)
 | 
			
		||||
			ah.channel.SendMessage([]byte{})
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		challenge := ah.GenChallenge(provisionalClientHostname, serverHostname)
 | 
			
		||||
 | 
			
		||||
		err = rsa.VerifyPKCS1v15(&publicKey, crypto.SHA256, challenge[:], res.GetProof().GetSignature())
 | 
			
		||||
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			// Signature is Good
 | 
			
		||||
			accepted, isKnownContact := ah.ServerAuthValid(provisionalClientHostname, publicKey)
 | 
			
		||||
 | 
			
		||||
			// Send Result
 | 
			
		||||
			messageBuilder := new(utils.MessageBuilder)
 | 
			
		||||
			result := messageBuilder.AuthResult(accepted, isKnownContact)
 | 
			
		||||
			ah.channel.DelegateAuthorization()
 | 
			
		||||
			ah.channel.SendMessage(result)
 | 
			
		||||
		} else {
 | 
			
		||||
			// Auth Failed
 | 
			
		||||
			messageBuilder := new(utils.MessageBuilder)
 | 
			
		||||
			result := messageBuilder.AuthResult(false, false)
 | 
			
		||||
			ah.channel.SendMessage(result)
 | 
			
		||||
			ah.ServerAuthInvalid(err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	} else if res.GetResult() != nil && ah.channel.Direction == Outbound {
 | 
			
		||||
		if ah.ClientAuthResult != nil {
 | 
			
		||||
			ah.ClientAuthResult(res.GetResult().GetAccepted(), res.GetResult().GetIsKnownContact())
 | 
			
		||||
		}
 | 
			
		||||
		if res.GetResult().GetAccepted() {
 | 
			
		||||
			ah.channel.DelegateAuthorization()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Any other combination of packets is completely invalid
 | 
			
		||||
	// Fail the Authorization right here.
 | 
			
		||||
	ah.channel.CloseChannel()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddClientCookie adds a client cookie to the state.
 | 
			
		||||
func (ah *HiddenServiceAuthChannel) AddClientCookie(cookie []byte) {
 | 
			
		||||
	copy(ah.clientCookie[:], cookie[:16])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddServerCookie adds a server cookie to the state.
 | 
			
		||||
func (ah *HiddenServiceAuthChannel) AddServerCookie(cookie []byte) {
 | 
			
		||||
	copy(ah.serverCookie[:], cookie[:16])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GenRandom generates a random 16byte cookie string.
 | 
			
		||||
func (ah *HiddenServiceAuthChannel) GenRandom() [16]byte {
 | 
			
		||||
	var cookie [16]byte
 | 
			
		||||
	io.ReadFull(rand.Reader, cookie[:])
 | 
			
		||||
	return cookie
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GenClientCookie generates and adds a client cookie to the state.
 | 
			
		||||
func (ah *HiddenServiceAuthChannel) GenClientCookie() [16]byte {
 | 
			
		||||
	ah.clientCookie = ah.GenRandom()
 | 
			
		||||
	return ah.clientCookie
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GenServerCookie generates and adds a server cookie to the state.
 | 
			
		||||
func (ah *HiddenServiceAuthChannel) GenServerCookie() [16]byte {
 | 
			
		||||
	ah.serverCookie = ah.GenRandom()
 | 
			
		||||
	return ah.serverCookie
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GenChallenge constructs the challenge parameter for the AuthHiddenService session.
 | 
			
		||||
// The challenge is the a Sha256HMAC(clientHostname+serverHostname, key=clientCookie+serverCookie)
 | 
			
		||||
func (ah *HiddenServiceAuthChannel) GenChallenge(clientHostname string, serverHostname string) []byte {
 | 
			
		||||
	key := make([]byte, 32)
 | 
			
		||||
	copy(key[0:16], ah.clientCookie[:])
 | 
			
		||||
	copy(key[16:], ah.serverCookie[:])
 | 
			
		||||
 | 
			
		||||
	value := []byte(clientHostname + serverHostname)
 | 
			
		||||
	mac := hmac.New(sha256.New, key)
 | 
			
		||||
	mac.Write(value)
 | 
			
		||||
	hmac := mac.Sum(nil)
 | 
			
		||||
	return hmac
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										53
									
								
								vendor/github.com/s-rah/go-ricochet/connection/autoconnectionhandler.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							
							
						
						
									
										53
									
								
								vendor/github.com/s-rah/go-ricochet/connection/autoconnectionhandler.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							| 
						 | 
				
			
			@ -0,0 +1,53 @@
 | 
			
		|||
package connection
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/s-rah/go-ricochet/channels"
 | 
			
		||||
	"github.com/s-rah/go-ricochet/utils"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// AutoConnectionHandler implements the ConnectionHandler interface on behalf of
 | 
			
		||||
// the provided application type by automatically providing support for any
 | 
			
		||||
// built-in channel type whose high level interface is implemented by the
 | 
			
		||||
// application. For example, if the application's type implements the
 | 
			
		||||
// ChatChannelHandler interface, `im.ricochet.chat` will be available to the peer.
 | 
			
		||||
//
 | 
			
		||||
// The application handler can be any other type. To override or augment any of
 | 
			
		||||
// AutoConnectionHandler's behavior (such as adding new channel types, or reacting
 | 
			
		||||
// to connection close events), this type can be embedded in the type that it serves.
 | 
			
		||||
type AutoConnectionHandler struct {
 | 
			
		||||
	handlerMap map[string]func() channels.Handler
 | 
			
		||||
	connection *Connection
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Init ...
 | 
			
		||||
// TODO: Split this into client and server init
 | 
			
		||||
func (ach *AutoConnectionHandler) Init() {
 | 
			
		||||
	ach.handlerMap = make(map[string]func() channels.Handler)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OnReady ...
 | 
			
		||||
func (ach *AutoConnectionHandler) OnReady(oc *Connection) {
 | 
			
		||||
	ach.connection = oc
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OnClosed is called when the OpenConnection has closed for any reason.
 | 
			
		||||
func (ach *AutoConnectionHandler) OnClosed(err error) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RegisterChannelHandler ...
 | 
			
		||||
func (ach *AutoConnectionHandler) RegisterChannelHandler(ctype string, handler func() channels.Handler) {
 | 
			
		||||
	_, exists := ach.handlerMap[ctype]
 | 
			
		||||
	if !exists {
 | 
			
		||||
		ach.handlerMap[ctype] = handler
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OnOpenChannelRequest ...
 | 
			
		||||
func (ach *AutoConnectionHandler) OnOpenChannelRequest(ctype string) (channels.Handler, error) {
 | 
			
		||||
	handler, ok := ach.handlerMap[ctype]
 | 
			
		||||
	if ok {
 | 
			
		||||
		h := handler()
 | 
			
		||||
		return h, nil
 | 
			
		||||
	}
 | 
			
		||||
	return nil, utils.UnknownChannelTypeError
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,114 @@
 | 
			
		|||
package connection
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/s-rah/go-ricochet/channels"
 | 
			
		||||
	"github.com/s-rah/go-ricochet/utils"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ChannelManager encapsulates the logic for server and client side assignment
 | 
			
		||||
// and removal of channels.
 | 
			
		||||
type ChannelManager struct {
 | 
			
		||||
	channels        map[int32]*channels.Channel
 | 
			
		||||
	nextFreeChannel int32
 | 
			
		||||
	isClient        bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewClientChannelManager construsts a new channel manager enforcing behaviour
 | 
			
		||||
// of a ricochet client
 | 
			
		||||
func NewClientChannelManager() *ChannelManager {
 | 
			
		||||
	channelManager := new(ChannelManager)
 | 
			
		||||
	channelManager.channels = make(map[int32]*channels.Channel)
 | 
			
		||||
	channelManager.nextFreeChannel = 1
 | 
			
		||||
	channelManager.isClient = true
 | 
			
		||||
	return channelManager
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewServerChannelManager construsts a new channel manager enforcing behaviour
 | 
			
		||||
// from a ricochet server
 | 
			
		||||
func NewServerChannelManager() *ChannelManager {
 | 
			
		||||
	channelManager := new(ChannelManager)
 | 
			
		||||
	channelManager.channels = make(map[int32]*channels.Channel)
 | 
			
		||||
	channelManager.nextFreeChannel = 2
 | 
			
		||||
	channelManager.isClient = false
 | 
			
		||||
	return channelManager
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OpenChannelRequest  constructs a channel type ready for processing given a request
 | 
			
		||||
// from the client.
 | 
			
		||||
func (cm *ChannelManager) OpenChannelRequest(chandler channels.Handler) (*channels.Channel, error) {
 | 
			
		||||
	// Some channels only allow us to open one of them per connection
 | 
			
		||||
	if chandler.Singleton() && cm.Channel(chandler.Type(), channels.Outbound) != nil {
 | 
			
		||||
		return nil, utils.AttemptToOpenMoreThanOneSingletonChannelError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	channel := new(channels.Channel)
 | 
			
		||||
	channel.ID = cm.nextFreeChannel
 | 
			
		||||
	cm.nextFreeChannel += 2
 | 
			
		||||
	channel.Type = chandler.Type()
 | 
			
		||||
	channel.Handler = chandler
 | 
			
		||||
	channel.Pending = true
 | 
			
		||||
	channel.Direction = channels.Outbound
 | 
			
		||||
	cm.channels[channel.ID] = channel
 | 
			
		||||
	return channel, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OpenChannelRequestFromPeer constructs a channel type ready for processing given a request
 | 
			
		||||
// from the remote peer.
 | 
			
		||||
func (cm *ChannelManager) OpenChannelRequestFromPeer(channelID int32, chandler channels.Handler) (*channels.Channel, error) {
 | 
			
		||||
	if cm.isClient && (channelID%2) != 0 {
 | 
			
		||||
		// Server is trying to open odd numbered channels
 | 
			
		||||
		return nil, utils.ServerAttemptedToOpenEvenNumberedChannelError
 | 
			
		||||
	} else if !cm.isClient && (channelID%2) == 0 {
 | 
			
		||||
		// Server is trying to open odd numbered channels
 | 
			
		||||
		return nil, utils.ClientAttemptedToOpenOddNumberedChannelError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, exists := cm.channels[channelID]
 | 
			
		||||
	if exists {
 | 
			
		||||
		return nil, utils.ChannelIDIsAlreadyInUseError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Some channels only allow us to open one of them per connection
 | 
			
		||||
	if chandler.Singleton() && cm.Channel(chandler.Type(), channels.Inbound) != nil {
 | 
			
		||||
		return nil, utils.AttemptToOpenMoreThanOneSingletonChannelError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	channel := new(channels.Channel)
 | 
			
		||||
	channel.ID = channelID
 | 
			
		||||
	channel.Type = chandler.Type()
 | 
			
		||||
	channel.Handler = chandler
 | 
			
		||||
 | 
			
		||||
	channel.Pending = true
 | 
			
		||||
	channel.Direction = channels.Inbound
 | 
			
		||||
	cm.channels[channelID] = channel
 | 
			
		||||
	return channel, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Channel finds an open or pending `type` channel in the direction `way` (Inbound
 | 
			
		||||
// or Outbound), and returns the associated state. Returns nil if no matching channel
 | 
			
		||||
// exists or if multiple matching channels exist.
 | 
			
		||||
func (cm *ChannelManager) Channel(ctype string, way channels.Direction) *channels.Channel {
 | 
			
		||||
	var foundChannel *channels.Channel
 | 
			
		||||
	for _, channel := range cm.channels {
 | 
			
		||||
		if channel.Handler.Type() == ctype && channel.Direction == way {
 | 
			
		||||
			if foundChannel == nil {
 | 
			
		||||
				foundChannel = channel
 | 
			
		||||
			} else {
 | 
			
		||||
				// we have found multiple channels.
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return foundChannel
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetChannel finds and returns a given channel if it is found
 | 
			
		||||
func (cm *ChannelManager) GetChannel(channelID int32) (*channels.Channel, bool) {
 | 
			
		||||
	channel, found := cm.channels[channelID]
 | 
			
		||||
	return channel, found
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RemoveChannel removes a given channel id.
 | 
			
		||||
func (cm *ChannelManager) RemoveChannel(channelID int32) {
 | 
			
		||||
	delete(cm.channels, channelID)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,498 @@
 | 
			
		|||
package connection
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/golang/protobuf/proto"
 | 
			
		||||
	"github.com/s-rah/go-ricochet/channels"
 | 
			
		||||
	"github.com/s-rah/go-ricochet/utils"
 | 
			
		||||
	"github.com/s-rah/go-ricochet/wire/control"
 | 
			
		||||
	"io"
 | 
			
		||||
	"log"
 | 
			
		||||
	"sync"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Connection encapsulates the state required to maintain a connection to
 | 
			
		||||
// a ricochet service.
 | 
			
		||||
type Connection struct {
 | 
			
		||||
	utils.RicochetNetwork
 | 
			
		||||
 | 
			
		||||
	channelManager *ChannelManager
 | 
			
		||||
 | 
			
		||||
	// Ricochet Network Loop
 | 
			
		||||
	packetChannel chan utils.RicochetData
 | 
			
		||||
	errorChannel  chan error
 | 
			
		||||
 | 
			
		||||
	breakChannel       chan bool
 | 
			
		||||
	breakResultChannel chan error
 | 
			
		||||
 | 
			
		||||
	unlockChannel         chan bool
 | 
			
		||||
	unlockResponseChannel chan bool
 | 
			
		||||
 | 
			
		||||
	messageBuilder utils.MessageBuilder
 | 
			
		||||
	trace          bool
 | 
			
		||||
 | 
			
		||||
	closed  bool
 | 
			
		||||
	closing bool
 | 
			
		||||
	// This mutex is exclusively for preventing races during blocking
 | 
			
		||||
	// interactions with Process; specifically Do and Break. Don't use
 | 
			
		||||
	// it for anything else. See those functions for an explanation.
 | 
			
		||||
	processBlockMutex sync.Mutex
 | 
			
		||||
 | 
			
		||||
	Conn           io.ReadWriteCloser
 | 
			
		||||
	IsInbound      bool
 | 
			
		||||
	Authentication map[string]bool
 | 
			
		||||
	RemoteHostname string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (rc *Connection) init() {
 | 
			
		||||
 | 
			
		||||
	rc.packetChannel = make(chan utils.RicochetData)
 | 
			
		||||
	rc.errorChannel = make(chan error)
 | 
			
		||||
 | 
			
		||||
	rc.breakChannel = make(chan bool)
 | 
			
		||||
	rc.breakResultChannel = make(chan error)
 | 
			
		||||
 | 
			
		||||
	rc.unlockChannel = make(chan bool)
 | 
			
		||||
	rc.unlockResponseChannel = make(chan bool)
 | 
			
		||||
 | 
			
		||||
	rc.Authentication = make(map[string]bool)
 | 
			
		||||
	go rc.start()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewInboundConnection creates a new Connection struct
 | 
			
		||||
// modelling an Inbound Connection
 | 
			
		||||
func NewInboundConnection(conn io.ReadWriteCloser) *Connection {
 | 
			
		||||
	rc := new(Connection)
 | 
			
		||||
	rc.Conn = conn
 | 
			
		||||
	rc.IsInbound = true
 | 
			
		||||
	rc.init()
 | 
			
		||||
	rc.channelManager = NewServerChannelManager()
 | 
			
		||||
	return rc
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewOutboundConnection creates a new Connection struct
 | 
			
		||||
// modelling an Inbound Connection
 | 
			
		||||
func NewOutboundConnection(conn io.ReadWriteCloser, remoteHostname string) *Connection {
 | 
			
		||||
	rc := new(Connection)
 | 
			
		||||
	rc.Conn = conn
 | 
			
		||||
	rc.IsInbound = false
 | 
			
		||||
	rc.init()
 | 
			
		||||
	rc.RemoteHostname = remoteHostname
 | 
			
		||||
	rc.channelManager = NewClientChannelManager()
 | 
			
		||||
	return rc
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (rc *Connection) TraceLog(enabled bool) {
 | 
			
		||||
	rc.trace = enabled
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// start
 | 
			
		||||
func (rc *Connection) start() {
 | 
			
		||||
	for {
 | 
			
		||||
		packet, err := rc.RecvRicochetPacket(rc.Conn)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			rc.errorChannel <- err
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		rc.packetChannel <- packet
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Do allows any function utilizing Connection to be run safely, if you're
 | 
			
		||||
// careful. All operations which require access (directly or indirectly) to
 | 
			
		||||
// Connection while Process is running need to use Do. Calls to Do without
 | 
			
		||||
// Process running will block unless the connection is closed, which is
 | 
			
		||||
// returned as ConnectionClosedError.
 | 
			
		||||
//
 | 
			
		||||
// Like a mutex, Do cannot be called recursively. This will deadlock. As
 | 
			
		||||
// a result, no API in this library that can be reached from the application
 | 
			
		||||
// should use Do, with few exceptions. This would make the API impossible
 | 
			
		||||
// to use safely in many cases.
 | 
			
		||||
//
 | 
			
		||||
// Do is safe to call from methods of connection.Handler and channel.Handler
 | 
			
		||||
// that are called by Process.
 | 
			
		||||
func (rc *Connection) Do(do func() error) error {
 | 
			
		||||
	// There's a complicated little dance here to prevent a race when the
 | 
			
		||||
	// Process call is returning for a connection error. The problem is
 | 
			
		||||
	// that if Do simply checked rc.closed and then tried to send, it's
 | 
			
		||||
	// possible for Process to change rc.closed and stop reading before the
 | 
			
		||||
	// send statement is executed, creating a deadlock.
 | 
			
		||||
	//
 | 
			
		||||
	// To prevent this, all of the functions that block on Process should
 | 
			
		||||
	// do so by acquiring processBlockMutex, aborting if rc.closed is true,
 | 
			
		||||
	// performing their blocking channel operations, and then releasing the
 | 
			
		||||
	// mutex.
 | 
			
		||||
	//
 | 
			
		||||
	// This works because Process will always use a separate goroutine to
 | 
			
		||||
	// acquire processBlockMutex before changing rc.closed, and the mutex
 | 
			
		||||
	// guarantees that no blocking channel operation can happen during or
 | 
			
		||||
	// after the value is changed. Since these operations block the Process
 | 
			
		||||
	// loop, the behavior of multiple concurrent calls to Do/Break doesn't
 | 
			
		||||
	// change: they just end up blocking on the mutex before blocking on the
 | 
			
		||||
	// channel.
 | 
			
		||||
	rc.processBlockMutex.Lock()
 | 
			
		||||
	defer rc.processBlockMutex.Unlock()
 | 
			
		||||
	if rc.closed {
 | 
			
		||||
		return utils.ConnectionClosedError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Force process to soft-break so we can lock
 | 
			
		||||
	rc.traceLog("request unlocking of process loop for do()")
 | 
			
		||||
	rc.unlockChannel <- true
 | 
			
		||||
	rc.traceLog("process loop is unlocked for do()")
 | 
			
		||||
	defer func() {
 | 
			
		||||
		rc.traceLog("giving up lock process loop after do() ")
 | 
			
		||||
		rc.unlockResponseChannel <- true
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	// Process sets rc.closing when it's trying to acquire the mutex and
 | 
			
		||||
	// close down the connection. Behave as if the connection was already
 | 
			
		||||
	// closed.
 | 
			
		||||
	if rc.closing {
 | 
			
		||||
		return utils.ConnectionClosedError
 | 
			
		||||
	}
 | 
			
		||||
	return do()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DoContext behaves in the same way as Do, but also respects the provided
 | 
			
		||||
// context when blocked, and passes the context to the callback function.
 | 
			
		||||
//
 | 
			
		||||
// DoContext should be used when any call to Do may need to be cancelled
 | 
			
		||||
// or timed out.
 | 
			
		||||
func (rc *Connection) DoContext(ctx context.Context, do func(context.Context) error) error {
 | 
			
		||||
	// .. see above
 | 
			
		||||
	rc.processBlockMutex.Lock()
 | 
			
		||||
	defer rc.processBlockMutex.Unlock()
 | 
			
		||||
	if rc.closed {
 | 
			
		||||
		return utils.ConnectionClosedError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Force process to soft-break so we can lock
 | 
			
		||||
	rc.traceLog("request unlocking of process loop for do()")
 | 
			
		||||
	select {
 | 
			
		||||
	case rc.unlockChannel <- true:
 | 
			
		||||
		break
 | 
			
		||||
	case <-ctx.Done():
 | 
			
		||||
		rc.traceLog("giving up on unlocking process loop for do() because context cancelled")
 | 
			
		||||
		return ctx.Err()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rc.traceLog("process loop is unlocked for do()")
 | 
			
		||||
	defer func() {
 | 
			
		||||
		rc.traceLog("giving up lock process loop after do() ")
 | 
			
		||||
		rc.unlockResponseChannel <- true
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	if rc.closing {
 | 
			
		||||
		return utils.ConnectionClosedError
 | 
			
		||||
	}
 | 
			
		||||
	return do(ctx)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RequestOpenChannel sends an OpenChannel message to the remote client.
 | 
			
		||||
// An error is returned only if the requirements for opening this channel
 | 
			
		||||
// are not met on the local side (a nil error return does not mean the
 | 
			
		||||
// channel was opened successfully, because channels open asynchronously).
 | 
			
		||||
func (rc *Connection) RequestOpenChannel(ctype string, handler channels.Handler) (*channels.Channel, error) {
 | 
			
		||||
	rc.traceLog(fmt.Sprintf("requesting open channel of type %s", ctype))
 | 
			
		||||
 | 
			
		||||
	// Check that we have the authentication already
 | 
			
		||||
	if handler.RequiresAuthentication() != "none" {
 | 
			
		||||
		// Enforce Authentication Check.
 | 
			
		||||
		_, authed := rc.Authentication[handler.RequiresAuthentication()]
 | 
			
		||||
		if !authed {
 | 
			
		||||
			return nil, utils.UnauthorizedActionError
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	channel, err := rc.channelManager.OpenChannelRequest(handler)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		rc.traceLog(fmt.Sprintf("failed to request open channel of type %v", err))
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	channel.SendMessage = func(message []byte) {
 | 
			
		||||
		rc.SendRicochetPacket(rc.Conn, channel.ID, message)
 | 
			
		||||
	}
 | 
			
		||||
	channel.DelegateAuthorization = func() {
 | 
			
		||||
		rc.Authentication[handler.Type()] = true
 | 
			
		||||
	}
 | 
			
		||||
	channel.CloseChannel = func() {
 | 
			
		||||
		rc.SendRicochetPacket(rc.Conn, channel.ID, []byte{})
 | 
			
		||||
		rc.channelManager.RemoveChannel(channel.ID)
 | 
			
		||||
	}
 | 
			
		||||
	response, err := handler.OpenOutbound(channel)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		rc.traceLog(fmt.Sprintf("requested open channel of type %s", ctype))
 | 
			
		||||
		rc.SendRicochetPacket(rc.Conn, 0, response)
 | 
			
		||||
	} else {
 | 
			
		||||
		rc.traceLog(fmt.Sprintf("failed to request open channel of type %v", err))
 | 
			
		||||
		rc.channelManager.RemoveChannel(channel.ID)
 | 
			
		||||
	}
 | 
			
		||||
	return channel, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// processUserCallback should be used to wrap any calls into handlers or
 | 
			
		||||
// application code from the Process goroutine. It handles calls to Do
 | 
			
		||||
// from within that code to prevent deadlocks.
 | 
			
		||||
func (rc *Connection) processUserCallback(cb func()) {
 | 
			
		||||
	done := make(chan struct{})
 | 
			
		||||
	go func() {
 | 
			
		||||
		defer close(done)
 | 
			
		||||
		cb()
 | 
			
		||||
	}()
 | 
			
		||||
	for {
 | 
			
		||||
		select {
 | 
			
		||||
		case <-done:
 | 
			
		||||
			return
 | 
			
		||||
		case <-rc.unlockChannel:
 | 
			
		||||
			<-rc.unlockResponseChannel
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Process receives socket and protocol events for the connection. Methods
 | 
			
		||||
// of the application-provided `handler` will be called from this goroutine
 | 
			
		||||
// for all events.
 | 
			
		||||
//
 | 
			
		||||
// Process must be running in order to handle any events on the connection,
 | 
			
		||||
// including connection close.
 | 
			
		||||
//
 | 
			
		||||
// Process blocks until the connection is closed or until Break() is called.
 | 
			
		||||
// If the connection is closed, a non-nil error is returned.
 | 
			
		||||
func (rc *Connection) Process(handler Handler) error {
 | 
			
		||||
	if rc.closed {
 | 
			
		||||
		return utils.ConnectionClosedError
 | 
			
		||||
	}
 | 
			
		||||
	rc.traceLog("entering process loop")
 | 
			
		||||
	rc.processUserCallback(func() { handler.OnReady(rc) })
 | 
			
		||||
 | 
			
		||||
	// There are exactly two ways out of this loop: a signal on breakChannel
 | 
			
		||||
	// caused by a call to Break, or a connection-fatal error on errorChannel.
 | 
			
		||||
	//
 | 
			
		||||
	// In the Break case, no particular care is necessary; it is the caller's
 | 
			
		||||
	// responsibility to make sure there aren't e.g. concurrent calls to Do.
 | 
			
		||||
	//
 | 
			
		||||
	// Because connection errors can happen spontaneously, they must carefully
 | 
			
		||||
	// prevent concurrent calls to Break or Do that could deadlock when Process
 | 
			
		||||
	// returns.
 | 
			
		||||
	for {
 | 
			
		||||
 | 
			
		||||
		var packet utils.RicochetData
 | 
			
		||||
		select {
 | 
			
		||||
		case <-rc.unlockChannel:
 | 
			
		||||
			<-rc.unlockResponseChannel
 | 
			
		||||
			continue
 | 
			
		||||
		case <-rc.breakChannel:
 | 
			
		||||
			rc.traceLog("process has ended after break")
 | 
			
		||||
			rc.breakResultChannel <- nil
 | 
			
		||||
			return nil
 | 
			
		||||
		case packet = <-rc.packetChannel:
 | 
			
		||||
			break
 | 
			
		||||
		case err := <-rc.errorChannel:
 | 
			
		||||
			rc.Conn.Close()
 | 
			
		||||
			rc.closing = true
 | 
			
		||||
 | 
			
		||||
			// In order to safely close down concurrent calls to Do or Break,
 | 
			
		||||
			// processBlockMutex must be held before setting rc.closed. That cannot
 | 
			
		||||
			// happen in this goroutine, because one of those calls may already hold
 | 
			
		||||
			// the mutex and be blocking on a channel send to this method. So the
 | 
			
		||||
			// process here is to have a goroutine acquire the lock, set rc.closed, and
 | 
			
		||||
			// signal back. Meanwhile, this one keeps handling unlockChannel and
 | 
			
		||||
			// breakChannel.
 | 
			
		||||
			closedChan := make(chan struct{})
 | 
			
		||||
			go func() {
 | 
			
		||||
				rc.processBlockMutex.Lock()
 | 
			
		||||
				defer rc.processBlockMutex.Unlock()
 | 
			
		||||
				rc.closed = true
 | 
			
		||||
				close(closedChan)
 | 
			
		||||
			}()
 | 
			
		||||
 | 
			
		||||
			// Keep accepting calls from Do or Break until closedChan signals that they're
 | 
			
		||||
			// safely shut down.
 | 
			
		||||
		clearLoop:
 | 
			
		||||
			for {
 | 
			
		||||
				select {
 | 
			
		||||
				case <-rc.unlockChannel:
 | 
			
		||||
					<-rc.unlockResponseChannel
 | 
			
		||||
				case <-rc.breakChannel:
 | 
			
		||||
					rc.breakResultChannel <- utils.ConnectionClosedError
 | 
			
		||||
				case <-closedChan:
 | 
			
		||||
					break clearLoop
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// This is the one case where processUserCallback isn't necessary, because
 | 
			
		||||
			// all calls to Do immediately return ConnectionClosedError now.
 | 
			
		||||
			handler.OnClosed(err)
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if packet.Channel == 0 {
 | 
			
		||||
			rc.traceLog(fmt.Sprintf("received control packet on channel %d", packet.Channel))
 | 
			
		||||
			res := new(Protocol_Data_Control.Packet)
 | 
			
		||||
			err := proto.Unmarshal(packet.Data[:], res)
 | 
			
		||||
			if err == nil {
 | 
			
		||||
				// Wrap controlPacket in processUserCallback, since it calls out in many
 | 
			
		||||
				// places, and wrapping the rest is harmless.
 | 
			
		||||
				rc.processUserCallback(func() { rc.controlPacket(handler, res) })
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			// Let's check to see if we have defined this channel.
 | 
			
		||||
			channel, found := rc.channelManager.GetChannel(packet.Channel)
 | 
			
		||||
			if found {
 | 
			
		||||
				if len(packet.Data) == 0 {
 | 
			
		||||
					rc.traceLog(fmt.Sprintf("removing channel %d", packet.Channel))
 | 
			
		||||
					rc.channelManager.RemoveChannel(packet.Channel)
 | 
			
		||||
					rc.processUserCallback(func() { channel.Handler.Closed(utils.ChannelClosedByPeerError) })
 | 
			
		||||
				} else {
 | 
			
		||||
					rc.traceLog(fmt.Sprintf("received packet on %v channel %d", channel.Handler.Type(), packet.Channel))
 | 
			
		||||
					// Send The Ricochet Packet to the Handler
 | 
			
		||||
					rc.processUserCallback(func() { channel.Handler.Packet(packet.Data[:]) })
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				// When a non-zero packet is received for an unknown
 | 
			
		||||
				// channel, the recipient responds by closing
 | 
			
		||||
				// that channel.
 | 
			
		||||
				rc.traceLog(fmt.Sprintf("received packet on unknown channel %d. closing.", packet.Channel))
 | 
			
		||||
				if len(packet.Data) != 0 {
 | 
			
		||||
					rc.SendRicochetPacket(rc.Conn, packet.Channel, []byte{})
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (rc *Connection) controlPacket(handler Handler, res *Protocol_Data_Control.Packet) {
 | 
			
		||||
 | 
			
		||||
	if res.GetOpenChannel() != nil {
 | 
			
		||||
 | 
			
		||||
		opm := res.GetOpenChannel()
 | 
			
		||||
		chandler, err := handler.OnOpenChannelRequest(opm.GetChannelType())
 | 
			
		||||
 | 
			
		||||
		if err != nil {
 | 
			
		||||
 | 
			
		||||
			response := rc.messageBuilder.RejectOpenChannel(opm.GetChannelIdentifier(), "UnknownTypeError")
 | 
			
		||||
			rc.SendRicochetPacket(rc.Conn, 0, response)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Check that we have the authentication already
 | 
			
		||||
		if chandler.RequiresAuthentication() != "none" {
 | 
			
		||||
			rc.traceLog(fmt.Sprintf("channel %v requires authorization of type %v", chandler.Type(), chandler.RequiresAuthentication()))
 | 
			
		||||
			// Enforce Authentication Check.
 | 
			
		||||
			_, authed := rc.Authentication[chandler.RequiresAuthentication()]
 | 
			
		||||
			if !authed {
 | 
			
		||||
				rc.SendRicochetPacket(rc.Conn, 0, []byte{})
 | 
			
		||||
				rc.traceLog(fmt.Sprintf("do not have required authorization to open channel type %v", chandler.Type()))
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			rc.traceLog("succeeded authorization check")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		channel, err := rc.channelManager.OpenChannelRequestFromPeer(opm.GetChannelIdentifier(), chandler)
 | 
			
		||||
 | 
			
		||||
		if err == nil {
 | 
			
		||||
 | 
			
		||||
			channel.SendMessage = func(message []byte) {
 | 
			
		||||
				rc.SendRicochetPacket(rc.Conn, channel.ID, message)
 | 
			
		||||
			}
 | 
			
		||||
			channel.DelegateAuthorization = func() {
 | 
			
		||||
				rc.Authentication[chandler.Type()] = true
 | 
			
		||||
			}
 | 
			
		||||
			channel.CloseChannel = func() {
 | 
			
		||||
				rc.SendRicochetPacket(rc.Conn, channel.ID, []byte{})
 | 
			
		||||
				rc.channelManager.RemoveChannel(channel.ID)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			response, err := chandler.OpenInbound(channel, opm)
 | 
			
		||||
			if err == nil && channel.Pending == false {
 | 
			
		||||
				rc.traceLog(fmt.Sprintf("opening channel %v on %v", channel.Type, channel.ID))
 | 
			
		||||
				rc.SendRicochetPacket(rc.Conn, 0, response)
 | 
			
		||||
			} else {
 | 
			
		||||
				rc.traceLog(fmt.Sprintf("removing channel %v", channel.ID))
 | 
			
		||||
				rc.channelManager.RemoveChannel(channel.ID)
 | 
			
		||||
				rc.SendRicochetPacket(rc.Conn, 0, []byte{})
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			// Send Error Packet
 | 
			
		||||
			response := rc.messageBuilder.RejectOpenChannel(opm.GetChannelIdentifier(), "GenericError")
 | 
			
		||||
			rc.traceLog(fmt.Sprintf("sending reject open channel for %v", opm.GetChannelIdentifier()))
 | 
			
		||||
			rc.SendRicochetPacket(rc.Conn, 0, response)
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
	} else if res.GetChannelResult() != nil {
 | 
			
		||||
		cr := res.GetChannelResult()
 | 
			
		||||
		id := cr.GetChannelIdentifier()
 | 
			
		||||
 | 
			
		||||
		channel, found := rc.channelManager.GetChannel(id)
 | 
			
		||||
 | 
			
		||||
		if !found {
 | 
			
		||||
			rc.traceLog(fmt.Sprintf("channel result recived for unknown channel: %v", channel.Type, id))
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if cr.GetOpened() {
 | 
			
		||||
			rc.traceLog(fmt.Sprintf("channel of type %v opened on %v", channel.Type, id))
 | 
			
		||||
			channel.Handler.OpenOutboundResult(nil, cr)
 | 
			
		||||
		} else {
 | 
			
		||||
			rc.traceLog(fmt.Sprintf("channel of type %v rejected on %v", channel.Type, id))
 | 
			
		||||
			channel.Handler.OpenOutboundResult(errors.New(""), cr)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	} else if res.GetKeepAlive() != nil {
 | 
			
		||||
		// XXX Though not currently part of the protocol
 | 
			
		||||
		// We should likely put these calls behind
 | 
			
		||||
		// authentication.
 | 
			
		||||
		rc.traceLog("received keep alive packet")
 | 
			
		||||
		if res.GetKeepAlive().GetResponseRequested() {
 | 
			
		||||
			messageBuilder := new(utils.MessageBuilder)
 | 
			
		||||
			raw := messageBuilder.KeepAlive(true)
 | 
			
		||||
			rc.traceLog("sending keep alive response")
 | 
			
		||||
			rc.SendRicochetPacket(rc.Conn, 0, raw)
 | 
			
		||||
		}
 | 
			
		||||
	} else if res.GetEnableFeatures() != nil {
 | 
			
		||||
		rc.traceLog("received features enabled packet")
 | 
			
		||||
		messageBuilder := new(utils.MessageBuilder)
 | 
			
		||||
		raw := messageBuilder.FeaturesEnabled([]string{})
 | 
			
		||||
		rc.traceLog("sending featured enabled empty response")
 | 
			
		||||
		rc.SendRicochetPacket(rc.Conn, 0, raw)
 | 
			
		||||
	} else if res.GetFeaturesEnabled() != nil {
 | 
			
		||||
		// TODO We should never send out an enabled features
 | 
			
		||||
		// request.
 | 
			
		||||
		rc.traceLog("sending unsolicited features enabled response")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (rc *Connection) traceLog(message string) {
 | 
			
		||||
	if rc.trace {
 | 
			
		||||
		log.Printf(message)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Break causes Process() to return, but does not close the underlying connection
 | 
			
		||||
// Break returns an error if it would not be valid to call Process() again for
 | 
			
		||||
// the connection now. Currently, the only such error is ConnectionClosedError.
 | 
			
		||||
func (rc *Connection) Break() error {
 | 
			
		||||
	// See Do() for an explanation of the concurrency here; it's complicated.
 | 
			
		||||
	// The summary is that this mutex prevents races on connection close that
 | 
			
		||||
	// could lead to deadlocks in Block().
 | 
			
		||||
	rc.processBlockMutex.Lock()
 | 
			
		||||
	defer rc.processBlockMutex.Unlock()
 | 
			
		||||
	if rc.closed {
 | 
			
		||||
		rc.traceLog("ignoring break because connection is already closed")
 | 
			
		||||
		return utils.ConnectionClosedError
 | 
			
		||||
	}
 | 
			
		||||
	rc.traceLog("breaking out of process loop")
 | 
			
		||||
	rc.breakChannel <- true
 | 
			
		||||
	return <-rc.breakResultChannel // Wait for Process to End
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Channel is a convienciance method for returning a given channel to the caller
 | 
			
		||||
// of Process() - TODO - this is kind of ugly.
 | 
			
		||||
func (rc *Connection) Channel(ctype string, way channels.Direction) *channels.Channel {
 | 
			
		||||
	return rc.channelManager.Channel(ctype, way)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,28 @@
 | 
			
		|||
package connection
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/s-rah/go-ricochet/channels"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Handler reacts to low-level events on a protocol connection.
 | 
			
		||||
// There should be a unique instance of a ConnectionHandler type per
 | 
			
		||||
// OpenConnection.
 | 
			
		||||
type Handler interface {
 | 
			
		||||
	// OnReady is called when the connection begins using this handler.
 | 
			
		||||
	OnReady(oc *Connection)
 | 
			
		||||
 | 
			
		||||
	// OnClosed is called when the OpenConnection has closed for any reason.
 | 
			
		||||
	OnClosed(err error)
 | 
			
		||||
 | 
			
		||||
	// OpenChannelRequest is called when the peer asks to open a channel of
 | 
			
		||||
	// `type`. `raw` contains the protocol OpenChannel message including any
 | 
			
		||||
	// extension data. If this channel type is recognized and allowed by this
 | 
			
		||||
	// connection in this state, return a type implementing ChannelHandler for
 | 
			
		||||
	// events related to this channel. Returning an error or nil rejects the
 | 
			
		||||
	// channel.
 | 
			
		||||
	//
 | 
			
		||||
	// Channel type handlers may implement additional state and sanity checks.
 | 
			
		||||
	// A non-nil return from this function does not guarantee that the channel
 | 
			
		||||
	// will be opened.
 | 
			
		||||
	OnOpenChannelRequest(ctype string) (channels.Handler, error)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										89
									
								
								vendor/github.com/s-rah/go-ricochet/connection/inboundconnectionhandler.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							
							
						
						
									
										89
									
								
								vendor/github.com/s-rah/go-ricochet/connection/inboundconnectionhandler.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							| 
						 | 
				
			
			@ -0,0 +1,89 @@
 | 
			
		|||
package connection
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/rsa"
 | 
			
		||||
	"github.com/s-rah/go-ricochet/channels"
 | 
			
		||||
	"github.com/s-rah/go-ricochet/policies"
 | 
			
		||||
	"github.com/s-rah/go-ricochet/utils"
 | 
			
		||||
	"sync"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// InboundConnectionHandler is a convieniance wrapper for handling inbound
 | 
			
		||||
// connections
 | 
			
		||||
type InboundConnectionHandler struct {
 | 
			
		||||
	connection *Connection
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HandleInboundConnection returns an InboundConnectionHandler given a connection
 | 
			
		||||
func HandleInboundConnection(c *Connection) *InboundConnectionHandler {
 | 
			
		||||
	ich := new(InboundConnectionHandler)
 | 
			
		||||
	ich.connection = c
 | 
			
		||||
	return ich
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ProcessAuthAsServer blocks until authentication has succeeded, failed, or the
 | 
			
		||||
// connection is closed. A non-nil error is returned in all cases other than successful
 | 
			
		||||
// and accepted authentication.
 | 
			
		||||
//
 | 
			
		||||
// ProcessAuthAsServer cannot be called at the same time as any other call to a Process
 | 
			
		||||
// function. Another Process function must be called after this function successfully
 | 
			
		||||
// returns to continue handling connection events.
 | 
			
		||||
//
 | 
			
		||||
// The acceptCallback function is called after receiving a valid authentication proof
 | 
			
		||||
// with the client's authenticated hostname and public key. acceptCallback must return
 | 
			
		||||
// true to accept authentication and allow the connection to continue, and also returns a
 | 
			
		||||
// boolean indicating whether the contact is known and recognized. Unknown contacts will
 | 
			
		||||
// assume they are required to send a contact request before any other activity.
 | 
			
		||||
func (ich *InboundConnectionHandler) ProcessAuthAsServer(privateKey *rsa.PrivateKey, sach func(hostname string, publicKey rsa.PublicKey) (allowed, known bool)) error {
 | 
			
		||||
 | 
			
		||||
	if privateKey == nil {
 | 
			
		||||
		return utils.PrivateKeyNotSetError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var breakOnce sync.Once
 | 
			
		||||
 | 
			
		||||
	var authAllowed, authKnown bool
 | 
			
		||||
	var authHostname string
 | 
			
		||||
 | 
			
		||||
	onAuthValid := func(hostname string, publicKey rsa.PublicKey) (allowed, known bool) {
 | 
			
		||||
		authAllowed, authKnown = sach(hostname, publicKey)
 | 
			
		||||
		if authAllowed {
 | 
			
		||||
			authHostname = hostname
 | 
			
		||||
		}
 | 
			
		||||
		breakOnce.Do(func() { go ich.connection.Break() })
 | 
			
		||||
		return authAllowed, authKnown
 | 
			
		||||
	}
 | 
			
		||||
	onAuthInvalid := func(err error) {
 | 
			
		||||
		// err is ignored at the moment
 | 
			
		||||
		breakOnce.Do(func() { go ich.connection.Break() })
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ach := new(AutoConnectionHandler)
 | 
			
		||||
	ach.Init()
 | 
			
		||||
	ach.RegisterChannelHandler("im.ricochet.auth.hidden-service",
 | 
			
		||||
		func() channels.Handler {
 | 
			
		||||
			return &channels.HiddenServiceAuthChannel{
 | 
			
		||||
				PrivateKey:        privateKey,
 | 
			
		||||
				ServerAuthValid:   onAuthValid,
 | 
			
		||||
				ServerAuthInvalid: onAuthInvalid,
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
	// Ensure that the call to Process() cannot outlive this function,
 | 
			
		||||
	// particularly for the case where the policy timeout expires
 | 
			
		||||
	defer breakOnce.Do(func() { ich.connection.Break() })
 | 
			
		||||
	policy := policies.UnknownPurposeTimeout
 | 
			
		||||
	err := policy.ExecuteAction(func() error {
 | 
			
		||||
		return ich.connection.Process(ach)
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		if authAllowed == true {
 | 
			
		||||
			ich.connection.RemoteHostname = authHostname
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		return utils.ClientFailedToAuthenticateError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										90
									
								
								vendor/github.com/s-rah/go-ricochet/connection/outboundconnectionhandler.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							
							
						
						
									
										90
									
								
								vendor/github.com/s-rah/go-ricochet/connection/outboundconnectionhandler.go
								
								
									generated
								
								
									vendored
								
								
									Normal file
								
							| 
						 | 
				
			
			@ -0,0 +1,90 @@
 | 
			
		|||
package connection
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/rsa"
 | 
			
		||||
	"github.com/s-rah/go-ricochet/channels"
 | 
			
		||||
	"github.com/s-rah/go-ricochet/policies"
 | 
			
		||||
	"github.com/s-rah/go-ricochet/utils"
 | 
			
		||||
	"sync"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// OutboundConnectionHandler is a convieniance wrapper for handling outbound
 | 
			
		||||
// connections
 | 
			
		||||
type OutboundConnectionHandler struct {
 | 
			
		||||
	connection *Connection
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HandleOutboundConnection returns an OutboundConnectionHandler given a connection
 | 
			
		||||
func HandleOutboundConnection(c *Connection) *OutboundConnectionHandler {
 | 
			
		||||
	och := new(OutboundConnectionHandler)
 | 
			
		||||
	och.connection = c
 | 
			
		||||
	return och
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ProcessAuthAsClient blocks until authentication has succeeded or failed with the
 | 
			
		||||
// provided privateKey, or the connection is closed. A non-nil error is returned in all
 | 
			
		||||
// cases other than successful authentication.
 | 
			
		||||
//
 | 
			
		||||
// ProcessAuthAsClient cannot be called at the same time as any other call to a Porcess
 | 
			
		||||
// function. Another Process function must be called after this function successfully
 | 
			
		||||
// returns to continue handling connection events.
 | 
			
		||||
//
 | 
			
		||||
// For successful authentication, the `known` return value indicates whether the peer
 | 
			
		||||
// accepts us as a known contact. Unknown contacts will generally need to send a contact
 | 
			
		||||
// request before any other activity.
 | 
			
		||||
func (och *OutboundConnectionHandler) ProcessAuthAsClient(privateKey *rsa.PrivateKey) (bool, error) {
 | 
			
		||||
 | 
			
		||||
	if privateKey == nil {
 | 
			
		||||
		return false, utils.PrivateKeyNotSetError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ach := new(AutoConnectionHandler)
 | 
			
		||||
	ach.Init()
 | 
			
		||||
 | 
			
		||||
	// Make sure that calls to Break in this function cannot race
 | 
			
		||||
	var breakOnce sync.Once
 | 
			
		||||
 | 
			
		||||
	var accepted, isKnownContact bool
 | 
			
		||||
	authCallback := func(accept, known bool) {
 | 
			
		||||
		accepted = accept
 | 
			
		||||
		isKnownContact = known
 | 
			
		||||
		// Cause the Process() call below to return.
 | 
			
		||||
		// If Break() is called from here, it _must_ use go, because this will
 | 
			
		||||
		// execute in the Process goroutine, and Break() will deadlock.
 | 
			
		||||
		breakOnce.Do(func() { go och.connection.Break() })
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	processResult := make(chan error, 1)
 | 
			
		||||
	go func() {
 | 
			
		||||
		// Break Process() if timed out; no-op if Process returned a conn error
 | 
			
		||||
		defer func() { breakOnce.Do(func() { och.connection.Break() }) }()
 | 
			
		||||
		policy := policies.UnknownPurposeTimeout
 | 
			
		||||
		err := policy.ExecuteAction(func() error {
 | 
			
		||||
			return och.connection.Process(ach)
 | 
			
		||||
		})
 | 
			
		||||
		processResult <- err
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	err := och.connection.Do(func() error {
 | 
			
		||||
		_, err := och.connection.RequestOpenChannel("im.ricochet.auth.hidden-service",
 | 
			
		||||
			&channels.HiddenServiceAuthChannel{
 | 
			
		||||
				PrivateKey:       privateKey,
 | 
			
		||||
				ServerHostname:   och.connection.RemoteHostname,
 | 
			
		||||
				ClientAuthResult: authCallback,
 | 
			
		||||
			})
 | 
			
		||||
		return err
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		breakOnce.Do(func() { och.connection.Break() })
 | 
			
		||||
		return false, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err = <-processResult; err != nil {
 | 
			
		||||
		return false, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if accepted == true {
 | 
			
		||||
		return isKnownContact, nil
 | 
			
		||||
	}
 | 
			
		||||
	return false, utils.ServerRejectedClientConnectionError
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -2,49 +2,104 @@ package main
 | 
			
		|||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/s-rah/go-ricochet"
 | 
			
		||||
	"github.com/s-rah/go-ricochet/channels"
 | 
			
		||||
	"github.com/s-rah/go-ricochet/connection"
 | 
			
		||||
	"github.com/s-rah/go-ricochet/utils"
 | 
			
		||||
	"log"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// EchoBotService is an example service which simply echoes back what a client
 | 
			
		||||
// sends it.
 | 
			
		||||
type EchoBotService struct {
 | 
			
		||||
	goricochet.StandardRicochetService
 | 
			
		||||
type RicochetEchoBot struct {
 | 
			
		||||
	connection.AutoConnectionHandler
 | 
			
		||||
	messages chan string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ebs *EchoBotService) OnNewConnection(oc *goricochet.OpenConnection) {
 | 
			
		||||
	ebs.StandardRicochetService.OnNewConnection(oc)
 | 
			
		||||
	go oc.Process(&EchoBotConnection{})
 | 
			
		||||
func (echobot *RicochetEchoBot) ContactRequest(name string, message string) string {
 | 
			
		||||
	return "Pending"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type EchoBotConnection struct {
 | 
			
		||||
	goricochet.StandardRicochetConnection
 | 
			
		||||
func (echobot *RicochetEchoBot) ContactRequestRejected() {
 | 
			
		||||
}
 | 
			
		||||
func (echobot *RicochetEchoBot) ContactRequestAccepted() {
 | 
			
		||||
}
 | 
			
		||||
func (echobot *RicochetEchoBot) ContactRequestError() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsKnownContact is configured to always accept Contact Requests
 | 
			
		||||
func (ebc *EchoBotConnection) IsKnownContact(hostname string) bool {
 | 
			
		||||
func (echobot *RicochetEchoBot) ChatMessage(messageID uint32, when time.Time, message string) bool {
 | 
			
		||||
	echobot.messages <- message
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OnContactRequest - we always accept new contact request.
 | 
			
		||||
func (ebc *EchoBotConnection) OnContactRequest(channelID int32, nick string, message string) {
 | 
			
		||||
	ebc.StandardRicochetConnection.OnContactRequest(channelID, nick, message)
 | 
			
		||||
	ebc.Conn.AckContactRequestOnResponse(channelID, "Accepted")
 | 
			
		||||
	ebc.Conn.CloseChannel(channelID)
 | 
			
		||||
func (echobot *RicochetEchoBot) ChatMessageAck(messageID uint32) {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OnChatMessage we acknowledge the message, grab the message content and send it back - opening
 | 
			
		||||
// a new channel if necessary.
 | 
			
		||||
func (ebc *EchoBotConnection) OnChatMessage(channelID int32, messageID int32, message string) {
 | 
			
		||||
	log.Printf("Received Message from %s: %s", ebc.Conn.OtherHostname, message)
 | 
			
		||||
	ebc.Conn.AckChatMessage(channelID, messageID)
 | 
			
		||||
	if ebc.Conn.GetChannelType(6) == "none" {
 | 
			
		||||
		ebc.Conn.OpenChatChannel(6)
 | 
			
		||||
func (echobot *RicochetEchoBot) Connect(privateKeyFile string, hostname string) {
 | 
			
		||||
 | 
			
		||||
	privateKey, _ := utils.LoadPrivateKeyFromFile(privateKeyFile)
 | 
			
		||||
	echobot.messages = make(chan string)
 | 
			
		||||
 | 
			
		||||
	echobot.Init()
 | 
			
		||||
	echobot.RegisterChannelHandler("im.ricochet.contact.request", func() channels.Handler {
 | 
			
		||||
		contact := new(channels.ContactRequestChannel)
 | 
			
		||||
		contact.Handler = echobot
 | 
			
		||||
		return contact
 | 
			
		||||
	})
 | 
			
		||||
	echobot.RegisterChannelHandler("im.ricochet.chat", func() channels.Handler {
 | 
			
		||||
		chat := new(channels.ChatChannel)
 | 
			
		||||
		chat.Handler = echobot
 | 
			
		||||
		return chat
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	rc, _ := goricochet.Open(hostname)
 | 
			
		||||
	known, err := connection.HandleOutboundConnection(rc).ProcessAuthAsClient(privateKey)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
 | 
			
		||||
		go rc.Process(echobot)
 | 
			
		||||
 | 
			
		||||
		if !known {
 | 
			
		||||
			err := rc.Do(func() error {
 | 
			
		||||
				_, err := rc.RequestOpenChannel("im.ricochet.contact.request",
 | 
			
		||||
					&channels.ContactRequestChannel{
 | 
			
		||||
						Handler: echobot,
 | 
			
		||||
						Name:    "EchoBot",
 | 
			
		||||
						Message: "I LIVE 😈😈!!!!",
 | 
			
		||||
					})
 | 
			
		||||
				return err
 | 
			
		||||
			})
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				log.Printf("could not contact %s", err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		rc.Do(func() error {
 | 
			
		||||
			_, err := rc.RequestOpenChannel("im.ricochet.chat", &channels.ChatChannel{Handler: echobot})
 | 
			
		||||
			return err
 | 
			
		||||
		})
 | 
			
		||||
		for {
 | 
			
		||||
			message := <-echobot.messages
 | 
			
		||||
			log.Printf("Received Message: %s", message)
 | 
			
		||||
			rc.Do(func() error {
 | 
			
		||||
				log.Printf("Finding Chat Channel")
 | 
			
		||||
				channel := rc.Channel("im.ricochet.chat", channels.Outbound)
 | 
			
		||||
				if channel != nil {
 | 
			
		||||
					log.Printf("Found Chat Channel")
 | 
			
		||||
					chatchannel, ok := channel.Handler.(*channels.ChatChannel)
 | 
			
		||||
					if ok {
 | 
			
		||||
						chatchannel.SendMessage(message)
 | 
			
		||||
					}
 | 
			
		||||
				} else {
 | 
			
		||||
					log.Printf("Could not find chat channel")
 | 
			
		||||
				}
 | 
			
		||||
				return nil
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	ebc.Conn.SendMessage(6, message)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	ricochetService := new(EchoBotService)
 | 
			
		||||
	ricochetService.Init("./private_key")
 | 
			
		||||
	ricochetService.Listen(ricochetService, 12345)
 | 
			
		||||
	echoBot := new(RicochetEchoBot)
 | 
			
		||||
	echoBot.Connect("private_key", "oqf7z4ot6kuejgam")
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,51 +0,0 @@
 | 
			
		|||
package goricochet
 | 
			
		||||
 | 
			
		||||
// ServiceHandler is the interface to handle events for an inbound connection listener
 | 
			
		||||
type ServiceHandler interface {
 | 
			
		||||
	// OnNewConnection is called for inbound connections to the service after protocol
 | 
			
		||||
	// version negotiation has completed successfully.
 | 
			
		||||
	OnNewConnection(oc *OpenConnection)
 | 
			
		||||
	// OnFailedConnection is called for inbound connections to the service which fail
 | 
			
		||||
	// to successfully complete version negotiation for any reason.
 | 
			
		||||
	OnFailedConnection(err error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ConnectionHandler is the interface to handle events for an open protocol connection,
 | 
			
		||||
// whether inbound or outbound. Each OpenConnection will need its own instance of an
 | 
			
		||||
// application type implementing ConnectionHandler, which could also be used to store
 | 
			
		||||
// application state related to the connection.
 | 
			
		||||
type ConnectionHandler interface {
 | 
			
		||||
	// OnReady is called before OpenConnection.Process() begins from the connection
 | 
			
		||||
	OnReady(oc *OpenConnection)
 | 
			
		||||
	// OnDisconnect is called when the connection is closed, just before
 | 
			
		||||
	// OpenConnection.Process() returns
 | 
			
		||||
	OnDisconnect()
 | 
			
		||||
 | 
			
		||||
	// Authentication Management
 | 
			
		||||
	OnAuthenticationRequest(channelID int32, clientCookie [16]byte)
 | 
			
		||||
	OnAuthenticationChallenge(channelID int32, serverCookie [16]byte)
 | 
			
		||||
	OnAuthenticationProof(channelID int32, publicKey []byte, signature []byte)
 | 
			
		||||
	OnAuthenticationResult(channelID int32, result bool, isKnownContact bool)
 | 
			
		||||
 | 
			
		||||
	// Contact Management
 | 
			
		||||
	IsKnownContact(hostname string) bool
 | 
			
		||||
	OnContactRequest(channelID int32, nick string, message string)
 | 
			
		||||
	OnContactRequestAck(channelID int32, status string)
 | 
			
		||||
 | 
			
		||||
	// Managing Channels
 | 
			
		||||
	OnOpenChannelRequest(channelID int32, channelType string)
 | 
			
		||||
	OnOpenChannelRequestSuccess(channelID int32)
 | 
			
		||||
	OnChannelClosed(channelID int32)
 | 
			
		||||
 | 
			
		||||
	// Chat Messages
 | 
			
		||||
	OnChatMessage(channelID int32, messageID int32, message string)
 | 
			
		||||
	OnChatMessageAck(channelID int32, messageID int32)
 | 
			
		||||
 | 
			
		||||
	// Handle Errors
 | 
			
		||||
	OnFailedChannelOpen(channelID int32, errorType string)
 | 
			
		||||
	OnGenericError(channelID int32)
 | 
			
		||||
	OnUnknownTypeError(channelID int32)
 | 
			
		||||
	OnUnauthorizedError(channelID int32)
 | 
			
		||||
	OnBadUsageError(channelID int32)
 | 
			
		||||
	OnFailedError(channelID int32)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,534 +0,0 @@
 | 
			
		|||
package goricochet
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto"
 | 
			
		||||
	"crypto/rsa"
 | 
			
		||||
	"encoding/asn1"
 | 
			
		||||
	"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/contact"
 | 
			
		||||
	"github.com/s-rah/go-ricochet/control"
 | 
			
		||||
	"github.com/s-rah/go-ricochet/utils"
 | 
			
		||||
	"log"
 | 
			
		||||
	"net"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// OpenConnection encapsulates the state required to maintain a connection to
 | 
			
		||||
// a ricochet service.
 | 
			
		||||
// Notably OpenConnection does not enforce limits on the channelIDs, channel Assignments
 | 
			
		||||
// or the direction of messages. These are considered to be service enforced rules.
 | 
			
		||||
// (and services are considered to be the best to define them).
 | 
			
		||||
type OpenConnection struct {
 | 
			
		||||
	conn        net.Conn
 | 
			
		||||
	authHandler map[int32]*AuthenticationHandler
 | 
			
		||||
	channels    map[int32]string
 | 
			
		||||
	rni         utils.RicochetNetworkInterface
 | 
			
		||||
 | 
			
		||||
	Client        bool
 | 
			
		||||
	IsAuthed      bool
 | 
			
		||||
	MyHostname    string
 | 
			
		||||
	OtherHostname string
 | 
			
		||||
	Closed        bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Init initializes a OpenConnection object to a default state.
 | 
			
		||||
func (oc *OpenConnection) Init(outbound bool, conn net.Conn) {
 | 
			
		||||
	oc.conn = conn
 | 
			
		||||
	oc.authHandler = make(map[int32]*AuthenticationHandler)
 | 
			
		||||
	oc.channels = make(map[int32]string)
 | 
			
		||||
	oc.rni = new(utils.RicochetNetwork)
 | 
			
		||||
 | 
			
		||||
	oc.Client = outbound
 | 
			
		||||
	oc.IsAuthed = false
 | 
			
		||||
	oc.MyHostname = ""
 | 
			
		||||
	oc.OtherHostname = ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UnsetChannel removes a type association from the channel.
 | 
			
		||||
func (oc *OpenConnection) UnsetChannel(channel int32) {
 | 
			
		||||
	oc.channels[channel] = "none"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetChannelType returns the type of the channel on this connection
 | 
			
		||||
func (oc *OpenConnection) GetChannelType(channel int32) string {
 | 
			
		||||
	if val, ok := oc.channels[channel]; ok {
 | 
			
		||||
		return val
 | 
			
		||||
	}
 | 
			
		||||
	return "none"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (oc *OpenConnection) setChannel(channel int32, channelType string) {
 | 
			
		||||
	oc.channels[channel] = channelType
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HasChannel returns true if the connection has a channel of an associated type, false otherwise
 | 
			
		||||
func (oc *OpenConnection) HasChannel(channelType string) bool {
 | 
			
		||||
	for _, val := range oc.channels {
 | 
			
		||||
		if val == channelType {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CloseChannel closes a given channel
 | 
			
		||||
// Prerequisites:
 | 
			
		||||
//              * Must have previously connected to a service
 | 
			
		||||
func (oc *OpenConnection) CloseChannel(channel int32) {
 | 
			
		||||
	oc.UnsetChannel(channel)
 | 
			
		||||
	oc.rni.SendRicochetPacket(oc.conn, channel, []byte{})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close closes the entire connection
 | 
			
		||||
func (oc *OpenConnection) Close() {
 | 
			
		||||
	oc.conn.Close()
 | 
			
		||||
	oc.Closed = true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Authenticate opens an Authentication Channel and send a client cookie
 | 
			
		||||
// Prerequisites:
 | 
			
		||||
//              * Must have previously connected to a service
 | 
			
		||||
func (oc *OpenConnection) Authenticate(channel int32) {
 | 
			
		||||
	defer utils.RecoverFromError()
 | 
			
		||||
 | 
			
		||||
	oc.authHandler[channel] = new(AuthenticationHandler)
 | 
			
		||||
	messageBuilder := new(MessageBuilder)
 | 
			
		||||
	data, err := messageBuilder.OpenAuthenticationChannel(channel, oc.authHandler[channel].GenClientCookie())
 | 
			
		||||
	utils.CheckError(err)
 | 
			
		||||
 | 
			
		||||
	oc.setChannel(channel, "im.ricochet.auth.hidden-service")
 | 
			
		||||
	oc.rni.SendRicochetPacket(oc.conn, 0, data)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ConfirmAuthChannel responds to a new authentication request.
 | 
			
		||||
// Prerequisites:
 | 
			
		||||
//              * Must have previously connected to a service
 | 
			
		||||
func (oc *OpenConnection) ConfirmAuthChannel(channel int32, clientCookie [16]byte) {
 | 
			
		||||
	defer utils.RecoverFromError()
 | 
			
		||||
 | 
			
		||||
	oc.authHandler[channel] = new(AuthenticationHandler)
 | 
			
		||||
	oc.authHandler[channel].AddClientCookie(clientCookie[:])
 | 
			
		||||
	messageBuilder := new(MessageBuilder)
 | 
			
		||||
	data, err := messageBuilder.ConfirmAuthChannel(channel, oc.authHandler[channel].GenServerCookie())
 | 
			
		||||
	utils.CheckError(err)
 | 
			
		||||
 | 
			
		||||
	oc.setChannel(channel, "im.ricochet.auth.hidden-service")
 | 
			
		||||
	oc.rni.SendRicochetPacket(oc.conn, 0, data)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SendProof sends an authentication proof in response to a challenge.
 | 
			
		||||
// Prerequisites:
 | 
			
		||||
//              * Must have previously connected to a service
 | 
			
		||||
//              * channel must be of type auth
 | 
			
		||||
func (oc *OpenConnection) SendProof(channel int32, serverCookie [16]byte, publicKeyBytes []byte, privateKey *rsa.PrivateKey) {
 | 
			
		||||
 | 
			
		||||
	if oc.authHandler[channel] == nil {
 | 
			
		||||
		return // NoOp
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	oc.authHandler[channel].AddServerCookie(serverCookie[:])
 | 
			
		||||
 | 
			
		||||
	challenge := oc.authHandler[channel].GenChallenge(oc.MyHostname, oc.OtherHostname)
 | 
			
		||||
	signature, _ := rsa.SignPKCS1v15(nil, privateKey, crypto.SHA256, challenge)
 | 
			
		||||
 | 
			
		||||
	defer utils.RecoverFromError()
 | 
			
		||||
	messageBuilder := new(MessageBuilder)
 | 
			
		||||
	data, err := messageBuilder.Proof(publicKeyBytes, signature)
 | 
			
		||||
	utils.CheckError(err)
 | 
			
		||||
 | 
			
		||||
	oc.rni.SendRicochetPacket(oc.conn, channel, data)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ValidateProof determines if the given public key and signature align with the
 | 
			
		||||
// already established challenge vector for this communication
 | 
			
		||||
// Prerequisites:
 | 
			
		||||
//              * Must have previously connected to a service
 | 
			
		||||
//              * Client and Server must have already sent their respective cookies (Authenticate and ConfirmAuthChannel)
 | 
			
		||||
func (oc *OpenConnection) ValidateProof(channel int32, publicKeyBytes []byte, signature []byte) bool {
 | 
			
		||||
 | 
			
		||||
	if oc.authHandler[channel] == nil {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	provisionalHostname := utils.GetTorHostname(publicKeyBytes)
 | 
			
		||||
	publicKey := new(rsa.PublicKey)
 | 
			
		||||
	_, err := asn1.Unmarshal(publicKeyBytes, publicKey)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	challenge := oc.authHandler[channel].GenChallenge(provisionalHostname, oc.MyHostname)
 | 
			
		||||
	err = rsa.VerifyPKCS1v15(publicKey, crypto.SHA256, challenge[:], signature)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		oc.OtherHostname = provisionalHostname
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SendAuthenticationResult responds to an existed authentication Proof
 | 
			
		||||
// Prerequisites:
 | 
			
		||||
//              * Must have previously connected to a service
 | 
			
		||||
//              * channel must be of type auth
 | 
			
		||||
func (oc *OpenConnection) SendAuthenticationResult(channel int32, accepted bool, isKnownContact bool) {
 | 
			
		||||
	defer utils.RecoverFromError()
 | 
			
		||||
	messageBuilder := new(MessageBuilder)
 | 
			
		||||
	data, err := messageBuilder.AuthResult(accepted, isKnownContact)
 | 
			
		||||
	utils.CheckError(err)
 | 
			
		||||
	oc.rni.SendRicochetPacket(oc.conn, channel, data)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OpenChatChannel opens a new chat channel with the given id
 | 
			
		||||
// Prerequisites:
 | 
			
		||||
//              * Must have previously connected to a service
 | 
			
		||||
//              * If acting as the client, id must be odd, else even
 | 
			
		||||
func (oc *OpenConnection) OpenChatChannel(channel int32) {
 | 
			
		||||
	defer utils.RecoverFromError()
 | 
			
		||||
	messageBuilder := new(MessageBuilder)
 | 
			
		||||
	data, err := messageBuilder.OpenChannel(channel, "im.ricochet.chat")
 | 
			
		||||
	utils.CheckError(err)
 | 
			
		||||
 | 
			
		||||
	oc.setChannel(channel, "im.ricochet.chat")
 | 
			
		||||
	oc.rni.SendRicochetPacket(oc.conn, 0, data)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OpenChannel opens a new chat channel with the given id
 | 
			
		||||
// Prerequisites:
 | 
			
		||||
//              * Must have previously connected to a service
 | 
			
		||||
//              * If acting as the client, id must be odd, else even
 | 
			
		||||
func (oc *OpenConnection) OpenChannel(channel int32, channelType string) {
 | 
			
		||||
	defer utils.RecoverFromError()
 | 
			
		||||
	messageBuilder := new(MessageBuilder)
 | 
			
		||||
	data, err := messageBuilder.OpenChannel(channel, channelType)
 | 
			
		||||
	utils.CheckError(err)
 | 
			
		||||
 | 
			
		||||
	oc.setChannel(channel, channelType)
 | 
			
		||||
	oc.rni.SendRicochetPacket(oc.conn, 0, data)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AckOpenChannel acknowledges a previously received open channel message
 | 
			
		||||
// Prerequisites:
 | 
			
		||||
//             * Must have previously connected and authenticated to a service
 | 
			
		||||
func (oc *OpenConnection) AckOpenChannel(channel int32, channeltype string) {
 | 
			
		||||
	defer utils.RecoverFromError()
 | 
			
		||||
	messageBuilder := new(MessageBuilder)
 | 
			
		||||
 | 
			
		||||
	data, err := messageBuilder.AckOpenChannel(channel)
 | 
			
		||||
	utils.CheckError(err)
 | 
			
		||||
 | 
			
		||||
	oc.setChannel(channel, channeltype)
 | 
			
		||||
	oc.rni.SendRicochetPacket(oc.conn, 0, data)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RejectOpenChannel acknowledges a rejects a previously received open channel message
 | 
			
		||||
// Prerequisites:
 | 
			
		||||
//             * Must have previously connected
 | 
			
		||||
func (oc *OpenConnection) RejectOpenChannel(channel int32, errortype string) {
 | 
			
		||||
	defer utils.RecoverFromError()
 | 
			
		||||
	messageBuilder := new(MessageBuilder)
 | 
			
		||||
	data, err := messageBuilder.RejectOpenChannel(channel, errortype)
 | 
			
		||||
	utils.CheckError(err)
 | 
			
		||||
 | 
			
		||||
	oc.rni.SendRicochetPacket(oc.conn, 0, data)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SendContactRequest initiates a contact request to the server.
 | 
			
		||||
// Prerequisites:
 | 
			
		||||
//             * Must have previously connected and authenticated to a service
 | 
			
		||||
func (oc *OpenConnection) SendContactRequest(channel int32, nick string, message string) {
 | 
			
		||||
	defer utils.RecoverFromError()
 | 
			
		||||
 | 
			
		||||
	messageBuilder := new(MessageBuilder)
 | 
			
		||||
	data, err := messageBuilder.OpenContactRequestChannel(channel, nick, message)
 | 
			
		||||
	utils.CheckError(err)
 | 
			
		||||
 | 
			
		||||
	oc.setChannel(channel, "im.ricochet.contact.request")
 | 
			
		||||
	oc.rni.SendRicochetPacket(oc.conn, 0, data)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AckContactRequestOnResponse responds a contact request from a client
 | 
			
		||||
// Prerequisites:
 | 
			
		||||
//             * Must have previously connected and authenticated to a service
 | 
			
		||||
//             * Must have previously received a Contact Request
 | 
			
		||||
func (oc *OpenConnection) AckContactRequestOnResponse(channel int32, status string) {
 | 
			
		||||
	defer utils.RecoverFromError()
 | 
			
		||||
 | 
			
		||||
	messageBuilder := new(MessageBuilder)
 | 
			
		||||
	data, err := messageBuilder.ReplyToContactRequestOnResponse(channel, status)
 | 
			
		||||
	utils.CheckError(err)
 | 
			
		||||
 | 
			
		||||
	oc.setChannel(channel, "im.ricochet.contact.request")
 | 
			
		||||
	oc.rni.SendRicochetPacket(oc.conn, 0, data)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AckContactRequest responds to contact request from a client
 | 
			
		||||
// Prerequisites:
 | 
			
		||||
//             * Must have previously connected and authenticated to a service
 | 
			
		||||
//             * Must have previously received a Contact Request
 | 
			
		||||
func (oc *OpenConnection) AckContactRequest(channel int32, status string) {
 | 
			
		||||
	defer utils.RecoverFromError()
 | 
			
		||||
 | 
			
		||||
	messageBuilder := new(MessageBuilder)
 | 
			
		||||
	data, err := messageBuilder.ReplyToContactRequest(channel, status)
 | 
			
		||||
	utils.CheckError(err)
 | 
			
		||||
 | 
			
		||||
	oc.setChannel(channel, "im.ricochet.contact.request")
 | 
			
		||||
	oc.rni.SendRicochetPacket(oc.conn, channel, data)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AckChatMessage acknowledges a previously received chat message.
 | 
			
		||||
// Prerequisites:
 | 
			
		||||
//             * Must have previously connected and authenticated to a service
 | 
			
		||||
//             * Must have established a known contact status with the other service
 | 
			
		||||
//             * Must have received a Chat message on an open im.ricochet.chat channel with the messageID
 | 
			
		||||
func (oc *OpenConnection) AckChatMessage(channel int32, messageID int32) {
 | 
			
		||||
	defer utils.RecoverFromError()
 | 
			
		||||
 | 
			
		||||
	messageBuilder := new(MessageBuilder)
 | 
			
		||||
	data, err := messageBuilder.AckChatMessage(messageID)
 | 
			
		||||
	utils.CheckError(err)
 | 
			
		||||
 | 
			
		||||
	oc.rni.SendRicochetPacket(oc.conn, channel, data)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SendMessage sends a Chat Message (message) to a give Channel (channel).
 | 
			
		||||
// Prerequisites:
 | 
			
		||||
//             * Must have previously connected and authenticated to a service
 | 
			
		||||
//             * Must have established a known contact status with the other service
 | 
			
		||||
//             * Must have previously opened channel with OpenChanel of type im.ricochet.chat
 | 
			
		||||
func (oc *OpenConnection) SendMessage(channel int32, message string) {
 | 
			
		||||
	defer utils.RecoverFromError()
 | 
			
		||||
	messageBuilder := new(MessageBuilder)
 | 
			
		||||
	data, err := messageBuilder.ChatMessage(message, 0)
 | 
			
		||||
	utils.CheckError(err)
 | 
			
		||||
	oc.rni.SendRicochetPacket(oc.conn, channel, data)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Process waits for new messages to arrive from the connection and uses the given
 | 
			
		||||
// ConnectionHandler to process them.
 | 
			
		||||
func (oc *OpenConnection) Process(handler ConnectionHandler) {
 | 
			
		||||
	handler.OnReady(oc)
 | 
			
		||||
	defer oc.Close()
 | 
			
		||||
	defer handler.OnDisconnect()
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		if oc.Closed {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		packet, err := oc.rni.RecvRicochetPacket(oc.conn)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			oc.Close()
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(packet.Data) == 0 {
 | 
			
		||||
			handler.OnChannelClosed(packet.Channel)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if packet.Channel == 0 {
 | 
			
		||||
 | 
			
		||||
			res := new(Protocol_Data_Control.Packet)
 | 
			
		||||
			err := proto.Unmarshal(packet.Data[:], res)
 | 
			
		||||
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				handler.OnGenericError(packet.Channel)
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if res.GetOpenChannel() != nil {
 | 
			
		||||
				opm := res.GetOpenChannel()
 | 
			
		||||
 | 
			
		||||
				if oc.GetChannelType(opm.GetChannelIdentifier()) != "none" {
 | 
			
		||||
					// Channel is already in use.
 | 
			
		||||
					handler.OnBadUsageError(opm.GetChannelIdentifier())
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				// If I am a Client, the server can only open even numbered channels
 | 
			
		||||
				if oc.Client && opm.GetChannelIdentifier()%2 != 0 {
 | 
			
		||||
					handler.OnBadUsageError(opm.GetChannelIdentifier())
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				// If I am a Server, the client can only open odd numbered channels
 | 
			
		||||
				if !oc.Client && opm.GetChannelIdentifier()%2 != 1 {
 | 
			
		||||
					handler.OnBadUsageError(opm.GetChannelIdentifier())
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				switch opm.GetChannelType() {
 | 
			
		||||
				case "im.ricochet.auth.hidden-service":
 | 
			
		||||
					if oc.Client {
 | 
			
		||||
						// Servers are authed by default and can't auth with hidden-service
 | 
			
		||||
						handler.OnBadUsageError(opm.GetChannelIdentifier())
 | 
			
		||||
					} else if oc.IsAuthed {
 | 
			
		||||
						// Can't auth if already authed
 | 
			
		||||
						handler.OnBadUsageError(opm.GetChannelIdentifier())
 | 
			
		||||
					} else if oc.HasChannel("im.ricochet.auth.hidden-service") {
 | 
			
		||||
						// Can't open more than 1 auth channel
 | 
			
		||||
						handler.OnBadUsageError(opm.GetChannelIdentifier())
 | 
			
		||||
					} else {
 | 
			
		||||
						clientCookie, err := proto.GetExtension(opm, Protocol_Data_AuthHiddenService.E_ClientCookie)
 | 
			
		||||
						if err == nil {
 | 
			
		||||
							clientCookieB := [16]byte{}
 | 
			
		||||
							copy(clientCookieB[:], clientCookie.([]byte)[:])
 | 
			
		||||
							handler.OnAuthenticationRequest(opm.GetChannelIdentifier(), clientCookieB)
 | 
			
		||||
						} else {
 | 
			
		||||
							// Must include Client Cookie
 | 
			
		||||
							handler.OnBadUsageError(opm.GetChannelIdentifier())
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				case "im.ricochet.chat":
 | 
			
		||||
					if !oc.IsAuthed {
 | 
			
		||||
						// Can't open chat channel if not authorized
 | 
			
		||||
						handler.OnUnauthorizedError(opm.GetChannelIdentifier())
 | 
			
		||||
					} else if !handler.IsKnownContact(oc.OtherHostname) {
 | 
			
		||||
						// Can't open chat channel if not a known contact
 | 
			
		||||
						handler.OnUnauthorizedError(opm.GetChannelIdentifier())
 | 
			
		||||
					} else {
 | 
			
		||||
						handler.OnOpenChannelRequest(opm.GetChannelIdentifier(), "im.ricochet.chat")
 | 
			
		||||
					}
 | 
			
		||||
				case "im.ricochet.contact.request":
 | 
			
		||||
					if oc.Client {
 | 
			
		||||
						// Servers are not allowed to send contact requests
 | 
			
		||||
						handler.OnBadUsageError(opm.GetChannelIdentifier())
 | 
			
		||||
					} else if !oc.IsAuthed {
 | 
			
		||||
						// Can't open a contact channel if not authed
 | 
			
		||||
						handler.OnUnauthorizedError(opm.GetChannelIdentifier())
 | 
			
		||||
					} else if oc.HasChannel("im.ricochet.contact.request") {
 | 
			
		||||
						// Only 1 contact channel is allowed to be open at a time
 | 
			
		||||
						handler.OnBadUsageError(opm.GetChannelIdentifier())
 | 
			
		||||
					} else {
 | 
			
		||||
						contactRequestI, err := proto.GetExtension(opm, Protocol_Data_ContactRequest.E_ContactRequest)
 | 
			
		||||
						if err == nil {
 | 
			
		||||
							contactRequest, check := contactRequestI.(*Protocol_Data_ContactRequest.ContactRequest)
 | 
			
		||||
							if check {
 | 
			
		||||
								handler.OnContactRequest(opm.GetChannelIdentifier(), contactRequest.GetNickname(), contactRequest.GetMessageText())
 | 
			
		||||
								break
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
						handler.OnBadUsageError(opm.GetChannelIdentifier())
 | 
			
		||||
					}
 | 
			
		||||
				default:
 | 
			
		||||
					handler.OnUnknownTypeError(opm.GetChannelIdentifier())
 | 
			
		||||
				}
 | 
			
		||||
			} else if res.GetChannelResult() != nil {
 | 
			
		||||
				crm := res.GetChannelResult()
 | 
			
		||||
				if crm.GetOpened() {
 | 
			
		||||
					switch oc.GetChannelType(crm.GetChannelIdentifier()) {
 | 
			
		||||
					case "im.ricochet.auth.hidden-service":
 | 
			
		||||
						serverCookie, err := proto.GetExtension(crm, Protocol_Data_AuthHiddenService.E_ServerCookie)
 | 
			
		||||
						if err == nil {
 | 
			
		||||
							serverCookieB := [16]byte{}
 | 
			
		||||
							copy(serverCookieB[:], serverCookie.([]byte)[:])
 | 
			
		||||
							handler.OnAuthenticationChallenge(crm.GetChannelIdentifier(), serverCookieB)
 | 
			
		||||
						} else {
 | 
			
		||||
							handler.OnBadUsageError(crm.GetChannelIdentifier())
 | 
			
		||||
						}
 | 
			
		||||
					case "im.ricochet.chat":
 | 
			
		||||
						handler.OnOpenChannelRequestSuccess(crm.GetChannelIdentifier())
 | 
			
		||||
					case "im.ricochet.contact.request":
 | 
			
		||||
						responseI, err := proto.GetExtension(res.GetChannelResult(), Protocol_Data_ContactRequest.E_Response)
 | 
			
		||||
						if err == nil {
 | 
			
		||||
							response, check := responseI.(*Protocol_Data_ContactRequest.Response)
 | 
			
		||||
							if check {
 | 
			
		||||
								handler.OnContactRequestAck(crm.GetChannelIdentifier(), response.GetStatus().String())
 | 
			
		||||
								break
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
						handler.OnBadUsageError(crm.GetChannelIdentifier())
 | 
			
		||||
					default:
 | 
			
		||||
						handler.OnBadUsageError(crm.GetChannelIdentifier())
 | 
			
		||||
					}
 | 
			
		||||
				} else {
 | 
			
		||||
					if oc.GetChannelType(crm.GetChannelIdentifier()) != "none" {
 | 
			
		||||
						handler.OnFailedChannelOpen(crm.GetChannelIdentifier(), crm.GetCommonError().String())
 | 
			
		||||
					} else {
 | 
			
		||||
						oc.CloseChannel(crm.GetChannelIdentifier())
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				// Unknown Message
 | 
			
		||||
				oc.CloseChannel(packet.Channel)
 | 
			
		||||
			}
 | 
			
		||||
		} else if oc.GetChannelType(packet.Channel) == "im.ricochet.auth.hidden-service" {
 | 
			
		||||
			res := new(Protocol_Data_AuthHiddenService.Packet)
 | 
			
		||||
			err := proto.Unmarshal(packet.Data[:], res)
 | 
			
		||||
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				oc.CloseChannel(packet.Channel)
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if res.GetProof() != nil && !oc.Client { // Only Clients Send Proofs
 | 
			
		||||
				handler.OnAuthenticationProof(packet.Channel, res.GetProof().GetPublicKey(), res.GetProof().GetSignature())
 | 
			
		||||
			} else if res.GetResult() != nil && oc.Client { // Only Servers Send Results
 | 
			
		||||
				handler.OnAuthenticationResult(packet.Channel, res.GetResult().GetAccepted(), res.GetResult().GetIsKnownContact())
 | 
			
		||||
			} else {
 | 
			
		||||
				// If neither of the above are satisfied we just close the connection
 | 
			
		||||
				oc.Close()
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		} else if oc.GetChannelType(packet.Channel) == "im.ricochet.chat" {
 | 
			
		||||
 | 
			
		||||
			// NOTE: These auth checks should be redundant, however they
 | 
			
		||||
			// are included here for defense-in-depth if for some reason
 | 
			
		||||
			// a previously authed connection becomes untrusted / not known and
 | 
			
		||||
			// the state is not cleaned up.
 | 
			
		||||
			if !oc.IsAuthed {
 | 
			
		||||
				// Can't send chat messages if not authorized
 | 
			
		||||
				handler.OnUnauthorizedError(packet.Channel)
 | 
			
		||||
			} else if !handler.IsKnownContact(oc.OtherHostname) {
 | 
			
		||||
				// Can't send chat message if not a known contact
 | 
			
		||||
				handler.OnUnauthorizedError(packet.Channel)
 | 
			
		||||
			} else {
 | 
			
		||||
				res := new(Protocol_Data_Chat.Packet)
 | 
			
		||||
				err := proto.Unmarshal(packet.Data[:], res)
 | 
			
		||||
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					oc.CloseChannel(packet.Channel)
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if res.GetChatMessage() != nil {
 | 
			
		||||
					handler.OnChatMessage(packet.Channel, int32(res.GetChatMessage().GetMessageId()), res.GetChatMessage().GetMessageText())
 | 
			
		||||
				} else if res.GetChatAcknowledge() != nil {
 | 
			
		||||
					handler.OnChatMessageAck(packet.Channel, int32(res.GetChatMessage().GetMessageId()))
 | 
			
		||||
				} else {
 | 
			
		||||
					// If neither of the above are satisfied we just close the connection
 | 
			
		||||
					oc.Close()
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		} else if oc.GetChannelType(packet.Channel) == "im.ricochet.contact.request" {
 | 
			
		||||
 | 
			
		||||
			// NOTE: These auth checks should be redundant, however they
 | 
			
		||||
			// are included here for defense-in-depth if for some reason
 | 
			
		||||
			// a previously authed connection becomes untrusted / not known and
 | 
			
		||||
			// the state is not cleaned up.
 | 
			
		||||
			if !oc.Client {
 | 
			
		||||
				// Clients are not allowed to send contact request responses
 | 
			
		||||
				handler.OnBadUsageError(packet.Channel)
 | 
			
		||||
			} else if !oc.IsAuthed {
 | 
			
		||||
				// Can't send a contact request if not authed
 | 
			
		||||
				handler.OnBadUsageError(packet.Channel)
 | 
			
		||||
			} else {
 | 
			
		||||
				res := new(Protocol_Data_ContactRequest.Response)
 | 
			
		||||
				err := proto.Unmarshal(packet.Data[:], res)
 | 
			
		||||
				log.Printf("%v", res)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					oc.CloseChannel(packet.Channel)
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				handler.OnContactRequestAck(packet.Channel, res.GetStatus().String())
 | 
			
		||||
			}
 | 
			
		||||
		} else if oc.GetChannelType(packet.Channel) == "none" {
 | 
			
		||||
			// Invalid Channel Assignment
 | 
			
		||||
			oc.CloseChannel(packet.Channel)
 | 
			
		||||
		} else {
 | 
			
		||||
			oc.Close()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,32 @@
 | 
			
		|||
package policies
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/s-rah/go-ricochet/utils"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// TimeoutPolicy is a convieance interface for enforcing common timeout patterns
 | 
			
		||||
type TimeoutPolicy time.Duration
 | 
			
		||||
 | 
			
		||||
// Selection of common timeout policies
 | 
			
		||||
const (
 | 
			
		||||
	UnknownPurposeTimeout TimeoutPolicy = TimeoutPolicy(15 * time.Second)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ExecuteAction runs a function and returns an error if it hasn't returned
 | 
			
		||||
// by the time specified by TimeoutPolicy
 | 
			
		||||
func (tp *TimeoutPolicy) ExecuteAction(action func() error) error {
 | 
			
		||||
 | 
			
		||||
	c := make(chan error)
 | 
			
		||||
	go func() {
 | 
			
		||||
		c <- action()
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	tick := time.Tick(time.Duration(*tp))
 | 
			
		||||
	select {
 | 
			
		||||
	case <-tick:
 | 
			
		||||
		return utils.ActionTimedOutError
 | 
			
		||||
	case err := <-c:
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,158 +1,90 @@
 | 
			
		|||
package goricochet
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"github.com/s-rah/go-ricochet/connection"
 | 
			
		||||
	"github.com/s-rah/go-ricochet/utils"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net"
 | 
			
		||||
	"sync"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Connect sets up a client ricochet connection to host e.g. qn6uo4cmsrfv4kzq.onion. If this
 | 
			
		||||
// function finished successfully then the connection can be assumed to
 | 
			
		||||
// be open and authenticated.
 | 
			
		||||
// To specify a local port using the format "127.0.0.1:[port]|ricochet-id".
 | 
			
		||||
func Connect(host string) (*OpenConnection, error) {
 | 
			
		||||
	networkResolver := utils.NetworkResolver{}
 | 
			
		||||
	conn, host, err := networkResolver.Resolve(host)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return Open(conn, host)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Open establishes a protocol session on an established net.Conn, and returns a new
 | 
			
		||||
// OpenConnection instance representing this connection. On error, the connection
 | 
			
		||||
// will be closed. This function blocks until version negotiation has completed.
 | 
			
		||||
// The application should call Process() on the returned OpenConnection to continue
 | 
			
		||||
// handling protocol messages.
 | 
			
		||||
func Open(conn net.Conn, remoteHostname string) (*OpenConnection, error) {
 | 
			
		||||
	oc, err := negotiateVersion(conn, true)
 | 
			
		||||
func Open(remoteHostname string) (*connection.Connection, error) {
 | 
			
		||||
	networkResolver := utils.NetworkResolver{}
 | 
			
		||||
	conn, remoteHostname, err := networkResolver.Resolve(remoteHostname)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rc, err := NegotiateVersionOutbound(conn, remoteHostname)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		conn.Close()
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	oc.OtherHostname = remoteHostname
 | 
			
		||||
	return oc, nil
 | 
			
		||||
	return rc, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Serve accepts incoming connections on a net.Listener, negotiates protocol,
 | 
			
		||||
// and calls methods of the ServiceHandler to handle inbound connections. All
 | 
			
		||||
// calls to ServiceHandler happen on the caller's goroutine. The listener can
 | 
			
		||||
// be closed at any time to close the service.
 | 
			
		||||
func Serve(ln net.Listener, handler ServiceHandler) error {
 | 
			
		||||
	defer ln.Close()
 | 
			
		||||
 | 
			
		||||
	connChannel := make(chan interface{})
 | 
			
		||||
	listenErrorChannel := make(chan error)
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		var pending sync.WaitGroup
 | 
			
		||||
		for {
 | 
			
		||||
			conn, err := ln.Accept()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				// Wait for pending connections before returning an error; this
 | 
			
		||||
				// prevents abandoned goroutines when the outer loop stops reading
 | 
			
		||||
				// from connChannel.
 | 
			
		||||
				pending.Wait()
 | 
			
		||||
				listenErrorChannel <- err
 | 
			
		||||
				close(connChannel)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			pending.Add(1)
 | 
			
		||||
			go func() {
 | 
			
		||||
				defer pending.Done()
 | 
			
		||||
				oc, err := negotiateVersion(conn, false)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					conn.Close()
 | 
			
		||||
					connChannel <- err
 | 
			
		||||
				} else {
 | 
			
		||||
					connChannel <- oc
 | 
			
		||||
				}
 | 
			
		||||
			}()
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	var listenErr error
 | 
			
		||||
	for {
 | 
			
		||||
		select {
 | 
			
		||||
		case err := <-listenErrorChannel:
 | 
			
		||||
			// Remember error, wait for connChannel to close
 | 
			
		||||
			listenErr = err
 | 
			
		||||
 | 
			
		||||
		case result, ok := <-connChannel:
 | 
			
		||||
			if !ok {
 | 
			
		||||
				return listenErr
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			switch v := result.(type) {
 | 
			
		||||
			case *OpenConnection:
 | 
			
		||||
				handler.OnNewConnection(v)
 | 
			
		||||
			case error:
 | 
			
		||||
				handler.OnFailedConnection(v)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Perform version negotiation on the connection, and create an OpenConnection if successful
 | 
			
		||||
func negotiateVersion(conn net.Conn, outbound bool) (*OpenConnection, error) {
 | 
			
		||||
// negotiate version takes an open network connection and executes
 | 
			
		||||
// the ricochet version negotiation procedure.
 | 
			
		||||
func NegotiateVersionOutbound(conn net.Conn, remoteHostname string) (*connection.Connection, error) {
 | 
			
		||||
	versions := []byte{0x49, 0x4D, 0x01, 0x01}
 | 
			
		||||
	if n, err := conn.Write(versions); err != nil || n < len(versions) {
 | 
			
		||||
		return nil, utils.VersionNegotiationError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Outbound side of the connection sends a list of supported versions
 | 
			
		||||
	if outbound {
 | 
			
		||||
		if n, err := conn.Write(versions); err != nil || n < len(versions) {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	res := make([]byte, 1)
 | 
			
		||||
	if _, err := io.ReadAtLeast(conn, res, len(res)); err != nil {
 | 
			
		||||
		return nil, utils.VersionNegotiationError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
		res := make([]byte, 1)
 | 
			
		||||
		if _, err := io.ReadAtLeast(conn, res, len(res)); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	if res[0] != 0x01 {
 | 
			
		||||
		return nil, utils.VersionNegotiationFailed
 | 
			
		||||
	}
 | 
			
		||||
	rc := connection.NewOutboundConnection(conn, remoteHostname)
 | 
			
		||||
	return rc, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		if res[0] != 0x01 {
 | 
			
		||||
			return nil, errors.New("unsupported protocol version")
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		// Read version response header
 | 
			
		||||
		header := make([]byte, 3)
 | 
			
		||||
		if _, err := io.ReadAtLeast(conn, header, len(header)); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
// NegotiateVersionInbound takes in a connection and performs version negotiation
 | 
			
		||||
// as if that connection was a client. Returns a ricochet connection if successful
 | 
			
		||||
// error otherwise.
 | 
			
		||||
func NegotiateVersionInbound(conn net.Conn) (*connection.Connection, error) {
 | 
			
		||||
	versions := []byte{0x49, 0x4D, 0x01, 0x01}
 | 
			
		||||
	// Read version response header
 | 
			
		||||
	header := make([]byte, 3)
 | 
			
		||||
	if _, err := io.ReadAtLeast(conn, header, len(header)); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
		if header[0] != versions[0] || header[1] != versions[1] || header[2] < 1 {
 | 
			
		||||
			return nil, errors.New("invalid protocol response")
 | 
			
		||||
		}
 | 
			
		||||
	if header[0] != versions[0] || header[1] != versions[1] || header[2] < 1 {
 | 
			
		||||
		return nil, utils.VersionNegotiationError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
		// Read list of supported versions (which is header[2] bytes long)
 | 
			
		||||
		versionList := make([]byte, header[2])
 | 
			
		||||
		if _, err := io.ReadAtLeast(conn, versionList, len(versionList)); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	// Read list of supported versions (which is header[2] bytes long)
 | 
			
		||||
	versionList := make([]byte, header[2])
 | 
			
		||||
	if _, err := io.ReadAtLeast(conn, versionList, len(versionList)); err != nil {
 | 
			
		||||
		return nil, utils.VersionNegotiationError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
		selectedVersion := byte(0xff)
 | 
			
		||||
		for _, v := range versionList {
 | 
			
		||||
			if v == 0x01 {
 | 
			
		||||
				selectedVersion = v
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if n, err := conn.Write([]byte{selectedVersion}); err != nil || n < 1 {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if selectedVersion == 0xff {
 | 
			
		||||
			return nil, errors.New("no supported protocol version")
 | 
			
		||||
	selectedVersion := byte(0xff)
 | 
			
		||||
	for _, v := range versionList {
 | 
			
		||||
		if v == 0x01 {
 | 
			
		||||
			selectedVersion = v
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	oc := new(OpenConnection)
 | 
			
		||||
	oc.Init(outbound, conn)
 | 
			
		||||
	return oc, nil
 | 
			
		||||
	if n, err := conn.Write([]byte{selectedVersion}); err != nil || n < 1 {
 | 
			
		||||
		return nil, utils.VersionNegotiationFailed
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if selectedVersion == 0xff {
 | 
			
		||||
		return nil, utils.VersionNegotiationFailed
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rc := connection.NewInboundConnection(conn)
 | 
			
		||||
	return rc, nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,210 +0,0 @@
 | 
			
		|||
package goricochet
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/rsa"
 | 
			
		||||
	"crypto/x509"
 | 
			
		||||
	"encoding/asn1"
 | 
			
		||||
	"encoding/pem"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"github.com/s-rah/go-ricochet/utils"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"log"
 | 
			
		||||
	"net"
 | 
			
		||||
	"strconv"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// StandardRicochetService implements all the necessary flows to implement a
 | 
			
		||||
// minimal, protocol compliant Ricochet Service. It can be built on by other
 | 
			
		||||
// applications to produce automated riochet applications, and is a useful
 | 
			
		||||
// example for other implementations.
 | 
			
		||||
type StandardRicochetService struct {
 | 
			
		||||
	PrivateKey     *rsa.PrivateKey
 | 
			
		||||
	serverHostname string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// StandardRicochetConnection implements the ConnectionHandler interface
 | 
			
		||||
// to handle events on connections. An instance of StandardRicochetConnection
 | 
			
		||||
// is created for each OpenConnection by the HandleConnection method.
 | 
			
		||||
type StandardRicochetConnection struct {
 | 
			
		||||
	Conn       *OpenConnection
 | 
			
		||||
	PrivateKey *rsa.PrivateKey
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Init initializes a StandardRicochetService with the cryptographic key given
 | 
			
		||||
// by filename.
 | 
			
		||||
func (srs *StandardRicochetService) Init(filename string) error {
 | 
			
		||||
	pemData, err := ioutil.ReadFile(filename)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return errors.New("Could not setup ricochet service: could not read private key")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	block, _ := pem.Decode(pemData)
 | 
			
		||||
	if block == nil || block.Type != "RSA PRIVATE KEY" {
 | 
			
		||||
		return errors.New("Could not setup ricochet service: no valid PEM data found")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	srs.PrivateKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return errors.New("Could not setup ricochet service: could not parse private key")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	publicKeyBytes, _ := asn1.Marshal(rsa.PublicKey{
 | 
			
		||||
		N: srs.PrivateKey.PublicKey.N,
 | 
			
		||||
		E: srs.PrivateKey.PublicKey.E,
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	srs.serverHostname = utils.GetTorHostname(publicKeyBytes)
 | 
			
		||||
	log.Printf("Initialised ricochet service for %s", srs.serverHostname)
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Listen starts listening for service connections on localhost `port`.
 | 
			
		||||
func (srs *StandardRicochetService) Listen(handler ServiceHandler, port int) {
 | 
			
		||||
	ln, err := net.Listen("tcp", "127.0.0.1:"+strconv.Itoa(port))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Printf("Cannot Listen on Port %v", port)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Serve(ln, handler)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Connect initiates a new client connection to `hostname`, which must be in one
 | 
			
		||||
// of the forms accepted by the goricochet.Connect() method.
 | 
			
		||||
func (srs *StandardRicochetService) Connect(hostname string) (*OpenConnection, error) {
 | 
			
		||||
	log.Printf("Connecting to...%s", hostname)
 | 
			
		||||
	oc, err := Connect(hostname)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, errors.New("Could not connect to: " + hostname + " " + err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	oc.MyHostname = srs.serverHostname
 | 
			
		||||
	return oc, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OnNewConnection is called for new inbound connections to our service. This
 | 
			
		||||
// method implements the ServiceHandler interface.
 | 
			
		||||
func (srs *StandardRicochetService) OnNewConnection(oc *OpenConnection) {
 | 
			
		||||
	oc.MyHostname = srs.serverHostname
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OnFailedConnection is called for inbound connections that fail to successfully
 | 
			
		||||
// complete version negotiation for any reason. This method implements the
 | 
			
		||||
// ServiceHandler interface.
 | 
			
		||||
func (srs *StandardRicochetService) OnFailedConnection(err error) {
 | 
			
		||||
	log.Printf("Inbound connection failed: %s", err)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ------
 | 
			
		||||
 | 
			
		||||
// OnReady is called when a client or server sucessfully passes Version Negotiation.
 | 
			
		||||
func (src *StandardRicochetConnection) OnReady(oc *OpenConnection) {
 | 
			
		||||
	src.Conn = oc
 | 
			
		||||
	if oc.Client {
 | 
			
		||||
		log.Printf("Successfully connected to %s", oc.OtherHostname)
 | 
			
		||||
		oc.IsAuthed = true // Connections to Servers are Considered Authenticated by Default
 | 
			
		||||
		oc.Authenticate(1)
 | 
			
		||||
	} else {
 | 
			
		||||
		log.Printf("Inbound connection received")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OnDisconnect is called when a connection is closed
 | 
			
		||||
func (src *StandardRicochetConnection) OnDisconnect() {
 | 
			
		||||
	log.Printf("Disconnected from %s", src.Conn.OtherHostname)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OnAuthenticationRequest is called when a client requests Authentication
 | 
			
		||||
func (src *StandardRicochetConnection) OnAuthenticationRequest(channelID int32, clientCookie [16]byte) {
 | 
			
		||||
	src.Conn.ConfirmAuthChannel(channelID, clientCookie)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OnAuthenticationChallenge constructs a valid authentication challenge to the serverCookie
 | 
			
		||||
func (src *StandardRicochetConnection) OnAuthenticationChallenge(channelID int32, serverCookie [16]byte) {
 | 
			
		||||
	// DER Encode the Public Key
 | 
			
		||||
	publickeyBytes, _ := asn1.Marshal(rsa.PublicKey{
 | 
			
		||||
		N: src.PrivateKey.PublicKey.N,
 | 
			
		||||
		E: src.PrivateKey.PublicKey.E,
 | 
			
		||||
	})
 | 
			
		||||
	src.Conn.SendProof(1, serverCookie, publickeyBytes, src.PrivateKey)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OnAuthenticationProof is called when a client sends Proof for an existing authentication challenge
 | 
			
		||||
func (src *StandardRicochetConnection) OnAuthenticationProof(channelID int32, publicKey []byte, signature []byte) {
 | 
			
		||||
	result := src.Conn.ValidateProof(channelID, publicKey, signature)
 | 
			
		||||
	// This implementation always sends 'true', indicating that the contact is known
 | 
			
		||||
	src.Conn.SendAuthenticationResult(channelID, result, true)
 | 
			
		||||
	src.Conn.IsAuthed = result
 | 
			
		||||
	src.Conn.CloseChannel(channelID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OnAuthenticationResult is called once a server has returned the result of the Proof Verification
 | 
			
		||||
func (src *StandardRicochetConnection) OnAuthenticationResult(channelID int32, result bool, isKnownContact bool) {
 | 
			
		||||
	src.Conn.IsAuthed = result
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsKnownContact allows a caller to determine if a hostname an authorized contact.
 | 
			
		||||
func (src *StandardRicochetConnection) IsKnownContact(hostname string) bool {
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OnContactRequest is called when a client sends a new contact request
 | 
			
		||||
func (src *StandardRicochetConnection) OnContactRequest(channelID int32, nick string, message string) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OnContactRequestAck is called when a server sends a reply to an existing contact request
 | 
			
		||||
func (src *StandardRicochetConnection) OnContactRequestAck(channelID int32, status string) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OnOpenChannelRequest is called when a client or server requests to open a new channel
 | 
			
		||||
func (src *StandardRicochetConnection) OnOpenChannelRequest(channelID int32, channelType string) {
 | 
			
		||||
	src.Conn.AckOpenChannel(channelID, channelType)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OnOpenChannelRequestSuccess is called when a client or server responds to an open channel request
 | 
			
		||||
func (src *StandardRicochetConnection) OnOpenChannelRequestSuccess(channelID int32) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OnChannelClosed is called when a client or server closes an existing channel
 | 
			
		||||
func (src *StandardRicochetConnection) OnChannelClosed(channelID int32) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OnChatMessage is called when a new chat message is received.
 | 
			
		||||
func (src *StandardRicochetConnection) OnChatMessage(channelID int32, messageID int32, message string) {
 | 
			
		||||
	src.Conn.AckChatMessage(channelID, messageID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OnChatMessageAck is called when a new chat message is ascknowledged.
 | 
			
		||||
func (src *StandardRicochetConnection) OnChatMessageAck(channelID int32, messageID int32) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OnFailedChannelOpen is called when a server fails to open a channel
 | 
			
		||||
func (src *StandardRicochetConnection) OnFailedChannelOpen(channelID int32, errorType string) {
 | 
			
		||||
	src.Conn.UnsetChannel(channelID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OnGenericError is called when a generalized error is returned from the peer
 | 
			
		||||
func (src *StandardRicochetConnection) OnGenericError(channelID int32) {
 | 
			
		||||
	src.Conn.RejectOpenChannel(channelID, "GenericError")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//OnUnknownTypeError is called when an unknown type error is returned from the peer
 | 
			
		||||
func (src *StandardRicochetConnection) OnUnknownTypeError(channelID int32) {
 | 
			
		||||
	src.Conn.RejectOpenChannel(channelID, "UnknownTypeError")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OnUnauthorizedError is called when an unathorized error is returned from the peer
 | 
			
		||||
func (src *StandardRicochetConnection) OnUnauthorizedError(channelID int32) {
 | 
			
		||||
	src.Conn.RejectOpenChannel(channelID, "UnauthorizedError")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OnBadUsageError is called when a bad usage error is returned from the peer
 | 
			
		||||
func (src *StandardRicochetConnection) OnBadUsageError(channelID int32) {
 | 
			
		||||
	src.Conn.RejectOpenChannel(channelID, "BadUsageError")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OnFailedError is called when a failed error is returned from the peer
 | 
			
		||||
func (src *StandardRicochetConnection) OnFailedError(channelID int32) {
 | 
			
		||||
	src.Conn.RejectOpenChannel(channelID, "FailedError")
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,56 @@
 | 
			
		|||
package utils
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/rsa"
 | 
			
		||||
	"crypto/x509"
 | 
			
		||||
	"encoding/pem"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"crypto/rand"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	InvalidPrivateKeyFileError = Error("InvalidPrivateKeyFileError")
 | 
			
		||||
	RICOCHET_KEY_SIZE = 1024
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Generate a private key for use
 | 
			
		||||
func GeneratePrivateKey() (*rsa.PrivateKey, error) {
 | 
			
		||||
	privateKey, err := rsa.GenerateKey(rand.Reader, RICOCHET_KEY_SIZE)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, errors.New("Could not generate key: " + err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	privateKeyDer := x509.MarshalPKCS1PrivateKey(privateKey)
 | 
			
		||||
	return x509.ParsePKCS1PrivateKey(privateKeyDer)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LoadPrivateKeyFromFile loads a private key from a file...
 | 
			
		||||
func LoadPrivateKeyFromFile(filename string) (*rsa.PrivateKey, error) {
 | 
			
		||||
	pemData, err := ioutil.ReadFile(filename)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ParsePrivateKey(pemData)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Convert a private key string to a usable private key
 | 
			
		||||
func ParsePrivateKey(pemData []byte) (*rsa.PrivateKey, error) {
 | 
			
		||||
	block, _ := pem.Decode(pemData)
 | 
			
		||||
	if block == nil || block.Type != "RSA PRIVATE KEY" {
 | 
			
		||||
		return nil, InvalidPrivateKeyFileError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return x509.ParsePKCS1PrivateKey(block.Bytes)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// turn a private key into storable string
 | 
			
		||||
func PrivateKeyToString(privateKey *rsa.PrivateKey) string {
 | 
			
		||||
	privateKeyBlock := pem.Block{
 | 
			
		||||
		Type: "RSA PRIVATE KEY",
 | 
			
		||||
		Headers: nil,
 | 
			
		||||
		Bytes: x509.MarshalPKCS1PrivateKey(privateKey),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return string(pem.EncodeToMemory(&privateKeyBlock))
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,17 +1,48 @@
 | 
			
		|||
package utils
 | 
			
		||||
 | 
			
		||||
import "fmt"
 | 
			
		||||
import "log"
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// RecoverFromError doesn't really recover from anything....see comment below
 | 
			
		||||
func RecoverFromError() {
 | 
			
		||||
	if r := recover(); r != nil {
 | 
			
		||||
		// This should only really happen if there is a failure de/serializing. If
 | 
			
		||||
		// this does happen then we currently error. In the future we might be
 | 
			
		||||
		// able to make this nicer.
 | 
			
		||||
		log.Fatalf("Recovered from panic() - this really shouldn't happen. Reason: %v", r)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
// Error captures various common ricochet errors
 | 
			
		||||
type Error string
 | 
			
		||||
 | 
			
		||||
func (e Error) Error() string { return string(e) }
 | 
			
		||||
 | 
			
		||||
// Defining Versions
 | 
			
		||||
const (
 | 
			
		||||
	VersionNegotiationError  = Error("VersionNegotiationError")
 | 
			
		||||
	VersionNegotiationFailed = Error("VersionNegotiationFailed")
 | 
			
		||||
 | 
			
		||||
	RicochetConnectionClosed = Error("RicochetConnectionClosed")
 | 
			
		||||
	RicochetProtocolError    = Error("RicochetProtocolError")
 | 
			
		||||
 | 
			
		||||
	UnknownChannelTypeError      = Error("UnknownChannelTypeError")
 | 
			
		||||
	UnauthorizedChannelTypeError = Error("UnauthorizedChannelTypeError")
 | 
			
		||||
 | 
			
		||||
	// Timeout Errors
 | 
			
		||||
	ActionTimedOutError = Error("ActionTimedOutError")
 | 
			
		||||
	PeerTimedOutError   = Error("PeerTimedOutError")
 | 
			
		||||
 | 
			
		||||
	// Authentication Errors
 | 
			
		||||
	ClientFailedToAuthenticateError     = Error("ClientFailedToAuthenticateError")
 | 
			
		||||
	ServerRejectedClientConnectionError = Error("ServerRejectedClientConnectionError")
 | 
			
		||||
 | 
			
		||||
	UnauthorizedActionError  = Error("UnauthorizedActionError")
 | 
			
		||||
	ChannelClosedByPeerError = Error("ChannelClosedByPeerError")
 | 
			
		||||
 | 
			
		||||
	// Channel Management Errors
 | 
			
		||||
	ServerAttemptedToOpenEvenNumberedChannelError = Error("ServerAttemptedToOpenEvenNumberedChannelError")
 | 
			
		||||
	ClientAttemptedToOpenOddNumberedChannelError  = Error("ClientAttemptedToOpenOddNumberedChannelError")
 | 
			
		||||
	ChannelIDIsAlreadyInUseError                  = Error("ChannelIDIsAlreadyInUseError")
 | 
			
		||||
	AttemptToOpenMoreThanOneSingletonChannelError = Error("AttemptToOpenMoreThanOneSingletonChannelError")
 | 
			
		||||
 | 
			
		||||
	// Library Use Errors
 | 
			
		||||
	PrivateKeyNotSetError = Error("ClientFailedToAuthenticateError")
 | 
			
		||||
 | 
			
		||||
	// Connection Errors
 | 
			
		||||
	ConnectionClosedError = Error("ConnectionClosedError")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// CheckError is a helper function for panicing on errors which we need to handle
 | 
			
		||||
// but should be very rare e.g. failures deserializing a protobuf object that
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,12 +1,11 @@
 | 
			
		|||
package goricochet
 | 
			
		||||
package utils
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"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/contact"
 | 
			
		||||
	"github.com/s-rah/go-ricochet/control"
 | 
			
		||||
	"github.com/s-rah/go-ricochet/utils"
 | 
			
		||||
	"github.com/s-rah/go-ricochet/wire/auth"
 | 
			
		||||
	"github.com/s-rah/go-ricochet/wire/chat"
 | 
			
		||||
	"github.com/s-rah/go-ricochet/wire/contact"
 | 
			
		||||
	"github.com/s-rah/go-ricochet/wire/control"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// MessageBuilder allows a client to construct specific data packets for the
 | 
			
		||||
| 
						 | 
				
			
			@ -16,7 +15,7 @@ type MessageBuilder struct {
 | 
			
		|||
 | 
			
		||||
// OpenChannel contructs a message which will request to open a channel for
 | 
			
		||||
// chat on the given channelID.
 | 
			
		||||
func (mb *MessageBuilder) OpenChannel(channelID int32, channelType string) ([]byte, error) {
 | 
			
		||||
func (mb *MessageBuilder) OpenChannel(channelID int32, channelType string) []byte {
 | 
			
		||||
	oc := &Protocol_Data_Control.OpenChannel{
 | 
			
		||||
		ChannelIdentifier: proto.Int32(channelID),
 | 
			
		||||
		ChannelType:       proto.String(channelType),
 | 
			
		||||
| 
						 | 
				
			
			@ -24,11 +23,13 @@ func (mb *MessageBuilder) OpenChannel(channelID int32, channelType string) ([]by
 | 
			
		|||
	pc := &Protocol_Data_Control.Packet{
 | 
			
		||||
		OpenChannel: oc,
 | 
			
		||||
	}
 | 
			
		||||
	return proto.Marshal(pc)
 | 
			
		||||
	ret, err := proto.Marshal(pc)
 | 
			
		||||
	CheckError(err)
 | 
			
		||||
	return ret
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AckOpenChannel constructs a message to acknowledge a previous open channel operation.
 | 
			
		||||
func (mb *MessageBuilder) AckOpenChannel(channelID int32) ([]byte, error) {
 | 
			
		||||
func (mb *MessageBuilder) AckOpenChannel(channelID int32) []byte {
 | 
			
		||||
	cr := &Protocol_Data_Control.ChannelResult{
 | 
			
		||||
		ChannelIdentifier: proto.Int32(channelID),
 | 
			
		||||
		Opened:            proto.Bool(true),
 | 
			
		||||
| 
						 | 
				
			
			@ -36,11 +37,13 @@ func (mb *MessageBuilder) AckOpenChannel(channelID int32) ([]byte, error) {
 | 
			
		|||
	pc := &Protocol_Data_Control.Packet{
 | 
			
		||||
		ChannelResult: cr,
 | 
			
		||||
	}
 | 
			
		||||
	return proto.Marshal(pc)
 | 
			
		||||
	ret, err := proto.Marshal(pc)
 | 
			
		||||
	CheckError(err)
 | 
			
		||||
	return ret
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RejectOpenChannel constructs a channel result message, stating the channel failed to open and a reason
 | 
			
		||||
func (mb *MessageBuilder) RejectOpenChannel(channelID int32, error string) ([]byte, error) {
 | 
			
		||||
func (mb *MessageBuilder) RejectOpenChannel(channelID int32, error string) []byte {
 | 
			
		||||
 | 
			
		||||
	errorNum := Protocol_Data_Control.ChannelResult_CommonError_value[error]
 | 
			
		||||
	commonError := Protocol_Data_Control.ChannelResult_CommonError(errorNum)
 | 
			
		||||
| 
						 | 
				
			
			@ -53,28 +56,32 @@ func (mb *MessageBuilder) RejectOpenChannel(channelID int32, error string) ([]by
 | 
			
		|||
	pc := &Protocol_Data_Control.Packet{
 | 
			
		||||
		ChannelResult: cr,
 | 
			
		||||
	}
 | 
			
		||||
	return proto.Marshal(pc)
 | 
			
		||||
	ret, err := proto.Marshal(pc)
 | 
			
		||||
	CheckError(err)
 | 
			
		||||
	return ret
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ConfirmAuthChannel constructs a message to acknowledge a previous open channel operation.
 | 
			
		||||
func (mb *MessageBuilder) ConfirmAuthChannel(channelID int32, serverCookie [16]byte) ([]byte, error) {
 | 
			
		||||
func (mb *MessageBuilder) ConfirmAuthChannel(channelID int32, serverCookie [16]byte) []byte {
 | 
			
		||||
	cr := &Protocol_Data_Control.ChannelResult{
 | 
			
		||||
		ChannelIdentifier: proto.Int32(channelID),
 | 
			
		||||
		Opened:            proto.Bool(true),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err := proto.SetExtension(cr, Protocol_Data_AuthHiddenService.E_ServerCookie, serverCookie[:])
 | 
			
		||||
	utils.CheckError(err)
 | 
			
		||||
	CheckError(err)
 | 
			
		||||
 | 
			
		||||
	pc := &Protocol_Data_Control.Packet{
 | 
			
		||||
		ChannelResult: cr,
 | 
			
		||||
	}
 | 
			
		||||
	return proto.Marshal(pc)
 | 
			
		||||
	ret, err := proto.Marshal(pc)
 | 
			
		||||
	CheckError(err)
 | 
			
		||||
	return ret
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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) {
 | 
			
		||||
func (mb *MessageBuilder) OpenContactRequestChannel(channelID int32, nick string, message string) []byte {
 | 
			
		||||
	// Construct a Contact Request Channel
 | 
			
		||||
	oc := &Protocol_Data_Control.OpenChannel{
 | 
			
		||||
		ChannelIdentifier: proto.Int32(channelID),
 | 
			
		||||
| 
						 | 
				
			
			@ -87,16 +94,18 @@ func (mb *MessageBuilder) OpenContactRequestChannel(channelID int32, nick string
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	err := proto.SetExtension(oc, Protocol_Data_ContactRequest.E_ContactRequest, contactRequest)
 | 
			
		||||
	utils.CheckError(err)
 | 
			
		||||
	CheckError(err)
 | 
			
		||||
 | 
			
		||||
	pc := &Protocol_Data_Control.Packet{
 | 
			
		||||
		OpenChannel: oc,
 | 
			
		||||
	}
 | 
			
		||||
	return proto.Marshal(pc)
 | 
			
		||||
	ret, err := proto.Marshal(pc)
 | 
			
		||||
	CheckError(err)
 | 
			
		||||
	return ret
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReplyToContactRequestOnResponse constructs a message to acknowledge contact request
 | 
			
		||||
func (mb *MessageBuilder) ReplyToContactRequestOnResponse(channelID int32, status string) ([]byte, error) {
 | 
			
		||||
func (mb *MessageBuilder) ReplyToContactRequestOnResponse(channelID int32, status string) []byte {
 | 
			
		||||
	cr := &Protocol_Data_Control.ChannelResult{
 | 
			
		||||
		ChannelIdentifier: proto.Int32(channelID),
 | 
			
		||||
		Opened:            proto.Bool(true),
 | 
			
		||||
| 
						 | 
				
			
			@ -109,42 +118,49 @@ func (mb *MessageBuilder) ReplyToContactRequestOnResponse(channelID int32, statu
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	err := proto.SetExtension(cr, Protocol_Data_ContactRequest.E_Response, contactRequest)
 | 
			
		||||
	utils.CheckError(err)
 | 
			
		||||
	CheckError(err)
 | 
			
		||||
 | 
			
		||||
	pc := &Protocol_Data_Control.Packet{
 | 
			
		||||
		ChannelResult: cr,
 | 
			
		||||
	}
 | 
			
		||||
	return proto.Marshal(pc)
 | 
			
		||||
	ret, err := proto.Marshal(pc)
 | 
			
		||||
	CheckError(err)
 | 
			
		||||
	return ret
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReplyToContactRequest constructs a message to acknowledge a contact request
 | 
			
		||||
func (mb *MessageBuilder) ReplyToContactRequest(channelID int32, status string) ([]byte, error) {
 | 
			
		||||
func (mb *MessageBuilder) ReplyToContactRequest(channelID int32, status string) []byte {
 | 
			
		||||
	statusNum := Protocol_Data_ContactRequest.Response_Status_value[status]
 | 
			
		||||
	responseStatus := Protocol_Data_ContactRequest.Response_Status(statusNum)
 | 
			
		||||
	contactRequest := &Protocol_Data_ContactRequest.Response{
 | 
			
		||||
		Status: &responseStatus,
 | 
			
		||||
	}
 | 
			
		||||
	return proto.Marshal(contactRequest)
 | 
			
		||||
 | 
			
		||||
	ret, err := proto.Marshal(contactRequest)
 | 
			
		||||
	CheckError(err)
 | 
			
		||||
	return ret
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OpenAuthenticationChannel constructs a message which will reuqest to open a channel for
 | 
			
		||||
// authentication on the given channelID, with the given cookie
 | 
			
		||||
func (mb *MessageBuilder) OpenAuthenticationChannel(channelID int32, clientCookie [16]byte) ([]byte, error) {
 | 
			
		||||
func (mb *MessageBuilder) OpenAuthenticationChannel(channelID int32, clientCookie [16]byte) []byte {
 | 
			
		||||
	oc := &Protocol_Data_Control.OpenChannel{
 | 
			
		||||
		ChannelIdentifier: proto.Int32(channelID),
 | 
			
		||||
		ChannelType:       proto.String("im.ricochet.auth.hidden-service"),
 | 
			
		||||
	}
 | 
			
		||||
	err := proto.SetExtension(oc, Protocol_Data_AuthHiddenService.E_ClientCookie, clientCookie[:])
 | 
			
		||||
	utils.CheckError(err)
 | 
			
		||||
	CheckError(err)
 | 
			
		||||
 | 
			
		||||
	pc := &Protocol_Data_Control.Packet{
 | 
			
		||||
		OpenChannel: oc,
 | 
			
		||||
	}
 | 
			
		||||
	return proto.Marshal(pc)
 | 
			
		||||
	ret, err := proto.Marshal(pc)
 | 
			
		||||
	CheckError(err)
 | 
			
		||||
	return ret
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Proof constructs a proof message with the given public key and signature.
 | 
			
		||||
func (mb *MessageBuilder) Proof(publicKeyBytes []byte, signatureBytes []byte) ([]byte, error) {
 | 
			
		||||
func (mb *MessageBuilder) Proof(publicKeyBytes []byte, signatureBytes []byte) []byte {
 | 
			
		||||
	proof := &Protocol_Data_AuthHiddenService.Proof{
 | 
			
		||||
		PublicKey: publicKeyBytes,
 | 
			
		||||
		Signature: signatureBytes,
 | 
			
		||||
| 
						 | 
				
			
			@ -155,11 +171,13 @@ func (mb *MessageBuilder) Proof(publicKeyBytes []byte, signatureBytes []byte) ([
 | 
			
		|||
		Result: nil,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return proto.Marshal(ahsPacket)
 | 
			
		||||
	ret, err := proto.Marshal(ahsPacket)
 | 
			
		||||
	CheckError(err)
 | 
			
		||||
	return ret
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AuthResult constructs a response to a Proof
 | 
			
		||||
func (mb *MessageBuilder) AuthResult(accepted bool, isKnownContact bool) ([]byte, error) {
 | 
			
		||||
func (mb *MessageBuilder) AuthResult(accepted bool, isKnownContact bool) []byte {
 | 
			
		||||
	// Construct a Result Message
 | 
			
		||||
	result := &Protocol_Data_AuthHiddenService.Result{
 | 
			
		||||
		Accepted:       proto.Bool(accepted),
 | 
			
		||||
| 
						 | 
				
			
			@ -171,29 +189,74 @@ func (mb *MessageBuilder) AuthResult(accepted bool, isKnownContact bool) ([]byte
 | 
			
		|||
		Result: result,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return proto.Marshal(ahsPacket)
 | 
			
		||||
	ret, err := proto.Marshal(ahsPacket)
 | 
			
		||||
	CheckError(err)
 | 
			
		||||
	return ret
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ChatMessage constructs a chat message with the given content.
 | 
			
		||||
func (mb *MessageBuilder) ChatMessage(message string, messageID int32) ([]byte, error) {
 | 
			
		||||
func (mb *MessageBuilder) ChatMessage(message string, messageID uint32) []byte {
 | 
			
		||||
	cm := &Protocol_Data_Chat.ChatMessage{
 | 
			
		||||
		MessageId:   proto.Uint32(uint32(messageID)),
 | 
			
		||||
		MessageId:   proto.Uint32(messageID),
 | 
			
		||||
		MessageText: proto.String(message),
 | 
			
		||||
	}
 | 
			
		||||
	chatPacket := &Protocol_Data_Chat.Packet{
 | 
			
		||||
		ChatMessage: cm,
 | 
			
		||||
	}
 | 
			
		||||
	return proto.Marshal(chatPacket)
 | 
			
		||||
	ret, err := proto.Marshal(chatPacket)
 | 
			
		||||
	CheckError(err)
 | 
			
		||||
	return ret
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AckChatMessage constructs a chat message acknowledgement.
 | 
			
		||||
func (mb *MessageBuilder) AckChatMessage(messageID int32) ([]byte, error) {
 | 
			
		||||
func (mb *MessageBuilder) AckChatMessage(messageID uint32) []byte {
 | 
			
		||||
	cr := &Protocol_Data_Chat.ChatAcknowledge{
 | 
			
		||||
		MessageId: proto.Uint32(uint32(messageID)),
 | 
			
		||||
		MessageId: proto.Uint32(messageID),
 | 
			
		||||
		Accepted:  proto.Bool(true),
 | 
			
		||||
	}
 | 
			
		||||
	pc := &Protocol_Data_Chat.Packet{
 | 
			
		||||
		ChatAcknowledge: cr,
 | 
			
		||||
	}
 | 
			
		||||
	return proto.Marshal(pc)
 | 
			
		||||
	ret, err := proto.Marshal(pc)
 | 
			
		||||
	CheckError(err)
 | 
			
		||||
	return ret
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// KeepAlive ...
 | 
			
		||||
func (mb *MessageBuilder) KeepAlive(responseRequested bool) []byte {
 | 
			
		||||
	ka := &Protocol_Data_Control.KeepAlive{
 | 
			
		||||
		ResponseRequested: proto.Bool(responseRequested),
 | 
			
		||||
	}
 | 
			
		||||
	pc := &Protocol_Data_Control.Packet{
 | 
			
		||||
		KeepAlive: ka,
 | 
			
		||||
	}
 | 
			
		||||
	ret, err := proto.Marshal(pc)
 | 
			
		||||
	CheckError(err)
 | 
			
		||||
	return ret
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EnableFeatures ...
 | 
			
		||||
func (mb *MessageBuilder) EnableFeatures(features []string) []byte {
 | 
			
		||||
	ef := &Protocol_Data_Control.EnableFeatures{
 | 
			
		||||
		Feature: features,
 | 
			
		||||
	}
 | 
			
		||||
	pc := &Protocol_Data_Control.Packet{
 | 
			
		||||
		EnableFeatures: ef,
 | 
			
		||||
	}
 | 
			
		||||
	ret, err := proto.Marshal(pc)
 | 
			
		||||
	CheckError(err)
 | 
			
		||||
	return ret
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FeaturesEnabled ...
 | 
			
		||||
func (mb *MessageBuilder) FeaturesEnabled(features []string) []byte {
 | 
			
		||||
	fe := &Protocol_Data_Control.FeaturesEnabled{
 | 
			
		||||
		Feature: features,
 | 
			
		||||
	}
 | 
			
		||||
	pc := &Protocol_Data_Control.Packet{
 | 
			
		||||
		FeaturesEnabled: fe,
 | 
			
		||||
	}
 | 
			
		||||
	ret, err := proto.Marshal(pc)
 | 
			
		||||
	CheckError(err)
 | 
			
		||||
	return ret
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -3,10 +3,14 @@ package utils
 | 
			
		|||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"io"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	InvalidPacketLengthError = Error("InvalidPacketLengthError")
 | 
			
		||||
	InvalidChannelIDError    = Error("InvalidChannelIDError")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// RicochetData is a structure containing the raw data and the channel it the
 | 
			
		||||
// message originated on.
 | 
			
		||||
type RicochetData struct {
 | 
			
		||||
| 
						 | 
				
			
			@ -36,11 +40,11 @@ type RicochetNetwork struct {
 | 
			
		|||
func (rn *RicochetNetwork) SendRicochetPacket(dst io.Writer, channel int32, data []byte) error {
 | 
			
		||||
	packet := make([]byte, 4+len(data))
 | 
			
		||||
	if len(packet) > 65535 {
 | 
			
		||||
		return errors.New("packet too large")
 | 
			
		||||
		return InvalidPacketLengthError
 | 
			
		||||
	}
 | 
			
		||||
	binary.BigEndian.PutUint16(packet[0:2], uint16(len(packet)))
 | 
			
		||||
	if channel < 0 || channel > 65535 {
 | 
			
		||||
		return errors.New("invalid channel ID")
 | 
			
		||||
		return InvalidChannelIDError
 | 
			
		||||
	}
 | 
			
		||||
	binary.BigEndian.PutUint16(packet[2:4], uint16(channel))
 | 
			
		||||
	copy(packet[4:], data[:])
 | 
			
		||||
| 
						 | 
				
			
			@ -68,7 +72,7 @@ func (rn *RicochetNetwork) RecvRicochetPacket(reader io.Reader) (RicochetData, e
 | 
			
		|||
 | 
			
		||||
	size := int(binary.BigEndian.Uint16(header[0:2]))
 | 
			
		||||
	if size < 4 {
 | 
			
		||||
		return packet, errors.New("invalid packet length")
 | 
			
		||||
		return packet, InvalidPacketLengthError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	packet.Channel = int32(binary.BigEndian.Uint16(header[2:4]))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,12 +1,17 @@
 | 
			
		|||
package utils
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"golang.org/x/net/proxy"
 | 
			
		||||
	"net"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	CannotResolveLocalTCPAddressError = Error("CannotResolveLocalTCPAddressError")
 | 
			
		||||
	CannotDialLocalTCPAddressError    = Error("CannotDialLocalTCPAddressError")
 | 
			
		||||
	CannotDialRicochetAddressError    = Error("CannotDialRicochetAddressError")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// NetworkResolver allows a client to resolve various hostnames to connections
 | 
			
		||||
// The supported types are onions address are:
 | 
			
		||||
//  * ricochet:jlq67qzo6s4yp3sp
 | 
			
		||||
| 
						 | 
				
			
			@ -21,11 +26,11 @@ func (nr *NetworkResolver) Resolve(hostname string) (net.Conn, string, error) {
 | 
			
		|||
		addrParts := strings.Split(hostname, "|")
 | 
			
		||||
		tcpAddr, err := net.ResolveTCPAddr("tcp", addrParts[0])
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, "", errors.New("Cannot Resolve Local TCP Address")
 | 
			
		||||
			return nil, "", CannotResolveLocalTCPAddressError
 | 
			
		||||
		}
 | 
			
		||||
		conn, err := net.DialTCP("tcp", nil, tcpAddr)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, "", errors.New("Cannot Dial Local TCP Address")
 | 
			
		||||
			return nil, "", CannotDialLocalTCPAddressError
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// return just the onion address, not the local override for the hostname
 | 
			
		||||
| 
						 | 
				
			
			@ -45,8 +50,8 @@ func (nr *NetworkResolver) Resolve(hostname string) (net.Conn, string, error) {
 | 
			
		|||
 | 
			
		||||
	conn, err := torDialer.Dial("tcp", resolvedHostname+".onion:9878")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, "", errors.New("Cannot Dial Remote Ricochet Address")
 | 
			
		||||
		return nil, "", CannotDialRicochetAddressError
 | 
			
		||||
	}
 | 
			
		||||
	//conn.SetDeadline(time.Now().Add(5 * time.Second))
 | 
			
		||||
 | 
			
		||||
	return conn, resolvedHostname, nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,7 +18,7 @@ package Protocol_Data_AuthHiddenService
 | 
			
		|||
import proto "github.com/golang/protobuf/proto"
 | 
			
		||||
import fmt "fmt"
 | 
			
		||||
import math "math"
 | 
			
		||||
import Protocol_Data_Control "github.com/s-rah/go-ricochet/control"
 | 
			
		||||
import Protocol_Data_Control "github.com/s-rah/go-ricochet/wire/control"
 | 
			
		||||
 | 
			
		||||
// Reference imports to suppress errors if they are not otherwise used.
 | 
			
		||||
var _ = proto.Marshal
 | 
			
		||||
| 
						 | 
				
			
			@ -17,7 +17,7 @@ package Protocol_Data_ContactRequest
 | 
			
		|||
import proto "github.com/golang/protobuf/proto"
 | 
			
		||||
import fmt "fmt"
 | 
			
		||||
import math "math"
 | 
			
		||||
import Protocol_Data_Control "github.com/s-rah/go-ricochet/control"
 | 
			
		||||
import Protocol_Data_Control "github.com/s-rah/go-ricochet/wire/control"
 | 
			
		||||
 | 
			
		||||
// Reference imports to suppress errors if they are not otherwise used.
 | 
			
		||||
var _ = proto.Marshal
 | 
			
		||||
| 
						 | 
				
			
			@ -61,10 +61,10 @@
 | 
			
		|||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"importpath": "github.com/s-rah/go-ricochet",
 | 
			
		||||
			"repository": "https://github.com/s-rah/go-ricochet",
 | 
			
		||||
			"repository": "https://github.com/special/go-ricochet-protocol",
 | 
			
		||||
			"vcs": "git",
 | 
			
		||||
			"revision": "5a720a08d052019a113e9c2a94d6f1a3f582f09e",
 | 
			
		||||
			"branch": "master",
 | 
			
		||||
			"revision": "5b54d50bf4611a36c23dd732bd1e9d0dad441980",
 | 
			
		||||
			"branch": "api-rework-fixes",
 | 
			
		||||
			"notests": true
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue