Passed
Pull Request — master (#46)
by Tim
02:35 queued 39s
created

EncryptedElementTrait::toXML()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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