package tapir import ( "crypto/rand" "encoding/binary" "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" ) // Service defines the interface for a Tapir Service type Service interface { Init(acn connectivity.ACN, privateKey ed25519.PrivateKey, identity identity.Identity) Connect(hostname string, application Application) (bool, error) Listen(application Application) error GetConnection(connectionID string) (*Connection, error) WaitForCapabilityOrClose(connectionID string, capability string) (*Connection, error) Shutdown() } // Connection defines a Tapir Connection type Connection struct { Hostname string conn net.Conn capabilities sync.Map encrypted bool key [32]byte App Application ID identity.Identity Outbound bool Closed bool MaxLength int } // NewConnection creates a new Connection 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 connection.MaxLength = 1024 go connection.App.Init(connection) return connection } // SetHostname sets the hostname on the connection func (c *Connection) SetHostname(hostname string) { log.Debugf("[%v -- %v] Asserting Remote Hostname: %v", c.ID.Hostname(), c.Hostname, hostname) c.Hostname = hostname } // SetCapability sets a capability on the connection func (c *Connection) SetCapability(name string) { log.Debugf("[%v -- %v] Setting Capability %v", c.ID.Hostname(), c.Hostname, name) c.capabilities.Store(name, true) } // HasCapability checks if the connection has a given capability func (c *Connection) HasCapability(name string) bool { _, ok := c.capabilities.Load(name) return ok } // Close forcibly closes the connection func (c *Connection) Close() { c.conn.Close() } // Expect blocks and reads a single Tapir packet , from the connection. func (c *Connection) Expect() []byte { buffer := make([]byte, c.MaxLength) n, err := io.ReadFull(c.conn, buffer) if n != c.MaxLength || err != nil { log.Errorf("[%v -> %v] Wire Error Reading, Read %d bytes, Error: %v", c.Hostname, c.ID.Hostname(), n, err) c.conn.Close() c.Closed = true return []byte{} } 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()) c.conn.Close() c.Closed = true return []byte{} } } len, _ := binary.Uvarint(buffer[0:2]) //cplog.Debugf("[%v -> %v] Wire Receive: (%d) %x", c.hostname, c.ID.Hostname(), len, buffer) return buffer[2 : len+2] } // SetEncryptionKey turns on application-level encryption on the connection using the given key. func (c *Connection) SetEncryptionKey(key [32]byte) { c.key = key c.encrypted = true } // Send writes a given message to a Tapir packet (of 1024 bytes in length). func (c *Connection) Send(message []byte) { buffer := make([]byte, c.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 { // TODO: Surface is Error c.conn.Close() c.Closed = true } // MaxLength - 40 = MaxLength - 24 nonce bytes and 16 auth tag. encrypted := secretbox.Seal(nonce[:], buffer[0:c.MaxLength-40], &nonce, &c.key) copy(buffer, encrypted[0:c.MaxLength]) } log.Debugf("[%v -> %v] Wire Send %x", c.ID.Hostname(), c.Hostname, buffer) _, err := c.conn.Write(buffer) if err != nil { c.conn.Close() c.Closed = true } }