-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathESP8266TOTP.cpp
146 lines (113 loc) · 4.15 KB
/
ESP8266TOTP.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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/*
* ESP8266TOTP.h
*
* Created on: 18 Nov 2016
* Author: Joe
*
* An ESP8266 arduino core TOTP implementation compatible with rfc6238 and Google Authenticator
*
* Dependencies:
* - Arduino.h, esp8266/arduino (https://github.com/esp8266/arduino)
* - Base32.h, cpp-base32 for base32 functionality (https://github.com/jjssoftware/cpp-base32)
* - sha1.h, Cryptosuite for sha1 and hmac functionality (https://github.com/jjssoftware/Cryptosuite)
* - ESP8266TrueRandom.h, ESP8266TrueRandom for random number functionality (https://github.com/jjssoftware/ESP8266TrueRandom)
*
* Use UTC for the epoch parameter passed to methods in this class with no timezone offset
*
*/
#include "ESP8266TOTP.h"
//RFC4648 defined 'standard' Base32 base32Alphabet
unsigned char ESP8266TOTP::base32Alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
//QR code uri parts
const char* ESP8266TOTP::qrCodeUriPre = "otpauth://totp/user@";
const char* ESP8266TOTP::qrCodeUriSecret = "?secret=";
const char* ESP8266TOTP::qrCodeUriIssuer = "&issuer=";
/*
* returns a new secret key of made up of random bytes of TOTP_SECRET_BYTE_COUNT length
* into the supplied keyBytes buffer
*/
bool ICACHE_FLASH_ATTR ESP8266TOTP::GetNewKey(uint8_t* keyBytes) {
for(int i = 0; i < TOTP_SECRET_BYTE_COUNT; i++) {
char rndByte = ESP8266TrueRandom.randomByte();
keyBytes[i] = rndByte;
}
return true;
}
/*
* converts the given keyBytes to a base32 representation into the supplied data32 buffer
*/
bool ICACHE_FLASH_ATTR ESP8266TOTP::GetBase32Key(uint8_t* keyBytes, unsigned char* data32) {
if (Base32::Encode32(keyBytes, TOTP_SECRET_BYTE_COUNT, data32)) {
return Base32::Map32(data32, BASE_32_ENCODE_LENGTH, ESP8266TOTP::base32Alphabet);
}
return false;
}
/*
* returns a TOTP hashed message authentication code for the given epoch point in time and secret key bytes
*/
uint8_t* ICACHE_FLASH_ATTR ESP8266TOTP::GetTOTPHMac(uint64_t epoch, uint8_t* keyBytes) {
uint64_t time = epoch / TOTP_EPOCH_INTERVAL;
uint8_t timeBytes[8];
timeBytes[0] = 0x00;
timeBytes[1] = 0x00;
timeBytes[2] = 0x00;
timeBytes[3] = 0x00;
timeBytes[4] = (int)((time >> 24) & 0xFF) ;
timeBytes[5] = (int)((time >> 16) & 0xFF) ;
timeBytes[6] = (int)((time >> 8) & 0XFF);
timeBytes[7] = (int)((time & 0XFF));
Sha1.initHmac(keyBytes, TOTP_SECRET_BYTE_COUNT);
for (int i = 0; i < 8; i++)
{
Sha1.write(timeBytes[i]);
}
return Sha1.resultHmac();
}
/*
* returns the TOTP token for the given epoch point in time and secret key bytes
*/
int ICACHE_FLASH_ATTR ESP8266TOTP::GetTOTPToken(uint64_t epoch, uint8_t* keyBytes) {
uint8_t *hash = ESP8266TOTP::GetTOTPHMac(epoch, keyBytes);
int offset = hash[19] & 0xF;
int otp = 0;
for (int i = 0; i < 4; ++i)
{
otp = otp << 8;
otp = otp | hash[offset + i];
}
otp = otp & 0x7FFFFFFF;
otp = otp % 1000000;
return otp;
}
/*
* calculates the TOTP token for the given epoch point in time and secret key bytes and returns
* an indicator whether that calculated TOTP token and the given candidateOtp exactly match
*/
bool ICACHE_FLASH_ATTR ESP8266TOTP::IsTokenValid(uint64_t epoch, uint8_t* keyBytes, int candidateOtp) {
int otp = ESP8266TOTP::GetTOTPToken(epoch, keyBytes);
return otp == candidateOtp;
}
/*
* returns a Google Authenticator key uri for the given keyBytes
* intended for rendering a QR code in a web page.
* https://github.com/google/google-authenticator/wiki/Key-Uri-Format
*
* This method exists to enable an easy way to add your secret key to Google Authenticator.
* If you use this method to render a QR code for your secret key into a web page, ensure
* that web page is served over TLS. Also consider implementing addition security rules around
* showing the QR code.
*/
String ICACHE_FLASH_ATTR ESP8266TOTP::GetQrCodeImageUri(uint8_t* keyBytes, String hostname, String issuer) {
String outStr;
unsigned char data32[BASE_32_ENCODE_LENGTH];
if (ESP8266TOTP::GetBase32Key(keyBytes, data32)) {
char* base32Key = reinterpret_cast<char*>(&data32);
outStr = String(ESP8266TOTP::qrCodeUriPre);
outStr += hostname;
outStr += ESP8266TOTP::qrCodeUriSecret;
outStr += String(base32Key);
outStr += ESP8266TOTP::qrCodeUriIssuer;
outStr += issuer;
}
return outStr;
}