1 /* pwgen.c - A password generator.
2  *
3  * Copyright 2020 Moritz R�hrich <moritz@ildefons.de>
4 
5 USE_PWGEN(NEWTOY(pwgen, ">2r(remove):c(capitalize)n(numerals)y(symbols)s(secure)B(ambiguous)h(help)C1vA(no-capitalize)0(no-numerals)[-cA][-n0][-C1]", TOYFLAG_USR|TOYFLAG_BIN))
6 
7 config PWGEN
8   bool "pwgen"
9   default y
10   help
11     usage: pwgen [-cAn0yrsBhC1v] [LENGTH] [COUNT]
12 
13     Generate human-readable random passwords. When output is to tty produces
14     a screenfull to defeat shoulder surfing (pick one and clear the screen).
15 
16     -c  --capitalize                  Permit capital letters.
17     -A  --no-capitalize               Don't include capital letters.
18     -n  --numerals                    Permit numbers.
19     -0  --no-numerals                 Don't include numbers.
20     -y  --symbols                     Permit special characters ($#%...).
21     -r <chars>  --remove=<chars>      Don't include the given characters.
22     -s  --secure                      Generate more random passwords.
23     -B  --ambiguous                   Avoid ambiguous characters (e.g. 0, O).
24     -h  --help                        Print this help message.
25     -C                                Print the output in columns.
26     -1                                Print the output one line each.
27     -v                                Don't include vowels.
28 */
29 
30 #define FOR_pwgen
31 #include "toys.h"
32 
GLOBALS(char * r;)33 GLOBALS(
34   char *r;
35 )
36 
37 void pwgen_main(void)
38 {
39   int length = 8, count, ii, jj, c, rand = 0, x = 0;
40   unsigned xx = 80, yy = 24;
41   char randbuf[16];
42 
43   if (isatty(1)) terminal_size(&xx, &yy);
44   else toys.optflags |= FLAG_1;
45 
46   if (toys.optc && (length = atolx(*toys.optargs))>sizeof(toybuf))
47     error_exit("bad length");
48   if (toys.optc>1) count = atolx(toys.optargs[1]);
49   else count = FLAG(1) ? 1 : (xx/(length+1))*(yy-1);
50 
51   for (jj = 0; jj<count; jj++) {
52     for (ii = 0; ii<length;) {
53       // Don't fetch more random than necessary, give each byte 2 tries to fit
54       if (!rand) xgetrandom(randbuf, rand = sizeof(randbuf), 0);
55       c = 33+randbuf[--rand]%93; // remainder 69 makes >102 less likely
56       if (FLAG(s)) randbuf[rand] = 0;
57 
58       if (c>='A' && c<='Z') {
59         if (FLAG(A)) continue;
60         // take out half the capital letters to be more human readable
61         else c |= (0x80&randbuf[rand])>>2;
62       }
63       if (FLAG(0) && c>='0' && c<='9') continue;
64       if (FLAG(B) && strchr("0O1lI'`.,", c)) continue;
65       if (FLAG(v) && strchr("aeiou", tolower(c))) continue;
66       if (!FLAG(y) || (0x80&randbuf[rand]))
67         if (c<'0' || (c>'9' && c<'A') || (c>'Z' && c<'a') || c>'z') continue;
68       if (TT.r && strchr(TT.r, c)) continue;
69 
70       toybuf[ii++] = c;
71     }
72     if (FLAG(1) || (x += length+1)+length>=xx) x = 0;
73     xprintf("%.*s%c", length, toybuf, x ? ' ' : '\n');
74   }
75   if (x) xputc('\n');
76 }
77