Passed
Push — master ( 843189...d0e20a )
by Charles
06:07
created

JsonParser::getEncryptionKey()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 26
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 14
nc 3
nop 3
dl 0
loc 26
rs 9.7998
c 0
b 0
f 0
1
<?php declare(strict_types=1);
2
3
namespace yrc\web\ncryptf;
4
5
use InvalidArgumentException;
6
use ncryptf\Request;
7
use ncryptf\Response;
8
use ncryptf\exceptions\DecryptionFailedException;
9
use yrc\models\redis\EncryptionKey;
10
use yrc\web\Request as YiiRequest;
11
use yii\base\Exception;
12
use yii\base\InvalidParamException;
13
use yii\helpers\Json;
14
use yii\web\BadRequestHttpException;
15
use Yii;
16
17
/**
18
 * Parses vnd.ncryptf+json
19
 * @class Ncryptf JsonParser
20
 */
21
class JsonParser extends \yii\web\JsonParser
22
{
23
    private $decryptedBody;
24
25
    /**
26
     * Returns the decrypted response
27
     *
28
     * @return string
29
     */
30
    public function getDecryptedBody() :? string
31
    {
32
        return $this->decryptedBody;
33
    }
34
35
    /**
36
     * Parses vnd.ncryptf+json
37
     *
38
     * @param string  $rawBody
39
     * @param string $contentType
40
     * @return mixed
41
     */
42
    public function parse($rawBody, $contentType)
43
    {
44
        if ($contentType === 'application/vnd.25519+json') {
45
            Yii::warning([
46
                'message' => '`application/vnd.25519+json` content type is deprecated. Migrate to `application/vnd.ncryptf+json'
47
            ]);
48
        }
49
50
        if ($rawBody === '') {
51
            $this->decryptedBody = '';
52
            return [];
53
        }
54
55
        $request = Yii::$app->request;
0 ignored issues
show
Documentation Bug introduced by
It seems like Yii::app->request can also be of type yii\web\Request. However, the property $request is declared as type yii\console\Request. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
56
        $version = Response::getVersion(\base64_decode($rawBody));
57
        $key = $this->getEncryptionKey($request, $rawBody, $version);
0 ignored issues
show
Bug introduced by
It seems like $request can also be of type yii\console\Request; however, parameter $request of yrc\web\ncryptf\JsonParser::getEncryptionKey() does only seem to accept yrc\web\Request, 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

57
        $key = $this->getEncryptionKey(/** @scrutinizer ignore-type */ $request, $rawBody, $version);
Loading history...
58
59
        try {
60
            $this->decryptedBody = $this->decryptRequest($key, $request, $rawBody, $version);
61
        } catch (DecryptionFailedException | InvalidArgumentException | InvalidSignatureException | InvalidChecksumException $e) {
0 ignored issues
show
Bug introduced by
The type yrc\web\ncryptf\InvalidSignatureException 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...
Bug introduced by
The type yrc\web\ncryptf\InvalidChecksumException 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...
62
            throw new BadRequestHttpException(Yii::t('yrc', 'Unable to decrypt response.'));
63
        } catch (\Exception $e) {
64
            Yii::warning([
65
                'message' => 'An unexpected error occured when decryption the response. See attached exception',
66
                'exception' => $e
67
            ]);
68
69
            throw new BadRequestHttpException(Yii::t('yrc', 'Unable to decrypt response.'));
70
        }
71
72
        try {
73
            $parameters = Json::decode($this->decryptedBody, $this->asArray);
74
            return $parameters ?? [];
75
        } catch (InvalidParamException $e) {
76
            if ($this->throwException) {
77
                throw new BadRequestHttpException('Invalid JSON data in request body: ' . $e->getMessage());
78
            }
79
            return [];
80
        }
81
    }
82
83
    /**
84
     * Decrypts the request using a given encryption key and request parameters
85
     *
86
     * @param EncryptionKey $key
87
     * @param Request $request
88
     * @param string $rawBody
89
     * @param string $version
90
     * @return string
91
     */
92
    private function decryptRequest(EncryptionKey $key, \yrc\web\Request $request, string $rawBody, int $version)
93
    {
94
        static $response = null;
95
        static $nonce = null;
96
        static $publicKey = null;
97
        
98
        $response = new Response(
99
            \base64_decode($key->secret)
100
        );
101
102
        if ($version === 1) {
103
            $publicKey = $request->headers->get('x-pubkey', null);
104
            $nonce = $request->headers->get('x-nonce', null);
105
106
            if ($publicKey === null || $nonce === null) {
0 ignored issues
show
introduced by
The condition $nonce === null is always false.
Loading history...
107
                throw new Exception(Yii::t('yrc', 'Missing nonce or public key header. Unable to decrypt request.'));
108
            }
109
            $nonce = \base64_decode($nonce);
0 ignored issues
show
Bug introduced by
It seems like $nonce can also be of type array; however, parameter $data of base64_decode() does only seem to accept string, 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

109
            $nonce = \base64_decode(/** @scrutinizer ignore-type */ $nonce);
Loading history...
110
            $publicKey = \base64_decode($publicKey);
111
        }
112
113
        $decryptedRequest = $response->decrypt(
114
            \base64_decode($rawBody),
115
            $publicKey,
116
            $nonce
117
        );
118
119
        if ($key->is_single_use) {
120
            $key->delete();
121
        }
122
123
        return $decryptedRequest;
124
    }
125
126
    /**
127
     * Fetches the local encryption key from the data provided in the request
128
     *
129
     * @param yrc\web\Request $request
0 ignored issues
show
Bug introduced by
The type yrc\web\ncryptf\yrc\web\Request was not found. Did you mean yrc\web\Request? If so, make sure to prefix the type with \.
Loading history...
130
     * @param string $rawBody
131
     * @param integer $version
132
     * @return EncryptionKey
133
     */
134
    private function getEncryptionKey(\yrc\web\Request $request, string $rawBody, int $version) : EncryptionKey
0 ignored issues
show
Unused Code introduced by
The parameter $version is not used and could be removed. ( Ignorable by Annotation )

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

134
    private function getEncryptionKey(\yrc\web\Request $request, string $rawBody, /** @scrutinizer ignore-unused */ int $version) : EncryptionKey

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $rawBody is not used and could be removed. ( Ignorable by Annotation )

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

134
    private function getEncryptionKey(\yrc\web\Request $request, /** @scrutinizer ignore-unused */ string $rawBody, int $version) : EncryptionKey

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
135
    {
136
        
137
        $lookup = $request->headers->get('x-hashid', null);
138
        if ($lookup === null) {
0 ignored issues
show
introduced by
The condition $lookup === null is always false.
Loading history...
139
            Yii::warning([
140
                'message' => 'X-HashId missing on request. Unable to decrypt response.'
141
            ]);
142
            throw new Exception(Yii::t('yrc', 'Unable to decrypt response.'));
143
        }
144
145
        $key = EncryptionKey::find()->where([
146
            'hash' => $lookup
147
        ])->one();
148
149
        if ($key === null) {
150
            Yii::warning([
151
                'message' => Yii::t('yrc', '{property} not found in database', [
152
                    'property' => $property
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $property seems to be never defined.
Loading history...
153
                ])
154
            ]);
155
            
156
            throw new Exception(Yii::t('yrc', 'Unable to decrypt response.'));
157
        }
158
159
        return $key;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $key returns the type array which is incompatible with the type-hinted return yrc\models\redis\EncryptionKey.
Loading history...
160
    }
161
}
162