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

cypht-org / cypht / 19104101509

05 Nov 2025 01:47PM UTC coverage: 78.204% (-1.4%) from 79.595%
19104101509

push

travis-ci

web-flow
Merge pull request #1773 from IrAlfred/fix-coverage-tests-issue

fix(other): fix coverage test issue

4650 of 5946 relevant lines covered (78.2%)

6.42 hits per line

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

96.84
/lib/crypt.php
1
<?php
2

3
/**
4
 * Encryption
5
 * @package framework
6
 * @subpackage crypt
7
 */
8

9
/**
10
 * Manage request keys for modules
11
 */
12
class Hm_Request_Key {
13

14
    /* site key */
15
    private static $site_hash = '';
16

17
    /**
18
     * Load the request key
19
     * @param object $session session interface
20
     * @param object $request request object
21
     * @param bool $just_logged_in true if the session was created on this request
22
     * @return void
23
     */
24
    public static function load($session, $request, $just_logged_in) {
18✔
25
        $user = '';
18✔
26
        $key = '';
18✔
27
        if ($session->is_active()) {
18✔
28
            if (!$just_logged_in) {
5✔
29
                $user = $session->get('username', '');
4✔
30
                $key = $session->get('request_key', '');
4✔
31
            }
32
            else {
33
                $session->set('request_key', Hm_Crypt::unique_id());
2✔
34
            }
35
        }
36
        $site_id = '';
18✔
37
        if (defined('SITE_ID')) {
18✔
38
            $site_id = SITE_ID;
18✔
39
        }
40
        self::$site_hash = $session->build_fingerprint($request->server, $key.$user.$site_id);
18✔
41
    }
42

43
    /**
44
     * Return the request key
45
     * @return string request key
46
     */
47
    public static function generate() {
9✔
48
        return self::$site_hash;
9✔
49
    }
50

51
    /**
52
     * Validate a request key
53
     * @param string $key value to check
54
     * @return bool true on success
55
     */
56
    public static function validate($key) {
4✔
57
        return $key === self::$site_hash;
4✔
58
    }
59
}
60

61
class Hm_Crypt_Base {
62

63
    static protected $method = 'aes-256-cbc';
64
    static protected $hmac = 'sha512';
65
    static protected $password_rounds = 86000;
66
    static protected $encryption_rounds = 100;
67
    static protected $hmac_rounds = 101;
68

69
    /**
70
     * Convert ciphertext to plaintext
71
     * @param string $string ciphertext to decrypt
72
     * @param string $key encryption key
73
     * @return string|false decrypted text
74
     */
75
    public static function plaintext($string, $key) {
7✔
76
        $string = base64_decode($string);
7✔
77

78
        /* bail if the crypt text is invalid */
79
        if (!$string || strlen($string) <= 200) {
7✔
80
            return false;
1✔
81
        }
82

83
        /* get the payload and salt */
84
        $crypt_string = substr($string, 192);
6✔
85
        $salt = substr($string, 0, 128);
6✔
86

87
        /* check the signature. Temporarily allow the same key for hmac validation, eventually remove the $encryption_rounds
88
         * check and require the hmac_rounds check only! */
89
        if (!self::check_hmac($crypt_string, substr($string, 128, 64), $salt, $key, self::$hmac_rounds) &&
6✔
90
            !self::check_hmac($crypt_string, substr($string, 128, 64), $salt, $key, self::$encryption_rounds)) {
6✔
91
            Hm_Debug::add('HMAC verification failed');
6✔
92
            return false;
6✔
93
        }
94

95
        /* generate remaining keys */
96
        $iv = self::pbkdf2($key, $salt, 16, self::$encryption_rounds, self::$hmac);
1✔
97
        $crypt_key = self::pbkdf2($key, $salt, 32, self::$encryption_rounds, self::$hmac);
1✔
98

99
        /* return the decrpted text */
100
        return openssl_decrypt($crypt_string, self::$method, $crypt_key, OPENSSL_RAW_DATA, $iv);
1✔
101

102
    }
103

104
    /**
105
     * Check hmac signature
106
     * @param string $crypt_string payload to check
107
     * @param string $hmac signature to check
108
     * @param string $salt from generate_salt()
109
     * @param string $key supplied key for the encryption
110
     * @param integer $rounds iterations
111
     * @return boolean
112
     */
113
    public static function check_hmac($crypt_string, $hmac, $salt, $key, $rounds) {
6✔
114
        $hmac_key = self::pbkdf2($key, $salt, 32, $rounds, self::$hmac);
6✔
115

116
        /* make sure the crypt text has not been tampered with */
117
        return self::hash_compare($hmac, hash_hmac(self::$hmac, $crypt_string, $hmac_key, true));
6✔
118
    }
119

120
    /**
121
     * Convert plaintext into ciphertext
122
     * @param string $string plaintext to encrypt
123
     * @param string $key encryption key
124
     * @return string encrypted text
125
     */
126
    public static function ciphertext($string, $key) {
1✔
127
        /* generate a strong salt */
128
        $salt = self::generate_salt();
1✔
129

130
        /* build required keys */
131
        $iv = self::pbkdf2($key, $salt, 16, self::$encryption_rounds, self::$hmac);
1✔
132
        $crypt_key = self::pbkdf2($key, $salt, 32, self::$encryption_rounds, self::$hmac);
1✔
133
        $hmac_key = self::pbkdf2($key, $salt, 32, self::$hmac_rounds, self::$hmac);
1✔
134

135
        /* encrypt the string */
136
        $crypt_string = openssl_encrypt($string, self::$method, $crypt_key, OPENSSL_RAW_DATA, $iv);
1✔
137

138
        /* build a hash of the crypted text */
139
        $hmac = hash_hmac(self::$hmac, $crypt_string, $hmac_key, true);
1✔
140

141
        /* return the salt, hash, and crypt text */
142
        return base64_encode($salt.$hmac.$crypt_string);
1✔
143
    }
144

145
    /**
146
     * Generate a strong random salt (hopefully)
147
     * @return string
148
     */
149
    public static function generate_salt() {
1✔
150
        /* generate random bytes */
151
        return self::random(128);
1✔
152
    }
153

154
    /**
155
     * Compare password hashes
156
     *
157
     * @param string $a hash
158
     * @param string $b hash
159
     * @return bool
160
    */
161
    private static function hash_equals($a, $b) {
1✔
162
        $res = 0;
1✔
163
        $len = strlen($a);
1✔
164
        for ($i = 0; $i < $len; $i++) {
1✔
165
            $res |= ord($a[$i]) ^ ord($b[$i]);
1✔
166
        }
167
        return $res === 0;
1✔
168
    }
169

170
    /**
171
     * Compare password hashes with hash_equals is available, otherwise use
172
     * timing attack safe comparison
173
     *
174
     * @param string $a hash
175
     * @param string $b hash
176
     * @return bool
177
     */
178
    public static function hash_compare($a, $b) {
7✔
179
        if (!is_string($a) || !is_string($b) || strlen($a) !== strlen($b)) {
7✔
180
            return false;
1✔
181
        }
182
        /* requires PHP >= 7.4 */
183
        if (Hm_Functions::function_exists('hash_equals')) {
7✔
184
            return hash_equals($a, $b);
7✔
185
        }
186
        return self::hash_equals($a, $b);
1✔
187
    }
188

189
    /**
190
     * Key derivation wth pbkdf2: http://en.wikipedia.org/wiki/PBKDF2
191
     * @param string $key payload
192
     * @param string $salt random string from generate_salt
193
     * @return string[]
194
     */
195
    protected static function keygen($key, $salt) {
31✔
196
        return [$salt, self::pbkdf2($key, $salt, 32, self::$encryption_rounds, self::$hmac)];
31✔
197
    }
198

199
    /**
200
     * Key derivation wth pbkdf2: http://en.wikipedia.org/wiki/PBKDF2
201
     * @param string $key payload
202
     * @param string $salt random string from generate_salt
203
     * @param integer $length result length
204
     * @param integer $count iterations
205
     * @param string $algo hash algorithm to use
206
     * @return string
207
     */
208
    public static function pbkdf2($key, $salt, $length, $count, $algo) {
33✔
209
        /* requires PHP >= 5.5 */
210
        if (Hm_Functions::function_exists('openssl_pbkdf2')) {
33✔
211
            return openssl_pbkdf2($key, $salt, $length, $count, $algo);
33✔
212
        }
213

214
        /* manual version */
215
        $size = strlen(hash($algo, '', true));
1✔
216
        $len = ceil($length / $size);
1✔
217
        $result = '';
1✔
218
        for ($i = 1; $i <= $len; $i++) {
1✔
219
            $tmp = hash_hmac($algo, $salt . pack('N', $i), $key, true);
1✔
220
            $res = $tmp;
1✔
221
            for ($j = 1; $j < $count; $j++) {
1✔
222
                 $tmp  = hash_hmac($algo, $tmp, $key, true);
1✔
223
                 $res ^= $tmp;
1✔
224
            }
225
            $result .= $res;
1✔
226
        }
227
        return substr($result, 0, $length);
1✔
228
    }
229

230
    /**
231
     * Hash a password using PBKDF2 or PHP password_hash if availble
232
     * @param string $password password to hash
233
     * @param string $salt salt to use, if false generate a new one
234
     * @param int $count interations for PBKDF2
235
     * @param string $algo PBKDF2 algo, defaults to sha512
236
     * @param string $type Can be either pbkdf2 or php
237
     * @return string
238
     */
239
    public static function hash_password($password, $salt = false, $count = false, $algo = 'sha512', $type = 'php') {
2✔
240
        if (function_exists('password_hash') && $type === 'php') {
2✔
241
            return password_hash($password,  PASSWORD_DEFAULT);
×
242
        }
243
        if ($salt === false) {
2✔
244
            $salt = self::generate_salt();
×
245
        }
246
        if ($count === false) {
2✔
247
            $count = self::$password_rounds;
×
248
        }
249
        return sprintf("%s:%s:%s:%s", $algo, $count, base64_encode($salt), base64_encode(
2✔
250
            self::pbkdf2($password, $salt, 32, $count, $algo)));
2✔
251
    }
252

253
    /**
254
     * Check a password against it's stored hash
255
     * @param string $password clear text password
256
     * @param string $hash hashed password
257
     * @return bool
258
     */
259
    public static function check_password($password, $hash) {
6✔
260
        $type = 'php';
6✔
261
        if (mb_substr($hash, 0, 6) === 'sha512') {
6✔
262
            $type = 'pbkdf2';
4✔
263
        }
264
        if (function_exists('password_verify') && $type === 'php') {
6✔
265
            return password_verify($password, $hash);
4✔
266
        }
267
        if (count(explode(':', $hash)) == 4) {
4✔
268
            list($algo, $count, $salt,,) = explode(':', $hash);
2✔
269
            return self::hash_compare(self::hash_password($password, base64_decode($salt), $count, $algo, $type), $hash);
2✔
270
        }
271
        return false;
2✔
272
    }
273

274
    /**
275
     * Return a unique-enough-key for session cookie ids
276
     * @param int $size length of the result
277
     * @return string
278
     */
279
    public static function unique_id($size = 128) {
11✔
280
        return base64_encode(openssl_random_pseudo_bytes($size));
11✔
281
    }
282

283
    /**
284
     * Generate a random string
285
     * @param int $size
286
     * @return string
287
     */
288
    public static function random($size = 128) {
2✔
289
        try {
290
            return Hm_Functions::random_bytes($size);
2✔
291
        } catch (Exception $e) {
1✔
292
            Hm_Functions::cease('No reliable random byte source found');
1✔
293
        }
294
    }
295
}
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