#!/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 "\n
\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)\n"; print <
" # 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; $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 "