CppSecurity 1.1.2.0
C++ Security Library
Loading...
Searching...
No Matches
google_authenticator.cpp
Go to the documentation of this file.
1
10
11#include "errors/exceptions.h"
12#include "memory/memory.h"
13#include "string/encoding.h"
14#include "string/format.h"
15#include "utility/countof.h"
16
17#include <cassert>
18
19#include <openssl/evp.h>
20#include <openssl/hmac.h>
21
22namespace CppSecurity {
23
24GoogleAuthenticator::GoogleAuthenticator(size_t secret_length, size_t pin_length)
25 : _secret_length(secret_length), _pin_length(pin_length)
26{
27 assert((secret_length >= 6) && "Secret should be at least 6 bytes!");
28 if (secret_length < 6)
29 throwex CppCommon::SecurityException("Invalid secret length!");
30 assert(((pin_length >= 3) && (pin_length <= 10)) && "Pin should be at least 3 digits and not more than 10 digits!");
31 if ((pin_length < 3) || (pin_length > 10))
32 throwex CppCommon::SecurityException("Invalid pin length!");
33}
34
36{
37 std::string result(secret_length(), 0);
38 CppCommon::Memory::CryptoFill(result.data(), result.size());
39 return result;
40}
41
42std::password GoogleAuthenticator::GenerateSecret(std::string_view password) const
43{
44 return std::password(CppCommon::Encoding::Base32Encode(password));
45}
46
47std::password GoogleAuthenticator::GenerateSecret(std::string_view password, std::string_view salt) const
48{
49 // Compute the HMAC-SHA1 of the secret and the challenge
50 uint8_t hash[EVP_MAX_MD_SIZE];
51 unsigned int size = EVP_MAX_MD_SIZE;
52 if (HMAC(EVP_sha1(), password.data(), (int)password.size(), (const uint8_t*)salt.data(), (int)salt.size(), hash, &size) == nullptr)
53 throwex CppCommon::SecurityException("HMAC-SHA1 calculation error!");
54
55 // Generate the secret
56 std::password result(secret_length(), 0);
57 for (size_t i = 0; i < result.size(); ++i)
58 result[i] = hash[i % size];
59 return GenerateSecret(result);
60}
61
62std::password GoogleAuthenticator::GenerateURL(std::string_view identifier, std::string_view secret) const
63{
64 return std::password(CppCommon::Encoding::URLEncode(CppCommon::format("otpauth://totp/{}?secret={}", identifier, secret)));
65}
66
67std::password GoogleAuthenticator::GenerateQRCodeLink(std::string_view url, size_t width, size_t height) const
68{
69 return std::password(CppCommon::format("https://chart.apis.google.com/chart?cht=qr&chs={}x{}&chl={}", width, height, url));
70}
71
72size_t GoogleAuthenticator::GenerateToken(std::string_view secret, const CppCommon::Timestamp& timestamp) const
73{
74 // Get the current timestamp in 30sec intervals
75 uint64_t seconds = timestamp.seconds() / 30;
76
77 // Perform big-endian timestamp conversion
78 uint8_t challenge[8];
79 for (size_t i = 8; i--; seconds >>= 8)
80 challenge[i] = seconds & 0xFF;
81
82 // Decode the secret from the Base32 encoding
83 std::password key(CppCommon::Encoding::Base32Decode(secret));
84
85 // Compute the HMAC-SHA1 of the secret and the challenge
86 uint8_t hash[EVP_MAX_MD_SIZE];
87 unsigned int size = EVP_MAX_MD_SIZE;
88 if (HMAC(EVP_sha1(), key.data(), (int)key.size(), challenge, CppCommon::countof(challenge), hash, &size) == nullptr)
89 throwex CppCommon::SecurityException("HMAC-SHA1 calculation error!");
90
91 // Pick the offset where to sample our hash value for the actual verification code
92 size_t offset = hash[size - 1] & 0xF;
93
94 // Compute the truncated hash in a byte-order independent loop
95 size_t truncated = 0;
96 for (size_t i = 0; i < 4; ++i)
97 {
98 truncated <<= 8;
99 truncated |= hash[offset + i];
100 }
101
102 // Compute the pin scale
103 size_t pin = 1;
104 for (size_t i = 0; i < pin_length(); ++i)
105 pin *= 10;
106
107 // Truncate to a smaller number of digits
108 truncated &= 0x7FFFFFFF;
109 truncated %= pin;
110 return truncated;
111}
112
113bool GoogleAuthenticator::Validate(size_t token, std::string_view secret, const CppCommon::Timestamp& timestamp) const
114{
115 return (token == GenerateToken(secret, timestamp));
116}
117
118bool GoogleAuthenticator::Validate(size_t token, std::string_view password, std::string_view salt, const CppCommon::Timestamp& timestamp) const
119{
120 return (token == GenerateToken(GenerateSecret(password, salt), timestamp));
121}
122
123} // namespace CppSecurity
size_t pin_length() const noexcept
Get the pin length.
size_t GenerateToken(std::string_view secret, const CppCommon::Timestamp &timestamp=CppCommon::UtcTimestamp()) const
Generate the Google Authenticator token for the given secret and UTC timestamp.
std::password GenerateURL(std::string_view identifier, std::string_view secret) const
Generate the Google Authenticator URL.
std::string GenerateSalt() const
Generate the unique password salt.
GoogleAuthenticator(size_t secret_length=12, size_t pin_length=6)
Initialize Google Authenticator with required parameters.
std::password GenerateQRCodeLink(std::string_view url, size_t width=100, size_t height=100) const
Generate the Google Authenticator QR Code link.
std::password GenerateSecret(std::string_view password) const
Generate the Google Authenticator secret from the given user password.
size_t secret_length() const noexcept
Get the secret length.
bool Validate(size_t token, std::string_view secret, const CppCommon::Timestamp &timestamp=CppCommon::UtcTimestamp()) const
Validate the Google Authenticator token over the given secret and UTC timestamp.
Password string.
Definition password.h:53
Google Authenticator definition.