KeyTransportAlgorithmFactory::__construct()   A
last analyzed

Complexity

Conditions 6
Paths 5

Size

Total Lines 18
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 7
c 1
b 0
f 0
nc 5
nop 1
dl 0
loc 18
rs 9.2222
1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\XMLSecurity\Alg\KeyTransport;
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 sprintf;
15
16
/**
17
 * Factory class to create and configure key transport algorithms.
18
 */
19
class KeyTransportAlgorithmFactory
20
{
21
    /**
22
     * An array of blacklisted algorithms.
23
     *
24
     * Defaults to RSA 1.5.
25
     *
26
     * @var string[]
27
     */
28
    public const DEFAULT_BLACKLIST = [
29
        C::KEY_TRANSPORT_RSA_1_5,
30
    ];
31
32
    /**
33
     * An array of default algorithms that can be used.
34
     *
35
     * @var class-string[]
36
     */
37
    private const SUPPORTED_DEFAULTS = [
38
        RSA::class,
39
    ];
40
41
42
    /**
43
     * A cache of algorithm implementations indexed by algorithm ID.
44
     *
45
     * @var array<string, \SimpleSAML\XMLSecurity\Alg\KeyTransport\KeyTransportAlgorithmInterface>
46
     */
47
    protected static array $cache = [];
48
49
    /**
50
     * Whether the factory has been initialized or not.
51
     *
52
     * @var bool
53
     */
54
    protected static bool $initialized = false;
55
56
57
    /**
58
     * Build a factory that creates algorithms.
59
     *
60
     * @param string[] $blacklist A list of algorithms forbidden for their use.
61
     */
62
    public function __construct(
63
        protected array $blacklist = self::DEFAULT_BLACKLIST,
64
    ) {
65
        // initialize the cache for supported algorithms per known implementation
66
        if (!self::$initialized) {
67
            foreach (self::SUPPORTED_DEFAULTS as $algorithm) {
68
                foreach ($algorithm::getSupportedAlgorithms() as $algId) {
69
                    if (array_key_exists($algId, self::$cache) && !array_key_exists($algId, $this->blacklist)) {
70
                        /*
71
                         * If the key existed before initialization, that means someone registered a handler for this
72
                         * algorithm, so we should respect that and skip registering the default here.
73
                         */
74
                        continue;
75
                    }
76
                    self::$cache[$algId] = $algorithm;
77
                }
78
            }
79
            self::$initialized = true;
80
        }
81
    }
82
83
84
    /**
85
     * Get a new object implementing the given digital signature algorithm.
86
     *
87
     * @param string $algId The identifier of the algorithm desired.
88
     * @param \SimpleSAML\XMLSecurity\Key\KeyInterface $key The key to use with the given algorithm.
89
     *
90
     * @return \SimpleSAML\XMLSecurity\Alg\KeyTransport\KeyTransportAlgorithmInterface An object implementing the given
91
     * algorithm.
92
     *
93
     * @throws \SimpleSAML\XMLSecurity\Exception\UnsupportedAlgorithmException If an error occurs, e.g. the given
94
     * algorithm is blacklisted, unknown or the given key is not suitable for it.
95
     */
96
    public function getAlgorithm(
97
        string $algId,
98
        #[\SensitiveParameter]
99
        KeyInterface $key,
100
    ): KeyTransportAlgorithmInterface {
101
        Assert::notInArray(
102
            $algId,
103
            $this->blacklist,
104
            sprintf('Blacklisted algorithm: \'%s\'.', $algId),
105
            BlacklistedAlgorithmException::class,
106
        );
107
        Assert::keyExists(
108
            self::$cache,
109
            $algId,
110
            sprintf('Unknown or unsupported algorithm: \'%s\'.', $algId),
111
            UnsupportedAlgorithmException::class,
112
        );
113
114
        return new self::$cache[$algId]($key, $algId);
115
    }
116
117
118
    /**
119
     * Register an implementation of some algorithm(s) for its use.
120
     *
121
     * @param class-string $className
122
     */
123
    public static function registerAlgorithm(string $className): void
124
    {
125
        Assert::implementsInterface(
126
            $className,
127
            KeyTransportAlgorithmInterface::class,
128
            sprintf(
129
                'Cannot register algorithm "%s", must implement %s.',
130
                $className,
131
                KeyTransportAlgorithmInterface::class,
132
            ),
133
        );
134
135
        foreach ($className::getSupportedAlgorithms() as $algId) {
136
            self::$cache[$algId] = $className;
137
        }
138
    }
139
}
140