Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer
 
PerlMonks  

Handling elements of an array of array references, where the function calls for an array?

by desertrat (Acolyte)
on May 14, 2012 at 20:20 UTC ( #970506=perlquestion: print w/ replies, xml ) Need Help??
desertrat has asked for the wisdom of the Perl Monks concerning the following question:

I don't even know if I'm on the right track here, but...

I have an account creation system that autogenerates passwords. We have recently changed policies so that a password needs to include at least one each from upper and lower case letters, numbers and non-numeric characters.

Just randomly generating passwords from that whole set is simple, but making sure at least one of each set is included and their inclusion is random in the password ie just selecting random pairs form each in line aaBB22%% is not acceptable.

So my strategy will be to shuffle an array of references then use the following scheme to build pieces of the password:

join("", @array[map {rand @array}(1..n)]);

but when I try to plug in array references, I get just one character from each array:

#!/usr/bin/perl use strict; use warnings; my @chars = ( "a" .. "k", "m","n", "p" .. "z"); my @upchars = ("A".."N","P..Z"); my @nona = qw(! @ $ % & ( ) + = { } [ ] < > ~); my @nums = (0 .. 9); my $lc = \@chars; my $uc = \@upchars; my $na = \@nona; my $nu = \@nums; my @types=($lc, $uc, $na, $nu); my $a = join("", $types[3][ map { rand \$types[3] } ( 1 .. 4 ) ]); $a .= join("", $types[2][ map { rand \$types[2] } ( 1 .. 4 ) ]); $a .= join("", $types[1][ map { rand \$types[1] } ( 1 .. 4 ) ]); $a .= join("", $types[0][ map { rand \$types[0] } ( 1 .. 4 ) ]); print "My selection is $a \n"; exit;

When I run that I get the result '4&Ee', if I reverse the array indexes thus:

my $a = join("", $types[0][ map { rand \$types[0] } ( 1 .. 4 ) ]); $a .= join("", $types[1][ map { rand \$types[1] } ( 1 .. 4 ) ]); $a .= join("", $types[2][ map { rand \$types[2] } ( 1 .. 4 ) ]); $a .= join("", $types[3][ map { rand \$types[3] } ( 1 .. 4 ) ]);

I get the result 'eE&4' so I know I'm pulling one character from the arrays.

It makes no difference if the map part is map { rand \$types[1] } ( 1 .. 4 ) or map { rand $types[1] } ( 1 .. 4 )

What am I doing wrong and am I even on the right track?

Comment on Handling elements of an array of array references, where the function calls for an array?
Select or Download Code
Re: Handling elements of an array of array references, where the function calls for an array?
by Anonymous Monk on May 14, 2012 at 20:42 UTC
Re: Handling elements of an array of array references, where the function calls for an array?
by mbethke (Hermit) on May 14, 2012 at 20:47 UTC

    Hi desertrat,
    before I spend time on working out how to do it, I'd rather spend a few minutes persuading you that it's not a good idea. I know, oftentimes it's come corporate-level decision that you just have to implement, but if you have any say in it: don't. It's fine to require some classes of characters to keep people from using all too simple variations of distionary words, but

    1. four is excessive, i.e. it already limits the number of allowable passwords too much, and
    2. imposing rules on supposedly "random" distribution limits them even more, making brute-force cracking actually easier.
    I'd suggest using Crypt::Cracklib or somesuch password checker on generated results and retrying if it finds the result all too easily crackable.

      Sadly it's a corporate-level decision being made by an entirely different corporation (We have a group contracted to do work for outside companies), so I have to do it this way. I know this is a PITA and just results in passwords being taped to the front of monitors, let alone under the keyboards, but a code monkey's gotta do what a code monkey's gotta do sometimes.

      Had I any say I would offer Randall Munroe's excellent treatise on the matter as the help file.

Re: Handling elements of an array of array references, where the function calls for an array?
by Perlbotics (Abbot) on May 14, 2012 at 22:03 UTC

    AM gave you a valuable hint about shuffling and I second mbethke's suggestion on rather testing the user provided password with an appropriate password checker.

    Despite the security issues, here's a hint to you Perl problem using your original code with some annotations and corrections:

    #!/usr/bin/perl use strict; use warnings; my @chars = ( "a" .. "k", "m","n", "p" .. "z"); my @upchars = ("A".."N","P".."Z"); # <-- NOTE: "P..Z" -> "P".."Z" my @nona = qw(! @ $ % & ( ) + = { } [ ] < > ~); my @nums = (0 .. 9); my $lc = \@chars; my $uc = \@upchars; my $na = \@nona; my $nu = \@nums; my @types = ($lc, $uc, $na, $nu); # compact: my @types = (\@chars +, \@upchars, ... my $a = join("", @{$types[3]}[ map { rand @{$types[3]} } ( 1 .. 4 ) ] +); $a .= join("", @{$types[2]}[ map { rand @{$types[2]} } ( 1 .. 4 ) ] +); $a .= join("", @{$types[1]}[ map { rand @{$types[1]} } ( 1 .. 4 ) ] +); $a .= join("", @{$types[0]}[ map { rand @{$types[0]} } ( 1 .. 4 ) ] +); # ^--deref/arrayslice ^-- deref for number of el +ements # nobody complained about using '$a' yet, so I don't do either ;-) print "My selection is $a \n"; # you should shuffle and shorten the result exit; __DATA__ My selection is 0548)~}+QRVKzkkt My selection is 7922&)(&AAZZwiaw My selection is 5263+!@>VXXBaezt

    Enforcing someone to use e.g. 0548)~}+QRVKzkkt as a password just increases post-it sales... ;-)

Re: Handling elements of an array of array references, where the function calls for an array?
by johngg (Abbot) on May 14, 2012 at 22:56 UTC

    Silly idea but, as you say, the decision is out of you hands :-(

    Set up the four sets already shuffled then make an array of references to them. Then create another array with a guaranteed one from each set (0 .. 3) then a random selection of four more from any set. Then shuffle the order. Finally, build the password by concatenating random letters from the sets in the already shuffled order.

    knoppix@Microknoppix:~$ perl -MList::Util=shuffle -Mstrict -wE ' my @lc = shuffle q{a} .. q{z}; my @uc = shuffle q{A} .. q{Z}; my @dig = shuffle q{0} .. q{9}; my @oth = shuffle qw{ ! " $ % ^ & * _ - + = @ ~ }; my @all = \ ( @lc, @uc, @dig, @oth ); my @order = ( 0 .. 3 ); push @order, int rand 4 for 1 .. 4; @order = shuffle @order; my $passwd = q{}; $passwd .= $all[ $_ ]->[ rand @{ $all[ $_ ] } ] for @order; say $passwd;' E5yR=$% knoppix@Microknoppix:~$

    Runs of this have produced &D0%@nn%, F9@F=Rc1, S~j@3WgG, 633Pm!T and o8S4pl!! so, as long as there is no stipulation barring repeated characters, this seems to work.

    I hope this is useful.

    Update: If repeated characters are not wanted then you can use splice to remove the chosen character from the array so it can't be re-used.

    ... $passwd .= splice @{ $all[ $_ ] }, rand @{ $all[ $_ ] }, 1 for @order; ...

    Cheers,

    JohnGG

      I think I see the way now, thank you to all for your help. I realize now that I completely neglected to mention that this was not the intended end function, but a test just to make sure I was getting some of all of them!

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://970506]
Approved by Eliya
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others cooling their heels in the Monastery: (9)
As of 2014-09-20 09:31 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    How do you remember the number of days in each month?











    Results (158 votes), past polls