From f4ed1c244b5df621a4d36f92964e6b7fdad08532 Mon Sep 17 00:00:00 2001 From: Sarah Jamie Lewis Date: Tue, 27 Jun 2017 10:39:33 -0700 Subject: [PATCH] Adding Inbound Version Negotiation + Error handling for missing private key setting --- channels/hiddenserviceauthchannel.go | 11 ++++++ channels/hiddenserviceauthchannel_test.go | 5 ++- connection/autoconnectionhandler.go | 2 ++ connection/inboundconnectionhandler.go | 4 +++ connection/outboundconnectionhandler.go | 6 ++++ ricochet.go | 43 +++++++++++++++++++++++ utils/error.go | 3 ++ 7 files changed, 73 insertions(+), 1 deletion(-) diff --git a/channels/hiddenserviceauthchannel.go b/channels/hiddenserviceauthchannel.go index 5a3be0b..e62f7d3 100644 --- a/channels/hiddenserviceauthchannel.go +++ b/channels/hiddenserviceauthchannel.go @@ -75,6 +75,11 @@ func (ah *HiddenServiceAuthChannel) Closed(err error) { // 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 { @@ -92,6 +97,12 @@ func (ah *HiddenServiceAuthChannel) OpenInbound(channel *Channel, oc *Protocol_D // 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 diff --git a/channels/hiddenserviceauthchannel_test.go b/channels/hiddenserviceauthchannel_test.go index 75456c9..8f3736f 100644 --- a/channels/hiddenserviceauthchannel_test.go +++ b/channels/hiddenserviceauthchannel_test.go @@ -71,9 +71,10 @@ func GetOpenAuthenticationChannelMessage() *Protocol_Data_Control.OpenChannel { } func TestAuthenticationOpenInbound(t *testing.T) { - + privateKey, _ := utils.LoadPrivateKeyFromFile("../testing/private_key") opm := GetOpenAuthenticationChannelMessage() authHandler := new(HiddenServiceAuthChannel) + authHandler.PrivateKey = privateKey channel := Channel{ID: 1} response, err := authHandler.OpenInbound(&channel, opm) @@ -90,7 +91,9 @@ func TestAuthenticationOpenInbound(t *testing.T) { } func TestAuthenticationOpenOutbound(t *testing.T) { + privateKey, _ := utils.LoadPrivateKeyFromFile("../testing/private_key") authHandler := new(HiddenServiceAuthChannel) + authHandler.PrivateKey = privateKey channel := Channel{ID: 1} response, err := authHandler.OpenOutbound(&channel) diff --git a/connection/autoconnectionhandler.go b/connection/autoconnectionhandler.go index 238d115..11a5ba6 100644 --- a/connection/autoconnectionhandler.go +++ b/connection/autoconnectionhandler.go @@ -24,7 +24,9 @@ type AutoConnectionHandler struct { } // Init ... +// TODO: Split this into client and server init func (ach *AutoConnectionHandler) Init(privateKey *rsa.PrivateKey, serverHostname string) { + ach.handlerMap = make(map[string]func() channels.Handler) ach.RegisterChannelHandler("im.ricochet.auth.hidden-service", func() channels.Handler { hsau := new(channels.HiddenServiceAuthChannel) diff --git a/connection/inboundconnectionhandler.go b/connection/inboundconnectionhandler.go index a070131..89a005c 100644 --- a/connection/inboundconnectionhandler.go +++ b/connection/inboundconnectionhandler.go @@ -35,6 +35,10 @@ func HandleInboundConnection(c *Connection) *InboundConnectionHandler { // 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 + } + ach := new(AutoConnectionHandler) ach.Init(privateKey, ich.connection.RemoteHostname) ach.SetServerAuthHandler(sach) diff --git a/connection/outboundconnectionhandler.go b/connection/outboundconnectionhandler.go index 2cc8b78..3e1fb38 100644 --- a/connection/outboundconnectionhandler.go +++ b/connection/outboundconnectionhandler.go @@ -4,6 +4,7 @@ import ( "crypto/rsa" "errors" "github.com/s-rah/go-ricochet/channels" + "github.com/s-rah/go-ricochet/utils" "github.com/s-rah/go-ricochet/policies" "log" ) @@ -33,6 +34,11 @@ func HandleOutboundConnection(c *Connection) *OutboundConnectionHandler { // 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(privateKey, och.connection.RemoteHostname) diff --git a/ricochet.go b/ricochet.go index c9e5f5f..257127c 100644 --- a/ricochet.go +++ b/ricochet.go @@ -53,3 +53,46 @@ func negotiateVersion(conn net.Conn, remoteHostname string) (*connection.Connect } +// 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, 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, 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, utils.VersionNegotiationFailed + } + + if selectedVersion == 0xff { + return nil, utils.VersionNegotiationFailed + } + + rc := connection.NewInboundConnection(conn) + return rc, nil +} + + + diff --git a/utils/error.go b/utils/error.go index b977245..beaba0f 100644 --- a/utils/error.go +++ b/utils/error.go @@ -24,6 +24,9 @@ const ( ActionTimedOutError = Error("ActionTimedOutError") ClientFailedToAuthenticateError = Error("ClientFailedToAuthenticateError") + + + PrivateKeyNotSetError = Error("PrivateKeyNotSetError") ) // RecoverFromError doesn't really recover from anything....see comment below