2019-01-04 21:44:21 +00:00
package connections
import (
"crypto/rsa"
"cwtch.im/cwtch/event"
"cwtch.im/cwtch/protocol"
"cwtch.im/cwtch/protocol/connections/peer"
"errors"
"git.openprivacy.ca/openprivacy/libricochet-go/application"
"git.openprivacy.ca/openprivacy/libricochet-go/channels"
"git.openprivacy.ca/openprivacy/libricochet-go/connectivity"
"git.openprivacy.ca/openprivacy/libricochet-go/identity"
"git.openprivacy.ca/openprivacy/libricochet-go/log"
"github.com/golang/protobuf/proto"
"golang.org/x/crypto/ed25519"
2019-01-08 18:58:01 +00:00
"sync"
2019-01-21 18:47:07 +00:00
"time"
2019-01-04 21:44:21 +00:00
)
2019-05-15 19:58:57 +00:00
type engine struct {
2019-01-04 21:44:21 +00:00
queue * event . Queue
connectionsManager * Manager
// Engine Attributes
2019-05-15 19:58:57 +00:00
identity identity . Identity
acn connectivity . ACN
2019-01-04 21:44:21 +00:00
app * application . RicochetApplication
// Engine State
started bool
2019-01-08 18:58:01 +00:00
// Blocklist
blocked sync . Map
2019-01-04 21:44:21 +00:00
// Pointer to the Global Event Manager
2019-06-05 20:40:55 +00:00
eventManager event . Manager
2019-05-15 19:58:57 +00:00
// Required for listen(), inaccessible from identity
privateKey ed25519 . PrivateKey
}
// Engine (ProtocolEngine) encapsulates the logic necessary to make and receive Cwtch connections.
// Note: ProtocolEngine doesn't have access to any information necessary to encrypt or decrypt GroupMessages
type Engine interface {
Identity ( ) identity . Identity
ACN ( ) connectivity . ACN
2019-06-05 20:40:55 +00:00
EventManager ( ) event . Manager
2019-05-15 19:58:57 +00:00
GetPeerHandler ( string ) * CwtchPeerHandler
ContactRequest ( string , string ) string
LookupContact ( string , rsa . PublicKey ) ( bool , bool )
LookupContactV3 ( string , ed25519 . PublicKey ) ( bool , bool )
Shutdown ( )
2019-01-04 21:44:21 +00:00
}
// NewProtocolEngine initializes a new engine that runs Cwtch using the given parameters
2019-06-05 20:40:55 +00:00
func NewProtocolEngine ( identity identity . Identity , privateKey ed25519 . PrivateKey , acn connectivity . ACN , eventManager event . Manager , blockedPeers [ ] string ) Engine {
2019-05-15 19:58:57 +00:00
engine := new ( engine )
engine . identity = identity
2019-01-04 21:44:21 +00:00
engine . privateKey = privateKey
engine . queue = event . NewEventQueue ( 100 )
go engine . eventHandler ( )
2019-05-15 19:58:57 +00:00
engine . acn = acn
engine . connectionsManager = NewConnectionsManager ( engine . acn )
2019-01-04 21:44:21 +00:00
go engine . connectionsManager . AttemptReconnections ( )
engine . eventManager = eventManager
2019-01-08 18:58:01 +00:00
2019-01-04 21:44:21 +00:00
engine . eventManager . Subscribe ( event . ProtocolEngineStartListen , engine . queue . EventChannel )
engine . eventManager . Subscribe ( event . PeerRequest , engine . queue . EventChannel )
engine . eventManager . Subscribe ( event . InvitePeerToGroup , engine . queue . EventChannel )
engine . eventManager . Subscribe ( event . JoinServer , engine . queue . EventChannel )
engine . eventManager . Subscribe ( event . SendMessageToGroup , engine . queue . EventChannel )
engine . eventManager . Subscribe ( event . SendMessageToPeer , engine . queue . EventChannel )
2019-01-08 18:58:01 +00:00
engine . eventManager . Subscribe ( event . BlockPeer , engine . queue . EventChannel )
for _ , peer := range blockedPeers {
engine . blocked . Store ( peer , true )
}
2019-01-04 21:44:21 +00:00
return engine
}
2019-05-15 19:58:57 +00:00
func ( e * engine ) ACN ( ) connectivity . ACN {
return e . acn
}
func ( e * engine ) Identity ( ) identity . Identity {
return e . identity
}
2019-06-05 20:40:55 +00:00
func ( e * engine ) EventManager ( ) event . Manager {
2019-05-15 20:12:11 +00:00
return e . eventManager
}
2019-01-04 21:44:21 +00:00
// eventHandler process events from other subsystems
2019-05-15 19:58:57 +00:00
func ( e * engine ) eventHandler ( ) {
2019-01-04 21:44:21 +00:00
for {
ev := e . queue . Next ( )
switch ev . EventType {
case event . StatusRequest :
e . eventManager . Publish ( event . Event { EventType : event . ProtocolEngineStatus , EventID : ev . EventID } )
case event . PeerRequest :
2019-05-15 19:58:57 +00:00
e . peerWithOnion ( ev . Data [ event . RemotePeer ] )
2019-01-04 21:44:21 +00:00
case event . InvitePeerToGroup :
2019-05-15 19:58:57 +00:00
e . inviteOnionToGroup ( ev . Data [ event . RemotePeer ] , [ ] byte ( ev . Data [ event . GroupInvite ] ) )
2019-01-04 21:44:21 +00:00
case event . JoinServer :
2019-05-15 19:58:57 +00:00
e . joinServer ( ev . Data [ event . GroupServer ] )
2019-01-04 21:44:21 +00:00
case event . SendMessageToGroup :
2019-05-15 19:58:57 +00:00
e . sendMessageToGroup ( ev . Data [ event . GroupServer ] , [ ] byte ( ev . Data [ event . Ciphertext ] ) , [ ] byte ( ev . Data [ event . Signature ] ) )
2019-01-04 21:44:21 +00:00
case event . SendMessageToPeer :
log . Debugf ( "Sending Message to Peer....." )
2019-01-21 20:08:03 +00:00
ppc := e . connectionsManager . GetPeerPeerConnectionForOnion ( ev . Data [ event . RemotePeer ] )
2019-02-14 21:04:40 +00:00
if ppc != nil && ppc . GetState ( ) == AUTHENTICATED {
2019-03-04 02:01:38 +00:00
err := ppc . SendPacket ( [ ] byte ( ev . Data [ event . Data ] ) )
if err != nil {
e . eventManager . Publish ( event . NewEvent ( event . SendMessageToPeerError , map [ event . Field ] string { event . RemotePeer : ev . Data [ event . RemotePeer ] , event . Signature : ev . EventID , event . Error : err . Error ( ) } ) )
}
2019-02-14 21:04:40 +00:00
} else {
2019-04-23 20:30:50 +00:00
e . eventManager . Publish ( event . NewEvent ( event . SendMessageToPeerError , map [ event . Field ] string { event . RemotePeer : ev . Data [ event . RemotePeer ] , event . Signature : ev . EventID , event . Error : "peer is offline or the connection has yet to finalize" } ) )
2019-01-04 21:44:21 +00:00
}
2019-01-08 18:58:01 +00:00
case event . BlockPeer :
2019-01-21 20:08:03 +00:00
e . blocked . Store ( ev . Data [ event . RemotePeer ] , true )
2019-01-21 20:37:06 +00:00
ppc := e . connectionsManager . GetPeerPeerConnectionForOnion ( ev . Data [ event . RemotePeer ] )
if ppc != nil {
ppc . Close ( )
}
e . app . Close ( ev . Data [ event . RemotePeer ] )
2019-01-04 21:44:21 +00:00
case event . ProtocolEngineStartListen :
go e . listenFn ( )
default :
return
}
}
}
// GetPeerHandler is an external interface function that allows callers access to a CwtchPeerHandler
// TODO: There is likely a slightly better way to encapsulate this behavior
2019-05-15 19:58:57 +00:00
func ( e * engine ) GetPeerHandler ( remotePeerHostname string ) * CwtchPeerHandler {
2019-01-04 21:44:21 +00:00
return & CwtchPeerHandler { Onion : remotePeerHostname , EventBus : e . eventManager }
}
// Listen sets up an onion listener to process incoming cwtch messages
2019-05-15 19:58:57 +00:00
func ( e * engine ) listenFn ( ) {
2019-01-04 21:44:21 +00:00
ra := new ( application . RicochetApplication )
2019-05-15 19:58:57 +00:00
onionService , err := e . acn . Listen ( e . privateKey , application . RicochetPort )
2019-01-04 21:44:21 +00:00
if err != nil /*&& fmt.Sprintf("%v", err) != "550 Unspecified Tor error: Onion address collision"*/ {
2019-05-15 19:58:57 +00:00
e . eventManager . Publish ( event . NewEvent ( event . ProtocolEngineStopped , map [ event . Field ] string { event . Identity : e . identity . Hostname ( ) , event . Error : err . Error ( ) } ) )
2019-01-04 21:44:21 +00:00
return
}
2019-01-23 20:50:53 +00:00
af := application . InstanceFactory { }
2019-01-04 21:44:21 +00:00
af . Init ( )
2019-01-23 20:50:53 +00:00
af . AddHandler ( "im.cwtch.peer" , func ( rai * application . Instance ) func ( ) channels . Handler {
2019-01-04 21:44:21 +00:00
cpi := new ( CwtchPeerInstance )
cpi . Init ( rai , ra )
return func ( ) channels . Handler {
cpc := new ( peer . CwtchPeerChannel )
cpc . Handler = e . GetPeerHandler ( rai . RemoteHostname )
return cpc
}
} )
2019-01-23 20:50:53 +00:00
af . AddHandler ( "im.cwtch.peer.data" , func ( rai * application . Instance ) func ( ) channels . Handler {
2019-01-04 21:44:21 +00:00
cpi := new ( CwtchPeerInstance )
cpi . Init ( rai , ra )
return func ( ) channels . Handler {
cpc := new ( peer . CwtchPeerDataChannel )
cpc . Handler = e . GetPeerHandler ( rai . RemoteHostname )
return cpc
}
} )
2019-05-15 19:58:57 +00:00
ra . Init ( e . ACN ( ) , e . identity . Name , e . identity , af , e )
2019-01-04 21:44:21 +00:00
log . Infof ( "Running cwtch peer on %v" , onionService . AddressFull ( ) )
e . started = true
e . app = ra
ra . Run ( onionService )
2019-05-15 19:58:57 +00:00
e . eventManager . Publish ( event . NewEvent ( event . ProtocolEngineStopped , map [ event . Field ] string { event . Identity : e . identity . Hostname ( ) } ) )
2019-01-04 21:44:21 +00:00
return
}
2019-01-08 18:58:01 +00:00
// LookupContact is a V2 API Call, we want to reject all V2 Peers
// TODO Deprecate
2019-05-15 19:58:57 +00:00
func ( e * engine ) LookupContact ( hostname string , publicKey rsa . PublicKey ) ( allowed , known bool ) {
2019-01-08 18:58:01 +00:00
return false , false
2019-01-04 21:44:21 +00:00
}
2019-01-08 18:58:01 +00:00
// ContactRequest is a V2 API Call needed to implement ContactRequestHandler Interface
// TODO Deprecate
2019-05-15 19:58:57 +00:00
func ( e * engine ) ContactRequest ( name string , message string ) string {
2019-01-08 18:58:01 +00:00
return "Rejected"
2019-01-04 21:44:21 +00:00
}
2019-01-08 18:58:01 +00:00
// LookupContactV3 returns that a contact is known and allowed to communicate for all cases.
2019-05-15 19:58:57 +00:00
func ( e * engine ) LookupContactV3 ( hostname string , publicKey ed25519 . PublicKey ) ( allowed , known bool ) {
2019-01-08 18:58:01 +00:00
// TODO: We want to autoblock those that are blocked, The known parameter has no use anymore and should be
// disregarded by peers, so we set it to false.
if _ , blocked := e . blocked . Load ( hostname ) ; blocked {
return false , false
}
return true , false
2019-01-04 21:44:21 +00:00
}
// Shutdown tears down the eventHandler goroutine
2019-05-15 19:58:57 +00:00
func ( e * engine ) Shutdown ( ) {
2019-01-04 21:44:21 +00:00
e . connectionsManager . Shutdown ( )
e . app . Shutdown ( )
e . queue . Shutdown ( )
}
2019-05-15 19:58:57 +00:00
// peerWithOnion is the entry point for cwtchPeer relationships
func ( e * engine ) peerWithOnion ( onion string ) * PeerPeerConnection {
2019-01-04 21:44:21 +00:00
return e . connectionsManager . ManagePeerConnection ( onion , e )
}
2019-05-15 19:58:57 +00:00
// inviteOnionToGroup kicks off the invite process
func ( e * engine ) inviteOnionToGroup ( onion string , invite [ ] byte ) error {
2019-01-04 21:44:21 +00:00
ppc := e . connectionsManager . GetPeerPeerConnectionForOnion ( onion )
if ppc == nil {
return errors . New ( "peer connection not setup for onion. peers must be trusted before sending" )
}
if ppc . GetState ( ) == AUTHENTICATED {
log . Infof ( "Got connection for group: %v - Sending Invite\n" , ppc )
ppc . SendGroupInvite ( invite )
} else {
return errors . New ( "cannot send invite to onion: peer connection is not ready" )
}
return nil
}
2019-05-15 19:58:57 +00:00
// receiveGroupMessage is a callback function that processes GroupMessages from a given server
func ( e * engine ) receiveGroupMessage ( server string , gm * protocol . GroupMessage ) {
2019-01-04 21:44:21 +00:00
// Publish Event so that a Profile Engine can deal with it.
// Note: This technically means that *multiple* Profile Engines could listen to the same ProtocolEngine!
2019-01-21 20:08:03 +00:00
e . eventManager . Publish ( event . NewEvent ( event . EncryptedGroupMessage , map [ event . Field ] string { event . Ciphertext : string ( gm . GetCiphertext ( ) ) , event . Signature : string ( gm . GetSignature ( ) ) } ) )
2019-01-04 21:44:21 +00:00
}
2019-05-15 19:58:57 +00:00
// finishedFetch is a callback function the processes the termination of a fetch channel from a given server
func ( e * engine ) finishedFetch ( server string ) {
2019-04-23 20:30:50 +00:00
e . eventManager . Publish ( event . NewEvent ( event . FinishedFetch , map [ event . Field ] string { event . GroupServer : server } ) )
}
2019-05-15 19:58:57 +00:00
// joinServer manages a new server connection with the given onion address
func ( e * engine ) joinServer ( onion string ) {
2019-05-15 20:12:11 +00:00
e . connectionsManager . ManageServerConnection ( onion , e , e . receiveGroupMessage , e . finishedFetch )
2019-01-04 21:44:21 +00:00
}
2019-05-15 19:58:57 +00:00
// sendMessageToGroup attempts to sent the given message to the given group id.
func ( e * engine ) sendMessageToGroup ( server string , ct [ ] byte , sig [ ] byte ) {
2019-01-04 21:44:21 +00:00
psc := e . connectionsManager . GetPeerServerConnectionForOnion ( server )
if psc == nil {
2019-02-20 20:58:05 +00:00
e . eventManager . Publish ( event . NewEvent ( event . SendMessageToGroupError , map [ event . Field ] string { event . GroupServer : server , event . Signature : string ( sig ) , event . Error : "server is offline or the connection has yet to finalize" } ) )
2019-01-04 21:44:21 +00:00
}
gm := & protocol . GroupMessage {
Ciphertext : ct ,
Signature : sig ,
}
err := psc . SendGroupMessage ( gm )
2019-02-20 20:58:05 +00:00
if err != nil {
e . eventManager . Publish ( event . NewEvent ( event . SendMessageToGroupError , map [ event . Field ] string { event . GroupServer : server , event . Signature : string ( sig ) , event . Error : err . Error ( ) } ) )
}
2019-01-04 21:44:21 +00:00
}
// CwtchPeerInstance encapsulates incoming peer connections
type CwtchPeerInstance struct {
2019-01-23 20:50:53 +00:00
rai * application . Instance
2019-01-04 21:44:21 +00:00
ra * application . RicochetApplication
}
// Init sets up a CwtchPeerInstance
2019-01-23 20:50:53 +00:00
func ( cpi * CwtchPeerInstance ) Init ( rai * application . Instance , ra * application . RicochetApplication ) {
2019-01-04 21:44:21 +00:00
cpi . rai = rai
cpi . ra = ra
}
// CwtchPeerHandler encapsulates handling of incoming CwtchPackets
type CwtchPeerHandler struct {
Onion string
2019-06-05 20:40:55 +00:00
EventBus event . Manager
2019-01-04 21:44:21 +00:00
DataHandler func ( string , [ ] byte ) [ ] byte
}
// HandleGroupInvite handles incoming GroupInvites
func ( cph * CwtchPeerHandler ) HandleGroupInvite ( gci * protocol . GroupChatInvite ) {
log . Debugf ( "Received GroupID from %v %v\n" , cph . Onion , gci . String ( ) )
marshal , err := proto . Marshal ( gci )
if err == nil {
2019-01-22 19:11:25 +00:00
cph . EventBus . Publish ( event . NewEvent ( event . NewGroupInvite , map [ event . Field ] string { event . TimestampReceived : time . Now ( ) . Format ( time . RFC3339Nano ) , event . RemotePeer : cph . Onion , event . GroupInvite : string ( marshal ) } ) )
2019-01-04 21:44:21 +00:00
}
}
// HandlePacket handles the Cwtch cwtchPeer Data Channel
func ( cph * CwtchPeerHandler ) HandlePacket ( data [ ] byte ) [ ] byte {
2019-01-22 19:11:25 +00:00
cph . EventBus . Publish ( event . NewEvent ( event . NewMessageFromPeer , map [ event . Field ] string { event . TimestampReceived : time . Now ( ) . Format ( time . RFC3339Nano ) , event . RemotePeer : cph . Onion , event . Data : string ( data ) } ) )
2019-01-04 21:44:21 +00:00
return [ ] byte { } // TODO remove this
}