forked from cwtch.im/tapir
179 lines
5.3 KiB
Go
179 lines
5.3 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"crypto/rand"
|
||
|
"crypto/sha512"
|
||
|
"cwtch.im/tapir"
|
||
|
"cwtch.im/tapir/primitives"
|
||
|
"encoding/hex"
|
||
|
"encoding/json"
|
||
|
"git.openprivacy.ca/openprivacy/libricochet-go/connectivity"
|
||
|
"git.openprivacy.ca/openprivacy/libricochet-go/identity"
|
||
|
"git.openprivacy.ca/openprivacy/libricochet-go/log"
|
||
|
"git.openprivacy.ca/openprivacy/libricochet-go/utils"
|
||
|
"golang.org/x/crypto/ed25519"
|
||
|
"os"
|
||
|
)
|
||
|
|
||
|
// This example implements a basic notification application which allows peers to notify each other of new messages without downloading
|
||
|
// the entire contents of the server.
|
||
|
// NOTE: Very Incomplete Prototype.
|
||
|
|
||
|
// Notification contains a Topic string and a Message.
|
||
|
type Notification struct {
|
||
|
Topic string // A hex encoded string of the hash of the topic string
|
||
|
Message string
|
||
|
}
|
||
|
|
||
|
type NotificationClient struct {
|
||
|
tapir.AuthApp
|
||
|
connection *tapir.Connection
|
||
|
}
|
||
|
|
||
|
// NewInstance should always return a new instantiation of the application.
|
||
|
func (nc NotificationClient) NewInstance() tapir.Application {
|
||
|
app := new(NotificationClient)
|
||
|
return app
|
||
|
}
|
||
|
|
||
|
// Init is run when the connection is first started.
|
||
|
func (nc * NotificationClient) Init(connection *tapir.Connection) {
|
||
|
// First run the Authentication App
|
||
|
nc.AuthApp.Init(connection)
|
||
|
if connection.HasCapability(tapir.AuthCapability) {
|
||
|
nc.connection = connection
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Publish transforms the given topic string into a hashed ID, and sends the ID along with the message
|
||
|
// NOTE: Server learns the hash of the topic (and therefore can correlate repeated use of the same topic)
|
||
|
func (nc NotificationClient) Publish(topic string, message string) {
|
||
|
log.Debugf("Sending Publish Request")
|
||
|
hashedTopic := sha512.Sum512([]byte(topic))
|
||
|
data,_ := json.Marshal(NotificationRequest{RequestType: "Publish", RequestData: map[string]string{"Topic": hex.EncodeToString(hashedTopic[:])}})
|
||
|
nc.connection.Send([]byte(data))
|
||
|
}
|
||
|
|
||
|
// Check returns true if the server might have notifications related to the topic.
|
||
|
// This check reveals nothing about the topic to the server.
|
||
|
func (nc NotificationClient) Check(topic string) bool {
|
||
|
log.Debugf("Sending Filter Request")
|
||
|
// Get an updated bloom filter
|
||
|
data,_ := json.Marshal(NotificationRequest{RequestType: "BloomFilter", RequestData: map[string]string{}})
|
||
|
nc.connection.Send(data)
|
||
|
response := nc.connection.Expect()
|
||
|
var bf primitives.BloomFilter
|
||
|
json.Unmarshal(response, &bf)
|
||
|
|
||
|
// Check the topic handle in the bloom filter
|
||
|
hashedTopic := sha512.Sum512([]byte(topic))
|
||
|
return bf.Check(hashedTopic[:])
|
||
|
}
|
||
|
|
||
|
|
||
|
type NotificationRequest struct {
|
||
|
RequestType string
|
||
|
RequestData map[string]string
|
||
|
}
|
||
|
|
||
|
// PIRApp is a trivial implementation of a basic p2p application
|
||
|
type NotificationsServer struct {
|
||
|
tapir.AuthApp
|
||
|
Filter * primitives.BloomFilter
|
||
|
}
|
||
|
|
||
|
// NewInstance should always return a new instantiation of the application.
|
||
|
func (ns NotificationsServer) NewInstance() tapir.Application {
|
||
|
app := new(NotificationsServer)
|
||
|
app.Filter = ns.Filter
|
||
|
return app
|
||
|
}
|
||
|
|
||
|
func (ns NotificationsServer) Init(connection *tapir.Connection) {
|
||
|
// First run the Authentication App
|
||
|
ns.AuthApp.Init(connection)
|
||
|
if connection.HasCapability(tapir.AuthCapability) {
|
||
|
for {
|
||
|
request := connection.Expect()
|
||
|
var nr NotificationRequest
|
||
|
json.Unmarshal(request, &nr)
|
||
|
log.Debugf("Received Request %v", nr)
|
||
|
switch nr.RequestType {
|
||
|
case "Publish":
|
||
|
{
|
||
|
log.Debugf("Received Publish Request")
|
||
|
topic := nr.RequestData["Topic"]
|
||
|
// message := nr.RequestData["Message"]
|
||
|
topicID, err := hex.DecodeString(topic)
|
||
|
if err == nil {
|
||
|
ns.Filter.Insert(topicID)
|
||
|
}
|
||
|
}
|
||
|
case "BloomFilter":
|
||
|
{
|
||
|
log.Debugf("Received Filter Request")
|
||
|
response, _ := json.Marshal(ns.Filter)
|
||
|
connection.Send(response)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func main() {
|
||
|
|
||
|
log.SetLevel(log.LevelDebug)
|
||
|
|
||
|
// Connect to Tor
|
||
|
var acn connectivity.ACN
|
||
|
acn, _ = connectivity.StartTor("./", "")
|
||
|
acn.WaitTillBootstrapped()
|
||
|
|
||
|
// Generate Server Keys
|
||
|
pubkey, privateKey, _ := ed25519.GenerateKey(rand.Reader)
|
||
|
sk := ed25519.PrivateKey(privateKey)
|
||
|
pk := ed25519.PublicKey(pubkey)
|
||
|
id := identity.InitializeV3("server", &sk, &pk)
|
||
|
|
||
|
// Init a Client to Connect to the Server
|
||
|
go client(acn, pubkey)
|
||
|
|
||
|
// Init the Server running the Simple App.
|
||
|
var service tapir.Service
|
||
|
service = new(tapir.BaseOnionService)
|
||
|
service.Init(acn, sk, id)
|
||
|
bf := new(primitives.BloomFilter)
|
||
|
bf.Init(1024)
|
||
|
service.Listen(NotificationsServer{Filter:bf})
|
||
|
}
|
||
|
|
||
|
// Client will Connect and launch it's own Echo App goroutine.
|
||
|
func client(acn connectivity.ACN, key ed25519.PublicKey) {
|
||
|
pubkey, privateKey, _ := ed25519.GenerateKey(rand.Reader)
|
||
|
sk := ed25519.PrivateKey(privateKey)
|
||
|
pk := ed25519.PublicKey(pubkey)
|
||
|
id := identity.InitializeV3("client", &sk, &pk)
|
||
|
var client tapir.Service
|
||
|
client = new(tapir.BaseOnionService)
|
||
|
client.Init(acn, sk, id)
|
||
|
|
||
|
cid, _ := client.Connect(utils.GetTorV3Hostname(key), new(NotificationClient))
|
||
|
|
||
|
|
||
|
conn, err := client.WaitForCapabilityOrClose(cid, tapir.AuthCapability)
|
||
|
if err == nil {
|
||
|
log.Debugf("Client has Auth: %v", conn.HasCapability(tapir.AuthCapability))
|
||
|
nc := conn.App.(*NotificationClient)
|
||
|
|
||
|
// Basic Demonstration of Notification
|
||
|
log.Infof("Checking #astronomy: %v", nc.Check("#astronomy"))
|
||
|
log.Infof("Publishing to #astronomy: %v", nc.Check("#astronomy"))
|
||
|
nc.Publish("#astronomy", "New #Astronomy Post!")
|
||
|
log.Infof("Checking #astronomy: %v", nc.Check("#astronomy"))
|
||
|
}
|
||
|
|
||
|
|
||
|
os.Exit(0)
|
||
|
}
|
||
|
|