@@ -881,32 +881,18 @@ generate_passphrase() {
881881 echo " [--lowercase|-l] Use lowercase words (default: false)."
882882 }
883883
884- # Helper subfunction to get a word from the dictionary based on dice rolls
885- get_word_from_dictionary () {
886- local rolls=" $1 "
887- local dictionary_file=" $2 "
888- local word=" "
889-
890- word=$( grep " ^$rolls " " $dictionary_file " | awk -F ' ' ' {print $2}' )
891- echo " $word "
892- }
893-
894- # Helper subfunction to generate dice rolls
895- generate_dice_rolls () {
896- TRACE_FUNC
897- local num_rolls=" $1 "
898- local rolls=" "
899- local random_bytes
900-
901- # Read num_rolls bytes from /dev/random, fed by CPU RRAND in one go
902- random_bytes=$( dd if=/dev/random bs=1 count=" $num_rolls " 2> /dev/null | hexdump -e ' 1/1 "%u\n"' )
903-
904- # Process each byte to generate a dice roll
905- while read -r byte; do
906- roll=$(( byte % 6 + 1 ))
907- rolls+=$roll
908- done <<< " $random_bytes"
909- echo " $rolls "
884+ # Helper subfunction to get a random word from the dictionary
885+ get_random_word_from_dictionary () {
886+ local dictionary_file=" $1 " lines random
887+
888+ lines=" $( wc -l < " $dictionary_file " ) "
889+ # 4 random bytes are used to reduce modulo bias to an acceptable
890+ # level. 4 bytes with modulus 1296 results in 0.000003% bias
891+ # toward the first 1263 words.
892+ random=" $( dd if=/dev/random bs=4 count=1 status=none | hexdump -e ' 1/4 "%u\n"' ) "
893+ (( random%= lines))
894+ (( ++ random)) # tail's line count is 1-based
895+ tail -n +" $random " " $dictionary_file " | head -1 | cut -d$' \t ' -f2
910896 }
911897
912898 TRACE_FUNC
@@ -961,25 +947,9 @@ generate_passphrase() {
961947
962948 local passphrase=" "
963949 local word=" "
964- local key=" "
965- local digits=0
966-
967- # Read the number of digits from the first line of the dictionary file
968- read -r key _ < " $dictionary_file "
969-
970- # Validate that the key is composed entirely of digits
971- if ! [[ $key =~ ^[0-9]+$ ]]; then
972- echo " Error: Dictionary is not compliant with EFF diceware dictionaries."
973- echo " The first line of the dictionary should be in the format: <digits> <word>"
974- echo " Example: 11111 word"
975- exit 1
976- fi
977-
978- digits=${# key} # Number of digits in dice rolls
979950
980951 for (( i = 0 ; i < num_words; ++ i)) ; do
981- key=$( generate_dice_rolls " $digits " )
982- word=$( get_word_from_dictionary " $key " " $dictionary_file " )
952+ word=$( get_random_word_from_dictionary " $dictionary_file " )
983953 if [[ " $lowercase " == " false" ]]; then
984954 word=${word^} # Capitalize the first letter
985955 fi
0 commit comments