Passed
Branch master (25d5dd)
by Alexey
02:30
created

PKDecryptionStreamFilter::filter()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 25
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 14
nc 5
nop 4
dl 0
loc 25
ccs 15
cts 15
cp 1
crap 4
rs 9.7998
c 0
b 0
f 0
1
<?php
2
3
namespace PhpZip\IO\Filter\Cipher\Pkware;
4
5
use PhpZip\Exception\ZipException;
6
use PhpZip\Model\ZipEntry;
7
8
/**
9
 * Decryption PKWARE Traditional Encryption.
10
 */
11
class PKDecryptionStreamFilter extends \php_user_filter
12
{
13
    const FILTER_NAME = 'phpzip.decryption.pkware';
14
15
    /** @var int */
16
    private $checkByte = 0;
17
18
    /** @var int */
19
    private $readLength = 0;
20
21
    /** @var int */
22
    private $size = 0;
23
24
    /** @var bool */
25
    private $readHeader = false;
26
27
    /** @var PKCryptContext */
28
    private $context;
29
30
    /**
31
     * @return bool
32
     */
33 5
    public static function register()
34
    {
35 5
        return stream_filter_register(self::FILTER_NAME, __CLASS__);
36
    }
37
38
    /**
39
     * @see https://php.net/manual/en/php-user-filter.oncreate.php
40
     *
41
     * @return bool
42
     */
43 5
    public function onCreate()
44
    {
45 5
        if (!isset($this->params['entry'])) {
46
            return false;
47
        }
48
49 5
        if (!($this->params['entry'] instanceof ZipEntry)) {
50
            throw new \RuntimeException('ZipEntry expected');
51
        }
52
        /** @var ZipEntry $entry */
53 5
        $entry = $this->params['entry'];
54 5
        $password = $entry->getPassword();
55
56 5
        if ($password === null) {
57
            return false;
58
        }
59
60 5
        $this->size = $entry->getCompressedSize();
61
62
        // init context
63 5
        $this->context = new PKCryptContext($password);
64
65
        // init check byte
66 5
        if ($entry->isDataDescriptorEnabled()) {
67 4
            $this->checkByte = ($entry->getDosTime() >> 8) & 0xff;
68
        } else {
69 1
            $this->checkByte = ($entry->getCrc() >> 24) & 0xff;
70
        }
71
72 5
        $this->readLength = 0;
73 5
        $this->readHeader = false;
74
75 5
        return true;
76
    }
77
78
    /**
79
     * Decryption filter.
80
     *
81
     * @param resource $in
82
     * @param resource $out
83
     * @param int      $consumed
84
     * @param bool     $closing
85
     *
86
     * @throws ZipException
87
     *
88
     * @return int
89
     *
90
     * @todo USE FFI in php 7.4
91
     */
92 5
    public function filter($in, $out, &$consumed, $closing)
93
    {
94 5
        while ($bucket = stream_bucket_make_writeable($in)) {
95 5
            $buffer = $bucket->data;
96 5
            $this->readLength += $bucket->datalen;
97
98 5
            if ($this->readLength > $this->size) {
99 5
                $buffer = substr($buffer, 0, $this->size - $this->readLength);
100
            }
101
102 5
            if (!$this->readHeader) {
103 5
                $header = substr($buffer, 0, PKCryptContext::STD_DEC_HDR_SIZE);
104 5
                $this->context->checkHeader($header, $this->checkByte);
105
106 4
                $buffer = substr($buffer, PKCryptContext::STD_DEC_HDR_SIZE);
107 4
                $this->readHeader = true;
108
            }
109
110 4
            $bucket->data = $this->context->decryptString($buffer);
111
112 4
            $consumed += $bucket->datalen;
113 4
            stream_bucket_append($out, $bucket);
114
        }
115
116 5
        return \PSFS_PASS_ON;
117
    }
118
}
119