Password generator

From Rosetta Code
Revision as of 18:11, 5 June 2016 by rosettacode>Gerard Schildberger (→‎{{header|REXX}}: fixed an error found by Walter Pachl (one generated PW didn't have an uppercase letter).)
Password generator is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.
File:Passwords.jpg

Create a password generation program which will generate passwords containing random ASCII characters from the following groups:

  • lower-case letters: a-z
  • upper-case letters: A-Z
  • digits: 0-9
  • other printable characters excluding white-space, backslash and grave:  !"#$%&'()*+,-./:;<=>?@[]^_{|}~


The generated password(s) must include at least one lower-case letter, one upper-case letter and one character from digits and 'other'.

The user must be able to specify the password length and the number of passwords to generate.

The passwords should be displayed or written to a file, one per line.

The randomness should be from a system source or library.

The program should implement a help option or button which should describe the program and options when invoked.

You may also allow the user to specify a seed value, and give the option of excluding visually similar characters.

For example:         Il1     O0     5S     2Z         where the characters are:

  •   capital eye, lowercase ell, the digit one
  •   capital oh, the digit zero
  •   the digit five, capital ess
  •   the digit two, capital zee



C++

<lang cpp>

  1. include <iostream>
  2. include <string>
  3. include <algorithm>
  4. include <ctime>

const std::string CHR[] = { "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz",

                           "0123456789", "!\"#$%&'()*+,-./:;<=>?@[]^_{|}~" };

const std::string UNS = "O0l1I5S2Z";

std::string createPW( int len, bool safe ) {

   std::string pw;
   char t;
   for( int x = 0; x < len; x += 4 ) {
       for( int y = x; y < x + 4 && y < len; y++ ) {
           do {
               t = CHR[y % 4].at( rand() % CHR[y % 4].size() );
           } while( safe && UNS.find( t ) != UNS.npos );
           pw.append( 1, t );
       }
   }
   std::random_shuffle( pw.begin(), pw.end() );
   return pw;

} void generate( int len, int count, bool safe ) {

   for( int c = 0; c < count; c++ ) {
       std::cout << createPW( len, safe ) << "\n";
   }
   std::cout << "\n\n";

} int main( int argc, char* argv[] ){

   if( argv[1][1] == '?' || argc < 5 ) {
       std::cout << "Syntax: PWGEN length count safe seed /?\n"
                    "length:\tthe length of the password(min 4)\n"
                    "count:\thow many passwords should be generated\n"
                    "safe:\t1 will exclud visually similar characters, 0 won't\n"
                    "seed:\tnumber to seed the random generator or 0\n"
                    "/?\tthis text\n\n";
   } else {
       int l = atoi( argv[1] ),
           c = atoi( argv[2] ),
           s = atoi( argv[4] );
       if( s == 0 ) s = int( std::time( 0 ) );
       if( l < 4 ) {
           std::cout << "Passwords must be at least 4 characters long.\n\n";
       } else {
           std::srand( unsigned( s ) );
           generate( l, c, argv[3][0] == '1' );
       }
   }
   return 0;

} </lang>

Output:
PWGEN 6 6 1 8
Rd3&Mm
$X6ttJ
fx7D,K
9sHh$L
d~U3Nn
9AE?ab

J

Implementation:

<lang J>thru=: <. + i.@(+*)@-~ chr=: a.&i.

lower=: 'a' thru&.chr 'z' upper=: 'A' thru&.chr 'Z' digit=: '0' thru&.chr '9' other=: ('!' thru&.chr '~')-.lower,upper,digit,'`\' all=: lower,upper,digit,other

pwgen =:verb define"0 :: pwhelp

 NB. pick one of each, remainder from all, then shuffle
 (?~y) { (?@# { ])every lower;upper;digit;other;(y-4)#<all
 pwgen x#y

)

pwhelp =:echo bind (noun define)

 [x] pwgen y - generates passwords of length y
 optional x says how many to generate (if you want more than 1)
 y must be at least 4 because 
 passwords must contain four different kinds of characters.

)</lang>

Example use (from J command line):

<lang J> pwgen'help'

 [x] pwgen y - generates passwords of length y
 optional x says how many to generate (if you want more than 1)
 y must be at least 4 because 
 passwords must contain four different kinds of characters.
  pwgen 10

S%"x8X0}:K

  5 pwgen 10

+u.9(XDM.O ]a@Yb6j~DI Oo?|2oc4yi 9V9[EJ:Txs $vYd(>4L:m</lang>

Java

Works with: Java version 7

<lang java>import java.util.*;

public class PasswordGenerator {

   final static Random rand = new Random();
   public static void main(String[] args) {
       int num, len;
       try {
           if (args.length != 2)
               throw new IllegalArgumentException();
           len = Integer.parseInt(args[0]);
           if (len < 4 || len > 16)
               throw new IllegalArgumentException();
           num = Integer.parseInt(args[1]);
           if (num < 1 || num > 10)
               throw new IllegalArgumentException();
           for (String pw : generatePasswords(num, len))
               System.out.println(pw);
       } catch (IllegalArgumentException e) {
           String s = "Provide the length of the passwords (min 4, max 16) you "
                   + "want to generate,\nand how many (min 1, max 10)";
           System.out.println(s);
       }
   }
   private static List<String> generatePasswords(int num, int len) {
       final String s = "!\"#$%&'()*+,-./:;<=>?@[]^_{|}~";
       List<String> result = new ArrayList<>();
       for (int i = 0; i < num; i++) {
           StringBuilder sb = new StringBuilder();
           sb.append(s.charAt(rand.nextInt(s.length())));
           sb.append((char) (rand.nextInt(10) + '0'));
           sb.append((char) (rand.nextInt(26) + 'a'));
           sb.append((char) (rand.nextInt(26) + 'A'));
           for (int j = 4; j < len; j++) {
               int r = rand.nextInt(93) + '!';
               if (r == 92 || r == 96) {
                   j--;
               } else {
                   sb.append((char) r);
               }
           }
           result.add(shuffle(sb));
       }
       return result;
   }
   public static String shuffle(StringBuilder sb) {
       int len = sb.length();
       for (int i = len - 1; i > 0; i--) {
           int r = rand.nextInt(i);
           char tmp = sb.charAt(i);
           sb.setCharAt(i, sb.charAt(r));
           sb.setCharAt(r, tmp);
       }
       return sb.toString();
   }

}</lang>

7V;m
4u>I
J2$s
I8~h

2Q4,dD.Y'z>8
<|U)qS7S9d_M
c4*hLisq^e26
'H$1e)BpYh*8
*'R8yEmvh9v]

ooRexx

Translation of: [[#|]] – REXX

<lang oorexx>/*REXX program generates a random password according to the Rosetta Code task's rules.*/ casl= 'abcdefghijklmnopqrstuvwxyz' /*define lowercase alphabet. */ casu= translate(casle) /*define uppercase alphabet. */ digs= '0123456789' /*define digits. */ /* avoiding the ambiguous characters Il1 o0 5S */ casl= 'abcdefghijkmnpqrtuvwxy' /*define lowercase alphabet. */ casu= translate(casl)||'L' /*define uppercase alphabet. */ casl=casl||'oz' /* lowercase o,z are ok */ digs= '346789' /*define digits. */ Say 'casl='casl Say 'casu='casu Say 'digs='digs Say 'spec='spec spec= !"#$%&()+,-./:;<=>?@[]^{|}~' /*define a bunch of special characters.*/ parse arg L N seed dbg /*obtain optional arguments from the CL*/ if L=='?' then call help /*does user want documentation shown? */ if L== | L=="," then L=8 /*Not specified? Then use the default.*/ If N== | N=="," then N=1 /* " " " " " " */ if seed> &,

  seed<>','            then Do
 if \datatype(seed,'W')then call ser "seed is not an integer:" seed
 Call random ,,seed                             /*the seed for repeatable RANDOM BIF #s*/
 End

if \datatype(L, 'W') then call ser "password length, it isn't an integer: " L if L<4 then call ser "password length, it's too small: " L if L>80 then call ser "password length, it's too large: " L if \datatype(N, 'W') then call ser "number of passwords, it isn't an integer: " N if N<0 then call ser "number of passwords, it's too small: " N

   do g=1  for N                                /*generate  N  passwords (default is 1)*/
   pw=letterL()||letterU()||numeral()||special()/*generate  4  random  PW constituents.*/
           do k=5  to  L;       z=random(1, 4)  /* [?]  flush out PW with more parts.  */
           if z==1  then pw=pw || letterL()     /*maybe append random lowercase letter.*/
           if z==2  then pw=pw || letterU()     /*  "      "      "   uppercase    "   */
           if z==3  then pw=pw || numeral()     /*  "      "      "       numeral      */
           if z==4  then pw=pw || special()     /*  "      "      "   special character*/
           end   /*k*/                          /* [?]  code below randomizes PW chars.*/
   t=length(pw)                                 /*the length of the password (in bytes)*/
           do L+L                               /*perform a random number of char swaps*/
           a=random(1,t);     x=substr(pw,a,1)  /*A: 1st char location;  X is the char.*/
           b=random(1,t);     y=substr(pw,b,1)  /*B: 2nd   "      "      Y  "  "    "  */
           pw=overlay(x,pw,b);  pw=overlay(y,pw,a)  /* swap the two chars.             */
           end  /*swaps*/                       /* [?]  perform extra swap to be sure. */
   say right(g,length(N))  'password is: ' pw counts() /*display the  Nth  password    */
   end       /*g*/

exit /*stick a fork in it, we're all done. */ /*--------------------------------------------------------------------------------------*/ ser: say; say '***error*** invalid' arg(1); exit 13 /*display an error message*/ letterL: return substr(casl, random(1, length(casl)), 1) /*return random lowercase.*/ letterU: return substr(casu, random(1, length(casu)), 1) /* " " uppercase.*/ numeral: return substr(digs, random(1, length(digs)), 1) /* " " numeral. */ special: return substr(spec, random(1, length(spec)), 1) /* " " special char*/ /*--------------------------------------------------------------------------------------*/ counts:

 If dbg> Then Do
   cnt.=0
   str.=
   Do j=1 To length(pw)
     c=substr(pw,j,1)
     If pos(c,casL)>0 Then Do; cnt.0casL=cnt.0casL+1; str.0casL=str.0casL||c; End
     If pos(c,casU)>0 Then Do; cnt.0casU=cnt.0casU+1; str.0casU=str.0casU||c; End
     If pos(c,digs)>0 Then Do; cnt.0digs=cnt.0digs+1; str.0digs=str.0digs||c; End
     If pos(c,spec)>0 Then Do; cnt.0spec=cnt.0spec+1; str.0spec=str.0spec||c; End
     End
   txt=cnt.0casL cnt.0casU cnt.0digs cnt.0spec,
       str.0casL str.0casU str.0digs str.0spec
   If pos('0',txt)>0 Then
     txt=txt 'error'
   End
 Else
   txt=
 Return txt

help: signal .; .: do j=sigL+2 to sourceline()-1; say sourceline(j); end; exit 0 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~ documentation begins on next line.~~~~~~~~~~~~~~~~~~~~~~~~~ +-----------------------------------------------------------------------------+ ¦ Documentation for the GENPW program: ¦ ¦ ¦ ¦ Rexx genpwd <length|,> <howmany|,> <seed|,> dbg ¦ ¦ 8 1 none none Defaults ¦ ¦ ¦ ¦--- where: ¦ ¦ length is the length of the passwords to be generated. ¦ ¦ The default is 8. ¦ ¦ If a comma (,) is specified, the default is used. ¦ ¦ The minimum is 4, the maximum is 80. ¦ ¦ ¦ ¦ howMany is the number of passwords to be generated. ¦ ¦ The default is 1. ¦ +-----------------------------------------------------------------------------+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~documentation ends on the previous line.~~~~~~~~~~~~~~~~~~~*/</lang>

Output:
D:\>D:\>rexx genpwd 12 4 33 .
casl=abcdefghijkmnpqrtuvwxyoz
casu=ABCDEFGHIJKMNPQRTUVWXYL
digs=346789
spec=SPEC
1 password is:  A78<3(3Pg(Q8 1 3 5 3 g APQ 78338 <((
2 password is:  v+k6I6WH^;[7 2 3 3 4 vk IWH 667 +^;[
3 password is:  g3JD:7?HxK{X 2 5 2 3 gx JDHKX 37 :?{
4 password is:  L49NAQj[~W9/ 1 5 3 3 j LNAQW 499 [~/

D:\>regina genpwd 12 4 33 x
casl=abcdefghijkmnpqrtuvwxyoz
casu=ABCDEFGHIJKMNPQRTUVWXYL
digs=346789
spec=SPEC
1 password is:  c4)CgJ4mx"vm 6 2 2 2 cgmxvm CJ 44 )"
2 password is:  9y3Y:a97DyM8 3 3 5 1 yay YDM 93978 :
3 password is:  p76-^j^H{8WD 2 3 3 4 pj HWD 768 -^^{
4 password is:  T$G@kHFEyi8c 4 5 1 2 kyic TGHFE 8 $@   

Perl 6

Works with: Rakudo version 2016.05

<lang perl6>my @chars =

 set('a' .. 'z'),
 set('A' .. 'Z'),
 set('0' .. '9'),
 set(<!"#$%&'()*+,-./:;<=>?@[]^_{|}~>.comb);
  1. bleh. unconfuse syntax highlighter. '"

sub MAIN ( Int :$l = 8, Int :$c = 1, Str :$x = ) {

   note 'Password length must be >= 4' and exit if $l < 4;
   note 'Can not generate fewer than 0 passwords' and exit if $c < 0;
   my $chars = [∪] @chars».=&filter;
   note 'Can not exclude an entire required character group' and exit
     if any(@chars».elems) == 0;
   for ^$c {
       my @pswd;
       @pswd.push( @chars[$_].roll ) for ^4;
       @pswd.push( $chars    .roll ) for 4 ..^ $l;
       say [~] @pswd.pick(*);
   }
   sub filter (Set $set) { $set ∖ set($x.comb) }

}

sub USAGE() {

   say qq:to/END/;
   Specify a length:              --l=8 (default 8),
   Specify a count:               --c=1 (default 1),
   Specify characters to exclude: --x=
   (must escape characters significant to the shell)
   E.G.
   {$*PROGRAM-NAME} --l=14 --c=5 --x=0O\\\"\\\'1l\\\|I
   END

}</lang> Sample output: Using defaults:

c?6!xU+u

With passed parameters: --l=14 --c=5 --x=0O\'\"1l\|I

6H~jC+5(+&H44x
+Rr}2>htHMa.Y9
t~#&N]sp_zGK2#
TcP73CJ@euFMjj
9%-tYX]z?8-xA5

REXX

This REXX code was written as generically and idiomatically as possible so that the   special characters   and  
the lowercase and uppercase alphabets may be extended with letters from other alphabets. <lang rexx>/*REXX program generates a random password according to the Rosetta Code task's rules.*/ @abcL= 'abcdefghijklmnopqrstuvwxyz' /*define a lowercase (Latin) alphabet. */ @abcU= @abcL; upper @abcU /* " " uppercase " " */ @digs= 0123456789 /* " " string of base ten numerals.*/ @spec= '!"#$%&()+,-./:;<=>?@[]^{|}~' || "'" /*define a bunch of special characters.*/ parse arg L N seed . /*obtain optional arguments from the CL*/ if L=='?' then call help /*does user want documentation shown? */ if L== | L=="," then L=8 /*Not specified? Then use the default.*/ if N== | N=="," then N=1 /* " " " " " " */ if datatype(seed,'W') then call random ,,seed /*the seed for repeatable RANDOM BIF #s*/ if \datatype(L, 'W') then call serr "password length, it isn't an integer: " L if L<4 then call serr "password length, it's too small: " L if L>80 then call serr "password length, it's too large: " L if \datatype(N, 'W') then call serr "number of passwords, it isn't an integer: " N if N<0 then call serr "number of passwords, it's too small: " N

   do g=1  for N                                /*generate N passwords (default is one)*/
   $=letL() || letU() || digs() || spec()       /*generate 4 constituent parts of a PW.*/
       do k=5  to  L;       z=random(1, 4)      /* [↓]  flush out PW with more parts.  */
       if z==1  then $=$ || letL()              /*maybe append random lowercase letter.*/
       if z==2  then $=$ || letU()              /*  "      "      "   uppercase    "   */
       if z==3  then $=$ || digs()              /*  "      "      "       numeral      */
       if z==4  then $=$ || spec()              /*  "      "      "   special character*/
       end   /*k*/
                                                /* [↑]  scrambles PW, hides gen order. */
       do L+L;  a=random(1,L); x=substr($,a,1)  /*A: 1st char location;  X is the char.*/
                b=random(1,L); y=substr($,b,1)  /*B: 2nd   "      "      Y  "  "    "  */
       $=overlay(x,$,b);       $=overlay(y,$,a) /*(both statements) swap the two chars.*/
       end  /*swaps*/                           /* [↑]  more swaps obfuscates gen order*/
   say right(g, length(N))  'password is: '  $  /*display the  Nth  password to console*/
   end      /*g*/

exit /*stick a fork in it, we're all done. */ /*──────────────────────────────────────────────────────────────────────────────────────*/ serr: say; say '***error*** invalid' arg(1); exit 13 /*display an error message*/ letL: return substr(@abcL, random(1, length(@abcL) ), 1) /*return random lowercase.*/ letU: return substr(@abcU, random(1, length(@abcU) ), 1) /* " " uppercase.*/ digs: return substr(@digs, random(1, length(@digs) ), 1) /* " " numeral. */ spec: return substr(@spec, random(1, length(@spec) ), 1) /* " " special char*/ /*──────────────────────────────────────────────────────────────────────────────────────*/ help: signal .; .: do j=sigL+2 to sourceline()-1; say sourceline(j); end; exit /*~~~~~~~~~~~~~~~~~~~~~~~~~~ documentation starts on next line.~~~~~~~~~~~~~~~~~~~~~~~~~~~ ╔═════════════════════════════════════════════════════════════════════════════╗ ║ Documentation for the GENPW program. The format of the command is: ║ ║ ║ ║ GENPW  ? ◄─── shows this documentation. ║ ║ GENPW ◄─── generates 1 password (with length 8). ║ ║ GENPW length ◄─── generates (all) passwords with this length. ║ ║ GENPW length howMany ◄─── generates howMany passwords. ║ ║ ║ ╟──────────── where (if a comma (,) is specified, the default is used): ║ ║ ║ ║ length is the length of the passwords to be generated. The default is 8. ║ ║ The minimum is 4, the maximum is 80. ║ ║ ║ ║ howMany is the number of passwords to be generated. The default is 1. ║ ╚═════════════════════════════════════════════════════════════════════════════╝ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ documentation ends on the previous line.~~~~~~~~~~~~~~~~~~~*/</lang> output   when using the inputs of:   10   20

 1 password is:  EQG@~?~a[6
 2 password is:  2J:"Cm|ol<
 3 password is:  Io32~fa[?L
 4 password is:  r)@;2;PsZE
 5 password is:  }#H"7Rk?4{
 6 password is:  cHB8Q!%@PI
 7 password is:  a7m75132.S
 8 password is:  Qcg4>FeNb7
 9 password is:  52vHl'+HIP
10 password is:  139Vk]aIL-
11 password is:  f1g;}b/W1W
12 password is:  lC74rIv5s<
13 password is:  O9$8g1}er4
14 password is:  c|E?Uh4c8~
15 password is:  :39OK2E8u#
16 password is:  vw=+uI-X+3
17 password is:  8YhJ)BS>>~
18 password is:  Vd]ZCUw%<0
19 password is:  6([b4Qk;O7
20 password is:  5ST.}:1t@O

Scala

Using SBT to run rather than a shell script or executable jar: <lang scala>object makepwd extends App {

 def newPassword( salt:String = "", length:Int = 13, strong:Boolean = true ) = {
   val saltHash = salt.hashCode & ~(1 << 31)
   
   import java.util.Calendar._
   val cal = java.util.Calendar.getInstance()
   val rand = new scala.util.Random((cal.getTimeInMillis+saltHash).toLong)
   val lower = ('a' to 'z').mkString
   val upper = ('A' to 'Z').mkString
   val nums = ('0' to '9').mkString
   val strongs = "!\"#$%&'()*+,-./:;<=>?@[]^_{|}~"
   val unwanted = if( strong ) "" else "0Ol"
   
   val pool = (lower + upper + nums + (if( strong ) strongs else "")).
              filterNot( c => unwanted.contains(c) )
   
   val pwd = for( n <- 1 to length; c = pool(rand.nextInt(pool.length) ) ) yield c
   pwd.mkString
 }
 val pwdLength = """^(\d{1,4})$""".r
 val howMany = """^\-n(\d{0,3})$""".r
 val help = """^\-\-(help)$""".r
 val pwdSalt = """^\-s(.*)""".r
 val strongOption = """(?i)(strong)""".r
 
 
 var (salt,length,strong,helpWanted,count,unknown) = ("",13,false,false,1,false)
 
 args.foreach{ 
   case pwdLength(l) =>    length = math.min(math.max(l.toInt,6),4000)
   case strongOption(s) => strong = true
   case pwdSalt(s) =>      salt = s
   case howMany(c) =>      count = math.min(c.toInt,100)
   case help(h) =>         helpWanted = true
   case _ =>               unknown = true
 }
 if( count > 1 ) println
 
 if( helpWanted || unknown ) {
   println( """
 makepwd <length> "strong" -s<salt> -n<how-many> --help
   <length>     = how long should the password be
   "strong"     = strong password, omit if special characters not wanted
   -s<salt>     = "-s" followed by any non-blank characters 
                    (increases password randomness)
   -n<how-many> = "-n" followed by the number of passwords wanted
   --help       = displays this
   
 For example: makepwd 13 strong -n20 -sABCDEFG

""".stripMargin )

 }
 else for( i <- 1 to count ) println( newPassword( i + salt, length, strong ) )
 if( count > 1 ) println  

}</lang>

Output:

> sbt "run --help"

...

  makepwd <length> "strong" -s<salt> -n<how-many> --help

    <length>     = how long should the password be
    "strong"     = strong password, omit if special characters not wanted
    -s<salt>     = "-s" followed by any non-blank characters
                     (increases password randomness)
    -n<how-many> = "-n" followed by the number of passwords wanted
    --help       = displays this

  For example: makepwd 13 strong -n20 -sABCDEFG

> sbt "run 13 strong -sMySecret -n3"

...

}mR46_*cOq&v0
Ab~A!ddH8%JPd
z{A.m+$cqy#9I

zkl

Put the following code into a file (such as pwdg.zkl): <lang zkl>var pwdLen=10, pwds=1, xclude="";

argh:=Utils.Argh( L("+xclude","","Don't use these characters",fcn(arg){ xclude=arg }), L("+len","","Number of characters in password", fcn(arg){ pwdLen=arg.toInt() } ), L("+num","","Number of passwords to generate", fcn(arg){ pwds=arg.toInt() } ), ); try{ argh.parse(vm.arglist) }catch{ System.exit(1) }

g1,g2,g3:=["a".."z"].walk(), ["A".."Z"].walk(), ["0".."9"].walk(); g4:="!\"#$%&'()*+,-./:;<=>?@[]^_{|}~".split(""); all:=(g1.extend(g2,g3,g4).concat() - xclude).split("");

do(pwds){

  pwd:=(T(g1,g2,g3,g4).pump(String,"shuffle",T("get",0)) - xclude).split("");
  println(pwd.extend(all.shuffle()[0,pwdLen - pwd.len()]).shuffle().concat());

}</lang> This is a command line program so output can be redirected.

Output:
$ zkl pwdg.zkl -?
Unknown option: ?
Options:
  --len <arg>: Number of characters in password
  --num <arg>: Number of passwords to generate
  --xclude <arg>: Don't use these characters

$ zkl pwdg.zkl --len 20 --xclude "012345678" --num 5
O?a~siD&lK&<vL]od$|(
yhHVH})#S'F@<~%,Tb-L
?pOq"fmHAPtxgWM$n~u9
h&"#!bZGCm)dXt]$*)/p
x]*x>qH/Wde;asRzhZ.)