diff --git a/src/OTP.php b/src/OTP.php
index 7ad84ac4eb0cbec31381562d0c722867e8c42252..cf91c983a541db6a14448d328227944f470fe9b8 100644
--- a/src/OTP.php
+++ b/src/OTP.php
@@ -16,6 +16,7 @@ namespace OTPHP;
 use Assert\Assertion;
 use ParagonIE\ConstantTime\Base32;
 use RuntimeException;
+use function Safe\hex2bin;
 use function Safe\ksort;
 use function Safe\sprintf;
 
@@ -43,13 +44,12 @@ abstract class OTP implements OTPInterface
     protected function generateOTP(int $input): string
     {
         $hash = hash_hmac($this->getDigest(), $this->intToByteString($input), $this->getDecodedSecret());
-        $hmac = [];
-        foreach (str_split($hash, 2) as $hex) {
-            $hmac[] = hexdec($hex);
-        }
-        $offset = $hmac[\count($hmac) - 1] & 0xF;
+
+        $hmac = unpack('C*', hex2bin($hash));
+
+        $offset = ($hmac[\count($hmac)] & 0xF) + 1;
         $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();
+        $otp = $code % (10 ** $this->getDigits());
 
         return str_pad((string) $otp, $this->getDigits(), '0', STR_PAD_LEFT);
     }
@@ -85,12 +85,10 @@ abstract class OTP implements OTPInterface
     private function getDecodedSecret(): string
     {
         try {
-            $secret = Base32::decodeUpper($this->getSecret());
+            return Base32::decodeUpper($this->getSecret());
         } catch (\Exception $e) {
             throw new RuntimeException('Unable to decode the secret. Is it correctly base32 encoded?');
         }
-
-        return $secret;
     }
 
     private function intToByteString(int $int): string