Passed
Push — master ( 8d59f2...e30bd8 )
by Tony Karavasilev (Тони
16:14
created

AbstractBlockCipherAlgorithm::encryptData()   A

Complexity

Conditions 6
Paths 17

Size

Total Lines 31
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 13
c 1
b 0
f 0
dl 0
loc 31
ccs 13
cts 13
cp 1
rs 9.2222
cc 6
nc 17
nop 1
crap 6
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
     * Block cipher algorithm constructor.
95
     */
96 372
    public function __construct()
97
    {
98
        /**
99
         * {@internal Serialization and initialization purposes for both the default key and IV. }}
100
         */
101 372
        if ($this->key === '') {
102 372
            $this->key = str_pad($this->key, static::KEY_SIZE, "\x0", STR_PAD_RIGHT);
103
        }
104
105 372
        if ($this->iv === '') {
106 372
            $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 372
    }
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 156
    public function encryptData($plainData)
129
    {
130 156
        if (!is_string($plainData)) {
131 12
            throw new \InvalidArgumentException('The data for encryption must be a string or a binary string.');
132
        } elseif ($plainData === '') {
133
            $plainData = str_pad($plainData, static::BLOCK_SIZE, "\x0", STR_PAD_RIGHT);
134 144
        }
135 24
136
        $isZeroPadding = ($this->padding === self::ZERO_PADDING);
137
138
        /**
139
         * {@internal The encryption standard is 8-bit wise (don not use StringBuilder) and utilizes performance. }}
140
         */
141 144
        if ($isZeroPadding) {
142 12
            $plainData .= str_repeat("\x0", (static::BLOCK_SIZE - (strlen($plainData) % static::BLOCK_SIZE)));
143
        }
144 12
145 12
        $method = $this->fetchAlgorithmMethodName();
146
147
        $iv = ($this->mode === self::ECB_MODE) ? '' : $this->iv;
148
149 144
        $cipherData = openssl_encrypt($plainData, $method, $this->key, $this->padding, $iv);
150
151 144
        /**
152
         * {@internal The zero padding in raw mode comes as Base64 string from OpenSSL by specification. }}
153 144
         */
154
        $cipherData = ($isZeroPadding) ? base64_decode($cipherData) : $cipherData;
155
156
        $cipherData = $this->changeOutputFormat($cipherData, true);
157
158 144
        return $cipherData;
159 12
    }
160
161
    /**
162 144
     * Decrypts the given cipher data.
163
     *
164 144
     * @param string $cipherData The encrypted input string.
165
     *
166
     * @return string The decrypted/plain data.
167
     * @throws \Exception Validation errors.
168
     */
169
    public function decryptData($cipherData)
170
    {
171
        if (!is_string($cipherData)) {
172
            throw new \InvalidArgumentException('The data for decryption must be a string or a binary string.');
173
        } elseif ($cipherData === '') {
174
            throw new \InvalidArgumentException('The string is empty and there is nothing to decrypt from it.');
175 144
        }
176
177 144
        $isZeroPadding = ($this->padding === self::ZERO_PADDING);
178 12
179 132
        $cipherData = $this->changeOutputFormat($cipherData, false);
180 12
181
        /**
182
         * {@internal The zero padding in raw mode comes as Base64 string from OpenSSL by specification. }}
183 120
         */
184
        $cipherData = ($isZeroPadding) ? base64_encode($cipherData) : $cipherData;
185
186
        $method = $this->fetchAlgorithmMethodName();
187
188 120
        $iv = ($this->mode === self::ECB_MODE) ? '' : $this->iv;
189 12
190
        $plainData = openssl_decrypt($cipherData, $method, $this->key, $this->padding, $iv);
191
192 120
        // Wrong format verification
193
        if ($plainData === false) {
194 120
            throw new \RuntimeException(
195
                "The passed string was not from the chosen outputting format `{$this->getCipherFormat()}`."
196 120
            );
197
        }
198
199 120
        return ($isZeroPadding) ? rtrim($plainData, "\x0") : $plainData;
200 12
    }
201 12
202
    /**
203
     * Get debug information for the class instance.
204
     *
205 108
     * @return array Debug information.
206 12
     */
207
    public function __debugInfo()
208
    {
209 108
        return [
210
            'standard' => static::ALGORITHM_NAME,
211
            'type' => 'symmetrical encryption or two-way block cipher',
212
            'block size in bits' => static::BLOCK_SIZE * 8,
213
            'key size in bits' => static::KEY_SIZE * 8,
214
            'iv size in bits' => static::IV_SIZE * 8,
215
            'block operation mode' => $this->mode,
216
            'padding standard' => $this->padding === self::PKCS7_PADDING ? 'PKCS7' : 'zero padding',
217 12
            'secret key' => $this->key,
218
            'initialization vector' => $this->iv,
219
            'internal algorithm full name' => $this->fetchAlgorithmMethodName(),
220 12
            'internal long data digestion algorithm' => 'HKDF-SHA-2-128',
221 12
        ];
222 12
    }
223
}
224