Passed
Pull Request — master (#1483)
by Andrew
30:18 queued 28:20
created

CryptKey::__construct()   B

Complexity

Conditions 11
Paths 16

Size

Total Lines 47
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 16.51

Importance

Changes 5
Bugs 3 Features 0
Metric Value
cc 11
eloc 27
c 5
b 3
f 0
nc 16
nop 3
dl 0
loc 47
ccs 18
cts 28
cp 0.6429
crap 16.51
rs 7.3166

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * Cryptography key holder.
5
 *
6
 * @author      Julián Gutiérrez <[email protected]>
7
 * @copyright   Copyright (c) Alex Bilbie
8
 * @license     http://mit-license.org/
9
 *
10
 * @link        https://github.com/thephpleague/oauth2-server
11
 */
12
13
declare(strict_types=1);
14
15
namespace League\OAuth2\Server;
16
17
use LogicException;
18
use OpenSSLAsymmetricKey;
19
use SensitiveParameter;
0 ignored issues
show
Bug introduced by
The type SensitiveParameter 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...
20
21
use function decoct;
22
use function file_get_contents;
23
use function fileperms;
24
use function in_array;
25
use function is_file;
26
use function is_readable;
27
use function openssl_pkey_get_details;
28
use function openssl_pkey_get_private;
29
use function openssl_pkey_get_public;
30
use function sprintf;
31
use function trigger_error;
32
33
class CryptKey implements CryptKeyInterface
34
{
35
    private const FILE_PREFIX = 'file://';
36
37
    /**
38
     * @var string Key contents
39
     */
40
    protected string $keyContents;
41
42
    protected string $keyPath;
43
44 74
    public function __construct(
45
        string $keyPath,
46
        #[SensitiveParameter]
47
        protected ?string $passPhrase = null,
48
        bool $keyPermissionsCheck = true
49
    ) {
50 74
        if (str_starts_with($keyPath, self::FILE_PREFIX) === false && $this->isValidKey($keyPath, $this->passPhrase ?? '')) {
51 3
            $this->keyContents = $keyPath;
52 3
            $this->keyPath = '';
53
            // There's no file, so no need for permission check.
54 3
            $keyPermissionsCheck = false;
55 71
        } elseif (is_file($keyPath)) {
56 69
            if (str_starts_with($keyPath, self::FILE_PREFIX) === false) {
57 1
                $keyPath = self::FILE_PREFIX . $keyPath;
58
            }
59
60 69
            if (!is_readable($keyPath)) {
61
                throw new LogicException(sprintf('Key path "%s" does not exist or is not readable', $keyPath));
62
            }
63
64 69
            $keyContents = file_get_contents($keyPath);
65
66 69
            if ($keyContents === false) {
67
                throw new LogicException('Unable to read key from file ' . $keyPath);
68
            }
69
70 69
            $this->keyContents = $keyContents;
71 69
            $this->keyPath = $keyPath;
72
73 69
            if (!$this->isValidKey($this->keyContents, $this->passPhrase ?? '')) {
74
                throw new LogicException('Unable to read key from file ' . $keyPath);
75
            }
76
        } else {
77 2
            throw new LogicException('Invalid key supplied');
78
        }
79
80 72
        if ($keyPermissionsCheck === true && PHP_OS_FAMILY !== 'Windows') {
81
            // Verify the permissions of the key
82 69
            $keyPathPerms = decoct(fileperms($this->keyPath) & 0777);
83 69
            if (in_array($keyPathPerms, ['400', '440', '600', '640', '660'], true) === false) {
84
                trigger_error(
85
                    sprintf(
86
                        'Key file "%s" permissions are not correct, recommend changing to 600 or 660 instead of %s',
87
                        $this->keyPath,
88
                        $keyPathPerms
89
                    ),
90
                    E_USER_NOTICE
91
                );
92
            }
93
        }
94
    }
95
96
    /**
97
     * {@inheritdoc}
98
     */
99 19
    public function getKeyContents(): string
100
    {
101 19
        return $this->keyContents;
102
    }
103
104
    /**
105
     * Validate key contents.
106
     */
107 74
    private function isValidKey(
108
        #[SensitiveParameter]
109
        string $contents,
110
        #[SensitiveParameter]
111
        string $passPhrase
112
    ): bool {
113 74
        $privateKey = openssl_pkey_get_private($contents, $passPhrase);
114
115 74
        $key = $privateKey instanceof OpenSSLAsymmetricKey ? $privateKey : openssl_pkey_get_public($contents);
116
117 74
        if ($key === false) {
0 ignored issues
show
introduced by
The condition $key === false is always false.
Loading history...
118 2
            return false;
119
        }
120
121 73
        $details = openssl_pkey_get_details($key);
122
123 73
        return $details !== false && in_array(
124 73
            $details['type'] ?? -1,
125 73
            [OPENSSL_KEYTYPE_RSA, OPENSSL_KEYTYPE_EC],
126 73
            true
127 73
        );
128
    }
129
130
    /**
131
     * {@inheritdoc}
132
     */
133 5
    public function getKeyPath(): string
134
    {
135 5
        return $this->keyPath;
136
    }
137
138
    /**
139
     * {@inheritdoc}
140
     */
141 21
    public function getPassPhrase(): ?string
142
    {
143 21
        return $this->passPhrase;
144
    }
145
}
146