Passed
Pull Request — master (#26)
by Jaime Pérez
01:59
created

EncryptedElementTrait::fromUnencryptedElement()   C

Complexity

Conditions 16
Paths 16

Size

Total Lines 44
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 16
eloc 31
c 1
b 0
f 0
nc 16
nop 2
dl 0
loc 44
rs 5.5666

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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