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
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)

72
main.go
View File

@ -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()

21
smp.go
View File

@ -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