Because of my own needs, I converted PasswordMaker's backend code to PHP. On my first go, it didn't go well because PHP doesn't have the >>> operator, so some bit shifting wasn't able to work (and there might be a chance that if there's an overflow with the 32-bit intergers, PHP converts to float might have something to do with it as well)
Anyway, while using Mr. Venkman, I noticed that the return value for the hash functions before the character set was applied was in binary. Now PHP have mhash and I notice that it returns in binary, so as a test, I converted just the rstr2any function and did a test with the hex string '0123456789abcdef', you know the one you use if you want to create passwords like in 0.6, and compared the mhash function through rstr2any with PHP's builtin MD5 and SHA1 functions, and it worked, so it was downhill from there since I didn't need to deal with >>> at all!
The classes: <?php
define('LEET_NONE', 0);
define('LEET_BEFORE', 1);
define('LEET_AFTER', 2);
define('LEET_BOTH', 3);
function generatepassword($hashAlgorithm, $key, $data, $whereToUseL33t, $l33tLevel, $passwordLength, $charset, $prefix, $suffix) {
global $PasswordMaker_l33t, $PasswordMaker_SHA256, $PasswordMaker_SHA1, $PasswordMaker_MD4, $PasswordMaker_MD5, $PasswordMaker_RIPEMD160;
// Never *ever, ever* allow the charset's length<2 else
// the hash algorithms will run indefinitely
if (strlen($charset) < 2)
return "";
// for non-hmac algorithms, the key is master pw and url concatenated
$usingHMAC = strpos($hashAlgorithm, "hmac") !== false;
if (!$usingHMAC)
$key .= $data;
// apply l33t before the algorithm?
if ($whereToUseL33t == LEET_BOTH || $whereToUseL33t == LEET_BEFORE) {
$key = $PasswordMaker_l33t->convert($l33tLevel, $key);
if ($usingHMAC) {
$data = $PasswordMaker_l33t->convert($l33tLevel, $data); // new for 0.3; 0.2 didn't apply l33t to _data_ for HMAC algorithms
}
}
// apply the algorithm
$password = '';
/**/
switch($hashAlgorithm) {
case "sha256":
$password = $PasswordMaker_SHA256->any_sha256($key, $charset);
break;
case "hmac-sha256":
$password = $PasswordMaker_SHA256->any_hmac_sha256($key, $data, $charset);
break;
case "sha1":
$password = $PasswordMaker_SHA1->any_sha1($key, $charset);
break;
case "hmac-sha1":
$password = $PasswordMaker_SHA1->any_hmac_sha1($key, $data, $charset);
break;
case "md4":
$password = $PasswordMaker_MD4->any_md4($key, $charset);
break;
case "hmac-md4":
$password = $PasswordMaker_MD4->any_hmac_md4($key, $data, $charset);
break;
case "md5":
$password = $PasswordMaker_MD5->any_md5($key, $charset);
break;
case "hmac-md5":
$password = $PasswordMaker_MD5->any_hmac_md5($key, $data, $charset);
break;
case "rmd160":
$password = $PasswordMaker_RIPEMD160->any_rmd160($key, $charset);
break;
case "hmac-rmd160":
$password = $PasswordMaker_RIPEMD160->any_hmac_rmd160($key, $data, $charset);
break;
}
/**/
// apply l33t after the algorithm?
if ($whereToUseL33t == LEET_BOTH || $whereToUseL33t == LEET_AFTER)
$password = $PasswordMaker_l33t->convert($l33tLevel, $password);/**/
if ($prefix)
$password = $prefix . $password;
if ($suffix)
$password = substr($password, 0, $passwordLength-strlen($suffix)) . $suffix;
return substr($password, 0, $passwordLength);
}
class PasswordMaker_l33t {
var $alphabet, $levels;
function PasswordMaker_l33t() {
for($i = 0; $i < 26; $i++) {
$t = chr(ord('a') + $i);
$t = "/$t/";
$this->alphabet[] = $t;
}
$this->levels = Array(
Array("4", "b", "c", "d", "3", "f", "g", "h", "i", "j", "k", "1", "m", "n", "0", "p", "9", "r", "s", "7", "u", "v", "w", "x", "y", "z"),
Array("4", "b", "c", "d", "3", "f", "g", "h", "1", "j", "k", "1", "m", "n", "0", "p", "9", "r", "5", "7", "u", "v", "w", "x", "y", "2"),
Array("4", "8", "c", "d", "3", "f", "6", "h", "'", "j", "k", "1", "m", "n", "0", "p", "9", "r", "5", "7", "u", "v", "w", "x", "'/", "2"),
Array("@", "8", "c", "d", "3", "f", "6", "h", "'", "j", "k", "1", "m", "n", "0", "p", "9", "r", "5", "7", "u", "v", "w", "x", "'/", "2"),
Array("@", "|3", "c", "d", "3", "f", "6", "#", "!", "7", "|<", "1", "m", "n", "0", "|>", "9", "|2", "$", "7", "u", "\\/", "w", "x", "'/", "2"),
Array("@", "|3", "c", "|)", "&", "|=", "6", "#", "!", ",|", "|<", "1", "m", "n", "0", "|>", "9", "|2", "$", "7", "u", "\\/", "w", "x", "'/", "2"),
Array("@", "|3", "[", "|)", "&", "|=", "6", "#", "!", ",|", "|<", "1", "^^", "^/", "0", "|*", "9", "|2", "5", "7", "(_)", "\\/", "\\/\\/", "><", "'/", "2"),
Array("@", "8", "(", "|)", "&", "|=", "6", "|-|", "!", "_|", "|\(", "1", "|\\/|", "|\\|", "()", "|>", "(,)", "|2", "$", "|", "|_|", "\\/", "\\^/", ")(", "'/", "\"/_"),
Array("@", "8", "(", "|)", "&", "|=", "6", "|-|", "!", "_|", "|\{", "|_", "/\\/\\", "|\\|", "()", "|>", "(,)", "|2", "$", "|", "|_|", "\\/", "\\^/", ")(", "'/", "\"/_"));
}
/**
* Convert the string in _message_ to l33t-speak
* using the l33t level specified by _leetLevel_.
* l33t levels are 1-9 with 1 being the simplest
* form of l33t-speak and 9 being the most complex.
*
* Note that _message_ is converted to lower-case if
* the l33t conversion is performed.
* Future versions can support mixed-case, if we need it.
*
* Using a _leetLevel_ <= 0 results in the original message
* being returned.
*
*/
function convert($leetLevel, $message) {
if ($leetLevel > -1 && $leetLevel < 9) {
$ret = strtolower($message);
for ($item = 0; $item < count($this->alphabet); $item++) {
$ret = preg_replace($this->alphabet[$item], $this->levels[$leetLevel][$item], $ret);
}
return $ret;
}
return $message;
}
}
$PasswordMaker_l33t = new PasswordMaker_l33t;
class PasswordMaker_HashUtils {
var $chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */
/*
* Encode a string as utf-8.
* For efficiency, this assumes the input is valid utf-16.
*/
/*str2rstr_utf8 : function(input) {
var output = "";
var i = -1;
var x, y;
while(++i < input.length)
{
// Decode utf-16 surrogate pairs
x = input.charCodeAt(i);
y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0;
if(0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF)
{
x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
i++;
}
// Encode output as utf-8
if(x <= 0x7F)
output += String.fromCharCode(x);
else if(x <= 0x7FF)
output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F),
0x80 | ( x & 0x3F));
else if(x <= 0xFFFF)
output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F),
0x80 | ((x >>> 6 ) & 0x3F),
0x80 | ( x & 0x3F));
else if(x <= 0x1FFFFF)
output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07),
0x80 | ((x >>> 12) & 0x3F),
0x80 | ((x >>> 6 ) & 0x3F),
0x80 | ( x & 0x3F));
}
return output;
}/**/
/*
* Convert a raw string to an array of little-endian words
* Characters >255 have their high-byte silently ignored.
*/
/*rstr2binl : function(input) {
var output = Array(input.length >> 2);
for(var i = 0; i < output.length; i++)
output[i] = 0;
for(var i = 0; i < input.length * 8; i += 8)
output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (i%32);
return output;
}/**/
/*
* Convert an array of little-endian words to a string
*/
/*binl2rstr : function(input) {
var output = "";
for(var i = 0; i < input.length * 32; i += 8)
output += String.fromCharCode((input[i>>5] >>> (i % 32)) & 0xFF);
return output;
}/**/
/*
* Convert a raw string to an arbitrary string encoding
*/
function rstr2any($input, $encoding) {
$divisor = strlen($encoding);
$remainders = Array();
/* Convert to an array of 16-bit big-endian values, forming the dividend */
// pad this
$dividend = array_pad(array(), strlen($input) / 2, 0);
$inp = $input; // Because Miquel is a lazy twit and didn't want to do a search and replace
for($i = 0; $i < count($dividend); $i++) {
$dividend[$i] = (ord($inp{$i * 2}) << 8) | ord($inp{$i * 2 + 1});
}
/*
* Repeatedly perform a long division. The binary array forms the dividend,
* the length of the encoding is the divisor. Once computed, the quotient
* forms the dividend for the next step. We stop when the dividend is zero.
* All remainders are stored for later use.
*/
while(count($dividend) > 0) {
$quotient = Array();
$x = 0;
for($i = 0; $i < count($dividend); $i++) {
$x = ($x << 16) + $dividend[$i];
$q = floor($x / $divisor);
$x -= $q * $divisor;
if(count($quotient) > 0 || $q > 0)
$quotient[count($quotient)] = $q;
}
$remainders[count($remainders)] = $x;
$dividend = $quotient;
}
/* Convert the remainders to the output string */
$output = "";
for($i = count($remainders) - 1; $i >= 0; $i--)
$output .= $encoding{$remainders[$i]};
return $output;
}
///===== big endian =====\\\
/*
* Convert a raw string to an array of big-endian words
* Characters >255 have their high-byte silently ignored.
*/
/*rstr2binb : function(input) {
var output = Array(input.length >> 2);
for(var i = 0; i < output.length; i++)
output[i] = 0;
for(var i = 0; i < input.length * 8; i += 8)
output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (24 - i % 32);
return output;
}/**/
/*
* Convert an array of big-endian words to a string
*/
/*binb2rstr : function(input) {
var output = "";
for(var i = 0; i < input.length * 32; i += 8)
output += String.fromCharCode((input[i>>5] >>> (24 - i % 32)) & 0xFF);
return output;
}/**/
/*
* Bitwise rotate a 32-bit number to the left.
*/
/*bit_rol : function(num, cnt) {
return (num << cnt) | (num >>> (32 - cnt));
}/**/
/*
* Add integers, wrapping at 2^32. This uses 16-bit operations internally
* to work around bugs in some JS interpreters.
*/
/*safe_add : function(x, y) {
var lsw = (x & 0xFFFF) + (y & 0xFFFF);
var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
return (msw << 16) | (lsw & 0xFFFF);
}/**/
}
$PasswordMaker_HashUtils = new PasswordMaker_HashUtils;
class PasswordMaker_MD5 {
/*
any_hmac_md5 : function(k, d, e) { return PasswordMaker_HashUtils.rstr2any(this.rstr_hmac_md5(PasswordMaker_HashUtils.str2rstr_utf8(k), PasswordMaker_HashUtils.str2rstr_utf8(d)), e); },
*/
function any_md5($s, $e) {
global $PasswordMaker_HashUtils;
return $PasswordMaker_HashUtils->rstr2any(mhash(MHASH_MD5, $s), $e);
}
function any_hmac_md5($k, $d, $e) {
global $PasswordMaker_HashUtils;
return $PasswordMaker_HashUtils->rstr2any(mhash(MHASH_MD5, $d, $k), $e);
}
}
$PasswordMaker_MD5 = new PasswordMaker_MD5;
class PasswordMaker_MD4 {
function any_md4($s, $e) {
global $PasswordMaker_HashUtils;
return $PasswordMaker_HashUtils->rstr2any(mhash(MHASH_MD4, $s), $e);
}
function any_hmac_md4($k, $d, $e) {
global $PasswordMaker_HashUtils;
return $PasswordMaker_HashUtils->rstr2any(mhash(MHASH_MD4, $d, $k), $e);
}
}
$PasswordMaker_MD4 = new PasswordMaker_MD4;
class PasswordMaker_RIPEMD160 {
function any_rmd160($s, $e) {
global $PasswordMaker_HashUtils;
return $PasswordMaker_HashUtils->rstr2any(mhash(MHASH_RIPEMD160 , $s), $e);
}
function any_hmac_rmd160($k, $d, $e) {
global $PasswordMaker_HashUtils;
return $PasswordMaker_HashUtils->rstr2any(mhash(MHASH_RIPEMD160, $d, $k), $e);
}
}
$PasswordMaker_RIPEMD160 = new PasswordMaker_RIPEMD160;
class PasswordMaker_SHA1 {
function any_sha1($s, $e) {
global $PasswordMaker_HashUtils;
return $PasswordMaker_HashUtils->rstr2any(mhash(MHASH_SHA1 , $s), $e);
}
function any_hmac_sha1($k, $d, $e) {
global $PasswordMaker_HashUtils;
return $PasswordMaker_HashUtils->rstr2any(mhash(MHASH_SHA1, $d, $k), $e);
}
}
$PasswordMaker_SHA1 = new PasswordMaker_SHA1;
class PasswordMaker_SHA256 {
function any_sha256($s, $e) {
global $PasswordMaker_HashUtils;
return $PasswordMaker_HashUtils->rstr2any(mhash(MHASH_SHA256 , $s), $e);
}
function any_hmac_sha256($k, $d, $e) {
global $PasswordMaker_HashUtils;
return $PasswordMaker_HashUtils->rstr2any(mhash(MHASH_SHA256, $d, $k), $e);
}
}
$PasswordMaker_SHA256 = new PasswordMaker_SHA256;
?>
And the test case file <?php
include 'passwordmaker_class.php';
$base93="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".
"0123456789`~!@#$%^&*()_-+={}|[]\:\";'<>?,./";
$hex = '0123456789abcdef';
// Test the function
$master = 'power';
$url = 'miquelfire.mfd';
$username = '';
$modifier = '';
$leetwhere = LEET_BEFORE; // Either constants or the numbers 0-3 LEET_NONE=0 LEET_BEFORE=1 LEET_AFTER=2 LEET_BOTH=3
$leetlevel = 4; // Subtract 1 from the level you're testing with. See the HTML code on www.password.org to see that this is true (0-8)
$passlen = 12;
$charset = $base93;
$prefix = '';
$suffix = '';
echo "MD4 generated password: ";
echo generatepassword('md4', $master, $url.$username.$modifier, $leetwhere, $leetlevel, $passlen, $charset, $prefix, $suffix)."\n";
echo "---\nHMAC-MD4 generated password: ";
echo generatepassword('hmac-md4', $master, $url.$username.$modifier, $leetwhere, $leetlevel, $passlen, $charset, $prefix, $suffix)."\n";
echo "---\nMD5 generated password: ";
echo generatepassword('md5', $master, $url.$username.$modifier, $leetwhere, $leetlevel, $passlen, $charset, $prefix, $suffix)."\n";
echo "---\nHMAC-MD5 generated password: ";
echo generatepassword('hmac-md5', $master, $url.$username.$modifier, $leetwhere, $leetlevel, $passlen, $charset, $prefix, $suffix)."\n";
echo "---\nSHA-1 generated password: ";
echo generatepassword('sha1', $master, $url.$username.$modifier, $leetwhere, $leetlevel, $passlen, $charset, $prefix, $suffix)."\n";
echo "---\nHMAC-SHA-1 generated password: ";
echo generatepassword('hmac-sha1', $master, $url.$username.$modifier, $leetwhere, $leetlevel, $passlen, $charset, $prefix, $suffix)."\n";
echo "---\nSHA-256 generated password: ";
echo generatepassword('sha256', $master, $url.$username.$modifier, $leetwhere, $leetlevel, $passlen, $charset, $prefix, $suffix)."\n";
echo "---\nHMAC-SHA-256 generated password: ";
echo generatepassword('hmac-sha256', $master, $url.$username.$modifier, $leetwhere, $leetlevel, $passlen, $charset, $prefix, $suffix)."\n";
echo "---\nRIPEMD-160 generated password: ";
echo generatepassword('rmd160', $master, $url.$username.$modifier, $leetwhere, $leetlevel, $passlen, $charset, $prefix, $suffix)."\n";
echo "---\nHMAC-RIPEMD-160 generated password: ";
echo generatepassword('hmac-rmd160', $master, $url.$username.$modifier, $leetwhere, $leetlevel, $passlen, $charset, $prefix, $suffix)."\n";
?>
Now unless the trimming of zeros popped it's head with this test case, HMAC-SHA256 is the only algorithm that doesn't work from some reason.
Compare with the online version if you don't want to trash your current settings (or risk it at least)