summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--default.nix2
-rw-r--r--go.mod5
-rw-r--r--go.sum4
-rw-r--r--internal/pwhash/pwhash.go18
-rw-r--r--roseh.moe.go12
-rw-r--r--tools/finditer/finditer.go42
-rw-r--r--tools/hashpw/hashpw.go35
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=";
}
diff --git a/go.mod b/go.mod
index 2e8667e..aff7edd 100644
--- a/go.mod
+++ b/go.mod
@@ -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
diff --git a/go.sum b/go.sum
index b57aaeb..1e01e29 100644
--- a/go.sum
+++ b/go.sum
@@ -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)
+ }
+}