-
Notifications
You must be signed in to change notification settings - Fork 22
/
Copy pathgoogle_authenticator.cpp
123 lines (100 loc) · 4.23 KB
/
google_authenticator.cpp
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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
/*!
\file google_authenticator.cpp
\brief Google Authenticator implementation
\author Ivan Shynkarenka
\date 11.06.2019
\copyright MIT License
*/
#include "security/google_authenticator.h"
#include "errors/exceptions.h"
#include "memory/memory.h"
#include "string/encoding.h"
#include "string/format.h"
#include "utility/countof.h"
#include <cassert>
#include <openssl/evp.h>
#include <openssl/hmac.h>
namespace CppSecurity {
GoogleAuthenticator::GoogleAuthenticator(size_t secret_length, size_t pin_length)
: _secret_length(secret_length), _pin_length(pin_length)
{
assert((secret_length >= 6) && "Secret should be at least 6 bytes!");
if (secret_length < 6)
throwex CppCommon::SecurityException("Invalid secret length!");
assert(((pin_length >= 3) && (pin_length <= 10)) && "Pin should be at least 3 digits and not more than 10 digits!");
if ((pin_length < 3) || (pin_length > 10))
throwex CppCommon::SecurityException("Invalid pin length!");
}
std::string GoogleAuthenticator::GenerateSalt() const
{
std::string result(secret_length(), 0);
CppCommon::Memory::CryptoFill(result.data(), result.size());
return result;
}
std::password GoogleAuthenticator::GenerateSecret(std::string_view password) const
{
return std::password(CppCommon::Encoding::Base32Encode(password));
}
std::password GoogleAuthenticator::GenerateSecret(std::string_view password, std::string_view salt) const
{
// Compute the HMAC-SHA1 of the secret and the challenge
uint8_t hash[EVP_MAX_MD_SIZE];
unsigned int size = EVP_MAX_MD_SIZE;
if (HMAC(EVP_sha1(), password.data(), (int)password.size(), (const uint8_t*)salt.data(), (int)salt.size(), hash, &size) == nullptr)
throwex CppCommon::SecurityException("HMAC-SHA1 calculation error!");
// Generate the secret
std::password result(secret_length(), 0);
for (size_t i = 0; i < result.size(); ++i)
result[i] = hash[i % size];
return GenerateSecret(result);
}
std::password GoogleAuthenticator::GenerateURL(std::string_view identifier, std::string_view secret) const
{
return std::password(CppCommon::Encoding::URLEncode(CppCommon::format("otpauth://totp/{}?secret={}", identifier, secret)));
}
std::password GoogleAuthenticator::GenerateQRCodeLink(std::string_view url, size_t width, size_t height) const
{
return std::password(CppCommon::format("https://chart.apis.google.com/chart?cht=qr&chs={}x{}&chl={}", width, height, url));
}
size_t GoogleAuthenticator::GenerateToken(std::string_view secret, const CppCommon::Timestamp& timestamp) const
{
// Get the current timestamp in 30sec intervals
uint64_t seconds = timestamp.seconds() / 30;
// Perform big-endian timestamp conversion
uint8_t challenge[8];
for (size_t i = 8; i--; seconds >>= 8)
challenge[i] = seconds & 0xFF;
// Decode the secret from the Base32 encoding
std::password key(CppCommon::Encoding::Base32Decode(secret));
// Compute the HMAC-SHA1 of the secret and the challenge
uint8_t hash[EVP_MAX_MD_SIZE];
unsigned int size = EVP_MAX_MD_SIZE;
if (HMAC(EVP_sha1(), key.data(), (int)key.size(), challenge, CppCommon::countof(challenge), hash, &size) == nullptr)
throwex CppCommon::SecurityException("HMAC-SHA1 calculation error!");
// Pick the offset where to sample our hash value for the actual verification code
size_t offset = hash[size - 1] & 0xF;
// Compute the truncated hash in a byte-order independent loop
size_t truncated = 0;
for (size_t i = 0; i < 4; ++i)
{
truncated <<= 8;
truncated |= hash[offset + i];
}
// Compute the pin scale
size_t pin = 1;
for (size_t i = 0; i < pin_length(); ++i)
pin *= 10;
// Truncate to a smaller number of digits
truncated &= 0x7FFFFFFF;
truncated %= pin;
return truncated;
}
bool GoogleAuthenticator::Validate(size_t token, std::string_view secret, const CppCommon::Timestamp& timestamp) const
{
return (token == GenerateToken(secret, timestamp));
}
bool GoogleAuthenticator::Validate(size_t token, std::string_view password, std::string_view salt, const CppCommon::Timestamp& timestamp) const
{
return (token == GenerateToken(GenerateSecret(password, salt), timestamp));
}
} // namespace CppSecurity