Completed
Branch master (3439e9)
by Tony Karavasilev (Тони
19:43 queued 16:40
created

AbstractBlockCipherAlgorithm::encryptData()   A

Complexity

Conditions 6
Paths 17

Size

Total Lines 28
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 6

Importance

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