Your IP : 18.117.107.188


Current Path : /var/www/www-root/data/www/monolith-realty.ru/bitrix/modules/main/lib/security/sign/
Upload File :
Current File : /var/www/www-root/data/www/monolith-realty.ru/bitrix/modules/main/lib/security/sign/timesigner.php

<?php
namespace Bitrix\Main\Security\Sign;

use Bitrix\Main\ArgumentException;
use Bitrix\Main\ArgumentTypeException;

/**
 * Class TimeSigner
 * @sine 14.0.7
 * @package Bitrix\Main\Security\Sign
 */
class TimeSigner
{
	/** @var Signer */
	protected $signer = null;

	/**
	 * Creates new TimeSigner object. If you want use your own signing algorithm - you can this
	 *
	 * @param SigningAlgorithm $algorithm Custom signing algorithm.
	 */
	public function __construct(SigningAlgorithm $algorithm = null)
	{
		$this->signer = new Signer($algorithm);
	}

	/**
	 * Set key for signing
	 *
	 * @param string $value Key.
	 * @return $this
	 * @throws \Bitrix\Main\ArgumentTypeException
	 */
	public function setKey($value)
	{
		$this->signer->setKey($value);
		return $this;
	}

	/**
	 * Return separator, used for packing/unpacking
	 *
	 * @return string
	 */
	public function getSeparator()
	{
		return $this->signer->getSeparator();
	}

	/**
	 * Set separator, used for packing/unpacking
	 *
	 * @param string $value Separator.
	 * @return $this
	 * @throws \Bitrix\Main\ArgumentTypeException
	 */
	public function setSeparator($value)
	{
		$this->signer->setSeparator($value);
		return $this;
	}

	/**
	 * Sign message with expired time, return string in format:
	 *  {message}{separator}{expired timestamp}{separator}{signature}
	 *
	 * Simple example:
	 * <code>
	 *  // If salt needed
	 *  $foo = (new TimeSigner)->sign('test', '+1 hour', 'my_salt');
	 *
	 *  // Otherwise
	 *  $bar = (new TimeSigner)->sign('test', '+1 day');
	 * </code>
	 *
	 * @param string $value Message for signing.
	 * @param string $time Timestamp or datetime description (presented in format accepted by strtotime).
	 * @param string|null $salt Salt, if needed.
	 * @return string
	 */
	public function sign($value, $time, $salt = null)
	{
		$timestamp = $this->getTimeStamp($time);
		$signature = $this->getSignature($value, $timestamp, $salt);
		return $this->pack(array($value, $timestamp, $signature));
	}

	/**
	 * Check message signature and it lifetime. If everything is OK - return original message.
	 *
	 * Simple example:
	 * <code>
	 *  $signer = new TimeSigner;
	 *
	 *  // Sing message for 1 second
	 *  $signedValue = $signer->sign('test', '+1 second');
	 *
	 *  // Or sign with expiring on some magic timestamp (e.g. 01.01.2030)
	 *  $signedValue = $signer->sign('test', 1893445200);
	 *
	 *  // Get original message with checking
	 *  echo $signer->unsign($signedValue);
	 *  // Output: 'test'
	 *
	 *  // Try to unsigning not signed value
	 *  echo $signer->unsign('test');
	 *  //throw BadSignatureException with message 'Separator not found in value'
	 *
	 *  // Or with invalid sign
	 *  echo $signer->unsign('test.invalid_sign');
	 *
	 *  // Or invalid salt
	 *  echo $signer->unsign($signedValue, 'invalid_salt');
	 *  //throw BadSignatureException with message 'Signature does not match'
	 *
	 *  // Or expired lifetime
	 *  echo $signer->unsign($signedValue);
	 *  //throw BadSignatureException with message 'Signature timestamp expired (1403039921 < 1403040024)'
	 *
	 * </code>
	 *
	 * @param string $signedValue  Signed value, must be in format: {message}{separator}{expired timestamp}{separator}{signature}.
	 * @param string|null $salt Salt, if used while signing.
	 * @return string
	 * @throws BadSignatureException
	 */
	public function unsign($signedValue, $salt = null)
	{
		$timedValue = $this->signer->unsign($signedValue, $salt);

		if (mb_strpos($signedValue, $timedValue) === false)
			throw new BadSignatureException('Timestamp missing');

		list($value, $time) = $this->unpack($timedValue);
		$time = (int) $time;

		if ($time <= 0)
			throw new BadSignatureException(sprintf('Malformed timestamp %d', $time));

		if ($time < time())
			throw new BadSignatureException(sprintf('Signature timestamp expired (%d < %d)', $time, time()));

		return $value;
	}

	/**
	 * Return message signature
	 *
	 * @param string $value Message.
	 * @param int $timestamp Expire timestamp.
	 * @param null $salt Salt (if needed).
	 * @return string
	 * @throws ArgumentTypeException
	 */
	public function getSignature($value, $timestamp, $salt = null)
	{
		if (!is_string($value))
			throw new ArgumentTypeException('value', 'string');

		$timedValue = $this->pack(array($value, $timestamp));
		return $this->signer->getSignature($timedValue, $salt);
	}

	/**
	 * Simply validation of message signature
	 *
	 * @param string $value Message.
	 * @param int $timestamp Expire timestamp.
	 * @param string $signature Signature.
	 * @param string|null $salt Salt, if used while signing.
	 * @return bool True if OK, otherwise - false.
	 */
	public function validate($value, $timestamp, $signature, $salt = null)
	{
		try
		{
			$signedValue = $this->pack(array($value, $timestamp, $signature));
			$this->unsign($signedValue, $salt);
			return true;
		}
		catch(BadSignatureException $e)
		{
			return false;
		}
	}

	/**
	 * Return timestamp parsed from English textual datetime description
	 *
	 * @param string|int $time Timestamp or datetime description (presented in format accepted by strtotime).
	 * @return int
	 * @throws \Bitrix\Main\ArgumentTypeException
	 * @throws \Bitrix\Main\ArgumentException
	 */
	protected function getTimeStamp($time)
	{
		if (!is_string($time) && !is_int($time))
			throw new ArgumentTypeException('time');

		if (is_string($time) && !is_numeric($time))
		{
			$timestamp = strtotime($time);
			if (!$timestamp)
				throw new ArgumentException(sprintf('Invalid time "%s" format. See "Date and Time Formats"', $time));
		}
		else
		{
			$timestamp = (int) $time;
		}

		if ($timestamp < time())
			throw new ArgumentException(sprintf('Timestamp %d must be greater than now()', $timestamp));

		return $timestamp;
	}

	/**
	 * Pack array values to single string:
	 * pack(['test', 'all', 'values']) -> 'test.all.values'
	 *
	 * @param array $values Values for packing.
	 * @return string
	 */
	public function pack(array $values)
	{
		return $this->signer->pack($values);
	}

	/**
	 * Unpack values from string (something like rsplit).
	 * Simple example for separator ".":
	 * <code>
	 *  // Unpack all values:
	 *  unpack('test.all.values', 0) -> ['test', 'all', 'values']
	 *
	 *  // Unpack 2 values (by default). First element containing the rest of string.
	 *  unpack('test.all.values') -> ['test.all', 'values']
	 *
	 *  // Exception if separator is missing
	 *  unpack('test.all values', 3) -> throws BadSignatureException
	 * </code>
	 *
	 * @param string $value String for unpacking.
	 * @param int $limit If $limit === 0 - unpack all values, default - 2.
	 * @return array
	 * @throws BadSignatureException
	 */
	public function unpack($value, $limit = 2)
	{
		return $this->signer->unpack($value, $limit);
	}
}