Entries Closed ⛳️ [Programming, Code Golf] Password Generator

Entries Closed ⛳️ [Programming, Code Golf] Password Generator

Adam

Mr. Webwide
Administrator
Local time
18:30
Joined
Sep 24, 2019
Messages
1,257
Pronouns
he/him

Close date: 3rd December 2019 12:00pm UTC

Code golf is a competition where the aim is to complete a task using the shortest possible source code. This challenge may be completed using vanilla JavaScript, PHP, Python or Ruby. 3rd-party dependencies are not allowed, standard libs are.

Submit your code below to solve the following problem:

  • Create a password generator
  • The password must contain exactly 16 characters, and the characters are limited to the following character classes: a-z, A-Z, 0-9, and !#$%&()*+,-./:;<=>? @[]^_{|}~ (note there is a space character in this class).
  • The password must contain a character from each character class.
  • Each character from all the character classes must have an equal chance of being picked.
  • The password cannot have predictable patterns and must generate a new one each time it is run.
  • The script must run from start to finish without any additional user input.
The shortest entry will receive a first place position and all working entries will receive points on our community challenge leaderboard.

If anybody stumbles on this thread after we're done, please don't use any of these in real applications... 😆

 
Last edited:

kylejrp

Member
Local time
10:30
Joined
Nov 20, 2019
Messages
3

Here's a WIP attempt in Javascript (217 characters), I'm going to refine this later after reading up on some code golf tips.

JavaScript:
var z='x'
while(!(z.match(/[a-z]/)&&z.match(/[A-Z]/)&&z.match(/\d/)&&z.match(/\W/)))
z=[...Array(95)].map((_,i)=>String.fromCharCode(32+i)).map((_,__,a)=>a[Math.floor((Math.random()*a.length))]).slice(0,16).join('')
z

 

Gummibeer

Astroneer
Moderator
Local time
19:30
Joined
Oct 5, 2019
Messages
1,169
Pronouns
he/him

@Adam I want and have to soften two rules.

Each character from all the character classes must have an equal chance of being picked.
Without a ton of code or external libraries/hardware it's impossible to guarantee this. The most common solution would be

a=[...]
b=a[round(random(0, 1) * (a.length()-1))]

But this won't have equal chances for all. At least the first and last elements will only have half the chance.

random(0,1) * (3-1)
will result in all numbers between 0-2 which will get rounded. By applying normal rounding this will result in.
0 = x<0.5 | 25%
1 = 0.5<=x<1.5 |50%
2 = 1.5<=x | 25%

So at least I would like to soften it to allow this implementation.

The password cannot have predictable patterns and must generate a new one each time it is run.
Most default random algorithms are predictable if you know the machine and/or timestamp of generation. Because most or time or garbage based, use low level code randomness and so on to be fast but cryptographic unsecure.
So this rule should also get softened.

 
Last edited:

Adam

Mr. Webwide
Administrator
Local time
18:30
Joined
Sep 24, 2019
Messages
1,257
Pronouns
he/him

@Adam I want and have to soften two rules.

Without a ton of code or external libraries/hardware it's impossible to guarantee this. The most common solution would be

a=[...]
b=a[round(random(0, 1) * (a.length()-1))]

But this won't have equal chances for all. At least the first and last elements will only have half the chance.

random(0,1) * (3-1)
will result in all numbers between 0-2 which will get rounded. By applying normal rounding this will result in.
0 = x<0.5 | 25%
1 = 0.5<=x<1.5 |50%
2 = 1.5<=x | 25%

So at least I would like to soften it to allow this implementation.

Most default random algorithms are predictable if you know the machine and/or timestamp of generation. Because most or time or garbage based, use low level code randomness and so on to be fast but cryptographic unsecure.
So this rule should also get softened.

That’s fine. 👍

 

Gummibeer

Astroneer
Moderator
Local time
19:30
Joined
Oct 5, 2019
Messages
1,169
Pronouns
he/him

And now I'm an asshole!
@kylejrp solution is wrong. 🙈

That's the code generating the possible character pool:

JavaScript:
var z = {};
[...Array(95)]
    .map((_, i) => String.fromCharCode(32 + i))
    .forEach(a => z[a] = /[a-zA-Z0-9!#\$%&\(\)\*\+,\-\.\/:;<=>\? @\[\]\^_{\|}~]/.test(a));
z;
And that's the result/value of z:
JSON:
{"0":true,"1":true,"2":true,"3":true,"4":true,"5":true,"6":true,"7":true,"8":true,"9":true," ":true,"!":true,"\"":false,"#":true,"$":true,"%":true,"&":true,"'":false,"(":true,")":true,"*":true,"+":true,",":true,"-":true,".":true,"/":true,":":true,";":true,"<":true,"=":true,">":true,"?":true,"@":true,"A":true,"B":true,"C":true,"D":true,"E":true,"F":true,"G":true,"H":true,"I":true,"J":true,"K":true,"L":true,"M":true,"N":true,"O":true,"P":true,"Q":true,"R":true,"S":true,"T":true,"U":true,"V":true,"W":true,"X":true,"Y":true,"Z":true,"[":true,"\\":false,"]":true,"^":true,"_":true,"`":false,"a":true,"b":true,"c":true,"d":true,"e":true,"f":true,"g":true,"h":true,"i":true,"j":true,"k":true,"l":true,"m":true,"n":true,"o":true,"p":true,"q":true,"r":true,"s":true,"t":true,"u":true,"v":true,"w":true,"x":true,"y":true,"z":true,"{":true,"|":true,"}":true,"~":true}
There are multiple false values like " ' \ ` and all of them pass the too broad \W regex which matches anything other than a letter, digit or underscore. Which introduces the next problem that aB0xxxxxxxxxxxx_ won't pass the while condition but it is a valid password because it contains all required character sets.
JavaScript:
var z = 'aB0xxxxxxxxxxxx_'; !(z.match(/[a-z]/)&&z.match(/[A-Z]/)&&z.match(/\d/)&&z.match(/\W/));
// z = true

But I like the idea behind this solution! :)

 

Gummibeer

Astroneer
Moderator
Local time
19:30
Joined
Oct 5, 2019
Messages
1,169
Pronouns
he/him

I would say that this is a fixed version with 243 chars

JavaScript:
for(var z='';!(/[a-z]/.test(z)&&/[A-Z]/.test(z)&&/[\d]/.test(z)&&/[!#$%&()*+,\-.\/:;<=>? @\[\]^_{|}~]/.test(z));)z=[...Array(95)].map((t,a)=>String.fromCharCode(32+a)).map((t,a,e)=>e[Math.floor(Math.random()*e.length)]).slice(0,16).join('')
z

It passes my quick mocha test-suite.

JavaScript:
require('should');
const mocha = require('mocha');
const describe = mocha.describe;
const it = mocha.it;

const kylejrp = require('../src/kylejrp');
const gummibeer = require('../src/gummibeer');

const test = function(user) {
    [...Array(10000)].forEach((_, i) => {
        it(i + ') will generate valid password', function () {
            const password = user();

            password.should.be.a.String().and.have.length(16);
            /[a-z]/.test(password).should.be.true(password + ' should contain [a-z]');
            /[A-Z]/.test(password).should.be.true(password + ' should contain [A-Z]]');
            /[0-9]/.test(password).should.be.true(password + ' should contain [0-9]');
            /[!#$%&()*+,\-.\/:;<=>? @\[\]^_{|}~]/.test(password).should.be.true(password + ' should contain [!#$%&()*+,-./:;<=>? @[]^_{|}~]');
        });
    });
};

describe('kylejrp', function () {
    test(kylejrp);
});

describe('gummibeer', function () {
    test(gummibeer);
});

GIT: Gummibeer/webwide-challenge-2019-11

 
Last edited:

Gummibeer

Astroneer
Moderator
Local time
19:30
Joined
Oct 5, 2019
Messages
1,169
Pronouns
he/him

And here is my PHP solution:

PHP:
<?php $s=array_values(array_filter(array_map('chr',range(32,126)),fn($c)=>strpos('\'"`\\',$c)===false));do{for($p='';strlen($p)<16;str_shuffle($p)){$p.=$s[random_int(0,count($s)-1)];}}while(!(preg_match('/[a-z]/',$p)&&preg_match('/[A-Z]/',$p)&&preg_match('/[\d]/',$p)&&preg_match('/[\!\#\$\%\&\(\)\*\+\,\-\.\/\:\;\<\=\>\? \@\[\]\^\_\{\|\}\~]/',$p)));return $p;

Including the opening tag it's 360 characters. 🎉 Most characters are used by all the function prefixes array_ preg_ str_ ...

I've also added the solution to TravisCI including the required tests to prove a solution (10000 runs each).

 
Top