1: <?php
2: /*
3: * SimpleID
4: *
5: * Copyright (C) Kelvin Mo 2010-2026
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).
70: *
71: * By default, the conversion method is a form of Base58 encoding, which strips
72: * out confusing characters such as I, l, O and 0. A custom encoding can be
73: * specified in the `$chars` parameter.
74: *
75: * @param int<1, max> $num_chars the number of characters in the password
76: * @param int<0, max> $group_size if greater than 0, the characters
77: * are grouped into groups of this size, separated by hyphens
78: * @param string $chars the set of characters to use for the password
79: * @return string the random password
80: */
81: function password($num_chars = 18, $group_size = 0, $chars = self::BASE58_CHARS) {
82: // determine mask for valid characters
83: $mask = 256 - (256 % strlen($chars));
84:
85: $result = '';
86: do {
87: $rand = self::bytes($num_chars);
88: for ($i = 0; $i < $num_chars; $i++) {
89: if (ord($rand[$i]) >= $mask) continue;
90: $result .= $chars[ord($rand[$i]) % strlen($chars)];
91: }
92: } while (strlen($result) < $num_chars);
93: $result = substr($result, 0, $num_chars);
94:
95: if ($group_size > 0) {
96: $grouped_result = [];
97: for ($i = 0; $i < strlen($result); $i += $group_size) {
98: $grouped_result[] = substr($result, $i, $group_size);
99: }
100: return implode('-', $grouped_result);
101: } else {
102: return $result;
103: }
104: }
105:
106: /**
107: * Generates a relatively unique identifier which can be used as, among other things,
108: * an OpenID association handle or an OAuth client identifier.
109: *
110: * Note that the identifier returned is not cryptographically secure.
111: *
112: * @return string a relatively unique identifier
113: */
114: function id() {
115: return (string) Ulid::generate(true);
116: }
117:
118: /**
119: * Generates a short-lived code as a number with a specified number of
120: * digits
121: *
122: * Note that the identifier returned is not cryptographically secure.
123: *
124: * @param int<1, max> $num_digits the number of digits
125: * @return string a short-lived code
126: */
127: function shortCode($num_digits = 6) {
128: $base = new BigNum(self::bytes($num_digits), 256);
129: $val = $base->val(10);
130: assert($val != false);
131: return substr($val, -$num_digits);
132: }
133: }
134: ?>
135: