we are trying to implement a function P_SHA1 means PHP. The pattern of the function written in Python. But, unfortunately, something is not working properly. Here is the imp
I try to summarize others' answers, and give a simplified version of likeuntomurphy's excellent answer.
As Yarik stated, P_SHA1
is defined in TLS 1.0 standard as a PRF, and is incorporated into WS-SecureConversation 1.3. It's noteworthy that this bizarre function has been is replaced by a standard one called HKDF in the latest TLS standard, circa 2018.
Anyway, here's the simplified code of P_SHA-1:
function p_sha1(string $client_secret, string $server_secret, int $num_bytes): string
{
$key = base64_decode($client_secret);
$data = base64_decode($server_secret);
$buff = $data;
$ret = "";
while (strlen($ret) < $num_bytes) {
$buff = hash_hmac("SHA1", $buff, $key, TRUE);
$ret .= hash_hmac("SHA1", $buff . $data, $key, TRUE);
}
return substr($ret, 0, $num_bytes);
}
Are you looking for the readymade SHA1 function or something else? This will give you the SHA1 hash, and if you put true in the second argument, it'll give you it in binary. Otherwise, It will give it to you in hex
You should have mentioned, that it is an implementation of "WS-SecureConversation 1.3" and that it is not SHA1, obviously, but rather a variation of HMAC-SHA1 with protocol specifics.
It turns out to be something from TLS (RFC 2246):
We use a subset of the mechanism defined for TLS in RFC 2246. Specifically, we use the P_SHA-1 function to generate a sequence of bytes that can be used to generate security keys.
You are not first one to ask, for example this question is without answer RFC 2246 PRF function in PHP
This is based on the C# method included in a reply to "signing SOAP message request via ADFS". I have successfully used it to sign SOAP requests and get the response I want.
function psha1($clientSecret, $serverSecret, $sizeBits = 256)
{
$sizeBytes = $sizeBits / 8;
$hmacKey = $clientSecret;
$hashSize = 160; // HMAC_SHA1 length is always 160
$bufferSize = $hashSize / 8 + strlen($serverSecret);
$i = 0;
$b1 = $serverSecret;
$b2 = "";
$temp = null;
$psha = array();
while ($i < $sizeBytes) {
$b1 = hash_hmac('SHA1', $b1, $hmacKey, true);
$b2 = $b1 . $serverSecret;
$temp = hash_hmac('SHA1', $b2, $hmacKey, true);
for ($j = 0; $j < strlen($temp); $j++) {
if ($i < $sizeBytes) {
$psha[$i] = $temp[$j];
$i++;
} else {
break;
}
}
}
return implode("", $psha);
}
One thing of importance to note is that the client secret and server secret should be base64 decoded before being passed to this function.