Passed
Pull Request — master (#1215)
by
unknown
30:48
created

CryptKey::isValidKey()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
eloc 9
nc 3
nop 2
dl 0
loc 13
ccs 0
cts 0
cp 0
crap 12
rs 9.9666
c 0
b 0
f 0
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
    private const FILE_PREFIX = 'file://';
23
24
    /**
25
     * @var string
26
     */
27
    protected $keyPath;
28
29
    /**
30
     * @var null|string
31
     */
32
    protected $passPhrase;
33
34
    /**
35
     * @param string      $keyPath
36
     * @param null|string $passPhrase
37 56
     * @param bool        $keyPermissionsCheck
38
     */
39 56
    public function __construct($keyPath, $passPhrase = null, $keyPermissionsCheck = true)
40 1
    {
41 55
        $this->keyPath = $keyPath;
42 1
        $this->passPhrase = $passPhrase;
43 1
44
        if (is_file($this->keyPath) && !$this->isFilePath()) {
45
            $this->keyPath = self::FILE_PREFIX . $this->keyPath;
46
        }
47 55
48 2
        if ($this->isFilePath()) {
49
            if (!\file_exists($this->keyPath) || !\is_readable($this->keyPath)) {
50
                throw new LogicException(\sprintf('Key path "%s" does not exist or is not readable', $this->keyPath));
51 55
            }
52 1
53
            $contents = file_get_contents($this->keyPath);
54
        } else {
55 54
            $contents = $keyPath;
56
        }
57 54
58 54
        if ($this->isValidKey($contents, $this->passPhrase ?? '')) {
59
            if (!$this->isFilePath()) {
60
                $this->keyPath = $this->saveKeyToFile($keyPath);
61
            }
62
        } else {
63
            throw new LogicException('Unable to read key' . ($this->isFilePath() ? " from file $keyPath" : '' ));
64
        }
65
66
        if ($keyPermissionsCheck === true) {
67 54
            // Verify the permissions of the key
68 54
            $keyPathPerms = \decoct(\fileperms($this->keyPath) & 0777);
69 54
            if (\in_array($keyPathPerms, ['400', '440', '600', '640', '660'], true) === false) {
70
                \trigger_error(
71
                    \sprintf(
72
                        'Key file "%s" permissions are not correct, recommend changing to 600 or 660 instead of %s',
73
                        $this->keyPath,
74
                        $keyPathPerms
75
                    ),
76
                    E_USER_NOTICE
77
                );
78 1
            }
79
        }
80 1
    }
81 1
82
    /**
83 1
     * @param string $key
84
     *
85
     * @throws RuntimeException
86
     *
87 1
     * @return string
88
     */
89
    private function saveKeyToFile($key)
90
    {
91
        $tmpDir = \sys_get_temp_dir();
92
        $keyPath = $tmpDir . '/' . \sha1($key) . '.key';
93 1
94
        if (\file_exists($keyPath)) {
95
            return self::FILE_PREFIX . $keyPath;
96
        }
97
98
        if (\file_put_contents($keyPath, $key) === false) {
99 1
            // @codeCoverageIgnoreStart
100
            throw new RuntimeException(\sprintf('Unable to write key file to temporary directory "%s"', $tmpDir));
101
            // @codeCoverageIgnoreEnd
102
        }
103
104
        if (\chmod($keyPath, 0600) === false) {
105
            // @codeCoverageIgnoreStart
106
            throw new RuntimeException(\sprintf('The key file "%s" file mode could not be changed with chmod to 600', $keyPath));
107 19
            // @codeCoverageIgnoreEnd
108
        }
109 19
110
        return self::FILE_PREFIX . $keyPath;
111
    }
112
113
    /**
114
     * Validate key contents.
115
     *
116
     * @param string $contents
117 10
     * @param string $passPhrase
118
     *
119 10
     * @return bool
120
     */
121
    private function isValidKey($contents, $passPhrase)
122
    {
123
        try {
124
            RSA::load($contents, $passPhrase);
125
            return true;
126
        } catch (NoKeyLoadedException $e) {}
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
127
128
        try {
129
            EC::load($contents, $passPhrase);
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
        return false;
134
    }
135
136
    /**
137
     * Checks whether the key is a file.
138
     *
139
     * @return bool
140
     */
141
    private function isFilePath()
142
    {
143
        return \strpos($this->keyPath, self::FILE_PREFIX) === 0;
144
    }
145
146
    /**
147
     * Retrieve key path.
148
     *
149
     * @return string
150
     */
151
    public function getKeyPath()
152
    {
153
        return $this->keyPath;
154
    }
155
156
    /**
157
     * Retrieve key pass phrase.
158
     *
159
     * @return null|string
160
     */
161
    public function getPassPhrase()
162
    {
163
        return $this->passPhrase;
164
    }
165
}
166