Skip to content

Commit 81ed59e

Browse files
Add key size validation (#612)
Co-authored-by: Marius Klocke <marius.klocke@gmail.com>
1 parent c03036f commit 81ed59e

File tree

4 files changed

+28
-33
lines changed

4 files changed

+28
-33
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ $passphrase = '[YOUR_PASSPHRASE]';
186186
// Can be generated with "ssh-keygen -t rsa -m pem"
187187
$privateKeyFile = '/path/to/key-with-passphrase.pem';
188188

189-
// Create a private key of type "resource"
189+
/** @var OpenSSLAsymmetricKey $privateKey */
190190
$privateKey = openssl_pkey_get_private(
191191
file_get_contents($privateKeyFile),
192192
$passphrase

src/JWT.php

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ public static function decode(
198198
* Converts and signs a PHP array into a JWT string.
199199
*
200200
* @param array<mixed> $payload PHP array
201-
* @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $key The secret key.
201+
* @param string|OpenSSLAsymmetricKey|OpenSSLCertificate $key The secret key.
202202
* @param string $alg Supported algorithms are 'ES384','ES256', 'ES256K', 'HS256',
203203
* 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512'
204204
* @param string $keyId
@@ -239,7 +239,7 @@ public static function encode(
239239
* Sign a string with a given key and algorithm.
240240
*
241241
* @param string $msg The message to sign
242-
* @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $key The secret key.
242+
* @param string|OpenSSLAsymmetricKey|OpenSSLCertificate $key The secret key.
243243
* @param string $alg Supported algorithms are 'EdDSA', 'ES384', 'ES256', 'ES256K', 'HS256',
244244
* 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512'
245245
*
@@ -265,17 +265,13 @@ public static function sign(
265265
return \hash_hmac($algorithm, $msg, $key, true);
266266
case 'openssl':
267267
$signature = '';
268-
if (!\is_resource($key)) {
269-
/** @var OpenSSLAsymmetricKey|OpenSSLCertificate|string $key */
270-
$key = $key;
271-
if (!$key = openssl_pkey_get_private($key)) {
272-
throw new DomainException('OpenSSL unable to validate key');
273-
}
274-
if (str_starts_with($alg, 'RS')) {
275-
self::validateRsaKeyLength($key);
276-
}
268+
if (!$key = openssl_pkey_get_private($key)) {
269+
throw new DomainException('OpenSSL unable to validate key');
277270
}
278-
$success = \openssl_sign($msg, $signature, $key, $algorithm); // @phpstan-ignore-line
271+
if (str_starts_with($alg, 'RS')) {
272+
self::validateRsaKeyLength($key);
273+
}
274+
$success = \openssl_sign($msg, $signature, $key, $algorithm);
279275
if (!$success) {
280276
throw new DomainException('OpenSSL unable to sign data');
281277
}
@@ -314,7 +310,7 @@ public static function sign(
314310
*
315311
* @param string $msg The original message (header and body)
316312
* @param string $signature The original signature
317-
* @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $keyMaterial For Ed*, ES*, HS*, a string key works. for RS*, must be an instance of OpenSSLAsymmetricKey
313+
* @param string|OpenSSLAsymmetricKey|OpenSSLCertificate $keyMaterial For Ed*, ES*, HS*, a string key works. for RS*, must be an instance of OpenSSLAsymmetricKey
318314
* @param string $alg The algorithm
319315
*
320316
* @return bool
@@ -334,14 +330,13 @@ private static function verify(
334330
list($function, $algorithm) = static::$supported_algs[$alg];
335331
switch ($function) {
336332
case 'openssl':
337-
if (!\is_resource($keyMaterial) && str_starts_with($algorithm, 'RS')) {
338-
/** @var OpenSSLAsymmetricKey|OpenSSLCertificate|string $keyMaterial */
339-
$keyMaterial = $keyMaterial;
340-
if ($key = openssl_pkey_get_private($keyMaterial)) {
341-
self::validateRsaKeyLength($key);
333+
if (str_starts_with($algorithm, 'RS')) {
334+
if (!$key = openssl_pkey_get_private($keyMaterial)) {
335+
throw new DomainException('OpenSSL unable to validate key');
342336
}
337+
self::validateRsaKeyLength($key);
343338
}
344-
$success = \openssl_verify($msg, $signature, $keyMaterial, $algorithm); // @phpstan-ignore-line
339+
$success = \openssl_verify($msg, $signature, $keyMaterial, $algorithm);
345340
if ($success === 1) {
346341
return true;
347342
}
@@ -699,6 +694,7 @@ private static function readDER(string $der, int $offset = 0): array
699694
*
700695
* @param string $key HMAC key material
701696
* @param string $algorithm The algorithm
697+
*
702698
* @throws DomainException Provided key is too short
703699
*/
704700
private static function validateHmacKeyLength(string $key, string $algorithm): void
@@ -716,12 +712,13 @@ private static function validateHmacKeyLength(string $key, string $algorithm): v
716712
* @param OpenSSLAsymmetricKey $key RSA key material
717713
* @throws DomainException Provided key is too short
718714
*/
719-
private static function validateRsaKeyLength(OpenSSLAsymmetricKey $key): void
715+
private static function validateRsaKeyLength(#[\SensitiveParameter] OpenSSLAsymmetricKey $key): void
720716
{
721-
if ($keyDetails = \openssl_pkey_get_details($key)) {
722-
if ($keyDetails['bits'] < self::RSA_KEY_MIN_LENGTH) {
723-
throw new DomainException('Provided key is too short');
724-
}
717+
if (!$keyDetails = openssl_pkey_get_details($key)) {
718+
throw new DomainException('Unable to validate key');
719+
}
720+
if ($keyDetails['bits'] < self::RSA_KEY_MIN_LENGTH) {
721+
throw new DomainException('Provided key is too short');
725722
}
726723
}
727724
}

src/Key.php

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
class Key
1111
{
1212
/**
13-
* @param string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $keyMaterial
13+
* @param string|OpenSSLAsymmetricKey|OpenSSLCertificate $keyMaterial
1414
* @param string $algorithm
1515
*/
1616
public function __construct(
@@ -21,9 +21,8 @@ public function __construct(
2121
!\is_string($keyMaterial)
2222
&& !$keyMaterial instanceof OpenSSLAsymmetricKey
2323
&& !$keyMaterial instanceof OpenSSLCertificate
24-
&& !\is_resource($keyMaterial)
2524
) {
26-
throw new TypeError('Key material must be a string, resource, or OpenSSLAsymmetricKey');
25+
throw new TypeError('Key material must be a string, OpenSSLCertificate, or OpenSSLAsymmetricKey');
2726
}
2827

2928
if (empty($keyMaterial)) {
@@ -46,7 +45,7 @@ public function getAlgorithm(): string
4645
}
4746

4847
/**
49-
* @return string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate
48+
* @return string|OpenSSLAsymmetricKey|OpenSSLCertificate
5049
*/
5150
public function getKeyMaterial()
5251
{

tests/JWTTest.php

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ public function testUrlSafeCharacters()
2828

2929
public function testMalformedUtf8StringsFail()
3030
{
31-
3231
$this->expectException(DomainException::class);
3332
JWT::encode(['message' => pack('c', 128)], $this->hmacKey->getKeyMaterial(), 'HS256');
3433
}
@@ -531,17 +530,17 @@ public function provideEncodeDecode()
531530
];
532531
}
533532

534-
public function testEncodeDecodeWithResource()
533+
public function testEncodeDecodeWithOpenSSLAsymmetricKey()
535534
{
536535
$pem = file_get_contents(__DIR__ . '/data/rsa1-public.pub');
537-
$resource = openssl_pkey_get_public($pem);
536+
$keyMaterial = openssl_pkey_get_public($pem);
538537
$privateKey = file_get_contents(__DIR__ . '/data/rsa1-private.pem');
539538

540539
$payload = ['foo' => 'bar'];
541540
$encoded = JWT::encode($payload, $privateKey, 'RS512');
542541

543542
// Verify decoding succeeds
544-
$decoded = JWT::decode($encoded, new Key($resource, 'RS512'));
543+
$decoded = JWT::decode($encoded, new Key($keyMaterial, 'RS512'));
545544

546545
$this->assertSame('bar', $decoded->foo);
547546
}

0 commit comments

Comments
 (0)