1

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 implementation function in JAVA: http://ws.apache.org/wss4j/xref/org/apache/ws/security/conversation/dkalgo/P_SHA1.html

our code:

<?php
  $newSeed    = $label . $seed; // concat as strings
  // $p_sha1
  $psha1 = p_hash('sha1', $secret, $newSeed, $length);
  $string = arrayToBytes($psha1);
  /**
  * P_SHA1 crypto alg calculation
  *
  * @return array of bytes - key
  **/
  function p_hash($algo, $secret, $seed, $length) {
    $bytes = array_fill(0, $length, 0); 
    $tmp = null;
    $A = $seed;
    $index = 0;

    while (1) {
      // hmac sha1: secret + seed
      $A = hash_hmac($algo, $secret, $A, true);

      // hmac sha1: secret + 1st hash + seed
      $output = hash_hmac($algo, $secret, ($A . $seed), true);

      foreach (bytesToArray($output) as $c) {
          if ($index >= $length) {
              return $bytes;
          }

          $bytes[$index] = $c;
          $index++;
      }
    }
    return $bytes;
}

function bytesToArray($bytes) { return unpack('C*', $bytes); }
function arrayToBytes($array) { return call_user_func_array("pack", array_merge(array("C*"), $array)); }
?>

Maybe someone knows where I can find a ready-made solution? Or anyone can help make a script to work properly?

Grigory
  • 23
  • 5
  • You want binary representation of SHA1 hash? It's `$binary = sha1('your string', true);` – N.B. Oct 25 '13 at 13:04

4 Answers4

3

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.

likeuntomurphy
  • 422
  • 1
  • 5
  • 15
1

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

Community
  • 1
  • 1
Yarik
  • 398
  • 4
  • 12
  • Thanks for the clarification and welcome to stackoverflow! Note that the question you pointed to only used the [tag:PHP] tag and did not use the [tag:cryptography] tag, so it is unlikely to have drawn much attention. – Maarten Bodewes Oct 25 '13 at 14:48
0

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);
}
Community
  • 1
  • 1
Sadeq Dousti
  • 3,346
  • 6
  • 35
  • 53
-1

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

scrblnrd3
  • 7,228
  • 9
  • 33
  • 64