diff --git a/src/Alg/AbstractAlgorithmFactory.php b/src/Alg/AbstractAlgorithmFactory.php deleted file mode 100644 index b66496bc..00000000 --- a/src/Alg/AbstractAlgorithmFactory.php +++ /dev/null @@ -1,124 +0,0 @@ - - */ - protected static array $cache = []; - - /** - * Build a factory that creates algorithms. - * - * @param string[]|null $blacklist A list of algorithms forbidden for their use. - * @param string[]|null $defaults A list of known implementations. - */ - public function __construct( - protected ?array $blacklist = null, - ?array $defaults = null, - ) { - // initialize the cache for supported algorithms per known implementation - if (!static::$initialized && $defaults !== null) { - foreach ($defaults as $algorithm) { - foreach ($algorithm::getSupportedAlgorithms() as $algId) { - if (array_key_exists($algId, static::$cache)) { - /* - * If the key existed before initialization, that means someone registered a handler for this - * algorithm, so we should respect that and skip registering the default here. - */ - continue; - } - static::$cache[$algId] = $algorithm; - } - } - static::$initialized = true; - } - } - - - /** - * Get a new object implementing the given digital signature algorithm. - * - * @param string $algId The identifier of the algorithm desired. - * @param \SimpleSAML\XMLSecurity\Key\KeyInterface $key The key to use with the given algorithm. - * - * @return \SimpleSAML\XMLSecurity\Alg\AlgorithmInterface An object implementing the given - * algorithm. - * - * @throws \SimpleSAML\XMLSecurity\Exception\UnsupportedAlgorithmException If an error occurs, e.g. the given - * algorithm is blacklisted, unknown or the given key is not suitable for it. - */ - public function getAlgorithm(string $algId, KeyInterface $key): AlgorithmInterface - { - Assert::false( - ($this->blacklist !== null) && in_array($algId, $this->blacklist, true), - sprintf('Blacklisted algorithm: \'%s\'.', $algId), - BlacklistedAlgorithmException::class, - ); - Assert::keyExists( - static::$cache, - $algId, - sprintf('Unknown or unsupported algorithm: \'%s\'.', $algId), - UnsupportedAlgorithmException::class, - ); - - return new static::$cache[$algId]($key, $algId); - } - - - /** - * Get the name of the abstract class our algorithm implementations must extend. - * - * @return class-string - */ - abstract protected static function getExpectedParent(): string; - - - /** - * Register an implementation of some algorithm(s) for its use. - * - * @param class-string $className - */ - public static function registerAlgorithm(string $className): void - { - $parent = static::getExpectedParent(); - Assert::subclassOf( - $className, - $parent, - 'Cannot register algorithm "' . $className . '", must implement ' . $parent . '.', - ); - - foreach ($className::getSupportedAlgorithms() as $algId) { - static::$cache[$algId] = $className; - } - } -} diff --git a/src/Alg/Encryption/AES.php b/src/Alg/Encryption/AES.php index b7c634e4..9379cb78 100644 --- a/src/Alg/Encryption/AES.php +++ b/src/Alg/Encryption/AES.php @@ -27,7 +27,7 @@ public function __construct(SymmetricKey $key, string $algId = C::BLOCK_ENC_AES2 /** - * @inheritDoc + * @return string[] */ public static function getSupportedAlgorithms(): array { diff --git a/src/Alg/Encryption/EncryptionAlgorithmFactory.php b/src/Alg/Encryption/EncryptionAlgorithmFactory.php index 1e469147..69ca61c2 100644 --- a/src/Alg/Encryption/EncryptionAlgorithmFactory.php +++ b/src/Alg/Encryption/EncryptionAlgorithmFactory.php @@ -5,19 +5,36 @@ namespace SimpleSAML\XMLSecurity\Alg\Encryption; use SimpleSAML\Assert\Assert; -use SimpleSAML\XMLSecurity\Alg\AbstractAlgorithmFactory; use SimpleSAML\XMLSecurity\Constants as C; use SimpleSAML\XMLSecurity\Exception\BlacklistedAlgorithmException; use SimpleSAML\XMLSecurity\Exception\UnsupportedAlgorithmException; use SimpleSAML\XMLSecurity\Key\KeyInterface; +use function array_key_exists; +use function in_array; +use function sprintf; + /** * Factory class to create and configure encryption algorithms. * * @package simplesamlphp/xml-security */ -final class EncryptionAlgorithmFactory extends AbstractAlgorithmFactory +final class EncryptionAlgorithmFactory { + /** + * A cache of algorithm implementations indexed by algorithm ID. + * + * @var array + */ + protected static array $cache = []; + + /** + * Whether the factory has been initialized or not. + * + * @var bool + */ + protected static bool $initialized = false; + /** * An array of blacklisted algorithms. * @@ -30,44 +47,92 @@ final class EncryptionAlgorithmFactory extends AbstractAlgorithmFactory ]; /** - * A cache of algorithm implementations indexed by algorithm ID. + * An array of default algorithms that can be used. * - * @var array + * @var class-string[] */ - protected static array $cache = []; + private const SUPPORTED_DEFAULTS = [ + TripleDES::class, + AES::class, + ]; + /** - * Whether the factory has been initialized or not. + * Build a factory that creates algorithms. * - * @var bool + * @param string[]|null $blacklist A list of algorithms forbidden for their use. */ - protected static bool $initialized = false; + public function __construct( + protected ?array $blacklist = self::DEFAULT_BLACKLIST, + ) { + // initialize the cache for supported algorithms per known implementation + if (!self::$initialized && $blacklist !== null) { + foreach (self::SUPPORTED_DEFAULTS as $algorithm) { + foreach ($algorithm::getSupportedAlgorithms() as $algId) { + if (array_key_exists($algId, self::$cache)) { + /* + * If the key existed before initialization, that means someone registered a handler for this + * algorithm, so we should respect that and skip registering the default here. + */ + continue; + } + self::$cache[$algId] = $algorithm; + } + } + self::$initialized = true; + } + } /** - * Build a factory that creates encryption algorithms. + * Get a new object implementing the given digital signature algorithm. * - * @param string[]|null $blacklist A list of algorithms forbidden for their use. + * @param string $algId The identifier of the algorithm desired. + * @param \SimpleSAML\XMLSecurity\Key\KeyInterface $key The key to use with the given algorithm. + * + * @return \SimpleSAML\XMLSecurity\Alg\Encryption\EncryptionAlgorithmInterface An object implementing the given + * algorithm. + * + * @throws \SimpleSAML\XMLSecurity\Exception\UnsupportedAlgorithmException If an error occurs, e.g. the given + * algorithm is blacklisted, unknown or the given key is not suitable for it. */ - public function __construct(array $blacklist = null) + public function getAlgorithm(string $algId, KeyInterface $key): EncryptionAlgorithmInterface { - parent::__construct( - $blacklist ?? self::DEFAULT_BLACKLIST, - [ - TripleDES::class, - AES::class, - ], + Assert::false( + ($this->blacklist !== null) && in_array($algId, $this->blacklist, true), + sprintf('Blacklisted algorithm: \'%s\'.', $algId), + BlacklistedAlgorithmException::class, ); + Assert::keyExists( + self::$cache, + $algId, + sprintf('Unknown or unsupported algorithm: \'%s\'.', $algId), + UnsupportedAlgorithmException::class, + ); + + return new self::$cache[$algId]($key, $algId); } /** - * Get the name of the abstract class our algorithm implementations must extend. + * Register an implementation of some algorithm(s) for its use. * - * @return string + * @param class-string $className */ - protected static function getExpectedParent(): string + public static function registerAlgorithm(string $className): void { - return EncryptionAlgorithmInterface::class; + Assert::implementsInterface( + $className, + EncryptionAlgorithmInterface::class, + sprintf( + 'Cannot register algorithm "%s", must implement %s.', + $className, + EncryptionAlgorithmInterface::class, + ), + ); + + foreach ($className::getSupportedAlgorithms() as $algId) { + self::$cache[$algId] = $className; + } } } diff --git a/src/Alg/Encryption/TripleDES.php b/src/Alg/Encryption/TripleDES.php index 84956add..5bbaa122 100644 --- a/src/Alg/Encryption/TripleDES.php +++ b/src/Alg/Encryption/TripleDES.php @@ -26,7 +26,7 @@ public function __construct(SymmetricKey $key) /** - * @inheritDoc + * @return string[] */ public static function getSupportedAlgorithms(): array { diff --git a/src/Alg/KeyTransport/AbstractKeyTransporter.php b/src/Alg/KeyTransport/AbstractKeyTransporter.php index 5b82e8b1..fb9847f8 100644 --- a/src/Alg/KeyTransport/AbstractKeyTransporter.php +++ b/src/Alg/KeyTransport/AbstractKeyTransporter.php @@ -5,7 +5,6 @@ namespace SimpleSAML\XMLSecurity\Alg\KeyTransport; use SimpleSAML\Assert\Assert; -use SimpleSAML\XMLSecurity\Alg\Encryption\EncryptionAlgorithmInterface; use SimpleSAML\XMLSecurity\Backend; use SimpleSAML\XMLSecurity\Backend\EncryptionBackend; use SimpleSAML\XMLSecurity\Exception\UnsupportedAlgorithmException; @@ -16,7 +15,7 @@ * * @package simplesamlphp/xml-security */ -abstract class AbstractKeyTransporter implements EncryptionAlgorithmInterface +abstract class AbstractKeyTransporter implements KeyTransportAlgorithmInterface { /** @var string */ protected const DEFAULT_BACKEND = Backend\OpenSSL::class; diff --git a/src/Alg/KeyTransport/KeyTransportAlgorithmFactory.php b/src/Alg/KeyTransport/KeyTransportAlgorithmFactory.php index 44669cc6..4a0106a8 100644 --- a/src/Alg/KeyTransport/KeyTransportAlgorithmFactory.php +++ b/src/Alg/KeyTransport/KeyTransportAlgorithmFactory.php @@ -4,17 +4,35 @@ namespace SimpleSAML\XMLSecurity\Alg\KeyTransport; -use SimpleSAML\XMLSecurity\Alg\AbstractAlgorithmFactory; -use SimpleSAML\XMLSecurity\Alg\AlgorithmInterface; -use SimpleSAML\XMLSecurity\Alg\Encryption\EncryptionAlgorithmInterface; +use SimpleSAML\Assert\Assert; use SimpleSAML\XMLSecurity\Constants as C; +use SimpleSAML\XMLSecurity\Exception\BlacklistedAlgorithmException; +use SimpleSAML\XMLSecurity\Exception\UnsupportedAlgorithmException; use SimpleSAML\XMLSecurity\Key\KeyInterface; +use function array_key_exists; +use function in_array; +use function sprintf; + /** * Factory class to create and configure key transport algorithms. */ -class KeyTransportAlgorithmFactory extends AbstractAlgorithmFactory +class KeyTransportAlgorithmFactory { + /** + * A cache of algorithm implementations indexed by algorithm ID. + * + * @var array + */ + protected static array $cache = []; + + /** + * Whether the factory has been initialized or not. + * + * @var bool + */ + protected static bool $initialized = false; + /** * An array of blacklisted algorithms. * @@ -26,54 +44,93 @@ class KeyTransportAlgorithmFactory extends AbstractAlgorithmFactory C::KEY_TRANSPORT_RSA_1_5, ]; - /** - * A cache of algorithm implementations indexed by algorithm ID. - * - * @var array - */ - protected static array $cache = []; /** - * Whether the factory has been initialized or not. + * An array of default algorithms that can be used. * - * @var bool + * @var class-string[] */ - protected static bool $initialized = false; + private const SUPPORTED_DEFAULTS = [ + RSA::class, + ]; /** - * Build a factory that creates key transport algorithms. + * Build a factory that creates algorithms. * * @param string[]|null $blacklist A list of algorithms forbidden for their use. */ - public function __construct(array $blacklist = null) - { - parent::__construct($blacklist ?? self::DEFAULT_BLACKLIST, [RSA::class]); - } - - /** - * @inheritDoc - */ - protected static function getExpectedParent(): string - { - return EncryptionAlgorithmInterface::class; + public function __construct( + protected ?array $blacklist = self::DEFAULT_BLACKLIST, + ) { + // initialize the cache for supported algorithms per known implementation + if (!self::$initialized && $blacklist !== null) { + foreach (self::SUPPORTED_DEFAULTS as $algorithm) { + foreach ($algorithm::getSupportedAlgorithms() as $algId) { + if (array_key_exists($algId, self::$cache)) { + /* + * If the key existed before initialization, that means someone registered a handler for this + * algorithm, so we should respect that and skip registering the default here. + */ + continue; + } + self::$cache[$algId] = $algorithm; + } + } + self::$initialized = true; + } } /** - * Get a new object implementing the given key transport algorithm. + * Get a new object implementing the given digital signature algorithm. * * @param string $algId The identifier of the algorithm desired. * @param \SimpleSAML\XMLSecurity\Key\KeyInterface $key The key to use with the given algorithm. * - * @return \SimpleSAML\XMLSecurity\Alg\AlgorithmInterface An object implementing the given + * @return \SimpleSAML\XMLSecurity\Alg\KeyTransport\KeyTransportAlgorithmInterface An object implementing the given * algorithm. * - * @throws \SimpleSAML\XMLSecurity\Exception\InvalidArgumentException If an error occurs, e.g. the given algorithm - * is blacklisted, unknown or the given key is not suitable for it. + * @throws \SimpleSAML\XMLSecurity\Exception\UnsupportedAlgorithmException If an error occurs, e.g. the given + * algorithm is blacklisted, unknown or the given key is not suitable for it. */ - public function getAlgorithm(string $algId, KeyInterface $key): AlgorithmInterface + public function getAlgorithm(string $algId, KeyInterface $key): KeyTransportAlgorithmInterface { - return parent::getAlgorithm($algId, $key); + Assert::false( + ($this->blacklist !== null) && in_array($algId, $this->blacklist, true), + sprintf('Blacklisted algorithm: \'%s\'.', $algId), + BlacklistedAlgorithmException::class, + ); + Assert::keyExists( + self::$cache, + $algId, + sprintf('Unknown or unsupported algorithm: \'%s\'.', $algId), + UnsupportedAlgorithmException::class, + ); + + return new self::$cache[$algId]($key, $algId); + } + + + /** + * Register an implementation of some algorithm(s) for its use. + * + * @param class-string $className + */ + public static function registerAlgorithm(string $className): void + { + Assert::implementsInterface( + $className, + KeyTransportAlgorithmInterface::class, + sprintf( + 'Cannot register algorithm "%s", must implement %s.', + $className, + KeyTransportAlgorithmInterface::class, + ), + ); + + foreach ($className::getSupportedAlgorithms() as $algId) { + self::$cache[$algId] = $className; + } } } diff --git a/src/Alg/KeyTransport/KeyTransportAlgorithmInterface.php b/src/Alg/KeyTransport/KeyTransportAlgorithmInterface.php new file mode 100644 index 00000000..85fab923 --- /dev/null +++ b/src/Alg/KeyTransport/KeyTransportAlgorithmInterface.php @@ -0,0 +1,18 @@ + + */ + protected static array $cache = []; + + /** + * Whether the factory has been initialized or not. + * + * @var bool + */ + protected static bool $initialized = false; + /** * An array of blacklisted algorithms. * @@ -30,45 +47,94 @@ final class SignatureAlgorithmFactory extends AbstractAlgorithmFactory C::SIG_HMAC_SHA1, ]; + /** - * A cache of algorithm implementations indexed by algorithm ID. + * An array of default algorithms that can be used. * - * @var array + * @var class-string[] */ - protected static array $cache = []; + private const SUPPORTED_DEFAULTS = [ + RSA::class, + HMAC::class, + ]; + /** - * Whether the factory has been initialized or not. + * Build a factory that creates algorithms. * - * @var bool + * @param string[]|null $blacklist A list of algorithms forbidden for their use. */ - protected static bool $initialized = false; + public function __construct( + protected ?array $blacklist = self::DEFAULT_BLACKLIST, + ) { + // initialize the cache for supported algorithms per known implementation + if (!self::$initialized && $blacklist !== null) { + foreach (self::SUPPORTED_DEFAULTS as $algorithm) { + foreach ($algorithm::getSupportedAlgorithms() as $algId) { + if (array_key_exists($algId, self::$cache)) { + /* + * If the key existed before initialization, that means someone registered a handler for this + * algorithm, so we should respect that and skip registering the default here. + */ + continue; + } + self::$cache[$algId] = $algorithm; + } + } + self::$initialized = true; + } + } /** - * Build a factory that creates signature algorithms. + * Get a new object implementing the given digital signature algorithm. * - * @param string[]|null $blacklist A list of algorithms forbidden for their use. + * @param string $algId The identifier of the algorithm desired. + * @param \SimpleSAML\XMLSecurity\Key\KeyInterface $key The key to use with the given algorithm. + * + * @return \SimpleSAML\XMLSecurity\Alg\Signature\SignatureAlgorithmInterface An object implementing the given + * algorithm. + * + * @throws \SimpleSAML\XMLSecurity\Exception\UnsupportedAlgorithmException If an error occurs, e.g. the given + * algorithm is blacklisted, unknown or the given key is not suitable for it. */ - public function __construct(array $blacklist = null) + public function getAlgorithm(string $algId, KeyInterface $key): SignatureAlgorithmInterface { - parent::__construct( - $blacklist ?? self::DEFAULT_BLACKLIST, - [ - RSA::class, - HMAC::class, - ], + Assert::false( + ($this->blacklist !== null) && in_array($algId, $this->blacklist, true), + sprintf('Blacklisted algorithm: \'%s\'.', $algId), + BlacklistedAlgorithmException::class, ); + Assert::keyExists( + self::$cache, + $algId, + sprintf('Unknown or unsupported algorithm: \'%s\'.', $algId), + UnsupportedAlgorithmException::class, + ); + + return new self::$cache[$algId]($key, $algId); } /** - * Get the name of the abstract class our algorithm implementations must extend. + * Register an implementation of some algorithm(s) for its use. * - * @return string + * @param class-string $className */ - protected static function getExpectedParent(): string + public static function registerAlgorithm(string $className): void { - return SignatureAlgorithmInterface::class; + Assert::implementsInterface( + $className, + SignatureAlgorithmInterface::class, + sprintf( + 'Cannot register algorithm "%s", must implement %s.', + $className, + SignatureAlgorithmInterface::class, + ), + ); + + foreach ($className::getSupportedAlgorithms() as $algId) { + self::$cache[$algId] = $className; + } } }