Passed
Push — master ( 41d929...1c0b0d )
by Tim
03:27
created

SignatureAlgorithmFactory::getExpectedParent()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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