diff --git a/client.go b/client.go index 592f6eb..90a209a 100644 --- a/client.go +++ b/client.go @@ -33,16 +33,17 @@ type ReadWriter struct { } type Client struct { - key *rsa.PrivateKey - host string - port int - nick string - conn net.Conn - done chan interface{} - mu sync.Mutex - prompt string - line []rune - prev *terminal.State + key *rsa.PrivateKey + host string + port int + nick string + conn net.Conn + done chan interface{} + mu sync.Mutex + prompt string + line []rune + prev *terminal.State + keyStore map[string]rsa.PublicKey } // establishes a connection to the server @@ -89,6 +90,8 @@ func (c *Client) handleMessage(m Envelope) error { return c.handleNote(m.Body) case "list-notes": return c.handleListNotes(m.Body) + case "key-response": + return c.handleKeyResponse(m.Body) default: return fmt.Errorf("received message of unsupported type: %v", m.Kind) } @@ -284,11 +287,17 @@ func (c *Client) exec(line string) { c.getNote(parts[1:]) case "notes/list": c.listNotes(parts[1:]) + case "keys/get": + c.getKey(parts[1:]) default: c.err("unrecognized client command: %s", parts[0]) } } +// ------------------------------------------------------------------------------ +// note functions +// ------------------------------------------------------------------------------ + func (c *Client) createNote(args []string) { if len(args) < 1 { c.err("yeah you need to specify a title.") @@ -371,6 +380,41 @@ func (c *Client) encryptNote(title string, message []rune) (*EncryptedNote, erro }, nil } +// ------------------------------------------------------------------------------ +// key functions +// ------------------------------------------------------------------------------ + +func (c *Client) getKey(args []string) { + if len(args) != 1 { + c.err("keys/get takes exactly one arg") + return + } + req := KeyRequest(args[0]) + if err := c.sendRequest(req); err != nil { + c.err("couldn't send key request: %v", err) + return + } +} + +func (c *Client) saveKey(nick string, key rsa.PublicKey) { + if c.keyStore == nil { + c.keyStore = make(map[string]rsa.PublicKey, 8) + } + c.keyStore[nick] = key +} + +func (c *Client) handleKeyResponse(body json.RawMessage) error { + c.info(string(body)) + var res KeyResponse + if err := json.Unmarshal(body, &res); err != nil { + c.err(err.Error()) + return err + } + c.info("%v", res) + c.saveKey(res.Nick, res.Key) + return nil +} + func (c *Client) readTextBlock() ([]rune, error) { // god dammit what have i gotten myself into msg := make([]rune, 0, 400) diff --git a/db.go b/db.go new file mode 100644 index 0000000..565d048 --- /dev/null +++ b/db.go @@ -0,0 +1,59 @@ +package main + +import ( + "crypto/rsa" + "encoding/json" + "fmt" + "github.com/syndtr/goleveldb/leveldb" + "sync" +) + +var ( + openDBs = make(map[string]userdb, 32) + dbopenlock sync.Mutex +) + +type userdb struct { + *leveldb.DB +} + +func (db *userdb) getPublicKey() (*rsa.PublicKey, error) { + val, err := db.Get([]byte("public_key"), nil) + if err != nil { + return nil, fmt.Errorf("unable to get public key: %v", err) + } + + var key rsa.PublicKey + if err := json.Unmarshal(val, &key); err != nil { + return nil, fmt.Errorf("unable to get public key: %v", err) + } + return &key, nil +} + +func getUserDB(nick string) (*userdb, error) { + if db, ok := openDBs[nick]; ok { + return &db, nil + } + + path := fmt.Sprintf("./%s.db", nick) + conn, err := leveldb.OpenFile(path, nil) + if err != nil { + return nil, fmt.Errorf("unable to open db file at %s: %v", path, err) + } + info_log.Printf("opened database file: %s", path) + + dbopenlock.Lock() + defer dbopenlock.Unlock() + + db := userdb{conn} + openDBs[nick] = db + return &db, nil +} + +func getUserKey(nick string) (*rsa.PublicKey, error) { + db, err := getUserDB(nick) + if err != nil { + return nil, err + } + return db.getPublicKey() +} diff --git a/key.go b/key.go index 158eb30..ccc5d78 100644 --- a/key.go +++ b/key.go @@ -121,3 +121,16 @@ type KeyRequest string func (k KeyRequest) Kind() string { return "key" } + +func (k KeyRequest) Nick() string { + return string(k) +} + +type KeyResponse struct { + Nick string + Key rsa.PublicKey +} + +func (k KeyResponse) Kind() string { + return "key-response" +} diff --git a/server.go b/server.go index 2f377a5..8948fab 100644 --- a/server.go +++ b/server.go @@ -32,7 +32,7 @@ type serverConnection struct { conn net.Conn nick string key rsa.PublicKey - db *leveldb.DB + db *userdb } func (s *serverConnection) sendMeta(template string, args ...interface{}) { @@ -56,6 +56,8 @@ func (s *serverConnection) handleRequest(request Envelope) error { return s.handleGetNoteRequest(request.Body) case "list-notes": return s.handleListNotesRequest(request.Body) + case "key": + return s.handleKeyRequest(request.Body) default: return fmt.Errorf("no such request type: %v", request.Kind) } @@ -84,6 +86,7 @@ func (s *serverConnection) handleAuthRequest(body json.RawMessage) error { error_log.Printf("man fuck all this stupid key bullshit i hate it: %v", err) break } + info_log.Printf("saved key for user %s", auth.Nick) case nil: var key rsa.PublicKey if err := json.Unmarshal(b, &key); err != nil { @@ -193,13 +196,29 @@ func (s *serverConnection) handleListNotesRequest(body json.RawMessage) error { return s.sendRequest(notes) } +func (s *serverConnection) handleKeyRequest(body json.RawMessage) error { + var req KeyRequest + if err := json.Unmarshal(body, &req); err != nil { + error_log.Printf("unable to read key request: %v", err) + return err + } + info_log.Printf("get key: %v", req.Nick()) + key, err := getUserKey(req.Nick()) + if err != nil { + return err + } + res := KeyResponse{ + Nick: req.Nick(), + Key: *key, + } + return s.sendRequest(res) +} + func (s *serverConnection) openDB() error { - path := fmt.Sprintf("./%s.db", s.nick) - db, err := leveldb.OpenFile(path, nil) + db, err := getUserDB(s.nick) if err != nil { - return fmt.Errorf("unable to open db file at %s: %v", path, err) + return err } - info_log.Printf("opened database file: %s", path) s.db = db return nil }