1: <?php
2: /*
3: * SimpleID
4: *
5: * Copyright (C) Kelvin Mo 2010-2025
6: *
7: * This program is free software; you can redistribute it and/or
8: * modify it under the terms of the GNU General Public
9: * License as published by the Free Software Foundation; either
10: * version 2 of the License, or (at your option) any later version.
11: *
12: * This program is distributed in the hope that it will be useful,
13: * but WITHOUT ANY WARRANTY; without even the implied warranty of
14: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15: * General Public License for more details.
16: *
17: * You should have received a copy of the GNU General Public
18: * License along with this program; if not, write to the Free
19: * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20: */
21:
22: namespace SimpleID\Crypt;
23:
24: use Ulid\Ulid;
25:
26: /**
27: * Functions related to generating random bits and unique values.
28: *
29: * @since 0.8
30: */
31: class Random {
32:
33: const BASE58_CHARS = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
34:
35: /**
36: * Obtains a number of random bytes using the native `random_bytes()` function.
37: *
38: * @param int<1, max> $num_bytes the number of bytes to generate
39: * @return string a string containing random bytes
40: */
41: static function bytes($num_bytes) {
42: return random_bytes($num_bytes);
43: }
44:
45: /**
46: * Obtains a random string of a specified number of bytes of entropy.
47: *
48: * The function calls the {@link bytes()} function with the specified
49: * number of bytes, then converts to a string containing only alphanumeric
50: * characters (case sensitive), plus the characters ., _ and -.
51: *
52: * The conversion method is based on the Base64 encoding. However, non-standard
53: * characters are used so that users are not confused and attempt to decode
54: * the returned string.
55: *
56: * @param int<1, max> $num_bytes the approximate number of bytes of entropy in the
57: * random string
58: * @return string the random string
59: */
60: function secret($num_bytes = 32) {
61: return strtr(trim(base64_encode(self::bytes($num_bytes)), '='), '+/', '-_');
62: }
63:
64: /**
65: * Generates a random string that can be used as a password.
66: *
67: * The function calls the {@link bytes()} function with the specified
68: * number of characters, then converts to a string containing only alphanumeric
69: * characters (case sensitive). The conversion method is a form of Base58
70: * encoding, which strips out confusing characters such as I, l, O and 0.
71: *
72: * @param int<1, max> $num_chars the number of characters in the password
73: * @return string the random password
74: */
75: function password($num_chars = 18) {
76: // determine mask for valid characters
77: $mask = 256 - (256 % strlen(self::BASE58_CHARS));
78:
79: $result = '';
80: do {
81: $rand = self::bytes($num_chars);
82: for ($i = 0; $i < $num_chars; $i++) {
83: if (ord($rand[$i]) >= $mask) continue;
84: $result .= self::BASE58_CHARS[ord($rand[$i]) % strlen(self::BASE58_CHARS)];
85: }
86: } while (strlen($result) < $num_chars);
87: return substr($result, 0, $num_chars);
88: }
89:
90: /**
91: * Generates a relatively unique identifier which can be used as, among other things,
92: * an OpenID association handle or an OAuth client identifier.
93: *
94: * Note that the identifier returned is not cryptographically secure.
95: *
96: * @return string a relatively unique identifier
97: */
98: function id() {
99: return (string) Ulid::generate(true);
100: }
101:
102: /**
103: * Generates a short-lived code as a number with a specified number of
104: * digits
105: *
106: * Note that the identifier returned is not cryptographically secure.
107: *
108: * @param int<1, max> $num_digits the number of digits
109: * @return string a short-lived code
110: */
111: function shortCode($num_digits = 6) {
112: $base = new BigNum(self::bytes($num_digits), 256);
113: $val = $base->val(10);
114: assert($val != false);
115: return substr($val, -$num_digits);
116: }
117: }
118: ?>
119: