1: <?php
2: /*
3: * SimpleID
4: *
5: * Copyright (C) Kelvin Mo 2014-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 SimpleID\Store\StoreManager;
25:
26: /**
27: * A generator and verifier of opaque identifier tokens.
28: *
29: * An opaque identifer token is a string which identifies something in SimpleID
30: * (e.g. a user), but encoded so that it provides no information to the
31: * recipient about the thing it identifies.
32: *
33: * An opaque identifier is generated by encoding a SimpleID identifier with
34: * a context array. The context array contains recipient-specific data so that
35: * the opaque identifier is bound to the recipient.
36: */
37: class OpaqueIdentifier {
38: /** @var string */
39: static private $opaque_token = null;
40:
41: function __construct() {
42: if (self::$opaque_token === null) self::$opaque_token = (StoreManager::instance())->getKey('opaque-token', true, 16);
43: }
44:
45: /**
46: * Verifies whether an opaque identifier token matches a specified identifier.
47: *
48: * @param string $token the identifier token
49: * @param string $expected_id the expected identifier
50: * @param array<mixed> $context additional data that have been encoded
51: * @return bool true if the identifier token matches the expected identifier
52: */
53: public function verify($token, $expected_id, $context = []) {
54: return ($this->generate($expected_id, $context) == $token);
55: }
56:
57: /**
58: * Generates an opaque identifier token
59: *
60: * @param string $id the identifier to encode
61: * @param array<mixed> $context additional data to encode
62: * @return string the opaque identifier token
63: */
64: public function generate($id, $context = []) {
65: $base_string = $id . ' ' . $this->formatArray($context);
66: $hash = hash_hmac('sha256', $base_string, self::$opaque_token, true);
67: return trim(strtr(base64_encode($hash), '+/', '-_'), '=');
68: }
69:
70:
71: /**
72: * Converts an array into a string. The array must be a single
73: * dimension with string values.
74: *
75: * @param array<string, string> $array the array the convert
76: * @return string the converted string
77: */
78: protected function formatArray($array) {
79: $output = [];
80:
81: ksort($array);
82: $keys = array_keys($array);
83:
84: foreach ($keys as $key) {
85: $output[] = $key . ": " . $array[$key];
86: }
87:
88: return implode('; ', $output);
89: }
90: }