Skip to content

Commit

Permalink
refactor: WopiProofValidator - Make sure timestamp is properly chec…
Browse files Browse the repository at this point in the history
…ked.
  • Loading branch information
drupol committed Sep 6, 2021
1 parent 06d808b commit f4e8b78
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 6 deletions.
1 change: 1 addition & 0 deletions composer.json
Expand Up @@ -10,6 +10,7 @@
"php": ">= 7.4",
"ext-SimpleXML": "*",
"ext-json": "*",
"ext-openssl": "*",
"loophp/psr17": "^1.0",
"phpseclib/phpseclib": "^3.0",
"psr/cache": "^1.0 || ^2.0 || ^3.0",
Expand Down
10 changes: 8 additions & 2 deletions spec/ChampsLibres/WopiLib/Service/WopiProofValidatorSpec.php
Expand Up @@ -10,7 +10,9 @@
namespace spec\ChampsLibres\WopiLib\Service;

use ChampsLibres\WopiLib\Discovery\WopiDiscoveryInterface;
use ChampsLibres\WopiLib\Service\Contract\ClockInterface;
use ChampsLibres\WopiLib\Service\WopiProofValidator;
use DateTimeImmutable;
use PhpSpec\ObjectBehavior;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\UriInterface;
Expand Down Expand Up @@ -53,7 +55,7 @@ public function it_is_initializable()
$this->shouldHaveType(WopiProofValidator::class);
}

public function let(WopiDiscoveryInterface $wopiDiscovery)
public function let(WopiDiscoveryInterface $wopiDiscovery, ClockInterface $clock)
{
$wopiDiscovery
->getPublicKey()
Expand All @@ -63,6 +65,10 @@ public function let(WopiDiscoveryInterface $wopiDiscovery)
->getPublicKeyOld()
->willReturn('BgIAAACkAABSU0ExAAgAAAEAAQCZf5Q9uM8yf7qPrZZr+Ou3i0e/0G2n8/o7ahMvHA/Xn1cvcQgoMdWKk/EJElrcG5aK9qWXqBsSE3mj49eq+D8uoUJKnsTnrC1FksmIp1YLqLdD7MDq1BvKHyBM5Nc1v/St7MfrRKD5f2UUzosbiVf8cZxdsTjqFYm3xTE2fu+gH/ug3IwbtqL3Uf0UcTyBC/RpCcX7GCEslqjr3TCuV0v8TqyQ6dKQWgEsTkymCTn2qWMLoBoNUpPKAXU5bl9to/VxMW7IVr0+6RRTX+rIqKcVaR4GNHpXHIwzjNwLMjZgfavYFqTZ3ul0j5evL4nrP3jTHVY320XhkU857eBjWWPN');

$this->beConstructedWith($wopiDiscovery);
$clock
->now()
->willReturn((new DateTimeImmutable())->setTimestamp(1630872600));

$this->beConstructedWith($wopiDiscovery, $clock);
}
}
21 changes: 21 additions & 0 deletions src/Service/Clock/SystemClock.php
@@ -0,0 +1,21 @@
<?php

/**
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/

declare(strict_types=1);

namespace ChampsLibres\WopiLib\Service\Clock;

use ChampsLibres\WopiLib\Service\Contract\ClockInterface;
use DateTimeImmutable;

final class SystemClock implements ClockInterface
{
public function now(): DateTimeImmutable
{
return new DateTimeImmutable();
}
}
20 changes: 20 additions & 0 deletions src/Service/Contract/ClockInterface.php
@@ -0,0 +1,20 @@
<?php

/**
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/

declare(strict_types=1);

namespace ChampsLibres\WopiLib\Service\Contract;

use DateTimeImmutable;

interface ClockInterface
{
/**
* Returns the current time as a DateTimeImmutable Object.
*/
public function now(): DateTimeImmutable;
}
21 changes: 17 additions & 4 deletions src/Service/WopiProofValidator.php
Expand Up @@ -10,7 +10,9 @@
namespace ChampsLibres\WopiLib\Service;

use ChampsLibres\WopiLib\Discovery\WopiDiscoveryInterface;
use ChampsLibres\WopiLib\Service\Contract\ClockInterface;
use ChampsLibres\WopiLib\Service\Contract\WopiProofValidatorInterface;
use DateTimeImmutable;
use phpseclib3\Crypt\PublicKeyLoader;
use Psr\Http\Message\RequestInterface;
use Throwable;
Expand All @@ -21,18 +23,29 @@

final class WopiProofValidator implements WopiProofValidatorInterface
{
private ClockInterface $clock;

private WopiDiscoveryInterface $wopiDiscovery;

public function __construct(WopiDiscoveryInterface $wopiDiscovery)
public function __construct(WopiDiscoveryInterface $wopiDiscovery, ClockInterface $clock)
{
$this->wopiDiscovery = $wopiDiscovery;
$this->clock = $clock;
}

public function isValid(RequestInterface $request): bool
{
$timestamp = $request->getHeaderLine('X-WOPI-Timestamp');

// Ensure that the X-WOPI-TimeStamp header is no more than 20 minutes old.
$date = (new DateTimeImmutable())->setTimestamp((int) (((float) $timestamp - 621355968000000000) / 10000000));

if (20 * 60 < ($this->clock->now()->getTimestamp() - $date->getTimestamp())) {
return false;
}

$xWopiProof = $request->getHeaderLine('X-WOPI-Proof');
$xWopiProofOld = $request->getHeaderLine('X-WOPI-ProofOld');
$timestamp = $request->getHeaderLine('X-WOPI-Timestamp');

$key = $this->wopiDiscovery->getPublicKey();
$keyOld = $this->wopiDiscovery->getPublicKeyOld();
Expand Down Expand Up @@ -69,8 +82,8 @@ private function verify(string $expected, string $proof, string $key): bool

return 1 === openssl_verify(
$expected,
base64_decode($proof, true),
$key,
(string) base64_decode($proof, true),
$key->toString('pkcs8'),
OPENSSL_ALGO_SHA256
);
}
Expand Down

0 comments on commit f4e8b78

Please sign in to comment.