Files

102 lines
3.1 KiB
PHP

<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Core\Security;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
/**
* Trait providing support for JWT using symmetric hash signing.
*
* The benefit of using a trait in this particular case is, that defaults in `self::class`
* (used as context during key derivation) are specific to a particular implementation.
*
* @internal
*/
trait JwtTrait
{
private static function getDefaultSigningAlgorithm(): string
{
return 'HS256';
}
private static function deriveKey(
#[\SensitiveParameter]
string $baseKey,
string $context,
): Key {
$jwtAlgo = self::getDefaultSigningAlgorithm();
[$hashAlgo, $length] = match ($jwtAlgo) {
'HS256' => ['sha256', 32],
'HS384' => ['sha384', 48],
'HS512' => ['sha512', 64],
default => throw new \InvalidArgumentException('Unsupported JWT algorithm: ' . $jwtAlgo, 1774954888),
};
return new Key(
hash_hkdf(
algo: $hashAlgo,
key: $baseKey,
length: $length,
info: $context,
),
$jwtAlgo
);
}
private static function createSigningKeyFromEncryptionKey(string $context = self::class): Key
{
return self::deriveKey(
$GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'] ?? '',
$context === '' ? self::class : $context
);
}
private static function createSigningSecret(SigningSecretInterface $secret, string $context = self::class): Key
{
return self::deriveKey(
$secret->getSigningSecret(),
$context === '' ? self::class : $context
);
}
private static function encodeHashSignedJwt(array $payload, Key $key, ?SecretIdentifier $identifier = null): string
{
$keyId = $identifier !== null ? json_encode($identifier) : null;
return JWT::encode($payload, $key->getKeyMaterial(), $key->getAlgorithm(), $keyId);
}
private static function decodeJwt(string $jwt, Key $key, bool $associative = false): \stdClass|array
{
$payload = JWT::decode($jwt, $key);
return $associative ? json_decode(json_encode($payload), true) : $payload;
}
private static function decodeJwtHeader(string $jwt, string $property): mixed
{
$parts = explode('.', $jwt);
if (count($parts) !== 3) {
return null;
}
$headerRaw = JWT::urlsafeB64Decode($parts[0]);
if (($header = JWT::jsonDecode($headerRaw)) === null) {
return null;
}
return $header->{$property} ?? null;
}
}