diff options
| -rw-r--r-- | internal/pwhash/pwhash.go | 25 | ||||
| -rw-r--r-- | roseh.moe.go | 75 |
2 files changed, 72 insertions, 28 deletions
diff --git a/internal/pwhash/pwhash.go b/internal/pwhash/pwhash.go index d886746..9ef5d2a 100644 --- a/internal/pwhash/pwhash.go +++ b/internal/pwhash/pwhash.go @@ -1,24 +1,33 @@ package pwhash import ( + "crypto/hkdf" "crypto/pbkdf2" - "crypto/sha512" + "crypto/sha256" ) -const SaltLen = sha512.Size +const SaltLen = 2 * sha256.Size -const defaultIterations = 1220340 // from cmd/finditers +const defaultIterations = 3749890 // from cmd/finditers func HashIter(password string, salt []byte, iter int) ([]byte, error) { - return pbkdf2.Key(sha512.New, password, salt, iter, sha512.Size) + return pbkdf2.Key(sha256.New, password, salt, iter, sha256.Size) } -func Hash(password string, salt []byte) (key, pwHash []byte, err error) { - const aesKeySize = 32 +func Hash(password string, salt []byte) (rawKey, pwHash []byte, err error) { + const hashLen = 32 - key, err = HashIter(password, salt, defaultIterations) + key, err := HashIter(password, salt[:sha256.Size], defaultIterations) if err != nil { return nil, nil, err } - return key[:aesKeySize], key[aesKeySize:], nil + key, err = hkdf.Extract(sha256.New, key, salt[sha256.Size:]) + if err != nil { + return nil, nil, err + } + pwHash, err = hkdf.Expand(sha256.New, key, "pwhash", hashLen) + if err != nil { + return nil, nil, err + } + return key, pwHash, nil } diff --git a/roseh.moe.go b/roseh.moe.go index 2cdf68f..3c5e7a9 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/sha256" @@ -90,34 +91,58 @@ func verify(msg []byte) ([]byte, bool) { var errNoKey = errors.New("not logged in") -func loadEncryptionKey() (cipher.AEAD, error) { +const ( + aesKeyLen = 32 + nonceLen = aes.BlockSize + certLen = sha256.Size +) + +func encrypt(msg string) ([]byte, error) { encryptionKeyMu.Lock() key := encryptionKey encryptionKeyMu.Unlock() if key == nil { return nil, errNoKey } - block, err := aes.NewCipher(key) + block, err := aes.NewCipher(key[:aesKeyLen]) if err != nil { return nil, err } - return cipher.NewGCMWithRandomNonce(block) + buf := make([]byte, nonceLen+len(msg)+certLen) + nonce := buf[:nonceLen] + rand.Read(nonce) + cipher.NewCTR(block, nonce).XORKeyStream(buf[nonceLen:], []byte(msg)) + cipherText := buf[:nonceLen+len(msg)] + mac := hmac.New(sha256.New, key[aesKeyLen:]) + mac.Write(cipherText) + return mac.Sum(cipherText), nil } -func encrypt(msg []byte) ([]byte, error) { - aead, err := loadEncryptionKey() - if err != nil { - return nil, err +func decrypt(msg []byte) (string, error) { + encryptionKeyMu.Lock() + key := encryptionKey + encryptionKeyMu.Unlock() + if key == nil { + return "", errNoKey } - return aead.Seal(nil, nil, msg, nil), nil -} - -func decrypt(msg []byte) ([]byte, error) { - aead, err := loadEncryptionKey() + if len(msg) < certLen+nonceLen { + return "", fmt.Errorf("message too short") + } + msg, messageMAC := msg[:len(msg)-certLen], msg[len(msg)-certLen:] + mac := hmac.New(sha256.New, key[aesKeyLen:]) + mac.Write(msg) + expectedMAC := mac.Sum(nil) + if !hmac.Equal(messageMAC, expectedMAC) { + return "", fmt.Errorf("bad signature") + } + block, err := aes.NewCipher(key[:aesKeyLen]) if err != nil { - return nil, err + return "", err } - return aead.Open(nil, nil, msg, nil) + nonce, msg := msg[:nonceLen], msg[nonceLen:] + buf := make([]byte, len(msg)) + cipher.NewCTR(block, nonce).XORKeyStream(buf, msg) + return string(buf), nil } var ( @@ -259,7 +284,7 @@ type loginTemplateArgs struct { } func login(w http.ResponseWriter, r *http.Request) { - key, pwHash, err := pwhash.Hash(r.FormValue("password"), notepadPasswordSalt) + 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 @@ -271,10 +296,20 @@ 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(sha256.New, rawKey, "encrypt", aesKeyLen+certLen) + if err != nil { + http.Error(w, fmt.Sprintf("failed to derive encryption key: %s", err), http.StatusInternalServerError) + return + } + encryptionKeyMu.Lock() + if encryptionKey == nil { + encryptionKey = key + } + encryptionKeyMu.Unlock() + } attachCookie(w) http.Redirect(w, r, "/notepad", http.StatusSeeOther) } @@ -288,7 +323,7 @@ func readNotepad() (string, error) { if err != nil { return "", err } - return string(decrypted), nil + return decrypted, nil } var ( @@ -332,7 +367,7 @@ func saveNote(w http.ResponseWriter, r *http.Request) error { if _, ok := cookieAuth(w, r); !ok { return fmt.Errorf("not logged in") } - encrypted, err := encrypt([]byte(r.FormValue("content"))) + encrypted, err := encrypt(r.FormValue("content")) if err != nil { return err } |
