summaryrefslogtreecommitdiff
path: root/pwned.go
diff options
context:
space:
mode:
Diffstat (limited to 'pwned.go')
-rw-r--r--pwned.go78
1 files changed, 78 insertions, 0 deletions
diff --git a/pwned.go b/pwned.go
new file mode 100644
index 0000000..a965c4d
--- /dev/null
+++ b/pwned.go
@@ -0,0 +1,78 @@
+package main
+
+// Adapted from https://github.com/lenartj/pwned-passwords
+import (
+ "crypto/sha1"
+ "fmt"
+ "io"
+ "log"
+ "os"
+ "sort"
+ "strings"
+)
+
+type pwdb struct {
+ f *os.File
+ n int
+ rs int
+ hash_length int
+}
+
+func NewPWDB(fn string) (*pwdb, error) {
+ f, err := os.Open(fn)
+ if err != nil {
+ return nil, err
+ }
+
+ stat, err := f.Stat()
+ if err != nil {
+ return nil, err
+ }
+
+ const rs = 63 // V2 has fixed width of 63 bytes
+ if stat.Size()%rs != 0 {
+ return nil, fmt.Errorf("Unexpected password file format (must be a text file with 63 char width starting with sha1)")
+ }
+ const hash_length = 40 // sha1 is 40 chars
+
+ return &pwdb{f, int(stat.Size() / rs), rs, hash_length}, nil
+}
+
+func (db *pwdb) record(i int) string {
+ b := make([]byte, db.hash_length)
+ db.f.ReadAt(b, int64(i*db.rs))
+ return string(b)
+}
+
+func (db *pwdb) SearchHash(hash string) bool {
+ if len(hash) != db.hash_length {
+ return false
+ }
+ needle := strings.ToUpper(hash)
+
+ i := sort.Search(db.n, func(i int) bool {
+ return db.record(i) >= needle
+ })
+ return i < db.n && db.record(i) == needle
+}
+
+func (db *pwdb) SearchPassword(password string) bool {
+ h := sha1.New()
+ io.WriteString(h, password)
+ return db.SearchHash(fmt.Sprintf("%x", h.Sum(nil)))
+}
+
+func (db *pwdb) Close() {
+ db.f.Close()
+}
+
+func IsPasswordCompromised(password string) bool {
+ pwndDB, err := NewPWDB(pwman.PwnedDBFile)
+ if err != nil {
+ log.Println("ERROR", "pwnedb", err)
+ return false
+ }
+ defer pwndDB.Close()
+
+ return pwndDB.SearchPassword(password)
+}