diff options
| author | Rose Hogenson <rosehogenson@posteo.net> | 2025-09-23 21:18:49 -0700 |
|---|---|---|
| committer | Rose Hogenson <rosehogenson@posteo.net> | 2025-09-23 21:18:49 -0700 |
| commit | 09517318b87e39506cbcb3232a395ac7d43c25f5 (patch) | |
| tree | 8b0698270dfc5382655b50dc6df3b5de6dd20715 | |
| parent | 5d40790dcf2043d23a5a0323aaeb3dec417795d3 (diff) | |
| download | roseh.moe-09517318b87e39506cbcb3232a395ac7d43c25f5.tar.zst | |
Use pbkdf2 for password hashing instead of sha512
| -rw-r--r-- | cmd/finditers/finditers.go | 44 | ||||
| -rw-r--r-- | cmd/hashpw/hashpw.go | 34 | ||||
| -rw-r--r-- | flake.lock | 6 | ||||
| -rw-r--r-- | flake.nix | 2 | ||||
| -rw-r--r-- | go.mod | 6 | ||||
| -rw-r--r-- | go.sum | 4 | ||||
| -rw-r--r-- | internal/pwhash/pwhash.go | 22 | ||||
| -rw-r--r-- | roseh.moe.go | 21 |
8 files changed, 129 insertions, 10 deletions
diff --git a/cmd/finditers/finditers.go b/cmd/finditers/finditers.go new file mode 100644 index 0000000..2a256c8 --- /dev/null +++ b/cmd/finditers/finditers.go @@ -0,0 +1,44 @@ +package main + +import ( + "encoding/hex" + "fmt" + "os" + "sort" + "testing" + "time" + + "gitlab.com/rhogenson/roseh.moe/internal/pwhash" +) + +var ( + iterations int + + salt, _ = hex.DecodeString("3fb84513fc3afcd6d3b230bf9ece91aaae2d2a99da17efbf7de83b21fafe3f08") +) + +func BenchmarkHashIter(b *testing.B) { + const password = "oboe shortness ether ideology undesired fresh freezable catching mashing glimpse" + for b.Loop() { + pwhash.HashIter(password, salt, iterations) + } +} + +func run() error { + for iterations = 8192; time.Duration(testing.Benchmark(BenchmarkHashIter).NsPerOp()) < 500*time.Millisecond; iterations *= 2 { + } + lo := iterations / 2 + hi := iterations + fmt.Println(lo + sort.Search(hi-lo, func(i int) bool { + iterations = lo + i + return time.Duration(testing.Benchmark(BenchmarkHashIter).NsPerOp()) > 500*time.Millisecond + })) + return nil +} + +func main() { + if err := run(); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} diff --git a/cmd/hashpw/hashpw.go b/cmd/hashpw/hashpw.go new file mode 100644 index 0000000..ffbdf79 --- /dev/null +++ b/cmd/hashpw/hashpw.go @@ -0,0 +1,34 @@ +package main + +import ( + "crypto/rand" + "fmt" + "os" + + "gitlab.com/rhogenson/roseh.moe/internal/pwhash" + "golang.org/x/term" +) + +func run() error { + fmt.Print("Enter password:") + password, err := term.ReadPassword(int(os.Stdin.Fd())) + fmt.Println() + if err != nil { + return err + } + salt := make([]byte, 32) + rand.Read(salt) + hashedPassword, err := pwhash.Hash(string(password), salt) + if err != nil { + return err + } + fmt.Printf("notepad-password=%x\nsalt=%x\n", hashedPassword, salt) + return nil +} + +func main() { + if err := run(); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1757545623, - "narHash": "sha256-mCxPABZ6jRjUQx3bPP4vjA68ETbPLNz9V2pk9tO7pRQ=", + "lastModified": 1758589230, + "narHash": "sha256-zMTCFGe8aVGTEr2RqUi/QzC1nOIQ0N1HRsbqB4f646k=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "8cd5ce828d5d1d16feff37340171a98fc3bf6526", + "rev": "d1d883129b193f0b495d75c148c2c3a7d95789a0", "type": "github" }, "original": { @@ -48,7 +48,7 @@ # remember to bump this hash when your dependencies change. # vendorHash = pkgs.lib.fakeHash; - vendorHash = null; + vendorHash = "sha256-L7nK+w4CB2H3b6vL0ZoFfaRMgCmpqzQo8ThMM60C76I="; }; }); @@ -1,3 +1,7 @@ module gitlab.com/rhogenson/roseh.moe -go 1.23.2 +go 1.24.0 + +require golang.org/x/term v0.35.0 + +require golang.org/x/sys v0.36.0 // indirect @@ -0,0 +1,4 @@ +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ= +golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA= diff --git a/internal/pwhash/pwhash.go b/internal/pwhash/pwhash.go new file mode 100644 index 0000000..a17ed2e --- /dev/null +++ b/internal/pwhash/pwhash.go @@ -0,0 +1,22 @@ +package pwhash + +import ( + "crypto/pbkdf2" + "crypto/sha256" + "crypto/sha512" +) + +const defaultIterations = 3670016 // from cmd/finditers + +func HashIter(password string, salt []byte, iter int) ([]byte, error) { + return pbkdf2.Key(sha256.New, password, salt, iter, 32) +} + +func Hash(password string, salt []byte) ([]byte, error) { + hashed, err := HashIter(password, salt, defaultIterations) + if err != nil { + return nil, err + } + sha := sha512.Sum512(hashed) + return sha[:], nil +} diff --git a/roseh.moe.go b/roseh.moe.go index b8e6e21..a15fd9b 100644 --- a/roseh.moe.go +++ b/roseh.moe.go @@ -4,7 +4,6 @@ import ( "bytes" "crypto/ed25519" "crypto/rand" - "crypto/sha512" "crypto/subtle" "embed" "encoding/base64" @@ -19,6 +18,8 @@ import ( "path/filepath" "strings" "time" + + "gitlab.com/rhogenson/roseh.moe/internal/pwhash" ) var ( @@ -30,9 +31,10 @@ var ( ) var ( - notepadPassword []byte - privateKey ed25519.PrivateKey - publicKey ed25519.PublicKey + notepadPassword []byte + notepadPasswordSalt []byte + privateKey ed25519.PrivateKey + publicKey ed25519.PublicKey ) func loadSecrets() error { @@ -56,6 +58,11 @@ func loadSecrets() error { } privateKey = ed25519.NewKeyFromSeed(seed) publicKey = privateKey.Public().(ed25519.PublicKey) + } else if salt, ok := bytes.CutPrefix(line, []byte("salt=")); ok { + notepadPasswordSalt = make([]byte, hex.DecodedLen(len(salt))) + if _, err := hex.Decode(notepadPasswordSalt, salt); err != nil { + return err + } } } return nil @@ -204,7 +211,11 @@ type loginTemplateArgs struct { } func login(w http.ResponseWriter, r *http.Request) { - pwHash := sha512.Sum512([]byte(r.FormValue("password"))) + 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 + } if subtle.ConstantTimeCompare(pwHash[:], notepadPassword) == 0 { if err := loginTemplate.Execute(w, loginTemplateArgs{Error: true, Redirect: r.FormValue("redirect")}); err != nil { log.Printf("Warning: login: %s", err) |
