Search code examples
phpxmppsaslsasl-scram

SCRAM-SHA-1 auth by PHP


help me please with implementing semantic code from manual about SCRAM-SHA-1 authorization in XMPP server. So, we got:

clientFinalMessageBare = "c=biws,r=" .. serverNonce
saltedPassword = PBKDF2-SHA-1(normalizedPassword, salt, i)
clientKey = HMAC-SHA-1(saltedPassword, "Client Key")
storedKey = SHA-1(clientKey)
authMessage = initialMessage .. "," .. serverFirstMessage .. "," .. clientFinalMessageBare
clientSignature = HMAC-SHA-1(storedKey, authMessage)
clientProof = clientKey XOR clientSignature
clientFinalMessage = clientFinalMessageBare .. ",p=" .. base64(clientProof)

My PHP code:

$cfmb = 'c=biws,r='.$salt;     
$saltpass = hash_pbkdf2('sha1', 'IDoMdGuFE9S0', $ps, $iter);        
//hash_pbkdf2('sha1', 'IDoMdGuFE9S0', $salt, $iter, 0, true); maybe like that???
$ckey = hash_hmac('sha1', $saltpass, 'Client Key');
$sckey = sha1($ckey);
$authmsg = $im.','.$chal.','.$cfmb;
$csign = hash_hmac('sha1', $sckey, $authmsg);
$cproof = bin2hex(pack('H*',$ckey) ^ pack('H*',$csign));
$cfm = $cfmb.',p='.base64_encode($cproof);

Somewhere error (maybe ALL big error ;)) and I very need your help for correcting my code, maybe I am use wrong functions, or arguments in wrong positions? Because result - fail, server sends me that:

"The response provided by the client doesn't match the one we calculated."

PS: Sorry for my bad English ;)


Solution

  • First of all, it's very confusing to use $salt for the serverNonce and $ps for the salt.

    But more importantly, you should take some care to keep track of whether the functions you use return binary data or hexadecimal encoded strings. hash_pbkdf2, sha1 and hash_hmac by default return hexadecimal encoded strings. You call pack('H*', ...) to decode them for the $cproof, but not when you calculate $ckey and $csign.

    A much easier way is to compute binary data directly, by always passing $raw_data = TRUE:

    $cfmb = 'c=biws,r='.$salt;     
    $saltpass = hash_pbkdf2('sha1', 'IDoMdGuFE9S0', $ps, $iter, 0, TRUE);        
    $ckey = hash_hmac('sha1', 'Client Key', $saltpass, TRUE);
    $sckey = sha1($ckey, TRUE);
    $authmsg = $im.','.$chal.','.$cfmb;
    $csign = hash_hmac('sha1', $authmsg, $sckey, TRUE);
    $cproof = $ckey ^ $csign;
    $cfm = $cfmb.',p='.base64_encode($cproof);
    

    Also, your hash_hmac calls were the wrong way around: first the data, then the key.