From 1111852adfbbe14bfa22524f177151beebc7ac16 Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Sat, 24 Nov 2018 18:28:58 +0100 Subject: [PATCH] Added password generation and hashing helper --- go.mod | 1 + go.sum | 2 + main.go | 33 +++ .../github.com/sethvargo/go-password/LICENSE | 20 ++ .../go-password/password/generate.go | 242 ++++++++++++++++++ vendor/modules.txt | 2 + 6 files changed, 300 insertions(+) create mode 100644 vendor/github.com/sethvargo/go-password/LICENSE create mode 100644 vendor/github.com/sethvargo/go-password/password/generate.go diff --git a/go.mod b/go.mod index 1768b90..63c4474 100644 --- a/go.mod +++ b/go.mod @@ -2,6 +2,7 @@ module ShareDAV require ( github.com/BurntSushi/toml v0.3.1 + github.com/sethvargo/go-password v0.1.2 golang.org/x/crypto v0.0.0-20181112202954-3d3f9f413869 golang.org/x/net v0.0.0-20181114220301-adae6a3d119a ) diff --git a/go.sum b/go.sum index a0753c4..9dcb364 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/sethvargo/go-password v0.1.2 h1:fhBF4thiPVKEZ7R6+CX46GWJiPyCyXshbeqZ7lqEeYo= +github.com/sethvargo/go-password v0.1.2/go.mod h1:qKHfdSjT26DpHQWHWWR5+X4BI45jT31dg6j4RI2TEb0= golang.org/x/crypto v0.0.0-20181112202954-3d3f9f413869 h1:kkXA53yGe04D0adEYJwEVQjeBppL01Exg+fnMjfUraU= golang.org/x/crypto v0.0.0-20181112202954-3d3f9f413869/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a h1:gOpx8G595UYyvj8UK4+OFyY4rx037g3fmfhe5SasG3U= diff --git a/main.go b/main.go index 67948ef..d3029ad 100644 --- a/main.go +++ b/main.go @@ -28,15 +28,48 @@ package main import ( "context" "flag" + "fmt" + pwgen "github.com/sethvargo/go-password/password" + "golang.org/x/crypto/bcrypt" "golang.org/x/net/webdav" + "math/rand" "net/http" + "time" ) var configFile = flag.String("config", "sharedav.toml", "Config file to be used.") +var genPassword = flag.Bool("genpass", false, "If set, a password will be generated and hashed.") +var hashPassword = flag.String("hashpass", "", "If set, the given password will be hashed.") func main() { flag.Parse() + if *genPassword { + // math.rand is not secure. For determining the number of digits in a password + // it should suffice, though. + rand.Seed(time.Now().UnixNano()) + pwLen := 32 + pw, err := pwgen.Generate(pwLen, rand.Intn(pwLen/2), 0, false, true) + if err != nil { + panic(err) + } + hash, err := bcrypt.GenerateFromPassword([]byte(pw), 0) + if err != nil { + panic(err) + } + + fmt.Printf("Password: %s\n", pw) + fmt.Printf(" Hash: %s\n", hash) + return + } else if *hashPassword != "" { + hash, err := bcrypt.GenerateFromPassword([]byte(*hashPassword), 0) + if err != nil { + panic(err) + } + fmt.Println(string(hash)) + return + } + c := LoadConfig(*configFile) h := &webdav.Handler{} diff --git a/vendor/github.com/sethvargo/go-password/LICENSE b/vendor/github.com/sethvargo/go-password/LICENSE new file mode 100644 index 0000000..c62ec3d --- /dev/null +++ b/vendor/github.com/sethvargo/go-password/LICENSE @@ -0,0 +1,20 @@ +Copyright 2017 Seth Vargo + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/sethvargo/go-password/password/generate.go b/vendor/github.com/sethvargo/go-password/password/generate.go new file mode 100644 index 0000000..176e00a --- /dev/null +++ b/vendor/github.com/sethvargo/go-password/password/generate.go @@ -0,0 +1,242 @@ +// Package password provides a library for generating high-entropy random +// password strings via the crypto/rand package. +// +// res, err := Generate(64, 10, 10, false, false) +// if err != nil { +// log.Fatal(err) +// } +// log.Printf(res) +// +// Most functions are safe for concurrent use. +package password + +import ( + "crypto/rand" + "errors" + "math/big" + "strings" +) + +const ( + // LowerLetters is the list of lowercase letters. + LowerLetters = "abcdefghijklmnopqrstuvwxyz" + + // UpperLetters is the list of uppercase letters. + UpperLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + + // Digits is the list of permitted digits. + Digits = "0123456789" + + // Symbols is the list of symbols. + Symbols = "~!@#$%^&*()_+`-={}|[]\\:\"<>?,./" +) + +var ( + // ErrExceedsTotalLength is the error returned with the number of digits and + // symbols is greater than the total length. + ErrExceedsTotalLength = errors.New("number of digits and symbols must be less than total length") + + // ErrLettersExceedsAvailable is the error returned with the number of letters + // exceeds the number of available letters and repeats are not allowed. + ErrLettersExceedsAvailable = errors.New("number of letters exceeds available letters and repeats are not allowed") + + // ErrDigitsExceedsAvailable is the error returned with the number of digits + // exceeds the number of available digits and repeats are not allowed. + ErrDigitsExceedsAvailable = errors.New("number of digits exceeds available digits and repeats are not allowed") + + // ErrSymbolsExceedsAvailable is the error returned with the number of symbols + // exceeds the number of available symbols and repeats are not allowed. + ErrSymbolsExceedsAvailable = errors.New("number of symbols exceeds available symbols and repeats are not allowed") +) + +// Generator is the stateful generator which can be used to customize the list +// of letters, digits, and/or symbols. +type Generator struct { + lowerLetters string + upperLetters string + digits string + symbols string +} + +// GeneratorInput is used as input to the NewGenerator function. +type GeneratorInput struct { + LowerLetters string + UpperLetters string + Digits string + Symbols string +} + +// NewGenerator creates a new Generator from the specified configuration. If no +// input is given, all the default values are used. This function is safe for +// concurrent use. +func NewGenerator(i *GeneratorInput) (*Generator, error) { + if i == nil { + i = new(GeneratorInput) + } + + g := &Generator{ + lowerLetters: i.LowerLetters, + upperLetters: i.UpperLetters, + digits: i.Digits, + symbols: i.Symbols, + } + + if g.lowerLetters == "" { + g.lowerLetters = LowerLetters + } + + if g.upperLetters == "" { + g.upperLetters = UpperLetters + } + + if g.digits == "" { + g.digits = Digits + } + + if g.symbols == "" { + g.symbols = Symbols + } + + return g, nil +} + +// Generate generates a password with the given requirements. length is the +// total number of characters in the password. numDigits is the number of digits +// to include in the result. numSymbols is the number of symbols to include in +// the result. noUpper excludes uppercase letters from the results. allowRepeat +// allows characters to repeat. +// +// The algorithm is fast, but it's not designed to be performant; it favors +// entropy over speed. This function is safe for concurrent use. +func (g *Generator) Generate(length, numDigits, numSymbols int, noUpper, allowRepeat bool) (string, error) { + letters := g.lowerLetters + if !noUpper { + letters += g.upperLetters + } + + chars := length - numDigits - numSymbols + if chars < 0 { + return "", ErrExceedsTotalLength + } + + if !allowRepeat && chars > len(letters) { + return "", ErrLettersExceedsAvailable + } + + if !allowRepeat && numDigits > len(g.digits) { + return "", ErrDigitsExceedsAvailable + } + + if !allowRepeat && numSymbols > len(g.symbols) { + return "", ErrSymbolsExceedsAvailable + } + + var result string + + // Characters + for i := 0; i < chars; i++ { + ch, err := randomElement(letters) + if err != nil { + return "", err + } + + if !allowRepeat && strings.Contains(result, ch) { + i-- + continue + } + + result, err = randomInsert(result, ch) + if err != nil { + return "", err + } + } + + // Digits + for i := 0; i < numDigits; i++ { + d, err := randomElement(g.digits) + if err != nil { + return "", err + } + + if !allowRepeat && strings.Contains(result, d) { + i-- + continue + } + + result, err = randomInsert(result, d) + if err != nil { + return "", err + } + } + + // Symbols + for i := 0; i < numSymbols; i++ { + sym, err := randomElement(g.symbols) + if err != nil { + return "", err + } + + if !allowRepeat && strings.Contains(result, sym) { + i-- + continue + } + + result, err = randomInsert(result, sym) + if err != nil { + return "", err + } + } + + return result, nil +} + +// MustGenerate is the same as Generate, but panics on error. +func (g *Generator) MustGenerate(length, numDigits, numSymbols int, noUpper, allowRepeat bool) string { + res, err := g.Generate(length, numDigits, numSymbols, noUpper, allowRepeat) + if err != nil { + panic(err) + } + return res +} + +// See Generator.Generate for usage. +func Generate(length, numDigits, numSymbols int, noUpper, allowRepeat bool) (string, error) { + gen, err := NewGenerator(nil) + if err != nil { + return "", err + } + + return gen.Generate(length, numDigits, numSymbols, noUpper, allowRepeat) +} + +// See Generator.MustGenerate for usage. +func MustGenerate(length, numDigits, numSymbols int, noUpper, allowRepeat bool) string { + res, err := Generate(length, numDigits, numSymbols, noUpper, allowRepeat) + if err != nil { + panic(err) + } + return res +} + +// randomInsert randomly inserts the given value into the given string. +func randomInsert(s, val string) (string, error) { + if s == "" { + return val, nil + } + + n, err := rand.Int(rand.Reader, big.NewInt(int64(len(s)+1))) + if err != nil { + return "", err + } + i := n.Int64() + return s[0:i] + val + s[i:len(s)], nil +} + +// randomElement extracts a random element from the given string. +func randomElement(s string) (string, error) { + n, err := rand.Int(rand.Reader, big.NewInt(int64(len(s)))) + if err != nil { + return "", err + } + return string(s[n.Int64()]), nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 69906bd..13fe18a 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,5 +1,7 @@ # github.com/BurntSushi/toml v0.3.1 github.com/BurntSushi/toml +# github.com/sethvargo/go-password v0.1.2 +github.com/sethvargo/go-password/password # golang.org/x/crypto v0.0.0-20181112202954-3d3f9f413869 golang.org/x/crypto/bcrypt golang.org/x/crypto/blowfish