From 3fd785654372d493c031d9b541ab33a881023a32 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Fri, 26 Feb 2021 19:16:17 +0300 Subject: * switch to composer for qrcode and otp dependencies * move most OTP-related stuff into userhelper * remove old phpqrcode and otphp libraries --- vendor/spomky-labs/otphp/src/OTP.php | 114 +++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 vendor/spomky-labs/otphp/src/OTP.php (limited to 'vendor/spomky-labs/otphp/src/OTP.php') diff --git a/vendor/spomky-labs/otphp/src/OTP.php b/vendor/spomky-labs/otphp/src/OTP.php new file mode 100644 index 000000000..932bcf97e --- /dev/null +++ b/vendor/spomky-labs/otphp/src/OTP.php @@ -0,0 +1,114 @@ +setSecret($secret); + $this->setDigest($digest); + $this->setDigits($digits); + } + + public function getQrCodeUri(string $uri, string $placeholder): string + { + $provisioning_uri = urlencode($this->getProvisioningUri()); + + return str_replace($placeholder, $provisioning_uri, $uri); + } + + /** + * The OTP at the specified input. + */ + protected function generateOTP(int $input): string + { + $hash = hash_hmac($this->getDigest(), $this->intToByteString($input), $this->getDecodedSecret(), true); + + $hmac = array_values(unpack('C*', $hash)); + + $offset = ($hmac[\count($hmac) - 1] & 0xF); + $code = ($hmac[$offset + 0] & 0x7F) << 24 | ($hmac[$offset + 1] & 0xFF) << 16 | ($hmac[$offset + 2] & 0xFF) << 8 | ($hmac[$offset + 3] & 0xFF); + $otp = $code % (10 ** $this->getDigits()); + + return str_pad((string) $otp, $this->getDigits(), '0', STR_PAD_LEFT); + } + + public function at(int $timestamp): string + { + return $this->generateOTP($timestamp); + } + + /** + * @param array $options + */ + protected function filterOptions(array &$options): void + { + foreach (['algorithm' => 'sha1', 'period' => 30, 'digits' => 6] as $key => $default) { + if (isset($options[$key]) && $default === $options[$key]) { + unset($options[$key]); + } + } + + ksort($options); + } + + /** + * @param array $options + */ + protected function generateURI(string $type, array $options): string + { + $label = $this->getLabel(); + Assertion::string($label, 'The label is not set.'); + Assertion::false($this->hasColon($label), 'Label must not contain a colon.'); + $options = array_merge($options, $this->getParameters()); + $this->filterOptions($options); + $params = str_replace(['+', '%7E'], ['%20', '~'], http_build_query($options)); + + return sprintf('otpauth://%s/%s?%s', $type, rawurlencode((null !== $this->getIssuer() ? $this->getIssuer().':' : '').$label), $params); + } + + private function getDecodedSecret(): string + { + try { + return Base32::decodeUpper($this->getSecret()); + } catch (\Exception $e) { + throw new RuntimeException('Unable to decode the secret. Is it correctly base32 encoded?'); + } + } + + private function intToByteString(int $int): string + { + $result = []; + while (0 !== $int) { + $result[] = \chr($int & 0xFF); + $int >>= 8; + } + + return str_pad(implode(array_reverse($result)), 8, "\000", STR_PAD_LEFT); + } + + protected function compareOTP(string $safe, string $user): bool + { + return hash_equals($safe, $user); + } +} -- cgit v1.2.3