getProperties(); $timestamp = $tokenInfo instanceof AuthTokenInfo ? $tokenInfo->getTimestamp() : time(); $data = ''; foreach($props as $name => $value) { // very smart solution for this issue, you definitely won't be confused by this later // down the line when a variable suddenly despawns from the token $nameLength = strlen($name); $valueLength = strlen($value); if($nameLength > 255 || $valueLength > 255) continue; $data .= chr($nameLength) . $name . chr($valueLength) . $value; } $prefix = pack('CN', 2, $timestamp - self::EPOCH_V2); $data = $prefix . hash_hmac('sha3-256', $prefix . $data, $this->secretKey, true) . $data; return UriBase64::encode($data); } public function unpack(?string $token): AuthTokenInfo { if($token === null || $token === '') return AuthTokenInfo::empty(); $data = UriBase64::decode($token); if($data === false || $data === '') return AuthTokenInfo::empty(); $builder = new AuthTokenBuilder; $version = ord($data[0]); $data = str_pad(substr($data, 1), 36, "\x00"); $timestamp = null; if($version === 1) { $data = unpack('Nuser/H*token', $data); if($data === false) return AuthTokenInfo::empty(); $builder->setUserId((string)$data['user']); $builder->setSessionToken($data['token']); } elseif($version === 2) { $timestamp = substr($data, 0, 4); $userHash = substr($data, 4, 32); $data = substr($data, 36); $realHash = hash_hmac('sha3-256', chr($version) . $timestamp . $data, $this->secretKey, true); if(!hash_equals($realHash, $userHash)) return AuthTokenInfo::empty(); $unpackTime = unpack('Nts', $timestamp); if($unpackTime === false) throw new RuntimeException('$token does not contain a valid timestamp.'); $timestamp = $unpackTime['ts'] + self::EPOCH_V2; $stream = MemoryStream::fromString($data); $stream->seek(0); for(;;) { $length = $stream->readChar(); if($length === null) break; $length = ord($length); if($length < 1) break; $name = $stream->read($length); $value = null; $length = $stream->readChar(); if($length !== null) { $length = ord($length); if($length > 0) $value = $stream->read($length); } $builder->setProperty($name, $value); } } else return AuthTokenInfo::empty(); return $builder->toInfo($timestamp); } }