diff --git a/dice.c b/dice.c index b082e32..3644307 100644 --- a/dice.c +++ b/dice.c @@ -9,101 +9,103 @@ #define DICTFILE "/usr/share/dict/american-english" #define VERSION "1.0" #define RLEN 1024 -#define WMIN 6 -#define WMAX 10 int humantoss = 0; +int WMIN = 5; +int WMAX = 10; int usage() { fprintf(stderr, "Generate a random diceware passphrase\n" "Usage: dice [-cfvh]\n" "Options: \n" - "-t Asks interactively for tossed dices\n" - "-c Number of words (default: 4)\n" - "-f Dictionary file to use (default:\n" - " /usr/share/dict/american-english)\n" - "-v Print program version\n" - "-h Print this help screen\n" + "-t --humantoss Asks interactively for tossed dices\n" + "-c --wordcount Number of words (default: 4)\n" + "-f --dictfile Dictionary file to use (default:\n" + " /usr/share/dict/american-english)\n" + "-l --minlen Minimum word len (default: 5)\n" + "-m --maxlen Maximum word len (default: 10)\n" + "-v --version Print program version\n" + "-h -? --help Print this help screen\n" ); return 1; } -void _dump(unsigned char *d, size_t s) { - size_t i; - int c; - for (i=0; i dices. if the global humandice is enabled (option -t), + then ask the human to toss dices and enter the numbers interactively. + otherwise generate tosses from /dev/random. + */ + + FILE *RAND; + int i; + int pos = 0; + uint8_t onedice; + unsigned char *tosses = NULL; + unsigned char *rand = NULL; + size_t len; + ssize_t linelen; - if(humantoss) { - char *line = NULL; - char digit[2]; - digit[1] = '\0'; + if(humantoss) { + char *line = NULL; + char digit[2]; + digit[1] = '\0'; - RETRY: - fprintf(stderr, "enter 5 digits, each between 1-6\n"); - linelen = getline(&line, &len, stdin); + RETRY: + fprintf(stderr, "enter 5 digits, each between 1-6\n"); + linelen = getline(&line, &len, stdin); + tosses = malloc((size_t)count); + + if(linelen < 6) /* 5 digits max allowed */ + goto RETRY; - if(linelen < 6) - goto RETRY; + for(i=0; i 6) /* no dice digit */ + goto RETRY; - if (onedice < 1 || onedice > 6) - goto RETRY; - - tosses[i] = onedice; - } - free(line); - } - else { - rand = malloc(RLEN); + tosses[i] = onedice; + } + free(line); + } + else { + rand = malloc(RLEN); - if((RAND = fopen("/dev/urandom", "rb")) == NULL) { - perror("Could not open /dev/urandom"); - } + if((RAND = fopen("/dev/urandom", "rb")) == NULL) { + perror("Could not open /dev/urandom"); + } - fread(rand, RLEN, 1, RAND); - fclose(RAND); + fread(rand, RLEN, 1, RAND); + fclose(RAND); + + tosses = malloc((size_t)count); + + for(i=0; i= 1 && onedice <= 6) { + tosses[pos] = onedice; + pos++; + } + if(pos == count) + break; + } + free(rand); + } - for(i=0; i= 1 && onedice <= 6) { - tosses[pos] = onedice; - pos++; - } - if(pos == count) - break; - } - free(rand); - } - - return tosses; + return tosses; } int rand_lim(int limit) { - /* return a random number in the range [0..limit) + /* + return a random number in the range [0..limit) */ int divisor = RAND_MAX/limit; @@ -116,32 +118,87 @@ int rand_lim(int limit) { return retval; } -void getwords(char *dictfile, int count) { +int *incr_dicedigit(int *digits) { + /* + increment an array of dice digits, we expect the first to + be a multiple of 10000, the 2nd a multiple of 1000 and so on. + */ + if(digits[4] == 6) { + digits[4] = 1; + if(digits[3] == 60) { + digits[3] = 10; + if(digits[2] == 600) { + digits[2] = 100; + if(digits[1] == 6000) { + digits[1] = 1000; + digits[0] += 10000; /* may overflow to 71111, must be catched by caller */ + } + else + digits[1] += 1000; + } + else + digits[2] += 100; + } + else + digits[3] += 10; + } + else + digits[4]++; + + return digits; +} + +int get_dicenum(int *digits) { + /* + get the actual number of an array of dice digits + */ + int i = 0; + int pos = 0; + for(i=0; i<5; i++) + pos += digits[i]; + + return pos; +} + + +char **fetch_dict(char *dictfile) { + /* + read in the dictionary file. we generate an array of max + 66666 entries from the dictionary, each entry's index will + be a dice number (1 <=> 6). to enhance randomness, we jump + over a number of lines (1-32 lines) and start from the beginning + of the file if we reach the end before our array is full. + */ + char **words; - int one, two, three, four, five, pos, i, next, jump; + int pos, i, next, jump; char *line = NULL; size_t len = 0; ssize_t linelen; FILE *DICT; - unsigned char *tosses; - int *tossed; - - one = 10000; - two = 1000; - three = 100; - four = 10; - five = 1; + int *digits; - pos = 11111; - - words = malloc(66666 * sizeof(char *)); - tossed = malloc(count); - jump = rand_lim(32); - if((DICT = fopen(dictfile, "rb")) == NULL) { perror("Could not open dictfile"); } + words = malloc(66666 * sizeof(char *)); + digits = malloc(5); + jump = rand_lim(32); + + digits[0] = 10000; + digits[1] = 1000; + digits[2] = 100; + digits[3] = 10; + digits[4] = 1; + + pos = 11111; + next = 0; + + for(i=0; i<6666; i++) + words[i] = NULL; + + LOOP: while ((linelen = getline(&line, &len, DICT)) != -1) { if(jump > 0) { @@ -170,29 +227,8 @@ void getwords(char *dictfile, int count) { strncpy( words[pos], line, linelen); //fprintf(stdout, "%d: %s\n", pos, line); - if(five == 6) { - five = 1; - if(four == 60) { - four = 10; - if(three == 600) { - three = 100; - if(two == 6000) { - two = 1000; - one += 10000; - } - else - two += 1000; - } - else - three += 100; - } - else - four += 10; - } - else - five++; - - pos = one + two + three + four + five; + digits = incr_dicedigit(digits); + pos = get_dicenum(digits); /* this is what pos gets next after 66666, which is max reachable with 5 dices */ if(pos == 71111) @@ -207,10 +243,28 @@ void getwords(char *dictfile, int count) { fclose(DICT); free(line); + free(digits); + return words; +} + +void getwords(char *dictfile, int count) { + /* + initiate dice tossing, extract matching number of words + froom the wordlist and print it. + */ + char **words; + int i, pos, one, two, three, four, five; + int *tossed; + unsigned char *tosses; + + words = fetch_dict(dictfile); + + tossed = malloc(count * sizeof(int)); + for(i=0; i