📄 fips.c
字号:
/*
* Execute this upon startup. This initializes the
* environment, including seed'ing the random number
* generator and loading the on-line dictionary.
*/
if (!been_here_before)
{
been_here_before = TRUE;
#ifndef RAN_DEBUG
set_seed(seed);
#endif
}
/*
* Check for minlen>maxlen. This is an error.
* and a length of 0.
*/
if (minlen > maxlen)
return (-1);
/*
* Check for zero length words. This is technically not an error,
* so we take the short cut and return a null word and a length of
0.
*/
if (maxlen == 0)
{
word[0] = '\0';
hyphenated_word[0] = '\0';
return (0);
}
/*
* Continue finding words until the criteria are satisfied.
* The criteria are, if restrict is set, that if the word appears
* as either a login name or as part of the on-line dictionary,
* throw out the word and look for another.
*/
loop_count = 0;
do
{
/*
* Get a random word. Its length is a random quantity
* from with the limits specified in the call to
* randomword().
*/
pwlen = get_word (word, hyphenated_word, get_random (minlen,
maxlen));
restrict = 0;
loop_count++;
}
while (restrict && (loop_count <= MAX_UNACCEPTABLE));
if (restrict) {
(void) fflush(stdout);
(void) fprintf(stderr, "could not find acceptable random password\n");
(void) fflush(stderr);
exit(1);
}
return (pwlen);
}
/*
* This is the routine that returns a random word -- as
* yet unchecked against the passwd file or the dictionary.
* It collects random syllables until a predetermined
* word length is found. If a retry threshold is reached,
* another word is tried. Given that the random number
* generator is uniformly distributed, eventually a word
* will be found if the retry limit is adequately large enough.
*/
static int
get_word (word, hyphenated_word, pwlen)
char *word;
char *hyphenated_word;
unsigned short int pwlen;
{
register unsigned short int word_length;
register unsigned short int syllable_length;
register char *new_syllable;
register unsigned short int *syllable_units;
register unsigned short int word_size;
register unsigned short int word_place;
int unsigned short *word_units;
int unsigned short syllable_size;
int unsigned tries;
/*
* Keep count of retries.
*/
tries = 0;
/*
* The length of the word in characters.
*/
word_length = 0;
/*
* The length of the word in character units (each of which is
one or
* two characters long.
*/
word_size = 0;
/*
* Initialize the array storing the word units. Since we know
the
* length of the word, we only need one of that length. This
method is
* preferable to a static array, since it allows us flexibility
in
* choosing arbitrarily long word lengths. Since a word can
contain one
* syllable, we should make syllable_units, the array holding
the
* analogous units for an individual syllable, the same length.
No
* explicit rule limits the length of syllables, but digram
rules and
* heuristics do so indirectly.
*/
word_units =
(unsigned short int *)
calloc (sizeof (unsigned short int), pwlen);
syllable_units =
(unsigned short int *)
calloc (sizeof (unsigned short int), pwlen);
new_syllable =
calloc (sizeof (unsigned short int), pwlen);
/*
* Find syllables until the entire word is constructed.
*/
while (word_length < pwlen)
{
/*
* Get the syllable and find its length.
*/
(void) get_syllable (new_syllable, pwlen - word_length,
syllable_units, &syllable_size);
syllable_length = strlen (new_syllable);
/*
* Append the syllable units to the word units.
*/
for (word_place = 0; word_place <= syllable_size;
word_place++)
word_units[word_size + word_place] =
syllable_units[word_place];
word_size += syllable_size + 1;
/*
* If the word has been improperly formed, throw out
* the syllable. The checks performed here are those
* that must be formed on a word basis. The other
* tests are performed entirely within the syllable.
* Otherwise, append the syllable to the word and
* append the syllable to the hyphenated version of
* the word.
*/
if (improper_word (word_units, word_size) ||
((word_length == 0) &&
have_initial_y (syllable_units, syllable_size)) ||
((word_length + syllable_length == pwlen) &&
have_final_split (syllable_units, syllable_size)))
word_size -= syllable_size + 1;
else
{
if (word_length == 0)
{
(void) strcpy (word, new_syllable);
(void) strcpy (hyphenated_word, new_syllable);
}
else
{
(void) strcat (word, new_syllable);
(void) strcat (hyphenated_word, "-");
(void) strcat (hyphenated_word, new_syllable);
}
word_length += syllable_length;
}
/*
* Keep track of the times we have tried to get
* syllables. If we have exceeded the threshold,
* reinitialize the pwlen and word_size variables, clear
* out the word arrays, and start from scratch.
*/
tries++;
if (tries > MAX_RETRIES)
{
word_length = 0;
word_size = 0;
tries = 0;
(void) strcpy (word, "");
(void) strcpy (hyphenated_word, "");
}
}
/*
* The units arrays and syllable storage are internal to this
* routine. Since the caller has no need for them, we
* release the space.
*/
free ((char *) new_syllable);
free ((char *) syllable_units);
free ((char *) word_units);
return ((int) word_length);
}
/*
* Check that the word does not contain illegal combinations
* that may span syllables. Specifically, these are:
* 1. An illegal pair of units between syllables.
* 2. Three consecutive vowel units.
* 3. Three consecutive consonant units.
* The checks are made against units (1 or 2 letters), not against
* the individual letters, so three consecutive units can have
* the length of 6 at most.
*/
static boolean
improper_word (units, word_size)
register unsigned short int *units;
register unsigned short int word_size;
{
register unsigned short int unit_count;
register boolean failure;
failure = FALSE;
for (unit_count = 0; !failure && (unit_count < word_size);
unit_count++)
{
/*
* Check for ILLEGAL_PAIR. This should have been caught
* for units within a syllable, but in some cases it
* would have gone unnoticed for units between syllables
* (e.g., when saved_unit's in get_syllable() were not
* used).
*/
if ((unit_count != 0) &&
(digram[units[unit_count - 1]][units[unit_count]] &
ILLEGAL_PAIR))
failure = TRUE;
/*
* Check for consecutive vowels or consonants. Because
* the initial y of a syllable is treated as a consonant
* rather than as a vowel, we exclude y from the first
* vowel in the vowel test. The only problem comes when
* y ends a syllable and two other vowels start the next,
* like fly-oint. Since such words are still
* pronounceable, we accept this.
*/
if (!failure && (unit_count >= 2))
{
/*
* Vowel check.
*/
if ((((rules[units[unit_count - 2]].flags & VOWEL) &&
!(rules[units[unit_count - 2]].flags &
ALTERNATE_VOWEL)) &&
(rules[units[unit_count - 1]].flags & VOWEL) &&
(rules[units[unit_count]].flags & VOWEL)) ||
/*
* Consonant check.
*/
(!(rules[units[unit_count - 2]].flags & VOWEL) &&
!(rules[units[unit_count - 1]].flags & VOWEL) &&
!(rules[units[unit_count]].flags & VOWEL)))
failure = TRUE;
}
}
return (failure);
}
/*
* Treating y as a vowel is sometimes a problem. Some words
* get formed that look irregular. One special group is when
* y starts a word and is the only vowel in the first syllable.
* The word ycl is one example. We discard words like these.
*/
static boolean
have_initial_y (units, unit_size)
register unsigned short int *units;
register unsigned short int unit_size;
{
register unsigned short int unit_count;
register unsigned short int vowel_count;
register unsigned short int normal_vowel_count;
vowel_count = 0;
normal_vowel_count = 0;
for (unit_count = 0; unit_count <= unit_size; unit_count++)
/*
* Count vowels.
*/
if (rules[units[unit_count]].flags & VOWEL)
{
vowel_count++;
/*
* Count the vowels that are not: 1. y, 2. at the start of
* the word.
*/
if (!(rules[units[unit_count]].flags & ALTERNATE_VOWEL)
||
(unit_count != 0))
normal_vowel_count++;
}
return ((vowel_count <= 1) && (normal_vowel_count == 0));
}
/*
* Besides the problem with the letter y, there is one with
* a silent e at the end of words, like face or nice. We
* allow this silent e, but we do not allow it as the only
* vowel at the end of the word or syllables like ble will
* be generated.
*/
static boolean
have_final_split (units, unit_size)
register unsigned short int *units;
register unsigned short int unit_size;
{
register unsigned short int unit_count;
register unsigned short int vowel_count;
vowel_count = 0;
/*
* Count all the vowels in the word.
*/
for (unit_count = 0; unit_count <= unit_size; unit_count++)
if (rules[units[unit_count]].flags & VOWEL)
vowel_count++;
/*
* Return TRUE iff the only vowel was e, found at the end if
the
* word.
*/
return ((vowel_count == 1) &&
(rules[units[unit_size]].flags & NO_FINAL_SPLIT));
}
/*
* Generate next unit to password, making sure that it follows
* these rules:
* 1. Each syllable must contain exactly 1 or 2 consecutive
* vowels, where y is considered a vowel.
* 2. Syllable end is determined as follows:
* a. Vowel is generated and previous unit is a
* consonant and syllable already has a vowel. In
* this case, new syllable is started and already
* contains a vowel.
* b. A pair determined to be a "break" pair is encountered.
* In this case new syllable is started with second unit
* of this pair.
* c. End of password is encountered.
* d. "begin" pair is encountered legally. New syllable is
* started with this pair.
* e. "end" pair is legally encountered. New syllable has
* nothing yet.
* 3. Try generating another unit if:
* a. third consecutive vowel and not y.
* b. "break" pair generated but no vowel yet in current
* or previous 2 units are "not_end".
* c. "begin" pair generated but no vowel in syllable
* preceding begin pair, or both previous 2 pairs are
* designated "not_end".
* d. "end" pair generated but no vowel
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -