Passed
Push — master ( dd2fca...3e322f )
by Andrew
27:54 queued 25:50
created

CryptKey::isValidKey()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 20
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 4

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 4
eloc 9
c 3
b 0
f 0
nc 6
nop 2
dl 0
loc 20
ccs 10
cts 10
cp 1
crap 4
rs 9.9666
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