• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

codeigniter4 / CodeIgniter4 / 20643293754

01 Jan 2026 06:18PM UTC coverage: 85.468% (-0.03%) from 85.499%
20643293754

Pull #9863

github

web-flow
Merge 0d306a76b into ff20d8106
Pull Request #9863: feat: encryption key rotation support

86 of 94 new or added lines in 4 files covered. (91.49%)

9 existing lines in 1 file now uncovered.

21838 of 25551 relevant lines covered (85.47%)

203.82 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

77.08
/system/Encryption/Handlers/SodiumHandler.php
1
<?php
2

3
declare(strict_types=1);
4

5
/**
6
 * This file is part of CodeIgniter 4 framework.
7
 *
8
 * (c) CodeIgniter Foundation <admin@codeigniter.com>
9
 *
10
 * For the full copyright and license information, please view
11
 * the LICENSE file that was distributed with this source code.
12
 */
13

14
namespace CodeIgniter\Encryption\Handlers;
15

16
use CodeIgniter\Encryption\Exceptions\EncryptionException;
17
use SensitiveParameter;
18

19
/**
20
 * SodiumHandler uses libsodium in encryption.
21
 *
22
 * @see https://github.com/jedisct1/libsodium/issues/392
23
 * @see \CodeIgniter\Encryption\Handlers\SodiumHandlerTest
24
 */
25
class SodiumHandler extends BaseHandler
26
{
27
    /**
28
     * Starter key
29
     *
30
     * @var string|null Null is used for buffer cleanup.
31
     */
32
    protected $key = '';
33

34
    /**
35
     * Block size for padding message.
36
     *
37
     * @var int
38
     */
39
    protected $blockSize = 16;
40

41
    /**
42
     * {@inheritDoc}
43
     */
44
    public function encrypt(#[SensitiveParameter] $data, #[SensitiveParameter] $params = null)
45
    {
46
        $key = $params !== null
10✔
47
            ? (is_array($params) && isset($params['key']) ? $params['key'] : $params)
5✔
48
            : $this->key;
5✔
49

50
        $blockSize = is_array($params) && isset($params['blockSize'])
10✔
NEW
51
            ? $params['blockSize']
×
52
            : $this->blockSize;
10✔
53

54
        if (empty($key)) {
10✔
55
            throw EncryptionException::forNeedsStarterKey();
1✔
56
        }
57

58
        // create a nonce for this operation
59
        $nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES); // 24 bytes
9✔
60

61
        // add padding before we encrypt the data
62
        if ($blockSize <= 0) {
9✔
63
            throw EncryptionException::forEncryptionFailed();
1✔
64
        }
65

66
        $data = sodium_pad($data, $blockSize);
8✔
67

68
        // encrypt message and combine with nonce
69
        $ciphertext = $nonce . sodium_crypto_secretbox($data, $nonce, $key);
8✔
70

71
        // cleanup buffers
72
        sodium_memzero($data);
8✔
73
        sodium_memzero($key);
8✔
74

75
        return $ciphertext;
8✔
76
    }
77

78
    /**
79
     * {@inheritDoc}
80
     */
81
    public function decrypt($data, #[SensitiveParameter] $params = null)
82
    {
83
        return $this->tryDecryptWithFallback($data, $params, function ($data, $params): string {
8✔
84
            $key = $params !== null
8✔
85
                ? (is_array($params) && isset($params['key']) ? $params['key'] : $params)
7✔
86
                : $this->key;
3✔
87

88
            $blockSize = is_array($params) && isset($params['blockSize'])
8✔
89
                ? $params['blockSize']
2✔
90
                : $this->blockSize;
6✔
91

92
            if (empty($key)) {
8✔
NEW
93
                throw EncryptionException::forNeedsStarterKey();
×
94
            }
95

96
            if (mb_strlen($data, '8bit') < (SODIUM_CRYPTO_SECRETBOX_NONCEBYTES + SODIUM_CRYPTO_SECRETBOX_MACBYTES)) {
8✔
97
                // message was truncated
98
                throw EncryptionException::forAuthenticationFailed();
1✔
99
            }
100

101
            // Extract info from encrypted data
102
            $nonce      = self::substr($data, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
7✔
103
            $ciphertext = self::substr($data, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
7✔
104

105
            // decrypt data
106
            $data = sodium_crypto_secretbox_open($ciphertext, $nonce, $key);
7✔
107

108
            if ($data === false) {
7✔
109
                // message was tampered in transit
110
                throw EncryptionException::forAuthenticationFailed(); // @codeCoverageIgnore
4✔
111
            }
112

113
            // remove extra padding during encryption
114
            if ($blockSize <= 0) {
5✔
115
                throw EncryptionException::forAuthenticationFailed();
1✔
116
            }
117

118
            $data = sodium_unpad($data, $blockSize);
4✔
119

120
            // cleanup buffers
121
            sodium_memzero($ciphertext);
4✔
122
            sodium_memzero($key);
4✔
123

124
            return $data;
4✔
125
        });
8✔
126
    }
127

128
    /**
129
     * Parse the $params before doing assignment.
130
     *
131
     * @param array|string|null $params
132
     *
133
     * @return void
134
     *
135
     * @throws EncryptionException If key is empty
136
     *
137
     * @deprecated 4.7.0 No longer used.
138
     */
139
    protected function parseParams($params)
140
    {
UNCOV
141
        if ($params === null) {
×
UNCOV
142
            return;
×
143
        }
144

UNCOV
145
        if (is_array($params)) {
×
UNCOV
146
            if (isset($params['key'])) {
×
UNCOV
147
                $this->key = $params['key'];
×
148
            }
149

UNCOV
150
            if (isset($params['blockSize'])) {
×
UNCOV
151
                $this->blockSize = $params['blockSize'];
×
152
            }
153

UNCOV
154
            return;
×
155
        }
156

UNCOV
157
        $this->key = (string) $params;
×
158
    }
159
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc