Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 52 additions & 1 deletion lib/Service/DiscoveryService.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ class DiscoveryService {

/**
*
* See https://www.imsglobal.org/spec/security/v1p1#approved-jwt-signing-algorithms.
* @var string[]
*/
private const SUPPORTED_JWK_ALGS = [
Expand Down Expand Up @@ -118,6 +117,55 @@ public function buildAuthorizationUrl(string $authorizationEndpoint, array $extr
. (empty($queryParams) ? '' : '?' . http_build_query($queryParams));
}

/**
* Validates the strength and correctness of a cryptographic key.
*
* This method checks:
* - RSA keys have a modulus of at least 2048 bits.
* - EC keys use one of the allowed curves: P-256, P-384, P-521.
* - EdDSA keys use the Ed25519 curve.
*
* @param array $key The key data as an associative array (JWK format).
* @param string $alg The algorithm intended to be used with this key (e.g., 'RS256', 'ES256').
*
* @throws \RuntimeException If the key is missing required fields, has an unsupported type,
* or does not meet the minimum security requirements.
*/
private function validateKeyStrength(array $key, string $alg): void {
$kty = $key['kty'] ?? throw new \RuntimeException('Key missing kty');

switch ($kty) {
case 'RSA':
if (empty($key['n'])) {
throw new \RuntimeException('RSA key missing modulus (n)');
}
$modulus = JWT::urlsafeB64Decode($key['n']);
$modulusBits = strlen($modulus) * 8;
if ($modulusBits < 2048) {
throw new \RuntimeException('RSA key too short: ' . $modulusBits . ' bits');
}
break;

case 'EC':
$curve = $key['crv'] ?? throw new \RuntimeException('EC key missing crv');
$allowedCurves = ['P-256', 'P-384', 'P-521'];
if (!in_array($curve, $allowedCurves, true)) {
throw new \RuntimeException('Unsupported EC curve: ' . $curve);
}
break;

case 'EdDSA':
$curve = $key['crv'] ?? throw new \RuntimeException('EdDSA key missing crv');
if ($curve !== 'Ed25519') {
throw new \RuntimeException('Unsupported EdDSA curve: ' . $curve);
}
break;

default:
throw new \RuntimeException('Unsupported key type: ' . $kty);
}
}

/**
* Inspired by https://github.com/snake/moodle/compare/880462a1685...MDL-77077-master
*
Expand Down Expand Up @@ -158,6 +206,9 @@ private function fixJwksAlg(array $jwks, string $jwt): array {
continue;
}

// Validate key strength
$this->validateKeyStrength($key, $alg);

// If JWT has a kid, match strictly
if ($kid !== null) {
if (($key['kid'] ?? null) !== $kid) {
Expand Down
Loading