package tapir import ( "crypto/rand" "encoding/base64" "encoding/binary" "errors" "git.openprivacy.ca/openprivacy/libricochet-go/connectivity" "git.openprivacy.ca/openprivacy/libricochet-go/identity" "git.openprivacy.ca/openprivacy/libricochet-go/log" "golang.org/x/crypto/ed25519" "golang.org/x/crypto/nacl/secretbox" "io" "net" "sync" ) type Service interface { Start(acn connectivity.ACN, privateKey ed25519.PrivateKey, identity identity.Identity) Connect(hostname string, application Application) error Listen(application Application) error } type Connection struct { hostname string conn net.Conn capabilities sync.Map encrypted bool key [32]byte app Application ID identity.Identity Outbound bool } func NewConnection(id identity.Identity, hostname string, outbound bool, conn net.Conn, app Application) *Connection { connection := new(Connection) connection.hostname = hostname connection.conn = conn connection.app = app connection.ID = id connection.Outbound = outbound go connection.app.Init(connection) return connection } func (c *Connection) SetHostname(hostname string) { log.Debugf("[%v -- %v] Asserting Remote Hostname: %v", c.ID.Hostname(), c.hostname, hostname) c.hostname = hostname } func (c *Connection) SetCapability(name string) { log.Debugf("[%v -- %v] Setting Capability %v", c.ID.Hostname(), c.hostname, name) c.capabilities.Store(name, true) } func (c *Connection) HasCapability(name string) bool { _, ok := c.capabilities.Load(name) return ok } func (c *Connection) Expect() []byte { buffer := make([]byte, 1024) // TODO handle ERROR io.ReadFull(c.conn, buffer) //log.Debugf("[%v -> %v] Wire Receive: %x", c.hostname, c.ID.Hostname(), buffer) if c.encrypted { var decryptNonce [24]byte copy(decryptNonce[:], buffer[:24]) decrypted, ok := secretbox.Open(nil, buffer[24:], &decryptNonce, &c.key) if ok { copy(buffer, decrypted) } else { log.Errorf("[%v -> %v] Error Decrypting Message On Wire", c.hostname, c.ID.Hostname()) return []byte{} } } len, _ := binary.Uvarint(buffer[0:2]) return buffer[2 : len+2] } func (c *Connection) SetEncryptionKey(key [32]byte) { c.key = key c.encrypted = true } func (c *Connection) Send(message []byte) { maxLength := 1024 buffer := make([]byte, maxLength) binary.PutUvarint(buffer[0:2], uint64(len(message))) copy(buffer[2:], message) if c.encrypted { var nonce [24]byte if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil { panic(err) } // MaxLength - 40 = MaxLength - 24 nonce bytes and 16 auth tag. encrypted := secretbox.Seal(nonce[:], buffer[0:maxLength-40], &nonce, &c.key) copy(buffer, encrypted[0:1024]) } //log.Debugf("[%v -> %v] Wire Send %x", c.ID.Hostname(), c.hostname, buffer) c.conn.Write(buffer) } type BaseOnionService struct { connections sync.Map acn connectivity.ACN id identity.Identity privateKey ed25519.PrivateKey } func (s *BaseOnionService) Start(acn connectivity.ACN, sk ed25519.PrivateKey, id identity.Identity) { // run add onion // get listen context s.acn = acn s.id = id s.privateKey = sk } func (s *BaseOnionService) GetConnection(hostname string) error { return errors.New("no connection found") } func (s *BaseOnionService) Connect(hostname string, app Application) error { // connects to a remote server // spins off to a connection struct log.Debugf("Connecting to %v", hostname) conn, _, err := s.acn.Open(hostname) if err == nil { log.Debugf("Connected to %v", hostname) s.connections.Store(hostname, NewConnection(s.id, hostname, true, conn, app.NewInstance())) return nil } log.Debugf("Error connecting to %v %v", hostname, err) return err } func (s *BaseOnionService) Listen(app Application) error { // accepts a new connection // spins off to a connection struct ls, err := s.acn.Listen(s.privateKey, 9878) log.Debugf("Starting a service on %v ", ls.AddressFull()) if err == nil { for { conn, err := ls.Accept() if err == nil { id := make([]byte, 10) rand.Read(id) tempHostname := "unknown-" + base64.StdEncoding.EncodeToString(id) log.Debugf("Accepted connection from %v", tempHostname) s.connections.Store(tempHostname, NewConnection(s.id, tempHostname, false, conn, app.NewInstance())) } else { ls.Close() log.Debugf("Error accepting connection %v", err) return err } } } log.Debugf("Error listening to connection %v", err) return err }