home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / compsrcs / misc / volume05 / pwgen < prev    next >
Encoding:
Internet Message Format  |  1991-08-27  |  11.6 KB

  1. From decwrl!ucbvax!agate!eos!ames!nrl-cmf!mailrus!cwjcc!hal!ncoast!allbery Sun Nov 27 00:29:52 PST 1988
  2. Article 735 of comp.sources.misc:
  3. Path: granite!decwrl!ucbvax!agate!eos!ames!nrl-cmf!mailrus!cwjcc!hal!ncoast!allbery
  4. From: allbery@ncoast.UUCP (Brandon S. Allbery)
  5. Newsgroups: comp.sources.misc
  6. Subject: v05i059: pwgen -- random-but-pronounceable password generator
  7. Message-ID: <13184@ncoast.UUCP>
  8. Date: 26 Nov 88 04:44:18 GMT
  9. Sender: allbery@ncoast.UUCP
  10. Reply-To: allbery@ncoast.UUCP (Brandon S. Allbery)
  11. Organization: Cleveland Public Access UN*X, Cleveland, Oh
  12. Lines: 328
  13. Approved: allbery@ncoast.UUCP
  14.  
  15. Posting-number: Volume 5, Issue 59
  16. Submitted-by: "Brandon S. Allbery" <allbery@ncoast.UUCP>
  17. Archive-name: pwgen
  18.  
  19. [Dr. Jekyll and Mr. Hyde, anyone?  ;-)  ++bsa]
  20.  
  21. I've described this program, in its OSI Superboard-II incarnation, in
  22. comp.unix.wizards and news.sysadmin.  Now, here's the code for a UN*X
  23. version.  It seems to work OK on ncoast, except for some oddness in the
  24. "random" selections -- which are probably the result of our not having a
  25. good RNG.  (Could someone mail me one or more of the alternative
  26. generators?  Or maybe even the source to BSD random(), assuming that it
  27. falls outside the subset of BSD code that can be traced to AT&T?  To put it
  28. mildly, our random number generator isn't.)
  29.  
  30. This is not as complete as the original program, which actually had a list
  31. of characters (the current one uses phonemes) which were most likely to
  32. follow other phonemes.  On the other hand, this version does use phonemes
  33. rather than characters, so its creations are more pronounceable as a rule
  34. than those of the original.  The resulting passwords aren't quite as
  35. "natural" as the original, however.  (That may be for the best; the original
  36. was intended as a simple experiment to see if a rule for "proper English
  37. words" could be defined in terms of rules which related letters.  This was
  38. before I got to college and learned that such things were rather unlikely,
  39. but the original program still did a pretty good job of spewing out some
  40. common English words.  That can't be said for *this* program.)
  41.  
  42. To compile:    cc -O -o pwgen pwgen.c -lm
  43. (The sqrt() function is used in an attempt to produce a random number which
  44. is "weighted" toward one end of the range, so as to prefer one end of a list
  45. of "spellings" for a phoneme over the other end.  It seems to work, but with
  46. this rotted RNG, I can't be absolutely certain.  A trial distribution seems
  47. to be correct, however.)
  48.  
  49. What's the intent?  I find that I can remember a "word" better if I can
  50. pronounce it.  This may or may not be true for other people, but *anything*
  51. that encourages the use of random passwords is an improvement on what I
  52. typically see at client sites every day.  So this program spits out
  53. passwords which are virtually guaranteed not to be found in the dictionary,
  54. but are (usually) pronounceable to speakers of English.  (The tables can be
  55. modified for other languages, but they're rather hairy.  Perhaps I'll write
  56. a version which loads "compiled" language descriptions, and let the compiler
  57. deal with the hairy stuff.  Hey, they were even hairier in the BASIC
  58. version!)
  59.  
  60. Oh, well, shar and enjoy.
  61.  
  62. ++Brandon, sitting on the other side of the fence for the moment
  63. -------------------------------------------------------------------------------
  64. #! /bin/sh
  65. # This file was wrapped with "dummyshar".  "sh" this file to extract.
  66. # Contents:  pwgen.c
  67. echo extracting 'pwgen.c'
  68. if test -f 'pwgen.c' -a -z "$1"; then echo Not overwriting 'pwgen.c'; else
  69. sed 's/^X//' << \EOF > 'pwgen.c'
  70. X/*
  71. X * Generate (hopefully) pronounceable random passwords.  These can often be
  72. X * remembered more easily than completely random passwords, and are immune to
  73. X * dictionary searches, etc.
  74. X *
  75. X * The original version of this program was written in BASIC on an OSI
  76. X * Superboard II SBC.  That version is long gone (the SB2's cassette drive
  77. X * was never trustworthy, it eventually scrambled the tape), but the basic
  78. X * (pardon the pun) idea lives on here, with a few modification like basing
  79. X * the selection on "graphs" (actually, they are a restricted set of phonemes)
  80. X * and having randomly-selected spellings for those graphs.
  81. X */
  82. X
  83. X#include <stdio.h>
  84. X#include <math.h>
  85. X
  86. X#define RANDOM(c)    ((int) (rand(c) / 32767.0 * (c)))
  87. X
  88. Xchar *spelling[] = {
  89. X/*a*/    "a",                (char *) 0,    /* 2*/
  90. X/*A*/    "a",    "ae",    "ai",        (char *) 0,    /* 6*/
  91. X/*b*/    "b",                (char *) 0,    /* 8*/
  92. X/*ch*/    "ch",                (char *) 0,    /*10*/
  93. X/*d*/    "d",                (char *) 0,    /*12*/
  94. X/*e*/    "e",                (char *) 0,    /*14*/
  95. X/*E*/    "e",    "ee",    "ie",        (char *) 0,    /*18*/
  96. X/*f*/    "f",    "ph",    "gh",        (char *) 0,    /*22*/
  97. X/*g*/    "g",                (char *) 0,    /*24*/
  98. X/*h*/    "h",                (char *) 0,    /*26*/
  99. X/*i*/    "i",    "e",            (char *) 0,    /*29*/
  100. X/*I*/    "i",    "ai",            (char *) 0,    /*32*/
  101. X/*i'*/    "i",    "ei",            (char *) 0,    /*35*/
  102. X/*j*/    "j",    "g",            (char *) 0,    /*38*/
  103. X/*k*/    "k",    "c",            (char *) 0,    /*41*/
  104. X/*l*/    "l",                (char *) 0,    /*43*/
  105. X/*m*/    "m",                (char *) 0,    /*45*/
  106. X/*n*/    "n",                (char *) 0,    /*47*/
  107. X/*ng*/    "ng",                (char *) 0,    /*49*/
  108. X/*o*/    "o",    "a",    "ah",        (char *) 0,    /*53*/
  109. X/*O*/    "o",    "oh",            (char *) 0,    /*56*/
  110. X/*oo*/    "oo",    "u",            (char *) 0,    /*59*/
  111. X/*OO*/    "oo",    "w",            (char *) 0,    /*62*/
  112. X/*p*/    "p",                (char *) 0,    /*64*/
  113. X/*qu*/    "qu",                (char *) 0,    /*66*/
  114. X/*r*/    "r",                (char *) 0,    /*68*/
  115. X/*s*/    "s",    "c",            (char *) 0,    /*71*/
  116. X/*sh*/    "sh",    "s",            (char *) 0,    /*74*/
  117. X/*t*/    "t",                (char *) 0,    /*76*/
  118. X/*th*/    "th",                (char *) 0,    /*78*/
  119. X/*TH*/    "th",                (char *) 0,    /*80*/
  120. X/*u*/    "u",                (char *) 0,    /*82*/
  121. X/*U*/    "u",    "oo",            (char *) 0,    /*85*/
  122. X/*v*/    "v",                (char *) 0,    /*87*/
  123. X/*x*/    "x",                (char *) 0,    /*89*/
  124. X/*y*/    "y",                (char *) 0,    /*91*/
  125. X/*z*/    "z",    "s",            (char *) 0,    /*94*/
  126. X};
  127. X
  128. Xstruct graph {
  129. X    char *graph;
  130. X    char type;
  131. X#define CONSONANT    0
  132. X#define VOWEL_LONG    1
  133. X#define VOWEL_SHORT    2
  134. X#define VOWEL_OTHER    3
  135. X#define VOWEL_MASK    3
  136. X#define iscons(c)    (((c)->type & VOWEL_MASK) == 0)
  137. X#define isvowel(c)    (((c)->type & VOWEL_MASK) != 0)
  138. X/*    char frequency;            */    /* unused for now */
  139. X    char **spellings;
  140. X/*    struct graph **following;    */    /* maybe later */
  141. X} graph[] = {
  142. X    "a",    VOWEL_SHORT,    &spelling[0],
  143. X    "A",    VOWEL_LONG,    &spelling[2],
  144. X    "b",    CONSONANT,    &spelling[6],
  145. X    "ch",    CONSONANT,    &spelling[8],
  146. X    "d",    CONSONANT,    &spelling[10],
  147. X    "e",    VOWEL_SHORT,    &spelling[12],
  148. X    "E",    VOWEL_LONG,    &spelling[14],
  149. X    "f",    CONSONANT,    &spelling[18],
  150. X    "g",    CONSONANT,    &spelling[22],
  151. X    "h",    CONSONANT,    &spelling[24],
  152. X    "i",    VOWEL_SHORT,    &spelling[26],
  153. X    "I",    VOWEL_LONG,    &spelling[29],
  154. X    "i'",    VOWEL_OTHER,    &spelling[32],
  155. X    "j",    CONSONANT,    &spelling[35],
  156. X    "k",    CONSONANT,    &spelling[38],
  157. X    "l",    CONSONANT,    &spelling[41],
  158. X    "m",    CONSONANT,    &spelling[43],
  159. X    "n",    CONSONANT,    &spelling[45],
  160. X    "ng",    CONSONANT,    &spelling[47],
  161. X    "o",    VOWEL_SHORT,    &spelling[49],
  162. X    "O",    VOWEL_LONG,    &spelling[53],
  163. X    "oo",    VOWEL_SHORT,    &spelling[56],
  164. X    "OO",    VOWEL_LONG,    &spelling[59],
  165. X    "p",    CONSONANT,    &spelling[62],
  166. X    "qu",    CONSONANT,    &spelling[64],
  167. X    "r",    CONSONANT,    &spelling[66],
  168. X    "s",    CONSONANT,    &spelling[68],
  169. X    "sh",    CONSONANT,    &spelling[71],
  170. X    "t",    CONSONANT,    &spelling[74],
  171. X    "th",    CONSONANT,    &spelling[76],
  172. X    "TH",    CONSONANT,    &spelling[78],
  173. X    "u",    VOWEL_SHORT,    &spelling[80],
  174. X    "U",    VOWEL_LONG,    &spelling[82],
  175. X    "v",    CONSONANT,    &spelling[85],
  176. X    "x",    CONSONANT,    &spelling[87],
  177. X    "y",    CONSONANT,    &spelling[89],
  178. X    "z",    CONSONANT,    &spelling[91],
  179. X    0,    0,        &spelling[94],
  180. X};
  181. X
  182. Xstruct graph *vowel[] = {
  183. X    &graph[0],    &graph[1],    &graph[5],    &graph[6],
  184. X    &graph[10],    &graph[11],    &graph[12],    &graph[19],
  185. X    &graph[20],    &graph[21],    &graph[22],    &graph[30],
  186. X    &graph[31],
  187. X    (struct graph *) 0,
  188. X};
  189. X
  190. Xstruct graph *consonant[] = {
  191. X    &graph[2],    &graph[3],    &graph[4],    &graph[7],
  192. X    &graph[8],    &graph[9],    &graph[13],    &graph[14],
  193. X    &graph[15],    &graph[16],    &graph[17],    &graph[18],
  194. X    &graph[23],    &graph[24],    &graph[25],    &graph[26],
  195. X    &graph[27],    &graph[28],    &graph[29],    &graph[32],
  196. X    &graph[33],    &graph[34],    &graph[35],
  197. X    (struct graph *) 0,
  198. X};
  199. X
  200. X/*
  201. X * Randomly select a graph from the specifield array.  Eventually, this should
  202. X * account for graph frequencies as well.
  203. X */
  204. X
  205. Xstruct graph *selgraph(graphs)
  206. X    struct graph **graphs;
  207. X{
  208. X    register int cnt;
  209. X
  210. X    for (cnt = 0; graphs[cnt] != (struct graph *) 0; cnt++)
  211. X        ;
  212. X    return graphs[RANDOM(cnt)];
  213. X}
  214. X
  215. X/*
  216. X * Randomly select a spelling for the specified graph.  This is not linear:
  217. X * earlier spellings are preferred over later ones, but the latter do
  218. X * sometimes sneak in.
  219. X */
  220. X
  221. Xchar *selspell(graph)
  222. X    struct graph *graph;
  223. X{
  224. X    register int cnt, sel;
  225. X
  226. X    for (cnt = 0; graph->spellings[cnt] != (char *) 0; cnt++)
  227. X        ;
  228. X    if (cnt == 0) {
  229. X        fprintf(stderr, "PANIC: selspell(%s) got count(spellings) == 0\n", graph->graph);
  230. X        exit(2);
  231. X    }
  232. X    if (cnt == 1)
  233. X        return *graph->spellings;
  234. X/*
  235. X * This may not be the best way to do it... maybe Weemba'd care to lend a
  236. X * hand here?  After all, my specialty is programming, NOT math.
  237. X */
  238. X    if ((sel = cnt - (int) sqrt((double) RANDOM(cnt * cnt) + 1) - 1) < 0 || sel >= cnt) {
  239. X#ifdef BUGCATCH
  240. X        fprintf(stderr, "PANIC: selspell(%s) got nlrand(%d) == %d\n", graph->graph, cnt, sel);
  241. X        exit(2);
  242. X#else
  243. X        sel = 0;
  244. X#endif
  245. X    }
  246. X    return graph->spellings[sel];
  247. X}
  248. X
  249. X/*
  250. X * Choose the next source for a graph.  The rules are:  a consonant MUST be
  251. X * followed by a vowel; a vowel may be followed by a vowel of a different
  252. X * type or by a consonant, but never more than two consecutive vowel graphs.
  253. X */
  254. X
  255. Xchoosenext(cur, prev)
  256. X{
  257. X    if (cur == CONSONANT)
  258. X        return VOWEL_MASK;
  259. X    else if (prev == -1 || (prev & VOWEL_MASK) != 0)
  260. X        return CONSONANT;
  261. X    else if (RANDOM(10) == 5)
  262. X        return VOWEL_MASK;
  263. X    else
  264. X        return CONSONANT;
  265. X}
  266. X
  267. X/*
  268. X * We are passed an array of (struct graph *); choose an entry randomly and
  269. X * assemble a string fitting the size constraint.  We use the original (OSI)
  270. X * paradigm:  alternate consonants and vowels, with the option of two vowels
  271. X * in a row occasionally.  The only difference is that they must be different
  272. X * *types* of vowels, a distinction that the OSI version didn't consider.
  273. X */
  274. X
  275. Xpwgen(initial, pw, maxlen)
  276. X    struct graph **initial;
  277. X    char *pw;
  278. X{
  279. X    int pwlen, state, prev, tmp;
  280. X    struct graph *graph;
  281. X    char *spelling;
  282. X
  283. X    pwlen = 0;
  284. X    state = initial[0]->type;
  285. X    prev = -1;
  286. X    while (pwlen < maxlen - 1) {
  287. X        do {
  288. X            graph = selgraph(initial);
  289. X        } while (state != CONSONANT && graph->type == prev);
  290. X        if ((spelling = selspell(graph)) == (char *) 0) {
  291. X            fprintf(stderr, "PANIC: got NULL in selspell(%s)\n", graph->graph);
  292. X            exit(2);
  293. X        }
  294. X        strcpy(pw, spelling);
  295. X        while (*pw != '\0')
  296. X            pwlen++, pw++;
  297. X        tmp = prev;
  298. X        prev = graph->type;
  299. X        if ((state = choosenext(prev, tmp)) == CONSONANT)
  300. X            initial = consonant;
  301. X        else
  302. X            initial = vowel;
  303. X    }
  304. X}
  305. X
  306. Xmain(argc, argv)
  307. X    char **argv;
  308. X{
  309. X    int cnt, len;
  310. X    char buf[20];
  311. X
  312. X    if (argc < 2 || argc > 3) {
  313. X        fprintf(stderr, "usage: %s length [count]\n", argv[0]);
  314. X        exit(1);
  315. X    }
  316. X    if ((len = atoi(argv[1])) < 4 || len > 16) {
  317. X        fprintf(stderr, "%s: invalid length %s\n", argv[0], argv[1]);
  318. X        exit(1);
  319. X    }
  320. X    if (argc == 2)
  321. X        cnt = 1;
  322. X    else if ((cnt = atoi(argv[2])) < 1) {
  323. X        fprintf(stderr, "%s: invalid count %s\n",  argv[0], argv[2]);
  324. X        exit(1);
  325. X    }
  326. X    srand(time(0) + (getpgrp() << 8) + getpid());
  327. X    while (cnt-- != 0) {
  328. X        pwgen((RANDOM(10) < 4? vowel: consonant), buf, len);
  329. X        printf("%s\n", buf);
  330. X    }
  331. X    exit(0);
  332. X}
  333. EOF
  334. chars=`wc -c < 'pwgen.c'`
  335. if test $chars !=    7757; then echo 'pwgen.c' is $chars characters, should be    7757 characters!; fi
  336. exit 0
  337. -- 
  338. Brandon S. Allbery, comp.sources.misc moderator and one admin of ncoast PA UN*X
  339. uunet!hal.cwru.edu!ncoast!allbery  <PREFERRED!>        ncoast!allbery@hal.cwru.edu
  340. allberyb@skybridge.sdi.cwru.edu          <ALSO>           allbery@uunet.uu.net
  341. comp.sources.misc is moving off ncoast -- please do NOT send submissions direct
  342.       Send comp.sources.misc submissions to comp-sources-misc@<backbone>.
  343.  
  344.  
  345.