78static std::string_view getQueryParam(std::string_view url, std::string_view param, std::string_view fallback = std::string_view())
160 const auto secret = decodeBase32(getQueryParam(url,
"secret"));
161 const auto period = CppUtilities::stringToNumber<std::uint64_t>(getQueryParam(url,
"period",
"30"));
162 const auto digits = CppUtilities::stringToNumber<int>(getQueryParam(url,
"digits",
"6"));
163 const auto algo = getQueryParam(url,
"algorithm",
"SHA1");
166 auto timeStamp =
static_cast<std::uint64_t
>(time.toTimeStamp());
167 auto counter = timeStamp / period;
168 auto remaining = period - (timeStamp % period);
169 auto counterBytes = std::array<unsigned char, 8>();
170 CppUtilities::BE::getBytes(counter,
reinterpret_cast<char *
>(counterBytes.data()));
173 EVP_MAC *
const mac = EVP_MAC_fetch(
nullptr,
"HMAC",
nullptr);
177 EVP_MAC_CTX *
const ctx = EVP_MAC_CTX_new(mac);
184 OSSL_PARAM params[2];
185 params[0] = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_DIGEST,
const_cast<char *
>(algo.data()), 0);
186 params[1] = OSSL_PARAM_construct_end();
189 if (EVP_MAC_init(ctx, secret.data(), secret.size(), params) != 1) {
190 EVP_MAC_CTX_free(ctx);
196 if (EVP_MAC_update(ctx, counterBytes.data(), counterBytes.size()) != 1) {
197 EVP_MAC_CTX_free(ctx);
203 auto out = std::array<unsigned char, EVP_MAX_MD_SIZE>();
204 auto outLen = std::size_t();
205 if (EVP_MAC_final(ctx, out.data(), &outLen, out.size()) != 1) {
206 EVP_MAC_CTX_free(ctx);
210 EVP_MAC_CTX_free(ctx);
214 const auto offset =
static_cast<std::size_t
>(out[outLen - 1] & 0x0F);
215 const auto truncatedHash = (
static_cast<std::uint32_t
>(out[offset] & 0x7F) << 24) | (
static_cast<std::uint32_t
>(out[offset + 1] & 0xFF) << 16)
216 | (
static_cast<std::uint32_t
>(out[offset + 2] & 0xFF) << 8) |
static_cast<std::uint32_t
>(out[offset + 3] & 0xFF);
217 const auto otp = truncatedHash %
static_cast<std::uint32_t
>(std::pow(10, digits));
219 .digits = (std::ostringstream() << std::setfill(
'0') << std::setw(digits) << otp).str(),
220 .period = CppUtilities::TimeSpan::fromSeconds(
static_cast<double>(period)),
221 .remaining = CppUtilities::TimeSpan::fromSeconds(
static_cast<double>(remaining)),