Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like Algorithm often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Algorithm, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
11 | abstract class Algorithm implements AESKeyWrapAlgorithm |
||
12 | { |
||
13 | /** |
||
14 | * Default initial value. |
||
15 | * |
||
16 | * @link https://tools.ietf.org/html/rfc3394#section-2.2.3.1 |
||
17 | * @var string |
||
18 | */ |
||
19 | const DEFAULT_IV = "\xA6\xA6\xA6\xA6\xA6\xA6\xA6\xA6"; |
||
20 | |||
21 | /** |
||
22 | * High order bytes of the alternative initial value for padding. |
||
23 | * |
||
24 | * @link https://tools.ietf.org/html/rfc5649#section-3 |
||
25 | * @var string |
||
26 | */ |
||
27 | const AIV_HI = "\xA6\x59\x59\xA6"; |
||
28 | |||
29 | /** |
||
30 | * Initial value. |
||
31 | * |
||
32 | * @var string $_iv |
||
33 | */ |
||
34 | protected $_iv; |
||
35 | |||
36 | /** |
||
37 | * Get OpenSSL cipher method. |
||
38 | * |
||
39 | * @return string |
||
40 | */ |
||
41 | abstract protected function _cipherMethod(); |
||
42 | |||
43 | /** |
||
44 | * Get key encryption key size. |
||
45 | * |
||
46 | * @return int |
||
47 | */ |
||
48 | abstract protected function _keySize(); |
||
49 | |||
50 | /** |
||
51 | * Constructor |
||
52 | * |
||
53 | * @param string $iv Initial value |
||
54 | */ |
||
55 | 48 | public function __construct($iv = self::DEFAULT_IV) { |
|
61 | |||
62 | /** |
||
63 | * Wrap a key using given key encryption key. |
||
64 | * |
||
65 | * Key length must be at least 64 bits (8 octets) and a multiple |
||
66 | * of 64 bits (8 octets). |
||
67 | * Use <i>wrapPad</i> to wrap a key of arbitrary length. |
||
68 | * |
||
69 | * Key encryption key must have a size of underlying AES algorithm, |
||
70 | * ie. 128, 196 or 256 bits. |
||
71 | * |
||
72 | * @param string $key Key to wrap |
||
73 | * @param string $kek Key encryption key |
||
74 | * @throws \UnexpectedValueException If the key length is invalid |
||
75 | * @return string Ciphertext |
||
76 | */ |
||
77 | 13 | public function wrap($key, $kek) { |
|
98 | |||
99 | /** |
||
100 | * Unwrap a key from a ciphertext using given key encryption key. |
||
101 | * |
||
102 | * @param string $ciphertext Ciphertext of the wrapped key |
||
103 | * @param string $kek Key encryption key |
||
104 | * @throws \UnexpectedValueException If the ciphertext is invalid |
||
105 | * @return string Unwrapped key |
||
106 | */ |
||
107 | 12 | public function unwrap($ciphertext, $kek) { |
|
124 | |||
125 | /** |
||
126 | * Wrap a key of arbitrary length using given key encryption key. |
||
127 | * |
||
128 | * This variant of wrapping does not place any restriction on key size. |
||
129 | * |
||
130 | * Key encryption key has the same restrictions as with <i>wrap</i> method. |
||
131 | * |
||
132 | * @param string $key Key to wrap |
||
133 | * @param string $kek Key encryption key |
||
134 | * @throws \UnexpectedValueException If the key length is invalid |
||
135 | * @return string Ciphertext |
||
136 | */ |
||
137 | 19 | public function wrapPad($key, $kek) { |
|
160 | |||
161 | /** |
||
162 | * Unwrap a key from a padded ciphertext using given key encryption key. |
||
163 | * |
||
164 | * This variant of unwrapping must be used if the key was wrapped using |
||
165 | * <i>wrapPad</i>. |
||
166 | * |
||
167 | * @param string $ciphertext Ciphertext of the wrapped and padded key |
||
168 | * @param string $kek Key encryption key |
||
169 | * @throws \UnexpectedValueException If the ciphertext is invalid |
||
170 | * @return string Unwrapped key |
||
171 | */ |
||
172 | 15 | public function unwrapPad($ciphertext, $kek) { |
|
186 | |||
187 | /** |
||
188 | * Check KEK size. |
||
189 | * |
||
190 | * @param string $kek |
||
191 | * @throws \UnexpectedValueException |
||
192 | * @return self |
||
193 | */ |
||
194 | 38 | protected function _checkKEKSize($kek) { |
|
201 | |||
202 | /** |
||
203 | * Apply Key Wrap to data blocks. |
||
204 | * |
||
205 | * Uses alternative version of the key wrap procedure described in the RFC. |
||
206 | * |
||
207 | * @link https://tools.ietf.org/html/rfc3394#section-2.2.1 |
||
208 | * @param string[] $P Plaintext, n 64-bit values <code>{P1, P2, ..., |
||
209 | * Pn}</code> |
||
210 | * @param string $kek Key encryption key |
||
211 | * @param string $iv Initial value |
||
212 | * @return string[] Ciphertext, (n+1) 64-bit values <code>{C0, C1, ..., |
||
213 | * Cn}</code> |
||
214 | */ |
||
215 | 17 | protected function _wrapBlocks(array $P, $kek, $iv) { |
|
244 | |||
245 | /** |
||
246 | * Unwrap the padded ciphertext producing plaintext and integrity value. |
||
247 | * |
||
248 | * @param string $ciphertext Ciphertext |
||
249 | * @param string $kek Encryption key |
||
250 | * @return array Tuple of plaintext <code>{P1, P2, ..., Pn}</code> and |
||
251 | * integrity value <code>A</code> |
||
252 | */ |
||
253 | 13 | protected function _unwrapPaddedCiphertext($ciphertext, $kek) { |
|
270 | |||
271 | /** |
||
272 | * Apply Key Unwrap to data blocks. |
||
273 | * |
||
274 | * Uses the index based version of key unwrap procedure |
||
275 | * described in the RFC. |
||
276 | * |
||
277 | * Does not compute step 3. |
||
278 | * |
||
279 | * @link https://tools.ietf.org/html/rfc3394#section-2.2.2 |
||
280 | * @param string[] $C Ciphertext, (n+1) 64-bit values <code>{C0, C1, ..., |
||
281 | * Cn}</code> |
||
282 | * @param string $kek Key encryption key |
||
283 | * @throws \UnexpectedValueException |
||
284 | * @return array Tuple of integrity value <code>A</code> and register |
||
285 | * <code>R</code> |
||
286 | */ |
||
287 | 15 | protected function _unwrapBlocks(array $C, $kek) { |
|
312 | |||
313 | /** |
||
314 | * Pad a key with zeroes and compute alternative initial value. |
||
315 | * |
||
316 | * @param string $key Key |
||
317 | * @return array Tuple of padded key and AIV |
||
318 | */ |
||
319 | 17 | protected function _padKey($key) { |
|
330 | |||
331 | /** |
||
332 | * Check that the integrity check value of the padded key is correct. |
||
333 | * |
||
334 | * @param string $A |
||
335 | * @throws \UnexpectedValueException |
||
336 | */ |
||
337 | 13 | protected function _checkPaddedIntegrity($A) { |
|
343 | |||
344 | /** |
||
345 | * Verify that the padding of the plaintext is valid. |
||
346 | * |
||
347 | * @param array $P Plaintext, n 64-bit values <code>{P1, P2, ..., |
||
348 | * Pn}</code> |
||
349 | * @param string $A Integrity check value |
||
350 | * @throws \UnexpectedValueException |
||
351 | * @return int Message length without padding |
||
352 | */ |
||
353 | 12 | protected function _verifyPadding(array $P, $A) { |
|
374 | |||
375 | /** |
||
376 | * Apply AES(K, W) operation (encrypt) to 64 bit block. |
||
377 | * |
||
378 | * @param string $kek |
||
379 | * @param string $block |
||
380 | * @throws \RuntimeException If encrypt fails |
||
381 | * @return string |
||
382 | */ |
||
383 | 26 | View Code Duplication | protected function _encrypt($kek, $block) { |
392 | |||
393 | /** |
||
394 | * Apply AES-1(K, W) operation (decrypt) to 64 bit block. |
||
395 | * |
||
396 | * @param string $kek |
||
397 | * @param string $block |
||
398 | * @throws \RuntimeException If decrypt fails |
||
399 | * @return string |
||
400 | */ |
||
401 | 23 | View Code Duplication | protected function _decrypt($kek, $block) { |
410 | |||
411 | /** |
||
412 | * Get the latest OpenSSL error message. |
||
413 | * |
||
414 | * @return string |
||
415 | */ |
||
416 | 2 | protected function _getLastOpenSSLError() { |
|
423 | |||
424 | /** |
||
425 | * Take 64 most significant bits from value. |
||
426 | * |
||
427 | * @param string $val |
||
428 | * @return string |
||
429 | */ |
||
430 | 20 | protected function _msb64($val) { |
|
433 | |||
434 | /** |
||
435 | * Take 64 least significant bits from value. |
||
436 | * |
||
437 | * @param string $val |
||
438 | * @return string |
||
439 | */ |
||
440 | 20 | protected function _lsb64($val) { |
|
443 | |||
444 | /** |
||
445 | * Convert number to 64 bit unsigned integer octet string with |
||
446 | * most significant bit first. |
||
447 | * |
||
448 | * @param int $num |
||
449 | * @return string |
||
450 | */ |
||
451 | 20 | protected function _uint64($num) { |
|
458 | } |
||
459 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.