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

EncryptedElementTrait::fromXML()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 21
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 1
eloc 13
c 3
b 0
f 0
nc 1
nop 1
dl 0
loc 21
rs 9.8333
1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\XMLSecurity\XML;
6
7
use SimpleSAML\Assert\Assert;
8
use SimpleSAML\XMLSecurity\Alg\Encryption\EncryptionAlgorithmFactory;
9
use SimpleSAML\XMLSecurity\Alg\Encryption\EncryptionAlgorithmInterface;
10
use SimpleSAML\XMLSecurity\Backend\EncryptionBackend;
11
use SimpleSAML\XMLSecurity\Constants as C;
12
use SimpleSAML\XMLSecurity\Exception\InvalidArgumentException;
13
use SimpleSAML\XMLSecurity\Exception\NoEncryptedDataException;
14
use SimpleSAML\XMLSecurity\Exception\RuntimeException;
15
use SimpleSAML\XMLSecurity\Key\SymmetricKey;
16
use SimpleSAML\XMLSecurity\XML\xenc\EncryptedData;
17
use SimpleSAML\XMLSecurity\XML\xenc\EncryptedKey;
18
19
use function base64_decode;
20
21
/**
22
 * Trait aggregating functionality for encrypted elements.
23
 *
24
 * @package simplesamlphp/xml-security
25
 */
26
trait EncryptedElementTrait
27
{
28
    /** @var \SimpleSAML\XMLSecurity\XML\xenc\EncryptedKey|null */
29
    protected ?EncryptedKey $encryptedKey = null;
30
31
32
    /**
33
     * Constructor for encrypted elements.
34
     *
35
     * @param \SimpleSAML\XMLSecurity\XML\xenc\EncryptedData $encryptedData The EncryptedData object.
36
     */
37
    public function __construct(
38
        protected EncryptedData $encryptedData,
39
    ) {
40
        $keyInfo = $this->encryptedData->getKeyInfo();
41
        if ($keyInfo === null) {
42
            return;
43
        }
44
45
        foreach ($keyInfo->getInfo() as $info) {
46
            if ($info instanceof EncryptedKey) {
47
                $this->encryptedKey = $info;
48
            }
49
        }
50
    }
51
52
53
    /**
54
     * Whether the encrypted object is accompanied by the decryption key or not.
55
     *
56
     * @return bool
57
     */
58
    public function hasDecryptionKey(): bool
59
    {
60
        return $this->encryptedKey !== null;
61
    }
62
63
64
    /**
65
     * Get the encrypted key used to encrypt the current element.
66
     *
67
     * @return \SimpleSAML\XMLSecurity\XML\xenc\EncryptedKey
68
     */
69
    public function getEncryptedKey(): EncryptedKey
70
    {
71
        return $this->encryptedKey;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->encryptedKey could return the type null which is incompatible with the type-hinted return SimpleSAML\XMLSecurity\XML\xenc\EncryptedKey. Consider adding an additional type-check to rule them out.
Loading history...
72
    }
73
74
75
    /**
76
     * Get the EncryptedData object.
77
     *
78
     * @return \SimpleSAML\XMLSecurity\XML\xenc\EncryptedData
79
     */
80
    public function getEncryptedData(): EncryptedData
81
    {
82
        return $this->encryptedData;
83
    }
84
85
86
    /**
87
     * Decrypt the data in any given element.
88
     *
89
     * Use this method to decrypt an EncryptedData XML elemento into a string. If the resulting plaintext represents
90
     * an XML document which has a corresponding implementation extending \SimpleSAML\XML\ElementInterface, you
91
     * can call this method to build an object from the resulting plaintext:
92
     *
93
     *     $data = $this->decryptData($decryptor);
94
     *     $xml = \SimpleSAML\XML\DOMDocumentFactory::fromString($data);
95
     *     $object = MyObject::fromXML($xml->documentElement);
96
     *
97
     * If the class using this trait implements \SimpleSAML\XMLSecurity\XML\EncryptedElementInterface, then the
98
     * decrypt() method will only need the proposed code and return the object.
99
     *
100
     * @param \SimpleSAML\XMLSecurity\Alg\Encryption\EncryptionAlgorithmInterface $decryptor The decryptor to use to
101
     * decrypt the object.
102
     *
103
     * @return string The decrypted data.
104
     */
105
    protected function decryptData(EncryptionAlgorithmInterface $decryptor): string
106
    {
107
        $encData = $this->getEncryptedData();
108
        if (!$encData instanceof EncryptedData) {
0 ignored issues
show
introduced by
$encData is always a sub-type of SimpleSAML\XMLSecurity\XML\xenc\EncryptedData.
Loading history...
109
            throw new NoEncryptedDataException();
110
        }
111
112
        $algId = $decryptor->getAlgorithmId();
113
        $encMethod = $this->getEncryptedData()->getEncryptionMethod();
114
        if ($encMethod !== null) {
115
            $algId = $encMethod->getAlgorithm();
116
        }
117
118
        if (in_array($decryptor->getAlgorithmId(), C::$KEY_TRANSPORT_ALGORITHMS)) {
119
            // the decryptor uses a key transport algorithm, check if we have a session key
120
            if ($this->hasDecryptionKey() === null) {
0 ignored issues
show
introduced by
The condition $this->hasDecryptionKey() === null is always false.
Loading history...
121
                throw new RuntimeException('Cannot use a key transport algorithm to decrypt an object.');
122
            }
123
124
            if ($encMethod === null) {
125
                throw new RuntimeException('Cannot decrypt data with a session key and no EncryptionMethod.');
126
            }
127
128
            $encryptedKey = $this->getEncryptedKey();
129
            $decryptionKey = $encryptedKey->decrypt($decryptor);
130
131
            $factory = new EncryptionAlgorithmFactory($this->getBlacklistedAlgorithms());
132
            $decryptor = $factory->getAlgorithm($encMethod->getAlgorithm(), new SymmetricKey($decryptionKey));
133
            $decryptor->setBackend($this->getEncryptionBackend());
134
        }
135
136
        if ($algId !== $decryptor->getAlgorithmId()) {
137
            throw new InvalidArgumentException('Decryption algorithm does not match EncryptionMethod.');
138
        }
139
140
        return $decryptor->decrypt(base64_decode($encData->getCipherData()->getCipherValue()->getContent()));
141
    }
142
143
144
    /**
145
     * Get the encryption backend to use for any encryption operation.
146
     *
147
     * @return \SimpleSAML\XMLSecurity\Backend\EncryptionBackend|null The encryption backend to use, or null if we
148
     * want to use the default.
149
     */
150
    abstract public function getEncryptionBackend(): ?EncryptionBackend;
151
152
153
    /**
154
     * Get the list of algorithms that are blacklisted for any encryption operation.
155
     *
156
     * @return string[]|null An array with all algorithm identifiers that are blacklisted, or null if we want to use the
157
     * defaults.
158
     */
159
    abstract public function getBlacklistedAlgorithms(): ?array;
160
}
161