add reduced symbol set
This commit is contained in:
151
main.go
151
main.go
@@ -22,8 +22,11 @@ type WordData struct {
|
|||||||
|
|
||||||
var words WordData
|
var words WordData
|
||||||
|
|
||||||
// Fixed symbol alphabet
|
// Symbol alphabets
|
||||||
const symbolAlphabet = "!@#$%^&*()-+',?"
|
const (
|
||||||
|
fullSymbolAlphabet = "!@#$%^&*()-+',?"
|
||||||
|
reducedSymbolAlphabet = "@#!"
|
||||||
|
)
|
||||||
|
|
||||||
type PageData struct {
|
type PageData struct {
|
||||||
// Form values
|
// Form values
|
||||||
@@ -32,6 +35,7 @@ type PageData struct {
|
|||||||
MaxLen int
|
MaxLen int
|
||||||
NumLen int
|
NumLen int
|
||||||
SymbolCount int // symbols per separator
|
SymbolCount int // symbols per separator
|
||||||
|
SymbolSet string // "full" or "reduced"
|
||||||
RandCaps string
|
RandCaps string
|
||||||
|
|
||||||
// Stats
|
// Stats
|
||||||
@@ -47,58 +51,6 @@ type PageData struct {
|
|||||||
GeneratedPhrases []string
|
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
|
// Single-file HTML template
|
||||||
var pageTmpl = template.Must(template.New("page").Parse(`
|
var pageTmpl = template.Must(template.New("page").Parse(`
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
@@ -150,6 +102,17 @@ var pageTmpl = template.Must(template.New("page").Parse(`
|
|||||||
<label for="symcount">Symbols per separator</label>
|
<label for="symcount">Symbols per separator</label>
|
||||||
<input type="number" id="symcount" name="symcount" min="0" value="{{.SymbolCount}}">
|
<input type="number" id="symcount" name="symcount" min="0" value="{{.SymbolCount}}">
|
||||||
</div>
|
</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>
|
<div>
|
||||||
<label for="randcaps">Capitalization</label>
|
<label for="randcaps">Capitalization</label>
|
||||||
<select id="randcaps" name="randcaps">
|
<select id="randcaps" name="randcaps">
|
||||||
@@ -218,6 +181,7 @@ func main() {
|
|||||||
words = wd
|
words = wd
|
||||||
log.Printf("Loaded %d distinct word lengths", len(words.ByLen))
|
log.Printf("Loaded %d distinct word lengths", len(words.ByLen))
|
||||||
|
|
||||||
|
// Wrap handler with common log middleware
|
||||||
http.Handle("/", commonLogMiddleware(http.HandlerFunc(handlePassphrase)))
|
http.Handle("/", commonLogMiddleware(http.HandlerFunc(handlePassphrase)))
|
||||||
|
|
||||||
port := os.Getenv("PORT")
|
port := os.Getenv("PORT")
|
||||||
@@ -265,14 +229,18 @@ func loadWordlist(path string) (WordData, error) {
|
|||||||
|
|
||||||
func handlePassphrase(w http.ResponseWriter, r *http.Request) {
|
func handlePassphrase(w http.ResponseWriter, r *http.Request) {
|
||||||
wordcount := parseIntQuery(r, "wordcount", 3)
|
wordcount := parseIntQuery(r, "wordcount", 3)
|
||||||
minlen := parseIntQuery(r, "minlen", 5)
|
minlen := parseIntQuery(r, "minlen", 1)
|
||||||
maxlen := parseIntQuery(r, "maxlen", 8)
|
maxlen := parseIntQuery(r, "maxlen", 8)
|
||||||
numlen := parseIntQuery(r, "numlen", 1)
|
numlen := parseIntQuery(r, "numlen", 2)
|
||||||
symbolCount := parseIntQuery(r, "symcount", 1)
|
symbolCount := parseIntQuery(r, "symcount", 1)
|
||||||
randcaps := r.URL.Query().Get("randcaps")
|
randcaps := r.URL.Query().Get("randcaps")
|
||||||
if randcaps == "" {
|
if randcaps == "" {
|
||||||
randcaps = "first"
|
randcaps = "first"
|
||||||
}
|
}
|
||||||
|
symbolSet := r.URL.Query().Get("symset")
|
||||||
|
if symbolSet == "" {
|
||||||
|
symbolSet = "full"
|
||||||
|
}
|
||||||
|
|
||||||
if minlen < 1 {
|
if minlen < 1 {
|
||||||
minlen = 1
|
minlen = 1
|
||||||
@@ -296,6 +264,7 @@ func handlePassphrase(w http.ResponseWriter, r *http.Request) {
|
|||||||
MaxLen: maxlen,
|
MaxLen: maxlen,
|
||||||
NumLen: numlen,
|
NumLen: numlen,
|
||||||
SymbolCount: symbolCount,
|
SymbolCount: symbolCount,
|
||||||
|
SymbolSet: symbolSet,
|
||||||
RandCaps: randcaps,
|
RandCaps: randcaps,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -310,6 +279,17 @@ func handlePassphrase(w http.ResponseWriter, r *http.Request) {
|
|||||||
avgLen := float64(totalLen) / float64(totalWords)
|
avgLen := float64(totalLen) / float64(totalWords)
|
||||||
wordBits := math.Log2(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
|
// Caps
|
||||||
var capBitsPerWord float64
|
var capBitsPerWord float64
|
||||||
var capMult 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
|
// 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
|
segments := 0
|
||||||
if segmentsEnabled {
|
if segmentsEnabled {
|
||||||
segments = wordcount
|
segments = wordcount
|
||||||
@@ -338,7 +318,7 @@ func handlePassphrase(w http.ResponseWriter, r *http.Request) {
|
|||||||
// Bits per separator segment
|
// Bits per separator segment
|
||||||
separatorBitsPerSegment := 0.0
|
separatorBitsPerSegment := 0.0
|
||||||
if segmentsEnabled {
|
if segmentsEnabled {
|
||||||
separatorBitsPerSegment = separatorBits(numlen, symbolCount, len(symbolAlphabet))
|
separatorBitsPerSegment = separatorBits(numlen, symbolCount, symAlphabetSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Total bits
|
// Total bits
|
||||||
@@ -350,7 +330,7 @@ func handlePassphrase(w http.ResponseWriter, r *http.Request) {
|
|||||||
combosCaps := math.Pow(capMult, float64(wordcount))
|
combosCaps := math.Pow(capMult, float64(wordcount))
|
||||||
segmentCombos := 1.0
|
segmentCombos := 1.0
|
||||||
if segmentsEnabled {
|
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
|
combos := combosPerWord * combosCaps * segmentCombos
|
||||||
|
|
||||||
@@ -367,7 +347,7 @@ func handlePassphrase(w http.ResponseWriter, r *http.Request) {
|
|||||||
data.EffectiveBits = int(passBits + 0.5)
|
data.EffectiveBits = int(passBits + 0.5)
|
||||||
data.TotalCombinations = combosStr
|
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")
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
if err := pageTmpl.Execute(w, data); err != nil {
|
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.
|
// 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
|
const numPhrases = 10
|
||||||
if len(usewords) == 0 {
|
if len(usewords) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
segmentsEnabled := (numlen > 0 || symCountPerSeg > 0)
|
segmentsEnabled := (numlen > 0 || (symCountPerSeg > 0 && len(symAlphabet) > 0))
|
||||||
symRunes := []rune(symbolAlphabet)
|
symRunes := []rune(symAlphabet)
|
||||||
|
|
||||||
phrases := make([]string, 0, numPhrases)
|
phrases := make([]string, 0, numPhrases)
|
||||||
for i := 0; i < numPhrases; i++ {
|
for i := 0; i < numPhrases; i++ {
|
||||||
@@ -593,3 +573,48 @@ func commafyInt(n int64) string {
|
|||||||
}
|
}
|
||||||
return sign + strings.Join(parts, ",")
|
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