Completed
Push — master ( b8fcd2...ffbcb5 )
by Tony Karavasilev (Тони
17:13
created

KeyPairTrait::setKeyPair()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 4
c 1
b 0
f 0
dl 0
loc 9
ccs 5
cts 5
cp 1
rs 10
cc 1
nc 1
nop 2
crap 1
1
<?php
2
3
/**
4
 * Trait implementation of the public and private key pair capabilities for asymmetric encryption/signing algorithms.
5
 */
6
7
namespace CryptoManana\Core\Traits\MessageEncryption;
8
9
use \CryptoManana\Core\Interfaces\MessageEncryption\KeyPairInterface as KeyPairSpecification;
10
11
/**
12
 * Trait KeyPairTrait - Reusable implementation of `KeyPairInterface`.
13
 *
14
 * @see \CryptoManana\Core\Interfaces\MessageDigestion\KeyPairInterface The abstract specification.
15
 *
16
 * @package CryptoManana\Core\Traits\MessageEncryption
17
 *
18
 * @property string $privateKey The private key string property storage.
19
 * @property string $publicKey The public key string property storage.
20
 *
21
 * @mixin KeyPairSpecification
22
 */
23
trait KeyPairTrait
24
{
25
    /**
26
     * Internal method for the validation of the private key string representation format.
27
     *
28
     * @param string $privateKey The private key input string.
29
     *
30
     * @throws \Exception Validation errors.
31
     *
32
     * @internal The parameter is passed via reference from the main logical method for performance reasons.
33
     */
34 288
    protected function validatePrivateKeyFormat(&$privateKey)
35
    {
36 288
        if (!is_string($privateKey)) {
37 8
            throw new \InvalidArgumentException('The private key must be a string or a binary string.');
38
        }
39
40
        $isPrivateBase64String = (
41 288
            !empty($privateKey) && preg_match('%^[a-zA-Z0-9/+]*={0,2}$%', $privateKey) && strlen($privateKey) % 4 === 0
42
        );
43
44 288
        if (!$isPrivateBase64String) {
45 8
            throw new \InvalidArgumentException('The private key must be a valid Base64 formatted string.');
46
        }
47 288
    }
48
49
    /**
50
     * Internal method for the validation of the public key string representation format.
51
     *
52
     * @param string $publicKey The public key input string.
53
     *
54
     * @throws \Exception Validation errors.
55
     *
56
     * @internal The parameter is passed via reference from the main logical method for performance reasons.
57
     */
58 288
    protected function validatePublicKeyFormat(&$publicKey)
59
    {
60 288
        if (!is_string($publicKey)) {
61 8
            throw new \InvalidArgumentException('The public key must be a string or a binary string.');
62
        }
63
64
        $isPublicBase64String = (
65 288
            !empty($publicKey) && preg_match('%^[a-zA-Z0-9/+]*={0,2}$%', $publicKey) && strlen($publicKey) % 4 === 0
66
        );
67
68 288
        if (!$isPublicBase64String) {
69 8
            throw new \InvalidArgumentException('The public key must be a valid Base64 formatted string.');
70
        }
71 288
    }
72
73
    /**
74
     * Internal method for the validation of the private key resource.
75
     *
76
     * @param string $privateKey The private key input string.
77
     *
78
     * @return string The extracted public key string from the private key resource.
79
     * @throws \Exception Validation errors.
80
     *
81
     * @internal The parameter is passed via reference from the main logical method for performance reasons.
82
     *
83
     * @codeCoverageIgnore
84
     */
85
    protected function validatePrivateKeyResource(&$privateKey)
86
    {
87
        // Get the private key
88
        $privateKeyResource = openssl_pkey_get_private(base64_decode($privateKey));
89
90
        if ($privateKeyResource === false) {
91
            throw new \RuntimeException(
92
                'Failed to import the private key, probably because of ' .
93
                'a misconfigured OpenSSL library or an invalid key.'
94
            );
95
        }
96
97
        $privateKeyInformation = openssl_pkey_get_details($privateKeyResource);
98
99
        if ($privateKeyInformation === false) {
100
            throw new \RuntimeException(
101
                'Failed to export the key\'s information, probably because of a misconfigured ' .
102
                'OpenSSL library or an invalid private key.'
103
            );
104
        } elseif ($privateKeyInformation['bits'] !== static::KEY_SIZE) {
105
            throw new \DomainException('The private key is not of the correct size: `' . static::KEY_SIZE . '`.');
106
        }
107
108
        // Free the private key (resource cleanup)
109
        openssl_free_key($privateKeyResource);
110
        $privateKeyResource = null;
111
112
        return base64_encode((string)$privateKeyInformation['key']); // <- The extracted public key
113
    }
114
115
    /**
116
     * Internal method for the validation of the public key resource.
117
     *
118
     * @param string $publicKey The public key input string.
119
     *
120
     * @throws \Exception Validation errors.
121
     *
122
     * @internal The parameter is passed via reference from the main logical method for performance reasons.
123
     *
124
     * @codeCoverageIgnore
125
     */
126
    protected function validatePublicKeyResource(&$publicKey)
127
    {
128
        $publicKeyResource = openssl_pkey_get_public(base64_decode($publicKey));
129
130
        if ($publicKeyResource === false) {
131
            throw new \RuntimeException(
132
                'Failed to import the public key, probably because of ' .
133
                'a misconfigured OpenSSL library or an invalid key.'
134
            );
135
        }
136
137
        $publicKeyInformation = openssl_pkey_get_details($publicKeyResource);
138
139
        if ($publicKeyInformation === false) {
140
            throw new \RuntimeException(
141
                'Failed to export the key\'s information, probably because of a misconfigured ' .
142
                'OpenSSL library or an invalid public key.'
143
            );
144
        } elseif ($publicKeyInformation['bits'] !== static::KEY_SIZE) {
145
            throw new \DomainException('The public key is not of the correct size: `' . static::KEY_SIZE . '`.');
146
        }
147
148
        // Free the public key (resource cleanup)
149
        openssl_free_key($publicKeyResource);
150
        $publicKeyResource = null;
151
    }
152
153
    /**
154
     * Internal method for the validation of the private and public key pair string representations.
155
     *
156
     * @param string $privateKey The private key input string.
157
     * @param string $publicKey The public key input string.
158
     *
159
     * @throws \Exception Validation errors.
160
     */
161 288
    protected function validateKeyPair($privateKey, $publicKey)
162
    {
163 288
        $this->validatePrivateKeyFormat($privateKey);
164 288
        $this->validatePublicKeyFormat($publicKey);
165
166 288
        $extractedPublicKey = $this->validatePrivateKeyResource($privateKey);
167
168 288
        $this->validatePublicKeyResource($publicKey);
169
170
        // @codeCoverageIgnoreStart
171
        $thePairIsNotMatching = (
172
            strlen($extractedPublicKey) !== strlen($publicKey) ||
173
            substr($extractedPublicKey, -8) !== substr($extractedPublicKey, -8) ||
174
            hash('sha256', $extractedPublicKey) !== hash('sha256', $publicKey)
175
        );
176
177
        if ($thePairIsNotMatching) {
178
            throw new \RuntimeException('The private and public key pair are not matching and can not be used.');
179
        }
180
        // @codeCoverageIgnoreEnd
181 288
    }
182
183
    /**
184
     * Setter for the whole key pair as an array.
185
     *
186
     * @param string $privateKey The private key string.
187
     * @param string $publicKey The public key string.
188
     *
189
     * @return $this The encryption/signature algorithm object.
190
     * @throws \Exception Validation errors.
191
     */
192 288
    public function setKeyPair($privateKey, $publicKey)
193
    {
194 288
        $this->validateKeyPair($privateKey, $publicKey);
195
196
        // Set the key pair
197 288
        $this->privateKey = $privateKey;
198 288
        $this->publicKey = $publicKey;
199
200 288
        return $this;
201
    }
202
203
    /**
204
     * Getter for the whole key pair as an array.
205
     *
206
     * @param bool|int|null $asArray Flag for exporting as an array, instead of an object.
207
     *
208
     * @return \stdClass|array The private and public key pair as an object/array.
209
     */
210 8
    public function getKeyPair($asArray = false)
211
    {
212 8
        if ($asArray == true) {
213
            return [
214 8
                self::PRIVATE_KEY_INDEX_NAME => $this->privateKey,
215 8
                self::PUBLIC_KEY_INDEX_NAME => $this->publicKey
216
            ];
217
        } else {
218 8
            $object = new \stdClass();
219
220 8
            $object->{self::PRIVATE_KEY_INDEX_NAME} = $this->privateKey;
221 8
            $object->{self::PUBLIC_KEY_INDEX_NAME} = $this->publicKey;
222
223 8
            return $object;
224
        }
225
    }
226
227
    /**
228
     * Setter for the private key string property.
229
     *
230
     * @param string $privateKey The private key string.
231
     *
232
     * @return $this The encryption/signature algorithm object.
233
     * @throws \Exception Validation errors.
234
     */
235 16
    public function setPrivateKey($privateKey)
236
    {
237 16
        $this->validatePrivateKeyFormat($privateKey);
238 16
        $this->validatePrivateKeyResource($privateKey);
239
240
        // Set the key pair
241 16
        $this->privateKey = $privateKey;
242
243 16
        return $this;
244
    }
245
246
    /**
247
     * Getter for the private key string property.
248
     *
249
     * @return string The private key string.
250
     */
251 24
    public function getPrivateKey()
252
    {
253 24
        return ($this->privateKey !== '') ? $this->privateKey : null;
254
    }
255
256
    /**
257
     * Setter for the public key string property.
258
     *
259
     * @param string $publicKey The public key string.
260
     *
261
     * @return $this The encryption/signature algorithm object.
262
     * @throws \Exception Validation errors.
263
     */
264 16
    public function setPublicKey($publicKey)
265
    {
266 16
        $this->validatePublicKeyFormat($publicKey);
267 16
        $this->validatePublicKeyResource($publicKey);
268
269
        // Set the key pair
270 16
        $this->publicKey = $publicKey;
271
272 16
        return $this;
273
    }
274
275
    /**
276
     * Getter for the public key string property.
277
     *
278
     * @return string The public key string.
279
     */
280 24
    public function getPublicKey()
281
    {
282 24
        return ($this->publicKey !== '') ? $this->publicKey : null;
283
    }
284
285
    /**
286
     * Checks if the private key is present.
287
     *
288
     * @throws \Exception If there is no private key set.
289
     */
290 128
    public function checkIfThePrivateKeyIsSet()
291
    {
292 128
        if ($this->privateKey === '') {
293 8
            throw new \RuntimeException('There is no private key set, please generate or import your key pair.');
294
        }
295 120
    }
296
297
    /**
298
     * Checks if the public key is present.
299
     *
300
     * @throws \Exception If there is no public key set.
301
     */
302 168
    public function checkIfThePublicKeyIsSet()
303
    {
304 168
        if ($this->publicKey === '') {
305 8
            throw new \RuntimeException('There is no public key set, please generate or import your key pair.');
306
        }
307 160
    }
308
}
309