passphrase/src/passphrase.nim
2021-07-27 10:41:23 -07:00

84 lines
1.9 KiB
Nim

import std/[os, strutils]
import nimcrypto/sysrand
type Dictionary = object
words: string
offsets: seq[uint32]
proc `[]`(d: Dictionary, i: Natural): string =
# last word has no following start index, so we have to fake it
# also strings are indexed with ints
let slice = if i == d.offsets.high:
d.offsets[i].int .. d.words.high
else:
d.offsets[i].int ..< d.offsets[i + 1].int
result = d.words[slice]
proc len(d: Dictionary): int =
result = d.offsets.len
proc loadWords(): Dictionary =
for word in staticRead("../data/dictionary.txt").strip().splitLines():
let startIdx = result.words.len.uint32
result.offsets.add(startIdx)
result.words.add(word)
const dict = loadWords()
proc genPassphrase(length, dictSize: int): string =
if dictSize < 100 or dictSize > dict.len:
quit("Dictionary size must be between 100 and " & $dict.len, 1)
var rands = newSeq[uint64](length)
discard randomBytes(rands)
var words: seq[string]
for r in rands:
let i = r mod dictSize.uint64
words.add(dict[i])
result = words.join(" ")
const help = """Usage:
passphrase [LENGTH] [DICTSIZE]
Defaults to length of 4 and dictionary size of 25,000."""
proc parseInput(): (int, int) =
let params = commandLineParams()
if "-h" in params or "--help" in params:
echo help
quit(0)
var
length = 4
dictSize = 25_000
if params.len > 0:
try:
length = parseInt(params[0])
except ValueError:
quit(params[0] & " is not a valid passphrase length.", 1)
if params.len > 1:
try:
dictSize = parseInt(params[1])
if dictSize < 100 or dictSize > dict.len:
quit("Dictionary size must be between 100 and " & $dict.len, 1)
except ValueError:
quit(params[1] & " is not a valid dictionary size.", 1)
result = (length, dictSize)
when isMainModule:
let (length, dictSize) = parseInput()
echo genPassphrase(length, dictSize)