sendafriend/main.go

322 lines
9.2 KiB
Go
Raw Normal View History

2018-10-07 02:21:20 +00:00
package main
import (
2018-10-09 23:49:20 +00:00
"bufio"
"cwtch.im/cwtch/model"
2018-10-07 02:21:20 +00:00
libpeer "cwtch.im/cwtch/peer"
"fmt"
"github.com/sethvargo/go-diceware/diceware"
2018-10-10 01:23:27 +00:00
"math/big"
2018-10-09 23:49:20 +00:00
"os"
"time"
2018-10-10 01:23:27 +00:00
"path"
"os/user"
2018-10-10 07:07:18 +00:00
"strings"
"log"
"io/ioutil"
"sync"
"cwtch.im/cwtch/peer/connections"
2018-10-07 02:21:20 +00:00
)
func driftoff() {
2018-10-10 07:07:18 +00:00
time.Sleep(time.Second * 7)
2018-10-07 02:21:20 +00:00
os.Exit(1)
}
func addContactEntry(peer libpeer.CwtchPeer, name string, onion string) {
pp := model.PublicProfile{
name,
nil,
false,
false,
onion}
if peer == nil {
fmt.Printf("peer is nil?!?!?\n")
}
peer.GetProfile().Contacts[onion] = &pp
peer.GetProfile().SetCustomAttribute(onion+"_name", name)
peer.GetProfile().SetCustomAttribute(name+"_onion", onion)
}
2018-10-10 07:07:18 +00:00
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)
}
2018-10-07 02:21:20 +00:00
func printHelp() {
fmt.Println("usage: sendafriend [command] [options]\nCommands: help, setname, info, list, add, send, receive, hide, seek")
}
func main() {
if len(os.Args) < 2 {
printHelp()
os.Exit(1)
}
2018-10-10 07:07:18 +00:00
if os.Getenv("SENDAFRIEND_DEBUG") != "YES" {
log.SetFlags(0)
log.SetOutput(ioutil.Discard)
}
2018-10-10 01:23:27 +00:00
// 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
2018-10-10 07:07:18 +00:00
inviteStr := "torv30I0zJgNiUz57gJlxFsp4PUq47WDoKNCL95GEadaLlEE=EsABCiA1MjQzYjNkNzczMjAwNzc1MDExMDI0MjJkM2NkOGNmZBIgsXZKp350ShZ2l3iqOh4ZhGJmj0Etb5QmkL5iLZno9AQaOGZxcWJkNWxrNzRnbG5zZ292aXVuNm92Z3V0MmNqY252Mm12aWdvamNuc21wd3NucTJ5ZGpmM3FkIkD5x/JNhpLVe982dE9+3mp1IEog6O8yGdrIuUs0Tb6F1Fh1J1zuv1nJBFECuzYyF1yhqNvOn9Z6MatXVi+PKQIO"
2018-10-07 02:21:20 +00:00
2018-10-10 01:23:27 +00:00
var dirname, filename string
if os.Getenv("SENDAFRIEND_FOLDER") != "" {
dirname = os.Getenv("SENDAFRIEND_FOLDER")
filename = path.Join(dirname, "identity.private")
} else {
usr, err := user.Current()
if err != nil {
fmt.Printf("\nerror: could not load current user: %v\n", err)
os.Exit(1)
}
dirname = path.Join(usr.HomeDir, ".sendafriend")
filename = path.Join(dirname, "identity.private")
}
os.MkdirAll(dirname, 0700)
peer, err := libpeer.LoadCwtchPeer(filename, "be gay do crime")
2018-10-07 02:21:20 +00:00
if err != nil {
2018-10-10 01:23:27 +00:00
fmt.Println("couldn't load your config file, attempting to create a new one now")
2018-10-07 02:21:20 +00:00
names, err := diceware.Generate(1)
2018-10-10 01:23:27 +00:00
peer, err = libpeer.NewCwtchPeer(names[0], "be gay do crime", filename)
2018-10-07 02:21:20 +00:00
if err != nil {
fmt.Println("couldn't create one either :( exiting")
os.Exit(1)
}
}
switch os.Args[1] {
default:
printHelp()
case "help":
printHelp()
case "iam":
if len(os.Args) != 3 {
fmt.Println("example: sendafriend iam alice")
os.Exit(1)
}
peer.GetProfile().Name = os.Args[2]
fmt.Printf("hi, %v!\n", os.Args[2])
case "setname":
if len(os.Args) == 3 {
fmt.Println("example: sendafriend setname alice xyz")
os.Exit(1)
}
2018-10-09 23:49:20 +00:00
_, exists := peer.GetProfile().GetCustomAttribute(os.Args[3] + "_name")
2018-10-07 02:21:20 +00:00
if !exists {
fmt.Printf("um you don't have anyone with that onion address in your list")
os.Exit(1)
}
peer.GetProfile().SetCustomAttribute(os.Args[3]+"_name", os.Args[2])
peer.GetProfile().SetCustomAttribute(os.Args[2]+"_onion", os.Args[3])
//todo: unset old one
fmt.Printf("okay, i'll remember that %v is named %v\n", os.Args[3], os.Args[2])
case "info":
2018-10-09 23:49:20 +00:00
fmt.Printf("your name is currently %v and your address is currently %v\n", peer.GetProfile().Name, peer.GetProfile().Onion)
2018-10-07 02:21:20 +00:00
case "list":
contacts := peer.GetContacts()
for i := range contacts {
attr, _ := peer.GetProfile().GetCustomAttribute(contacts[i] + "_name")
fmt.Printf("%v <%v>\n", attr, contacts[i])
}
case "add":
if len(os.Args) != 4 {
fmt.Println("example: sendafriend add bob xyz")
os.Exit(1)
}
2018-10-09 23:49:20 +00:00
_, exists := peer.GetProfile().GetCustomAttribute(os.Args[3] + "_name")
2018-10-07 02:21:20 +00:00
if exists {
fmt.Printf("woah you already have someone named %v\n", os.Args[3])
os.Exit(1)
}
addContactEntry(peer, os.Args[2], os.Args[3])
fmt.Printf("okay, added %v <%v>\n", os.Args[2], os.Args[3])
case "send":
if len(os.Args) != 3 {
fmt.Println("example: sendafriend send bob < file.txt")
os.Exit(1)
}
2018-10-09 23:49:20 +00:00
onion, exists := peer.GetProfile().GetCustomAttribute(os.Args[2] + "_onion")
2018-10-07 02:21:20 +00:00
if !exists {
fmt.Printf("you don't seem to have a contact named %v\n", os.Args[2])
}
processData := func(onion string, data []byte) []byte {
return nil
}
peer.SetPeerDataHandler(processData)
fmt.Printf("connecting to %v...\n", os.Args[2])
connection := peer.PeerWithOnion(onion)
fmt.Printf("sending data...\n")
reader := bufio.NewReader(os.Stdin)
2018-10-09 23:49:20 +00:00
packet := make([]byte, 65531)
2018-10-07 02:21:20 +00:00
br, _ := reader.Read(packet)
2018-10-10 01:23:27 +00:00
if br > 65530 {
fmt.Printf("sorry but i can't send more than 65530 bytes at once right now :( errorinn is working on it!\n")
os.Exit(1)
}
2018-10-07 02:21:20 +00:00
connection.SendPacket(packet[:br])
case "receive":
if len(os.Args) != 3 {
fmt.Fprintf(os.Stderr, "example: sendafriend receive bob > file.txt\n")
os.Exit(1)
}
2018-10-09 23:49:20 +00:00
verifyOnion, exists := peer.GetProfile().GetCustomAttribute(os.Args[2] + "_onion")
2018-10-07 02:21:20 +00:00
if !exists {
fmt.Fprintf(os.Stderr, "hmm you don't have a contact named %v\n", os.Args[2])
os.Exit(1)
}
processData := func(onion string, data []byte) []byte {
if onion != verifyOnion {
fmt.Fprintf(os.Stderr, "woah, got some unauthenticated data. discarding it!\n")
os.Exit(1)
}
fmt.Printf("%s", data)
os.Exit(0)
return nil
}
peer.SetPeerDataHandler(processData)
fmt.Fprintf(os.Stderr, "waiting for %v to send...\n", os.Args[2])
2018-10-09 23:49:20 +00:00
err = peer.Listen()
if err != nil {
fmt.Printf("error listening for connections: %v\n", err)
}
2018-10-07 02:21:20 +00:00
case "hide":
if len(os.Args) != 3 {
fmt.Fprintf(os.Stderr, "example: sendafriend hide bob\n")
os.Exit(1)
}
2018-10-10 07:07:18 +00:00
suggestedSecret, _ := diceware.Generate(2)
fmt.Printf("enter a secret that only the two of you would know [%v %v]: ", suggestedSecret[0], suggestedSecret[1])
2018-10-07 02:21:20 +00:00
in := bufio.NewReader(os.Stdin)
secret, _ := in.ReadString('\n')
2018-10-09 23:49:20 +00:00
secret = secret[:len(secret)-1]
2018-10-07 02:21:20 +00:00
if secret == "" {
2018-10-10 07:07:18 +00:00
secret = strings.Join(suggestedSecret, " ")
2018-10-07 02:21:20 +00:00
}
2018-10-10 07:07:18 +00:00
fmt.Println("okay, now please wait a moment while some magic happens...")
2018-10-07 02:21:20 +00:00
ephemeralPeer, err := libpeer.NewCwtchPeer("Alice", "alicepass", "")
if err != nil {
fmt.Printf("couldn't create an ephemeral onion address: %v\n", err)
os.Exit(1)
}
groupId, err := ephemeralPeer.ImportGroup(inviteStr)
if err != nil {
fmt.Printf("couldn't configure the bbs settings: %v\n", err)
os.Exit(1)
}
group := ephemeralPeer.GetGroup(groupId)
ephemeralPeer.JoinServer(group.GroupServer)
2018-10-10 07:07:18 +00:00
mp := ephemeralPeer.GetServers()
for ; mp[group.GroupServer] != connections.AUTHENTICATED; mp = ephemeralPeer.GetServers() {
time.Sleep(time.Millisecond * 500)
}
2018-10-07 02:21:20 +00:00
err = ephemeralPeer.SendMessageToGroup(groupId, "be gay do crimes")
if err != nil {
2018-10-10 07:07:18 +00:00
fmt.Printf("the hide-n-seek server is down or something? try again i guess; %v\n", err)
2018-10-07 02:21:20 +00:00
os.Exit(1)
}
convos := make(map[string]*Conversation)
processData := func(onion string, data []byte) []byte {
if _, contains := convos[onion]; !contains {
convos[onion] = new(Conversation)
secretInt := new(big.Int)
secretInt.SetBytes([]byte(secret))
convos[onion].smp.secret = secretInt
}
tlvPacket := arr2tlv(data)
if tlvPacket.typ == 42 {
fmt.Printf("you've been found! adding %v <%s> to contact list\n", os.Args[2], tlvPacket.data)
addContactEntry(peer, os.Args[2], string(tlvPacket.data))
peer.Save()
finalPacket := tlv{42, uint16(len(peer.GetProfile().Onion)), []byte(peer.GetProfile().Onion)}
go driftoff()
return tlv2arr(finalPacket)
}
out, _, _ := convos[onion].processSMP(tlvPacket)
return tlv2arr(out)
}
ephemeralPeer.SetPeerDataHandler(processData)
2018-10-10 07:07:18 +00:00
go ephemeralPeer.Listen()
exitOnEnter()
2018-10-07 02:21:20 +00:00
case "seek":
if len(os.Args) != 3 {
fmt.Fprintf(os.Stderr, "example: sendafriend seek alice\n")
os.Exit(1)
}
fmt.Print("enter a secret that only the two of you would know: ")
in := bufio.NewReader(os.Stdin)
secret, _ := in.ReadString('\n')
2018-10-09 23:49:20 +00:00
secret = secret[:len(secret)-1]
2018-10-10 07:07:18 +00:00
fmt.Println("okay, now please wait a moment while some magic happens...")
2018-10-07 02:21:20 +00:00
ephemeralPeer, err := libpeer.NewCwtchPeer("Alice", "alicepass", "")
if err != nil {
fmt.Printf("couldn't create an ephemeral onion address: %v\n", err)
os.Exit(1)
}
groupId, err := ephemeralPeer.ImportGroup(inviteStr)
if err != nil {
fmt.Printf("couldn't configure the bbs settings: %v\n", err)
os.Exit(1)
}
group := ephemeralPeer.GetGroup(groupId)
ephemeralPeer.JoinServer(group.GroupServer)
2018-10-10 07:07:18 +00:00
//sadly we cannot listen for messages normally because we want to go in reverse order
2018-10-07 02:21:20 +00:00
messages := group.GetTimeline()
2018-10-10 07:07:18 +00:00
for ; len(messages) == 0; messages = group.GetTimeline() {
time.Sleep(time.Second * 3)
}
2018-10-07 02:21:20 +00:00
2018-10-10 07:07:18 +00:00
var waitGroup sync.WaitGroup
fmt.Printf("got advertisements. checking them...")
2018-10-07 02:21:20 +00:00
for i := len(messages) - 1; i >= 0; i-- {
if messages[i].Message == "be gay do crimes" {
2018-10-10 07:07:18 +00:00
// 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])
2018-10-07 02:21:20 +00:00
}
}
2018-10-10 07:07:18 +00:00
waitGroup.Wait()
2018-10-07 02:21:20 +00:00
}
peer.Save()
2018-10-09 23:49:20 +00:00
}