CryptKey   A
last analyzed

Complexity

Total Complexity 17

Size/Duplication

Total Lines 103
Duplicated Lines 0 %

Test Coverage

Coverage 76.09%

Importance

Changes 12
Bugs 3 Features 0
Metric Value
eloc 42
c 12
b 3
f 0
dl 0
loc 103
ccs 35
cts 46
cp 0.7609
rs 10
wmc 17

5 Methods

Rating   Name   Duplication   Size   Complexity  
A getKeyContents() 0 3 1
A isValidKey() 0 16 4
A getKeyPath() 0 3 1
A getPassPhrase() 0 3 1
B __construct() 0 43 10
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
20
use function decoct;
21
use function file_get_contents;
22
use function fileperms;
23
use function in_array;
24
use function is_file;
25
use function is_readable;
26
use function openssl_pkey_get_details;
27
use function openssl_pkey_get_private;
28
use function openssl_pkey_get_public;
29
use function sprintf;
30
use function strpos;
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 72
    public function __construct(string $keyPath, protected ?string $passPhrase = null, bool $keyPermissionsCheck = true)
45
    {
46 72
        if (strpos($keyPath, self::FILE_PREFIX) !== 0 && $this->isValidKey($keyPath, $this->passPhrase ?? '')) {
47 3
            $this->keyContents = $keyPath;
48 3
            $this->keyPath = '';
49
            // There's no file, so no need for permission check.
50 3
            $keyPermissionsCheck = false;
51 69
        } elseif (is_file($keyPath)) {
52 67
            if (strpos($keyPath, self::FILE_PREFIX) !== 0) {
53 1
                $keyPath = self::FILE_PREFIX . $keyPath;
54
            }
55
56 67
            if (!is_readable($keyPath)) {
57
                throw new LogicException(sprintf('Key path "%s" does not exist or is not readable', $keyPath));
58
            }
59
60 67
            $keyContents = file_get_contents($keyPath);
61
62 67
            if ($keyContents === false) {
63
                throw new LogicException('Unable to read key from file ' . $keyPath);
64
            }
65
66 67
            $this->keyContents = $keyContents;
67 67
            $this->keyPath = $keyPath;
68
69 67
            if (!$this->isValidKey($this->keyContents, $this->passPhrase ?? '')) {
70
                throw new LogicException('Unable to read key from file ' . $keyPath);
71
            }
72
        } else {
73 2
            throw new LogicException('Invalid key supplied');
74
        }
75
76 70
        if ($keyPermissionsCheck === true) {
77
            // Verify the permissions of the key
78 67
            $keyPathPerms = decoct(fileperms($this->keyPath) & 0777);
79 67
            if (in_array($keyPathPerms, ['400', '440', '600', '640', '660'], true) === false) {
80
                trigger_error(
81
                    sprintf(
82
                        'Key file "%s" permissions are not correct, recommend changing to 600 or 660 instead of %s',
83
                        $this->keyPath,
84
                        $keyPathPerms
85
                    ),
86
                    E_USER_NOTICE
87
                );
88
            }
89
        }
90
    }
91
92
    /**
93
     * {@inheritdoc}
94
     */
95 18
    public function getKeyContents(): string
96
    {
97 18
        return $this->keyContents;
98
    }
99
100
    /**
101
     * Validate key contents.
102
     */
103 72
    private function isValidKey(string $contents, string $passPhrase): bool
104
    {
105 72
        $privateKey = openssl_pkey_get_private($contents, $passPhrase);
106
107 72
        $key = $privateKey instanceof OpenSSLAsymmetricKey ? $privateKey : openssl_pkey_get_public($contents);
108
109 72
        if ($key === false) {
0 ignored issues
show
introduced by
The condition $key === false is always false.
Loading history...
110 2
            return false;
111
        }
112
113 71
        $details = openssl_pkey_get_details($key);
114
115 71
        return $details !== false && in_array(
116 71
            $details['type'] ?? -1,
117 71
            [OPENSSL_KEYTYPE_RSA, OPENSSL_KEYTYPE_EC],
118 71
            true
119 71
        );
120
    }
121
122
    /**
123
     * {@inheritdoc}
124
     */
125 5
    public function getKeyPath(): string
126
    {
127 5
        return $this->keyPath;
128
    }
129
130
    /**
131
     * {@inheritdoc}
132
     */
133 20
    public function getPassPhrase(): ?string
134
    {
135 20
        return $this->passPhrase;
136
    }
137
}
138