Passed
Push — master ( d0dfd7...5d1985 )
by Tim
12:54
created

EncryptionAlgorithmFactory   A

Complexity

Total Complexity 10

Size/Duplication

Total Lines 114
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 33
c 1
b 0
f 0
dl 0
loc 114
rs 10
wmc 10

3 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 18 6
A getAlgorithm() 0 15 2
A registerAlgorithm() 0 14 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\XMLSecurity\Alg\Encryption;
6
7
use SimpleSAML\Assert\Assert;
8
use SimpleSAML\XMLSecurity\Constants as C;
9
use SimpleSAML\XMLSecurity\Exception\BlacklistedAlgorithmException;
10
use SimpleSAML\XMLSecurity\Exception\UnsupportedAlgorithmException;
11
use SimpleSAML\XMLSecurity\Key\KeyInterface;
12
13
use function array_key_exists;
14
use function in_array;
15
use function sprintf;
16
17
/**
18
 * Factory class to create and configure encryption algorithms.
19
 *
20
 * @package simplesamlphp/xml-security
21
 */
22
final class EncryptionAlgorithmFactory
23
{
24
    /**
25
     * A cache of algorithm implementations indexed by algorithm ID.
26
     *
27
     * @var array<string, \SimpleSAML\XMLSecurity\Alg\Encryption\EncryptionAlgorithmInterface>
28
     */
29
    protected static array $cache = [];
30
31
    /**
32
     * Whether the factory has been initialized or not.
33
     *
34
     * @var bool
35
     */
36
    protected static bool $initialized = false;
37
38
    /**
39
     * An array of blacklisted algorithms.
40
     *
41
     * Defaults to 3DES.
42
     *
43
     * @var string[]
44
     */
45
    private const DEFAULT_BLACKLIST = [
46
        C::BLOCK_ENC_3DES,
47
    ];
48
49
    /**
50
     * An array of default algorithms that can be used.
51
     *
52
     * @var class-string[]
53
     */
54
    private const SUPPORTED_DEFAULTS = [
55
        TripleDES::class,
56
        AES::class,
57
    ];
58
59
60
    /**
61
     * Build a factory that creates algorithms.
62
     *
63
     * @param string[]|null $blacklist A list of algorithms forbidden for their use.
64
     */
65
    public function __construct(
66
        protected ?array $blacklist = self::DEFAULT_BLACKLIST,
67
    ) {
68
        // initialize the cache for supported algorithms per known implementation
69
        if (!self::$initialized && $blacklist !== null) {
70
            foreach (self::SUPPORTED_DEFAULTS as $algorithm) {
71
                foreach ($algorithm::getSupportedAlgorithms() as $algId) {
72
                    if (array_key_exists($algId, self::$cache)) {
73
                        /*
74
                         * If the key existed before initialization, that means someone registered a handler for this
75
                         * algorithm, so we should respect that and skip registering the default here.
76
                         */
77
                        continue;
78
                    }
79
                    self::$cache[$algId] = $algorithm;
80
                }
81
            }
82
            self::$initialized = true;
83
        }
84
    }
85
86
87
    /**
88
     * Get a new object implementing the given digital signature algorithm.
89
     *
90
     * @param string $algId The identifier of the algorithm desired.
91
     * @param \SimpleSAML\XMLSecurity\Key\KeyInterface $key The key to use with the given algorithm.
92
     *
93
     * @return \SimpleSAML\XMLSecurity\Alg\Encryption\EncryptionAlgorithmInterface An object implementing the given
94
     * algorithm.
95
     *
96
     * @throws \SimpleSAML\XMLSecurity\Exception\UnsupportedAlgorithmException If an error occurs, e.g. the given
97
     * algorithm is blacklisted, unknown or the given key is not suitable for it.
98
     */
99
    public function getAlgorithm(string $algId, KeyInterface $key): EncryptionAlgorithmInterface
100
    {
101
        Assert::false(
102
            ($this->blacklist !== null) && in_array($algId, $this->blacklist, true),
103
            sprintf('Blacklisted algorithm: \'%s\'.', $algId),
104
            BlacklistedAlgorithmException::class,
105
        );
106
        Assert::keyExists(
107
            self::$cache,
108
            $algId,
109
            sprintf('Unknown or unsupported algorithm: \'%s\'.', $algId),
110
            UnsupportedAlgorithmException::class,
111
        );
112
113
        return new self::$cache[$algId]($key, $algId);
114
    }
115
116
117
    /**
118
     * Register an implementation of some algorithm(s) for its use.
119
     *
120
     * @param class-string $className
1 ignored issue
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
121
     */
122
    public static function registerAlgorithm(string $className): void
123
    {
124
        Assert::implementsInterface(
125
            $className,
126
            EncryptionAlgorithmInterface::class,
127
            sprintf(
128
                'Cannot register algorithm "%s", must implement %s.',
129
                $className,
130
                EncryptionAlgorithmInterface::class,
131
            ),
132
        );
133
134
        foreach ($className::getSupportedAlgorithms() as $algId) {
135
            self::$cache[$algId] = $className;
136
        }
137
    }
138
}
139