From 95df5d765a3a71a4ff75d598fbba1bceb6fa2e6c Mon Sep 17 00:00:00 2001 From: Rose Hogenson Date: Tue, 23 Sep 2025 22:48:50 -0700 Subject: Don't reuse cipher.AEAD between threads I'm not sure if it's safe or not, but better to be cautious --- roseh.moe.go | 53 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 19 deletions(-) diff --git a/roseh.moe.go b/roseh.moe.go index f24bc58..99d1bed 100644 --- a/roseh.moe.go +++ b/roseh.moe.go @@ -40,7 +40,7 @@ var ( privateKey []byte encryptionKeyMu sync.Mutex - encryptionKey cipher.AEAD + encryptionKey []byte ) func loadSecrets() error { @@ -51,7 +51,7 @@ func loadSecrets() error { for _, line := range bytes.Split(bytes.TrimSuffix(secrets, []byte("\n")), []byte("\n")) { if pw, ok := bytes.CutPrefix(line, []byte("notepad-password=")); ok { buf := make([]byte, hex.DecodedLen(len(pw))) - if _, err := hex.Decode(notepadPassword, pw); err != nil { + if _, err := hex.Decode(buf, pw); err != nil { return err } notepadPasswordSalt, notepadPassword = buf[:pwhash.SaltLen], buf[pwhash.SaltLen:] @@ -215,7 +215,7 @@ type loginTemplateArgs struct { } func login(w http.ResponseWriter, r *http.Request) { - keyBytes, pwHash, err := pwhash.Hash(r.FormValue("password"), notepadPasswordSalt) + key, pwHash, err := pwhash.Hash(r.FormValue("password"), notepadPasswordSalt) if err != nil { http.Error(w, fmt.Sprintf("Unable to hash password: %s", err), http.StatusInternalServerError) return @@ -226,12 +226,6 @@ func login(w http.ResponseWriter, r *http.Request) { } return } - block, err := aes.NewCipher(keyBytes) - if err != nil { - http.Error(w, fmt.Sprintf("Could not create AES key: %s", err), http.StatusInternalServerError) - return - } - key, err := cipher.NewGCMWithRandomNonce(block) encryptionKeyMu.Lock() if encryptionKey == nil { encryptionKey = key @@ -241,6 +235,26 @@ func login(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, r.FormValue("redirect"), http.StatusSeeOther) } +func readNotepad(key []byte) (string, error) { + encrypted, err := os.ReadFile(*notepadFile) + if err != nil { + return "", err + } + block, err := aes.NewCipher(key) + if err != nil { + return "", err + } + aead, err := cipher.NewGCMWithRandomNonce(block) + if err != nil { + return "", err + } + decrypted, err := aead.Open(nil, nil, encrypted, nil) + if err != nil { + return "", err + } + return string(decrypted), nil +} + var ( //go:embed templates/note.html.template notepadString string @@ -269,16 +283,9 @@ func notepad(w http.ResponseWriter, r *http.Request) { } return } - var currentContent string - if currentContentBytes, err := os.ReadFile(*notepadFile); err != nil { + currentContent, err := readNotepad(key) + if err != nil { currentContent = fmt.Sprintf("Error reading notepad file: %s", err) - } else { - decrypted, err := key.Open(nil, nil, currentContentBytes, nil) - if err != nil { - currentContent = fmt.Sprintf("Could not decrypt notepad contents: %s", err) - } else { - currentContent = string(decrypted) - } } if err := notepadTemplate.Execute(w, notepadTemplateArgs{Content: currentContent, CSRFToken: csrfToken}); err != nil { log.Printf("Warning: notepad: %s", err) @@ -298,12 +305,20 @@ func saveNote(w http.ResponseWriter, r *http.Request) error { if key == nil { return fmt.Errorf("not logged in") } + block, err := aes.NewCipher(key) + if err != nil { + return err + } + aead, err := cipher.NewGCMWithRandomNonce(block) + if err != nil { + return err + } f, err := os.CreateTemp(filepath.Dir(*notepadFile), "notepad") if err != nil { return err } defer f.Close() - if _, err = f.Write(key.Seal(nil, nil, []byte(r.FormValue("content")), nil)); err != nil { + if _, err = f.Write(aead.Seal(nil, nil, []byte(r.FormValue("content")), nil)); err != nil { return err } if err := f.Close(); err != nil { -- cgit v1.3.1