@@ -88,6 +88,29 @@ namespace hmac {
8888 return false ;
8989 }
9090
91+ namespace detail {
92+ int hotp_from_digest (const std::vector<uint8_t >& hmac_result, int digits) {
93+ if (hmac_result.empty ()) {
94+ throw std::runtime_error (" HOTP: HMAC result too short" );
95+ }
96+ int offset = hmac_result.back () & 0x0F ;
97+ if (hmac_result.size () < static_cast <size_t >(offset) + 4 ) {
98+ throw std::runtime_error (" HOTP: HMAC result too short" );
99+ }
100+ uint32_t bin_code =
101+ ((hmac_result[offset] & 0x7F ) << 24 ) |
102+ ((hmac_result[offset + 1 ] & 0xFF ) << 16 ) |
103+ ((hmac_result[offset + 2 ] & 0xFF ) << 8 ) |
104+ ((hmac_result[offset + 3 ] & 0xFF ));
105+ static const uint64_t divisor[] = {
106+ 10UL , 100UL , 1000UL , 10000UL ,
107+ 100000UL , 1000000UL , 10000000UL ,
108+ 100000000UL , 1000000000UL
109+ };
110+ return bin_code % divisor[digits - 1 ];
111+ }
112+ }
113+
91114 int get_hotp_code (const void * key_ptr, size_t key_len, uint64_t counter, int digits, TypeHash hash_type) {
92115 if (digits < 1 || digits > 9 ) throw std::invalid_argument (" HOTP: digits must be in range [1, 9]" );
93116
@@ -101,21 +124,8 @@ namespace hmac {
101124 // Step 2: Compute HMAC
102125 std::vector<uint8_t > hmac_result = hmac::get_hmac (key_ptr, key_len, counter_bytes, 8 , hash_type);
103126
104- // Step 3: Dynamic truncation
105- int offset = hmac_result.back () & 0x0F ;
106- uint32_t bin_code =
107- ((hmac_result[offset] & 0x7F ) << 24 ) |
108- ((hmac_result[offset + 1 ] & 0xFF ) << 16 ) |
109- ((hmac_result[offset + 2 ] & 0xFF ) << 8 ) |
110- ((hmac_result[offset + 3 ] & 0xFF ));
111-
112- // Step 4: Modulo to get N-digit code
113- static const uint64_t divisor[] = {
114- 10UL , 100UL , 1000UL , 10000UL ,
115- 100000UL , 1000000UL , 10000000UL ,
116- 100000000UL , 1000000000UL
117- };
118- return bin_code % divisor[digits - 1 ];
127+ // Step 3: Dynamic truncation and modulo
128+ return detail::hotp_from_digest (hmac_result, digits);
119129 }
120130
121131 int get_totp_code_at (
0 commit comments