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("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)