xref: /dflybsd-src/contrib/pam_passwdqc/passwdqc_random.c (revision 44624ade5c7148822b0294730bae4f64e40124db)
116d0e647SPeter Avalos /*
2*8c117293SSascha Wildner  * Copyright (c) 2000-2002,2005,2008,2010,2013,2016,2021 by Solar Designer
3*8c117293SSascha Wildner  * See LICENSE
416d0e647SPeter Avalos  */
516d0e647SPeter Avalos 
6*8c117293SSascha Wildner #ifdef _MSC_VER
7*8c117293SSascha Wildner #define _CRT_NONSTDC_NO_WARNINGS /* we use POSIX function names */
8*8c117293SSascha Wildner #include <windows.h>
9*8c117293SSascha Wildner #include <wincrypt.h>
10*8c117293SSascha Wildner #else
11*8c117293SSascha Wildner #include <limits.h>
1216d0e647SPeter Avalos #include <unistd.h>
1316d0e647SPeter Avalos #include <errno.h>
1416d0e647SPeter Avalos #include <fcntl.h>
15*8c117293SSascha Wildner #endif
16*8c117293SSascha Wildner 
17*8c117293SSascha Wildner #include <string.h>
1816d0e647SPeter Avalos 
1916d0e647SPeter Avalos #include "passwdqc.h"
20*8c117293SSascha Wildner #include "wordset_4k.h"
2116d0e647SPeter Avalos 
2216d0e647SPeter Avalos /*
2316d0e647SPeter Avalos  * We separate words in the generated "passphrases" with random special
24*8c117293SSascha Wildner  * characters out of a set of 16 (so we encode 4 bits per separator
2516d0e647SPeter Avalos  * character).  To enable the use of our "passphrases" within FTP URLs
2616d0e647SPeter Avalos  * (and similar), we pick characters that are defined by RFC 3986 as
2716d0e647SPeter Avalos  * being safe within "userinfo" part of URLs without encoding and
2816d0e647SPeter Avalos  * without having a special meaning.  Out of those, we avoid characters
2916d0e647SPeter Avalos  * that are visually ambiguous or difficult over the phone.  This
30*8c117293SSascha Wildner  * happens to leave us with exactly 8 symbols, and we add 8 digits that
31*8c117293SSascha Wildner  * are not visually ambiguous.  Unfortunately, the exclamation mark
32*8c117293SSascha Wildner  * might be confused for the digit 1 (which we don't use), though.
3316d0e647SPeter Avalos  */
34*8c117293SSascha Wildner #define SEPARATORS			"-_!$&*+=23456789"
3516d0e647SPeter Avalos 
36*8c117293SSascha Wildner /*
37*8c117293SSascha Wildner  * Number of bits encoded per separator character.
38*8c117293SSascha Wildner  */
39*8c117293SSascha Wildner #define SEPARATOR_BITS			4
40*8c117293SSascha Wildner 
41*8c117293SSascha Wildner /*
42*8c117293SSascha Wildner  * Number of bits encoded per word.  We use 4096 words, which gives 12 bits,
43*8c117293SSascha Wildner  * and we toggle the case of the first character, which gives one bit more.
44*8c117293SSascha Wildner  */
45*8c117293SSascha Wildner #define WORD_BITS			13
46*8c117293SSascha Wildner 
47*8c117293SSascha Wildner /*
48*8c117293SSascha Wildner  * Number of bits encoded per separator and word.
49*8c117293SSascha Wildner  */
50*8c117293SSascha Wildner #define SWORD_BITS \
51*8c117293SSascha Wildner 	(SEPARATOR_BITS + WORD_BITS)
52*8c117293SSascha Wildner 
53*8c117293SSascha Wildner /*
54*8c117293SSascha Wildner  * Maximum number of words to use.
55*8c117293SSascha Wildner  */
56*8c117293SSascha Wildner #define WORDS_MAX			8
57*8c117293SSascha Wildner 
58*8c117293SSascha Wildner /*
59*8c117293SSascha Wildner  * Minimum and maximum number of bits to encode.  With the settings above,
60*8c117293SSascha Wildner  * these are 24 and 136, respectively.
61*8c117293SSascha Wildner  */
62*8c117293SSascha Wildner #define BITS_MIN \
63*8c117293SSascha Wildner 	(2 * (WORD_BITS - 1))
64*8c117293SSascha Wildner #define BITS_MAX \
65*8c117293SSascha Wildner 	(WORDS_MAX * SWORD_BITS)
66*8c117293SSascha Wildner 
67*8c117293SSascha Wildner #ifndef _MSC_VER
read_loop(int fd,void * buffer,size_t count)68*8c117293SSascha Wildner static ssize_t read_loop(int fd, void *buffer, size_t count)
6916d0e647SPeter Avalos {
70*8c117293SSascha Wildner 	ssize_t offset, block;
7116d0e647SPeter Avalos 
7216d0e647SPeter Avalos 	offset = 0;
73*8c117293SSascha Wildner 	while (count > 0 && count <= SSIZE_MAX) {
74*8c117293SSascha Wildner 		block = read(fd, (char *)buffer + offset, count);
7516d0e647SPeter Avalos 
7616d0e647SPeter Avalos 		if (block < 0) {
77*8c117293SSascha Wildner 			if (errno == EINTR)
78*8c117293SSascha Wildner 				continue;
7916d0e647SPeter Avalos 			return block;
8016d0e647SPeter Avalos 		}
81*8c117293SSascha Wildner 
82*8c117293SSascha Wildner 		if (!block)
83*8c117293SSascha Wildner 			return offset;
8416d0e647SPeter Avalos 
8516d0e647SPeter Avalos 		offset += block;
8616d0e647SPeter Avalos 		count -= block;
8716d0e647SPeter Avalos 	}
8816d0e647SPeter Avalos 
8916d0e647SPeter Avalos 	return offset;
9016d0e647SPeter Avalos }
91*8c117293SSascha Wildner #endif
9216d0e647SPeter Avalos 
passwdqc_random(const passwdqc_params_qc_t * params)93*8c117293SSascha Wildner char *passwdqc_random(const passwdqc_params_qc_t *params)
9416d0e647SPeter Avalos {
95*8c117293SSascha Wildner 	int bits = params->random_bits; /* further code assumes signed type */
96*8c117293SSascha Wildner 	if (bits < BITS_MIN || bits > BITS_MAX)
97*8c117293SSascha Wildner 		return NULL;
98*8c117293SSascha Wildner 
99*8c117293SSascha Wildner /*
100*8c117293SSascha Wildner  * Calculate the number of words to use.  The first word is always present
101*8c117293SSascha Wildner  * (hence the "1 +" and the "- WORD_BITS").  Each one of the following words,
102*8c117293SSascha Wildner  * if any, is prefixed by a separator character, so we use SWORD_BITS when
103*8c117293SSascha Wildner  * calculating how many additional words to use.  We divide "bits - WORD_BITS"
104*8c117293SSascha Wildner  * by SWORD_BITS with rounding up (hence the addition of "SWORD_BITS - 1").
105*8c117293SSascha Wildner  */
106*8c117293SSascha Wildner 	int word_count = 1 + (bits + (SWORD_BITS - 1 - WORD_BITS)) / SWORD_BITS;
107*8c117293SSascha Wildner 
108*8c117293SSascha Wildner /*
109*8c117293SSascha Wildner  * Special case: would we still encode enough bits if we omit the final word,
110*8c117293SSascha Wildner  * but keep the would-be-trailing separator?
111*8c117293SSascha Wildner  */
112*8c117293SSascha Wildner 	int trailing_separator = (SWORD_BITS * (word_count - 1) >= bits);
113*8c117293SSascha Wildner 	word_count -= trailing_separator;
114*8c117293SSascha Wildner 
115*8c117293SSascha Wildner /*
116*8c117293SSascha Wildner  * To determine whether we need to use different separator characters or maybe
117*8c117293SSascha Wildner  * not, calculate the number of words we'd need to use if we don't use
118*8c117293SSascha Wildner  * different separators.  We calculate it by dividing "bits" by WORD_BITS with
119*8c117293SSascha Wildner  * rounding up (hence the addition of "WORD_BITS - 1").  The resulting number
120*8c117293SSascha Wildner  * is either the same as or greater than word_count.  Use different separators
121*8c117293SSascha Wildner  * only if their use, in the word_count calculation above, has helped reduce
122*8c117293SSascha Wildner  * word_count.
123*8c117293SSascha Wildner  */
124*8c117293SSascha Wildner 	int use_separators = ((bits + (WORD_BITS - 1)) / WORD_BITS != word_count);
125*8c117293SSascha Wildner 	trailing_separator &= use_separators;
126*8c117293SSascha Wildner 
127*8c117293SSascha Wildner /*
128*8c117293SSascha Wildner  * Toggle case of the first character of each word only if we wouldn't achieve
129*8c117293SSascha Wildner  * sufficient entropy otherwise.
130*8c117293SSascha Wildner  */
131*8c117293SSascha Wildner 	int toggle_case = (bits >
132*8c117293SSascha Wildner 	    ((WORD_BITS - 1) * word_count) +
133*8c117293SSascha Wildner 	    (use_separators ? (SEPARATOR_BITS * (word_count - !trailing_separator)) : 0));
134*8c117293SSascha Wildner 
135*8c117293SSascha Wildner 	char output[0x100];
136*8c117293SSascha Wildner 
137*8c117293SSascha Wildner /*
138*8c117293SSascha Wildner  * Calculate and check the maximum possible length of a "passphrase" we may
139*8c117293SSascha Wildner  * generate for a given word_count.  We add 1 to WORDSET_4K_LENGTH_MAX to
140*8c117293SSascha Wildner  * account for separators (whether different or not).  When there's no
141*8c117293SSascha Wildner  * trailing separator, we subtract 1.  The check against sizeof(output) uses
142*8c117293SSascha Wildner  * ">=" to account for NUL termination.
143*8c117293SSascha Wildner  */
144*8c117293SSascha Wildner 	unsigned int max_length = word_count * (WORDSET_4K_LENGTH_MAX + 1) - !trailing_separator;
145*8c117293SSascha Wildner 	if (max_length >= sizeof(output) || (int)max_length > params->max)
146*8c117293SSascha Wildner 		return NULL;
147*8c117293SSascha Wildner 
148*8c117293SSascha Wildner 	unsigned char rnd[WORDS_MAX * 3];
149*8c117293SSascha Wildner 
150*8c117293SSascha Wildner #ifdef _MSC_VER
151*8c117293SSascha Wildner 	HCRYPTPROV hprov;
152*8c117293SSascha Wildner 	if (!CryptAcquireContextA(&hprov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
153*8c117293SSascha Wildner 		return NULL;
154*8c117293SSascha Wildner 	memset(rnd, 0, sizeof(rnd)); /* old Windows would use previous content as extra seed */
155*8c117293SSascha Wildner 	if (!CryptGenRandom(hprov, sizeof(rnd), rnd)) {
156*8c117293SSascha Wildner 		CryptReleaseContext(hprov, 0);
157*8c117293SSascha Wildner 		return NULL;
158*8c117293SSascha Wildner 	}
159*8c117293SSascha Wildner 	CryptReleaseContext(hprov, 0);
160*8c117293SSascha Wildner #else
16116d0e647SPeter Avalos 	int fd;
162*8c117293SSascha Wildner 	if ((fd = open("/dev/urandom", O_RDONLY)) < 0)
16316d0e647SPeter Avalos 		return NULL;
164*8c117293SSascha Wildner 	if (read_loop(fd, rnd, sizeof(rnd)) != sizeof(rnd)) {
16516d0e647SPeter Avalos 		close(fd);
16616d0e647SPeter Avalos 		return NULL;
16716d0e647SPeter Avalos 	}
16816d0e647SPeter Avalos 	close(fd);
169*8c117293SSascha Wildner #endif
170*8c117293SSascha Wildner 
171*8c117293SSascha Wildner 	size_t length = 0;
172*8c117293SSascha Wildner 	const unsigned char *rndptr;
173*8c117293SSascha Wildner 
174*8c117293SSascha Wildner 	for (rndptr = rnd; rndptr <= rnd + sizeof(rnd) - 3; rndptr += 3) {
175*8c117293SSascha Wildner /*
176*8c117293SSascha Wildner  * Append a word.
177*8c117293SSascha Wildner  *
178*8c117293SSascha Wildner  * Treating *rndptr as little-endian, we use bits 0 to 11 for the word index
179*8c117293SSascha Wildner  * and bit 13 for toggling the case of the first character.  Bits 12, 14, and
180*8c117293SSascha Wildner  * 15 are left unused.  Bits 16 to 23 are left for the separator.
181*8c117293SSascha Wildner  */
182*8c117293SSascha Wildner 		unsigned int i = (((unsigned int)rndptr[1] & 0x0f) << 8) | rndptr[0];
183*8c117293SSascha Wildner 		const char *start = _passwdqc_wordset_4k[i];
184*8c117293SSascha Wildner 		const char *end = memchr(start, '\0', WORDSET_4K_LENGTH_MAX);
185*8c117293SSascha Wildner 		if (!end)
186*8c117293SSascha Wildner 			end = start + WORDSET_4K_LENGTH_MAX;
187*8c117293SSascha Wildner 		size_t extra = end - start;
188*8c117293SSascha Wildner /* The ">=" leaves room for either one more separator or NUL */
189*8c117293SSascha Wildner 		if (length + extra >= sizeof(output))
190*8c117293SSascha Wildner 			break;
19116d0e647SPeter Avalos 		memcpy(&output[length], start, extra);
192*8c117293SSascha Wildner 		if (toggle_case) {
193*8c117293SSascha Wildner /* Toggle case if bit set (we assume ASCII) */
194*8c117293SSascha Wildner 			output[length] ^= rndptr[1] & 0x20;
195*8c117293SSascha Wildner 			bits--;
196*8c117293SSascha Wildner 		}
19716d0e647SPeter Avalos 		length += extra;
198*8c117293SSascha Wildner 		bits -= WORD_BITS - 1;
19916d0e647SPeter Avalos 
200*8c117293SSascha Wildner 		if (bits <= 0)
201*8c117293SSascha Wildner 			break;
202*8c117293SSascha Wildner 
203*8c117293SSascha Wildner /*
204*8c117293SSascha Wildner  * Append a separator character.  We use bits 16 to 19.  Bits 20 to 23 are left
205*8c117293SSascha Wildner  * unused.
206*8c117293SSascha Wildner  *
207*8c117293SSascha Wildner  * Special case: we may happen to leave a trailing separator if it provides
208*8c117293SSascha Wildner  * enough bits on its own.  With WORD_BITS 13 and SEPARATOR_BITS 4, this
209*8c117293SSascha Wildner  * happens e.g. for bits values from 31 to 34, 48 to 51, 65 to 68.
210*8c117293SSascha Wildner  */
211*8c117293SSascha Wildner 		if (use_separators) {
212*8c117293SSascha Wildner 			i = rndptr[2] & 0x0f;
21316d0e647SPeter Avalos 			output[length++] = SEPARATORS[i];
214*8c117293SSascha Wildner 			bits -= SEPARATOR_BITS;
215*8c117293SSascha Wildner 		} else {
216*8c117293SSascha Wildner 			output[length++] = SEPARATORS[0];
217*8c117293SSascha Wildner 		}
21816d0e647SPeter Avalos 
219*8c117293SSascha Wildner 		if (bits <= 0)
220*8c117293SSascha Wildner 			break;
221*8c117293SSascha Wildner 	}
222*8c117293SSascha Wildner 
223*8c117293SSascha Wildner 	char *retval = NULL;
224*8c117293SSascha Wildner 
225*8c117293SSascha Wildner 	if (bits <= 0 && length < sizeof(output)) {
22616d0e647SPeter Avalos 		output[length] = '\0';
227*8c117293SSascha Wildner 		retval = strdup(output);
228*8c117293SSascha Wildner 	}
22916d0e647SPeter Avalos 
230*8c117293SSascha Wildner 	_passwdqc_memzero(rnd, sizeof(rnd));
231*8c117293SSascha Wildner 	_passwdqc_memzero(output, length);
23216d0e647SPeter Avalos 
233*8c117293SSascha Wildner 	return retval;
23416d0e647SPeter Avalos }
235