#!/usr/bin/perl # Copyright 2000 - 2015 George Shaffer (g~e~o~r~g~e at g-e-o-d-s-o-f-t dot c=o=m)) # Anyone may use or modify this code for any purpose PROVIDED # that as long as it is recognizably derived from this code, # that the copyright notice, this comment and the immediately # following comments on passwords up to and including the words # "anything from this point forward." remain intact and unchanged. # No warrantees of any kind are expressed or implied. # In the web version the copyright notice displayed at the # bottom of the web page must also remain intact, which # means from the word "Copyright" to "top of the source code." # This notice must remain centered and occupy 3 (or more) lines. # The entire form may be wrapped in a frame, a table element, # or another structural component of a web page. # You may add your own copyright notice above or below mine # pertaining to the changes you have made. You may reduce # the size of my notice by one font size relative to your # notice. If the color of the type or background for my notice # differs from yours, my notice must have as much or slightly # more contrast than your notice. # No warrantees of any kind are expressed or implied. # What does "recognizably derived from" mean. It means # 4 or more consecutive lines (not counting blank lines or # comments) with the same logical structure, OR # a total of 10 lines in the whole program with the same # logical structure. Changed varible names are irrelevant. # Ahy variable in the same location is the same variable. # How do you get code that is not recognizeably derived? # I can only think of two ways. 1: You cheat. You go through # line by and systematically find a way to do the same thing # using different logic. It's tedious but not hard, but # why bother? If you are going to cheat, just steal the code. # Delete my copyright notice; delete any comments you # don't want. Cheater or thief? It does not matter. # They are the same when it comes to intellectual property. # You have to live with it, not me; I'll never know. # The honest way to get code not derived from mine is to rewrite # from scratch without ever looking at my code again, # or any code that you wrote that was derived from mine. # You keep one copy of the code, readable only by the # website. You can run the generater as often as you want # so you get all the functionality and features that # are present. But you write the new Perl or any other # language from scratch. There are a lot less than 200 # executable lines. There is nothing that is difficult # or tricky. If you never again look at my source, your # program will not look like mine even if it only did # the same things as my original generator. No two people # wrte code in quite the same way. # I have no problem acknowledging my creative inspiration, # the State Departments password generator. It's an important # part of who I am and what I've done. Of the hundreds, # or maybe thousands with techical backgrounds, who worked # with this tool, it seems I'm the only one who recognized # its brilliance, or cared enough to do anything with it. # I never knew who wrote it or saw a line of code and it # was almost 15 years after I last saw it that I did anything # related to it. # But without having used the State Department's Controlled # User Environment password generator, I do not belive I could # have ever written mine. If I had tried it would have been some # thing quite different. CVC99CVC is something seared into my # brain. It's had a major influence on all my thinking about # good passwords. In a very important way it is the figurative # cornerston of everything said about passwords on GeodSoft.com, # one of the more important password sites on the Internet. # The anonymous analysts and programers who workded for the # State Department in the early to mid 1980's need to be # acknowleged. # Passwords: There are password generators that generate # far more random passwords choosing from letters, letters # and numbers, or the whole keyboard. These passwords are # impossible for normal human beings to remember # assuring that they will be written down in a readily # accessible location, thus largely defeating the purpose # of good passwords. Real security threats are more # likely to be internal than external. Some years ago I # had the opportunity to work with the U.S. State Department's # Controlled User Environment password generator. It generated # 8 character passwords in the form of CVC99CVC or consonant, # vowel, consonant, digit, digit, consonant, vowel, consonant. # These passwords had two easily pronounceable pieces separated # by two digits. For passwords not subject to dictionary # attacks or easily associated with personal interests or # information, these passwords were surprisingly easy to # remember, making much better practical passwords than true # randomly generated passwords. The only problem with this # approach is that there are only some 400+ million passwords # which is not enough with today's computing power and # brute force attacks. # I have tried to extend the core concept but keep most # of it's advantages. With the defaults, password.pl # pseudo randomly adds up to two additional # consonants. Further the first # character of either or both letter sequences is pseudo # randomly upper cased. Also one of the digits is sometimes # replaced by a punctuation mark. Resulting passwords are # 7 - 10 characters and have at least one digit and one upper # case character. Many have a punctuation mark or symbol but # only from those characters that I find relatively easy to # type without looking at the keyboard. I estimate this # extends password universe by approximately 1000 times but # could be way off. If you change the defaults or logic # you'll change the number of possible passwords. # The previous paragraph was written in 2000. In 2015 I # finally did the calculations. This version generates # 18.4 thousand times as many passwords as the original State # Department generator. In 2015 a fast password cracking # network should be able to crack all 7 trillion passwords # this generator creates, with default settings, in somewhat # over a minute. Doing all 94 printable characters up to ten # character passwords would take about 23 years. If a cracker # can get your password file, they can probably ask a user # who has seen the generated passwords, what they look like. # With the right questions, my guess is the crack time for # all passwords in your file would be hours to a few days. # All password crack time estimates require multiple # untestable assumptions. I would strongly suggest that # that both base length and symbol odds be increased to # 10, or that you add comparable complexity of your own # design. Playing with patterns in the current online # generator could show you, what various changes you may # consider, would look like, such as nCc0v2cC0nCc0v2cC0. # Run with my original settings you get 10 passwords at a time. # Some should meet virtually any requirements for length, [15 years ago] # letters, numbers and symbols. Typically a few are fairly # easy to remember but still much better than those I have # been choosing for myself and the system admin accounts # I have been responsible for over the past 10 or so years. # If $siz = 8, $addConsonants = 0, $firstUpper = 0, # $mixedCase = 0, and $symbolOdds = 0 you'll see State # Department style passwords except in lower case. # I suggest that anyone who makes frequent use of this, # change the "User Changeable Constants" to fit your tastes # and environment. I chose hardcoded values rather than # command line options in the belief that anyone who can use # a text editor can easily and permanently adjust the behavior # of this program more easily than remembering or typing # command line options. Each choice is documented. # The logic for this version was written in 2000. Most of the # passwords displayed were good then. In mid 2012 I considder the # good 10 character passwords the bare minimum for accounts where # good passwords are needed. At least increase the base length to 9 or 10. # It would be even better to look at the current online pattern # based generater and try to add some of that flexibility here. # If you don't make it public, i.e., only use it inside your organization # or home, keep 1 or two pronouceable alpha chunks, use 11 or 12 characters, # no one else ever needs to know where the digits and symbols may be. # Anything that you can remember while adding length and character # diversity makes better passwords. Don't tell people you use a # password generator or if you do, not where it comes from. The more changes # to new defaults, new logic, and away from the original, the better. # You can always manually add a vowel to make something pronounceable or # take pieces from two passwords. # Please feel free to change anything from this point forward. # George Shaffer - have fun! use CGI ':standard'; # This Root_Dir code is probably not needed as it was mainly used to # find the include files which contained the HTML code for the standard # GeodSoft layout and navigation aids. It cannot hurt ahything ahd # you may wish to keep it in case you have and common functions or # code you wish to include, and it is located in a standard location, # such as cgi-bin, relative to the website's document root directory. $Root_Dir = $ENV{DOCUMENT_ROOT}; if ($Root_Dir eq "") { $Root_Dir = $ENV{PATH_TRANSLATED}; $Root_Dir =~ s|\\|/|g; $Root_Dir =~ s|(.*)/cgi-bin.*|$1|; } $Root_Dir =~ s|\\|/|g; $CGI::POST_MAX = 16 * 1024; print "Content-type: text/html\n\n"; print "\nPassword Generator\n". "\n". "\n"; $x = param('referer'); my $referer = param('referer'); #print "x=$x referer = $referer
\n"; my $referer = $ENV{'HTTP_REFERER'} if ($referer eq ""); # I experimented at considerable length to find seed logic that # was very random on Windows NT which generates a very small # universe of process ID numbers ($$) compared to Unix. srand(time() ^ ($$ + $$ << 21)); # USER CHANGEABLE CONSTANTS FOLLOW # Change $howMany to change the number of generated passwords. my $howMany = param('howMany'); $howMany = 10 unless $howMany; $howMany = 1000 if ($howMany > 1000); # Increase the default 8 to change the generated password size # and extra letters will be added to the end. Decrease and # you'll lose some or all of the second string of letters. # Depending on the value of $addConsonants the actual # password length may range from $siz to $siz + 2. # Size interacts with other choices. If $addConsonants is false # size will be fixed length and is achieved by truncation after # checking for upper case and digits so short sizes (3 - 5) may # not have the variability you desire. my $siz = param('siz'); $siz = 8 unless $siz; # A $siz less than 3 creates an endless loop. $siz = 3 if ($siz < 3); # Change $addConsonats to 0 to prevent some extra consonants # from being tacked on to letter sequences. Leave $addConsonants # at 1 to sometimes add an extra consonant to letter sequences. # If left at 1 the password size will vary from $siz to $siz+2. # Changing this to 2 will logically turn it into "addLetters" # as vowels will also be used and the results may be more # pronouncable as well as more varied. my $addConsonants = param('addConsonants'); $addConsonants = 1 unless ($addConsonants eq "0" or $addConsonants eq "2"); # Change $firstUpper to 0 to prevent the first character of each # letter sequence from being upper case. Leave it as 1 if you # want some of the first characters to be upper case. my $firstUpper = param('firstUpper'); $firstUpper = 1 unless ($firstUpper eq "0"); # Change $mixedCase to 1 to mix the case of all letters. # $mixedCase is not random as subsequent checks force at # least one upper and one lower case letter in each password. # Leave it at 0 so all letters will be lower case or only # the first or each letter sequence may be upper case. my $mixedCase = param('mixedCase'); $mixedCase = 0 unless ($mixedCase == 1); # By changing $symbolOdds from 0 to 10 you change the likelihood # of having two numbers or a number and a symbol. At 0 you will # always get 2 digits. At 1 you will usually only get one digit # but will sometimes get a second digit or a symbol. At 10 you # will always get two numbers or a number and a symbol with the # about even chances that one of the two characters will be a # symbol. The odds are affected by what characters are added to # or removed from the $sym initialization string. # The default is 7. my $symbolOdds = param('symbolOdds'); $symbolOdds = 7 if ($symbolOdds eq ""); $symbolOdds = 7 unless ($symbolOdds >= 0 and $symbolOdds <= 10); # Change $across to a 1 to print passwords across the screen. # Leave $across as a 0 to print a single column down the screen. my $across = param('across'); $across = 1 unless ($across eq "0"); # Add or remove symbols to make passwords easier or harder # to type. Delete the second set of digits to increase # the relative frequency of symbols and punctuation. # Add some vowels or consonants to really change the patterns # but these will also get much harder to remember. # The $ needs to be escaped if it is to be available as a # password character. $sym = ",.<>~`!@#\$%^&*()-_+="; $numb = "56781234901278903456" . $sym; $lnumb = length($numb); # USER CHANGEABLE CONSTANTS END - Changing the constants as # specified above has been fairly well tested. Any changes # below here and you are changing the logic of the program. # You should be familiar with programming if you make changes # after this point. my $letr; # Unless you plan to change the logic in the loop below, # leave this next alone and control case with $firstUpper and # $mixedCase above. $mixedCase supercedes if both are true. $upr = "BbCcDdFfGgHhJjKkLlMNPXYZmnpqrstvQRSTVWwxyz"; $cons = "bcdrstvwxyzfghjklmnpq"; if ($mixedCase) { $vowel = "aAeEiIoOuU"; $cons = $upr; $letr = $vowel . $upr; } else { $vowel = "uoiea"; $letr = $vowel . $cons; } $upr = $cons unless ($firstUpper); $lvowel = length($vowel); $lcons = length($cons); $lupr = length($upr); $lletr = length($letr); $realSize = $siz; $realSize += 2 if ($addConsonants); ($across) ? ($down = " ") : ($down = "\n"); $linelen = 0; my $tsiz = $siz; $tsiz -= 1 unless ($symbolOdds == 0 or $symbolOdds == 10); my $heading = "Structured Arbitrary Passwords"; print "

$heading

\n"; print "Home\n"; print "Back
\n";

$j = 0;
while (1) {
   $pass = "";
   $k = 0;
   for ($i=0; $i<$siz; $i++) {
      # The basic password structure is cvc99cvc.  Depending on
      # how $cons and $upr have been initialized above case will
      # be all lower, first upper or random.
      # As $i is incremented, the code is looking to put specific
      # types of characters at specifc locations.
      if ($i==0 or $i==2 or $i==5 or $i==7) {
         if ($i==0 or $i==5) {
            $pass .= substr($upr,int(rand($lupr)),1);
         } else {
            $pass .= substr($cons,int(rand($lcons)),1);
         }
         # The next will conditionally add up to 2 consonants or letters
         # pseudo randomly after the four "standard" consonants. The
         # $letr and $lletr variables were added with the option of a 2
         # in $addConsonants so that the full alphabet, rather than just
         # consonats could be randomly inserted after the 4 standard
         # consonat locations. This makes the passwords more diverse
         # and often more pronouncable. This is a simple exaple of the
         # password structure can be modified and extended without major
         # changes to the program logic. 

         # The $k test could be changed
         # to randomly allow one or two more letters at specific locations
         # or reduced to 1 to allow only 1 of the four possible letters to
         # to be added. 

         # With 5 lines of code it could be made a user
         # controllable variable via the web form. 
         # To do this, above where you see new variables being initialized
         # by the param function you should add three lines. The first
         # should be "my $newvar = param('newvar');". This will initialize
         # $newvar to the web supplied value. Second you should add a line
         # "$newvar = 2 unless $newvar;" This provides a default value
         # for the first time the form is displayed and if the user ever
         # puts a space in th form. Third you should add a line like
         # "$newvar = 2 if ($newvar gt "4" or $newvar lt "1");" This returns the 
         # returns the value to the default if the user enters an invalid 
         # value. 

         # Notice that even though we are comparing numbers I used a string
         # comparison. The only integers between 1 and 4 are 2 and 3. The
         # string test returns it to an integer if the user enters letters.
         # The downside is 22, 33, 2a, and 3zzz are all between "1" and "4"
         # as strings. You might follow with 
         # "$newvar = 2 if ($newvar > 4 or $newvar < 1);" This will get the
         # 22 and 33 but I'm not sure about the alphanumerics. I think
         # "$newvar = 2 if (length($newvar) > 1)"
         # in conjunction with the string test will handle all cases. How
         # far you want to go with this depends on who beside you will be
         # using your password generator and how strongly you feel about
         # controlling them.

         # Strictly speaking none of this is necessary. In the first test
         # below "if $addConsonants" anything but 0 is true and if it is
         # not 2, the program will simply revert to the default behavior
         # of adding consonants. It's just a matter of how you feel about
         # will it work or break versus appearances. I'm somewhat of both
         # a perfectionist and purist. I tend to want control and want
         # to keep data entry values strictly controlled to appropriate
         # values, even if I know invalid values work. This is about how
         # you, and maybe your boss feel about such things.

         # There is no need to use 2 as the default. Anything from 
         # 1 to 4 makes sense. Personally I would only use 2 or 3, but
         # that is purely my personal preferance on the structure variability
         # of passwords. If it makes sense to you, 3 or 4 would be good
         # if you prefer a looser more random structure or 1 if you liked
         # to stay closer to a firm or nearly constant structure because
         # it is easier to remember.

         # Anything that makes sense to you and is different than what
         # I have done is good. There is no right or wrong here. If
         # you ever let it slip that you are using a generator derived from
         # mine, the more changes you have made, the less anyone knows
         # about your password structure. If you like firm structure
         # I would never publicly anounce that and surely never reveal
         # what that structure might be.

         # To put it in the web form, below you need to add something like:
         # ""
         # "Maximum number of letters to add after consonants (1 - 4)
" # There is no need to keep the webform name and variable the same. # I do it simply because it makes it a lot easier to remember # and keep striaght what goes with what. if ($addConsonants and (int(rand(4)) == 3) and $k < 2) { if ($addConsonants == 2) { $pass .= substr($letr,int(rand($lletr)),1); } else { $pass .= substr($cons,int(rand($lcons)),1); } $k++; } } # Pad the password with letters if $siz is over 7. if ($i > 7) { if (int(rand(26)) <= 5) { $pass .= substr($vowel,int(rand($lvowel)),1); } else { $pass .= substr($cons,int(rand($lcons)),1); } } # Put the vowels in cvc99cvc. Case depends on how $vowel # was initialized above. $pass .= substr($vowel,int(rand($lvowel)),1) if ($i==1 or $i==6); # Change $symbolOdds initialization above to affect the # number of numbers and symbols and their ratio. if ($i==3 or $i==4) { # If $symbolOdds is non zero take any character # from the $numb string which has digits, symbols # and punctuation. if ($symbolOdds) { $pass .= substr($numb,int(rand($lnumb)),1) if (int(rand(10)) <= $symbolOdds); } else { # If $symbolOdds is zero keep trying until a # a digit is found. $n = ""; until ($n =~ /[0-9]/) { $n = substr($numb,int(rand($lnumb)),1); } $pass .= $n; } } } # Check the password length. $pass = substr($pass,0,$realSize) if (length($pass) > $realSize); # Plan to use this password unless . . . $skipThisOne = 0; # Include at least one digit. $skipThisOne = 1 unless ($pass =~ /[0-9]/); # Include at least one lower case letter. $skipThisOne = 1 unless ($pass =~ /[a-z]/); # Conditionally insure at least one upper case character. $skipThisOne = 1 if (!($pass =~ /[A-Z]/) and ($firstUpper or $mixedCase)); # If any test fails get another password. if ($skipThisOne) { next; } # Print the passwords in a single column or across # the screen based on $down which is set based on the # the value of $across. if ($down ne "\n") { # Don't wrap passwords or trailing whitespace. if ($linelen + length($pass) + length($down) > 64) { print "\n"; $linelen = 0; } $linelen += length($pass) + length($down); } $pass =~ s//>/g; print "$pass\t$down"; $j++; last if ($j >= $howMany); } # Be sure to end the last line with an end of line. print "\n" if $down ne "\n"; print "
\n"; print < Use Refresh/Reload to see more. Don\'t use these passwords. Instead, view the free open source original command line version, or this web version; then save it to disk and run it locally or install this in your website. It requires Perl 5. Other patterns: State Department style, better, still better, strong, stronger, hard, easy. There are over one million "easy" passwords. Change options below to vary the difficulty of the displayed passwords.


How many passwords?
Base password length (may vary by -1 to +2)?
Add extra consonants? (0, 1, or 2)
First letter upper case? (0 or 1)
Mixed case? (0 or 1)
Symbol Odds? (0 to 10)
Display accross? (0 or 1)
EOT print "



Copyright © 2000 - 2012 George Shaffer. Created by George Shaffer (GeodSoft.com)
\n". "This version may be modified in accordence with the license agreement
in the comments at the top of the source code.

\n"; print "\n\n";