Completed
Push — master ( 8055c8...3e2656 )
by Tony Karavasilev (Тони
16:45
created

AbstractBlockCipherAlgorithm   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 205
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 22
eloc 65
c 1
b 0
f 0
dl 0
loc 205
ccs 0
cts 74
cp 0
rs 10

5 Methods

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