Passed
Branch feature/php83 (e52173)
by Tim
33:00 queued 16:50
created

SignatureAlgorithmFactory   A

Complexity

Total Complexity 9

Size/Duplication

Total Lines 120
Duplicated Lines 0 %

Importance

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