Created
February 27, 2018 14:58
-
-
Save betawaffle/a8d1162af27ff7b48c2450eaf1260477 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"bytes" | |
"crypto/sha1" | |
"encoding/hex" | |
"errors" | |
"fmt" | |
"io" | |
"io/ioutil" | |
"log" | |
"net/http" | |
"os" | |
"strings" | |
"golang.org/x/crypto/ssh/terminal" | |
) | |
func main() { | |
if terminal.IsTerminal(0) { | |
log.SetPrefix("[!] ") | |
log.SetFlags(log.Lmicroseconds) | |
} else { | |
log.Fatalf("stdin is not a terminal") | |
} | |
fmt.Fprintf(os.Stdin, "Password: ") | |
pass, err := terminal.ReadPassword(0) | |
fmt.Fprintln(os.Stdin) | |
if err != nil { | |
log.Fatal(err) | |
} | |
if err := checkPassword(pass); err != nil { | |
log.Fatal(err) | |
} | |
fmt.Printf("Nope, all good!") | |
} | |
func checkPassword(pass []byte) error { | |
sum := sha1.Sum(pass) | |
str := hex.EncodeToString(sum[:]) | |
r, err := http.Get("https://api.pwnedpasswords.com/range/" + str[:5]) | |
if err != nil { | |
return err | |
} | |
b, err := readClose(r.Body) | |
if err != nil { | |
return err | |
} | |
if r.StatusCode != 200 { | |
return fmt.Errorf("HTTP %d: body=%q", r.StatusCode, b) | |
} | |
if string(b[:len("\ufeff")]) == "\ufeff" { | |
b = b[len("\ufeff"):] | |
} | |
return checkSuffixCount(b, strings.ToUpper(str[5:])) | |
} | |
func checkSuffixCount(b []byte, suffix string) error { | |
const ( | |
prefixLen = 5 | |
suffixLen = sha1.Size*2 - prefixLen | |
) | |
if len(suffix) != suffixLen { | |
return errors.New("incorrect suffix length") | |
} | |
for len(b) >= suffixLen+2 { | |
if b[suffixLen] != ':' { | |
return fmt.Errorf("invalid response: missing colon in %q", b) | |
} | |
var match, count []byte | |
match, b = b[:suffixLen], b[suffixLen+1:] | |
if i := bytes.IndexByte(b, '\n'); i != -1 { | |
if b[i-1] == '\r' { | |
count, b = b[:i-1], b[i+1:] | |
} else { | |
count, b = b[:i], b[i+1:] | |
} | |
} else { | |
count, b = b, nil | |
} | |
if string(match) == suffix { | |
return fmt.Errorf("password has been pwned at least %d times", count) | |
} | |
} | |
return nil | |
} | |
func readClose(rc io.ReadCloser) ([]byte, error) { | |
b, err := ioutil.ReadAll(rc) | |
rc.Close() | |
return b, err | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment