#include "secret_token.h"
#include <algorithm>
#include <array>
#include <cstddef>
#include <string>
namespace
{
constexpr std::array<char, 16> HEX = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
mw::E<std::string> decodeToken(const std::string& token)
{
if(token.size() != 64)
{
return std::unexpected(mw::runtimeError("Invalid token"));
}
std::string bytes(32, '\0');
for(std::size_t index = 0; index < bytes.size(); ++index)
{
const auto nibble = [](char value) -> int
{
if(value >= '0' && value <= '9')
{
return value - '0';
}
if(value >= 'a' && value <= 'f')
{
return value - 'a' + 10;
}
return -1;
};
const int high = nibble(token[index * 2]);
const int low = nibble(token[index * 2 + 1]);
if(high < 0 || low < 0)
{
return std::unexpected(mw::runtimeError("Invalid token"));
}
bytes[index] = static_cast<char>((high << 4) | low);
}
return bytes;
}
} // namespace
mw::E<SecretToken> generateSecretToken(mw::CryptoInterface& crypto)
{
auto random = crypto.randomBytes(32);
if(!random)
{
return std::unexpected(std::move(random.error()));
}
std::string bytes;
bytes.reserve(random->size());
std::string value;
value.reserve(random->size() * 2);
for(std::byte byte : *random)
{
const unsigned int number = std::to_integer<unsigned int>(byte);
bytes.push_back(static_cast<char>(number));
value.push_back(HEX[number >> 4]);
value.push_back(HEX[number & 0x0f]);
}
auto hash = mw::SHA256Hasher().hashToBytes(bytes);
if(!hash)
{
return std::unexpected(std::move(hash.error()));
}
return SecretToken{std::move(value), std::move(*hash)};
}
mw::E<std::vector<unsigned char>> hashSecretToken(const std::string& token)
{
auto bytes = decodeToken(token);
if(!bytes)
{
return std::unexpected(std::move(bytes.error()));
}
return mw::SHA256Hasher().hashToBytes(*bytes);
}
bool constantTimeEqual(const std::string& first, const std::string& second)
{
const std::size_t length = std::max(first.size(), second.size());
std::size_t difference = first.size() ^ second.size();
for(std::size_t index = 0; index < length; ++index)
{
const unsigned char left = index < first.size() ? first[index] : 0;
const unsigned char right = index < second.size() ? second[index] : 0;
difference |= static_cast<std::size_t>(left ^ right);
}
return difference == 0;
}