ILIAS  trunk Revision v13.0_alpha-62-g3b72d83f2e5
ILIAS\WOPI\Handler\ProofValidator Class Reference

Verifies that a request was sent by the WOPI client, see https://learn.microsoft.com/en-us/microsoft-365/cloud-storage-partner-program/online/scenarios/proofkeys. More...

+ Collaboration diagram for ILIAS\WOPI\Handler\ProofValidator:

Public Member Functions

 __construct (private ?ProofKeys $keys, ?\Closure $clock=null)
 
 withKeys (ProofKeys $keys)
 
 isValid (string $access_token, string $url, ?string $timestamp, ?string $proof, ?string $old_proof)
 

Static Public Member Functions

static expected (string $access_token, string $url, int $timestamp)
 

Private Member Functions

 verify (string $expected, string $signature, ProofKey $key)
 

Private Attributes

const TICKS_AT_UNIX_EPOCH = 621_355_968_000_000_000
 X-WOPI-TimeStamp counts the 100 nanosecond intervals since 0001-01-01 (.NET ticks). More...
 
const TICKS_PER_SECOND = 10_000_000
 
const MAX_AGE = 20 * 60
 
Closure $clock
 

Detailed Description

Verifies that a request was sent by the WOPI client, see https://learn.microsoft.com/en-us/microsoft-365/cloud-storage-partner-program/online/scenarios/proofkeys.

The client signs the access token, the URL and a timestamp of each request with its private key. Without proof keys in the discovery there is nothing to verify against, every request is accepted then.

Author
Fabian Schmid fabia.nosp@m.n@sr.nosp@m..solu.nosp@m.tion.nosp@m.s

Definition at line 36 of file ProofValidator.php.

Constructor & Destructor Documentation

◆ __construct()

ILIAS\WOPI\Handler\ProofValidator::__construct ( private ?ProofKeys  $keys,
?\Closure  $clock = null 
)
Parameters

Closure(): int|null $clock returns the current unix timestamp, for testing

Definition at line 53 of file ProofValidator.php.

56 {
57 $this->clock = $clock ?? static fn(): int => time();
58 }

References ILIAS\WOPI\Handler\ProofValidator\$clock.

Member Function Documentation

◆ expected()

static ILIAS\WOPI\Handler\ProofValidator::expected ( string  $access_token,
string  $url,
int  $timestamp 
)
static

Definition at line 99 of file ProofValidator.php.

99 : string
100 {
101 $url = strtoupper($url);
102
103 return pack('N', strlen($access_token)) . $access_token
104 . pack('N', strlen($url)) . $url
105 . pack('N', 8) . pack('J', $timestamp);
106 }
foreach($mandatory_scripts as $file) $timestamp
Definition: buildRTE.php:70

References $timestamp.

Referenced by ILIAS\WOPI\Handler\ProofValidatorTest\sign(), ILIAS\WOPI\Handler\RequestHandlerTest\signedHeaders(), and ILIAS\WOPI\Handler\RequestHandlerTest\testRequestWithValidProofIsAccepted().

+ Here is the caller graph for this function:

◆ isValid()

ILIAS\WOPI\Handler\ProofValidator::isValid ( string  $access_token,
string  $url,
?string  $timestamp,
?string  $proof,
?string  $old_proof 
)
Parameters
string$urlthe URL the client requested, including the query string

Definition at line 71 of file ProofValidator.php.

77 : bool {
78 if ($this->keys === null) {
79 return true;
80 }
81 if ($timestamp === null || $proof === null || !ctype_digit($timestamp)) {
82 return false;
83 }
84 $age = ($this->clock)() - intdiv((int) $timestamp - self::TICKS_AT_UNIX_EPOCH, self::TICKS_PER_SECOND);
85 if (abs($age) > self::MAX_AGE) {
86 return false;
87 }
88
89 $expected = self::expected($access_token, $url, (int) $timestamp);
90
91 // the current key signed the proof, or it signed the old proof (the host has the new key,
92 // the client still signs with the old one as well), or the old key signed the proof
93 // (the client has rotated its keys and the host has not seen the new discovery yet)
94 return $this->verify($expected, $proof, $this->keys->current)
95 || ($old_proof !== null && $this->verify($expected, $old_proof, $this->keys->current))
96 || ($this->keys->old !== null && $this->verify($expected, $proof, $this->keys->old));
97 }
verify(string $expected, string $signature, ProofKey $key)
static expected(string $access_token, string $url, int $timestamp)

Referenced by ILIAS\WOPI\Handler\RequestHandler\hasValidProof().

+ Here is the caller graph for this function:

◆ verify()

ILIAS\WOPI\Handler\ProofValidator::verify ( string  $expected,
string  $signature,
ProofKey  $key 
)
private

Definition at line 108 of file ProofValidator.php.

108 : bool
109 {
110 $decoded = base64_decode($signature, true);
111 if ($decoded === false || $decoded === '') {
112 return false;
113 }
114 try {
115 $public_key = openssl_pkey_get_public($key->toPem());
116 } catch (\InvalidArgumentException) {
117 return false;
118 }
119 if ($public_key === false) {
120 return false;
121 }
122
123 return openssl_verify($expected, $decoded, $public_key, OPENSSL_ALGO_SHA256) === 1;
124 }

References ILIAS\WOPI\Discovery\ProofKey\toPem().

+ Here is the call graph for this function:

◆ withKeys()

ILIAS\WOPI\Handler\ProofValidator::withKeys ( ProofKeys  $keys)

Definition at line 60 of file ProofValidator.php.

60 : self
61 {
62 $clone = clone $this;
63 $clone->keys = $keys;
64
65 return $clone;
66 }

Field Documentation

◆ $clock

Closure ILIAS\WOPI\Handler\ProofValidator::$clock
private

Definition at line 48 of file ProofValidator.php.

Referenced by ILIAS\WOPI\Handler\ProofValidator\__construct().

◆ MAX_AGE

const ILIAS\WOPI\Handler\ProofValidator::MAX_AGE = 20 * 60
private

Definition at line 43 of file ProofValidator.php.

◆ TICKS_AT_UNIX_EPOCH

const ILIAS\WOPI\Handler\ProofValidator::TICKS_AT_UNIX_EPOCH = 621_355_968_000_000_000
private

X-WOPI-TimeStamp counts the 100 nanosecond intervals since 0001-01-01 (.NET ticks).

Definition at line 41 of file ProofValidator.php.

◆ TICKS_PER_SECOND

const ILIAS\WOPI\Handler\ProofValidator::TICKS_PER_SECOND = 10_000_000
private

Definition at line 42 of file ProofValidator.php.


The documentation for this class was generated from the following file: