-
Notifications
You must be signed in to change notification settings - Fork 6
/
Totp.hpp
53 lines (51 loc) · 2.11 KB
/
Totp.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#pragma once
#include <openssl/hmac.h>
#include <openssl/sha.h>
#include <algorithm>
#include <boost/endian/conversion.hpp>
#include <boost/range/counting_range.hpp>
#include <chrono>
#include <cmath>
#include <cstddef>
#include <gsl/span>
// Implements RFC 6238 (TOTP) and RFC 4226 (HOTP) for SHA1
namespace CollabVm::Server::Totp {
inline int GenerateTotp(
const gsl::span<const std::byte> key,
const int digits = 6,
const std::chrono::seconds time_step = std::chrono::seconds(30),
const int time_window = 0,
const std::chrono::seconds timestamp =
std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch())) {
const auto timer =
boost::endian::native_to_big(timestamp / time_step - time_window);
const auto digest =
gsl::make_span(HMAC(EVP_sha1(), key.data(), key.size(),
reinterpret_cast<const uint8_t*>(&timer),
sizeof(timer), nullptr, nullptr),
SHA_DIGEST_LENGTH);
constexpr unsigned long lowest_4_bits = (1u << 4) - 1;
const int offset = digest[SHA_DIGEST_LENGTH - 1] & lowest_4_bits;
constexpr unsigned long lowest_31_bits = (1u << 31) - 1;
const int binary = boost::endian::endian_reverse(
*reinterpret_cast<const int*>(&digest[offset])) &
lowest_31_bits;
return binary % static_cast<int>(std::pow(10, digits));
}
inline bool ValidateTotp(
const int input,
const gsl::span<const std::byte> key,
const int digits = 6,
const std::chrono::seconds time_step = std::chrono::seconds(30),
const int time_window = 1) {
const auto now = std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch());
const auto counter = boost::counting_range(0, time_window + 1);
return std::any_of(
counter.begin(), counter.end(), [&](int current_time_window) {
return GenerateTotp(key, digits, time_step, current_time_window, now) ==
input;
});
}
} // namespace CollabVm::Server::Totp