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