Passed
Branch master (9b4352)
by Rutger
13:03
created

Oauth2Encryptor::hasKey()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
namespace rhertogh\Yii2Oauth2Server\components\encryption;
4
5
use Defuse\Crypto\Crypto;
6
use Defuse\Crypto\Exception\BadFormatException;
7
use Defuse\Crypto\Exception\EnvironmentIsBrokenException;
8
use Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException;
9
use Defuse\Crypto\Key;
10
use rhertogh\Yii2Oauth2Server\interfaces\components\encryption\Oauth2EncryptorInterface;
11
use rhertogh\Yii2Oauth2Server\interfaces\components\factories\encryption\Oauth2EncryptionKeyFactoryInterface;
12
use Yii;
13
use yii\base\Component;
14
use yii\base\InvalidArgumentException;
15
use yii\base\InvalidConfigException;
16
use yii\helpers\Json;
17
18
class Oauth2Encryptor extends Component implements Oauth2EncryptorInterface
19
{
20
    /**
21
     * Separator between different parts in the data. E.g. the keyName and secret.
22
     * @var string
23
     */
24
    public $dataSeparator = '::';
25
26
    /**
27
     * @var Key[]|null
28
     */
29
    protected $_keys = null;
30
31
    /**
32
     * @var string|null
33
     */
34
    protected $_defaultKeyName = null;
35
36
    /**
37
     * @inheritDoc
38
     * @throws InvalidConfigException
39
     */
40 9
    public function setKeys($keys)
41
    {
42 9
        if ($keys && is_string($keys)) {
43 3
            $keys = Json::decode($keys);
44
        }
45
46
        /** @var Oauth2EncryptionKeyFactoryInterface $keyFactory */
47 9
        $keyFactory = Yii::createObject(Oauth2EncryptionKeyFactoryInterface::class);
48 9
        $this->_keys = [];
49 9
        foreach ($keys as $keyName => $key) {
50
            try {
51 9
                $this->_keys[$keyName] = $keyFactory->createFromAsciiSafeString($key);
52 2
            } catch (BadFormatException $e) {
53 1
                throw new InvalidConfigException(
54 1
                    'Encryption key "' . $keyName . '" is malformed: ' . $e->getMessage(),
55
                    0,
56
                    $e
57
                );
58 1
            } catch (EnvironmentIsBrokenException $e) {
59 1
                throw new InvalidConfigException(
60 1
                    'Could not instantiate key "' . $keyName . '": ' . $e->getMessage(),
61
                    0,
62
                    $e
63
                );
64
            }
65
        }
66
    }
67
68
    /**
69
     * @inheritDoc
70
     */
71 6
    public function getDefaultKeyName()
72
    {
73 6
        if (empty($this->_defaultKeyName)) {
74 1
            throw new \BadMethodCallException(
75
                'Unable to get the defaultKeyName since it is not set.'
76
            );
77
        }
78
79 5
        return $this->_defaultKeyName;
80
    }
81
82
    /**
83
     * @inheritDoc
84
     */
85 8
    public function setDefaultKeyName($name)
86
    {
87 8
        $this->_defaultKeyName = $name;
88
    }
89
90
    /**
91
     * @inheritDoc
92
     */
93 3
    public function hasKey($name)
94
    {
95 3
        return array_key_exists($name, $this->_keys);
96
    }
97
98
99
    /**
100
     * @inheritDoc
101
     * @throws InvalidConfigException
102
     * @throws EnvironmentIsBrokenException
103
     */
104 6
    public function encryp($data, $keyName = null)
105
    {
106 6
        if (empty($keyName)) {
107 5
            $keyName = $this->getDefaultKeyName();
108
        }
109
110 5
        if (empty($this->_keys[$keyName])) {
111 1
            throw new \BadMethodCallException('Unable to encrypt, no key with name "' . $keyName . '" has been set.');
112
        }
113
114 4
        if (empty($this->dataSeparator)) {
115 1
            throw new InvalidConfigException('Unable to encrypt, dataSeparator is empty.');
116
        }
117
118 3
        if (strpos($keyName, $this->dataSeparator) !== false) {
119 1
            throw new \BadMethodCallException(
120 1
                'Unable to encrypt, key name "' . $keyName . '" contains dataSeparator "' . $this->dataSeparator . '".'
121
            );
122
        }
123
124
        return $keyName
125 2
            . $this->dataSeparator
126 2
            . base64_encode(Crypto::encrypt($data, $this->_keys[$keyName], true));
127
    }
128
129
    /**
130
     * @inheritDoc
131
     */
132 4
    public function parseData($data)
133
    {
134 4
        $parts = explode($this->dataSeparator, $data);
135 4
        if (count($parts) !== 2) {
136 1
            throw new InvalidArgumentException('Could not parse encrypted data: invalid number of parts, expected 2 got ' . count($parts));
137
        }
138 3
        return array_combine(['keyName', 'ciphertext'], $parts);
139
    }
140
141
    /**
142
     * @inheritDoc
143
     * @throws EnvironmentIsBrokenException
144
     * @throws WrongKeyOrModifiedCiphertextException
145
     */
146 4
    public function decrypt($data)
147
    {
148
        try {
149 4
            list('keyName' => $keyName, 'ciphertext' => $ciphertext) = $this->parseData($data);
150 1
        } catch (\Throwable $e) {
151 1
            throw new \InvalidArgumentException(
152 1
                'Unable to decrypt, $data must be in format "keyName' . $this->dataSeparator . 'ciphertext".'
153
            );
154
        }
155
156 3
        if (empty($this->_keys[$keyName])) {
157 1
            throw new \BadMethodCallException('Unable to decrypt, no key with name "' . $keyName . '" has been set.');
158
        }
159
160 2
        return Crypto::decrypt(base64_decode($ciphertext), $this->_keys[$keyName], true);
161
    }
162
163
    /**
164
     * @inheritDoc
165
     * @throws InvalidConfigException
166
     * @throws EnvironmentIsBrokenException
167
     * @throws WrongKeyOrModifiedCiphertextException
168
     */
169 1
    public function rotateKey($data, $newKeyName = null)
170
    {
171 1
        if (empty($newKeyName)) {
172 1
            $newKeyName = $this->getDefaultKeyName();
173
        }
174
175 1
        list($keyName) = explode($this->dataSeparator, $data, 2);
176
177 1
        if ($keyName === $newKeyName) {
178 1
            return $data; // Key hasn't changed.
179
        }
180
181 1
        return $this->encryp($this->decrypt($data), $newKeyName);
182
    }
183
}
184