Passed
Pull Request — master (#2)
by Tim
02:03
created

SignatureAlgorithmFactory::updateCache()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 2
nc 2
nop 1
dl 0
loc 5
rs 10
c 0
b 0
f 0
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\Alg\SignatureAlgorithm;
9
use SimpleSAML\XMLSecurity\Constants;
10
use SimpleSAML\XMLSecurity\Exception\InvalidArgumentException;
11
use SimpleSAML\XMLSecurity\Key\AbstractKey;
12
13
use function in_array;
14
15
/**
16
 * Factory class to create and configure digital signature algorithms.
17
 *
18
 * @package simplesamlphp/xml-security
19
 */
20
final class SignatureAlgorithmFactory
21
{
22
    /**
23
     * An array holding the known classes extending \SimpleSAML\XMLSecurity\Alg\Signature\AbstractSigner.
24
     *
25
     * @var string[]
26
     */
27
    private static array $algorithms = [
28
        RSA::class,
29
        HMAC::class
30
    ];
31
32
    /**
33
     * An array of blacklisted algorithms.
34
     *
35
     * Defaults to RSA-SHA1 & HMAC-SHA1 due to the weakness of SHA1.
36
     *
37
     * @var string[]
38
     */
39
    private array $blacklist = [
40
        Constants::SIG_RSA_SHA1,
41
        Constants::SIG_HMAC_SHA1,
42
    ];
43
44
    /**
45
     * A cache of signers indexed by algorithm ID.
46
     *
47
     * @var string[]
48
     */
49
    private static array $cache = [];
50
51
    /**
52
     * Whether the factory has been initialized or not.
53
     *
54
     * @var bool
55
     */
56
    private static bool $initialized = false;
57
58
59
    /**
60
     * Build a factory that creates signature algorithms.
61
     *
62
     * @param string[]|null $blacklist
63
     */
64
    public function __construct(array $blacklist = null)
65
    {
66
        if ($blacklist !== null) {
67
            $this->blacklist = $blacklist;
68
        }
69
70
        // initialize the cache for supported algorithms per known signer
71
        if (!self::$initialized) {
72
            foreach (self::$algorithms as $algorithm) {
73
                self::updateCache($algorithm);
74
            }
75
            self::$initialized = true;
76
        }
77
    }
78
79
80
    /**
81
     * Get a new object implementing the given digital signature algorithm.
82
     *
83
     * @param string $algId The identifier of the algorithm desired.
84
     * @param \SimpleSAML\XMLSecurity\Key\AbstractKey $key The key to use with the given algorithm.
85
     *
86
     * @return \SimpleSAML\XMLSecurity\Alg\SignatureAlgorithm An object implementing the given algorithm.
87
     *
88
     * @throws \SimpleSAML\XMLSecurity\Exception\InvalidArgumentException If an error occurs, e.g. the given algorithm
89
     * is blacklisted, unknown or the given key is not suitable for it.
90
     */
91
    public function getAlgorithm(string $algId, AbstractKey $key): SignatureAlgorithm
92
    {
93
        if (in_array($algId, $this->blacklist)) {
94
            throw new InvalidArgumentException('Blacklisted signature algorithm');
95
        }
96
97
        if (!array_key_exists($algId, self::$cache)) {
98
            throw new InvalidArgumentException('Unknown algorithm identifier.');
99
        }
100
101
        return new self::$cache[$algId]($key, $algId);
102
    }
103
104
105
    /**
106
     * Register a signature algorithm for its use.
107
     *
108
     * @note Algorithms must extend \SimpleSAML\XMLSecurity\Alg\Signature\AbstractSigner.
109
     *
110
     * @param string $className
111
     */
112
    public static function registerAlgorithm(string $className): void
113
    {
114
        Assert::subclassOf(
115
            $className,
116
            AbstractSigner::class,
117
            'Cannot register algorithm "' . $className . '", must implement ' . "\SimpleSAML\XMLSecurity\Alg\SignatureInterface.",
118
            InvalidArgumentException::class
119
        );
120
121
        self::$algorithms[] = $className;
122
        self::updateCache($className);
123
    }
124
125
126
    /**
127
     * Update the cache with a new signer implementation.
128
     *
129
     * @param string $signer
130
     */
131
    private static function updateCache(string $signer): void
132
    {
133
        /** @var \SimpleSAML\XMLSecurity\Alg\Signature\AbstractSigner $signer */
134
        foreach ($signer::getSupportedAlgorithms() as $algId) {
135
            self::$cache[$algId] = $signer;
136
        }
137
    }
138
}
139