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

codeigniter4 / CodeIgniter4 / 20589198684

30 Dec 2025 04:55AM UTC coverage: 84.503% (-0.03%) from 84.53%
20589198684

Pull #9853

github

web-flow
Merge f1d8312ec into e2fc5243b
Pull Request #9853: feat(encryption): Add previous keys fallback feature

65 of 77 new or added lines in 5 files covered. (84.42%)

38 existing lines in 3 files now uncovered.

21572 of 25528 relevant lines covered (84.5%)

203.31 hits per line

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

97.44
/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
        $this->parseParams($params);
6✔
47

48
        if (empty($this->key)) {
6✔
49
            throw EncryptionException::forNeedsStarterKey();
1✔
50
        }
51

52
        // create a nonce for this operation
53
        $nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES); // 24 bytes
5✔
54

55
        // add padding before we encrypt the data
56
        if ($this->blockSize <= 0) {
5✔
57
            throw EncryptionException::forEncryptionFailed();
1✔
58
        }
59

60
        $data = sodium_pad($data, $this->blockSize);
4✔
61

62
        // encrypt message and combine with nonce
63
        $ciphertext = $nonce . sodium_crypto_secretbox($data, $nonce, $this->key);
4✔
64

65
        // cleanup buffers
66
        sodium_memzero($data);
4✔
67
        sodium_memzero($this->key);
4✔
68

69
        return $ciphertext;
4✔
70
    }
71

72
    /**
73
     * {@inheritDoc}
74
     */
75
    public function decrypt($data, #[SensitiveParameter] $params = null)
76
    {
77
        $this->parseParams($params);
4✔
78

79
        $decryptParams = $params ?? $this->key;
4✔
80

81
        if (empty($decryptParams)) {
4✔
82
            throw EncryptionException::forNeedsStarterKey();
1✔
83
        }
84

85
        return $this->tryDecryptWithFallback($data, $decryptParams, function ($data, $params): string {
3✔
86
            $key = is_array($params) && isset($params['key']) ? $params['key'] : $params;
3✔
87

88
            if (mb_strlen($data, '8bit') < (SODIUM_CRYPTO_SECRETBOX_NONCEBYTES + SODIUM_CRYPTO_SECRETBOX_MACBYTES)) {
3✔
89
                // message was truncated
90
                throw EncryptionException::forAuthenticationFailed();
1✔
91
            }
92

93
            // Extract info from encrypted data
94
            $nonce      = self::substr($data, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
2✔
95
            $ciphertext = self::substr($data, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
2✔
96

97
            // decrypt data
98
            $data = sodium_crypto_secretbox_open($ciphertext, $nonce, $key);
2✔
99

100
            if ($data === false) {
2✔
101
                // message was tampered in transit
NEW
102
                throw EncryptionException::forAuthenticationFailed(); // @codeCoverageIgnore
×
103
            }
104

105
            // remove extra padding during encryption
106
            if ($this->blockSize <= 0) {
2✔
107
                throw EncryptionException::forAuthenticationFailed();
1✔
108
            }
109

110
            $data = sodium_unpad($data, $this->blockSize);
1✔
111

112
            // cleanup buffers
113
            sodium_memzero($ciphertext);
1✔
114

115
            return $data;
1✔
116
        });
3✔
117
    }
118

119
    /**
120
     * Parse the $params before doing assignment.
121
     *
122
     * @param array|string|null $params
123
     *
124
     * @return void
125
     *
126
     * @throws EncryptionException If key is empty
127
     */
128
    protected function parseParams(#[SensitiveParameter] $params)
129
    {
130
        if ($params === null) {
6✔
131
            return;
5✔
132
        }
133

134
        if (is_array($params)) {
4✔
135
            if (isset($params['key'])) {
2✔
136
                $this->key = $params['key'];
2✔
137
            }
138

139
            if (isset($params['blockSize'])) {
2✔
140
                $this->blockSize = $params['blockSize'];
2✔
141
            }
142

143
            return;
2✔
144
        }
145

146
        $this->key = (string) $params;
2✔
147
    }
148
}
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

© 2025 Coveralls, Inc