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:
parent
65f36de79e
commit
cf903a3b7d
|
@ -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
|
||||||
|
|
||||||
|
mutex sync.Mutex
|
||||||
|
|
||||||
address string
|
address string
|
||||||
privateKey *rsa.PrivateKey
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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")
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue