mirror of
https://github.com/Luzifer/password.git
synced 2024-12-20 04:41:17 +00:00
Add ability to check the passwords against HIBP database
Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
parent
d5e96482dc
commit
8985ecda87
2 changed files with 66 additions and 1 deletions
18
cmdGet.go
18
cmdGet.go
|
@ -5,9 +5,10 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/Luzifer/password/hasher"
|
||||
pwd "github.com/Luzifer/password/lib"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func getCmdGet() *cobra.Command {
|
||||
|
@ -25,6 +26,8 @@ func getCmdGet() *cobra.Command {
|
|||
cmd.Flags().BoolVarP(&flags.CLI.XKCD, "xkcd", "x", false, "use XKCD style password")
|
||||
cmd.Flags().BoolVarP(&flags.CLI.PrependDate, "date", "d", true, "prepend current date to XKCD style passwords")
|
||||
|
||||
cmd.Flags().Bool("check-hibp", false, "Check HaveIBeenPwned for this password")
|
||||
|
||||
return &cmd
|
||||
}
|
||||
|
||||
|
@ -36,12 +39,25 @@ func actionCmdGet(cmd *cobra.Command, args []string) {
|
|||
|
||||
for i := 0; i < flags.CLI.Num; i++ {
|
||||
|
||||
regenerate:
|
||||
if flags.CLI.XKCD {
|
||||
password, err = pwd.DefaultXKCD.GeneratePassword(flags.CLI.Length, flags.CLI.PrependDate)
|
||||
} else {
|
||||
password, err = pwd.NewSecurePassword().GeneratePassword(flags.CLI.Length, flags.CLI.SpecialCharacters)
|
||||
}
|
||||
|
||||
if c, _ := cmd.Flags().GetBool("check-hibp"); c {
|
||||
switch pwd.CheckHIBPPasswordHash(password) {
|
||||
case pwd.ErrPasswordInBreach:
|
||||
goto regenerate
|
||||
case nil:
|
||||
// Just do nothing
|
||||
default:
|
||||
fmt.Printf("Unable to check for password pwnage: %s", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
switch {
|
||||
case err == pwd.ErrLengthTooLow:
|
||||
|
|
49
lib/hibp.go
Normal file
49
lib/hibp.go
Normal file
|
@ -0,0 +1,49 @@
|
|||
package securepassword
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/sha1"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// ErrPasswordInBreach signals the password passed was found in any
|
||||
// breach at least once. The password should not be used if this
|
||||
// error is returned.
|
||||
var ErrPasswordInBreach = errors.New("Given password is known to HaveIBeenPwned")
|
||||
|
||||
// CheckHIBPPasswordHash accesses the HaveIBeenPwned API with the
|
||||
// first 5 characters of the SHA1 hash of the password and scans the
|
||||
// result for the password hash. If the hash is found the
|
||||
// ErrPasswordInBreach error is thrown. In case of an HTTP error
|
||||
// another error is thrown. The result will be nil when the password
|
||||
// hash was not returned in the API output.
|
||||
//
|
||||
// See more details at https://haveibeenpwned.com/API/v2#PwnedPasswords
|
||||
func CheckHIBPPasswordHash(password string) error {
|
||||
fullHash := fmt.Sprintf("%x", sha1.Sum([]byte(password)))
|
||||
checkHash := fullHash[0:5]
|
||||
|
||||
resp, err := http.Get("https://api.pwnedpasswords.com/range/" + checkHash)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "HTTP request failed")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Response format:
|
||||
// 0018A45C4D1DEF81644B54AB7F969B88D65:1
|
||||
// 00D4F6E8FA6EECAD2A3AA415EEC418D38EC:2
|
||||
|
||||
scanner := bufio.NewScanner(resp.Body)
|
||||
for scanner.Scan() {
|
||||
if strings.HasPrefix(scanner.Text(), fullHash) {
|
||||
// We don't care for the exact number but only for a match
|
||||
return ErrPasswordInBreach
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
Loading…
Reference in a new issue