diff options
| author | Rose Hogenson <rosehogenson@posteo.net> | 2025-12-07 14:46:56 -0800 |
|---|---|---|
| committer | Rose Hogenson <rosehogenson@posteo.net> | 2025-12-07 14:46:56 -0800 |
| commit | 66e4d0829d919d575bf23d5b6bb76e69bde0c3c8 (patch) | |
| tree | d3bb13e08de41763a22b560ff3aef241f3440706 | |
| parent | 1c63df9bb9be05182f681c96412cda304c5051f2 (diff) | |
| download | roseh.moe-66e4d0829d919d575bf23d5b6bb76e69bde0c3c8.tar.zst | |
Increase target security to 32 bytes
| -rw-r--r-- | default.nix | 2 | ||||
| -rw-r--r-- | go.mod | 5 | ||||
| -rw-r--r-- | go.sum | 4 | ||||
| -rw-r--r-- | internal/pwhash/pwhash.go | 18 | ||||
| -rw-r--r-- | roseh.moe.go | 12 | ||||
| -rw-r--r-- | tools/finditer/finditer.go | 42 | ||||
| -rw-r--r-- | tools/hashpw/hashpw.go | 35 |
7 files changed, 109 insertions, 9 deletions
diff --git a/default.nix b/default.nix index 20f2454..7610d8b 100644 --- a/default.nix +++ b/default.nix @@ -16,5 +16,5 @@ pkgs.buildGoModule rec { # remember to bump this hash when your dependencies change. # vendorHash = pkgs.lib.fakeHash; - vendorHash = "sha256-X+zeW+5aFOaVCLIzV08s0jP/69R6X9r9vyM8+fNr4KE="; + vendorHash = "sha256-iIQ/AwhRYTA2sMtmBUh1lyQ4Uj4dZk8ESUyuNjyc8/Y="; } @@ -1,9 +1,12 @@ -module roseh.moe/pkg/roseh.moe +module roseh.moe/cmd/roseh.moe go 1.25 require ( github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e + golang.org/x/term v0.37.0 roseh.moe/pkg/ccl v1.0.0 roseh.moe/pkg/wordlist v1.0.2 ) + +require golang.org/x/sys v0.38.0 // indirect @@ -2,6 +2,10 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= +golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= roseh.moe/pkg/ccl v1.0.0 h1:S1Y5zY2aoZMYefob416lezVrc1vPd/g8Rw7EAEEgR8M= roseh.moe/pkg/ccl v1.0.0/go.mod h1:OCcZ30pQDtjXgqgpN8eLo6f/pTi6XVKIHPs1k+gups0= roseh.moe/pkg/wordlist v1.0.2 h1:riB2RqCU5zXfCQjaPHYTXV/688FV9/Bp6Hnb0IAkP9c= diff --git a/internal/pwhash/pwhash.go b/internal/pwhash/pwhash.go new file mode 100644 index 0000000..22b5368 --- /dev/null +++ b/internal/pwhash/pwhash.go @@ -0,0 +1,18 @@ +package pwhash + +import ( + "crypto/pbkdf2" + "crypto/sha512" +) + +const SaltSize = 8 + +const defaultIter = 12500992 // from tools/finditer + +func HashIter(password string, salt []byte, iter int) ([]byte, error) { + return pbkdf2.Key(sha512.New, password, salt, iter, 32) +} + +func Hash(password string, salt []byte) ([]byte, error) { + return HashIter(password, salt, defaultIter) +} diff --git a/roseh.moe.go b/roseh.moe.go index 6d1ccea..5791f46 100644 --- a/roseh.moe.go +++ b/roseh.moe.go @@ -24,6 +24,7 @@ import ( "time" "github.com/skip2/go-qrcode" + "roseh.moe/cmd/roseh.moe/internal/pwhash" "roseh.moe/pkg/ccl" "roseh.moe/pkg/wordlist" ) @@ -39,8 +40,6 @@ var ( serverStartTime = time.Now() ) -const targetSecurityLevel = 16 - var notepadPassword, secretKey []byte func loadSecrets() error { @@ -322,14 +321,14 @@ func wormholeQR(w http.ResponseWriter, r *http.Request) { w.Write(qr) } -const macSize = targetSecurityLevel +const macSize = sha256.Size func mac(msg []byte, purpose string) []byte { mac := hmac.New(sha256.New, secretKey) io.WriteString(mac, purpose) io.WriteString(mac, ":") mac.Write(msg) - return mac.Sum(nil)[:macSize] + return mac.Sum(nil) } func verify(msg, messageMAC []byte, purpose string) bool { @@ -422,9 +421,8 @@ func cookieAuth(w http.ResponseWriter, r *http.Request) bool { } func checkPassword(password string) bool { - const pwHashSize = targetSecurityLevel - hash := sha256.Sum256([]byte(password)) - return subtle.ConstantTimeCompare(hash[:pwHashSize], notepadPassword) != 0 + hash, err := pwhash.Hash(password, notepadPassword[:pwhash.SaltSize]) + return err == nil && subtle.ConstantTimeCompare(hash, notepadPassword[pwhash.SaltSize:]) != 0 } var ( diff --git a/tools/finditer/finditer.go b/tools/finditer/finditer.go new file mode 100644 index 0000000..e788d89 --- /dev/null +++ b/tools/finditer/finditer.go @@ -0,0 +1,42 @@ +package main + +import ( + "encoding/hex" + "fmt" + "sort" + "testing" + "time" + + "roseh.moe/cmd/roseh.moe/internal/pwhash" +) + +func mustHex(s string) []byte { + b, err := hex.DecodeString(s) + if err != nil { + panic(err) + } + return b +} + +var salt = mustHex("34539b5dd2591415") + +func benchmarkHashIter(iter int) time.Duration { + const password = "wavy upend 4z jk linda k zing four atop pi" + return time.Duration(testing.Benchmark(func(b *testing.B) { + for b.Loop() { + pwhash.HashIter(password, salt, iter) + } + }).NsPerOp()) +} + +func main() { + const targetTime = 5 * time.Second + iter := 8192 + for ; benchmarkHashIter(iter) < targetTime; iter *= 2 { + } + lo := iter / 2 + hi := iter + fmt.Println(lo + sort.Search(hi-lo, func(n int) bool { + return benchmarkHashIter(lo+n) > targetTime + })) +} diff --git a/tools/hashpw/hashpw.go b/tools/hashpw/hashpw.go new file mode 100644 index 0000000..f1bc10d --- /dev/null +++ b/tools/hashpw/hashpw.go @@ -0,0 +1,35 @@ +package main + +import ( + "crypto/rand" + "encoding/base64" + "fmt" + "os" + + "golang.org/x/term" + "roseh.moe/cmd/roseh.moe/internal/pwhash" +) + +func hashpw() error { + fmt.Fprint(os.Stderr, "Enter password: ") + pw, err := term.ReadPassword(int(os.Stdin.Fd())) + fmt.Fprintln(os.Stderr) + if err != nil { + return err + } + salt := make([]byte, pwhash.SaltSize) + rand.Read(salt) + pwHash, err := pwhash.Hash(string(pw), salt) + if err != nil { + return err + } + fmt.Printf("NotepadPassword:\"%s\"\n", base64.StdEncoding.EncodeToString(append(salt, pwHash...))) + return nil +} + +func main() { + if err := hashpw(); err != nil { + fmt.Fprintf(os.Stderr, "hashpw: %s\n", err) + os.Exit(1) + } +} |
