1
0
Fork 0
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:
Knut Ahlers 2019-01-31 23:42:42 +01:00
parent d5e96482dc
commit 8985ecda87
Signed by: luzifer
GPG key ID: DC2729FDD34BE99E
2 changed files with 66 additions and 1 deletions

View file

@ -5,9 +5,10 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/spf13/cobra"
"github.com/Luzifer/password/hasher" "github.com/Luzifer/password/hasher"
pwd "github.com/Luzifer/password/lib" pwd "github.com/Luzifer/password/lib"
"github.com/spf13/cobra"
) )
func getCmdGet() *cobra.Command { 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.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().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 return &cmd
} }
@ -36,12 +39,25 @@ func actionCmdGet(cmd *cobra.Command, args []string) {
for i := 0; i < flags.CLI.Num; i++ { for i := 0; i < flags.CLI.Num; i++ {
regenerate:
if flags.CLI.XKCD { if flags.CLI.XKCD {
password, err = pwd.DefaultXKCD.GeneratePassword(flags.CLI.Length, flags.CLI.PrependDate) password, err = pwd.DefaultXKCD.GeneratePassword(flags.CLI.Length, flags.CLI.PrependDate)
} else { } else {
password, err = pwd.NewSecurePassword().GeneratePassword(flags.CLI.Length, flags.CLI.SpecialCharacters) 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 { if err != nil {
switch { switch {
case err == pwd.ErrLengthTooLow: case err == pwd.ErrLengthTooLow:

49
lib/hibp.go Normal file
View 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
}