1 // upwgen generates random internationalized passwords
2 // Copyright (C) 2019 Aaron Ball <nullspoon@oper.io>
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #include <stdlib.h>
17 #include <stdio.h>
18 #include <string.h>
19 #include <time.h>
20 #include <locale.h>
21
22 #include "i18n_set.h"
23
24 // Shamelessly ripped off from the GCC docs on stringizing
25 // This (xstr) converts a macro to a string literal
26 #define xstr(s) str(s)
27 #define str(s) #s
28
29
30 void usage() {
31 printf(
32 "Upwgen is a password generator with international support. If no length\n"
33 "is specified, defaults to 32 characters output length, selecting from\n"
34 "the standard English character set (lower case, upper case, numerals,\n"
35 "and symbols).\n\n"
36 "Usage:\n upwgen [options] [length]\n\n"
37 "Options:\n"
38 " -0,--no-numerals Include at least one numeral in output\n"
39 " -A,--no-capitalize Include at least one numeral in output\n"
40 " -c,--capitalize Include at least one capital letter in output\n"
41 " -n,--numerals Include at least one numeral in output\n"
42 " -y,--symbols Include at least one symbol in output\n"
43 " -i,--i18n Include at least one international letter in output\n"
44 " -1 Include chars from the most used scripts in the world\n"
45 " -2 Include chars from the second most used scripts in the world\n"
46 " -3 Include chars from the third most used scripts in the world\n"
47 " -4 Include chars from the forth most used scripts in the world\n"
48 "\n"
49 " -h,--help Print this help text\n"
50 " -d,--debug Enable debug mode (prints entire character pool)\n"
51 " -V,--version Prints the version\n"
52 );
53 }
54
55
56 int main(int argc, char* argv[]) {
57 struct timespec ts; // Timespec for seeding rng
58 int debug; // Debug mode switch
59 int len; // Password length
60 int i; // Arg index
61 unsigned long seed; // Seed for the RNG (current seconds * nanoseconds)
62
63 struct i18n_set* set = NULL; // Linked list of i18n_set structs
64 struct i18n_set* cursor = NULL; // Cursor to track current position with in
65 // the linked list
66
67 // Initialize
68 debug = 0;
69 len = 32;
70 i = 1;
71 setlocale(LC_ALL, "");
72
73 // Start with the defaults
74 set = i18n_set_new(I18N_TYPE_ASCII_UPPER);
75 i18n_set_add(set, I18N_TYPE_ASCII_LOWER);
76 i18n_set_add(set, I18N_TYPE_ASCII_NUMERALS);
77
78
79 while(i < argc) {
80 if(strcmp(argv[i], "-0") == 0 || strcmp(argv[i], "--no-numerals") == 0) {
81 set = i18n_set_rm_type(set, I18N_TYPE_ASCII_NUMERALS);
82
83 } else if(strcmp(argv[i], "-A") == 0 || strcmp(argv[i], "--no-capitalize") == 0) {
84 set = i18n_set_rm_type(set, I18N_TYPE_ASCII_UPPER);
85
86 } else if(strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--capitalize") == 0) {
87 i18n_set_add(set, I18N_TYPE_ASCII_UPPER);
88
89 } else if(strcmp(argv[i], "-n") == 0 || strcmp(argv[i], "--numerals") == 0) {
90 i18n_set_add(set, I18N_TYPE_ASCII_NUMERALS);
91
92 } else if(strcmp(argv[i], "-y") == 0 || strcmp(argv[i], "--symbols") == 0) {
93 i18n_set_add(set, I18N_TYPE_ASCII_SYMBOLS);
94
95 } else if(strcmp(argv[i], "-i") == 0 || strcmp(argv[i], "--i18n") == 0) {
96 i18n_set_add(set, I18N_TYPE_ONE);
97 i18n_set_add(set, I18N_TYPE_TWO);
98 i18n_set_add(set, I18N_TYPE_THREE);
99 i18n_set_add(set, I18N_TYPE_FOUR);
100
101 } else if(strcmp(argv[i], "-1") == 0) {
102 i18n_set_add(set, I18N_TYPE_ONE);
103
104 } else if(strcmp(argv[i], "-2") == 0) {
105 i18n_set_add(set, I18N_TYPE_TWO);
106
107 } else if(strcmp(argv[i], "-3") == 0) {
108 i18n_set_add(set, I18N_TYPE_THREE);
109
110 } else if(strcmp(argv[i], "-4") == 0) {
111 i18n_set_add(set, I18N_TYPE_FOUR);
112
113 } else if(strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
114 usage();
115 return 0;
116
117 } else if(strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "--debug") == 0) {
118 debug = 1;
119
120 } else if(strcmp(argv[i], "-V") == 0 || strcmp(argv[i], "--version") == 0) {
121 printf("upwgen %s\n", xstr(VERSTR));
122 return 0;
123
124 } else {
125 // If we reach this block, the user specified a custom length (or
126 // fatfingered something). Test for ability to convert from str to int
127 // and set length if possible.
128 if(atoi(argv[i]) == 0) {
129 printf("Invalid length \"%s\"\n", argv[i]);
130 return 1;
131 } else {
132 len = atoi(argv[i]);
133 }
134 }
135 i++;
136 }
137
138
139 if(debug)
140 i18n_set_dump(set);
141
142 // Get the random data seed
143 clock_gettime(CLOCK_REALTIME, &ts);
144 seed = ts.tv_sec + ts.tv_nsec;
145 srand((unsigned)seed);
146
147 cursor = set;
148 while(len > 0) {
149 // Randomly select the number of character sets to advance through (max 10)
150 int i = rand() % 10;
151 while(i > 0) {
152 // Loop
153 if(!cursor->next) {
154 cursor = set;
155 } else {
156 cursor = cursor->next;
157 }
158 i--;
159 }
160
161 // Randomly select an integer within the set size
162 int rc = rand() % cursor->count; // Random char within set
163 printf("%lc", cursor->chars[rc]);
164 len--;
165 }
166 printf("\n");
167
168 i18n_set_free(set);
169 return 0;
170 }
|