Reputation: 852
I am trying to decrypt data being communicated to our server. Its a certain 8 digit number which is encrypted using a scheme. I have the encryption and integrity keys with me. I have a documentation on how to decrypt it which says -
The value is encrypted using a custom encryption scheme. The encryption scheme uses a keyed HMAC-SHA1 algorithm to generate a secret pad based on the unique event ID. The encrypted value has a fixed length of 28 bytes. It is comprised of a 16-byte initialization vector, 8 bytes of ciphertext, and a 4-byte integrity signature. The encrypted value is web-safe base-64-encoded, according to RFC 3548, with padding characters omitted. Thus, the 28-byte encrypted value is encoded as a 38 character web-safe base-64 string. The value is encrypted as:
value xor HMAC-SHA1(encryption_key, initialization_vector)>
so decryption calculates:
HMAC-SHA1(encryption_key, initialization_vector)
and xor's with the encrypted value to reverse the encryption. The integrity stage takes 4 bytes of
HMAC-SHA1(integrity_key, value||initialization_vector)>
where || is concatenation.
So i wrote the following PHP Code.
$value= "[VALUE]"; //38 character base64
$ekey=hextostr("[ENCRYPTIONKEY]"); //64 byte hex encoded key . 32 byte key
$ikey=hextostr("[INTEGRITYKEY]"); //64 byte hex encoded key . 32 byte key
$value=str_replace("-","+",$value);
$value=str_replace("_","/",$value);
$value=$value."==";
$dvalue=base64_decode($value); //Gets a 28 byte encrypted string.
$initvec=substr($dvalue,0,16);
$ciphertext=substr($dvalue,16,8);
$integritysig=substr($dvalue,24,4);
$pad=hash_hmac("sha1",$initvec,$ekey); //Generates 40 byte pad
$uncipher=$ciphertext^$pad;
print($uncipher); //This is 8 byte binary. Dumps some binary on screen. Result should be a 8 byte number
Unable to get around this problem. Please advice.
Upvotes: 2
Views: 6134
Reputation: 11
Try this
function decrypt_google_winning_price($value, $ekey, $ikey, &$reason = '') {
if (strlen($value) != 38)
{
$reason = "Wrong encrypted value length";
return false;
}
$ekey = base64_decode($ekey);
$ikey = base64_decode($ikey);
$value = strtr($value, '-_,', '+/=') . "==";
$enc_value = base64_decode($value); //Gets a 28 byte encrypted string.
if (strlen($enc_value) != 28)
{
$reason = "Wrong encrypted value length after base64_decode()";
return false;
}
$iv = substr($enc_value, 0, 16);// initialization vector (16 bytes - unique to the impression)
$p = substr($enc_value, 16, 8); // encryption key (32 bytes - provided at account set up)
$sig = substr($enc_value, 24, 4);// integrity signature (4 bytes)
$price_pad = hash_hmac("sha1", $iv, $ekey, true);
$price = $p ^ $price_pad;// XOR
$conf_sig = substr(hex2bin(hash_hmac("sha1", $price . $iv, $ikey)), 0, 4);
if ($sig !== $conf_sig)
{
$reason = "Signature is not valid";
return false;
}
return hexdec(bin2hex($price)); //This is 8 byte binary. Dumps some binary on screen. Result should be a 8 byte number
}
$value = "[VALUE]"; //38 character base64
$ekey = "[ENCRYPTIONKEY]"; //64 byte hex encoded key . 32 byte key
$ikey "[INTEGRITYKEY]"; //64 byte hex encoded key . 32 byte key
var_dump(decrypt_google_winning_price($value, $ekey, $ikey));
Upvotes: 1
Reputation: 51
Your posted code should look like this
$value= "[VALUE]"; //38 character base64
$ekey=hextostr("[ENCRYPTIONKEY]"); //64 byte hex encoded key . 32 byte key
$ikey=hextostr("[INTEGRITYKEY]"); //64 byte hex encoded key . 32 byte key
$value=str_replace("-","+",$value);
$value=str_replace("_","/",$value);
$value=$value."==";
$dvalue=base64_decode($value); //Gets a 28 byte encrypted string.
$initvec=substr($dvalue,0,16);
$ciphertext=substr($dvalue,16,8);
$integritysig=substr($dvalue,24,4);
//here is the change
$pad=hash_hmac("sha1",$initvec,$ekey, true);
$uncipher=$ciphertext^$pad;
print(hexdec(strToHex($uncipher))); //This is 8 byte binary. Dumps some binary on screen. Result should be a 8 byte number
Upvotes: 1
Reputation: 13985
$pad=hash_hmac("sha1",$initvec,$ekey); // returns a hexstring, but XOR interprets
// as ASCII string and converts to binary
// accordingly
$ciphertext=substr($dvalue,16,8); // this is ASCII, converted to binary by XOR
$uncipher=$ciphertext^$pad; // so the XOR operation is confused in interpretation.
try changing it to,
function bin2asc($in)#syntax - bin2asc("binary to convert");
{
$out = '';
for ($i = 0, $len = strlen($in); $i < $len; $i += 8)
{
$out .= chr(bindec(substr($in,$i,8)));
}
return $out;
}
$pad= hash_hmac("sha1",$initvec,$ekey, true); // now it will return in binary
$pad = bin2asc($pad);
$uncipher=$ciphertext^$pad;
hope this solve your problem.
Upvotes: 1