mirror of
https://github.com/Luzifer/password.git
synced 2024-12-20 04:41:17 +00:00
Add XKCD style password generator
Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
parent
2d5397c819
commit
cb3fd042e1
5 changed files with 4022 additions and 5 deletions
11
Makefile
11
Makefile
|
@ -1,15 +1,16 @@
|
|||
default: pack
|
||||
|
||||
compile_coffee:
|
||||
coffee -c frontend/assets/application.coffee
|
||||
coffee -c frontend/assets/application.coffee
|
||||
|
||||
debug: compile_coffee
|
||||
go-bindata --debug frontend/...
|
||||
go run *.go serve
|
||||
open http://127.0.0.1:3000/
|
||||
go-bindata --debug frontend/...
|
||||
go run *.go serve
|
||||
open http://127.0.0.1:3000/
|
||||
|
||||
pack: compile_coffee
|
||||
go-bindata frontend/...
|
||||
go-bindata frontend/...
|
||||
bash generateXKCDWordList.sh
|
||||
|
||||
publish:
|
||||
curl -sSLo golang.sh https://raw.githubusercontent.com/Luzifer/github-publish/master/golang.sh
|
||||
|
|
22
generateXKCDWordList.sh
Normal file
22
generateXKCDWordList.sh
Normal file
|
@ -0,0 +1,22 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -eu
|
||||
set -o pipefail
|
||||
|
||||
VERSION="8744120d"
|
||||
SOURCE="https://cdn.rawgit.com/leonardr/olipy/${VERSION}/data/more-corpora/scribblenauts_words.txt"
|
||||
|
||||
WORDS=$(curl -sL "${SOURCE}" | awk '/^[a-z]{4}[a-z]*$/{ print "\""$1"\"," }')
|
||||
|
||||
cat -s <<EOF > lib/xkcd_words.go
|
||||
package securepassword
|
||||
|
||||
// xkcdWordList contains a list of words derived from the scribblenauts
|
||||
// word list inside the olipy library by leonardr
|
||||
// https://github.com/leonardr/olipy
|
||||
var xkcdWordList = []string{
|
||||
${WORDS}
|
||||
}
|
||||
EOF
|
||||
|
||||
gofmt -s -w lib/xkcd_words.go
|
56
lib/xkcd.go
Normal file
56
lib/xkcd.go
Normal file
|
@ -0,0 +1,56 @@
|
|||
package securepassword
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Luzifer/go_helpers/str"
|
||||
)
|
||||
|
||||
type XKCD struct{}
|
||||
|
||||
var (
|
||||
// ErrTooFewWords represents an error thrown if the password will
|
||||
// have fewer than four words and are not considered to be safe
|
||||
ErrTooFewWords = errors.New("XKCD passwords with less than 4 words makes no sense")
|
||||
// DefaultXKCD contains an default instance of the XKCD password
|
||||
// generator
|
||||
DefaultXKCD = NewXKCDGenerator()
|
||||
)
|
||||
|
||||
// NewXKCDGenerator initializes a new XKCD password generator
|
||||
// https://xkcd.com/936/
|
||||
func NewXKCDGenerator() *XKCD { return &XKCD{} }
|
||||
|
||||
// GeneratePassword generates a password with the number of words
|
||||
// given and optionally the current date prepended
|
||||
func (x XKCD) GeneratePassword(length int, addDate bool) (string, error) {
|
||||
if length < 4 {
|
||||
return "", ErrTooFewWords
|
||||
}
|
||||
|
||||
var (
|
||||
password string
|
||||
usedWords []string
|
||||
)
|
||||
|
||||
if addDate {
|
||||
password = time.Now().Format("20060102.")
|
||||
}
|
||||
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
for len(usedWords) < length {
|
||||
word := strings.Title(xkcdWordList[rand.Intn(len(xkcdWordList))])
|
||||
if str.StringInSlice(word, usedWords) {
|
||||
// Don't use a word twice
|
||||
continue
|
||||
}
|
||||
|
||||
password = password + word
|
||||
usedWords = append(usedWords, word)
|
||||
}
|
||||
|
||||
return password, nil
|
||||
}
|
50
lib/xkcd_test.go
Normal file
50
lib/xkcd_test.go
Normal file
|
@ -0,0 +1,50 @@
|
|||
package securepassword
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestXKCDWordList(t *testing.T) {
|
||||
if w := len(xkcdWordList); w < 1000 {
|
||||
t.Fatalf("Word list is expected to contain at least 1000 words, has %d", w)
|
||||
}
|
||||
}
|
||||
|
||||
func TestXKCDGeneratePassword(t *testing.T) {
|
||||
for i := 4; i < 20; i++ {
|
||||
pwd, err := DefaultXKCD.GeneratePassword(i, false)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Generated had an error: %s", err)
|
||||
}
|
||||
|
||||
if !regexp.MustCompile(fmt.Sprintf("^([A-Z][a-z]+){%d}$", i)).MatchString(pwd) {
|
||||
t.Errorf("Password %q is expected to contain %d words, did not match expected RegEx", pwd, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestXKCDDatePrepend(t *testing.T) {
|
||||
pwd, err := DefaultXKCD.GeneratePassword(4, true)
|
||||
if err != nil {
|
||||
t.Fatalf("Generated had an error: %s", err)
|
||||
}
|
||||
|
||||
if !regexp.MustCompile(`^[0-9]{8}\.([A-Z][a-z]+){4}$`).MatchString(pwd) {
|
||||
t.Errorf("Password %q did not match expected RegEx", pwd)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGeneratePasswords4Words(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
DefaultXKCD.GeneratePassword(4, false)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGeneratePasswords20Words(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
DefaultXKCD.GeneratePassword(20, false)
|
||||
}
|
||||
}
|
3888
lib/xkcd_words.go
Normal file
3888
lib/xkcd_words.go
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue