Issues (234)

src/XML/EncryptedElementTrait.php (5 issues)

1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\XMLSecurity\XML;
6
7
use DOMElement;
8
use SimpleSAML\Assert\Assert;
9
use SimpleSAML\XML\AbstractElement;
10
use SimpleSAML\XMLSchema\Exception\InvalidDOMElementException;
11
use SimpleSAML\XMLSchema\Exception\TooManyElementsException;
12
use SimpleSAML\XMLSecurity\Alg\Encryption\EncryptionAlgorithmFactory;
0 ignored issues
show
The type SimpleSAML\XMLSecurity\A...ryptionAlgorithmFactory was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
13
use SimpleSAML\XMLSecurity\Alg\Encryption\EncryptionAlgorithmInterface;
14
use SimpleSAML\XMLSecurity\Backend\EncryptionBackend;
15
use SimpleSAML\XMLSecurity\Constants as C;
0 ignored issues
show
The type SimpleSAML\XMLSecurity\Constants was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
16
use SimpleSAML\XMLSecurity\Exception\InvalidArgumentException;
17
use SimpleSAML\XMLSecurity\Exception\NoEncryptedDataException;
18
use SimpleSAML\XMLSecurity\Exception\OpenSSLException;
19
use SimpleSAML\XMLSecurity\Exception\RuntimeException;
20
use SimpleSAML\XMLSecurity\Key\SymmetricKey;
21
use SimpleSAML\XMLSecurity\XML\xenc\EncryptedData;
22
use SimpleSAML\XMLSecurity\XML\xenc\EncryptedKey;
23
24
use function strval;
25
26
/**
27
 * Trait aggregating functionality for encrypted elements.
28
 *
29
 * @package simplesamlphp/xml-security
30
 *
31
 * @phpstan-ignore trait.unused
32
 */
33
trait EncryptedElementTrait
34
{
35
    /** @var \SimpleSAML\XMLSecurity\XML\xenc\EncryptedKey[] */
36
    protected array $encryptedKey = [];
37
38
39
    /**
40
     * Constructor for encrypted elements.
41
     *
42
     * @param \SimpleSAML\XMLSecurity\XML\xenc\EncryptedData $encryptedData The EncryptedData object.
43
     */
44
    final public function __construct(
45
        protected EncryptedData $encryptedData,
46
    ) {
47
        $keyInfo = $this->encryptedData->getKeyInfo();
48
        if ($keyInfo === null) {
49
            return;
50
        }
51
52
        foreach ($keyInfo->getInfo() as $info) {
53
            if ($info instanceof EncryptedKey) {
54
                $this->encryptedKey = [$info];
55
                break;
56
            }
57
        }
58
    }
59
60
61
    /**
62
     * Whether the encrypted object is accompanied by the decryption key or not.
63
     */
64
    public function hasDecryptionKey(): bool
65
    {
66
        return !empty($this->encryptedKey);
67
    }
68
69
70
    /**
71
     * Get the encrypted key used to encrypt the current element.
72
     *
73
     * @return \SimpleSAML\XMLSecurity\XML\xenc\EncryptedKey[]
74
     */
75
    public function getEncryptedKeys(): array
76
    {
77
        return $this->encryptedKey;
78
    }
79
80
81
    /**
82
     * Get the EncryptedData object.
83
     *
84
     * @return \SimpleSAML\XMLSecurity\XML\xenc\EncryptedData
85
     */
86
    public function getEncryptedData(): EncryptedData
87
    {
88
        return $this->encryptedData;
89
    }
90
91
92
    /**
93
     * Decrypt the data in any given element.
94
     *
95
     * Use this method to decrypt an EncryptedData XML element into a string. If the resulting plaintext represents
96
     * an XML document which has a corresponding implementation extending \SimpleSAML\XML\ElementInterface, you
97
     * can call this method to build an object from the resulting plaintext:
98
     *
99
     *     $data = $this->decryptData($decryptor);
100
     *     $xml = \SimpleSAML\XML\DOMDocumentFactory::fromString($data);
101
     *     $object = MyObject::fromXML($xml->documentElement);
102
     *
103
     * If the class using this trait implements \SimpleSAML\XMLSecurity\XML\EncryptedElementInterface, then the
104
     * decrypt() method will only need the proposed code and return the object.
105
     *
106
     * @param \SimpleSAML\XMLSecurity\Alg\Encryption\EncryptionAlgorithmInterface $decryptor The decryptor to use to
107
     * decrypt the object.
108
     *
109
     * @return string The decrypted data.
110
     */
111
    protected function decryptData(EncryptionAlgorithmInterface $decryptor): string
112
    {
113
        $encData = $this->getEncryptedData();
114
        if (!$encData instanceof EncryptedData) {
0 ignored issues
show
$encData is always a sub-type of SimpleSAML\XMLSecurity\XML\xenc\EncryptedData.
Loading history...
115
            throw new NoEncryptedDataException();
116
        }
117
118
        $algId = $decryptor->getAlgorithmId();
119
        $encMethod = $this->getEncryptedData()->getEncryptionMethod();
120
        if ($encMethod !== null) {
121
            $algId = $encMethod->getAlgorithm();
122
        }
123
124
        if (in_array($decryptor->getAlgorithmId(), C::$KEY_TRANSPORT_ALGORITHMS)) {
125
            // the decryptor uses a key transport algorithm, check if we have a session key
126
            if ($this->hasDecryptionKey() === null) {
0 ignored issues
show
The condition $this->hasDecryptionKey() === null is always false.
Loading history...
127
                throw new RuntimeException('Cannot use a key transport algorithm to decrypt an object.');
128
            }
129
130
            if ($encMethod === null) {
131
                throw new RuntimeException('Cannot decrypt data with a session key and no EncryptionMethod.');
132
            }
133
134
            $factory = new EncryptionAlgorithmFactory(
135
                $this->getBlacklistedAlgorithms() ?? EncryptionAlgorithmFactory::DEFAULT_BLACKLIST,
136
            );
137
138
            $decryptionKey = null;
139
            foreach ($this->getEncryptedKeys() as $encryptedKey) {
140
                try {
141
                    $decryptionKey = $encryptedKey->decrypt($decryptor);
142
                } catch (OpenSSLException $e) {
143
                    continue;
144
                }
145
            }
146
147
            if ($decryptionKey === null) {
148
                throw new RuntimeException('Cannot decrypt the session key with any of the provided decryption keys.');
149
            }
150
151
            $decryptor = $factory->getAlgorithm(
152
                $encMethod->getAlgorithm()->getValue(),
153
                new SymmetricKey($decryptionKey),
154
            );
155
            $decryptor->setBackend($this->getEncryptionBackend());
156
        }
157
158
        if ($algId->getValue() !== $decryptor->getAlgorithmId()) {
159
            throw new InvalidArgumentException('Decryption algorithm does not match EncryptionMethod.');
160
        }
161
162
        return $decryptor->decrypt(
163
            base64_decode(strval($encData->getCipherData()->getCipherValue()->getContent()), true),
164
        );
165
    }
166
167
168
    /**
169
     * @inheritDoc
170
     *
171
     * @throws \SimpleSAML\XML\Exception\InvalidDOMElementException
172
     *   If the qualified name of the supplied element is wrong
173
     */
174
    public static function fromXML(DOMElement $xml): static
175
    {
176
        Assert::same(
177
            $xml->localName,
178
            AbstractElement::getClassName(static::class),
179
            InvalidDOMElementException::class,
180
        );
181
        Assert::same($xml->namespaceURI, static::NS, InvalidDOMElementException::class);
0 ignored issues
show
The constant SimpleSAML\XMLSecurity\X...cryptedElementTrait::NS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
182
183
        $ed = EncryptedData::getChildrenOfClass($xml);
184
        Assert::count(
185
            $ed,
186
            1,
187
            sprintf(
188
                'No more or less than one EncryptedData element allowed in %s.',
189
                AbstractElement::getClassName(static::class),
190
            ),
191
            TooManyElementsException::class,
192
        );
193
194
        return new static($ed[0]);
195
    }
196
197
198
    /**
199
     * @inheritDoc
200
     */
201
    public function toXML(?DOMElement $parent = null): DOMElement
202
    {
203
        $e = $this->instantiateParentElement($parent);
204
        $this->encryptedData->toXML($e);
205
        return $e;
206
    }
207
208
209
    /**
210
     * Create a document structure for this element.
211
     *
212
     * The AbstractElement class implements this method. If your object inherits from that class, you will already
213
     * have this method out of the box.
214
     *
215
     * @param \DOMElement|null $parent The element we should append to.
216
     */
217
    abstract public function instantiateParentElement(?DOMElement $parent = null): DOMElement;
218
219
220
    /**
221
     * Get the encryption backend to use for any encryption operation.
222
     *
223
     * @return \SimpleSAML\XMLSecurity\Backend\EncryptionBackend|null The encryption backend to use, or null if we
224
     * want to use the default.
225
     */
226
    abstract public function getEncryptionBackend(): ?EncryptionBackend;
227
228
229
    /**
230
     * Get the list of algorithms that are blacklisted for any encryption operation.
231
     *
232
     * @return string[]|null An array with all algorithm identifiers that are blacklisted, or null to use this
233
     * libraries default.
234
     */
235
    abstract public function getBlacklistedAlgorithms(): ?array;
236
}
237