add reduced symbol set
This commit is contained in:
153
main.go
153
main.go
@@ -22,8 +22,11 @@ type WordData struct {
|
||||
|
||||
var words WordData
|
||||
|
||||
// Fixed symbol alphabet
|
||||
const symbolAlphabet = "!@#$%^&*()-+',?"
|
||||
// Symbol alphabets
|
||||
const (
|
||||
fullSymbolAlphabet = "!@#$%^&*()-+',?"
|
||||
reducedSymbolAlphabet = "@#!"
|
||||
)
|
||||
|
||||
type PageData struct {
|
||||
// Form values
|
||||
@@ -31,7 +34,8 @@ type PageData struct {
|
||||
MinLen int
|
||||
MaxLen int
|
||||
NumLen int
|
||||
SymbolCount int // symbols per separator
|
||||
SymbolCount int // symbols per separator
|
||||
SymbolSet string // "full" or "reduced"
|
||||
RandCaps string
|
||||
|
||||
// Stats
|
||||
@@ -47,58 +51,6 @@ type PageData struct {
|
||||
GeneratedPhrases []string
|
||||
}
|
||||
|
||||
// commonLogMiddleware logs requests in Apache/Nginx "Common Log Format"
|
||||
func commonLogMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// Wrap ResponseWriter so we can capture status/size
|
||||
lrw := &loggingResponseWriter{ResponseWriter: w, status: 200}
|
||||
|
||||
start := time.Now()
|
||||
next.ServeHTTP(lrw, r)
|
||||
duration := time.Since(start)
|
||||
|
||||
// Determine client IP
|
||||
ip := r.RemoteAddr
|
||||
if xf := r.Header.Get("X-Forwarded-For"); xf != "" {
|
||||
ip = xf
|
||||
}
|
||||
|
||||
// Common Log Format:
|
||||
// 127.0.0.1 - - [10/Oct/2000:13:55:36 -0700] "GET /path HTTP/1.1" 200 2326
|
||||
log.Printf(`%s - - [%s] "%s %s %s" %d %d "%s" "%s" %v`,
|
||||
ip,
|
||||
time.Now().Format("02/Jan/2006:15:04:05 -0700"),
|
||||
r.Method,
|
||||
r.RequestURI,
|
||||
r.Proto,
|
||||
lrw.status,
|
||||
lrw.bytes,
|
||||
r.Referer(),
|
||||
r.UserAgent(),
|
||||
duration,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
// loggingResponseWriter allows us to capture status code & bytes written
|
||||
type loggingResponseWriter struct {
|
||||
http.ResponseWriter
|
||||
status int
|
||||
bytes int
|
||||
}
|
||||
|
||||
func (lrw *loggingResponseWriter) WriteHeader(code int) {
|
||||
lrw.status = code
|
||||
lrw.ResponseWriter.WriteHeader(code)
|
||||
}
|
||||
|
||||
func (lrw *loggingResponseWriter) Write(b []byte) (int, error) {
|
||||
n, err := lrw.ResponseWriter.Write(b)
|
||||
lrw.bytes += n
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Single-file HTML template
|
||||
var pageTmpl = template.Must(template.New("page").Parse(`
|
||||
<!doctype html>
|
||||
@@ -150,6 +102,17 @@ var pageTmpl = template.Must(template.New("page").Parse(`
|
||||
<label for="symcount">Symbols per separator</label>
|
||||
<input type="number" id="symcount" name="symcount" min="0" value="{{.SymbolCount}}">
|
||||
</div>
|
||||
<div>
|
||||
<label for="symset">Symbol set</label>
|
||||
<select id="symset" name="symset">
|
||||
<option value="full" {{if or (eq .SymbolSet "") (eq .SymbolSet "full")}}selected{{end}}>
|
||||
Full (!@#$%^&*()-+',?)
|
||||
</option>
|
||||
<option value="reduced" {{if eq .SymbolSet "reduced"}}selected{{end}}>
|
||||
Reduced (@#!)
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="randcaps">Capitalization</label>
|
||||
<select id="randcaps" name="randcaps">
|
||||
@@ -218,6 +181,7 @@ func main() {
|
||||
words = wd
|
||||
log.Printf("Loaded %d distinct word lengths", len(words.ByLen))
|
||||
|
||||
// Wrap handler with common log middleware
|
||||
http.Handle("/", commonLogMiddleware(http.HandlerFunc(handlePassphrase)))
|
||||
|
||||
port := os.Getenv("PORT")
|
||||
@@ -265,14 +229,18 @@ func loadWordlist(path string) (WordData, error) {
|
||||
|
||||
func handlePassphrase(w http.ResponseWriter, r *http.Request) {
|
||||
wordcount := parseIntQuery(r, "wordcount", 3)
|
||||
minlen := parseIntQuery(r, "minlen", 5)
|
||||
minlen := parseIntQuery(r, "minlen", 1)
|
||||
maxlen := parseIntQuery(r, "maxlen", 8)
|
||||
numlen := parseIntQuery(r, "numlen", 1)
|
||||
numlen := parseIntQuery(r, "numlen", 2)
|
||||
symbolCount := parseIntQuery(r, "symcount", 1)
|
||||
randcaps := r.URL.Query().Get("randcaps")
|
||||
if randcaps == "" {
|
||||
randcaps = "first"
|
||||
}
|
||||
symbolSet := r.URL.Query().Get("symset")
|
||||
if symbolSet == "" {
|
||||
symbolSet = "full"
|
||||
}
|
||||
|
||||
if minlen < 1 {
|
||||
minlen = 1
|
||||
@@ -296,6 +264,7 @@ func handlePassphrase(w http.ResponseWriter, r *http.Request) {
|
||||
MaxLen: maxlen,
|
||||
NumLen: numlen,
|
||||
SymbolCount: symbolCount,
|
||||
SymbolSet: symbolSet,
|
||||
RandCaps: randcaps,
|
||||
}
|
||||
|
||||
@@ -310,6 +279,17 @@ func handlePassphrase(w http.ResponseWriter, r *http.Request) {
|
||||
avgLen := float64(totalLen) / float64(totalWords)
|
||||
wordBits := math.Log2(float64(totalWords))
|
||||
|
||||
// Determine symbol alphabet based on dropdown & symbolCount
|
||||
symAlphabet := ""
|
||||
if symbolCount > 0 {
|
||||
if symbolSet == "reduced" {
|
||||
symAlphabet = reducedSymbolAlphabet
|
||||
} else {
|
||||
symAlphabet = fullSymbolAlphabet
|
||||
}
|
||||
}
|
||||
symAlphabetSize := len(symAlphabet)
|
||||
|
||||
// Caps
|
||||
var capBitsPerWord float64
|
||||
var capMult float64
|
||||
@@ -329,7 +309,7 @@ func handlePassphrase(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// Separators: after every word if any digits or symbols are enabled
|
||||
segmentsEnabled := (numlen > 0 || symbolCount > 0)
|
||||
segmentsEnabled := (numlen > 0 || (symbolCount > 0 && symAlphabetSize > 0))
|
||||
segments := 0
|
||||
if segmentsEnabled {
|
||||
segments = wordcount
|
||||
@@ -338,7 +318,7 @@ func handlePassphrase(w http.ResponseWriter, r *http.Request) {
|
||||
// Bits per separator segment
|
||||
separatorBitsPerSegment := 0.0
|
||||
if segmentsEnabled {
|
||||
separatorBitsPerSegment = separatorBits(numlen, symbolCount, len(symbolAlphabet))
|
||||
separatorBitsPerSegment = separatorBits(numlen, symbolCount, symAlphabetSize)
|
||||
}
|
||||
|
||||
// Total bits
|
||||
@@ -350,7 +330,7 @@ func handlePassphrase(w http.ResponseWriter, r *http.Request) {
|
||||
combosCaps := math.Pow(capMult, float64(wordcount))
|
||||
segmentCombos := 1.0
|
||||
if segmentsEnabled {
|
||||
segmentCombos = math.Pow(separatorCombos(numlen, symbolCount, len(symbolAlphabet)), float64(segments))
|
||||
segmentCombos = math.Pow(separatorCombos(numlen, symbolCount, symAlphabetSize), float64(segments))
|
||||
}
|
||||
combos := combosPerWord * combosCaps * segmentCombos
|
||||
|
||||
@@ -367,7 +347,7 @@ func handlePassphrase(w http.ResponseWriter, r *http.Request) {
|
||||
data.EffectiveBits = int(passBits + 0.5)
|
||||
data.TotalCombinations = combosStr
|
||||
|
||||
data.GeneratedPhrases = generatePassphrases(usewords, wordcount, numlen, symbolCount, randcaps)
|
||||
data.GeneratedPhrases = generatePassphrases(usewords, wordcount, numlen, symbolCount, symAlphabet, randcaps)
|
||||
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
if err := pageTmpl.Execute(w, data); err != nil {
|
||||
@@ -433,13 +413,13 @@ func separatorBits(numlen, symCountPerSeg, symAlphabetSize int) float64 {
|
||||
}
|
||||
|
||||
// generatePassphrases builds N phrases with separators after each word when enabled.
|
||||
func generatePassphrases(usewords []string, wordcount, numlen, symCountPerSeg int, randcaps string) []string {
|
||||
func generatePassphrases(usewords []string, wordcount, numlen, symCountPerSeg int, symAlphabet string, randcaps string) []string {
|
||||
const numPhrases = 10
|
||||
if len(usewords) == 0 {
|
||||
return nil
|
||||
}
|
||||
segmentsEnabled := (numlen > 0 || symCountPerSeg > 0)
|
||||
symRunes := []rune(symbolAlphabet)
|
||||
segmentsEnabled := (numlen > 0 || (symCountPerSeg > 0 && len(symAlphabet) > 0))
|
||||
symRunes := []rune(symAlphabet)
|
||||
|
||||
phrases := make([]string, 0, numPhrases)
|
||||
for i := 0; i < numPhrases; i++ {
|
||||
@@ -593,3 +573,48 @@ func commafyInt(n int64) string {
|
||||
}
|
||||
return sign + strings.Join(parts, ",")
|
||||
}
|
||||
|
||||
// commonLogMiddleware logs requests in Apache/Nginx-style log format
|
||||
func commonLogMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
lrw := &loggingResponseWriter{ResponseWriter: w, status: 200}
|
||||
start := time.Now()
|
||||
next.ServeHTTP(lrw, r)
|
||||
duration := time.Since(start)
|
||||
|
||||
ip := r.RemoteAddr
|
||||
if xf := r.Header.Get("X-Forwarded-For"); xf != "" {
|
||||
ip = xf
|
||||
}
|
||||
|
||||
log.Printf(`%s - - [%s] "%s %s %s" %d %d "%s" "%s" %v`,
|
||||
ip,
|
||||
time.Now().Format("02/Jan/2006:15:04:05 -0700"),
|
||||
r.Method,
|
||||
r.RequestURI,
|
||||
r.Proto,
|
||||
lrw.status,
|
||||
lrw.bytes,
|
||||
r.Referer(),
|
||||
r.UserAgent(),
|
||||
duration,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
type loggingResponseWriter struct {
|
||||
http.ResponseWriter
|
||||
status int
|
||||
bytes int
|
||||
}
|
||||
|
||||
func (lrw *loggingResponseWriter) WriteHeader(code int) {
|
||||
lrw.status = code
|
||||
lrw.ResponseWriter.WriteHeader(code)
|
||||
}
|
||||
|
||||
func (lrw *loggingResponseWriter) Write(b []byte) (int, error) {
|
||||
n, err := lrw.ResponseWriter.Write(b)
|
||||
lrw.bytes += n
|
||||
return n, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user