AbstractBlockCipherAlgorithm   A
last analyzed

Complexity

Total Complexity 22

Size/Duplication

Total Lines 216
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 22
eloc 61
c 1
b 0
f 0
dl 0
loc 216
ccs 53
cts 53
cp 1
rs 10

7 Methods

Rating   Name   Duplication   Size   Complexity  
A fetchAlgorithmMethodName() 0 3 1
A validatePlainDataForEncryption() 0 4 2
A __construct() 0 19 4
A validateCipherDataForDecryption() 0 6 3
A encryptData() 0 28 5
A decryptData() 0 24 5
A __debugInfo() 0 14 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 integer code 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 248
    protected function fetchAlgorithmMethodName()
89
    {
90 248
        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
     * @param string $plainData The plain input string.
97
     *
98
     * @throws \Exception Validation errors.
99
     */
100 122
    protected function validatePlainDataForEncryption($plainData)
101
    {
102 122
        if (!is_string($plainData)) {
103 9
            throw new \InvalidArgumentException('The data for encryption must be a string or a binary string.');
104
        }
105
    }
106
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 104
    protected function validateCipherDataForDecryption($cipherData)
115
    {
116 104
        if (!is_string($cipherData)) {
117 10
            throw new \InvalidArgumentException('The data for decryption must be a string or a binary string.');
118 94
        } elseif ($cipherData === '') {
119 7
            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 279
    public function __construct()
127
    {
128
        /**
129
         * {@internal Serialization and initialization purposes for both the default key and IV. }}
130
         */
131 279
        if ($this->key === '') {
132 279
            $this->key = str_pad($this->key, static::KEY_SIZE, "\x0", STR_PAD_RIGHT);
133
        }
134
135 279
        if ($this->iv === '') {
136 279
            $this->iv = str_pad($this->iv, static::BLOCK_SIZE, "\x0", STR_PAD_RIGHT);
137
        }
138
139
        // @codeCoverageIgnoreStart
140
        if (!in_array(strtolower($this->fetchAlgorithmMethodName()), openssl_get_cipher_methods(), true)) {
141
            throw new \RuntimeException(
142
                'The algorithm `' .
143
                $this->fetchAlgorithmMethodName() .
144
                '`is not supported under the current system configuration.'
145
            );
146
        }
147
        // @codeCoverageIgnoreEnd
148
    }
149
150
    /**
151
     * Get debug information for the class instance.
152
     *
153
     * @return array Debug information.
154
     */
155 7
    public function __debugInfo()
156
    {
157 7
        return [
158 7
            'standard' => static::ALGORITHM_NAME,
159 7
            'type' => 'symmetrical encryption or two-way block cipher',
160 7
            'block size in bits' => static::BLOCK_SIZE * 8,
161 7
            'key size in bits' => static::KEY_SIZE * 8,
162 7
            'iv size in bits' => static::IV_SIZE * 8,
163 7
            'block operation mode' => $this->mode,
164 7
            'padding standard' => $this->padding === self::PKCS7_PADDING ? 'PKCS7' : 'zero padding',
165 7
            'secret key' => $this->key,
166 7
            'initialization vector' => $this->iv,
167 7
            'internal algorithm full name' => $this->fetchAlgorithmMethodName(),
168 7
            'internal long data digestion algorithm' => 'HKDF-SHA-2-128',
169 7
        ];
170
    }
171
172
    /**
173
     * Encrypts the given plain data.
174
     *
175
     * @param string $plainData The plain input string.
176
     *
177
     * @return string The cipher/encrypted data.
178
     * @throws \Exception Validation errors.
179
     */
180 122
    public function encryptData($plainData)
181
    {
182 122
        $this->validatePlainDataForEncryption($plainData);
183
184 113
        if ($plainData === '') {
185 25
            $plainData = str_pad($plainData, static::BLOCK_SIZE, "\x0", STR_PAD_RIGHT);
186
        }
187
188 113
        $isZeroPadding = ($this->padding === self::ZERO_PADDING);
189 113
        $iv = ($this->mode === self::ECB_MODE) ? '' : $this->iv;
190
191
        /**
192
         * {@internal The encryption standard is 8-bit wise (do not use StringBuilder) and utilizes performance. }}
193
         */
194 113
        if ($isZeroPadding) {
195 10
            $plainData .= str_repeat("\x0", (static::BLOCK_SIZE - (strlen($plainData) % static::BLOCK_SIZE)));
196
        }
197
198 113
        $cipherData = openssl_encrypt($plainData, $this->fetchAlgorithmMethodName(), $this->key, $this->padding, $iv);
199
200
        /**
201
         * {@internal The zero padding in raw mode comes as Base64 string from OpenSSL by specification. }}
202
         */
203 113
        $cipherData = ($isZeroPadding) ? base64_decode($cipherData) : $cipherData;
204
205 113
        $cipherData = $this->changeOutputFormat($cipherData, true);
206
207 113
        return $cipherData;
208
    }
209
210
    /**
211
     * Decrypts the given cipher data.
212
     *
213
     * @param string $cipherData The encrypted input string.
214
     *
215
     * @return string The decrypted/plain data.
216
     * @throws \Exception Validation errors.
217
     */
218 104
    public function decryptData($cipherData)
219
    {
220 104
        $this->validateCipherDataForDecryption($cipherData);
221
222 87
        $iv = ($this->mode === self::ECB_MODE) ? '' : $this->iv;
223 87
        $isZeroPadding = ($this->padding === self::ZERO_PADDING);
224
225 87
        $cipherData = $this->changeOutputFormat($cipherData, false);
226
227
        /**
228
         * {@internal The zero padding in raw mode comes as Base64 string from OpenSSL by specification. }}
229
         */
230 87
        $cipherData = ($isZeroPadding) ? base64_encode($cipherData) : $cipherData;
231
232 87
        $plainData = openssl_decrypt($cipherData, $this->fetchAlgorithmMethodName(), $this->key, $this->padding, $iv);
233
234
        // Wrong format verification
235 87
        if ($plainData === false) {
236 10
            throw new \InvalidArgumentException(
237 10
                "The passed string was not from the chosen outputting format `{$this->getCipherFormat()}`."
238 10
            );
239
        }
240
241 77
        return ($isZeroPadding) ? rtrim($plainData, "\x0") : $plainData;
242
    }
243
}
244