diff --git a/connect.go b/connect.go index 3583eee5..79ca0528 100644 --- a/connect.go +++ b/connect.go @@ -1,6 +1,7 @@ package main import ( + "encoding/base64" "encoding/hex" "encoding/json" "fmt" @@ -18,6 +19,7 @@ import ( "time" "github.com/dustin/go-humanize" + "github.com/schollz/messagebox/keypair" "github.com/schollz/peerdiscovery" "github.com/schollz/progressbar" tarinator "github.com/schollz/tarinator-go" @@ -43,6 +45,8 @@ type Connection struct { Wait bool bar *progressbar.ProgressBar rate int + keypair keypair.KeyPair + encryptedPassword string } type FileMetaData struct { @@ -73,6 +77,7 @@ func NewConnection(config *AppConfig) (*Connection, error) { c.Yes = config.Yes c.rate = config.Rate c.Local = config.Local + c.keypair, _ = keypair.New() if c.Local { c.Yes = true @@ -213,6 +218,10 @@ func (c *Connection) Run() error { if err := SplitFile(c.File.Name+".enc", c.NumberOfConnections); err != nil { return err } + // remove the file now since we still have pieces + if err := os.Remove(c.File.Name + ".enc"); err != nil { + return err + } // get file hash var err error @@ -221,14 +230,10 @@ func (c *Connection) Run() error { return err } // get file size - c.File.Size, err = FileSize(c.File.Name + ".enc") + c.File.Size, err = FileSize(c.File.Name) if err != nil { return err } - // remove the file now since we still have pieces - if err := os.Remove(c.File.Name + ".enc"); err != nil { - return err - } // remove compressed archive if c.File.IsDir { @@ -241,16 +246,19 @@ func (c *Connection) Run() error { fmt.Fprintf(os.Stderr, "Code is: %s\n", c.Code) // broadcast local connection from sender - log.Debug("settings payload to ", c.Code) - go func() { - go peerdiscovery.Discover(peerdiscovery.Settings{ - Limit: 1, - TimeLimit: 600 * time.Second, - Delay: 50 * time.Millisecond, - Payload: []byte(c.Code), - }) - runClientError <- c.runClient("localhost") - }() + if c.Server == "" { + log.Debug("settings payload to ", c.Code) + go func() { + go peerdiscovery.Discover(peerdiscovery.Settings{ + Limit: 1, + TimeLimit: 600 * time.Second, + Delay: 50 * time.Millisecond, + Payload: []byte(c.Code), + }) + runClientError <- c.runClient("localhost") + }() + + } } log.Debug("checking code validity") @@ -330,19 +338,19 @@ func (c *Connection) runClient(serverName string) error { log.Error(err) } encryptedMetaData, salt, iv := Encrypt(metaData, c.Code) - sendMessage("s."+c.HashedCode+"."+hex.EncodeToString(encryptedMetaData)+"-"+salt+"-"+iv, connection) + sendMessage("s."+c.keypair.Public+"."+c.HashedCode+"."+hex.EncodeToString(encryptedMetaData)+"-"+salt+"-"+iv, connection) } else { log.Debugf("telling relay (%s): %s", c.Server, "r."+c.Code) if c.Wait { // tell server to wait for sender - sendMessage("r."+c.HashedCode+".0.0.0", connection) + sendMessage("r."+c.keypair.Public+"."+c.HashedCode+".0.0.0", connection) } else { // tell server to cancel if sender doesn't exist - sendMessage("c."+c.HashedCode+".0.0.0", connection) + sendMessage("c."+c.keypair.Public+"."+c.HashedCode+".0.0.0", connection) } } if c.IsSender { // this is a sender - log.Debug("waiting for ok from relay") + log.Debugf("[%d] waiting for ok from relay", id) message = receiveMessage(connection) if message == "timeout" { responses.Lock() @@ -359,10 +367,33 @@ func (c *Connection) runClient(serverName string) error { responses.gotConnectionInUse = true responses.Unlock() } else { - log.Debug("got ok from relay") + // message is IP address, lets check next message + log.Debugf("[%d] got ok from relay: %s", id, message) + publicKeyRecipient := receiveMessage(connection) if id == 0 { - fmt.Fprintf(os.Stderr, "\nSending (->%s)..\n", message) + fmt.Fprintf(os.Stderr, "\nSending (->%s@%s)..\n", publicKeyRecipient, message) + // check if okay again + // TODO + encryptedPassword, err := c.keypair.Encrypt([]byte(RandStringBytesMaskImprSrc(20)), publicKeyRecipient) + if err != nil { + panic(err) + } + // encrypt files + + c.encryptedPassword = base64.StdEncoding.EncodeToString(encryptedPassword) } + log.Debugf("[%d] waiting for 0 thread to encrypt", id) + for { + if c.encryptedPassword != "" { + break + } + time.Sleep(10 * time.Millisecond) + } + log.Debugf("sending encrypted passphrase: %s", c.encryptedPassword) + sendMessage(c.encryptedPassword, connection) + // wait for relay go + receiveMessage(connection) + // wait for pipe to be made time.Sleep(100 * time.Millisecond) // Write data from file @@ -399,6 +430,9 @@ func (c *Connection) runClient(serverName string) error { } else if strings.Split(sendersAddress, ":")[0] == "127.0.0.1" { sendersAddress = strings.Replace(sendersAddress, "127.0.0.1", c.Server, 1) } + // now get public key + publicKeySender := receiveMessage(connection) + // have the main thread ask for the okay if id == 0 { encryptedBytes, err := hex.DecodeString(encryptedData) @@ -436,6 +470,7 @@ func (c *Connection) runClient(serverName string) error { fmt.Fprintf(os.Stderr, "Will not overwrite file!") os.Exit(1) } + fmt.Fprintf(os.Stderr, "incoming file from "+publicKeySender+"\n") getOK := "y" if !c.Yes { getOK = getInput("ok? (y/n): ") @@ -466,6 +501,9 @@ func (c *Connection) runClient(serverName string) error { if !gotOK { sendMessage("not ok", connection) } else { + sendMessage("ok", connection) + c.encryptedPassword = receiveMessage(connection) + log.Debugf("[%d] got encrypted passphrase: %s", id, c.encryptedPassword) sendMessage("ok", connection) log.Debug("receive file") if id == 0 { diff --git a/relay.go b/relay.go index eae9e1db..d960113c 100644 --- a/relay.go +++ b/relay.go @@ -19,6 +19,10 @@ type connectionMap struct { sender map[string]net.Conn metadata map[string]string potentialReceivers map[string]struct{} + rpublicKey map[string]string + spublicKey map[string]string + passphrase map[string]string + receiverReady map[string]bool sync.RWMutex } @@ -64,7 +68,11 @@ func (r *Relay) Run() { r.connections.receiver = make(map[string]net.Conn) r.connections.sender = make(map[string]net.Conn) r.connections.metadata = make(map[string]string) + r.connections.spublicKey = make(map[string]string) + r.connections.rpublicKey = make(map[string]string) + r.connections.passphrase = make(map[string]string) r.connections.potentialReceivers = make(map[string]struct{}) + r.connections.receiverReady = make(map[string]bool) r.connections.Unlock() r.runServer() } @@ -124,12 +132,13 @@ func (r *Relay) clientCommuncation(id int, connection net.Conn) { sendMessage("who?", connection) m := strings.Split(receiveMessage(connection), ".") - if len(m) < 3 { + if len(m) < 4 { logger.Debug("exiting, not enough information") sendMessage("not enough information", connection) return } - connectionType, codePhrase, metaData := m[0], m[1], m[2] + connectionType, publicKey, codePhrase, metaData := m[0], m[1], m[2], m[3] + logger.Debugf("got connection from %s", publicKey) key := codePhrase + "-" + strconv.Itoa(id) switch connectionType { @@ -142,9 +151,11 @@ func (r *Relay) clientCommuncation(id int, connection net.Conn) { r.connections.Lock() r.connections.metadata[key] = metaData r.connections.sender[key] = connection + r.connections.spublicKey[key] = publicKey r.connections.Unlock() // wait for receiver receiversAddress := "" + receiversPublicKey := "" isTimeout := time.Duration(0) for { if CONNECTION_TIMEOUT <= isTimeout { @@ -154,16 +165,38 @@ func (r *Relay) clientCommuncation(id int, connection net.Conn) { r.connections.RLock() if _, ok := r.connections.receiver[key]; ok { receiversAddress = r.connections.receiver[key].RemoteAddr().String() - logger.Debug("got receiver") - r.connections.RUnlock() - break + } + if _, ok := r.connections.rpublicKey[key]; ok { + receiversPublicKey = r.connections.rpublicKey[key] } r.connections.RUnlock() + if receiversAddress != "" && receiversPublicKey != "" { + break + } time.Sleep(100 * time.Millisecond) isTimeout += 100 * time.Millisecond } logger.Debug("telling sender ok") sendMessage(receiversAddress, connection) + sendMessage(receiversPublicKey, connection) + // TODO ASK FOR OKAY HERE TOO + logger.Debug("waiting for encrypted passphrase") + encryptedPassphrase := receiveMessage(connection) + r.connections.Lock() + r.connections.passphrase[key] = encryptedPassphrase + r.connections.Unlock() + + // wait for receiver ready + for { + r.connections.RLock() + if _, ok := r.connections.receiverReady[key]; ok { + r.connections.RUnlock() + break + } + r.connections.RUnlock() + } + // go reciever ready tell sender to go + sendMessage("go", connection) logger.Debug("preparing pipe") r.connections.Lock() con1 := r.connections.sender[key] @@ -192,20 +225,28 @@ func (r *Relay) clientCommuncation(id int, connection net.Conn) { // add as a potential receiver r.connections.Lock() r.connections.potentialReceivers[key] = struct{}{} + r.connections.rpublicKey[key] = publicKey + r.connections.receiver[key] = connection r.connections.Unlock() // wait for sender's metadata sendersAddress := "" + sendersPublicKey := "" for { r.connections.RLock() if _, ok := r.connections.metadata[key]; ok { if _, ok2 := r.connections.sender[key]; ok2 { sendersAddress = r.connections.sender[key].RemoteAddr().String() logger.Debug("got sender meta data") - r.connections.RUnlock() - break } } + if _, ok := r.connections.spublicKey[key]; ok { + sendersPublicKey = r.connections.spublicKey[key] + logger.Debugf("got sender public key: %s", sendersPublicKey) + } r.connections.RUnlock() + if sendersAddress != "" && sendersPublicKey != "" { + break + } if connectionType == "c" { sendMessage("0-0-0-0.0.0.0", connection) // sender is not ready so delete connection @@ -219,16 +260,49 @@ func (r *Relay) clientCommuncation(id int, connection net.Conn) { // send meta data r.connections.RLock() sendMessage(r.connections.metadata[key]+"-"+sendersAddress, connection) + sendMessage(sendersPublicKey, connection) r.connections.RUnlock() + + // now get passphrase + sendersPassphrase := "" + for { + r.connections.RLock() + if _, ok := r.connections.passphrase[key]; ok { + sendersPassphrase = r.connections.passphrase[key] + logger.Debugf("got sender passphrase: %s", sendersPassphrase) + } + r.connections.RUnlock() + if sendersPassphrase != "" { + break + } + } + // check for receiver's consent consent := receiveMessage(connection) logger.Debugf("consent: %s", consent) if consent == "ok" { logger.Debug("got consent") - r.connections.Lock() - r.connections.receiver[key] = connection - r.connections.Unlock() + // wait for encrypted passphrase + encryptedPassphrase := "" + for { + r.connections.RLock() + if _, ok := r.connections.passphrase[key]; ok { + encryptedPassphrase = r.connections.passphrase[key] + logger.Debugf("got passphrase: %s", r.connections.passphrase[key]) + } + r.connections.RUnlock() + if encryptedPassphrase != "" { + break + } + time.Sleep(10 * time.Millisecond) + } + sendMessage(encryptedPassphrase, connection) } + receiveMessage(connection) + time.Sleep(10 * time.Millisecond) + r.connections.Lock() + r.connections.receiverReady[key] = true + r.connections.Unlock() default: logger.Debugf("Got unknown protocol: '%s'", connectionType) } diff --git a/utils.go b/utils.go index 6da0ccba..5b4a99f7 100644 --- a/utils.go +++ b/utils.go @@ -5,9 +5,11 @@ import ( "fmt" "io" "math" + math_rand "math/rand" "net" "os" "strconv" + "time" "github.com/pkg/errors" ) @@ -173,3 +175,32 @@ func GetLocalIP() string { } return bestIP } + +// src is seeds the random generator for generating random strings +var src = math_rand.NewSource(time.Now().UnixNano()) + +const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" +const ( + letterIdxBits = 6 // 6 bits to represent a letter index + letterIdxMask = 1<= 0; { + if remain == 0 { + cache, remain = src.Int63(), letterIdxMax + } + if idx := int(cache & letterIdxMask); idx < len(letterBytes) { + b[i] = letterBytes[idx] + i-- + } + cache >>= letterIdxBits + remain-- + } + + return string(b) +}