Passed
Branch master (7d5d56)
by Carlos
04:10
created

Encryptor::signature()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 0
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
ccs 0
cts 3
cp 0
crap 2
1
<?php
2
3
/*
4
 * This file is part of the overtrue/wechat.
5
 *
6
 * (c) overtrue <[email protected]>
7
 *
8
 * This source file is subject to the MIT license that is bundled
9
 * with this source code in the file LICENSE.
10
 */
11
12
namespace EasyWeChat\Kernel;
13
14
use EasyWeChat\Kernel\Exceptions\RuntimeException;
15
use EasyWeChat\Kernel\Support\AES;
16
use EasyWeChat\Kernel\Support\XML;
17
use Throwable;
18
use function EasyWeChat\Kernel\Support\str_random;
0 ignored issues
show
introduced by
The function EasyWeChat\Kernel\Support\str_random was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
19
20
/**
21
 * Class Encryptor.
22
 *
23
 * @author overtrue <[email protected]>
24
 */
25
class Encryptor
26
{
27
    const ERROR_INVALID_SIGNATURE = -40001; // Signature verification failed
28
    const ERROR_PARSE_XML = -40002; // Parse XML failed
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 9 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
29
    const ERROR_CALC_SIGNATURE = -40003; // Calculating the signature failed
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 4 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
30
    const ERROR_INVALID_AES_KEY = -40004; // Invalid AESKey
1 ignored issue
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 3 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
31
    const ERROR_INVALID_APP_ID = -40005; // Check AppID failed
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 4 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
32
    const ERROR_ENCRYPT_AES = -40006; // AES EncryptionInterface failed
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 7 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
33
    const ERROR_DECRYPT_AES = -40007; // AES decryption failed
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 7 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
34
    const ERROR_INVALID_XML = -40008; // Invalid XML
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 7 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
35
    const ERROR_BASE64_ENCODE = -40009; // Base64 encoding failed
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 5 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
36
    const ERROR_BASE64_DECODE = -40010; // Base64 decoding failed
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 5 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
37
    const ERROR_XML_BUILD = -40011; // XML build failed
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 9 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
38
    const ILLEGAL_BUFFER = -41003; // Illegal buffer
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 10 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
39
40
    /**
41
     * App id.
42
     *
43
     * @var string
44
     */
45
    protected $appId;
46
47
    /**
48
     * App token.
49
     *
50
     * @var string
51
     */
52
    protected $token;
53
54
    /**
55
     * @var string
56
     */
57
    protected $aesKey;
58
59
    /**
60
     * Block size.
61
     *
62
     * @var int
63
     */
64
    protected $blockSize = 32;
65
66
    /**
67
     * Constructor.
68
     *
69
     * @param string      $appId
70
     * @param string|null $token
71
     * @param string|null $aesKey
72
     */
73
    public function __construct(string $appId, string $token = null, string $aesKey = null)
74
    {
75
        $this->appId = $appId;
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 2 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
76
        $this->token = $token;
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 2 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
77
        $this->aesKey = base64_decode($aesKey.'=', true);
78
    }
79
80
    /**
81
     * Get the app token.
82
     *
83
     * @return string
84
     */
85
    public function getToken(): string
86
    {
87
        return $this->token;
88
    }
89
90
    /**
91
     * Encrypt the message and return XML.
92
     *
93
     * @param string $xml
94
     * @param string $nonce
95
     * @param int    $timestamp
96
     *
97
     * @return string
98
     *
99
     * @throws \EasyWeChat\Kernel\Exceptions\RuntimeException
100
     */
101
    public function encrypt($xml, $nonce = null, $timestamp = null): string
102
    {
103
        try {
104
            $xml = $this->pkcs7Pad(str_random(16).pack('N', strlen($xml)).$xml.$this->appId, $this->blockSize);
105
106
            $encrypted = base64_encode(AES::encrypt(
107
                $xml,
108
                $this->aesKey,
109
                substr($this->aesKey, 0, 16),
110
                OPENSSL_NO_PADDING
111
            ));
112
        } catch (Throwable $e) { // @codeCoverageIgnore
113
            throw new RuntimeException($e->getMessage(), self::ERROR_ENCRYPT_AES); // @codeCoverageIgnore
114
        }
115
116
        !is_null($nonce) || $nonce = substr($this->appId, 0, 10);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 9 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
117
        !is_null($timestamp) || $timestamp = time();
118
119
        $response = [
120
            'Encrypt' => $encrypted,
121
            'MsgSignature' => $this->signature($this->token, $timestamp, $nonce, $encrypted),
122
            'TimeStamp' => $timestamp,
123
            'Nonce' => $nonce,
124
        ];
125
126
        //生成响应xml
127
        return XML::build($response);
128
    }
129
130
    /**
131
     * Decrypt message.
132
     *
133
     * @param string $content
134
     * @param string $msgSignature
135
     * @param string $nonce
136
     * @param string $timestamp
137
     *
138
     * @return string
139
     *
140
     * @throws \EasyWeChat\Kernel\Exceptions\RuntimeException
141
     */
142
    public function decrypt($content, $msgSignature, $nonce, $timestamp): string
143
    {
144
        $signature = $this->signature($this->token, $timestamp, $nonce, $content);
145
146
        if ($signature !== $msgSignature) {
147
            throw new RuntimeException('Invalid Signature.', self::ERROR_INVALID_SIGNATURE);
148
        }
149
150
        $decrypted = AES::decrypt(
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 2 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
151
            base64_decode($content, true),
152
            $this->aesKey,
153
            substr($this->aesKey, 0, 16),
154
            OPENSSL_NO_PADDING
155
        );
156
        $result = $this->pkcs7Unpad($decrypted);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 5 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
157
        $content = substr($result, 16, strlen($result));
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 4 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
158
        $contentLen = unpack('N', substr($content, 0, 4))[1];
0 ignored issues
show
Bug introduced by
The call to unpack() has too few arguments starting with offset. ( Ignorable by Annotation )

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

158
        $contentLen = /** @scrutinizer ignore-call */ unpack('N', substr($content, 0, 4))[1];

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
159
160
        if (trim(substr($content, $contentLen + 4)) !== $this->appId) {
161
            throw new RuntimeException('Invalid appId.', self::ERROR_INVALID_APP_ID);
162
        }
163
164
        return substr($content, 4, $contentLen);
165
    }
166
167
    /**
168
     * Get SHA1.
169
     *
170
     * @return string
171
     *
172
     * @throws self
173
     */
174
    public function signature(): string
175
    {
176
        $array = func_get_args();
177
        sort($array, SORT_STRING);
178
179
        return sha1(implode($array));
0 ignored issues
show
Bug introduced by
The call to implode() has too few arguments starting with pieces. ( Ignorable by Annotation )

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

179
        return sha1(/** @scrutinizer ignore-call */ implode($array));

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
Bug introduced by
$array of type array is incompatible with the type string expected by parameter $glue of implode(). ( Ignorable by Annotation )

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

179
        return sha1(implode(/** @scrutinizer ignore-type */ $array));
Loading history...
180
    }
181
182
    /**
183
     * PKCS#7 pad.
184
     *
185
     * @param string $text
186
     * @param int    $blockSize
187
     *
188
     * @return string
189
     *
190
     * @throws \EasyWeChat\Kernel\Exceptions\RuntimeException
191
     */
192
    public function pkcs7Pad(string $text, int $blockSize): string
193
    {
194
        if ($blockSize > 256) {
195
            throw new RuntimeException('$blockSize may not be more than 256');
196
        }
197
        $padding = $blockSize - (strlen($text) % $blockSize);
198
        $pattern = chr($padding);
199
200
        return $text.str_repeat($pattern, $padding);
201
    }
202
203
    /**
204
     * PKCS#7 unpad.
205
     *
206
     * @param string $text
207
     *
208
     * @return string
209
     */
210
    public function pkcs7Unpad(string $text): string
211
    {
212
        $pad = ord(substr($text, -1));
213
        if ($pad < 1 || $pad > $this->blockSize) {
214
            $pad = 0;
215
        }
216
217
        return substr($text, 0, (strlen($text) - $pad));
218
    }
219
}
220