From eed44ab1a989b5703901b3bdd72075517413c4f0 Mon Sep 17 00:00:00 2001 From: Avinal Kumar Date: Wed, 5 Oct 2022 01:01:33 +0530 Subject: [PATCH] add totp generator code Signed-off-by: Avinal Kumar --- otpgen/main.cpp | 111 +++++++++++++++++++++++++++ otpgen/sha1.cpp | 194 ++++++++++++++++++++++++++++++++++++++++++++++++ otpgen/sha1.h | 43 +++++++++++ otpgen/topt.cpp | 94 +++++++++++++++++++++++ otpgen/topt.h | 39 ++++++++++ 5 files changed, 481 insertions(+) create mode 100644 otpgen/main.cpp create mode 100644 otpgen/sha1.cpp create mode 100644 otpgen/sha1.h create mode 100644 otpgen/topt.cpp create mode 100644 otpgen/topt.h diff --git a/otpgen/main.cpp b/otpgen/main.cpp new file mode 100644 index 0000000..905fb23 --- /dev/null +++ b/otpgen/main.cpp @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: BSD-3-Clause +// SPDX-FileCopyrightText: 2022 Avinal Kumar + +#include + +#include +#include +#include +#include + +#include "badger2040.hpp" +#include "common/pimoroni_common.hpp" +#include "pico/platform.h" +#include "pico/stdlib.h" +#include "pico/time.h" +#include "topt.h" + +using namespace pimoroni; + +Badger2040 badger; + +constexpr int WIDTH = 296; +constexpr int HEIGHT = 128; +constexpr long IST_OFFSET = 19800; + +std::string time(const long& st) { + // Add offset for IST + std::time_t t = st; + std::tm* tm_gmt = std::gmtime(&t); + // return time as string in HH:MM:SS format + return std::to_string(tm_gmt->tm_hour) + ":" + + std::to_string(tm_gmt->tm_min) + ":" + std::to_string(tm_gmt->tm_sec) + + " IST"; +} + +std::string get_totp(const long& st) { + // echo "@vinal47Kbadger2FA" | base32 + // IB3GS3TBNQ2DOS3CMFSGOZLSGJDECCQ= + uint8_t hmacKey[] = {0x40, 0x76, 0x69, 0x6e, 0x61, 0x6c, 0x34, + 0x37, 0x4b, 0x62, 0x61, 0x64, 0x67, 0x65, + 0x72, 0x32, 0x46, 0x41, 0x0a}; + TOTP(hmacKey, 19, 10); + auto code = std::to_string(getCodeFromTimestamp(st)); + if (code.length() < 6) { + code = std::string(6 - code.length(), '0') + code; + } + return code; +} + +void draw(const std::string& code, const std::string& time) { + int32_t hms_width = badger.measure_text(code, 1.8f); + int hms_offset = WIDTH / 2 - hms_width / 2; + int32_t time_width = badger.measure_text(time, 0.7f); + int time_offset = WIDTH / 2 - time_width / 2; + badger.pen(15); + badger.clear(); + badger.pen(0); + badger.thickness(5); + badger.text(code, hms_offset, 54, 1.8f); + badger.thickness(2); + badger.text(time, time_offset, 110, 0.7f); + badger.update(); +} + +void draw_timer(int seconds) { + while (seconds) { + badger.pen(15); + badger.clear(); + badger.pen(0); + badger.thickness(2); + badger.text(std::to_string(seconds), 143, 8, 0.6f); + badger.partial_update(140, 0, 16, 16); + sleep_ms(1000); + seconds--; + } +} + +void init_draw(const std::string& message) { + int32_t msg_width = badger.measure_text(message, 0.7f); + int msg_offset = WIDTH / 2 - msg_width / 2; + badger.pen(15); + badger.clear(); + badger.pen(0); + badger.thickness(3); + badger.font("sans"); + badger.text(message, msg_offset, 65, 0.7f); + badger.update(); +} + +int main() { + stdio_init_all(); + + sleep_ms(20); + + // Unix time starts on 1st January 1970 + long start_time; + + badger.init(); + badger.update_speed(2); + init_draw("Waiting for RTC sync..."); + scanf("%d", &start_time); + init_draw("RTC sync complete :)"); + while (true) { + // Increase unix time by 1 second + draw(get_totp(start_time), time(start_time + IST_OFFSET)); + draw_timer(10); + badger.led(100); + start_time += 10; + badger.led(0); + } +} diff --git a/otpgen/sha1.cpp b/otpgen/sha1.cpp new file mode 100644 index 0000000..4a11153 --- /dev/null +++ b/otpgen/sha1.cpp @@ -0,0 +1,194 @@ +/* +MIT License + +Copyright (c) 2019 Weravech + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +https://github.com/Netthaw/TOTP-MCU +*/ +#include "sha1.h" + +#include + +#define SHA1_K0 0x5a827999 +#define SHA1_K20 0x6ed9eba1 +#define SHA1_K40 0x8f1bbcdc +#define SHA1_K60 0xca62c1d6 +union _buffer { + uint8_t b[BLOCK_LENGTH]; + uint32_t w[BLOCK_LENGTH / 4]; +} buffer; +union _state { + uint8_t b[HASH_LENGTH]; + uint32_t w[HASH_LENGTH / 4]; +} state; + +uint8_t bufferOffset; +uint32_t byteCount; +uint8_t keyBuffer[BLOCK_LENGTH]; +uint8_t innerHash[HASH_LENGTH]; +uint8_t sha1InitState[] = { + 0x01, 0x23, 0x45, 0x67, // H0 + 0x89, 0xab, 0xcd, 0xef, // H1 + 0xfe, 0xdc, 0xba, 0x98, // H2 + 0x76, 0x54, 0x32, 0x10, // H3 + 0xf0, 0xe1, 0xd2, 0xc3 // H4 +}; + +void init(void) { + memcpy(state.b, sha1InitState, HASH_LENGTH); + byteCount = 0; + bufferOffset = 0; +} + +uint32_t rol32(uint32_t number, uint8_t bits) { + return ((number << bits) | (uint32_t)(number >> (32 - bits))); +} + +void hashBlock() { + uint8_t i; + uint32_t a, b, c, d, e, t; + + a = state.w[0]; + b = state.w[1]; + c = state.w[2]; + d = state.w[3]; + e = state.w[4]; + for (i = 0; i < 80; i++) { + if (i >= 16) { + t = buffer.w[(i + 13) & 15] ^ buffer.w[(i + 8) & 15] ^ + buffer.w[(i + 2) & 15] ^ buffer.w[i & 15]; + buffer.w[i & 15] = rol32(t, 1); + } + if (i < 20) { + t = (d ^ (b & (c ^ d))) + SHA1_K0; + } else if (i < 40) { + t = (b ^ c ^ d) + SHA1_K20; + } else if (i < 60) { + t = ((b & c) | (d & (b | c))) + SHA1_K40; + } else { + t = (b ^ c ^ d) + SHA1_K60; + } + t += rol32(a, 5) + e + buffer.w[i & 15]; + e = d; + d = c; + c = rol32(b, 30); + b = a; + a = t; + } + state.w[0] += a; + state.w[1] += b; + state.w[2] += c; + state.w[3] += d; + state.w[4] += e; +} + +void addUncounted(uint8_t data) { + buffer.b[bufferOffset ^ 3] = data; + bufferOffset++; + if (bufferOffset == BLOCK_LENGTH) { + hashBlock(); + bufferOffset = 0; + } +} + +void write(uint8_t data) { + ++byteCount; + addUncounted(data); + + return; +} + +void writeArray(uint8_t* buffer, uint8_t size) { + while (size--) { + write(*buffer++); + } +} + +void pad() { + // Implement SHA-1 padding (fips180-2 ��5.1.1) + + // Pad with 0x80 followed by 0x00 until the end of the block + addUncounted(0x80); + while (bufferOffset != 56) addUncounted(0x00); + + // Append length in the last 8 bytes + addUncounted(0); // We're only using 32 bit lengths + addUncounted(0); // But SHA-1 supports 64 bit lengths + addUncounted(0); // So zero pad the top bits + addUncounted(byteCount >> 29); // Shifting to multiply by 8 + addUncounted(byteCount >> 21); // as SHA-1 supports bitstreams as well as + addUncounted(byteCount >> 13); // byte. + addUncounted(byteCount >> 5); + addUncounted(byteCount << 3); +} + +uint8_t* result(void) { + // Pad to complete the last block + pad(); + + // Swap byte order back + uint8_t i; + for (i = 0; i < 5; i++) { + uint32_t a, b; + a = state.w[i]; + b = a << 24; + b |= (a << 8) & 0x00ff0000; + b |= (a >> 8) & 0x0000ff00; + b |= a >> 24; + state.w[i] = b; + } + + // Return pointer to hash (20 characters) + return state.b; +} + +#define HMAC_IPAD 0x36 +#define HMAC_OPAD 0x5c + +void initHmac(const uint8_t* key, uint8_t keyLength) { + uint8_t i; + memset(keyBuffer, 0, BLOCK_LENGTH); + if (keyLength > BLOCK_LENGTH) { + // Hash long keys + init(); + for (; keyLength--;) write(*key++); + memcpy(keyBuffer, result(), HASH_LENGTH); + } else { + // Block length keys are used as is + memcpy(keyBuffer, key, keyLength); + } + // Start inner hash + init(); + for (i = 0; i < BLOCK_LENGTH; i++) { + write(keyBuffer[i] ^ HMAC_IPAD); + } +} + +uint8_t* resultHmac(void) { + uint8_t i; + // Complete inner hash + memcpy(innerHash, result(), HASH_LENGTH); + // Calculate outer hash + init(); + for (i = 0; i < BLOCK_LENGTH; i++) write(keyBuffer[i] ^ HMAC_OPAD); + for (i = 0; i < HASH_LENGTH; i++) write(innerHash[i]); + return result(); +} diff --git a/otpgen/sha1.h b/otpgen/sha1.h new file mode 100644 index 0000000..7a42a50 --- /dev/null +++ b/otpgen/sha1.h @@ -0,0 +1,43 @@ +/* +MIT License + +Copyright (c) 2019 Weravech + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +https://github.com/Netthaw/TOTP-MCU +*/ +#ifndef SHA1_H +#define SHA1_H + +#include + +#define HASH_LENGTH 20 +#define BLOCK_LENGTH 64 + + + +void init(void); +void initHmac(const uint8_t* secret, uint8_t secretLength); +uint8_t* result(void); +uint8_t* resultHmac(void); +void write(uint8_t); +void writeArray(uint8_t* buffer, uint8_t size); + +#endif // SHA1_H \ No newline at end of file diff --git a/otpgen/topt.cpp b/otpgen/topt.cpp new file mode 100644 index 0000000..0469c0c --- /dev/null +++ b/otpgen/topt.cpp @@ -0,0 +1,94 @@ +/* +MIT License + +Copyright (c) 2019 Weravech + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +https://github.com/Netthaw/TOTP-MCU +*/ +#include "sha1.h" +#include "topt.h" + + + +uint8_t* _hmacKey; +uint8_t _keyLength; +uint8_t _timeZoneOffset; +uint32_t _timeStep; + +// Init the library with the private key, its length and the timeStep duration +void TOTP(uint8_t* hmacKey, uint8_t keyLength, uint32_t timeStep) { + _hmacKey = hmacKey; + _keyLength = keyLength; + _timeStep = timeStep; +} + +void setTimezone(uint8_t timezone) { _timeZoneOffset = timezone; } + +uint32_t TimeStruct2Timestamp(struct tm time) { + // time.tm_mon -= 1; + // time.tm_year -= 1900; + return mktime(&(time)) - (_timeZoneOffset * 3600) - 2208988800; +} + +// Generate a code, using the timestamp provided +uint32_t getCodeFromTimestamp(uint32_t timeStamp) { + uint32_t steps = timeStamp / _timeStep; + return getCodeFromSteps(steps); +} + +// Generate a code, using the timestamp provided +uint32_t getCodeFromTimeStruct(struct tm time) { + return getCodeFromTimestamp(TimeStruct2Timestamp(time)); +} + +// Generate a code, using the number of steps provided +uint32_t getCodeFromSteps(uint32_t steps) { + // STEP 0, map the number of steps in a 8-bytes array (counter value) + uint8_t _byteArray[8]; + _byteArray[0] = 0x00; + _byteArray[1] = 0x00; + _byteArray[2] = 0x00; + _byteArray[3] = 0x00; + _byteArray[4] = (uint8_t)((steps >> 24) & 0xFF); + _byteArray[5] = (uint8_t)((steps >> 16) & 0xFF); + _byteArray[6] = (uint8_t)((steps >> 8) & 0XFF); + _byteArray[7] = (uint8_t)((steps & 0XFF)); + + // STEP 1, get the HMAC-SHA1 hash from counter and key + initHmac(_hmacKey, _keyLength); + writeArray(_byteArray, 8); + uint8_t* _hash = resultHmac(); + + // STEP 2, apply dynamic truncation to obtain a 4-bytes string + uint32_t _truncatedHash = 0; + uint8_t _offset = _hash[20 - 1] & 0xF; + uint8_t j; + for (j = 0; j < 4; ++j) { + _truncatedHash <<= 8; + _truncatedHash |= _hash[_offset + j]; + } + + // STEP 3, compute the OTP value + _truncatedHash &= 0x7FFFFFFF; // Disabled + _truncatedHash %= 1000000; + + return _truncatedHash; +} diff --git a/otpgen/topt.h b/otpgen/topt.h new file mode 100644 index 0000000..1b430c5 --- /dev/null +++ b/otpgen/topt.h @@ -0,0 +1,39 @@ +/* +MIT License + +Copyright (c) 2019 Weravech + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +https://github.com/Netthaw/TOTP-MCU +*/ +#ifndef TOPT_H +#define TOPT_H + +#include + +#include "time.h" + +void TOTP(uint8_t* hmacKey, uint8_t keyLength, uint32_t timeStep); +void setTimezone(uint8_t timezone); +uint32_t getCodeFromTimestamp(uint32_t timeStamp); +uint32_t getCodeFromTimeStruct(struct tm time); +uint32_t getCodeFromSteps(uint32_t steps); + +#endif // TOPT_H \ No newline at end of file