MultipleEncryption::__clone()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 2
c 1
b 0
f 0
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
cc 1
nc 1
nop 0
crap 1
1
<?php
2
3
/**
4
 * Cryptographic protocol for multiple encryption.
5
 */
6
7
namespace CryptoManana\CryptographicProtocol;
8
9
use CryptoManana\Core\Abstractions\Containers\AbstractCryptographicProtocol as CryptographicProtocol;
10
use CryptoManana\Core\Abstractions\MessageDigestion\AbstractKeyMaterialDerivationFunction as KeyDerivationFunction;
11
use CryptoManana\Core\Abstractions\MessageEncryption\AbstractBlockCipherAlgorithm as SymmetricBlockCipher;
12
use CryptoManana\Core\Interfaces\MessageEncryption\DataEncryptionInterface as DataEncryption;
13
use CryptoManana\Core\Interfaces\Containers\KeyExpansionInjectableInterface as KeyExpansionFunctionSetter;
14
use CryptoManana\Core\Interfaces\Containers\SymmetricEncryptionInjectableInterface as SymmetricCipherSetter;
15
use CryptoManana\Core\Interfaces\Containers\MultipleEncryptionInterface as MultiplePassDataProcessing;
16
use CryptoManana\Core\Traits\Containers\KeyExpansionInjectableTrait as KeyExpansionFunctionSetterImplementation;
17
use CryptoManana\Core\Traits\Containers\SymmetricEncryptionInjectableTrait as SymmetricCipherSetterImplementation;
18
use CryptoManana\Hashing\HkdfShaTwo384 as DefaultDerivationSource;
19
20
/**
21
 * Class MultipleEncryption - The multiple encryption protocol object.
22
 *
23
 * @package CryptoManana\CryptographicProtocol
24
 *
25
 * @mixin KeyExpansionFunctionSetterImplementation
26
 * @mixin SymmetricCipherSetterImplementation
27
 */
28
class MultipleEncryption extends CryptographicProtocol implements
29
    KeyExpansionFunctionSetter,
30
    SymmetricCipherSetter,
31
    MultiplePassDataProcessing
32
{
33
    /**
34
     * The message key expansion derivation service dependency injection via a setter method implementation.
35
     *
36
     * {@internal Reusable implementation of `KeyExpansionInjectableInterface`. }}
37
     */
38
    use KeyExpansionFunctionSetterImplementation;
39
40
    /**
41
     * The message symmetric encryption service dependency injection via a setter method implementation.
42
     *
43
     * {@internal Reusable implementation of `SymmetricEncryptionInjectableInterface`. }}
44
     */
45
    use SymmetricCipherSetterImplementation;
46
47
    /**
48
     * The key expansion derivation algorithm service property storage.
49
     *
50
     * @var KeyDerivationFunction|null The key expansion derivation service.
51
     */
52
    protected $keyExpansionSource = null;
53
54
    /**
55
     * The message symmetric encryption algorithm service property storage.
56
     *
57
     * @var SymmetricBlockCipher|null The message symmetric encryption service.
58
     */
59
    protected $symmetricCipherSource = null;
60
61
    /**
62
     * Internal method for integer internal iteration count validation.
63
     *
64
     * @param int $iterations The number of internal iterations to perform.
65
     *
66
     * @throws \Exception Validation errors.
67
     */
68 14
    protected function validateIterationsNumber($iterations)
69
    {
70 14
        $iterations = filter_var(
71 14
            $iterations,
72 14
            FILTER_VALIDATE_INT,
73 14
            [
74 14
                "options" => [
75 14
                    "min_range" => 2,
76 14
                    "max_range" => PHP_INT_MAX,
77 14
                ],
78 14
            ]
79 14
        );
80
81 14
        if ($iterations === false) {
82 4
            throw new \InvalidArgumentException(
83 4
                'The multiple encryption or decryption iterations must be a valid integer bigger than 1.'
84 4
            );
85
        }
86
    }
87
88
    /**
89
     * Internal method for generating a list of secret keys and initialization vectors.
90
     *
91
     * @param int $iterations The number of internal iterations to perform.
92
     * @param bool|int|null $direction Flag for encryption direction (encrypt => `true` or decrypt => `false`).
93
     *
94
     * @return array The list of keys and IVs.
95
     * @throws \Exception Key expansion service errors.
96
     */
97 12
    protected function generateProcessingConfiguration($iterations, $direction = true)
98
    {
99
        // The key list property
100 12
        $keyList = [];
101
102
        // First key and IV are the original ones
103 12
        $keyList[0]['key'] = $this->symmetricCipherSource->getSecretKey();
104 12
        $keyList[0]['iv'] = $this->symmetricCipherSource->getInitializationVector();
105
106
        // Generate more keys and IVs via expand and extract procedure
107 12
        for ($i = 1; $i < $iterations; $i++) {
108 12
            $keyList[$i] = [];
109
110 12
            $keyList[$i]['key'] = $this->keyExpansionSource->hashData($keyList[$i - 1]['key']);
111 12
            $keyList[$i]['iv'] = $this->keyExpansionSource->hashData($keyList[$i - 1]['iv']);
112
        }
113
114 12
        return ($direction == true) ? $keyList : array_reverse($keyList, false);
115
    }
116
117
    /**
118
     * Container constructor.
119
     *
120
     * @param SymmetricBlockCipher|null $cipher the message symmetric encryption service.
121
     * @param KeyDerivationFunction|null $hasher The key expansion derivation service.
122
     *
123
     * @throws \Exception Initialization validation.
124
     */
125 17
    public function __construct(SymmetricBlockCipher $cipher = null, KeyDerivationFunction $hasher = null)
126
    {
127 17
        if ($cipher instanceof DataEncryption) {
128 15
            $this->symmetricCipherSource = $cipher;
129
        } else {
130 2
            throw new \RuntimeException('No symmetric encryption service has been set.');
131
        }
132
133 15
        if ($hasher !== null) {
134 6
            $this->keyExpansionSource = $hasher;
135 11
        } elseif (isset($this->symmetricCipherSource)) {
136 11
            $this->keyExpansionSource = new DefaultDerivationSource();
137
138 11
            $this->keyExpansionSource->setContextualString('CryptoMañana')
139 11
                ->setDerivationSalt($this->symmetricCipherSource->getSecretKey())
140 11
                ->setSalt($this->symmetricCipherSource->getInitializationVector());
141
        }
142
    }
143
144
    /**
145
     * Container destructor.
146
     */
147 15
    public function __destruct()
148
    {
149 15
        unset($this->keyExpansionSource, $this->symmetricCipherSource);
150
    }
151
152
    /**
153
     * Container cloning via deep copy.
154
     */
155 2
    public function __clone()
156
    {
157 2
        $this->keyExpansionSource = clone $this->keyExpansionSource;
158 2
        $this->symmetricCipherSource = clone $this->symmetricCipherSource;
159
    }
160
161
    /**
162
     * Encrypts the given plain data multiple times with different extracted keys.
163
     *
164
     * @param string $plainData The plain input string.
165
     * @param int $iterations The number of internal iterations to perform.
166
     *
167
     * @return string The cipher/encrypted data.
168
     * @throws \Exception Validation errors.
169
     */
170 13
    public function multipleEncryptData($plainData, $iterations = 2)
171
    {
172 13
        $this->validateIterationsNumber($iterations);
173
174 11
        $last = ($iterations - 1);
175 11
        $list = $this->generateProcessingConfiguration($iterations, true);
176 11
        $oldCipherFormat = $this->symmetricCipherSource->getCipherFormat();
177 11
        $this->symmetricCipherSource->setCipherFormat(SymmetricBlockCipher::ENCRYPTION_OUTPUT_RAW);
178
179 11
        for ($i = 0; $i <= $last; $i++) {
180 11
            $this->symmetricCipherSource->setSecretKey($list[$i]['key'])->setInitializationVector($list[$i]['iv']);
181
182 11
            if ($i === $last) {
183 10
                $this->symmetricCipherSource->setCipherFormat($oldCipherFormat);
184
            }
185
186 11
            $plainData = $this->symmetricCipherSource->encryptData($plainData);
187
        }
188
189 10
        $this->symmetricCipherSource->setSecretKey($list[0]['key'])->setInitializationVector($list[0]['iv']);
190
191 10
        return $plainData;
192
    }
193
194
    /**
195
     * Decrypts the given cipher data multiple times with different extracted keys.
196
     *
197
     * @param string $cipherData The encrypted input string.
198
     * @param int $iterations The number of internal iterations to perform.
199
     *
200
     * @return string The decrypted/plain data.
201
     * @throws \Exception Validation errors.
202
     */
203 7
    public function multipleDecryptData($cipherData, $iterations = 2)
204
    {
205 7
        $this->validateIterationsNumber($iterations);
206
207 5
        $last = ($iterations - 1);
208 5
        $list = $this->generateProcessingConfiguration($iterations, false);
209 5
        $oldCipherFormat = $this->symmetricCipherSource->getCipherFormat();
210
211 5
        for ($i = 0; $i <= $last; $i++) {
212 5
            $this->symmetricCipherSource->setSecretKey($list[$i]['key'])->setInitializationVector($list[$i]['iv']);
213
214 5
            $format = ($i === 0) ? $oldCipherFormat : SymmetricBlockCipher::ENCRYPTION_OUTPUT_RAW;
215 5
            $this->symmetricCipherSource->setCipherFormat($format);
216
217 5
            $cipherData = $this->symmetricCipherSource->decryptData($cipherData);
218
        }
219
220 4
        $this->symmetricCipherSource->setCipherFormat($oldCipherFormat);
221 4
        $this->symmetricCipherSource->setSecretKey($list[$last]['key'])->setInitializationVector($list[$last]['iv']);
222
223 4
        return $cipherData;
224
    }
225
}
226