Test Failed
Pull Request — master (#667)
by
unknown
02:48
created

NewFileKey::hashContinue()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 3
nc 1
nop 0
dl 0
loc 5
rs 10
c 1
b 0
f 0
1
<?php
2
3
/**
4
 * This file is based on code of tecnickcom/TCPDF PDF library.
5
 *
6
 * Original author Nicola Asuni ([email protected]) and
7
 * contributors (https://github.com/tecnickcom/TCPDF/graphs/contributors).
8
 *
9
 * @see https://github.com/tecnickcom/TCPDF
10
 *
11
 * Original code was licensed on the terms of the LGPL v3.
12
 *
13
 * ------------------------------------------------------------------------------
14
 *
15
 * @file This file is part of the PdfParser library.
16
 *
17
 * @author  Alastair Irvine <[email protected]>
18
 *
19
 * @date    2024-01-12
20
 *
21
 * @license LGPLv3
22
 *
23
 * @url     <https://github.com/smalot/pdfparser>
24
 *
25
 *  PdfParser is a pdf library written in PHP, extraction oriented.
26
 *  Copyright (C) 2017 - Sébastien MALOT <[email protected]>
27
 *
28
 *  This program is free software: you can redistribute it and/or modify
29
 *  it under the terms of the GNU Lesser General Public License as published by
30
 *  the Free Software Foundation, either version 3 of the License, or
31
 *  (at your option) any later version.
32
 *
33
 *  This program is distributed in the hope that it will be useful,
34
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
35
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
36
 *  GNU Lesser General Public License for more details.
37
 *
38
 *  You should have received a copy of the GNU Lesser General Public License
39
 *  along with this program.
40
 *  If not, see <http://www.pdfparser.org/sites/default/LICENSE.txt>.
41
 */
42
43
namespace Smalot\PdfParser\Encryption;
44
45
/**
46
 * Creates the file's decryption key from the info about the file and
47
 * optionally the owner and/or user password.  Doesn't call
48
 * Info::getEncAlgorithm(), but figures out what to do based on the encryption
49
 * version and revision instead.
50
 */
51
abstract class FileKey
52
{
53
    function __construct(Info $info)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
54
    {
55
        $this->info = $info;
0 ignored issues
show
Bug Best Practice introduced by
The property info does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
56
    }
57
58
59
    public function getKey()
60
    {
61
        return $this->fileKey;
62
    }
63
64
65
    /**
66
     * Creates the file's decryption key.  Internally, creates an instance of
67
     * the appropriate child class.
68
     *
69
     * @return byte string
0 ignored issues
show
Bug introduced by
The type Smalot\PdfParser\Encryption\byte was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
70
     */
71
    public static function generate(Info $info, $ownerPassword = null, $userPassword = null)
72
    {
73
        // Create an instance of the appropriate child class.
74
        switch ($info->getRevision()) {
75
            case 2:
76
            case 3:
77
                $helper = new OldFileKey($info, $ownerPassword, $userPassword);
78
                break;
79
80
            case 5:
81
            case 6:
82
                $helper = new NewFileKey($info, $ownerPassword, $userPassword);
83
                break;
84
85
            default:
86
                throw new InvalidRevision("Unsupported revision in makeFileKey()");
87
        }
88
89
        return $helper->getKey();
90
    }
91
}
92
93
94
/**
95
 * Handles file keys for encryption revisions 2 and 3.
96
 */
97
class OldFileKey extends FileKey
98
{
99
    /**
100
     * @throws InvalidPassword if neither of the supplied passwords are valid
101
     */
102
    function __construct(Info $info, $ownerPassword = null, $userPassword = null)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
103
    {
104
        parent::__construct($info);
105
106
        $passwordPaddingBytes = [ 0x28, 0xbf, 0x4e, 0x5e, 0x4e, 0x75, 0x8a, 0x41, 0x64, 0x00, 0x4e, 0x56, 0xff, 0xfa, 0x01, 0x08, 0x2e, 0x2e, 0x00, 0xb6, 0xd0, 0x68, 0x3e, 0x80, 0x2f, 0x0c, 0xa9, 0xfe, 0x64, 0x53, 0x69, 0x7a ];
107
        $this->passwordPadding = \implode(\array_map("chr", $passwordPaddingBytes));
0 ignored issues
show
Bug Best Practice introduced by
The property passwordPadding does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
108
109
        if (!empty($ownerPassword) && \strlen($ownerPassword) > 32) {
110
            $this->ownerPassword = \substr($ownerPassword, 0, 32);
0 ignored issues
show
Bug Best Practice introduced by
The property ownerPassword does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
111
        } else {
112
            $this->ownerPassword = $ownerPassword;
113
        }
114
        if (!empty($userPassword) && \strlen($userPassword) > 32) {
115
            $this->userPassword = \substr($userPassword, 0, 32);
0 ignored issues
show
Bug Best Practice introduced by
The property userPassword does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
116
        } else {
117
            $this->userPassword = $userPassword;
118
        }
119
120
        if (!\is_null($this->ownerPassword)) {
121
            $password = $this->decryptPassword($this->ownerPassword);
122
            $this->fileKey = $this->makeFileKeyOld($password);
0 ignored issues
show
Bug Best Practice introduced by
The property fileKey does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
123
            if (!$this->testFileKey($this->fileKey)) {
124
                $this->fileKey = null;
125
            }
126
        } else {
127
            $this->fileKey = null;
128
        }
129
130
        // If owner password was invalid, try user password
131
        if (\is_null($this->fileKey)) {
132
            $password = \is_null($this->userPassword) ? "" : $this->userPassword;
133
            $this->fileKey = $this->makeFileKeyOld($password);
134
            if (!$this->testFileKey($this->fileKey))
135
                throw new InvalidPassword();
136
        }
137
    }
138
139
140
    /**
141
     * Generate the user password by creating a key by hashing the supplied
142
     * owner password and using it to decrypt the owner key from the file data.
143
     */
144
    function decryptPassword(string $password)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
145
    {
146
        if (\strlen($password) >= 32) {
147
            $data = \substr($password, 0, 32);
148
        } else {
149
            $data = $password.\substr($this->passwordPadding, 0, 32 - \strlen($password));
150
        }
151
        $hash = \md5($data, true);
152
153
        switch ($this->info->getRevision()) {
154
            case 2:
155
                // Try to decrypt the hashed user password and see if matches the padding string
156
                return \openssl_decrypt($this->info->getOwnerKey(), "RC4-40", $hash, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING);
157
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
158
159
            case 3:
160
                for ($round = 0; $round < 50; ++$round) {
161
                    $hash = \md5($hash, true);
162
                }
163
                return $this->magicDecrypt($this->info->getOwnerKey(), $hash);
164
                break;
165
        }
166
    }
167
168
169
    function makeFileKeyOld($password)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
170
    {
171
        $permBytes = \Smalot\PdfParser\Utils::lowestBytesStr($this->info->getPerms(), 4);
172
        $padding = \substr($this->passwordPadding, 0, 32 - \strlen($password));
173
        $data = $password.$padding.$this->info->getOwnerKey().$permBytes.$this->info->getDocID();
174
        $len = \strlen($data);
0 ignored issues
show
Unused Code introduced by
The assignment to $len is dead and can be removed.
Loading history...
175
        if (!$this->info->getEncryptMetadata()) {
176
            $data .= \Smalot\PdfParser\Utils::lowestBytesStr(-1, 4);
177
        }
178
179
        $hash = \md5($data, true);
180
        if ($this->info->getRevision() == 3) {
181
            for ($round = 0; $round < 50; ++$round) {
182
                $hash = \md5($hash, true);
183
            }
184
        }
185
        return \substr($hash, 0, $this->info->getFileKeyLength());
186
    }
187
188
189
    /**
190
     * Check that the file key is valid.
191
     *
192
     * @return bool
193
     */
194
    function testFileKey(string $key)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
195
    {
196
        switch ($this->info->getRevision()) {
197
            case 2:
198
                // Try to decrypt the hashed user password and see if matches the padding string
199
                $plaintext = \openssl_decrypt($this->info->getUserKey(), "RC4-40", $key, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING);
200
                return $plaintext === $this->passwordPadding;
201
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
202
203
            case 3:
204
                // Try to decrypt the hashed user password 20 times using a XOR
205
                // cycling of the key and see if matches the padding string
206
                $data = $this->magicDecrypt($this->info->getUserKey(), $key);
207
                $hash = \md5($this->passwordPadding.$this->info->getDocID(), true);
208
                return \substr($data, 0, 16) == $hash;
209
                break;
210
211
            default:
212
                throw new InvalidRevision("Mismatch between caller and testPasswordOld()");
213
        }
214
    }
215
216
217
    /**
218
     * Multi-round basic decryption used by revision 3.
219
     *
220
     * @return byte string
221
     */
222
    function magicDecrypt(string $data, string $key)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
223
    {
224
        for ($i = 19; $i >= 0; --$i) {
225
            $roundKey = \implode(\array_map(
226
                function($c) use ($i) { return \chr(\ord($c) ^ $i); },
227
                \str_split($key)
0 ignored issues
show
Bug introduced by
It seems like str_split($key) can also be of type true; however, parameter $array of array_map() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

227
                /** @scrutinizer ignore-type */ \str_split($key)
Loading history...
228
            ));
229
            $data = \openssl_decrypt($data, "RC4-40", $roundKey, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING);
230
        }
231
        return $data;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $data returns the type string which is incompatible with the documented return type Smalot\PdfParser\Encryption\byte.
Loading history...
232
    }
233
}
234
235
236
/**
237
* Handles file keys for encryption revisions 5 and 6.
238
*/
239
class NewFileKey extends FileKey
240
{
241
    /**
242
     * @throws InvalidPassword if neither of the supplied passwords are valid
243
     */
244
    function __construct(Info $info, $ownerPassword = null, $userPassword = null)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
245
    {
246
        parent::__construct($info);
247
248
        if (!empty($ownerPassword) && \strlen($ownerPassword) > 127) {
249
            $this->ownerPassword = \substr($ownerPassword, 0, 127);
0 ignored issues
show
Bug Best Practice introduced by
The property ownerPassword does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
250
        } else {
251
            $this->ownerPassword = $ownerPassword;
252
        }
253
        if (!empty($userPassword) && \strlen($userPassword) > 127) {
254
            $this->userPassword = \substr($userPassword, 0, 127);
0 ignored issues
show
Bug Best Practice introduced by
The property userPassword does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
255
        } else {
256
            $this->userPassword = $userPassword;
257
        }
258
259
        // Revision 5 and 6 keys are 48 bytes long
260
        //   Bytes 0-31: password hash
261
        //   Bytes 32-39: password check hash salt
262
        //   Bytes 40-47: file key hash salt
263
        // Note that when using the owner password, the whole user key
264
        // is also included in the hash input when checking the
265
        // password and decrypting the file key (which is encrypted
266
        // twice, using different intermediate keys, in ownerEnc and
267
        // userEnc)
268
        if (!\is_null($this->ownerPassword)) {
269
            $mainKey = $this->info->getOwnerKey();
270
            $additionalKey = $this->info->getUserKey();
271
            $password = $this->ownerPassword;
272
            $encryptedFileKey = $this->info->getOwnerEnc();
273
            $passwordHash = $this->hashPassword($password, $mainKey, $additionalKey);
274
            if (!$this->testPasswordHash($passwordHash, $mainKey)) {
275
                $passwordHash = null;
276
            }
277
        } else {
278
            $passwordHash = null;
279
        }
280
281
        if (\is_null($passwordHash)) {
282
            $mainKey = $this->info->getUserKey();
283
            $additionalKey = "";
284
            $password = \is_null($this->userPassword) ? "" : $this->userPassword;
285
            $encryptedFileKey = $this->info->getUserEnc();
286
            $passwordHash = $this->hashPassword($password, $mainKey, $additionalKey);
287
            if (!$this->testPasswordHash($passwordHash, $mainKey)) {
288
                throw new InvalidPassword();
289
            }
290
        }
291
292
        $this->fileKey = $this->makeFileKeyNew($password, $mainKey, $additionalKey, $encryptedFileKey);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $encryptedFileKey does not seem to be defined for all execution paths leading up to this point.
Loading history...
Bug Best Practice introduced by
The property fileKey does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
Comprehensibility Best Practice introduced by
The variable $additionalKey does not seem to be defined for all execution paths leading up to this point.
Loading history...
Comprehensibility Best Practice introduced by
The variable $mainKey does not seem to be defined for all execution paths leading up to this point.
Loading history...
Comprehensibility Best Practice introduced by
The variable $password does not seem to be defined for all execution paths leading up to this point.
Loading history...
293
    }
294
295
296
    /**
297
     * Make a hash that can be used to check whether a given password matches
298
     * the one the file was created with.
299
     *
300
     * @param $password         Owner or user password
301
     * @param $mainKey
302
     * @param $additionalKey    0 or 48 byte string (when using owner password)
0 ignored issues
show
Documentation Bug introduced by
The doc comment 0 at position 0 could not be parsed: Unknown type name '0' at position 0 in 0.
Loading history...
303
     * @return 32 byte string
0 ignored issues
show
Documentation Bug introduced by
The doc comment 32 at position 0 could not be parsed: Unknown type name '32' at position 0 in 32.
Loading history...
304
     */
305
    function hashPassword(string $password, string $mainKey, string $additionalKey)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
306
    {
307
        // Use first half of latter 16 bytes of $mainKey and all of
308
        // $additionalKey (if any)
309
        return $this->intermediateKey($password, \substr($mainKey, 32, 8), $additionalKey);
310
    }
311
312
313
    /**
314
     * Check the hash against the first 32 bytes of the key.
315
     *
316
     * @return bool
317
     */
318
    function testPasswordHash(string $passwordHash, string $key)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
319
    {
320
        return \substr($key, 0, 32) == $passwordHash;
321
    }
322
323
324
    /**
325
     * Decrypt the relevant ...Enc field.
326
     *
327
     * @param $password         Owner or user password
328
     * @param $mainKey
329
     * @param $additionalKey    0 or 48 byte string (when using owner password)
0 ignored issues
show
Documentation Bug introduced by
The doc comment 0 at position 0 could not be parsed: Unknown type name '0' at position 0 in 0.
Loading history...
330
     * @param $encryptedFileKey 32 byte string
331
     *
332
     * @return 32 byte string
0 ignored issues
show
Documentation Bug introduced by
The doc comment 32 at position 0 could not be parsed: Unknown type name '32' at position 0 in 32.
Loading history...
333
     *
334
     * @throws DecryptionError if the password can't decrypt the file key; shouldn't be possible
335
     */
336
    function makeFileKeyNew($password, $mainKey, $additionalKey, $encryptedFileKey)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
337
    {
338
        // Use second half of latter 16 bytes of $mainKey and all of
339
        // $additionalKey (if any)
340
        $key = $this->intermediateKey($password, \substr($mainKey, 40, 8), $additionalKey);
341
        $iv = \Smalot\PdfParser\Utils::byteString(16);
342
        $decryptedKey = \openssl_decrypt($encryptedFileKey, "aes-256-cbc", $key, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING, $iv);
343
        if ($decryptedKey === false) {
344
            throw new DecryptionError("Decryption failed");
345
        }
346
        return $decryptedKey;
347
    }
348
349
350
    /**
351
     * Generate a hash that can either be used to decrypt the relevant ...Enc
352
     * field and get the file key, or verify the password.
353
     *
354
     * @param $password         Owner or user password
355
     * @param $saltKey          8 byte string: part of the corresponding key
0 ignored issues
show
Documentation Bug introduced by
The doc comment 8 at position 0 could not be parsed: Unknown type name '8' at position 0 in 8.
Loading history...
356
     * @param $additionalKey    0 or 48 byte string (when using owner password)
357
     * @return 32 byte string
0 ignored issues
show
Documentation Bug introduced by
The doc comment 32 at position 0 could not be parsed: Unknown type name '32' at position 0 in 32.
Loading history...
358
     */
359
    function intermediateKey($password, $saltKey, $additionalKey)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
360
    {
361
        $hashInput = $password.$saltKey.$additionalKey;
362
363
        switch ($this->info->getRevision()) {
364
            case 5:
365
                return \hash("sha256", $hashInput, true);
366
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
367
368
            case 6:
369
                /* Each round encrypts a plaintext comprising 64 copies of the
370
                 * input data plus the hash of the previous round, using a key
371
                 * and IV derived from the hash of the previous round.  Then a
372
                 * hash of that round's cyphertext is produced using a
373
                 * different hash algorithm (selected based on the cyphertext).
374
                 * The number of rounds is not fixed; the decision to continue
375
                 * is based on the cyphertext (see PDFDecryptionKey::hashContinue()).
376
                 * 32 bytes of the hash produced by the final round is returned.
377
                 */
378
379
                // initial hash
380
                $hash = \hash("sha256", $hashInput, true);
381
382
                $this->round = 0;
0 ignored issues
show
Bug Best Practice introduced by
The property round does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
383
                do {
384
                    $hashInput = $password.$hash.$additionalKey;
385
                    $blob = "";
386
                    for ($i = 0; $i < 64; ++$i) {
387
                        $blob .= $hashInput;
388
                    }
389
                    $key = \substr($hash, 0, 16);
390
                    $iv = \substr($hash, 16, 16);
391
392
                    // This is different from the key decryption algorithm
393
                    $this->cyphertext = \openssl_encrypt($blob, "aes-128-cbc", $key, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING, $iv);
0 ignored issues
show
Bug Best Practice introduced by
The property cyphertext does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
394
                    // Derive some 64 bit numbers from the cyphertext and
395
                    // analyse them to determine the hash algorithm to use this
396
                    // round
397
                    $splinters = self::extractSplinters($this->cyphertext);
398
                    $remainder = (((((($splinters[0] % 3) << 32) | $splinters[1]) % 3) << 32) | $splinters[2]) % 3;
399
                    switch ($remainder) {
400
                        case 0:
401
                            $hash = \hash("sha256", $this->cyphertext, true);
402
                            break;
403
404
                        case 1:
405
                            $hash = \hash("sha384", $this->cyphertext, true);
406
                            break;
407
408
                        case 2:
409
                            $hash = \hash("sha512", $this->cyphertext, true);
410
                            break;
411
                    }
412
                    ++$this->round;
413
                } while ($this->hashContinue());
414
415
                return \substr($hash, 0, 32);
416
                break;
417
418
            default:
419
                throw new InvalidRevision("Unsupported revision in intermediateKey()");
420
        }
421
    }
422
423
424
    /**
425
     * Determine whether to continue based on the round count and an arbitrary
426
     * value within the current round's cyphertext.  A minimum of 64 and a
427
     * maximum of 288 rounds will be done.
428
     *
429
     * @return boolean
430
     */
431
    function hashContinue()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
432
    {
433
        $lastByte = \ord(\substr($this->cyphertext, -1));
434
        $currentLimit = \max(64, $lastByte + 32);
435
        return $this->round < $currentLimit;
436
    }
437
438
439
    /**
440
     * Derive 3 integers from the first 16 bytes of $input.
441
     * Bytes are treated as being unsigned, in big-endian order.
442
     *
443
     * @input a byte string of at least 16 characters
444
     *
445
     * @return array of 3 integers
446
     */
447
    static function extractSplinters($input)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
448
    {
449
        $result = [];
450
        $result[] = \hexdec(\bin2hex(\substr($input, 0, 8)));
451
        $result[] = \hexdec(\bin2hex(\Smalot\PdfParser\Utils::byteString(4).\substr($input, 8, 4)));
452
        $result[] = \hexdec(\bin2hex(\Smalot\PdfParser\Utils::byteString(4).\substr($input, 12, 4)));
453
        return $result;
454
    }
455
}
456