home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume5 / pwgen < prev    next >
Encoding:
Internet Message Format  |  1989-02-03  |  11.4 KB

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