diff options
| author | Rose Hogenson <rosehogenson@posteo.net> | 2025-09-23 23:56:06 -0700 |
|---|---|---|
| committer | Rose Hogenson <rosehogenson@posteo.net> | 2025-09-23 23:56:06 -0700 |
| commit | 83954192ebc1b5987c7a34dcb45d82aafe2ebd83 (patch) | |
| tree | 57b3469de7cc64df10469fe33dc6442928604c00 /roseh.moe.go | |
| parent | Don't reuse cipher.AEAD between threads (diff) | |
| download | roseh.moe-83954192ebc1b5987c7a34dcb45d82aafe2ebd83.tar.zst | |
Use HKDF to expand key into both auth and encrypt
I guess having the password hash depend on the encryption key isn't a great idea. Let's make them independent using hkdf
Diffstat (limited to 'roseh.moe.go')
| -rw-r--r-- | roseh.moe.go | 56 |
1 files changed, 39 insertions, 17 deletions
diff --git a/roseh.moe.go b/roseh.moe.go index 99d1bed..c8d61e9 100644 --- a/roseh.moe.go +++ b/roseh.moe.go @@ -4,6 +4,7 @@ import ( "bytes" "crypto/aes" "crypto/cipher" + "crypto/hkdf" "crypto/hmac" "crypto/rand" "crypto/sha512" @@ -54,6 +55,9 @@ func loadSecrets() error { if _, err := hex.Decode(buf, pw); err != nil { return err } + if len(buf) < pwhash.SaltLen { + return fmt.Errorf("bad password hash") + } notepadPasswordSalt, notepadPassword = buf[:pwhash.SaltLen], buf[pwhash.SaltLen:] } else if key, ok := bytes.CutPrefix(line, []byte("secret-key=")); ok { if hex.DecodedLen(len(key)) != sha512.Size256 { @@ -155,9 +159,25 @@ func attachCookie(w http.ResponseWriter) error { return nil } -func cookieAuth(w http.ResponseWriter, r *http.Request) (string, bool) { +func attachCSRFToken(w http.ResponseWriter) string { const csrfTokenLen = 32 + buf := make([]byte, csrfTokenLen) + rand.Read(buf) + csrfToken := base64.RawStdEncoding.EncodeToString(buf) + http.SetCookie(w, &http.Cookie{ + Name: "csrf-token", + Value: csrfToken, + Path: "/notepad", + Secure: true, + HttpOnly: true, + SameSite: http.SameSiteStrictMode, + Partitioned: true, + }) + return csrfToken +} + +func cookieAuth(w http.ResponseWriter, r *http.Request) (string, bool) { cookie, err := r.Cookie("auth") if err != nil { return "", false @@ -188,18 +208,7 @@ func cookieAuth(w http.ResponseWriter, r *http.Request) (string, bool) { if csrfToken, err := r.Cookie("csrf-token"); err == nil { return csrfToken.Value, true } - buf := make([]byte, csrfTokenLen) - rand.Read(buf) - csrfToken := base64.RawStdEncoding.EncodeToString(buf) - http.SetCookie(w, &http.Cookie{ - Name: "csrf-token", - Value: csrfToken, - Path: "/notepad", - Secure: true, - HttpOnly: true, - SameSite: http.SameSiteStrictMode, - Partitioned: true, - }) + csrfToken := attachCSRFToken(w) return csrfToken, true } @@ -215,7 +224,9 @@ type loginTemplateArgs struct { } func login(w http.ResponseWriter, r *http.Request) { - key, pwHash, err := pwhash.Hash(r.FormValue("password"), notepadPasswordSalt) + const aesKeySize = 32 + + rawKey, 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 @@ -227,11 +238,22 @@ func login(w http.ResponseWriter, r *http.Request) { return } encryptionKeyMu.Lock() - if encryptionKey == nil { - encryptionKey = key - } + currentKey := encryptionKey encryptionKeyMu.Unlock() + if currentKey == nil { + key, err := hkdf.Expand(sha512.New, rawKey, "encrypt", aesKeySize) + if err != nil { + http.Error(w, fmt.Sprintf("Unable to derive encryption key: %s", err), http.StatusInternalServerError) + return + } + encryptionKeyMu.Lock() + if encryptionKey == nil { + encryptionKey = key + } + encryptionKeyMu.Unlock() + } attachCookie(w) + attachCSRFToken(w) http.Redirect(w, r, r.FormValue("redirect"), http.StatusSeeOther) } |
