Passed
Push — master ( 2242a1...272748 )
by Tony Karavasilev (Тони
16:42
created

validatePlainDataForEncryption()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 2
c 0
b 0
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 2
nc 2
nop 1
crap 2
1
<?php
2
3
/**
4
 * The symmetric block cipher encryption algorithm abstraction specification.
5
 */
6
7
namespace CryptoManana\Core\Abstractions\MessageEncryption;
8
9
use \CryptoManana\Core\Abstractions\MessageEncryption\AbstractSymmetricEncryptionAlgorithm as SymmetricCipherAlgorithm;
10
use \CryptoManana\Core\Interfaces\MessageEncryption\BlockOperationsInterface as CipherBlockOperations;
11
use \CryptoManana\Core\Interfaces\MessageEncryption\ObjectEncryptionInterface as ObjectEncryption;
12
use \CryptoManana\Core\Interfaces\MessageEncryption\FileEncryptionInterface as FileEncryption;
13
use \CryptoManana\Core\Traits\MessageEncryption\BlockOperationsTrait as CipherBlockInteractions;
14
use \CryptoManana\Core\Traits\MessageEncryption\ObjectEncryptionTrait as EncryptObjects;
15
use \CryptoManana\Core\Traits\MessageEncryption\FileEncryptionTrait as EncryptFiles;
16
17
/**
18
 * Class AbstractBlockCipherAlgorithm - The symmetric block cipher algorithm abstraction representation.
19
 *
20
 * @package CryptoManana\Core\Abstractions\MessageEncryption
21
 *
22
 * @mixin CipherBlockInteractions
23
 * @mixin EncryptObjects
24
 * @mixin EncryptFiles
25
 */
26
abstract class AbstractBlockCipherAlgorithm extends SymmetricCipherAlgorithm implements
27
    CipherBlockOperations,
28
    ObjectEncryption,
29
    FileEncryption
30
{
31
    /**
32
     * Block cipher capabilities and data operations.
33
     *
34
     * {@internal Reusable implementation of `BlockOperationsInterface`. }}
35
     */
36
    use CipherBlockInteractions;
37
38
    /**
39
     * Object encryption and decryption capabilities.
40
     *
41
     * {@internal Reusable implementation of `ObjectEncryptionInterface`. }}
42
     */
43
    use EncryptObjects;
44
45
    /**
46
     * File content encryption and decryption capabilities.
47
     *
48
     * {@internal Reusable implementation of `FileEncryptionInterface`. }}
49
     */
50
    use EncryptFiles;
51
52
    /**
53
     * The internal operational block size measured in raw bytes length for the algorithm
54
     */
55
    const BLOCK_SIZE = 0;
56
57
    /**
58
     * The internal initialization vector (IV) size measured in raw bytes length for the algorithm
59
     */
60
    const IV_SIZE = 0;
61
62
    /**
63
     * The initialization vector (IV) string property storage.
64
     *
65
     * @var string The initialization vector (IV) string value.
66
     */
67
    protected $iv = '';
68
69
    /**
70
     * The block encryption operation mode string property.
71
     *
72
     * @var string The block encryption operation mode string value.
73
     */
74
    protected $mode = self::CBC_MODE;
75
76
    /**
77
     * The final block padding operation property.
78
     *
79
     * @var int The final block padding operation string value.
80
     */
81
    protected $padding = self::PKCS7_PADDING;
82
83
    /**
84
     * Fetch the correctly formatted internal encryption algorithm method name.
85
     *
86
     * @return string The symmetric encryption algorithm standard.
87
     */
88 372
    protected function fetchAlgorithmMethodName()
89
    {
90 372
        return static::ALGORITHM_NAME . '-' . (static::KEY_SIZE * 8) . '-' . $this->mode;
91
    }
92
93
    /**
94
     * Internal method for the validation of plain data used at encryption operations.
95
     *
96 372
     * @param string $plainData The plain input string.
97
     *
98
     * @throws \Exception Validation errors.
99
     */
100
    protected function validatePlainDataForEncryption($plainData)
101 372
    {
102 372
        if (!is_string($plainData)) {
103
            throw new \InvalidArgumentException('The data for encryption must be a string or a binary string.');
104
        }
105 372
    }
106 372
107
    /**
108
     * Internal method for the validation of cipher data used at decryption operations.
109
     *
110
     * @param string $cipherData The encrypted input string.
111
     *
112
     * @throws \Exception Validation errors.
113
     */
114
    protected function validateCipherDataForDecryption($cipherData)
115
    {
116
        if (!is_string($cipherData)) {
117
            throw new \InvalidArgumentException('The data for decryption must be a string or a binary string.');
118 372
        } elseif ($cipherData === '') {
119
            throw new \InvalidArgumentException('The string is empty and there is nothing to decrypt from it.');
120
        }
121
    }
122
123
    /**
124
     * Block cipher algorithm constructor.
125
     */
126
    public function __construct()
127
    {
128 156
        /**
129
         * {@internal Serialization and initialization purposes for both the default key and IV. }}
130 156
         */
131 12
        if ($this->key === '') {
132
            $this->key = str_pad($this->key, static::KEY_SIZE, "\x0", STR_PAD_RIGHT);
133
        }
134 144
135 24
        if ($this->iv === '') {
136
            $this->iv = str_pad($this->iv, static::BLOCK_SIZE, "\x0", STR_PAD_RIGHT);
137
        }
138
139
        // @codeCoverageIgnoreStart
140
        if (!in_array($this->fetchAlgorithmMethodName(), openssl_get_cipher_methods(), true)) {
141 144
            throw new \RuntimeException(
142 12
                'The algorithm `' .
143
                $this->fetchAlgorithmMethodName() .
144 12
                '`is not supported under the current system configuration.'
145 12
            );
146
        }
147
        // @codeCoverageIgnoreEnd
148
    }
149 144
150
    /**
151 144
     * Encrypts the given plain data.
152
     *
153 144
     * @param string $plainData The plain input string.
154
     *
155
     * @return string The cipher/encrypted data.
156
     * @throws \Exception Validation errors.
157
     */
158 144
    public function encryptData($plainData)
159 12
    {
160
        $this->validatePlainDataForEncryption($plainData);
161
162 144
        if ($plainData === '') {
163
            $plainData = str_pad($plainData, static::BLOCK_SIZE, "\x0", STR_PAD_RIGHT);
164 144
        }
165
166
        $isZeroPadding = ($this->padding === self::ZERO_PADDING);
167
        $iv = ($this->mode === self::ECB_MODE) ? '' : $this->iv;
168
169
        /**
170
         * {@internal The encryption standard is 8-bit wise (don not use StringBuilder) and utilizes performance. }}
171
         */
172
        if ($isZeroPadding) {
173
            $plainData .= str_repeat("\x0", (static::BLOCK_SIZE - (strlen($plainData) % static::BLOCK_SIZE)));
174
        }
175 144
176
        $cipherData = openssl_encrypt($plainData, $this->fetchAlgorithmMethodName(), $this->key, $this->padding, $iv);
177 144
178 12
        /**
179 132
         * {@internal The zero padding in raw mode comes as Base64 string from OpenSSL by specification. }}
180 12
         */
181
        $cipherData = ($isZeroPadding) ? base64_decode($cipherData) : $cipherData;
182
183 120
        $cipherData = $this->changeOutputFormat($cipherData, true);
184
185
        return $cipherData;
186
    }
187
188 120
    /**
189 12
     * Decrypts the given cipher data.
190
     *
191
     * @param string $cipherData The encrypted input string.
192 120
     *
193
     * @return string The decrypted/plain data.
194 120
     * @throws \Exception Validation errors.
195
     */
196 120
    public function decryptData($cipherData)
197
    {
198
        $this->validateCipherDataForDecryption($cipherData);
199 120
200 12
        $iv = ($this->mode === self::ECB_MODE) ? '' : $this->iv;
201 12
        $isZeroPadding = ($this->padding === self::ZERO_PADDING);
202
203
        $cipherData = $this->changeOutputFormat($cipherData, false);
204
205 108
        /**
206 12
         * {@internal The zero padding in raw mode comes as Base64 string from OpenSSL by specification. }}
207
         */
208
        $cipherData = ($isZeroPadding) ? base64_encode($cipherData) : $cipherData;
209 108
210
        $plainData = openssl_decrypt($cipherData, $this->fetchAlgorithmMethodName(), $this->key, $this->padding, $iv);
211
212
        // Wrong format verification
213
        if ($plainData === false) {
214
            throw new \RuntimeException(
215
                "The passed string was not from the chosen outputting format `{$this->getCipherFormat()}`."
216
            );
217 12
        }
218
219
        return ($isZeroPadding) ? rtrim($plainData, "\x0") : $plainData;
220 12
    }
221 12
222 12
    /**
223 12
     * Get debug information for the class instance.
224 12
     *
225 12
     * @return array Debug information.
226 12
     */
227 12
    public function __debugInfo()
228 12
    {
229 12
        return [
230 12
            'standard' => static::ALGORITHM_NAME,
231
            'type' => 'symmetrical encryption or two-way block cipher',
232
            'block size in bits' => static::BLOCK_SIZE * 8,
233
            'key size in bits' => static::KEY_SIZE * 8,
234
            'iv size in bits' => static::IV_SIZE * 8,
235
            'block operation mode' => $this->mode,
236
            'padding standard' => $this->padding === self::PKCS7_PADDING ? 'PKCS7' : 'zero padding',
237
            'secret key' => $this->key,
238
            'initialization vector' => $this->iv,
239
            'internal algorithm full name' => $this->fetchAlgorithmMethodName(),
240
            'internal long data digestion algorithm' => 'HKDF-SHA-2-128',
241
        ];
242
    }
243
}
244