Subversion Repositories oidplus

Rev

Blame | Last modification | View Log | RSS feed

  1. <?php
  2. /**
  3.  * SBrook\JWS\Jws
  4.  */
  5.  
  6. namespace SBrook\JWS;
  7.  
  8. use SBrook\JWS\Exception\JwsException;
  9.  
  10. /**
  11.  * Class Jws
  12.  * @package SBrook\JWS
  13.  * @throws JwsException:
  14.  *  Encode:
  15.  *   10. Header should be an array
  16.  *   11. Payload should be a non empty string
  17.  *   12. Unknown signature algorithm in header
  18.  *  Decode:
  19.  *   20. JWS should be a non empty string
  20.  *   21. Invalid JWS header
  21.  *   22. Error while decoding JWS header
  22.  *   23. Error while decoding JWS payload
  23.  */
  24. abstract class Jws {
  25.         /**
  26.          * Create JWS from payload and optional header and sign it.
  27.          * @param $payload - Payload.
  28.          * @param $header - Header data.
  29.          */
  30.         abstract public function sign($payload, $header);
  31.  
  32.         /**
  33.          * Verify JWS signature.
  34.          * @param $jws - JWS.
  35.          */
  36.         abstract public function verify($jws);
  37.  
  38.         /**
  39.          * Check validity of signature algorithm.
  40.          * @param string $algorithm - Algorithm name.
  41.          * @return bool - TRUE on valid algorithm, FALSE on invalid.
  42.          */
  43.         abstract protected function isValidAlgorithm(string $algorithm): bool;
  44.  
  45.         /**
  46.          * Get JWS header.
  47.          * @param string $jws - JWS.
  48.          * @return array - Decoded JWS header.
  49.          * @throws JwsException
  50.          */
  51.         public function getHeader($jws) {
  52.                 if (is_string($jws) && strlen($jws) > 0) {
  53.                         list($h, , ) = explode(".", $jws);
  54.                         $header = json_decode(base64_decode($h, true), true);
  55.                         if (is_null($header)) {
  56.                                 throw new JwsException("Error while decoding JWS header", 22);
  57.                         } else {
  58.                                 return $header;
  59.                         }
  60.                 } else {
  61.                         throw new JwsException("JWS should be a non empty string", 20);
  62.                 }
  63.         }
  64.  
  65.         /**
  66.          * Get JWS payload.
  67.          * @param string $jws - JWS.
  68.          * @return string - Decoded JWS payload.
  69.          * @throws JwsException
  70.          */
  71.         public function getPayload($jws) {
  72.                 if (is_string($jws) && strlen($jws) > 0) {
  73.                         list(, $p, ) = explode(".", $jws);
  74.                         $payload = base64_decode($p, true);
  75.                         if ($payload) {
  76.                                 return $payload;
  77.                         } else {
  78.                                 throw new JwsException("Error while decoding JWS payload", 23);
  79.                         }
  80.                 } else {
  81.                         throw new JwsException("JWS should be a non empty string", 20);
  82.                 }
  83.         }
  84.  
  85.         /**
  86.          * Validate and prepare data to sign JWS.
  87.          * @param string $defaultAlgo - Default signature algorithm name.
  88.          * @param string $payload - Payload.
  89.          * @param array $header - Header data.
  90.          * @return array - Required data to sign JWS.
  91.          * @throws JwsException
  92.          */
  93.         protected function prepareSign($defaultAlgo, $payload, $header): array {
  94.                 if (is_array($header)) {
  95.                         if (is_string($payload) && strlen($payload) > 0) {
  96.                                 // Remove header parameters with empty string values:
  97.                                 foreach ($header as $key => $value) {
  98.                                         if (is_string($value) && strlen($value) == 0) {
  99.                                                 unset($header[$key]);
  100.                                         }
  101.                                 }
  102.  
  103.                                 // If not specified, set default signature algorithm:
  104.                                 if (!array_key_exists("alg", $header)) {
  105.                                         $header["alg"] = $defaultAlgo;
  106.                                 }
  107.  
  108.                                 // Don't trust anyone:
  109.                                 $header["alg"] = strtoupper($header["alg"]);
  110.  
  111.                                 if ($this->isValidAlgorithm($header["alg"])) {
  112.                                         return [
  113.                                                 "alg" => $header["alg"],
  114.                                                 "h" => base64_encode(json_encode($header)),
  115.                                                 "p" => base64_encode($payload)
  116.                                         ];
  117.                                 } else {
  118.                                         throw new JwsException("Unknown signature algorithm in header", 12);
  119.                                 }
  120.                         } else {
  121.                                 throw new JwsException("Payload should be a non empty string", 11);
  122.                         }
  123.                 } else {
  124.                         throw new JwsException("Header should be an array", 10);
  125.                 }
  126.         }
  127.  
  128.         /**
  129.          * Validate and prepare data to verify JWS.
  130.          * @param string $jws - JWS.
  131.          * @return array - Required data to verify JWS.
  132.          * @throws JwsException
  133.          */
  134.         protected function prepareVerify($jws): array {
  135.                 if (is_string($jws) && strlen(trim($jws)) > 0) {
  136.                         list($h, $p, $s) = explode(".", $jws);
  137.                         $header = json_decode(base64_decode($h, true), true);
  138.  
  139.                         if (is_array($header) && array_key_exists("alg", $header) && $this->isValidAlgorithm($header["alg"])) {
  140.                                 return [
  141.                                         "alg" => strtoupper($header["alg"]),
  142.                                         "sig" => base64_decode($s),
  143.                                         "h" => $h,
  144.                                         "p" => $p
  145.                                 ];
  146.                         } else {
  147.                                 throw new JwsException("Invalid JWS header", 21);
  148.                         }
  149.                 } else {
  150.                         throw new JwsException("JWS should be a non empty string", 20);
  151.                 }
  152.         }
  153. }
  154.