From a70413d6aae2b8ffa9446c42170839bf7392c87f Mon Sep 17 00:00:00 2001 From: erinn Date: Wed, 10 Oct 2018 00:07:18 -0700 Subject: [PATCH] initial commit --- README.md | 4 ++++ main.go | 72 +++++++++++++++++++++++++++++++------------------------ smp.go | 21 +++++++--------- 3 files changed, 53 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index e1200ee..ca6be53 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,15 @@ ## sendafriend +WARNING: sendafriend has not been audited and probably contains security vulnerabilities. please do not use it for anything sensitive yet + quickly send files or control signals between machines by piping them on the command line, with bidirectional authentication and metadata resistance via tor (built on cwtch) includes a way to quickly exchange onion addresses using a low-entropy shared secret (using pairwise socialist millionaires' protocol on a bbs) and a contact manager note that we only support data up to 64k right now, sorry :( (a fix for this is in the works) +sendafriend is rated Adults Only because it generates random words sometimes and i haven't read all the words personally + ## installation requires you to have go installed and your gopath configured correctly (sorry) diff --git a/main.go b/main.go index e3e0a11..98f7587 100644 --- a/main.go +++ b/main.go @@ -11,10 +11,15 @@ import ( "time" "path" "os/user" + "strings" + "log" + "io/ioutil" + "sync" + "cwtch.im/cwtch/peer/connections" ) func driftoff() { - time.Sleep(time.Second * 15) + time.Sleep(time.Second * 7) os.Exit(1) } @@ -35,6 +40,14 @@ func addContactEntry(peer libpeer.CwtchPeer, name string, onion string) { peer.GetProfile().SetCustomAttribute(name+"_onion", onion) } +func exitOnEnter() { + fmt.Println("okay, now have your friend type \"sendafriend seek\" and enter the secret") + fmt.Println("or press enter to cancel the game and exit") + reader := bufio.NewReader(os.Stdin) + reader.ReadString('\n') + os.Exit(0) +} + func printHelp() { fmt.Println("usage: sendafriend [command] [options]\nCommands: help, setname, info, list, add, send, receive, hide, seek") } @@ -45,13 +58,15 @@ func main() { os.Exit(1) } - //log.SetFlags(0) - //log.SetOutput(ioutil.Discard) + if os.Getenv("SENDAFRIEND_DEBUG") != "YES" { + log.SetFlags(0) + log.SetOutput(ioutil.Discard) + } // this is a cwtch server used only to make the "pair" command work // it is completely untrusted and only blinded information is passed through it // it can point to any ol cwtch group, as long as both people running "pair" are using the same one - inviteStr := "torv30I0zJgNiUz57gJlxFsp4PUq47WDoKNCL95GEadaLlEE=EsABCiA4MjViZTVjODU0ZDFhNGRhNjM3NmExNTBlYjM2ZjM2NxIgPAl3zAsMfauSZQyiGXlj7u6M6usYN/HqtPD2jafjwTAaOGloZDNlYXdhbjZ2aG1kaDdudDVicXh5aHVlM2k0aXgzb3kyZWZrcHdkaDZyNGptcTNhZXJ0dWlkIkDhF3Ko8nAtEJPvjpCjwFM4py0CeAhYCTc9l9H+Wah8traU15D/JzIHEAKhaDiIzmKqBOECk9Q+G40Y6pXPWKkI" + inviteStr := "torv30I0zJgNiUz57gJlxFsp4PUq47WDoKNCL95GEadaLlEE=EsABCiA1MjQzYjNkNzczMjAwNzc1MDExMDI0MjJkM2NkOGNmZBIgsXZKp350ShZ2l3iqOh4ZhGJmj0Etb5QmkL5iLZno9AQaOGZxcWJkNWxrNzRnbG5zZ292aXVuNm92Z3V0MmNqY252Mm12aWdvamNuc21wd3NucTJ5ZGpmM3FkIkD5x/JNhpLVe982dE9+3mp1IEog6O8yGdrIuUs0Tb6F1Fh1J1zuv1nJBFECuzYyF1yhqNvOn9Z6MatXVi+PKQIO" var dirname, filename string if os.Getenv("SENDAFRIEND_FOLDER") != "" { @@ -202,16 +217,17 @@ func main() { os.Exit(1) } - suggestedSecret, _ := diceware.Generate(1) - fmt.Printf("enter a secret that only the two of you would know [%v]: ", suggestedSecret[0]) + suggestedSecret, _ := diceware.Generate(2) + fmt.Printf("enter a secret that only the two of you would know [%v %v]: ", suggestedSecret[0], suggestedSecret[1]) in := bufio.NewReader(os.Stdin) secret, _ := in.ReadString('\n') secret = secret[:len(secret)-1] if secret == "" { - secret = suggestedSecret[0] + secret = strings.Join(suggestedSecret, " ") } + fmt.Println("okay, now please wait a moment while some magic happens...") ephemeralPeer, err := libpeer.NewCwtchPeer("Alice", "alicepass", "") if err != nil { fmt.Printf("couldn't create an ephemeral onion address: %v\n", err) @@ -226,16 +242,17 @@ func main() { group := ephemeralPeer.GetGroup(groupId) ephemeralPeer.JoinServer(group.GroupServer) - time.Sleep(time.Second * 30) //TODO: can this be shorter? + mp := ephemeralPeer.GetServers() + for ; mp[group.GroupServer] != connections.AUTHENTICATED; mp = ephemeralPeer.GetServers() { + time.Sleep(time.Millisecond * 500) + } err = ephemeralPeer.SendMessageToGroup(groupId, "be gay do crimes") if err != nil { - fmt.Printf("the hide-n-seek server is down or something? try again i guess\n") + fmt.Printf("the hide-n-seek server is down or something? try again i guess; %v\n", err) os.Exit(1) } - fmt.Println("okay, now have your friend type \"sendafriend seek\" and enter the secret") - convos := make(map[string]*Conversation) processData := func(onion string, data []byte) []byte { if _, contains := convos[onion]; !contains { @@ -259,14 +276,8 @@ func main() { return tlv2arr(out) } ephemeralPeer.SetPeerDataHandler(processData) - err = ephemeralPeer.Listen() - if err != nil { - fmt.Printf("error running ephemeral onion: %v\n", err) - } - - fmt.Println("press enter to cancel the game and exit") - reader := bufio.NewReader(os.Stdin) - reader.ReadString('\n') + go ephemeralPeer.Listen() + exitOnEnter() case "seek": if len(os.Args) != 3 { fmt.Fprintf(os.Stderr, "example: sendafriend seek alice\n") @@ -277,6 +288,7 @@ func main() { in := bufio.NewReader(os.Stdin) secret, _ := in.ReadString('\n') secret = secret[:len(secret)-1] + fmt.Println("okay, now please wait a moment while some magic happens...") ephemeralPeer, err := libpeer.NewCwtchPeer("Alice", "alicepass", "") if err != nil { @@ -292,25 +304,23 @@ func main() { group := ephemeralPeer.GetGroup(groupId) ephemeralPeer.JoinServer(group.GroupServer) - time.Sleep(time.Second * 10) //TODO: can this be shorter? + //sadly we cannot listen for messages normally because we want to go in reverse order messages := group.GetTimeline() + for ; len(messages) == 0; messages = group.GetTimeline() { + time.Sleep(time.Second * 3) + } + var waitGroup sync.WaitGroup + fmt.Printf("got advertisements. checking them...") for i := len(messages) - 1; i >= 0; i-- { - fmt.Printf("checking advert %d/%d...\n", len(messages)-i, len(messages)) - //fmt.Printf("%v <%v> %v\n", messages[i].Timestamp, messages[i].PeerID, messages[i].Message) - if messages[i].Message == "be gay do crimes" { - onion, success := doSMP(messages[i].PeerID, secret, os.Args[2], peer.GetProfile().Onion) - if success { - addContactEntry(peer, os.Args[2], onion) - peer.Save() - os.Exit(0) - } else { - fmt.Printf("debug:fail\n") - } + // this is not as scary as it seems because only one of these goroutines will actually use the peer + waitGroup.Add(1) + go doSMP(peer, messages[i].PeerID, secret, os.Args[2]) } } + waitGroup.Wait() } peer.Save() diff --git a/smp.go b/smp.go index 30ddbab..16e959d 100644 --- a/smp.go +++ b/smp.go @@ -61,8 +61,7 @@ import ( "math/big" "os" "strconv" - "time" -) + ) func arr2tlv(data []byte) tlv { var typ, len uint16 @@ -83,7 +82,7 @@ func tlv2arr(t tlv) []byte { return ret } -func doSMP(onion string, secret string, friendname string, longtermonion string) (string, bool) { +func doSMP(longtermpeer libpeer.CwtchPeer, onion string, secret string, friendname string) { var alice Conversation secretInt := new(big.Int) secretInt.SetBytes([]byte(secret)) @@ -95,23 +94,22 @@ func doSMP(onion string, secret string, friendname string, longtermonion string) os.Exit(1) } - var wishandaprayer string - var success bool processData := func(onion string, data []byte) []byte { tlvPacket := arr2tlv(data) out, complete, err := alice.processSMP(tlvPacket) if tlvPacket.typ == 42 { fmt.Printf("found them! adding %v <%s> to contact list\n", friendname, tlvPacket.data) - wishandaprayer = string(tlvPacket.data) - success = true - return nil + + addContactEntry(longtermpeer, os.Args[2], string(tlvPacket.data)) + longtermpeer.Save() + os.Exit(0) } if err != nil { - os.Exit(1) + return nil } else if complete { - finalPacket := tlv{42, uint16(len(longtermonion)), []byte(longtermonion)} + finalPacket := tlv{42, uint16(len(longtermpeer.GetProfile().Onion)), []byte(longtermpeer.GetProfile().Onion)} return tlv2arr(finalPacket) } @@ -124,9 +122,6 @@ func doSMP(onion string, secret string, friendname string, longtermonion string) for i := range tlvList { connection.SendPacket(tlv2arr(tlvList[i])) } - - time.Sleep(time.Minute) - return wishandaprayer, success } type smpFailure string