Take the 2-minute tour ×
Programming Puzzles & Code Golf Stack Exchange is a question and answer site for programming puzzle enthusiasts and code golfers. It's 100% free, no registration required.

The domain server requires that all employees have a strong, random password conforming to the following rules:

  • Exactly 15 characters long.
  • Keyboard-typeable characters only (as shown in code-type below). Teaching the sales to use ALT+NUMPAD codes is not permitted.
  • At least 1 lower case letter: abcdefghijklmnopqrstuvwxyz
  • At least 1 upper case letter: ABCDEFGHIJKLMNOPQRSTUVWXYZ
  • At least 1 numeric digit: 0123456789
  • At least 1 symbol: `~!@#$%^&*()_+-={}|[]\:";'<>?,./

For this purpose IT have commissioned and will be distributing a Random Password Generator to all employees. All employees will be required to use the Random Password Generator. The requirements for the Random Password Generator are, in addition to the password restrictions above:

  • It must be able to generate all permutations of all allowable characters.
  • It must display the generated password on the screen.
  • The code is required to be as small as possible (in bytes).

Please submit your proposed solution within the next week.

share|improve this question
4  
You should also demand that all passwords which are allowed appear with the same probability (otherwise I can simply make a 30 characters long list with allowed characters, shuffle it, and give the first 15 ones) –  moose 5 hours ago
 
@moose, agreed. I've added a new rule. –  Hand-E-Food 5 hours ago
add comment

12 Answers

Mathematica (18)

Let me a little cheat

= 15char ASCII pwd
&(^F7yP8k:*1P<t

P.S. not safety :)

share|improve this answer
 
Where's the code? –  David Carraher 4 hours ago
2  
Is that guaranteed to satisfy the at least one of each character class requirement? –  Hand-E-Food 4 hours ago
 
@DavidCarraher when you print "=" then the following will be converted to WolframAlpha expression. It is roughly the same as WolframAlpha["ASCII password 15 char"]. –  ybeltukov 4 hours ago
 
@Hand-E-Food Yes, it is! If you look at the interpretation you will see: password length 15, lower-case letters required, upper-case letters required, numbers required, special characters required. –  ybeltukov 4 hours ago
1  
+1 Clever, but sneaky. –  David Carraher 4 hours ago
show 1 more comment

Ruby, 74 bytes

Just randomly sample from the ascii range 33 - 126 until all classes of characters are present:

$_=[*?!..?~].sample(15)*''until[/\d/,/[a-z]/,/[A-Z]/,/\W/].all?{|r|~r}
p$_

Ruby, 39 bytes

Using moose's clever discovery:

p"0123abcdABCD-+/<".chars.sample(15)*''
share|improve this answer
1  
+1 for the attribution and for nice usage of regex :-) –  moose 5 hours ago
 
+1 This is the way I would have done it, but not in Ruby. –  Tim 5 hours ago
2  
Could you please explain your code a bit? What does .all?{|r|~r} do? What does $_= do? –  moose 4 hours ago
1  
You might want to use \u and \l for lower and upper case letters. That saves you 6 more characters. –  moose 4 hours ago
 
You can make it a little shorter without the call to #all?, something like: until~/\d/&&~/[a-z]/&&~/[A-Z]/&&~/\W/ –  Chron 1 hour ago
add comment

Python 2.X + 3.X (229 characters): Generate and replace

Idea

  1. First make a list with 15 allowed symbols
  2. Replace a random position r by a random digit
  3. Replace a random position s, with s != r, by an upper case letter
  4. The same for lower case letter and symbol as in 2 and 3.

Code

from random import randint as r, shuffle as s
a=list(range(15))
p=a[:]
for i in range(15):
    a[i]=chr(r(32,126))
s(p)
a[p.pop()]=chr(r(48,57))
a[p.pop()]=chr(r(65,90))
a[p.pop()]=chr(r(97,122))
a[p.pop()]=chr(r(33,47))
print(a)

Python 2.X + 3.X (167 characters): Generate and check

import random
from re import search as s
p=""
while not all([s("\d",p),s("\l",p),s("\u",p),s("\W",p)]):
 p=str(map(chr,random.sample(list(range(33,127)),15)))
print(p)

Using flaw in the problem description

Currently, the problem description does not demand that every symbol / digit appears with the same probability. With the following solution, you cannot make any assumption about a single symbol and/or position. But you can do it with multiple ones.

Python 2.X+ 3.X (62 characters)

from random import sample
print(sample("0123abcdABCD-+/<",15))

Thanks to daniero for the idea to use sample.

share|improve this answer
 
+1 for reading the rules properly :D –  daniero 5 hours ago
 
Very smooth finding the flaw! I've plugged that one, but bonus points for identifying it. :-) –  Hand-E-Food 5 hours ago
 
Your gen&check is similar to my approach. Out of curiosity:where is this \l and so on for python regexes documented? Don't see it in the reference. My Python 3.3.3 won't even accept "\u". The str(…) does not join the letters in either 3.3.3 or 2.7.6. One suggestion for optmization: all(s("\\"+i,p)for i in "dluW"). –  MvG 24 mins ago
add comment

Java 8 - 354 329 characters

Just for fun, using lambdas with Java 8 - each possible output has the same probability of being found.

class A {
    static int a, A, d, p;

    public static void main(String[] x) {
        String s;
        do {
            s = new java.util.Random()
                    .ints(33, 127)
                    .limit(15)
                    .mapToObj(i -> "" + (char) i)
                    .collect(java.util.stream.Collectors.joining());
            a = A = d = p = 0;
            s.chars().map(c -> c>96&c<123 ? a++ : c>64&c<90 ? A++ : c>47&c<58 ? d++ : p++).min();
        } while (a == 0 | A == 0 | d == 0 | p == 0);
        System.out.println(s);
    }
}

Sample output:

.*;Tm?svthiEK`3  
o.dzMgtW5|Q?ATo  
FUmVsu<4JF4eB]1

Compressed program:

class A{static int a,A,d,p;public static void main(String[] x){String s;do{s=new java.util.Random().ints(33, 127).limit(15).mapToObj(i->""+(char)i).collect(java.util.stream.Collectors.joining());a=A=d=p=0;s.chars().map(c->c>96&c<123?a++:c>64&c<90?A++:c>47&c<58?d++:p++).min();}while(a==0|A==0|d==0|p==0);System.out.println(s);}}

share|improve this answer
add comment

Python 2.7 (182)

import random as r,string as s
z=r.sample
j=list(z(s.ascii_lowercase,12)+z(s.ascii_uppercase,1)+z(s.digits,1)+z('`~!@#$%^&*()_+-={}|[]\\:";\'<>?,./',1))
r.shuffle(j)
print ''.join(j)
share|improve this answer
 
You can get 9 digits less by removing the join as it is not required by the problem descript. Another 2 less by removing spaces. –  moose 5 hours ago
 
@moose I took out the spaces right before you commented :-) I feel like the join kinda has to be there: Would users be expected to understand python list syntax from the output: ['q', 'u', ...]? –  Jonathon Reinhart 5 hours ago
1  
I thought about removing print at all. When size in bytes is important, they might live in the punch card time. That way, they might be able to read the memory ... just by looking at it. Or they are "real programmers": xkcd.com/378 –  moose 5 hours ago
1  
If I'm reading the code correctly, this does not fulfill the all permutations requirement, it will always have 12 lowercase characters, making passwords with more than one of the other groups (like aA$bc1111111111) impossible. –  IQAndreas 5 hours ago
1  
In Johnathon's defence, I think the permutations rule was added 5 minutes after his post. –  Hand-E-Food 4 hours ago
show 4 more comments

Mathematica 170

r=RandomSample;f[i_]:=(FromCharacterCode/@Range@@i);
{t,A,a,n}=f/@{{33,126},{65,90},{97,122},{48,57}};
s=Complement[t,A,a,n];
""<>r[Join[RandomChoice/@{A,a,n,s},r[t,11]],15]

Examples

"<]}Pg3/e?3+Z~Oz"
"X/8jWe@f(_x5P:="
"2wz2VQhtJC?*R7^"

share|improve this answer
add comment

PHP, 235

This script shuffles characters around and then is checked via RegEx to make sure the password is strong (or it is regenerated).

<?php
while(true){ $p = substr(str_shuffle('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789`~!@#$%^&*()_+-={}|[]\:";\'<>?,./'),0,15); if(preg_match('/^(?=.*[A-Z])(?=.*[^A-Za-z])(?=.*[0-9])(?=.*[a-z]).{15}$/',$p)) break; }
echo $p;
share|improve this answer
1  
Clever, but doesn't allow duplicate characters. –  Hand-E-Food 2 hours ago
add comment

JavaScript (269 characters compacted)

For clarity, this is the code before I compacted it down JS-Fiddle of it:

var lowerLetters = "abcdefghijklmnopqrstuvwxyz";
var upperLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
var numbers = "0123456789";
var symbols = "`~!@#$%^&*()_+-={}|[]\\:\";'<>?,./";
var allCharacters = lowerLetters + upperLetters + numbers + symbols;

String.prototype.randomChar = function() {
    return this[Math.floor(this.length * Math.random())];
}

var minLength = 15;
var result = [];

// Start off by picking one random character from each group
result.push(lowerLetters.randomChar());
result.push(upperLetters.randomChar());
result.push(numbers.randomChar());
result.push(symbols.randomChar());
// Next, pick a random character from all groups until the desired length is met
while(result.length < minLength) {
    result.push(allCharacters.randomChar());
}
result.shuffle(); // Finally, shuffle the items (custom function; doesn't actually exist in JavaScript, but is very easy to add) -> http://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array
result.join("");

Here it is compacted down to 269 characters (JS-Fiddle of it):

l="abcdefghijklmnopqrstuvwxyz";
u=l.toUpperCase();
n="0123456789";
s="`~!@#$%^&*()_+-={}|[]\\:\";'<>?,./";
R=Math.random;

function r(t){
    return t[~~(t.length*R())]
}

for(x=[r(l),r(u),r(n),r(s)];x.length<15;x.push(r(l+u+n+s)));
x.sort(function(){return .5-R()});
alert(x.join(""));
share|improve this answer
 
Since I'm ending lines with semi-colons, all removable whitespace was ignored for character counting, but left in for clarity. –  IQAndreas 4 hours ago
 
What do you mean by shuffle() being a "custom function". Is it part of JavaScript or code you would have to write it yourself? –  Hand-E-Food 2 hours ago
 
@Hand-E-Food I meant it is not built into JavaScript, and since any developers here should know how to shuffle an array, I felt including the function in the code was unnecessary. It is available in the JS-Fiddle though (line 16). –  IQAndreas 2 hours ago
add comment

Powershell

First, get the four requirements sorted out (digit, symbol, uppercase, lowercase) and put it into a string for later.

Then, fill the pass with 11 random ASCII characters, all of which can be found directly on your keyboard.

Then, insert the requirement string at a random position so that generated passwords are more unique.

function pwGen {

    $upper = [char](get-random (65..90))
    $lower = [char](get-random (97..122))
    $digit = [char](get-random (48..57))
    $symbol = [char](get-random (33..47))

    $requirement = $upper + $lower + $digit + $symbol

    foreach ($number in (1..11)) {
        $ascii = [char](get-random (32..126))
        $pass += $ascii
    }

    $pass = $pass.insert((Get-Random (1..11)),$requirement)

    return $pass
}
share|improve this answer
add comment

JavaScript 258

R=Math.random;function a(b){return b[b.length*R()|0]}for(x=[a(l="abcdefghijklmnopqrstuvwxyz"),a(u=l.toUpperCase()),a(n="0123456789"),a(s="`~!@#$%^&*()_+-={}|[]\\:\";'<>?,./")];15>x.length;x.push(a(l+u+n+s)));alert(x.sort(function(){return 0.5-R()}).join(""))
share|improve this answer
 
Thanks to @IQAndreas for the inspiration –  Eliseo d'Annunzio 37 mins ago
add comment

Bash on *nix (114)

while ! grep -q [A-Z].*[a-z].*[0-9].*[^A-Za-z0-9]<<<$a$a$a$a
do a=`tr -dc !-~</dev/urandom|head -c15`
done
echo $a

To work correctly, $a must not be set to a valid but non-random password up front. If you want to include a= and a line break up front, that's three more characters but it allows you to run the thing repeatedly. You can obviously also replace all newlines with ; so you have a one-liner which you can execute as often as you whish.

Furthermore, you should have set LC_ALL=C or not set any locale-specific environment variables (LANG and LC_CTYPE in particular), since the character ranges depend on collation order being equal to ascii order.

/dev/urandom is the a source of random bytes. !-~ is the range of all permissible characters, as specified in the question. tr -dc removes all characters not listed in its next argument. head takes 15 of the remaining characters. grep checks whether each of the required kinds does occur at least once. Its input consists of four copies of the candidate, so order of the symbols does not matter, hence all possible passwords stand a chance of getting selected. The -q to grep suppresses output.

For reasons unknown, /dev/random instead of /dev/urandom takes ages. It seems like entropy got exhausted pretty quickly. If you cd into /dev, you can avoid some more bytes, but that feels a bit like cheating.

Python 2 (153) or Python 3 (154)

Same idea.

import re,random
a=''
while not re.search('[A-Z].*[a-z].*[0-9].*[^A-Za-z0-9]',a*4):
 a=''.join(random.sample(''.join(map(chr,range(33,127))),15))
print a

For Python 3, the you'd write print(a), which costs one more byte.

JavaScript (168)

a=[];for(i=33;i<127;)a.push(s=String.fromCharCode(i++));
while(!/[A-Z].*[a-z].*[0-9].*[^A-Za-z0-9]/.test(s+s+s+s))
for(i=0,s="";i<15;++i)s+=a[Math.random()*94|0];alert(s)

I added the newlines for readability, but did not count them.

share|improve this answer
add comment

Clojure (63):

(->> (map char (range 33 128)) (shuffle) (take 15) (apply str))

But need to be improved to ensure that containing at least 1 character of each category (Upper, Lower, Digit, Symbol).

share
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.