diff --git a/main.go b/main.go index 89fb5e9..27e1c13 100644 --- a/main.go +++ b/main.go @@ -3,6 +3,7 @@ package main import ( // "database/sql" "bufio" + "bytes" "crypto/aes" "crypto/cipher" "crypto/pbkdf2" @@ -16,12 +17,14 @@ import ( "log" "net/http" "net/url" + "os" + "os/exec" "os/signal" + "strings" "syscall" - "os" - "golang.org/x/term" + _ "modernc.org/sqlite" ) @@ -35,6 +38,14 @@ var ( scanner bufio.Scanner ) +type Note struct { + Body string `json:"body"` + Color int64 `json:"color"` + Date string `json:"date"` + ID string `json:"id"` + Title string `json:"title"` +} + func check(err error, format string, a ...any) bool { if err != nil { log.Fatalf(format, a, err) @@ -43,7 +54,7 @@ func check(err error, format string, a ...any) bool { return true } -func ReadPassword() (string, error) { +func readPassword() (string, error) { stdin := int(syscall.Stdin) oldState, err := term.GetState(stdin) if err != nil { @@ -68,6 +79,22 @@ func ReadPassword() (string, error) { return string(password), nil } +func pkcs7strip(data []byte, blockSize int) ([]byte, error) { + length := len(data) + if length == 0 { + return nil, errors.New("pkcs7: Data is empty") + } + if length%blockSize != 0 { + return nil, errors.New("pkcs7: Data is not block-aligned") + } + padLen := int(data[length-1]) + ref := bytes.Repeat([]byte{byte(padLen)}, padLen) + if padLen > blockSize || padLen == 0 || !bytes.HasSuffix(data, ref) { + return nil, errors.New("pkcs7: Invalid padding") + } + return data[:length-padLen], nil +} + func main() { scanner = *bufio.NewScanner(os.Stdin) home := os.Getenv("HOME") @@ -116,13 +143,22 @@ func main() { err = json.Unmarshal(body, &jsonBody) check(err, "Error unmarshalling response body: %s") - decryptedBody := make([]string, len(jsonBody)) + fzf := exec.Command("fzf") + stdin, err := fzf.StdinPipe() + check(err, "Error executing fzf: %s") + check(fzf.Start(), "Error starting fzf: %s") + + decryptedBody := make([]Note, len(jsonBody)) for i := range len(jsonBody) { - decryptedBody[i] = decrypt(jsonBody[i][1], password) - jsonDecryptedBody := json.Marshal(decryptedBody) - decryptedBody[i] + err := json.Unmarshal([]byte(decrypt(jsonBody[i][1], password)), &decryptedBody[i]) + if err != nil { + continue + } + if len(decryptedBody[i].Title) > 0 && len(decryptedBody[i].Body) > 0 { + fmt.Fprintf(stdin, "%s | %s\n", decryptedBody[i].Title, strings.ReplaceAll(decryptedBody[i].Body, "\n", " ")) + } } - fmt.Println(decryptedBody) + fzf.Wait() default: login() @@ -139,12 +175,12 @@ func login() { fmt.Print("Username: ") scanner.Scan() - username = scanner.Text() + username = url.QueryEscape(scanner.Text()) fmt.Print("Password: ") - passwordBytes, err := ReadPassword() - password = string(passwordBytes) + passwordBytes, err := readPassword() check(err, "Error reading user input (password): %s") + password = url.QueryEscape(string(passwordBytes)) jsonData, err := json.Marshal([]string{serverAddress, username, password}) check(err, "Error marshalling server address and credentials: %s") @@ -163,7 +199,6 @@ func decrypt(data string, password string) string { check(err, "Error decoding base64 data: %s") if len(dataBytes) < 32 { - fmt.Printf("Not enough data (got %d bytes, expected at least 32)\n", len(dataBytes)) return "" } @@ -179,9 +214,15 @@ func decrypt(data string, password string) string { key, err := pbkdf2.Key(sha256.New, password, salt, 65536, 32) check(err, "Error deriving pbkdf2-sha256 key from password: %s") block, err := aes.NewCipher(key) + check(err, "Error creating AES key: %s") mode := cipher.NewCBCDecrypter(block, iv) plaintext := make([]byte, len(ciphertext)) mode.CryptBlocks(plaintext, ciphertext) - return string(plaintext) + + strippedPlaintext, err := pkcs7strip(plaintext, 16) + if err != nil { + return "" + } + return string(strippedPlaintext) }