(module password mzscheme
(require (planet "macro.ss" ("schematics" "macro.plt" 1))
(planet "port.ss" ("schematics" "port.plt" 1))
(lib "etc.ss")
(lib "string.ss" "srfi" "13")
"leet.ss"
"random.ss")
(provide min-length
max-length
string->password
make-passwords)
(define min-length 8)
(define max-length 16)
(define (read-words path)
(list->vector (port->string-list (open-input-file path))))
(define (choose-word words)
(choose/uniform words))
(define make-passwords
(case-lambda
[(number)
(let ((dict1 (string->path "/usr/share/dict/words"))
(dict2 (build-path
(this-expression-source-directory)
"words")))
(if (file-exists? dict1)
(make-passwords number dict1)
(if (file-exists? dict2)
(make-passwords number dict2)
(error "Cannot find a file containing a list of words"))))]
[(number path)
(let ((words (read-words path)))
(for ((i 0 number) (passwords null))
(cons (string->password (choose-word words))
passwords)))]))
(define (correct-length input)
(define (pad input offset pad-length)
(let ((pad-string (make-string pad-length #\0))
(chooser
(lambda ()
(choose/uniform
#(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)))))
(for! (i 0 pad-length)
(string-set! pad-string i (chooser)))
(string-append input pad-string)))
(let ((len (string-length input)))
(cond
((> len max-length)
(substring input 0 max-length))
((< len min-length)
(pad input len (- min-length len)))
(else input))))
(define (string->password input)
(let ((input (correct-length input)))
(add-noise input)))
(define (add-noise input)
(string-map
(lambda (char)
((choose/uniform (vector char-upcase char-downcase char->leet)) char))
input))
)