I came up with base 90 for reducing md5 to 20 multi-byte characters (that I tested to fit properly in a mysql's varchar(20) column). Unfortunately this actually makes the string potentially larger than even the 32 bytes from php's md5, with the only advantage that they can be stored in varchar(20) columns. Of course you could just replace the alphabet with single-byte ones if your worries are about storage...
There are a couple of rules that are important to have in mind if your idea is to use this reduced hash as a lookup key in something like mysql and for other kinds of processing:
By default MySQL does not differentiate Upper Case from Lower Case in a typical where clause which takes out a lot of characters right out of the possible target alphabets. This include not only english character but also almost all characters in other languages.
It's important that your hash can be upper-cased and lower-cased transparently since many systems uppercase these keys, so to keep it consistent with md5 in that sense you should use only lowercase when using case-able characters.
This is the alphabet I used (I handpicked each character to make the hashes as nice as possible):
define('NICIESTCHARS', [
"0","1","2","3","4","5","6","7","8","9",
"a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z",
"¢","£","¥","§","¶","ø","œ","ƒ","α","δ","ε","η","θ","ι","λ","μ","ν","π","σ","τ","φ","ψ","ω","ћ","џ","ѓ","ѝ","й","ќ","ў","ф","э","ѣ","ѷ","ѻ","ѿ","ҁ","∂","∆","∑","√","∫",
"!","#","$","%","&","*","+","=","@","~","¤","±"
]);
Here is the code in PHP (I suppose it's not the best code but does the job). And keep in mind that it only works for strings in hexa (0-F) that are a multiple of 8 in length like md5 in php which is 32 0-f bytes:
function mbStringToArray ($string) {
$strlen = mb_strlen($string);
while ($strlen) {
$array[] = mb_substr($string,0,1,"UTF-8");
$string = mb_substr($string,1,$strlen,"UTF-8");
$strlen = mb_strlen($string);
}
return $array;
}
class Base90{
static function toHex5($s){
// Converts a base 90 number with a multiple of 5 digits to hex (compatible with "hexdec").
$chars = preg_split('//u', $s, null, PREG_SPLIT_NO_EMPTY);
$map = array_flip(NICIESTCHARS);
$rt = '';
$part = [];
$b90part = '';
foreach($chars as $c){
$b90part .= $c;
$part[] = $map[$c];
if(count($part) == 5){
$int = base90toInt($part);
$rt .= str_pad(dechex($int), 8, "0", STR_PAD_LEFT);
$part = [];
$b90part = '';
}
}
return $rt;
}
static function fromHex8($m){
// Converts an hexadecimal number compatible with "hexdec" to base 90
$parts = [];
$part = '';
foreach(str_split($m) as $i => $c){
$part.= $c;
if(strlen($part) === 8){
$parts[] = intToBase90(hexdec($part));
$part = '';
}
}
return implode('', $parts);
}
}
function intToBase90($int){
$residue = $int;
$result = [];
while($residue){
$digit = $residue % 90;
$residue -= $digit;
$residue = $residue / 90;
array_unshift($result, NICIESTCHARS[$digit]);
}
$result = implode('', $result);
return $result;
}
function base90toInt($digits){
$weight = 1;
$rt = 0;
while(count($digits)){
$rt += array_pop($digits)*$weight;
$weight *= 90;
}
return $rt;
}
str_rot13()
call somewhere. On a more serious note, if you need to retrieve the decrypted version, you can't usemd5()
anyway since that's a hashing function, which makes it almost essentially a one-way encryption. – Hydrodynamicsmd5($data)
toAlphabet::convert($hash, Alphabet::HEX, Alphabet::ALPHANUMERIC)
and thensubstr($result, 0, 12)
to preserve the highest amount of information. – Elegance