| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226 | 
							- <?php
 - 
 - namespace Macaroons;
 - 
 - use Macaroons\Exceptions\SignatureMismatchException;
 - use Macaroons\Exceptions\CaveatUnsatisfiedException;
 - 
 - class Verifier
 - {
 -   private $predicates = array();
 -   private $callbacks = array();
 -   private $calculatedSignature;
 - 
 -   /**
 -    * return predicates to verify
 -    * @return Array|array
 -    */
 -   public function getPredicates()
 -   {
 -     return $this->predicates;
 -   }
 - 
 -   /**
 -    * returns verifier callbacks
 -    * @return Array|array
 -    */
 -   public function getCallbacks()
 -   {
 -     return $this->callbacks;
 -   }
 - 
 -   /**
 -    * sets array of predicates
 -    * @param Array $predicates
 -    */
 -   public function setPredicates(Array $predicates)
 -   {
 -     $this->predicates = $predicates;
 -   }
 - 
 -   /**
 -    * set array of callbacks
 -    * @param Array $callbacks
 -    */
 -   public function setCallbacks(Array $callbacks)
 -   {
 -     $this->callbacks = $callbacks;
 -   }
 - 
 -   /**
 -    * adds a predicate to the verifier
 -    * @param string
 -    */
 -   public function satisfyExact($predicate)
 -   {
 -     if (!isset($predicate))
 -       throw new \InvalidArgumentException('Must provide predicate');
 -     array_push($this->predicates, $predicate);
 -   }
 - 
 -   /**
 -    * adds a callback to array of callbacks
 -    * $callback can be anything that is callable including objects
 -    * that implement __invoke
 -    * See http://php.net/manual/en/language.types.callable.php for more details
 -    * @param function|object|array
 -    */
 -   public function satisfyGeneral($callback)
 -   {
 -     if (!isset($callback))
 -       throw new \InvalidArgumentException('Must provide a callback function');
 -     if (!is_callable($callback))
 -       throw new \InvalidArgumentException('Callback must be a function');
 -     array_push($this->callbacks, $callback);
 -   }
 - 
 -   /**
 -    * [verify description]
 -    * @param  Macaroon $macaroon
 -    * @param  string   $key
 -    * @param  Array    $dischargeMacaroons
 -    * @return boolean
 -    */
 -   public function verify(Macaroon $macaroon, $key, Array $dischargeMacaroons = array())
 -   {
 -     $key = Utils::generateDerivedKey($key);
 -     return $this->verifyDischarge(
 -                                   $macaroon,
 -                                   $macaroon,
 -                                   $key,
 -                                   $dischargeMacaroons
 -                                   );
 -   }
 - 
 -   /**
 -    * [verifyDischarge description]
 -    * @param  Macaroon    $rootMacaroon
 -    * @param  Macaroon    $macaroon
 -    * @param  string      $key
 -    * @param  Array|array $dischargeMacaroons
 -    * @return boolean|throws SignatureMismatchException
 -    */
 -   public function verifyDischarge(Macaroon $rootMacaroon, Macaroon $macaroon, $key, Array $dischargeMacaroons = array())
 -   {
 -     $this->calculatedSignature = Utils::hmac($key, $macaroon->getIdentifier());
 -     $this->verifyCaveats($macaroon, $dischargeMacaroons);
 - 
 -     if ($rootMacaroon != $macaroon)
 -     {
 -       $this->calculatedSignature = $rootMacaroon->bindSignature(strtolower(Utils::hexlify($this->calculatedSignature)));
 -     }
 - 
 -     $signature = Utils::unhexlify($macaroon->getSignature());
 -     if ($this->signaturesMatch($this->calculatedSignature, $signature) === FALSE)
 -     {
 -       throw new SignatureMismatchException('Signatures do not match.');
 -     }
 -     return true;
 -   }
 - 
 -   /**
 -    * verifies all first and third party caveats of macaroon are valid
 -    * @param  Macaroon
 -    * @param  Array
 -    */
 -   private function verifyCaveats(Macaroon $macaroon, Array $dischargeMacaroons = array())
 -   {
 -     foreach ($macaroon->getCaveats() as $caveat)
 -     {
 -       $caveatMet = false;
 -       if ($caveat->isFirstParty())
 -         $caveatMet = $this->verifyFirstPartyCaveat($caveat);
 -       else if ($caveat->isThirdParty())
 -         $caveatMet = $this->verifyThirdPartyCaveat($caveat, $macaroon, $dischargeMacaroons);
 -       if (!$caveatMet)
 -         throw new CaveatUnsatisfiedException("Caveat not met. Unable to satisfy: {$caveat->getCaveatId()}");
 -     }
 -   }
 - 
 -   private function verifyFirstPartyCaveat(Caveat $caveat)
 -   {
 -     $caveatMet = false;
 -     if (in_array($caveat->getCaveatId(), $this->predicates))
 -       $caveatMet = true;
 -     else
 -     {
 -       foreach ($this->callbacks as $callback)
 -       {
 -         if ($callback($caveat->getCaveatId()))
 -           $caveatMet = true;
 -       }
 -     }
 -     if ($caveatMet)
 -       $this->calculatedSignature = Utils::signFirstPartyCaveat($this->calculatedSignature, $caveat->getCaveatId());
 - 
 -     return $caveatMet;
 -   }
 - 
 -   private function verifyThirdPartyCaveat(Caveat $caveat, Macaroon $rootMacaroon, Array $dischargeMacaroons)
 -   {
 -     $caveatMet = false;
 - 
 -     $dischargesMatchingCaveat = array_filter($dischargeMacaroons, function($discharge) use ($rootMacaroon, $caveat) {
 -       return $discharge->getIdentifier() === $caveat->getCaveatId();
 -     });
 - 
 -     $caveatMacaroon = array_shift($dischargesMatchingCaveat);
 - 
 -     if (!$caveatMacaroon)
 -       throw new CaveatUnsatisfiedException("Caveat not met. No discharge macaroon found for identifier: {$caveat->getCaveatId()}");
 - 
 -     $caveatKey = $this->extractCaveatKey($this->calculatedSignature, $caveat);
 -     $caveatMacaroonVerifier = new Verifier();
 -     $caveatMacaroonVerifier->setPredicates($this->predicates);
 -     $caveatMacaroonVerifier->setCallbacks($this->callbacks);
 - 
 -     $caveatMet = $caveatMacaroonVerifier->verifyDischarge(
 -                                                           $rootMacaroon,
 -                                                           $caveatMacaroon,
 -                                                           $caveatKey,
 -                                                           $dischargeMacaroons
 -                                                           );
 -     if ($caveatMet)
 -     {
 -       $this->calculatedSignature = Utils::signThirdPartyCaveat(
 -                                                                 $this->calculatedSignature,
 -                                                                 $caveat->getVerificationId(),
 -                                                                 $caveat->getCaveatId()
 -                                                               );
 -     }
 - 
 -     return $caveatMet;
 -   }
 - 
 -   /**
 -    * returns the derived key from the caveat verification id
 -    * @param  string $signature
 -    * @param  Caveat $caveat
 -    * @return string
 -    */
 -   private function extractCaveatKey($signature, Caveat $caveat)
 -   {
 -     $verificationHash = $caveat->getVerificationId();
 -     $nonce            = substr($verificationHash, 0, \Sodium\CRYPTO_SECRETBOX_NONCEBYTES);
 -     $verificationId   = substr($verificationHash, \Sodium\CRYPTO_SECRETBOX_NONCEBYTES);
 -     $key              = Utils::truncateOrPad($signature);
 -     return \Sodium\crypto_secretbox_open($verificationId, $nonce, $key);
 -   }
 - 
 -   /**
 -    * compares the calculated signature of a macaroon and the macaroon supplied
 -    * by the client
 -    * The user supplied string MUST be the second argument or this will leak
 -    * the length of the actual signature
 -    * @param  string $a known signature from our key and macaroon metadata
 -    * @param  string $b signature from macaroon we are verifying (from the client)
 -    * @return boolean
 -    */
 -   private function signaturesMatch($a, $b)
 -   {
 -     $ret = strlen($a) ^ strlen($b);
 -     $ret |= array_sum(unpack("C*", $a^$b));
 - 
 -     return !$ret;
 -   }
 - }
 
 
  |