Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
 
3
 
4
namespace lbuchs\WebAuthn\Binary;
5
use lbuchs\WebAuthn\WebAuthnException;
6
 
7
/**
8
 * Modified version of https://github.com/madwizard-thomas/webauthn-server/blob/master/src/Format/ByteBuffer.php
9
 * Copyright © 2018 Thomas Bleeker - MIT licensed
10
 * Modified by Lukas Buchs
11
 * Thanks Thomas for your work!
12
 */
13
class ByteBuffer implements \JsonSerializable, \Serializable {
14
    /**
15
     * @var bool
16
     */
17
    public static $useBase64UrlEncoding = false;
18
 
19
    /**
20
     * @var string
21
     */
22
    private $_data;
23
 
24
    /**
25
     * @var int
26
     */
27
    private $_length;
28
 
29
    public function __construct($binaryData) {
30
        $this->_data = (string)$binaryData;
31
        $this->_length = \strlen($binaryData);
32
    }
33
 
34
 
35
    // -----------------------
36
    // PUBLIC STATIC
37
    // -----------------------
38
 
39
    /**
40
     * create a ByteBuffer from a base64 url encoded string
41
     * @param string $base64url
42
     * @return ByteBuffer
43
     */
44
    public static function fromBase64Url($base64url): ByteBuffer {
45
        $bin = self::_base64url_decode($base64url);
46
        if ($bin === false) {
47
            throw new WebAuthnException('ByteBuffer: Invalid base64 url string', WebAuthnException::BYTEBUFFER);
48
        }
49
        return new ByteBuffer($bin);
50
    }
51
 
52
    /**
53
     * create a ByteBuffer from a base64 url encoded string
54
     * @param string $hex
55
     * @return ByteBuffer
56
     */
57
    public static function fromHex($hex): ByteBuffer {
58
        $bin = \hex2bin($hex);
59
        if ($bin === false) {
60
            throw new WebAuthnException('ByteBuffer: Invalid hex string', WebAuthnException::BYTEBUFFER);
61
        }
62
        return new ByteBuffer($bin);
63
    }
64
 
65
    /**
66
     * create a random ByteBuffer
67
     * @param string $length
68
     * @return ByteBuffer
69
     */
70
    public static function randomBuffer($length): ByteBuffer {
71
        if (\function_exists('random_bytes')) { // >PHP 7.0
72
            return new ByteBuffer(\random_bytes($length));
73
 
74
        } else if (\function_exists('openssl_random_pseudo_bytes')) {
75
            return new ByteBuffer(\openssl_random_pseudo_bytes($length));
76
 
77
        } else {
78
            throw new WebAuthnException('ByteBuffer: cannot generate random bytes', WebAuthnException::BYTEBUFFER);
79
        }
80
    }
81
 
82
    // -----------------------
83
    // PUBLIC
84
    // -----------------------
85
 
86
    public function getBytes($offset, $length): string {
87
        if ($offset < 0 || $length < 0 || ($offset + $length > $this->_length)) {
88
            throw new WebAuthnException('ByteBuffer: Invalid offset or length', WebAuthnException::BYTEBUFFER);
89
        }
90
        return \substr($this->_data, $offset, $length);
91
    }
92
 
93
    public function getByteVal($offset): int {
94
        if ($offset < 0 || $offset >= $this->_length) {
95
            throw new WebAuthnException('ByteBuffer: Invalid offset', WebAuthnException::BYTEBUFFER);
96
        }
97
        return \ord(\substr($this->_data, $offset, 1));
98
    }
99
 
100
    public function getJson($jsonFlags=0) {
101
        $data = \json_decode($this->getBinaryString(), null, 512, $jsonFlags);
102
        if (\json_last_error() !== JSON_ERROR_NONE) {
103
            throw new WebAuthnException(\json_last_error_msg(), WebAuthnException::BYTEBUFFER);
104
        }
105
        return $data;
106
    }
107
 
108
    public function getLength(): int {
109
        return $this->_length;
110
    }
111
 
112
    public function getUint16Val($offset) {
113
        if ($offset < 0 || ($offset + 2) > $this->_length) {
114
            throw new WebAuthnException('ByteBuffer: Invalid offset', WebAuthnException::BYTEBUFFER);
115
        }
116
        return unpack('n', $this->_data, $offset)[1];
117
    }
118
 
119
    public function getUint32Val($offset) {
120
        if ($offset < 0 || ($offset + 4) > $this->_length) {
121
            throw new WebAuthnException('ByteBuffer: Invalid offset', WebAuthnException::BYTEBUFFER);
122
        }
123
        $val = unpack('N', $this->_data, $offset)[1];
124
 
125
        // Signed integer overflow causes signed negative numbers
126
        if ($val < 0) {
127
            throw new WebAuthnException('ByteBuffer: Value out of integer range.', WebAuthnException::BYTEBUFFER);
128
        }
129
        return $val;
130
    }
131
 
132
    public function getUint64Val($offset) {
133
        if (PHP_INT_SIZE < 8) {
134
            throw new WebAuthnException('ByteBuffer: 64-bit values not supported by this system', WebAuthnException::BYTEBUFFER);
135
        }
136
        if ($offset < 0 || ($offset + 8) > $this->_length) {
137
            throw new WebAuthnException('ByteBuffer: Invalid offset', WebAuthnException::BYTEBUFFER);
138
        }
139
        $val = unpack('J', $this->_data, $offset)[1];
140
 
141
        // Signed integer overflow causes signed negative numbers
142
        if ($val < 0) {
143
            throw new WebAuthnException('ByteBuffer: Value out of integer range.', WebAuthnException::BYTEBUFFER);
144
        }
145
 
146
        return $val;
147
    }
148
 
149
    public function getHalfFloatVal($offset) {
150
        //FROM spec pseudo decode_half(unsigned char *halfp)
151
        $half = $this->getUint16Val($offset);
152
 
153
        $exp = ($half >> 10) & 0x1f;
154
        $mant = $half & 0x3ff;
155
 
156
        if ($exp === 0) {
157
            $val = $mant * (2 ** -24);
158
        } elseif ($exp !== 31) {
159
            $val = ($mant + 1024) * (2 ** ($exp - 25));
160
        } else {
161
            $val = ($mant === 0) ? INF : NAN;
162
        }
163
 
164
        return ($half & 0x8000) ? -$val : $val;
165
    }
166
 
167
    public function getFloatVal($offset) {
168
        if ($offset < 0 || ($offset + 4) > $this->_length) {
169
            throw new WebAuthnException('ByteBuffer: Invalid offset', WebAuthnException::BYTEBUFFER);
170
        }
171
        return unpack('G', $this->_data, $offset)[1];
172
    }
173
 
174
    public function getDoubleVal($offset) {
175
        if ($offset < 0 || ($offset + 8) > $this->_length) {
176
            throw new WebAuthnException('ByteBuffer: Invalid offset', WebAuthnException::BYTEBUFFER);
177
        }
178
        return unpack('E', $this->_data, $offset)[1];
179
    }
180
 
181
    /**
182
     * @return string
183
     */
184
    public function getBinaryString(): string {
185
        return $this->_data;
186
    }
187
 
188
    /**
189
     * @param string|ByteBuffer $buffer
190
     * @return bool
191
     */
192
    public function equals($buffer): bool {
193
        if (is_object($buffer) && $buffer instanceof ByteBuffer) {
194
            return $buffer->getBinaryString() === $this->getBinaryString();
195
 
196
        } else if (is_string($buffer)) {
197
            return $buffer === $this->getBinaryString();
198
        }
199
 
200
        return false;
201
    }
202
 
203
    /**
204
     * @return string
205
     */
206
    public function getHex(): string {
207
        return \bin2hex($this->_data);
208
    }
209
 
210
    /**
211
     * @return bool
212
     */
213
    public function isEmpty(): bool {
214
        return $this->_length === 0;
215
    }
216
 
217
 
218
    /**
219
     * jsonSerialize interface
220
     * return binary data in RFC 1342-Like serialized string
221
     * @return string
222
     */
223
    public function jsonSerialize(): string {
224
        if (ByteBuffer::$useBase64UrlEncoding) {
225
            return self::_base64url_encode($this->_data);
226
 
227
        } else {
228
            return '=?BINARY?B?' . \base64_encode($this->_data) . '?=';
229
        }
230
    }
231
 
232
    /**
233
     * Serializable-Interface
234
     * @return string
235
     */
236
    public function serialize(): string {
237
        return \serialize($this->_data);
238
    }
239
 
240
    /**
241
     * Serializable-Interface
242
     * @param string $serialized
243
     */
244
    public function unserialize($serialized) {
245
        $this->_data = \unserialize($serialized);
246
        $this->_length = \strlen($this->_data);
247
    }
248
 
249
    /**
250
     * (PHP 8 deprecates Serializable-Interface)
251
     * @return array
252
     */
253
    public function __serialize(): array {
254
        return [
255
            'data' => \serialize($this->_data)
256
        ];
257
    }
258
 
259
    /**
260
     * object to string
261
     * @return string
262
     */
263
    public function __toString(): string {
264
        return $this->getHex();
265
    }
266
 
267
    /**
268
     * (PHP 8 deprecates Serializable-Interface)
269
     * @param array $data
270
     * @return void
271
     */
272
    public function __unserialize($data) {
273
        if ($data && isset($data['data'])) {
274
            $this->_data = \unserialize($data['data']);
275
            $this->_length = \strlen($this->_data);
276
        }
277
    }
278
 
279
    // -----------------------
280
    // PROTECTED STATIC
281
    // -----------------------
282
 
283
    /**
284
     * base64 url decoding
285
     * @param string $data
286
     * @return string
287
     */
288
    protected static function _base64url_decode($data): string {
289
        return \base64_decode(\strtr($data, '-_', '+/') . \str_repeat('=', 3 - (3 + \strlen($data)) % 4));
290
    }
291
 
292
    /**
293
     * base64 url encoding
294
     * @param string $data
295
     * @return string
296
     */
297
    protected static function _base64url_encode($data): string {
298
        return \rtrim(\strtr(\base64_encode($data), '+/', '-_'), '=');
299
    }
300
}