5
\$\begingroup\$

Oplop is an algorithm to generate website specific passwords based on a master password and a keyword.

It is described here: http://code.google.com/p/oplop/wiki/HowItWorks

There's an online implementation here: https://oplop.appspot.com/

My attempt in Q is 204 200 167 165 139 characters ignoring whitespace:

{d:.Q.A,.Q.a,(n:"0123456789"),"-_";
b:d 2 sv'6 cut(,/)0b vs'(md5 x,y),2#0x00;
i:b in n;j:i?1b;k:(j _i)?0b;
8#$[0=0+/i;"1";8>j;();b j+(!)k],b}

EDIT: I achieved significant character savings by removing some redundant code from my Hex->Base64 conversion.

2012.03.07 - Cut out 2 more characters

2012.03.11 - Removed duplication and got accurate char count

2012.03.20 - Down to 141

Criteria

Candidate functions/implementations should take two strings as arguments and return an 8 character password as defined in the algorithm.

Judging

Code golf so shortest code takes the prize.

\$\endgroup\$
  • \$\begingroup\$ When will the judging be done? \$\endgroup\$ – Mark Thomas Mar 21 '12 at 12:22
  • \$\begingroup\$ I'll give it until the end of the month. 2012-04-01 00:00:00.000 \$\endgroup\$ – skeevey Mar 21 '12 at 13:16
  • 1
    \$\begingroup\$ Imho, the problem description should be complete in that way, that an average programmer knows what to do. So you should name the steps to generate the code, while it is to the user to find out how to md5sum or to base64encode something. A website for further details is welcome, of course. Preparing a challenge on meta or in chat is - btw. - recommended. \$\endgroup\$ – user unknown Mar 28 '12 at 20:46
3
\$\begingroup\$

Ruby (113 110 chars)

require 'digest'
b=Digest::MD5.base64digest(ARGV.join)
puts "#{b} 1".sub(/(^\D{8,}(\d*))/,'\2\1')[0..7].tr'+/','-_'

require'digest'
b=Digest::MD5.base64digest(ARGV.join)
puts"#{b} 1".sub(/(^\D{8,}(\d*))/,'\2\1')[0,8].tr'+/','-_'

I notice that all the non-Python languages have to do a translation of the + and / to be - and _, respectively. It seems that Python's base64 is different/wrong?

\$\endgroup\$
  • 2
    \$\begingroup\$ There are several variants for different applications where different characters need escaping. Note that the Python method used is called urlsafe_b64encode \$\endgroup\$ – Peter Taylor Mar 8 '12 at 11:10
5
\$\begingroup\$

Python - 198 166 152 150

import base64,md5,sys,re
b=base64.urlsafe_b64encode(md5.md5(sys.argv[1]+sys.argv[2]).digest())
r=re.findall('\d+',b)if all(not c.isdigit()for c in b[:8])else['']
print((r[0]if len(r)else'1')+b)[:8]

import base64,md5,sys,re
b=base64.urlsafe_b64encode(md5.md5("".join(sys.argv[1:])).digest())
print((''if re.search('\d',b[:8])else re.findall('\d+',b+'1')[0])+b)[:8]

import base64,md5,sys,re
b=base64.urlsafe_b64encode(md5.md5("".join(sys.argv[1:])).digest())
print(re.findall('(?:[^\d]{8,}(\d*)|)',b+' 1')[0]+b)[:8]

This was a fun one. Could be a lot shorter if not for the verbosity of the md5 and base64 modules. The third and fourth lines are the interesting ones. My favourite trick was using a list containing only an empty string to avoid having to use any real if statements.

EDIT: Had to change from checking c.isalpha() to not c.isdigit() (added 4 characters)

EDIT: Shaved off 32 characters. Many thanks to Ugoren!

EDIT: Shaved off 14 more chars again many thanks to Ugoren. Used some regex trickiness

print(re.findall('(?:[^\d]{8,}(\d*)|)',b+' 1')[0]+b)[:8]

instead of

print((re.findall('^[^\d]{8,}(\d*)',b+' 1')+[''])[0]+b)[:8]

To save three chars.

\$\endgroup\$
  • \$\begingroup\$ This seems to give incorrect values for some input eg. (a,aaaa) \$\endgroup\$ – skeevey Feb 28 '12 at 2:09
  • \$\begingroup\$ Oh shoot, I know why. I'll update in a second, thanks for catching that. \$\endgroup\$ – Gordon Bailey Feb 28 '12 at 2:11
  • \$\begingroup\$ Small improvements: "".join(sys.argv[1:3]) (or even [1:]), if r can replace if len(r) (I think), re.match('^[^\d]{8}',b). \$\endgroup\$ – ugoren Feb 28 '12 at 13:46
  • \$\begingroup\$ Also - r=re.findall('\d+',b+' 1') allows you to remove the if-else later (and then you don't need r). And reversing if-else on line 3 allows using re.match('\d',b[:8]) instead of my above suggestion. \$\endgroup\$ – ugoren Feb 28 '12 at 14:16
  • \$\begingroup\$ Nice tips, shaved off 32 characters, thanks! \$\endgroup\$ – Gordon Bailey Feb 28 '12 at 15:44
5
\$\begingroup\$

Shell - 116

echo -n $1$2|md5sum|xxd -r -p|base64|sed -re'y!+/!-_!;/^.{0,7}[0-9]/!{s/^([^0-9]*)([0-9]+)/\2\1/;t;s/^/1/}'|cut -c-8

Depends on cut, md5sum and base64 from coreutils, xxd and GNU sed.

\$\endgroup\$
2
\$\begingroup\$

PHP (126)

function f($m,$n){return substr(preg_replace('/^\D{8,}(\d+)/','$1$0',strtr(base64_encode(md5($m.$n,1)),'+/','-_').' 1'),0,8);}

Beautified:

function oplop( $master, $nick ) {
    return substr( preg_replace(
        '/^\D{8,}(\d+)/', '$1$0',
        strtr( base64_encode( md5( $master . $nick, true ) ), '+/', '-_' ) . ' 1'
    ), 0, 8 );
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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