initial commit

This commit is contained in:
erinn 2018-10-10 00:07:18 -07:00
parent 0af046784f
commit a70413d6aa
3 changed files with 53 additions and 44 deletions

View File

@ -1,11 +1,15 @@
## sendafriend ## 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) 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 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) 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 ## installation
requires you to have go installed and your gopath configured correctly (sorry) requires you to have go installed and your gopath configured correctly (sorry)

72
main.go
View File

@ -11,10 +11,15 @@ import (
"time" "time"
"path" "path"
"os/user" "os/user"
"strings"
"log"
"io/ioutil"
"sync"
"cwtch.im/cwtch/peer/connections"
) )
func driftoff() { func driftoff() {
time.Sleep(time.Second * 15) time.Sleep(time.Second * 7)
os.Exit(1) os.Exit(1)
} }
@ -35,6 +40,14 @@ func addContactEntry(peer libpeer.CwtchPeer, name string, onion string) {
peer.GetProfile().SetCustomAttribute(name+"_onion", onion) 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() { func printHelp() {
fmt.Println("usage: sendafriend [command] [options]\nCommands: help, setname, info, list, add, send, receive, hide, seek") 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) os.Exit(1)
} }
//log.SetFlags(0) if os.Getenv("SENDAFRIEND_DEBUG") != "YES" {
//log.SetOutput(ioutil.Discard) log.SetFlags(0)
log.SetOutput(ioutil.Discard)
}
// this is a cwtch server used only to make the "pair" command work // 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 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 // 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 var dirname, filename string
if os.Getenv("SENDAFRIEND_FOLDER") != "" { if os.Getenv("SENDAFRIEND_FOLDER") != "" {
@ -202,16 +217,17 @@ func main() {
os.Exit(1) os.Exit(1)
} }
suggestedSecret, _ := diceware.Generate(1) suggestedSecret, _ := diceware.Generate(2)
fmt.Printf("enter a secret that only the two of you would know [%v]: ", suggestedSecret[0]) fmt.Printf("enter a secret that only the two of you would know [%v %v]: ", suggestedSecret[0], suggestedSecret[1])
in := bufio.NewReader(os.Stdin) in := bufio.NewReader(os.Stdin)
secret, _ := in.ReadString('\n') secret, _ := in.ReadString('\n')
secret = secret[:len(secret)-1] secret = secret[:len(secret)-1]
if secret == "" { 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", "") ephemeralPeer, err := libpeer.NewCwtchPeer("Alice", "alicepass", "")
if err != nil { if err != nil {
fmt.Printf("couldn't create an ephemeral onion address: %v\n", err) fmt.Printf("couldn't create an ephemeral onion address: %v\n", err)
@ -226,16 +242,17 @@ func main() {
group := ephemeralPeer.GetGroup(groupId) group := ephemeralPeer.GetGroup(groupId)
ephemeralPeer.JoinServer(group.GroupServer) 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") err = ephemeralPeer.SendMessageToGroup(groupId, "be gay do crimes")
if err != nil { 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) os.Exit(1)
} }
fmt.Println("okay, now have your friend type \"sendafriend seek\" and enter the secret")
convos := make(map[string]*Conversation) convos := make(map[string]*Conversation)
processData := func(onion string, data []byte) []byte { processData := func(onion string, data []byte) []byte {
if _, contains := convos[onion]; !contains { if _, contains := convos[onion]; !contains {
@ -259,14 +276,8 @@ func main() {
return tlv2arr(out) return tlv2arr(out)
} }
ephemeralPeer.SetPeerDataHandler(processData) ephemeralPeer.SetPeerDataHandler(processData)
err = ephemeralPeer.Listen() go ephemeralPeer.Listen()
if err != nil { exitOnEnter()
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')
case "seek": case "seek":
if len(os.Args) != 3 { if len(os.Args) != 3 {
fmt.Fprintf(os.Stderr, "example: sendafriend seek alice\n") fmt.Fprintf(os.Stderr, "example: sendafriend seek alice\n")
@ -277,6 +288,7 @@ func main() {
in := bufio.NewReader(os.Stdin) in := bufio.NewReader(os.Stdin)
secret, _ := in.ReadString('\n') secret, _ := in.ReadString('\n')
secret = secret[:len(secret)-1] secret = secret[:len(secret)-1]
fmt.Println("okay, now please wait a moment while some magic happens...")
ephemeralPeer, err := libpeer.NewCwtchPeer("Alice", "alicepass", "") ephemeralPeer, err := libpeer.NewCwtchPeer("Alice", "alicepass", "")
if err != nil { if err != nil {
@ -292,25 +304,23 @@ func main() {
group := ephemeralPeer.GetGroup(groupId) group := ephemeralPeer.GetGroup(groupId)
ephemeralPeer.JoinServer(group.GroupServer) 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() 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-- { 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" { if messages[i].Message == "be gay do crimes" {
onion, success := doSMP(messages[i].PeerID, secret, os.Args[2], peer.GetProfile().Onion) // this is not as scary as it seems because only one of these goroutines will actually use the peer
if success { waitGroup.Add(1)
addContactEntry(peer, os.Args[2], onion) go doSMP(peer, messages[i].PeerID, secret, os.Args[2])
peer.Save()
os.Exit(0)
} else {
fmt.Printf("debug:fail\n")
}
} }
} }
waitGroup.Wait()
} }
peer.Save() peer.Save()

21
smp.go
View File

@ -61,8 +61,7 @@ import (
"math/big" "math/big"
"os" "os"
"strconv" "strconv"
"time" )
)
func arr2tlv(data []byte) tlv { func arr2tlv(data []byte) tlv {
var typ, len uint16 var typ, len uint16
@ -83,7 +82,7 @@ func tlv2arr(t tlv) []byte {
return ret 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 var alice Conversation
secretInt := new(big.Int) secretInt := new(big.Int)
secretInt.SetBytes([]byte(secret)) secretInt.SetBytes([]byte(secret))
@ -95,23 +94,22 @@ func doSMP(onion string, secret string, friendname string, longtermonion string)
os.Exit(1) os.Exit(1)
} }
var wishandaprayer string
var success bool
processData := func(onion string, data []byte) []byte { processData := func(onion string, data []byte) []byte {
tlvPacket := arr2tlv(data) tlvPacket := arr2tlv(data)
out, complete, err := alice.processSMP(tlvPacket) out, complete, err := alice.processSMP(tlvPacket)
if tlvPacket.typ == 42 { if tlvPacket.typ == 42 {
fmt.Printf("found them! adding %v <%s> to contact list\n", friendname, tlvPacket.data) fmt.Printf("found them! adding %v <%s> to contact list\n", friendname, tlvPacket.data)
wishandaprayer = string(tlvPacket.data)
success = true addContactEntry(longtermpeer, os.Args[2], string(tlvPacket.data))
return nil longtermpeer.Save()
os.Exit(0)
} }
if err != nil { if err != nil {
os.Exit(1) return nil
} else if complete { } 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) return tlv2arr(finalPacket)
} }
@ -124,9 +122,6 @@ func doSMP(onion string, secret string, friendname string, longtermonion string)
for i := range tlvList { for i := range tlvList {
connection.SendPacket(tlv2arr(tlvList[i])) connection.SendPacket(tlv2arr(tlvList[i]))
} }
time.Sleep(time.Minute)
return wishandaprayer, success
} }
type smpFailure string type smpFailure string