Test Failed
Push — develop ( 8521e7...4e95f0 )
by Paul
15:47
created

Encryption::nonce()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2.032

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 4
c 1
b 0
f 0
dl 0
loc 7
ccs 4
cts 5
cp 0.8
rs 10
cc 2
nc 2
nop 0
crap 2.032
1
<?php
2
3
namespace GeminiLabs\SiteReviews\Modules;
4
5
class Encryption
6
{
7 8
    public function decode(string $string): string
8
    {
9 8
        return base64_decode(str_replace(['-', '_'], ['+', '/'], $string));
10
    }
11
12
    /**
13
     * @return string|false
14
     */
15 8
    public function decrypt(string $message)
16
    {
17
        $decoded = $this->decode($message);
18 8
        if (empty($decoded)) {
19 8
            return '';
20
        }
21
        try {
22
            $nonceLength = \SODIUM_CRYPTO_SECRETBOX_NONCEBYTES;
23
            if (strlen($decoded) >= $nonceLength + 1) { // Minimum for new format
24
                $nonce = substr($decoded, 0, $nonceLength);
25
                $ciphertext = substr($decoded, $nonceLength);
26
                if (strlen($nonce) === $nonceLength) {
27
                    $plaintext = sodium_crypto_secretbox_open($ciphertext, $nonce, $this->key());
28
                    if (false !== $plaintext) {
29
                        return $plaintext; // Success with new format
30
                    }
31
                }
32
            }
33
            return $this->legacyDecrypt($decoded);
34
        } catch (\Exception $e) {
35
            glsr_log()->error($e->getMessage());
36
            return false;
37 35
        }
38
    }
39 35
40
    public function decryptRequest(string $message): array
41
    {
42
        if ($message = $this->decrypt($message)) {
43
            $data = explode('|', $message);
44
            $data = array_map('sanitize_text_field', $data);
45 35
            $action = array_shift($data);
46
            return compact('action', 'data');
47
        }
48 35
        return [];
49 35
    }
50
51
    public function encode(string $string): string
52
    {
53
        return str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($string));
54
    }
55
56 24
    /**
57
     * @return string|false
58 24
     */
59 24
    public function encrypt(string $message)
60 24
    {
61 24
        try {
62
            $nonce = random_bytes(\SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
63
            $ciphertext = sodium_crypto_secretbox($message, $nonce, $this->key());
64 35
            // Prepend nonce to ciphertext
65
            return $this->encode($nonce.$ciphertext);
66 35
        } catch (\Exception $e) {
67
            glsr_log()->error($e->getMessage());
68
            return false;
69 35
        }
70 35
    }
71
72
    public function encryptRequest(string $action, array $data): string
73 35
    {
74
        $values = array_values(array_map('sanitize_text_field', $data));
75 35
        $message = implode('|', $values);
76
        $message = sprintf('%s|%s', $action, $message);
77
        return (string) $this->encrypt($message);
78 35
    }
79 35
80
    /**
81
     * @return string|false
82
     */
83
    protected function legacyDecrypt(string $ciphertext)
84
    {
85
        try {
86
            $plaintext = sodium_crypto_secretbox_open($ciphertext, $this->legacyNonce(), $this->key());
87
            if (false === $plaintext) {
88
                throw new \Exception('Legacy decryption failed');
89
            }
90
            return $plaintext;
91
        } catch (\Exception $e) {
92
            glsr_log()->error($e->getMessage());
93
            return false;
94
        }
95
    }
96
97
    protected function legacyNonce(): string
98
    {
99
        $nonce = defined('NONCE_SALT') ? \NONCE_SALT : '';
100
        $nonce = substr($nonce, 0, \SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
101
        return str_pad($nonce, \SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '#');
102
    }
103
104
    protected function key(): string
105
    {
106
        $key = defined('NONCE_KEY') ? \NONCE_KEY : '';
107
        $key = substr($key, 0, \SODIUM_CRYPTO_SECRETBOX_KEYBYTES);
108
        return str_pad($key, \SODIUM_CRYPTO_SECRETBOX_KEYBYTES, '#');
109
    }
110
}
111