Passed
Push — master ( 07feba...51d451 )
by Charles
06:12
created

Json25519Parser::getDecryptedBody()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace yrc\web;
4
5
use ncryptf\Request;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, yrc\web\Request. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
6
use ncryptf\Response;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, yrc\web\Response. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
7
use yrc\models\redis\EncryptionKey;
8
use yii\base\InvalidParamException;
9
use yii\helpers\Json;
10
use yii\web\JsonParser;
11
use yii\web\BadRequestHttpException;
12
use Yii;
13
14
use Exception;
15
16
/**
17
 * Allows for requests to be encrypted and signed via Curve/Ed 25519 cryptography via libsodium
18
 * @class Json25519 Parser
19
 */
20
class Json25519Parser extends JsonParser
21
{
22
    /**
23
     * @const HASH_HEADER
24
     */
25
    const HASH_HEADER = 'x-hashid';
26
27
    /**
28
     * @const PUBLICKEY_HEADER
29
     */
30
    const PUBLICKEY_HEADER = 'x-pubkey';
31
32
    /**
33
     * @const NONCE_HEADER
34
     */
35
    const NONCE_HEADER = 'x-nonce';
36
37
    private $decryptedBody;
38
39
    public function getDecryptedBody()
40
    {
41
        return $this->decryptedBody;
42
    }
43
    
44
    /**
45
     * Parses a HTTP request body.
46
     * @param string $rawBody the raw HTTP request body.
47
     * @param string $contentType the content type specified for the request body.
48
     * @return array parameters parsed from the request body
49
     * @throws BadRequestHttpException if the body contains invalid json and [[throwException]] is `true`.
50
     */
51
    public function parse($rawBody, $contentType)
52
    {
53
        $key = self::getKeyFromHash(Yii::$app->request->getHeaders()->get(self::HASH_HEADER, null));
54
        $nonce = Yii::$app->request->getHeaders()->get(self::NONCE_HEADER, null);
55
        $public = Yii::$app->request->getHeaders()->get(self::PUBLICKEY_HEADER, null);
56
57
        try {
58
            $rawBody = $this->getRawBodyFromTokenAndNonce(
59
                $key,
60
                \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

60
                \base64_decode(/** @scrutinizer ignore-type */ $nonce),
Loading history...
61
                \base64_decode($public),
62
                \base64_decode($rawBody)
63
            );
64
65
            if ($rawBody === false) {
66
                throw new BadRequestHttpException(Yii::t('yrc', 'Unable to decrypt request.'));
67
            }
68
        } catch (\Exception $e) {
69
            throw new BadRequestHttpException(Yii::t('yrc', 'Invalid security headers.'));
70
        }
71
72
        try {
73
            $this->decryptedBody = $rawBody;
74
            $parameters = Json::decode($rawBody, $this->asArray);
75
            return $parameters === null ? [] : $parameters;
76
        } catch (InvalidParamException $e) {
77
            if ($this->throwException) {
78
                throw new BadRequestHttpException('Invalid JSON data in request body: ' . $e->getMessage());
79
            }
80
            return [];
81
        }
82
    }
83
84
    /**
85
     * Decrypts the raw body using EncryptionKey, the client submitted nonce and crypto_box_open
86
     * @param EncryptionKey $key
87
     * @param string $nonce
88
     * @param string $rawBody
89
     * @return string
90
     */
91
    private function getRawBodyFromTokenAndNonce(EncryptionKey $key, string $nonce, string $public, string $rawBody)
92
    {
93
        try {
94
            $response = new Response(
95
                \base64_decode($key->secret),
96
                $public
97
            );
98
99
            $rawBody = $response->decrypt(
100
                $rawBody,
101
                $nonce
102
            );
103
104
            $key->delete();
105
106
            return $rawBody;
107
        } catch (\Exception $e) {
108
            Yii::error('Unable to decrypt request.', 'yrc');
109
            throw new BadRequestHttpException(Yii::t('yrc', 'Unable decrypt response with provided data.'));
110
        }
111
    }
112
113
    /**
114
     * Helper method to retrieve and valdiate the token
115
     * @return Token
0 ignored issues
show
Bug introduced by
The type yrc\web\Token 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...
116
     */
117
    public static function getKeyFromHash($hash = null)
118
    {
119
        if ($hash === null) {
120
            $hash = Yii::$app->request->getHeaders()->get(self::HASH_HEADER, null);
121
        }
122
123
        // Fetch the hash from the header
124
        if ($hash === null) {
125
            throw new BadRequestHttpException(Yii::t('yrc', 'Missing x-hashid header'));
126
        }
127
128
        $token = EncryptionKey::find()
129
            ->where([
130
                'hash' => $hash
131
            ])->one();
132
133
        if ($token === null) {
134
            throw new BadRequestHttpException(Yii::t('yrc', 'Invalid x-hashid header.'));
135
        }
136
137
        return $token;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $token returns the type yii\redis\ActiveRecord|array which is incompatible with the documented return type yrc\web\Token.
Loading history...
138
    }
139
}
140