diff options
Diffstat (limited to 'pwned.go')
-rw-r--r-- | pwned.go | 78 |
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) +} |