diff --git a/cmd/ppass/ppass.go b/cmd/ppass/ppass.go index a94e7a2..3fbe499 100644 --- a/cmd/ppass/ppass.go +++ b/cmd/ppass/ppass.go @@ -1,31 +1,17 @@ package main import ( - "bufio" - "context" "flag" "fmt" - "os" - "time" - "os/signal" - "strings" - "syscall" - pubsub "github.com/libp2p/go-libp2p-pubsub" - "github.com/libp2p/go-libp2p/core/peer" - ipfslite "github.com/hsanjuan/ipfs-lite" - badger "github.com/ipfs/go-ds-badger2" logging "github.com/ipfs/go-log/v2" - "github.com/k4lipso/pentapass/storage" - "github.com/k4lipso/pentapass/crypto/age" + "github.com/k4lipso/pentapass/rpc" "github.com/k4lipso/pentapass/crypto" ) var ( - topicNameFlag = flag.String("topicName", "akdjlask-23klaj2idalj2-ajl2kjd3i-2ldakjd2", "name of topic to join") dbPath = flag.String("db", "./db", "db file path") - nameSpace = flag.String("namespace", "crdt", "namespace") logger = logging.Logger("globaldb") // topicName = "globaldb-example" // netTopic = "globaldb-example-net" @@ -35,262 +21,237 @@ var ( func main() { flag.Parse() - ctx := context.Background() - data := *dbPath - key, err := age.LoadOrGenerateKeys(*dbPath + "/age.key") + client, err := rpc.Receive(*dbPath) if err != nil { - panic(err) - } - - fmt.Printf("AgeKey: %s\n", key.String()) - fmt.Printf("AgePublicKey: %s\n", key.Recipient().String()) - - //cipher, err := age.Encrypt([]byte("Test Message"), []string{key.Recipient().String()}) - //fmt.Printf("Encrypted: %s\n", cipher) - //decrypted, err := age.Decrypt(cipher, key) - //fmt.Printf("Decrypted: %s\n", decrypted) - - h, dht, err := storage.SetupLibp2pHost(ctx, *dbPath) - - pid := h.ID().String() - fmt.Println(h.ID().String()) - - if err != nil { - panic(err) - } - - ps, err := pubsub.NewGossipSub(ctx, h) - if err != nil { - panic(err) - } - - //topic, err := ps.Join(*topicNameFlag) - //if err != nil { - // panic(err) - //} - - go storage.DiscoverPeers(ctx, h, dht) - - store, err := badger.NewDatastore(data, &badger.DefaultOptions) - if err != nil { - logger.Fatal(err) - } - defer store.Close() - - ipfs, err := ipfslite.New(ctx, store, nil, h, dht, nil) - if err != nil { - logger.Fatal(err) - } - - Cfg, err := storage.NewConfig(*dbPath + "/config.json") - - if err != nil { - logger.Fatal(err) - } - - storageHandler := storage.StorageHandler{ - Ctx: ctx , - Store: store, - Host: h, - Ipfs: ipfs, - PubSub: ps, - Key: key, - Config: Cfg, - } - - storageHandler.InitNamespaces() - - for _, val := range storageHandler.Namespaces { - defer val.Close() - } - - fmt.Printf(` -Peer ID: %s -Listen address: %s -Topic: %s -Data Folder: %s - -Ready! - -Commands: - -> list -> list items in the store -> get -> get value for a key -> put -> store value on a key -> exit -> quit - - -`, - pid, storage.Listen, *topicNameFlag, data, - ) - - if len(os.Args) > 1 && os.Args[1] == "daemon" { - fmt.Println("Running in daemon mode") - go func() { - for { - fmt.Printf("%s - %d connected peers\n", time.Now().Format(time.Stamp), len(storage.ConnectedPeers(h))) - time.Sleep(10 * time.Second) - } - }() - signalChan := make(chan os.Signal, 20) - signal.Notify( - signalChan, - syscall.SIGINT, - syscall.SIGTERM, - syscall.SIGHUP, - ) - <-signalChan + fmt.Printf("dialing: %s\n", err) return } - fmt.Printf("> ") - scanner := bufio.NewScanner(os.Stdin) - for scanner.Scan() { - text := scanner.Text() - fields := strings.Fields(text) - if len(fields) == 0 { - fmt.Printf("> ") - continue - } + var names []string + namespace := "root" + err = client.Call("Query.GetAllNames", &namespace, &names) - cmd := fields[0] - - switch cmd { - case "exit", "quit": - return - case "debug": - if len(fields) < 2 { - fmt.Println("debug ") - } - st := fields[1] - switch st { - case "on": - logging.SetLogLevel("globaldb", "debug") - case "off": - logging.SetLogLevel("globaldb", "error") - case "peers": - for _, p := range storage.ConnectedPeers(h) { - addrs, err := peer.AddrInfoToP2pAddrs(p) - if err != nil { - logger.Warn(err) - continue - } - for _, a := range addrs { - fmt.Println(a) - } - } - } - case "list": - if len(fields) < 2 { - fmt.Printf("Available Namespaces:\n") - for k := range storageHandler.Namespaces { - fmt.Printf("%s\n", k) - } - continue - } - - namespace := fields[1] - - fmt.Printf("Listing content of %s", namespace) - - val, ok := storageHandler.Namespaces[namespace] - - if !ok { - fmt.Println("Namespace does not exist") - continue - } - - val.List() - case "get": - if len(fields) < 3 { - fmt.Println("get ") - fmt.Println("> ") - continue - } - - namespace := fields[1] - - val, ok := storageHandler.Namespaces[namespace] - - if !ok { - fmt.Println("Namespace does not exist") - continue - } - - k := fields[2] - v, err := val.Get(k) - if err != nil { - printErr(err) - continue - } - - fmt.Printf("[%s] -> %s\n", k, string(v)) - case "generate": - if len(fields) < 3 { - fmt.Println("generate ") - fmt.Println("> ") - continue - } - - namespace := fields[1] - - val, ok := storageHandler.Namespaces[namespace] - - if !ok { - fmt.Println("Namespace does not exist") - continue - } - - service := fields[2] - password := crypto.NewPassword() - password.Service = service - - data, err := password.ToJson() - if err != nil { - printErr(err) - continue - } - - encryptedPassword, err := age.Encrypt(data, val.GetRecipients()) - if err != nil { - printErr(err) - continue - } - - err = val.Put(password.Id.String(), string(encryptedPassword)) - if err != nil { - printErr(err) - continue - } - case "put": - if len(fields) < 4 { - fmt.Println("put ") - fmt.Println("> ") - continue - } - - namespace := fields[1] - - val, ok := storageHandler.Namespaces[namespace] - - if !ok { - fmt.Println("Namespace does not exist") - continue - } - - - k := fields[2] - v := strings.Join(fields[3:], " ") - err := val.Put(k, v) - if err != nil { - printErr(err) - continue - } - } - fmt.Printf("> ") + if err != nil { + fmt.Println(err) } + + fmt.Println(names) + + var password *crypto.Password + np := rpc.NamespaceService{ Namespace: "root", Service: "Test" } + err = client.Call("Query.Generate", &np, &password) + + if err != nil { + fmt.Println(err) + } + + fmt.Println(*password) + + var success bool + err = client.Call("Query.Delete", &np, &success) + + if success == true { + fmt.Println("Deleted Test") + } + + var password2 *crypto.Password + err = client.Call("Query.Get", &np, &password2) + + if err != nil { + fmt.Println(err) + return + } + + fmt.Println(*password2) + +// fmt.Printf(` +//Peer ID: %s +//Listen address: %s +//Topic: %s +//Data Folder: %s +// +//Ready! +// +//Commands: +// +//> list -> list items in the store +//> get -> get value for a key +//> put -> store value on a key +//> exit -> quit +// +// +//`, +// pid, storage.Listen, *topicNameFlag, data, +// ) +// +// if len(os.Args) > 1 && os.Args[1] == "daemon" { +// fmt.Println("Running in daemon mode") +// go func() { +// for { +// fmt.Printf("%s - %d connected peers\n", time.Now().Format(time.Stamp), len(storage.ConnectedPeers(h))) +// time.Sleep(10 * time.Second) +// } +// }() +// signalChan := make(chan os.Signal, 20) +// signal.Notify( +// signalChan, +// syscall.SIGINT, +// syscall.SIGTERM, +// syscall.SIGHUP, +// ) +// <-signalChan +// return +// } +// +// fmt.Printf("> ") +// scanner := bufio.NewScanner(os.Stdin) +// for scanner.Scan() { +// text := scanner.Text() +// fields := strings.Fields(text) +// if len(fields) == 0 { +// fmt.Printf("> ") +// continue +// } +// +// cmd := fields[0] +// +// switch cmd { +// case "exit", "quit": +// return +// case "debug": +// if len(fields) < 2 { +// fmt.Println("debug ") +// } +// st := fields[1] +// switch st { +// case "on": +// logging.SetLogLevel("globaldb", "debug") +// case "off": +// logging.SetLogLevel("globaldb", "error") +// case "peers": +// for _, p := range storage.ConnectedPeers(h) { +// addrs, err := peer.AddrInfoToP2pAddrs(p) +// if err != nil { +// logger.Warn(err) +// continue +// } +// for _, a := range addrs { +// fmt.Println(a) +// } +// } +// } +// case "list": +// if len(fields) < 2 { +// fmt.Printf("Available Namespaces:\n") +// for k := range storageHandler.Namespaces { +// fmt.Printf("%s\n", k) +// } +// continue +// } +// +// namespace := fields[1] +// +// fmt.Printf("Listing content of %s", namespace) +// +// val, ok := storageHandler.Namespaces[namespace] +// +// if !ok { +// fmt.Println("Namespace does not exist") +// continue +// } +// +// val.List() +// case "get": +// if len(fields) < 3 { +// fmt.Println("get ") +// fmt.Println("> ") +// continue +// } +// +// namespace := fields[1] +// +// val, ok := storageHandler.Namespaces[namespace] +// +// if !ok { +// fmt.Println("Namespace does not exist") +// continue +// } +// +// k := fields[2] +// v, err := val.Get(k) +// if err != nil { +// printErr(err) +// continue +// } +// +// fmt.Printf("[%s] -> %s\n", k, string(v)) +// case "generate": +// if len(fields) < 3 { +// fmt.Println("generate ") +// fmt.Println("> ") +// continue +// } +// +// namespace := fields[1] +// +// val, ok := storageHandler.Namespaces[namespace] +// +// if !ok { +// fmt.Println("Namespace does not exist") +// continue +// } +// +// service := fields[2] +// password := crypto.NewPassword() +// password.Service = service +// +// data, err := password.ToJson() +// if err != nil { +// printErr(err) +// continue +// } +// +// encryptedPassword, err := age.Encrypt(data, val.GetRecipients()) +// if err != nil { +// printErr(err) +// continue +// } +// +// //err = val.Put(password.Id.String(), string(encryptedPassword)) +// err = val.Put(password.Service, string(encryptedPassword)) +// if err != nil { +// printErr(err) +// continue +// } +// case "put": +// if len(fields) < 4 { +// fmt.Println("put ") +// fmt.Println("> ") +// continue +// } +// +// namespace := fields[1] +// +// val, ok := storageHandler.Namespaces[namespace] +// +// if !ok { +// fmt.Println("Namespace does not exist") +// continue +// } +// +// +// k := fields[2] +// v := strings.Join(fields[3:], " ") +// err := val.Put(k, v) +// if err != nil { +// printErr(err) +// continue +// } +// } +// fmt.Printf("> ") +// } } func printErr(err error) { diff --git a/cmd/ppassd/ppassd.go b/cmd/ppassd/ppassd.go new file mode 100644 index 0000000..f919635 --- /dev/null +++ b/cmd/ppassd/ppassd.go @@ -0,0 +1,103 @@ +package main + +import ( + "context" + "flag" + "fmt" + + pubsub "github.com/libp2p/go-libp2p-pubsub" + ipfslite "github.com/hsanjuan/ipfs-lite" + badger "github.com/ipfs/go-ds-badger2" + logging "github.com/ipfs/go-log/v2" + + "github.com/k4lipso/pentapass/storage" + "github.com/k4lipso/pentapass/rpc" + "github.com/k4lipso/pentapass/crypto/age" +) + +var ( + topicNameFlag = flag.String("topicName", "akdjlask-23klaj2idalj2-ajl2kjd3i-2ldakjd2", "name of topic to join") + dbPath = flag.String("db", "./db", "db file path") + nameSpace = flag.String("namespace", "crdt", "namespace") + logger = logging.Logger("globaldb") +// topicName = "globaldb-example" +// netTopic = "globaldb-example-net" +// config = "globaldb-example" +) + + +func main() { + flag.Parse() + ctx := context.Background() + data := *dbPath + + key, err := age.LoadOrGenerateKeys(*dbPath + "/age.key") + + if err != nil { + panic(err) + } + + fmt.Printf("AgeKey: %s\n", key.String()) + fmt.Printf("AgePublicKey: %s\n", key.Recipient().String()) + + //cipher, err := age.Encrypt([]byte("Test Message"), []string{key.Recipient().String()}) + //fmt.Printf("Encrypted: %s\n", cipher) + //decrypted, err := age.Decrypt(cipher, key) + //fmt.Printf("Decrypted: %s\n", decrypted) + + h, dht, err := storage.SetupLibp2pHost(ctx, *dbPath) + + fmt.Println(h.ID().String()) + + if err != nil { + panic(err) + } + + ps, err := pubsub.NewGossipSub(ctx, h) + if err != nil { + panic(err) + } + + //topic, err := ps.Join(*topicNameFlag) + //if err != nil { + // panic(err) + //} + + go storage.DiscoverPeers(ctx, h, dht) + + store, err := badger.NewDatastore(data, &badger.DefaultOptions) + if err != nil { + logger.Fatal(err) + } + defer store.Close() + + ipfs, err := ipfslite.New(ctx, store, nil, h, dht, nil) + if err != nil { + logger.Fatal(err) + } + + Cfg, err := storage.NewConfig(*dbPath + "/config.json") + + if err != nil { + logger.Fatal(err) + } + + storageHandler := storage.StorageHandler{ + Ctx: ctx , + Store: store, + Host: h, + Ipfs: ipfs, + PubSub: ps, + Key: key, + Config: Cfg, + } + + storageHandler.InitNamespaces() + + for _, val := range storageHandler.Namespaces { + defer val.Close() + } + + rpc.StorageHandler = &storageHandler + rpc.Serve(*dbPath) +} diff --git a/crypto/crypto.go b/crypto/crypto.go index b4fc4fb..c06ae8e 100644 --- a/crypto/crypto.go +++ b/crypto/crypto.go @@ -1,11 +1,14 @@ package crypto import ( + "math/rand" "encoding/json" "github.com/google/uuid" ) +const DEFAULT_LENGTH int = 25 + type Password struct { Service string `json:"Service"` Url string `json:"Url"` @@ -21,7 +24,7 @@ func (p *Password) ToJson() ([]byte, error) { func GetPasswordFromJson(b []byte) (Password, error) { var result Password - err := json.Unmarshal(b, result) + err := json.Unmarshal(b, &result) if err != nil { return Password{}, err @@ -30,9 +33,20 @@ func GetPasswordFromJson(b []byte) (Password, error) { return result, nil } -func NewPassword() *Password { +func NewPassword(length int) *Password { return &Password{ Id: uuid.New(), + Password: GenerateRandomString(length), } } +func GenerateRandomString(length int) string { + charset := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + b := make([]byte, length) + for i := range b { + b[i] = charset[rand.Intn(len(charset))] + } + return string(b) +} + + diff --git a/rpc/rpc.go b/rpc/rpc.go new file mode 100644 index 0000000..3304fd4 --- /dev/null +++ b/rpc/rpc.go @@ -0,0 +1,139 @@ +package rpc + +import ( + "fmt" + "net" + "net/rpc" + "net/http" + + "os" + "os/signal" + "syscall" + + "github.com/k4lipso/pentapass/storage" + "github.com/k4lipso/pentapass/crypto" + "github.com/k4lipso/pentapass/crypto/age" +) + +var StorageHandler *storage.StorageHandler + +type Query int + +type NamespaceService struct { + Namespace string + Service string +} + +func (t *Query) Generate(np *NamespaceService, reply *crypto.Password) error { + val, ok := StorageHandler.Namespaces[np.Namespace] + + if !ok { + return fmt.Errorf("Namespace does not exist") + } + + password := crypto.NewPassword(crypto.DEFAULT_LENGTH) + password.Service = np.Service + + data, err := password.ToJson() + if err != nil { + return err + } + + encryptedPassword, err := age.Encrypt(data, val.GetRecipients()) + if err != nil { + return err + } + + err = val.Put(password.Service, string(encryptedPassword)) + if err != nil { + return err + } + + *reply = *password + return nil +} + +func (t *Query) Get(np *NamespaceService, reply *crypto.Password) error { + namespace := np.Namespace + val, ok := StorageHandler.Namespaces[namespace] + + if !ok { + return fmt.Errorf("Namespace does not exist") + } + + v, err := val.GetPassword(np.Service) + if err != nil { + return err + } + + *reply = v + return nil +} + +//func (t *Query) Add(password *crypto.Password, reply *[]string) error { +// return nil +//} + +func (t *Query) Delete(np *NamespaceService, success *bool) error { + namespace := np.Namespace + val, ok := StorageHandler.Namespaces[namespace] + + if !ok { + return fmt.Errorf("Namespace does not exist") + } + + err := val.Delete(np.Service) + if err != nil { + *success = false + return err + } + + *success = true + return nil +} + +func (t *Query) GetAllNames(namespace *string, reply *[]string) error { + fmt.Println("RPC Request: Query::LoadedTriggers") + fmt.Printf("Listing content of %s", *namespace) + + val, ok := StorageHandler.Namespaces[*namespace] + + if !ok { + return fmt.Errorf("Namesapce does not exist") + } + + *reply = val.GetAllNames() + return nil +} + +func Serve(path string) { + query := new(Query) + rpc.Register(query) + rpc.HandleHTTP() + l, err := net.Listen("unix", path + "/rpc_test.socket") + + if err != nil { + fmt.Printf("Error while listening on unix socket: %s\n", err) + } + + go http.Serve(l, nil) + + sigc := make(chan os.Signal, 1) + signal.Notify(sigc, os.Interrupt, syscall.SIGTERM) + func(ln net.Listener, c chan os.Signal) { + sig := <-c + fmt.Printf("Caught signal %s: shutting down.\n", sig) + ln.Close() + os.Exit(0) + }(l, sigc) +} + +func Receive(path string) (*rpc.Client, error) { + client, err := rpc.DialHTTP("unix", path + "/rpc_test.socket") + + if err != nil { + fmt.Printf("Cant connect to RPC server: %s\n", err) + } + + return client, err +} diff --git a/storage/storage.go b/storage/storage.go index 7003b96..28dca83 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -216,6 +216,42 @@ func (n *Namespace) Put(k string, v string) error { return err } +func (n *Namespace) Delete(k string) error { + key := ds.NewKey(k) + err := n.Datastore.Delete(n.ctx, key) + + if err != nil { + printErr(err) + } + + return err +} + +func (n *Namespace) GetPassword(k string) (password.Password, error) { + v, err := n.Datastore.Get(n.ctx, ds.NewKey(k)) + if err != nil { + printErr(err) + return password.Password{}, err + } + + val, err := age.Decrypt(v, n.Key) + + if err != nil { + printErr(err) + return password.Password{}, err + } + + pw, err := password.GetPasswordFromJson(val) + + if err != nil { + printErr(err) + return password.Password{}, err + } + + return pw, nil +} + + func (n *Namespace) Get(k string) (string, error) { v, err := n.Datastore.Get(n.ctx, ds.NewKey(k)) if err != nil { @@ -226,6 +262,27 @@ func (n *Namespace) Get(k string) (string, error) { return string(v), nil } +func (n *Namespace) GetAllNames() []string { + q := query.Query{} + results, err := n.Datastore.Query(n.ctx, q) + + if err != nil { + printErr(err) + } + + var result []string + for r := range results.Next() { + if r.Error != nil { + printErr(err) + continue + } + + result = append(result, r.Key) + } + + return result +} + func (n *Namespace) List() { q := query.Query{} results, err := n.Datastore.Query(n.ctx, q)