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) }