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"
"encoding/base64"
"errors"
"github.com/special/notricochet/core/utils"
"github.com/yawning/bulb/utils/pkcs1"
"log"
"sync"
)
// Identity represents the local user, including their contact address,
// and contains the contacts list.
type Identity struct {
core *Ricochet
address string
privateKey *rsa.PrivateKey
mutex sync.Mutex
address string
privateKey *rsa.PrivateKey
contactList *ContactList
}
@ -22,19 +27,19 @@ func CreateIdentity(core *Ricochet) (*Identity, error) {
core: core,
}
err := me.loadIdentity()
if err != nil {
log.Printf("Loading identity failed: %v", err)
if err := me.loadIdentity(); err != nil {
log.Printf("Failed loading identity: %v", err)
return nil, err
}
contactList, err := LoadContactList(core)
if err != nil {
log.Printf("Loading contact list failed: %v", err)
log.Printf("Failed loading contact list: %v", err)
return nil, err
}
me.contactList = contactList
go me.publishService(me.privateKey)
return me, nil
}
@ -52,19 +57,69 @@ func (me *Identity) loadIdentity() error {
if err != nil {
return err
}
me.address, err = pkcs1.OnionAddr(&me.privateKey.PublicKey)
me.address, err = utils.RicochetAddressFromKey(&me.privateKey.PublicKey)
if err != nil {
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
}
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 {
return me.address
}
@ -72,3 +127,7 @@ func (me *Identity) Address() string {
func (me *Identity) ContactList() *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
// the service is added or the command has failed. If the control connection
// 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) {
if key == nil {
// 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 {
Config *Config
Network *Network
Protocol *Protocol
Identity *Identity
}
@ -10,6 +11,7 @@ func (core *Ricochet) Init(conf *Config) error {
var err error
core.Config = conf
core.Network = CreateNetwork()
core.Protocol = CreateProtocol(core)
core.Identity, err = CreateIdentity(core)
if err != nil {
return err