Passed
Push — master ( e9ba59...c2c890 )
by Tony Karavasilev (Тони
19:20
created

MultipleEncryption::__construct()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 16
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 11
c 1
b 0
f 0
dl 0
loc 16
ccs 0
cts 14
cp 0
rs 9.9
cc 4
nc 4
nop 2
crap 20
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
    protected function validateIterationsNumber($iterations)
69
    {
70
        $iterations = filter_var(
71
            $iterations,
72
            FILTER_VALIDATE_INT,
73
            [
74
                "options" => [
75
                    "min_range" => 2,
76
                    "max_range" => PHP_INT_MAX,
77
                ],
78
            ]
79
        );
80
81
        if ($iterations === false) {
82
            throw new \InvalidArgumentException(
83
                'The multiple encryption or decryption iterations must be a valid integer bigger than 1.'
84
            );
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
    protected function generateProcessingConfiguration($iterations, $direction = true)
98
    {
99
        // The key list property
100
        $keyList = [];
101
102
        // First key and IV are the original ones
103
        $keyList[0]['key'] = $this->symmetricCipherSource->getSecretKey();
1 ignored issue
show
Bug introduced by
The method getSecretKey() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

103
        /** @scrutinizer ignore-call */ 
104
        $keyList[0]['key'] = $this->symmetricCipherSource->getSecretKey();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
104
        $keyList[0]['iv'] = $this->symmetricCipherSource->getInitializationVector();
105
106
        // Generate more keys and IVs via expand and extract procedure
107
        for ($i = 1; $i < $iterations; $i++) {
108
            $keyList[$i] = [];
109
110
            $keyList[$i]['key'] = $this->keyExpansionSource->hashData($keyList[$i - 1]['key']);
1 ignored issue
show
Bug introduced by
The method hashData() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

110
            /** @scrutinizer ignore-call */ 
111
            $keyList[$i]['key'] = $this->keyExpansionSource->hashData($keyList[$i - 1]['key']);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
111
            $keyList[$i]['iv'] = $this->keyExpansionSource->hashData($keyList[$i - 1]['iv']);
112
        }
113
114
        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
    public function __construct(SymmetricBlockCipher $cipher = null, KeyDerivationFunction $hasher = null)
126
    {
127
        if ($cipher instanceof DataEncryption) {
128
            $this->symmetricCipherSource = $cipher;
129
        } else {
130
            throw new \RuntimeException('No symmetric encryption service has been set.');
131
        }
132
133
        if ($hasher !== null) {
134
            $this->keyExpansionSource = $hasher;
135
        } elseif (isset($this->symmetricCipherSource)) {
136
            $this->keyExpansionSource = new DefaultDerivationSource();
137
138
            $this->keyExpansionSource->setContextualString('CryptoMañana')
139
                ->setDerivationSalt($this->symmetricCipherSource->getSecretKey())
140
                ->setSalt($this->symmetricCipherSource->getInitializationVector());
141
        }
142
    }
143
144
    /**
145
     * Container destructor.
146
     */
147
    public function __destruct()
148
    {
149
        unset($this->keyExpansionSource, $this->symmetricCipherSource);
150
    }
151
152
    /**
153
     * Container cloning via deep copy.
154
     */
155
    public function __clone()
156
    {
157
        $this->keyExpansionSource = clone $this->keyExpansionSource;
158
        $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
    public function multipleEncryptData($plainData, $iterations = 2)
171
    {
172
        $this->validateIterationsNumber($iterations);
173
174
        $last = ($iterations - 1);
175
        $list = $this->generateProcessingConfiguration($iterations, true);
176
        $oldCipherFormat = $this->symmetricCipherSource->getCipherFormat();
177
        $this->symmetricCipherSource->setCipherFormat(SymmetricBlockCipher::ENCRYPTION_OUTPUT_RAW);
178
179
        for ($i = 0; $i <= $last; $i++) {
180
            $this->symmetricCipherSource->setSecretKey($list[$i]['key'])->setInitializationVector($list[$i]['iv']);
181
182
            if ($i === $last) {
183
                $this->symmetricCipherSource->setCipherFormat($oldCipherFormat);
184
            }
185
186
            $plainData = $this->symmetricCipherSource->encryptData($plainData);
187
        }
188
189
        $this->symmetricCipherSource->setSecretKey($list[0]['key'])->setInitializationVector($list[0]['iv']);
190
191
        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
    public function multipleDecryptData($cipherData, $iterations = 2)
204
    {
205
        $this->validateIterationsNumber($iterations);
206
207
        $last = ($iterations - 1);
208
        $list = $this->generateProcessingConfiguration($iterations, false);
209
        $oldCipherFormat = $this->symmetricCipherSource->getCipherFormat();
210
211
        for ($i = 0; $i <= $last; $i++) {
212
            $this->symmetricCipherSource->setSecretKey($list[$i]['key'])->setInitializationVector($list[$i]['iv']);
213
214
            $format = ($i === 0) ? $oldCipherFormat : SymmetricBlockCipher::ENCRYPTION_OUTPUT_RAW;
215
            $this->symmetricCipherSource->setCipherFormat($format);
216
217
            $cipherData = $this->symmetricCipherSource->decryptData($cipherData);
218
        }
219
220
        $this->symmetricCipherSource->setCipherFormat($oldCipherFormat);
221
        $this->symmetricCipherSource->setSecretKey($list[$last]['key'])->setInitializationVector($list[$last]['iv']);
222
223
        return $cipherData;
224
    }
225
}
226