initial commit
This commit is contained in:
parent
0af046784f
commit
a70413d6aa
|
@ -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
72
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()
|
||||
|
|
21
smp.go
21
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
|
||||
|
|
Loading…
Reference in New Issue