Passed
Pull Request — master (#1215)
by
unknown
95:54
created

CryptKey::__construct()   B

Complexity

Conditions 11
Paths 30

Size

Total Lines 38
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 12.5763

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 11
eloc 24
c 2
b 0
f 0
nc 30
nop 3
dl 0
loc 38
ccs 13
cts 17
cp 0.7647
crap 12.5763
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
 * Cryptography key holder.
4
 *
5
 * @author      Julián Gutiérrez <[email protected]>
6
 * @copyright   Copyright (c) Alex Bilbie
7
 * @license     http://mit-license.org/
8
 *
9
 * @link        https://github.com/thephpleague/oauth2-server
10
 */
11
12
namespace League\OAuth2\Server;
13
14
use LogicException;
15
use phpseclib3\Crypt\EC;
16
use phpseclib3\Crypt\RSA;
17
use phpseclib3\Exception\NoKeyLoadedException;
18
use RuntimeException;
19
20
class CryptKey
21
{
22
    /** @deprecated left for backward compatibility check */
23
    const RSA_KEY_PATTERN =
24
        '/^(-----BEGIN (RSA )?(PUBLIC|PRIVATE) KEY-----)\R.*(-----END (RSA )?(PUBLIC|PRIVATE) KEY-----)\R?$/s';
25
26
    private const FILE_PREFIX = 'file://';
27
28
    /**
29
     * @var string
30
     */
31
    protected $keyPath;
32
33
    /**
34
     * @var null|string
35
     */
36
    protected $passPhrase;
37 56
38
    /**
39 56
     * @param string      $keyPath
40 1
     * @param null|string $passPhrase
41 55
     * @param bool        $keyPermissionsCheck
42 1
     */
43 1
    public function __construct($keyPath, $passPhrase = null, $keyPermissionsCheck = true)
44
    {
45
        $this->keyPath = $keyPath;
46
        $this->passPhrase = $passPhrase;
47 55
48 2
        if (\is_file($this->keyPath) && !$this->isFilePath()) {
49
            $this->keyPath = self::FILE_PREFIX . $this->keyPath;
50
        }
51 55
52 1
        if ($this->isFilePath()) {
53
            if (!\file_exists($this->keyPath) || !\is_readable($this->keyPath)) {
54
                throw new LogicException(\sprintf('Key path "%s" does not exist or is not readable', $this->keyPath));
55 54
            }
56
57 54
            $contents = \file_get_contents($this->keyPath);
58 54
        } else {
59
            $contents = $keyPath;
60
        }
61
62
        if ($this->isValidKey($contents, $this->passPhrase ?? '')) {
63
            if (!$this->isFilePath()) {
64
                $this->keyPath = $this->saveKeyToFile($keyPath);
65
            }
66
        } else {
67 54
            throw new LogicException('Unable to read key' . ($this->isFilePath() ? " from file $keyPath" : ''));
68 54
        }
69 54
70
        if ($keyPermissionsCheck === true) {
71
            // Verify the permissions of the key
72
            $keyPathPerms = \decoct(\fileperms($this->keyPath) & 0777);
73
            if (\in_array($keyPathPerms, ['400', '440', '600', '640', '660'], true) === false) {
74
                \trigger_error(
75
                    \sprintf(
76
                        'Key file "%s" permissions are not correct, recommend changing to 600 or 660 instead of %s',
77
                        $this->keyPath,
78 1
                        $keyPathPerms
79
                    ),
80 1
                    E_USER_NOTICE
81 1
                );
82
            }
83 1
        }
84
    }
85
86
    /**
87 1
     * @param string $key
88
     *
89
     * @throws RuntimeException
90
     *
91
     * @return string
92
     */
93 1
    private function saveKeyToFile($key)
94
    {
95
        $tmpDir = \sys_get_temp_dir();
96
        $keyPath = $tmpDir . '/' . \sha1($key) . '.key';
97
98
        if (\file_exists($keyPath)) {
99 1
            return self::FILE_PREFIX . $keyPath;
100
        }
101
102
        if (\file_put_contents($keyPath, $key) === false) {
103
            // @codeCoverageIgnoreStart
104
            throw new RuntimeException(\sprintf('Unable to write key file to temporary directory "%s"', $tmpDir));
105
            // @codeCoverageIgnoreEnd
106
        }
107 19
108
        if (\chmod($keyPath, 0600) === false) {
109 19
            // @codeCoverageIgnoreStart
110
            throw new RuntimeException(\sprintf('The key file "%s" file mode could not be changed with chmod to 600', $keyPath));
111
            // @codeCoverageIgnoreEnd
112
        }
113
114
        return self::FILE_PREFIX . $keyPath;
115
    }
116
117 10
    /**
118
     * Validate key contents.
119 10
     *
120
     * @param string $contents
121
     * @param string $passPhrase
122
     *
123
     * @return bool
124
     */
125
    private function isValidKey($contents, $passPhrase)
126
    {
127
        try {
128
            RSA::load($contents, $passPhrase);
129
130
            return true;
131
        } catch (NoKeyLoadedException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
132
        }
133
134
        try {
135
            EC::load($contents, $passPhrase);
136
137
            return true;
138
        } catch (NoKeyLoadedException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
139
        }
140
141
        return false;
142
    }
143
144
    /**
145
     * Checks whether the key is a file.
146
     *
147
     * @return bool
148
     */
149
    private function isFilePath()
150
    {
151
        return \strpos($this->keyPath, self::FILE_PREFIX) === 0;
152
    }
153
154
    /**
155
     * Retrieve key path.
156
     *
157
     * @return string
158
     */
159
    public function getKeyPath()
160
    {
161
        return $this->keyPath;
162
    }
163
164
    /**
165
     * Retrieve key pass phrase.
166
     *
167
     * @return null|string
168
     */
169
    public function getPassPhrase()
170
    {
171
        return $this->passPhrase;
172
    }
173
}
174