Rsa::sign()   B
last analyzed

Complexity

Conditions 4
Paths 8

Size

Total Lines 28
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 28
c 0
b 0
f 0
rs 8.5806
cc 4
eloc 13
nc 8
nop 2
1
<?php namespace nyx\auth\signers;
2
3
// Internal dependencies
4
use nyx\auth;
5
6
/**
7
 * RSA Signer
8
 *
9
 * Note: Relies on the OpenSSL extension being available but will *not throw* on its own until a function
10
 * provided by the OpenSSL extension gets invoked.
11
 *
12
 * @package     Nyx\Auth
13
 * @version     0.1.0
14
 * @author      Michal Chojnacki <[email protected]>
15
 * @copyright   2012-2017 Nyx Dev Team
16
 * @link        https://github.com/unyx/nyx
17
 */
18
abstract class Rsa extends auth\Signer
19
{
20
    /**
21
     * {@inheritDoc}
22
     */
23
    const METHOD = 'rsa';
24
25
    /**
26
     * {@inheritDoc}
27
     */
28
    public function sign(string $payload, $key) : string
29
    {
30
        // If a Credentials instance is given, we are going to use its *private* part.
31
        if ($key instanceof auth\interfaces\Credentials) {
32
            $key = $key->getSecret();
33
        }
34
35
        // Load the actual key as a resource.
36
        // Note: This is correct - the initial private key can itself be a Credentials instance where
37
        // the secret is its passphrase.
38
        if ($key instanceof auth\interfaces\Credentials) {
39
            $key = openssl_get_privatekey($key->getId(), $key->getSecret());
40
        } else {
41
            $key = openssl_get_privatekey($key);
42
        }
43
44
        $signature = '';
45
        $success   = openssl_sign($payload, $signature, $this->validateKey($key), $this->getAlgorithm());
46
47
        // Free the resource in any case, before the potential throw.
48
        openssl_free_key($key);
49
50
        if (false === $success) {
51
            throw new \RuntimeException('Failed to sign the payload. Reason: '.openssl_error_string());
52
        }
53
54
        return $signature;
55
    }
56
57
    /**
58
     * {@inheritDoc}
59
     */
60
    public function verify(string $expected, string $payload, $key) : bool
61
    {
62
        // If a Credentials instance is given, we are going to use its *public* part.
63
        if ($key instanceof auth\interfaces\Credentials) {
64
            $key = $key->getId();
65
        }
66
67
        // Load the key.
68
        $key     = openssl_get_publickey($key);
69
        $success = openssl_verify($payload, $expected, $this->validateKey($key), $this->getAlgorithm());
70
71
        // Free the resource in any case, before the potential throw.
72
        openssl_free_key($key);
73
74
        if (-1 === $success) {
75
            throw new \RuntimeException('An error occurred while verifying the signature: '.openssl_error_string());
76
        }
77
78
        // $success will be 1 if the signatures matched, 0 otherwise.
79
        return 1 === $success;
80
    }
81
82
    /**
83
     * Validates the given key resource (as opened by one of the openssl_get_*key functions).
84
     *
85
     * @param   resource    $key
86
     * @return  resource
87
     * @throws  \InvalidArgumentException   When the key is not a valid resource or is not an RSA key.
88
     */
89
    protected function validateKey($key)
90
    {
91
        // First, ensure the respective get_*key method did not return false, meaning we got a valid
92
        // resource pointer at hand.
93
        if (false === $key) {
94
            throw new \InvalidArgumentException('Failed to parse the RSA key. Reason: '.openssl_error_string());
95
        }
96
97
        // We need to make there is actually a valid RSA key in that resource.
98
        $details = openssl_pkey_get_details($key);
99
100
        if (!isset($details['key']) || $details['type'] !== OPENSSL_KEYTYPE_RSA) {
101
            throw new \InvalidArgumentException('This key does not appear to be a valid RSA key.');
102
        }
103
104
        return $key;
105
    }
106
}
107