core: Add protocol from s-rah and accept inbound connections

Start using Sarah's protocol implementation (plus some patches to be
merged there), publish the local onion service, and handle inbound
connections.
This commit is contained in:
John Brooks 2016-09-10 22:35:03 -06:00
parent 65f36de79e
commit cf903a3b7d
4 changed files with 208 additions and 12 deletions

View File

@ -4,16 +4,21 @@ import (
"crypto/rsa" "crypto/rsa"
"encoding/base64" "encoding/base64"
"errors" "errors"
"github.com/special/notricochet/core/utils"
"github.com/yawning/bulb/utils/pkcs1" "github.com/yawning/bulb/utils/pkcs1"
"log" "log"
"sync"
) )
// Identity represents the local user, including their contact address,
// and contains the contacts list.
type Identity struct { type Identity struct {
core *Ricochet core *Ricochet
address string mutex sync.Mutex
privateKey *rsa.PrivateKey
address string
privateKey *rsa.PrivateKey
contactList *ContactList contactList *ContactList
} }
@ -22,19 +27,19 @@ func CreateIdentity(core *Ricochet) (*Identity, error) {
core: core, core: core,
} }
err := me.loadIdentity() if err := me.loadIdentity(); err != nil {
if err != nil { log.Printf("Failed loading identity: %v", err)
log.Printf("Loading identity failed: %v", err)
return nil, err return nil, err
} }
contactList, err := LoadContactList(core) contactList, err := LoadContactList(core)
if err != nil { if err != nil {
log.Printf("Loading contact list failed: %v", err) log.Printf("Failed loading contact list: %v", err)
return nil, err return nil, err
} }
me.contactList = contactList me.contactList = contactList
go me.publishService(me.privateKey)
return me, nil return me, nil
} }
@ -52,19 +57,69 @@ func (me *Identity) loadIdentity() error {
if err != nil { if err != nil {
return err return err
} }
me.address, err = utils.RicochetAddressFromKey(&me.privateKey.PublicKey)
me.address, err = pkcs1.OnionAddr(&me.privateKey.PublicKey)
if err != nil { if err != nil {
return err return err
} else if me.address == "" {
return errors.New("Invalid onion address")
} }
me.address = "ricochet:" + me.address
log.Printf("Loaded identity %s", me.address)
} else {
log.Printf("Initializing new identity")
} }
return nil return nil
} }
func (me *Identity) setPrivateKey(key *rsa.PrivateKey) error {
me.mutex.Lock()
defer me.mutex.Unlock()
if me.privateKey != nil || me.address != "" {
return errors.New("Cannot change private key on identity")
}
// Save key to config
keyData, err := pkcs1.EncodePrivateKeyDER(key)
if err != nil {
return err
}
config := me.core.Config.OpenWrite()
config.Identity.ServiceKey = base64.StdEncoding.EncodeToString(keyData)
config.Save()
// Update Identity
me.address, err = utils.RicochetAddressFromKey(&key.PublicKey)
if err != nil {
return err
}
me.privateKey = key
log.Printf("Created new identity %s", me.address)
return nil
}
// BUG(special): No error handling for failures under publishService
func (me *Identity) publishService(key *rsa.PrivateKey) {
service, listener, err := me.core.Network.NewOnionListener(9878, key)
if err != nil {
log.Printf("Identity listener failed: %v", err)
// XXX handle
return
}
if key == nil {
err := me.setPrivateKey(service.PrivateKey.(*rsa.PrivateKey))
if err != nil {
log.Printf("Setting private key failed: %v", err)
// XXX handle
return
}
}
log.Printf("Identity service published, accepting connections")
go me.core.Protocol.ServeListener(listener)
}
func (me *Identity) Address() string { func (me *Identity) Address() string {
return me.address return me.address
} }
@ -72,3 +127,7 @@ func (me *Identity) Address() string {
func (me *Identity) ContactList() *ContactList { func (me *Identity) ContactList() *ContactList {
return me.contactList return me.contactList
} }
func (me *Identity) PrivateKey() rsa.PrivateKey {
return *me.privateKey
}

View File

@ -154,7 +154,7 @@ func (n *Network) getConnection() *bulb.Conn {
// This function will block until a control connection is available and // This function will block until a control connection is available and
// the service is added or the command has failed. If the control connection // the service is added or the command has failed. If the control connection
// is lost and reconnected, the service will be re-added automatically. // is lost and reconnected, the service will be re-added automatically.
// BUG: Errors that occur after reconnecting cannot be detected. // BUG(special): Errors that occur after reconnecting cannot be detected.
func (n *Network) AddOnionPorts(ports []bulb.OnionPortSpec, key crypto.PrivateKey) (*OnionService, error) { func (n *Network) AddOnionPorts(ports []bulb.OnionPortSpec, key crypto.PrivateKey) (*OnionService, error) {
if key == nil { if key == nil {
// Ask for a new key, force RSA1024 // Ask for a new key, force RSA1024

135
core/protocol.go Normal file
View File

@ -0,0 +1,135 @@
package core
import (
"encoding/asn1"
protocol "github.com/s-rah/go-ricochet"
"log"
"net"
)
type Protocol struct {
core *Ricochet
service *protocol.Ricochet
handler *protocolHandler
}
// Implements protocol.RicochetService
type protocolHandler struct {
p *Protocol
}
func CreateProtocol(core *Ricochet) *Protocol {
p := &Protocol{
core: core,
service: new(protocol.Ricochet),
}
p.handler = &protocolHandler{p: p}
p.service.Init()
return p
}
func (p *Protocol) ServeListener(listener net.Listener) {
p.service.ServeListener(p.handler, listener)
}
// Strangely, ServeListener starts a background routine that watches a channel
// on p.service for new connections and dispatches their events to the handler
// for the listener. API needs a little work here.
func (p *Protocol) Connect(address string) (*protocol.OpenConnection, error) {
oc, err := p.service.Connect(address)
if err != nil {
return nil, err
}
oc.MyHostname = p.core.Identity.Address()[9:]
return oc, nil
}
func (handler *protocolHandler) OnReady() {
log.Printf("protocol: OnReady")
}
func (handler *protocolHandler) OnConnect(oc *protocol.OpenConnection) {
log.Printf("protocol: OnConnect: %v", oc)
if oc.Client {
log.Printf("Connected to %s", oc.OtherHostname)
oc.IsAuthed = true // Outbound connections are authenticated
oc.Authenticate(1)
} else {
// Strip ricochet:
oc.MyHostname = handler.p.core.Identity.Address()[9:]
}
}
// Authentication Management
func (handler *protocolHandler) OnAuthenticationRequest(oc *protocol.OpenConnection, channelID int32, clientCookie [16]byte) {
log.Printf("protocol: OnAuthenticationRequest")
oc.ConfirmAuthChannel(channelID, clientCookie)
}
func (handler *protocolHandler) OnAuthenticationChallenge(oc *protocol.OpenConnection, channelID int32, serverCookie [16]byte) {
log.Printf("protocol: OnAuthenticationChallenge")
privateKey := handler.p.core.Identity.PrivateKey()
publicKeyBytes, _ := asn1.Marshal(privateKey.PublicKey)
oc.SendProof(1, serverCookie, publicKeyBytes, &privateKey)
}
func (handler *protocolHandler) OnAuthenticationProof(oc *protocol.OpenConnection, channelID int32, publicKey []byte, signature []byte, isKnownContact bool) {
result := oc.ValidateProof(channelID, publicKey, signature)
log.Printf("protocol: OnAuthenticationProof, result: %v", result)
oc.SendAuthenticationResult(channelID, result, isKnownContact)
oc.IsAuthed = result
oc.CloseChannel(channelID)
}
func (handler *protocolHandler) OnAuthenticationResult(oc *protocol.OpenConnection, channelID int32, result bool, isKnownContact bool) {
log.Printf("protocol: OnAuthenticationResult, result: %v, known: %v", result, isKnownContact)
oc.IsAuthed = result
}
// Contact Management
func (handler *protocolHandler) IsKnownContact(hostname string) bool {
return true
}
func (handler *protocolHandler) OnContactRequest(oc *protocol.OpenConnection, channelID int32, nick string, message string) {
}
func (handler *protocolHandler) OnContactRequestAck(oc *protocol.OpenConnection, channelID int32, status string) {
}
// Managing Channels
func (handler *protocolHandler) OnOpenChannelRequest(oc *protocol.OpenConnection, channelID int32, channelType string) {
oc.AckOpenChannel(channelID, channelType)
}
func (handler *protocolHandler) OnOpenChannelRequestSuccess(oc *protocol.OpenConnection, channelID int32) {
}
func (handler *protocolHandler) OnChannelClosed(oc *protocol.OpenConnection, channelID int32) {
}
// Chat Messages
func (handler *protocolHandler) OnChatMessage(oc *protocol.OpenConnection, channelID int32, messageID int32, message string) {
}
func (handler *protocolHandler) OnChatMessageAck(oc *protocol.OpenConnection, channelID int32, messageID int32) {
}
// Handle Errors
func (handler *protocolHandler) OnFailedChannelOpen(oc *protocol.OpenConnection, channelID int32, errorType string) {
oc.UnsetChannel(channelID)
}
func (handler *protocolHandler) OnGenericError(oc *protocol.OpenConnection, channelID int32) {
oc.RejectOpenChannel(channelID, "GenericError")
}
func (handler *protocolHandler) OnUnknownTypeError(oc *protocol.OpenConnection, channelID int32) {
oc.RejectOpenChannel(channelID, "UnknownTypeError")
}
func (handler *protocolHandler) OnUnauthorizedError(oc *protocol.OpenConnection, channelID int32) {
oc.RejectOpenChannel(channelID, "UnauthorizedError")
}
func (handler *protocolHandler) OnBadUsageError(oc *protocol.OpenConnection, channelID int32) {
oc.RejectOpenChannel(channelID, "BadUsageError")
}
func (handler *protocolHandler) OnFailedError(oc *protocol.OpenConnection, channelID int32) {
oc.RejectOpenChannel(channelID, "FailedError")
}

View File

@ -3,6 +3,7 @@ package core
type Ricochet struct { type Ricochet struct {
Config *Config Config *Config
Network *Network Network *Network
Protocol *Protocol
Identity *Identity Identity *Identity
} }
@ -10,6 +11,7 @@ func (core *Ricochet) Init(conf *Config) error {
var err error var err error
core.Config = conf core.Config = conf
core.Network = CreateNetwork() core.Network = CreateNetwork()
core.Protocol = CreateProtocol(core)
core.Identity, err = CreateIdentity(core) core.Identity, err = CreateIdentity(core)
if err != nil { if err != nil {
return err return err