Moving towards a release candidate.

This commit is contained in:
Sarah Jamie Lewis 2019-08-07 13:08:02 -07:00
parent 5d6514a3de
commit 63d6c1a1aa
13 changed files with 257 additions and 242 deletions

44
.drone.yml Normal file
View File

@ -0,0 +1,44 @@
workspace:
base: /go
path: src/cwtch.im/tapir
pipeline:
fetch:
image: golang
commands:
- wget https://git.openprivacy.ca/openprivacy/buildfiles/raw/master/tor/tor
- wget https://git.openprivacy.ca/openprivacy/buildfiles/raw/master/tor/torrc
- chmod a+x tor
- go list ./... | xargs go get
- go get -u golang.org/x/lint/golint
quality:
image: golang
commands:
- go list ./... | xargs go vet
- go list ./... | xargs golint -set_exit_status
units-tests:
image: golang
commands:
- export PATH=$PATH:/go/src/cwtch.im/tapir
- sh testing/tests.sh
integ-test:
image: golang
commands:
- ./tor -f ./torrc
- sleep 15
- go test -v cwtch.im/tapir/testing
notify-email:
image: drillster/drone-email
host: build.openprivacy.ca
port: 25
skip_verify: true
from: drone@openprivacy.ca
when:
status: [ failure ]
notify-gogs:
image: openpriv/drone-gogs
when:
event: pull_request
status: [ success, changed, failure ]
secrets: [gogs_account_token]
gogs_url: https://git.openprivacy.ca

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
vendor/ vendor/
.idea .idea
/tor/ /tor/
coverage.out

View File

@ -3,5 +3,5 @@ package tapir
// Application defines the interface for all Tapir Applications // Application defines the interface for all Tapir Applications
type Application interface { type Application interface {
NewInstance() Application NewInstance() Application
Init(connection *Connection) Init(connection Connection)
} }

View File

@ -33,8 +33,8 @@ func (ea AuthApp) NewInstance() tapir.Application {
// Init runs the entire AuthApp protocol, at the end of the protocol either the connection is granted AUTH capability // Init runs the entire AuthApp protocol, at the end of the protocol either the connection is granted AUTH capability
// or the connection is closed. // or the connection is closed.
func (ea AuthApp) Init(connection *tapir.Connection) { func (ea AuthApp) Init(connection tapir.Connection) {
longTermPubKey := ed25519.PublicKey(connection.ID.PublicKeyBytes()) longTermPubKey := ed25519.PublicKey(connection.ID().PublicKeyBytes())
epk, esk, _ := ed25519.GenerateKey(rand.Reader) epk, esk, _ := ed25519.GenerateKey(rand.Reader)
ephemeralPublicKey := ed25519.PublicKey(epk) ephemeralPublicKey := ed25519.PublicKey(epk)
ephemeralPrivateKey := ed25519.PrivateKey(esk) ephemeralPrivateKey := ed25519.PrivateKey(esk)
@ -52,13 +52,13 @@ func (ea AuthApp) Init(connection *tapir.Connection) {
} }
// 3DH Handshake // 3DH Handshake
l2e := connection.ID.EDH(remoteAuthMessage.EphemeralPublicKey) l2e := connection.ID().EDH(remoteAuthMessage.EphemeralPublicKey)
e2l := ephemeralIdentity.EDH(remoteAuthMessage.LongTermPublicKey) e2l := ephemeralIdentity.EDH(remoteAuthMessage.LongTermPublicKey)
e2e := ephemeralIdentity.EDH(remoteAuthMessage.EphemeralPublicKey) e2e := ephemeralIdentity.EDH(remoteAuthMessage.EphemeralPublicKey)
// We need to define an order for the result concatenation so that both sides derive the same key. // We need to define an order for the result concatenation so that both sides derive the same key.
var result [96]byte var result [96]byte
if connection.Outbound { if connection.IsOutbound() {
copy(result[0:32], l2e) copy(result[0:32], l2e)
copy(result[32:64], e2l) copy(result[32:64], e2l)
copy(result[64:96], e2e) copy(result[64:96], e2e)
@ -69,15 +69,24 @@ func (ea AuthApp) Init(connection *tapir.Connection) {
} }
connection.SetEncryptionKey(sha3.Sum256(result[:])) connection.SetEncryptionKey(sha3.Sum256(result[:]))
// Wait to Sync // Wait to Sync (we need to ensure that both the Local and Remote server have turned encryption on
// otherwise our next Send will fail.
time.Sleep(time.Second) time.Sleep(time.Second)
// TODO: Replace this with proper transcript // TODO: Replace this with proper transcript primitive
challengeRemote, err := json.Marshal(remoteAuthMessage) challengeRemote, err := json.Marshal(remoteAuthMessage)
if err != nil {
connection.Close()
return
}
challengeLocal, err := json.Marshal(authMessage) challengeLocal, err := json.Marshal(authMessage)
if err != nil {
connection.Close()
return
}
challenge := sha3.New512() challenge := sha3.New512()
if connection.Outbound { if connection.IsOutbound() {
challenge.Write(challengeLocal) challenge.Write(challengeLocal)
challenge.Write(challengeRemote) challenge.Write(challengeRemote)
} else { } else {

83
applications/auth_test.go Normal file
View File

@ -0,0 +1,83 @@
package applications
import (
"crypto/rand"
"encoding/json"
"git.openprivacy.ca/openprivacy/libricochet-go/identity"
"golang.org/x/crypto/ed25519"
"testing"
)
type MockConnection struct {
id identity.Identity
outbound bool
}
func (mc *MockConnection) Init(outbound bool) {
pubkey, privateKey, _ := ed25519.GenerateKey(rand.Reader)
sk := ed25519.PrivateKey(privateKey)
pk := ed25519.PublicKey(pubkey)
mc.id = identity.InitializeV3("", &sk, &pk)
mc.outbound = outbound
return
}
func (MockConnection) Hostname() string {
panic("implement me")
}
func (mc MockConnection) IsOutbound() bool {
return mc.outbound
}
func (mc MockConnection) ID() *identity.Identity {
return &mc.id
}
func (mc MockConnection) Expect() []byte {
longTermPubKey := ed25519.PublicKey(mc.id.PublicKeyBytes())
epk, _, _ := ed25519.GenerateKey(rand.Reader)
ephemeralPublicKey := ed25519.PublicKey(epk)
//ephemeralPrivateKey := ed25519.PrivateKey(esk)
//ephemeralIdentity := identity.InitializeV3("", &ephemeralPrivateKey, &ephemeralPublicKey)
authMessage := AuthMessage{LongTermPublicKey: longTermPubKey, EphemeralPublicKey: ephemeralPublicKey}
serialized, _ := json.Marshal(authMessage)
return serialized
}
func (MockConnection) SetHostname(hostname string) {
panic("implement me")
}
func (MockConnection) HasCapability(name string) bool {
panic("implement me")
}
func (MockConnection) SetCapability(name string) {
panic("implement me")
}
func (MockConnection) SetEncryptionKey(key [32]byte) {
// no op
}
func (MockConnection) Send(message []byte) {
// no op
}
func (MockConnection) Close() {
// no op
}
func (MockConnection) IsClosed() bool {
panic("implement me")
}
func TestAuthApp_Failed(t *testing.T) {
var authApp AuthApp
ai := authApp.NewInstance()
mc := new(MockConnection)
mc.Init(true)
ai.Init(mc)
}

View File

@ -25,7 +25,7 @@ func (ea SimpleApp) NewInstance() tapir.Application {
} }
// Init is run when the connection is first started. // Init is run when the connection is first started.
func (ea SimpleApp) Init(connection *tapir.Connection) { func (ea SimpleApp) Init(connection tapir.Connection) {
// First run the Authentication App // First run the Authentication App
ea.AuthApp.Init(connection) ea.AuthApp.Init(connection)
@ -44,9 +44,8 @@ func CheckConnection(service tapir.Service, hostname string) {
if err == nil { if err == nil {
log.Infof("Authed!") log.Infof("Authed!")
return return
} else {
log.Errorf("Error %v", err)
} }
log.Errorf("Error %v", err)
time.Sleep(time.Second) time.Sleep(time.Second)
} }
} }

View File

@ -34,14 +34,14 @@ func (s *BaseOnionService) Init(acn connectivity.ACN, sk ed25519.PrivateKey, id
// WaitForCapabilityOrClose blocks until the connection has the given capability or the underlying connection is closed // WaitForCapabilityOrClose blocks until the connection has the given capability or the underlying connection is closed
// (through error or user action) // (through error or user action)
func (s *BaseOnionService) WaitForCapabilityOrClose(cid string, name string) (*tapir.Connection, error) { func (s *BaseOnionService) WaitForCapabilityOrClose(cid string, name string) (tapir.Connection, error) {
conn, err := s.GetConnection(cid) conn, err := s.GetConnection(cid)
if err == nil { if err == nil {
for { for {
if conn.HasCapability(name) { if conn.HasCapability(name) {
return conn, nil return conn, nil
} }
if conn.Closed { if conn.IsClosed() {
return nil, errors.New("connection is closed") return nil, errors.New("connection is closed")
} }
time.Sleep(time.Millisecond * 200) time.Sleep(time.Millisecond * 200)
@ -51,12 +51,12 @@ func (s *BaseOnionService) WaitForCapabilityOrClose(cid string, name string) (*t
} }
// GetConnection returns a connection for a given hostname. // GetConnection returns a connection for a given hostname.
func (s *BaseOnionService) GetConnection(hostname string) (*tapir.Connection, error) { func (s *BaseOnionService) GetConnection(hostname string) (tapir.Connection, error) {
var conn *tapir.Connection var conn tapir.Connection
s.connections.Range(func(key, value interface{}) bool { s.connections.Range(func(key, value interface{}) bool {
connection := value.(*tapir.Connection) connection := value.(tapir.Connection)
if connection.Hostname == hostname { if connection.Hostname() == hostname {
if !connection.Closed { if !connection.IsClosed() {
conn = connection conn = connection
return false return false
} }
@ -136,10 +136,11 @@ func (s *BaseOnionService) Listen(app tapir.Application) error {
return err return err
} }
// Shutdown closes the service and ensures that any connections are closed.
func (s *BaseOnionService) Shutdown() { func (s *BaseOnionService) Shutdown() {
s.ls.Close() s.ls.Close()
s.connections.Range(func(key, value interface{}) bool { s.connections.Range(func(key, value interface{}) bool {
connection := value.(*tapir.Connection) connection := value.(tapir.Connection)
connection.Close() connection.Close()
return true return true
}) })

View File

@ -1,192 +0,0 @@
package main
import (
"bytes"
"compress/gzip"
"crypto/rand"
"crypto/sha512"
"cwtch.im/tapir"
"cwtch.im/tapir/applications"
"cwtch.im/tapir/networks/tor"
"cwtch.im/tapir/primitives"
"encoding/hex"
"encoding/json"
"git.openprivacy.ca/openprivacy/libricochet-go/connectivity"
"git.openprivacy.ca/openprivacy/libricochet-go/identity"
"git.openprivacy.ca/openprivacy/libricochet-go/log"
"git.openprivacy.ca/openprivacy/libricochet-go/utils"
"golang.org/x/crypto/ed25519"
"io/ioutil"
"os"
"time"
)
// This example implements a basic notification application which allows peers to notify each other of new messages without downloading
// the entire contents of the server.
// NOTE: Very Incomplete Prototype.
// Notification contains a Topic string and a Message.
type Notification struct {
Topic string // A hex encoded string of the hash of the topic string
Message string
}
// NotificationClient allows publishing and reading from the notifications server
type NotificationClient struct {
applications.AuthApp
connection *tapir.Connection
}
// NewInstance should always return a new instantiation of the application.
func (nc NotificationClient) NewInstance() tapir.Application {
app := new(NotificationClient)
return app
}
// Init is run when the connection is first started.
func (nc *NotificationClient) Init(connection *tapir.Connection) {
// First run the Authentication App
nc.AuthApp.Init(connection)
if connection.HasCapability(applications.AuthCapability) {
nc.connection = connection
}
}
// Publish transforms the given topic string into a hashed ID, and sends the ID along with the message
// NOTE: Server learns the hash of the topic (and therefore can correlate repeated use of the same topic)
func (nc NotificationClient) Publish(topic string, message string) {
log.Debugf("Sending Publish Request")
hashedTopic := sha512.Sum512([]byte(topic))
data, _ := json.Marshal(notificationRequest{RequestType: "Publish", RequestData: map[string]string{"Topic": hex.EncodeToString(hashedTopic[:])}})
nc.connection.Send([]byte(data))
}
// Check returns true if the server might have notifications related to the topic.
// This check reveals nothing about the topic to the server.
func (nc NotificationClient) Check(topic string) bool {
log.Debugf("Sending Filter Request")
// Get an updated bloom filter
data, _ := json.Marshal(notificationRequest{RequestType: "BloomFilter", RequestData: map[string]string{}})
nc.connection.Send(data)
response := nc.connection.Expect()
var bf []primitives.BloomFilter
r, _ := gzip.NewReader(bytes.NewReader(response))
bfb, _ := ioutil.ReadAll(r)
json.Unmarshal(bfb, &bf)
// Check the topic handle in the bloom filter
hashedTopic := sha512.Sum512([]byte(topic))
return bf[time.Now().Hour()].Check(hashedTopic[:])
}
type notificationRequest struct {
RequestType string
RequestData map[string]string
}
// NotificationsServer implements the metadata resistant notifications server
type NotificationsServer struct {
applications.AuthApp
Filter []*primitives.BloomFilter
timeProvider primitives.TimeProvider
}
const DefaultNumberOfBuckets = 24 // 1 per hour of the day
// NewInstance should always return a new instantiation of the application.
func (ns NotificationsServer) NewInstance() tapir.Application {
app := new(NotificationsServer)
app.timeProvider = new(primitives.OSTimeProvider)
app.Filter = make([]*primitives.BloomFilter, DefaultNumberOfBuckets)
for i := range app.Filter {
app.Filter[i] = new(primitives.BloomFilter)
app.Filter[i].Init(1024)
}
return app
}
// Configure overrides the default parameters for the Notification Server
func (ns NotificationsServer) Configure(timeProvider primitives.TimeProvider) {
ns.timeProvider = timeProvider
}
// Init initializes the application.
func (ns NotificationsServer) Init(connection *tapir.Connection) {
// First run the Authentication App
ns.AuthApp.Init(connection)
if connection.HasCapability(applications.AuthCapability) {
for {
request := connection.Expect()
var nr notificationRequest
json.Unmarshal(request, &nr)
log.Debugf("Received Request %v", nr)
switch nr.RequestType {
case "Publish":
log.Debugf("Received Publish Request")
topic := nr.RequestData["Topic"]
// message := nr.RequestData["Message"]
topicID, err := hex.DecodeString(topic)
if err == nil {
currentBucket := ns.timeProvider.GetCurrentTime().Hour()
ns.Filter[currentBucket].Insert(topicID)
}
case "BloomFilter":
log.Debugf("Received Filter Request")
response, _ := json.Marshal(ns.Filter)
var b bytes.Buffer
w := gzip.NewWriter(&b)
w.Write(response)
w.Close()
connection.Send(b.Bytes())
}
}
}
}
func main() {
log.SetLevel(log.LevelDebug)
// Connect to Tor
var acn connectivity.ACN
acn, _ = connectivity.StartTor("./", "")
acn.WaitTillBootstrapped()
// Generate Server Keys
pubkey, privateKey, _ := ed25519.GenerateKey(rand.Reader)
sk := ed25519.PrivateKey(privateKey)
pk := ed25519.PublicKey(pubkey)
id := identity.InitializeV3("server", &sk, &pk)
// Init a Client to Connect to the Server
go client(acn, pubkey)
rm
}
// Client will Connect and launch it's own Echo App goroutine.
func client(acn connectivity.ACN, key ed25519.PublicKey) {
pubkey, privateKey, _ := ed25519.GenerateKey(rand.Reader)
sk := ed25519.PrivateKey(privateKey)
pk := ed25519.PublicKey(pubkey)
id := identity.InitializeV3("client", &sk, &pk)
var client tapir.Service
client = new(tor.BaseOnionService)
client.Init(acn, sk, id)
cid, _ := client.Connect(utils.GetTorV3Hostname(key), new(NotificationClient))
conn, err := client.WaitForCapabilityOrClose(cid, applications.AuthCapability)
if err == nil {
log.Debugf("Client has Auth: %v", conn.HasCapability(applications.AuthCapability))
nc := conn.App.(*NotificationClient)
// Basic Demonstration of Notification
log.Infof("Publishing to #astronomy: %v", nc.Check("#astronomy"))
nc.Publish("#astronomy", "New #Astronomy Post!")
log.Infof("Checking #astronomy: %v", nc.Check("#astronomy"))
}
os.Exit(0)
}

View File

@ -18,7 +18,7 @@ func (bf *BloomFilter) Init(m int16) {
// Hash transforms a message to a set of bit flips // Hash transforms a message to a set of bit flips
// Supports up to m == 65535 // Supports up to m == 65535
func (bf BloomFilter) Hash(msg []byte) []int { func (bf *BloomFilter) Hash(msg []byte) []int {
hash := sha256.Sum256(msg) hash := sha256.Sum256(msg)
pos1a := (int(hash[0]) + int(hash[1]) + int(hash[2]) + int(hash[3])) % 0xFF pos1a := (int(hash[0]) + int(hash[1]) + int(hash[2]) + int(hash[3])) % 0xFF
@ -53,7 +53,7 @@ func (bf *BloomFilter) Insert(msg []byte) {
// Check returns true if the messages might be in the BloomFilter // Check returns true if the messages might be in the BloomFilter
// (No false positives, possible false negatives due to the probabilistic nature of the filter) // (No false positives, possible false negatives due to the probabilistic nature of the filter)
func (bf BloomFilter) Check(msg []byte) bool { func (bf *BloomFilter) Check(msg []byte) bool {
pos := bf.Hash(msg) pos := bf.Hash(msg)
if bf.B[pos[0]] && bf.B[pos[1]] && bf.B[pos[2]] && bf.B[pos[3]] { if bf.B[pos[0]] && bf.B[pos[1]] && bf.B[pos[2]] && bf.B[pos[3]] {
return true return true

View File

@ -15,6 +15,7 @@ type TimeProvider interface {
type OSTimeProvider struct { type OSTimeProvider struct {
} }
// GetCurrentTime returns the time provided by the OS
func (ostp OSTimeProvider) GetCurrentTime() time.Time { func (ostp OSTimeProvider) GetCurrentTime() time.Time {
return time.Now() return time.Now()
} }

View File

@ -18,70 +18,107 @@ type Service interface {
Init(acn connectivity.ACN, privateKey ed25519.PrivateKey, identity identity.Identity) Init(acn connectivity.ACN, privateKey ed25519.PrivateKey, identity identity.Identity)
Connect(hostname string, application Application) (bool, error) Connect(hostname string, application Application) (bool, error)
Listen(application Application) error Listen(application Application) error
GetConnection(connectionID string) (*Connection, error) GetConnection(connectionID string) (Connection, error)
WaitForCapabilityOrClose(connectionID string, capability string) (*Connection, error) WaitForCapabilityOrClose(connectionID string, capability string) (Connection, error)
Shutdown() Shutdown()
} }
// Connection Interface
type Connection interface {
Hostname() string
IsOutbound() bool
ID() *identity.Identity
Expect() []byte
SetHostname(hostname string)
HasCapability(name string) bool
SetCapability(name string)
SetEncryptionKey(key [32]byte)
Send(message []byte)
Close()
IsClosed() bool
}
// Connection defines a Tapir Connection // Connection defines a Tapir Connection
type Connection struct { type connection struct {
Hostname string hostname string
conn net.Conn conn net.Conn
capabilities sync.Map capabilities sync.Map
encrypted bool encrypted bool
key [32]byte key [32]byte
App Application App Application
ID identity.Identity identity *identity.Identity
Outbound bool outbound bool
Closed bool closed bool
MaxLength int MaxLength int
} }
// NewConnection creates a new Connection // NewConnection creates a new Connection
func NewConnection(id identity.Identity, hostname string, outbound bool, conn net.Conn, app Application) *Connection { func NewConnection(id identity.Identity, hostname string, outbound bool, conn net.Conn, app Application) Connection {
connection := new(Connection) connection := new(connection)
connection.Hostname = hostname connection.hostname = hostname
connection.conn = conn connection.conn = conn
connection.App = app connection.App = app
connection.ID = id connection.identity = &id
connection.Outbound = outbound connection.outbound = outbound
connection.MaxLength = 1024 connection.MaxLength = 1024
go connection.App.Init(connection) go connection.App.Init(connection)
return connection return connection
} }
// ID returns an identity.Identity encapsulation (for the purposes of cryptographic protocols)
func (c *connection) ID() *identity.Identity {
return c.identity
}
// Hostname returns the hostname of the connection (if the connection has not been authorized it will return the
// temporary hostname identifier)
func (c *connection) Hostname() string {
return c.hostname
}
// IsOutbound returns true if this caller was the originator of the connection (i.e. the connection was started
// by calling Connect() rather than Accept()
func (c *connection) IsOutbound() bool {
return c.outbound
}
// IsClosed returns true if the connection is closed (connections cannot be reopened)
func (c *connection) IsClosed() bool {
return c.closed
}
// SetHostname sets the hostname on the connection // SetHostname sets the hostname on the connection
func (c *Connection) SetHostname(hostname string) { func (c *connection) SetHostname(hostname string) {
log.Debugf("[%v -- %v] Asserting Remote Hostname: %v", c.ID.Hostname(), c.Hostname, hostname) log.Debugf("[%v -- %v] Asserting Remote Hostname: %v", c.identity.Hostname(), c.hostname, hostname)
c.Hostname = hostname c.hostname = hostname
} }
// SetCapability sets a capability on the connection // SetCapability sets a capability on the connection
func (c *Connection) SetCapability(name string) { func (c *connection) SetCapability(name string) {
log.Debugf("[%v -- %v] Setting Capability %v", c.ID.Hostname(), c.Hostname, name) log.Debugf("[%v -- %v] Setting Capability %v", c.identity.Hostname(), c.hostname, name)
c.capabilities.Store(name, true) c.capabilities.Store(name, true)
} }
// HasCapability checks if the connection has a given capability // HasCapability checks if the connection has a given capability
func (c *Connection) HasCapability(name string) bool { func (c *connection) HasCapability(name string) bool {
_, ok := c.capabilities.Load(name) _, ok := c.capabilities.Load(name)
return ok return ok
} }
// Close forcibly closes the connection // Close forcibly closes the connection
func (c *Connection) Close() { func (c *connection) Close() {
c.conn.Close() c.conn.Close()
} }
// Expect blocks and reads a single Tapir packet , from the connection. // Expect blocks and reads a single Tapir packet , from the connection.
func (c *Connection) Expect() []byte { func (c *connection) Expect() []byte {
buffer := make([]byte, c.MaxLength) buffer := make([]byte, c.MaxLength)
n, err := io.ReadFull(c.conn, buffer) n, err := io.ReadFull(c.conn, buffer)
if n != c.MaxLength || err != nil { 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) log.Errorf("[%v -> %v] Wire Error Reading, Read %d bytes, Error: %v", c.hostname, c.identity.Hostname(), n, err)
c.conn.Close() c.conn.Close()
c.Closed = true c.closed = true
return []byte{} return []byte{}
} }
@ -92,9 +129,9 @@ func (c *Connection) Expect() []byte {
if ok { if ok {
copy(buffer, decrypted) copy(buffer, decrypted)
} else { } else {
log.Errorf("[%v -> %v] Error Decrypting Message On Wire", c.Hostname, c.ID.Hostname()) log.Errorf("[%v -> %v] Error Decrypting Message On Wire", c.hostname, c.identity.Hostname())
c.conn.Close() c.conn.Close()
c.Closed = true c.closed = true
return []byte{} return []byte{}
} }
} }
@ -104,13 +141,13 @@ func (c *Connection) Expect() []byte {
} }
// SetEncryptionKey turns on application-level encryption on the connection using the given key. // SetEncryptionKey turns on application-level encryption on the connection using the given key.
func (c *Connection) SetEncryptionKey(key [32]byte) { func (c *connection) SetEncryptionKey(key [32]byte) {
c.key = key c.key = key
c.encrypted = true c.encrypted = true
} }
// Send writes a given message to a Tapir packet (of 1024 bytes in length). // Send writes a given message to a Tapir packet (of 1024 bytes in length).
func (c *Connection) Send(message []byte) { func (c *connection) Send(message []byte) {
buffer := make([]byte, c.MaxLength) buffer := make([]byte, c.MaxLength)
binary.PutUvarint(buffer[0:2], uint64(len(message))) binary.PutUvarint(buffer[0:2], uint64(len(message)))
@ -121,16 +158,16 @@ func (c *Connection) Send(message []byte) {
if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil { if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil {
// TODO: Surface is Error // TODO: Surface is Error
c.conn.Close() c.conn.Close()
c.Closed = true c.closed = true
} }
// MaxLength - 40 = MaxLength - 24 nonce bytes and 16 auth tag. // MaxLength - 40 = MaxLength - 24 nonce bytes and 16 auth tag.
encrypted := secretbox.Seal(nonce[:], buffer[0:c.MaxLength-40], &nonce, &c.key) encrypted := secretbox.Seal(nonce[:], buffer[0:c.MaxLength-40], &nonce, &c.key)
copy(buffer, encrypted[0:c.MaxLength]) copy(buffer, encrypted[0:c.MaxLength])
} }
log.Debugf("[%v -> %v] Wire Send %x", c.ID.Hostname(), c.Hostname, buffer) log.Debugf("[%v -> %v] Wire Send %x", c.identity.Hostname(), c.hostname, buffer)
_, err := c.conn.Write(buffer) _, err := c.conn.Write(buffer)
if err != nil { if err != nil {
c.conn.Close() c.conn.Close()
c.Closed = true c.closed = true
} }
} }

24
testing/quality.sh Executable file
View File

@ -0,0 +1,24 @@
#!/bin/sh
echo "Checking code quality (you want to see no output here)"
echo ""
echo "Vetting:"
go list ./... | xargs go vet
echo ""
echo "Linting:"
go list ./... | xargs golint
echo "Time to format"
gofmt -l -s -w .
# ineffassign (https://github.com/gordonklaus/ineffassign)
echo "Checking for ineffectual assignment of errors (unchecked errors...)"
ineffassign .
# misspell (https://github.com/client9/misspell/cmd/misspell)
echo "Checking for misspelled words..."
misspell . | grep -v "vendor/" | grep -v "go.sum" | grep -v ".idea"

8
testing/tests.sh Executable file
View File

@ -0,0 +1,8 @@
#!/bin/bash
set -e
pwd
go test ${1} -coverprofile=applications.cover.out -v ./applications
echo "mode: set" > coverage.out && cat *.cover.out | grep -v mode: | sort -r | \
awk '{if($1 != last) {print $0;last=$1}}' >> coverage.out
rm -rf *.cover.out