Test Failed
Push — master ( 42e257...dadfc2 )
by Charles
02:28
created

NcryptfPayload::checkRequest()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 9
rs 10
c 0
b 0
f 0
cc 3
nc 3
nop 1
1
<?php declare(strict_types=1);
2
3
namespace ncryptf\middleware;
4
5
use Exception;
6
7
use ncryptf\Response;
8
use ncryptf\middleware\EncryptionKeyInterface;
9
10
use Middlewares\JsonPayload;
11
use Middlewares\Utils\HttpErrorException;
12
use Psr\SimpleCache\CacheInterface;
13
use Psr\SimpleCache\InvalidArgumentException;
14
15
use Psr\Http\Server\MiddlewareInterface;
16
use Psr\Http\Message\StreamInterface;
17
use Psr\Http\Message\ResponseInterface;
18
use Psr\Http\Message\ServerRequestInterface;
19
use Psr\Http\Server\RequestHandlerInterface;
20
21
final class NcryptfPayload extends JsonPayload implements MiddlewareInterface
22
{
23
    /**
24
     * @var array $contentType
25
     */
26
    protected $contentType = [
27
        'application/vnd.25519+json',
28
        'application/vnd.ncryptf+json'
29
    ];
30
31
    /**
32
     * @var int $options
33
     * JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRESERVE_ZERO_FRACTION
34
     */
35
    private $options = 1344;
36
37
    /**
38
     * Constructor
39
     * @param CacheInterface $cache     A PSR-16 CacheInterface
40
     */
41
    public function __construct(CacheInterface $cache)
42
    {
43
        $this->cache = $cache;
0 ignored issues
show
Bug Best Practice introduced by
The property cache does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
44
    }
45
46
    /**
47
     * Processes the request
48
     * @param ServerRequestInterface $request
49
     * @param RequestHandlerInterface $handler
50
     */
51
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
52
    {
53
        if ($this->checkRequest($request)) {
54
            try {
55
                $rawBody = \base64_decode($request->getBody()->getContents());
56
                if ($rawBody === '') {
57
                    $request = $request->withParsedBody([]);
58
                    $request->withAttribute('ncryptf-decrypted-body', '');
59
                } else {
60
                    $version = Response::getVersion($rawBody);
61
                    $key = $this->getEncryptionKey($request);
62
63
                    $body = $this->decryptRequest($key, $request, $rawBody, $version);
64
                    $request = $request->withParsedBody(\json_decode($body, true, $this->options))
65
                        ->withAttribute('ncryptf-decrypted-body', $body)
66
                        ->withAttribute('ncryptf-version', $version);
67
                }
68
            } catch (DecryptionFailedException | InvalidArgumentException | InvalidSignatureException | InvalidChecksumException | Exception $e) {
0 ignored issues
show
Bug introduced by
The type ncryptf\middleware\DecryptionFailedException 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 ncryptf\middleware\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 ncryptf\middleware\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...
69
                throw HttpErrorException::create(400, [], $e);
70
            }
71
        }
72
73
        return $handler->handle($request);
74
    }
75
76
    /**
77
     * Decrypts a request
78
     * @param EncryptionKeyInterface $key
79
     * @param ServerRequestInterface $request
80
     * @param string $rawBody
81
     * @param int $version
82
     * @return string|null
83
     */
84
    private function decryptRequest(EncryptionKeyInterface $key, ServerRequestInterface $request, string $rawBody, int $version) :? string
85
    {
86
        static $response = null;
87
        static $nonce = null;
88
        static $publicKey = null;
89
90
        $response = new Response(
91
            $key->getBoxSecretKey()
92
        );
93
94
        if ($version === 1) {
95
            if (!$request->hasHeader('x-pubkey') || !$equest->hasHeader('x-nonce')) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $equest does not exist. Did you maybe mean $request?
Loading history...
96
                throw new Exception('Missing nonce or public key header. Unable to decrypt response.');
97
            }
98
99
            $publicKey = \base64_decode($request->getHeaderLine('x-pubkey'));
100
            $nonce = \base64_decode($request->getHeaderLine('x-nonce'));
101
        }
102
        $decryptedRequest = $response->decrypt(
103
            $rawBody,
104
            $publicKey,
105
            $nonce
106
        );
107
108
        $hashKey = $request->getHeaderLine('x-hashid');
109
        if ($key->isEphemeral()) {
110
            $this->cache->delete($hashKey);
111
        }
112
113
        return $decryptedRequest;
114
    }
115
116
    /**
117
     * Determines the key from the X-HashId header
118
     * @param ServerRequestInterface
119
     * @return EncryptionKeyInterface
120
     */
121
    private function getEncryptionKey(ServerRequestInterface $request) : EncryptionKeyInterface
122
    {
123
        if (!$request->hasHeader('x-hashid')) {
124
            throw new Exception('Unable to decrypt request.');
125
        }
126
127
        $hashKey = $request->getHeaderLine('x-hashid');
128
129
        try {
130
            return $this->cache->get($hashKey);
131
        } catch (InvalidArgumentException $e) {
132
            throw new Exception('Unable to decrypt request.', null, $e);
133
        }
134
    }
135
136
    /**
137
     * Check whether the request payload need to be processed
138
     */
139
    private function checkRequest(ServerRequestInterface $request): bool
140
    {
141
        $contentType = $request->getHeaderLine('Content-Type');
142
        foreach ($this->contentType as $allowedType) {
143
            if (stripos($contentType, $allowedType) === 0) {
144
                return true;
145
            }
146
        }
147
        return false;
148
    }
149
}
150