ILIAS  trunk Revision v13.0_alpha-62-g3b72d83f2e5
ILIAS\Authentication\SessionReminder\TokenCipher Class Reference

Encodes the opaque token the session reminder sends to sessioncheck.php. More...

+ Collaboration diagram for ILIAS\Authentication\SessionReminder\TokenCipher:

Public Member Functions

 __construct (private string $key)
 
 encode (int $user_id, string $session_hash)
 
 decode (string $token)
 

Static Public Member Functions

static fromKeyFile ()
 

Private Member Functions

 deriveSubkey (string $salt)
 

Private Attributes

const string CIPHER = 'aes-256-gcm'
 
const int KEY_LENGTH = 32
 
const int SALT_LENGTH = 16
 
const int NONCE_LENGTH = 12
 
const int TAG_LENGTH = 16
 
const string CONTEXT = 'ilias_session_reminder_v1'
 Binds the subkeys and the ciphertext to their purpose, a token of another context cannot be decrypted. More...
 
const string USER_ID = 'u'
 
const string SESSION_HASH = 'h'
 

Detailed Description

Encodes the opaque token the session reminder sends to sessioncheck.php.

The token is encrypted (authenticated encryption, AES-256-GCM), so the client can neither read nor modify the contained user id and session hash.

Each token is encrypted with its own subkey, derived from the secret key and a random salt (HKDF). This way the limited number of encryptions per key (because of the 96 bit nonce of GCM) does not apply to the secret key, which is never replaced.

Token format (base64url): salt (16 bytes) | nonce (12 bytes) | ciphertext | authentication tag (16 bytes)

Definition at line 36 of file TokenCipher.php.

Constructor & Destructor Documentation

◆ __construct()

ILIAS\Authentication\SessionReminder\TokenCipher::__construct ( private string  $key)
Parameters
string$keyBinary key

Definition at line 53 of file TokenCipher.php.

55 {
56 if (\strlen($this->key) !== self::KEY_LENGTH) {
57 throw new \InvalidArgumentException('Invalid key length.');
58 }
59 }

Member Function Documentation

◆ decode()

ILIAS\Authentication\SessionReminder\TokenCipher::decode ( string  $token)
Returns
array{user_id: int, session_hash: string}|null NULL if the token is invalid

Definition at line 113 of file TokenCipher.php.

113 : ?array
114 {
115 if (preg_match('/^[A-Za-z0-9_-]+$/', $token) !== 1) {
116 return null;
117 }
118
119 $raw = base64_decode(strtr($token, '-_', '+/'), true);
120 if ($raw === false || \strlen($raw) <= self::SALT_LENGTH + self::NONCE_LENGTH + self::TAG_LENGTH) {
121 return null;
122 }
123
124 $salt = substr($raw, 0, self::SALT_LENGTH);
125 $nonce = substr($raw, self::SALT_LENGTH, self::NONCE_LENGTH);
126 $ciphertext = substr($raw, self::SALT_LENGTH + self::NONCE_LENGTH, -self::TAG_LENGTH);
127 $tag = substr($raw, -self::TAG_LENGTH);
128
129 // Fails if the token has been modified (authentication tag)
130 $plaintext = openssl_decrypt(
131 $ciphertext,
132 self::CIPHER,
133 $this->deriveSubkey($salt),
134 OPENSSL_RAW_DATA,
135 $nonce,
136 $tag,
137 self::CONTEXT
138 );
139 if ($plaintext === false) {
140 return null;
141 }
142
143 $data = json_decode($plaintext, true, 512, JSON_THROW_ON_ERROR);
144 if (!\is_int($data[self::USER_ID] ?? null) || !\is_string($data[self::SESSION_HASH] ?? null)) {
145 return null;
146 }
147
148 return [
149 'user_id' => $data[self::USER_ID],
150 'session_hash' => $data[self::SESSION_HASH],
151 ];
152 }
$data
Definition: storeScorm.php:45
$token
Definition: xapitoken.php:69

References $data, $token, ILIAS\Authentication\SessionReminder\TokenCipher\deriveSubkey(), ILIAS\Authentication\SessionReminder\TokenCipher\SESSION_HASH, and ILIAS\Authentication\SessionReminder\TokenCipher\USER_ID.

+ Here is the call graph for this function:

◆ deriveSubkey()

ILIAS\Authentication\SessionReminder\TokenCipher::deriveSubkey ( string  $salt)
private

Definition at line 154 of file TokenCipher.php.

154 : string
155 {
156 return hash_hkdf('sha256', $this->key, self::KEY_LENGTH, self::CONTEXT, $salt);
157 }

Referenced by ILIAS\Authentication\SessionReminder\TokenCipher\decode(), and ILIAS\Authentication\SessionReminder\TokenCipher\encode().

+ Here is the caller graph for this function:

◆ encode()

ILIAS\Authentication\SessionReminder\TokenCipher::encode ( int  $user_id,
string  $session_hash 
)

Definition at line 85 of file TokenCipher.php.

85 : string
86 {
87 $salt = random_bytes(self::SALT_LENGTH);
88 $nonce = random_bytes(self::NONCE_LENGTH);
89 $tag = '';
90 $ciphertext = openssl_encrypt(
91 json_encode([
92 self::USER_ID => $user_id,
93 self::SESSION_HASH => $session_hash,
94 ], JSON_THROW_ON_ERROR),
95 self::CIPHER,
96 $this->deriveSubkey($salt),
97 OPENSSL_RAW_DATA,
98 $nonce,
99 $tag,
100 self::CONTEXT,
101 self::TAG_LENGTH
102 );
103 if ($ciphertext === false) {
104 throw new \RuntimeException('Could not encrypt the session reminder token.');
105 }
106
107 return rtrim(strtr(base64_encode($salt . $nonce . $ciphertext . $tag), '+/', '-_'), '=');
108 }

References $user_id, and ILIAS\Authentication\SessionReminder\TokenCipher\deriveSubkey().

+ Here is the call graph for this function:

◆ fromKeyFile()

static ILIAS\Authentication\SessionReminder\TokenCipher::fromKeyFile ( )
static
Returns
self|null NULL if the key has not been created, yet (see: SessionReminderKeyCreatedObjective), or the cipher is not available

Definition at line 65 of file TokenCipher.php.

65 : ?self
66 {
68 if (!\extension_loaded('openssl')
69 || !is_readable($path)
70 || !\in_array(self::CIPHER, openssl_get_cipher_methods(), true)) {
71 return null;
72 }
73
74 $data = require $path;
75 $key = \is_array($data) && \is_string($data[SessionReminderKeyCreatedObjective::KEY] ?? null)
77 : false;
78 if ($key === false || \strlen($key) !== self::KEY_LENGTH) {
79 return null;
80 }
81
82 return new self($key);
83 }

References $data, ILIAS\Authentication\Setup\SessionReminderKeyCreatedObjective\KEY, and ILIAS\FileDelivery\Setup\BuildStaticConfigStoredObjective\PATH().

+ Here is the call graph for this function:

Field Documentation

◆ CIPHER

const string ILIAS\Authentication\SessionReminder\TokenCipher::CIPHER = 'aes-256-gcm'
private

Definition at line 38 of file TokenCipher.php.

◆ CONTEXT

const string ILIAS\Authentication\SessionReminder\TokenCipher::CONTEXT = 'ilias_session_reminder_v1'
private

Binds the subkeys and the ciphertext to their purpose, a token of another context cannot be decrypted.

Definition at line 46 of file TokenCipher.php.

◆ KEY_LENGTH

const int ILIAS\Authentication\SessionReminder\TokenCipher::KEY_LENGTH = 32
private

Definition at line 39 of file TokenCipher.php.

◆ NONCE_LENGTH

const int ILIAS\Authentication\SessionReminder\TokenCipher::NONCE_LENGTH = 12
private

Definition at line 41 of file TokenCipher.php.

◆ SALT_LENGTH

const int ILIAS\Authentication\SessionReminder\TokenCipher::SALT_LENGTH = 16
private

Definition at line 40 of file TokenCipher.php.

◆ SESSION_HASH

const string ILIAS\Authentication\SessionReminder\TokenCipher::SESSION_HASH = 'h'
private

◆ TAG_LENGTH

const int ILIAS\Authentication\SessionReminder\TokenCipher::TAG_LENGTH = 16
private

Definition at line 42 of file TokenCipher.php.

◆ USER_ID

const string ILIAS\Authentication\SessionReminder\TokenCipher::USER_ID = 'u'
private

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