+++ /dev/null
-Subproject commit 646474a8757e1fca490792e81082b2ad89b966a3
--- /dev/null
+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.
--- /dev/null
+TOTP Pure C Library for ALL MCU
+====================
+
+Library to generate Time-based One-Time Passwords.
+
+Implements the Time-based One-Time Password algorithm specified in [RFC 6238](https://tools.ietf.org/html/rfc6238).
+Supports different time steps and is compatible with tokens that use the same standard (including software ones, like the Google Authenticator app).
+
+Tested on MCUs: MSP430, RP2040
+
+Installation & usage:
+--------------------
+First include header to your file
+```
+#include <totp.h>
+```
+After included, define key ex. Key is ```MyLegoDoor```
+- Note: The format of hmacKey is array of hexadecimal bytes.
+- Most websites provide the key encoded in base32 - RFC3548/RFC4648, either upper or lower case. You can use [this site](https://cryptii.com/pipes/base32-to-hex) to convert the base32 string to hex (make sure you upcase it first if it's lowercase and remove all whitespaces).
+```
+uint8_t hmacKey[] = {0x4d, 0x79, 0x4c, 0x65, 0x67, 0x6f, 0x44, 0x6f, 0x6f, 0x72}; // Secret key
+```
+Instantiate the TOTP class by providing the secret hmacKey, the length of the hmacKey and the Timestep between codes.
+```
+TOTP(hmacKey, 10, 30); // Secret key, Secret key length, Timestep (30s)
+```
+Use the ```getCodeFromTimestamp()``` function to get a TOTP from a unix epoch timestamp
+```
+uint32_t newCode = getCodeFromTimestamp(1557414000); // Current timestamp since Unix epoch in seconds
+```
+Or ```getCodeFromTimeStruct()``` if you want to get a TOTP from a tm struct (Time Struct in C),
+```
+struct tm datetime;
+datetime.tm_hour = 9;
+datetime.tm_min = 0;
+datetime.tm_sec = 0;
+datetime.tm_mday = 13;
+datetime.tm_mon = 5;
+datetime.tm_year = 2019;
+uint32_t newCode = getCodeFromTimeStruct(datetime);
+```
+
+If the provided unix timestamp isn't in UTC±0, use ```setTimezone()``` before ```getCodeFromTimestamp()``` or ```getCodeFromTimeStruct()``` to offset the time.
+
+```
+setTimezone(9); // Set timezone +9 Japan
+```
+
+You can see an example in blink.c
+
+Thanks to:
+----------
+
+* Jose Damico, https://github.com/damico/ARDUINO-OATH-TOKEN
+* Peter Knight, https://github.com/Cathedrow/Cryptosuite
+* Maniacbug, https://github.com/maniacbug/Cryptosuite
+* lucadentella, https://github.com/lucadentella/TOTP-Arduino
--- /dev/null
+#include "TOTP.h"\r
+#include "sha1.h"\r
+\r
+uint8_t* _hmacKey;\r
+uint8_t _keyLength;\r
+uint8_t _timeZoneOffset;\r
+uint32_t _timeStep;\r
+\r
+// Init the library with the private key, its length and the timeStep duration\r
+void TOTP(uint8_t* hmacKey, uint8_t keyLength, uint32_t timeStep) {\r
+ _hmacKey = hmacKey;\r
+ _keyLength = keyLength;\r
+ _timeStep = timeStep;\r
+}\r
+\r
+void setTimezone(uint8_t timezone){\r
+ _timeZoneOffset = timezone;\r
+}\r
+\r
+uint32_t TimeStruct2Timestamp(struct tm time){\r
+ //time.tm_mon -= 1;\r
+ //time.tm_year -= 1900;\r
+ return mktime(&(time)) - (_timeZoneOffset * 3600) - 2208988800;\r
+}\r
+\r
+// Generate a code, using the timestamp provided\r
+uint32_t getCodeFromTimestamp(uint32_t timeStamp) {\r
+ uint32_t steps = timeStamp / _timeStep;\r
+ return getCodeFromSteps(steps);\r
+}\r
+\r
+// Generate a code, using the timestamp provided\r
+uint32_t getCodeFromTimeStruct(struct tm time) {\r
+ return getCodeFromTimestamp(TimeStruct2Timestamp(time));\r
+}\r
+\r
+// Generate a code, using the number of steps provided\r
+uint32_t getCodeFromSteps(uint32_t steps) {\r
+ // STEP 0, map the number of steps in a 8-bytes array (counter value)\r
+ uint8_t _byteArray[8];\r
+ _byteArray[0] = 0x00;\r
+ _byteArray[1] = 0x00;\r
+ _byteArray[2] = 0x00;\r
+ _byteArray[3] = 0x00;\r
+ _byteArray[4] = (uint8_t)((steps >> 24) & 0xFF);\r
+ _byteArray[5] = (uint8_t)((steps >> 16) & 0xFF);\r
+ _byteArray[6] = (uint8_t)((steps >> 8) & 0XFF);\r
+ _byteArray[7] = (uint8_t)((steps & 0XFF));\r
+\r
+ // STEP 1, get the HMAC-SHA1 hash from counter and key\r
+ initHmac(_hmacKey, _keyLength);\r
+ writeArray(_byteArray, 8);\r
+ uint8_t* _hash = resultHmac();\r
+\r
+ // STEP 2, apply dynamic truncation to obtain a 4-bytes string\r
+ uint32_t _truncatedHash = 0;\r
+ uint8_t _offset = _hash[20 - 1] & 0xF;\r
+ uint8_t j;\r
+ for (j = 0; j < 4; ++j) {\r
+ _truncatedHash <<= 8;\r
+ _truncatedHash |= _hash[_offset + j];\r
+ }\r
+\r
+ // STEP 3, compute the OTP value\r
+ _truncatedHash &= 0x7FFFFFFF; //Disabled\r
+ _truncatedHash %= 1000000;\r
+\r
+ return _truncatedHash;\r
+}\r
--- /dev/null
+#include <inttypes.h>\r
+#include "time.h"\r
+\r
+void TOTP(uint8_t* hmacKey, uint8_t keyLength, uint32_t timeStep);\r
+void setTimezone(uint8_t timezone);\r
+uint32_t getCodeFromTimestamp(uint32_t timeStamp);\r
+uint32_t getCodeFromTimeStruct(struct tm time);\r
+uint32_t getCodeFromSteps(uint32_t steps);\r
--- /dev/null
+#include <msp430.h> \r
+#include <totp.h>\r
+#include <stdint.h>\r
+\r
+/**\r
+ * blink.c\r
+ */\r
+void main(void)\r
+{\r
+ WDTCTL = WDTPW | WDTHOLD; // stop watchdog timer\r
+ P1DIR |= 0x01; // configure P1.0 as output\r
+\r
+ uint8_t hmacKey[] = {0x4d, 0x79, 0x4c, 0x65, 0x67, 0x6f, 0x44, 0x6f, 0x6f, 0x72}; // Secret key\r
+ TOTP(hmacKey, 10, 7200); // Secret key, Key length, Timestep (7200s - 2hours)\r
+\r
+ setTimezone(9); // Set timezone\r
+ uint32_t newCode = getCodeFromTimestamp(1557414000); // Timestamp Now\r
+\r
+ ///////////////// For struct tm //////////////////\r
+ // struct tm datetime;\r
+ // datetime.tm_hour = 9;\r
+ // datetime.tm_min = 0;\r
+ // datetime.tm_sec = 0;\r
+ // datetime.tm_mday = 13;\r
+ // datetime.tm_mon = 5;\r
+ // datetime.tm_year = 2019;\r
+ // uint32_t newCode = getCodeFromTimeStruct(datetime);\r
+ ///////////////////////////////////////////////////\r
+\r
+ volatile unsigned int i; // volatile to prevent optimization\r
+\r
+ while(1)\r
+ {\r
+ if (newCode == 0){ // 0 = INPUT HERE\r
+ P1OUT ^= 0x01; // toggle P1.0\r
+ }\r
+ for(i=10000; i>0; i--); // delay\r
+ }\r
+}\r
--- /dev/null
+#include <string.h>\r
+#include "sha1.h"\r
+\r
+#define SHA1_K0 0x5a827999\r
+#define SHA1_K20 0x6ed9eba1\r
+#define SHA1_K40 0x8f1bbcdc\r
+#define SHA1_K60 0xca62c1d6\r
+\r
+uint8_t sha1InitState[] = {\r
+ 0x01,0x23,0x45,0x67, // H0\r
+ 0x89,0xab,0xcd,0xef, // H1\r
+ 0xfe,0xdc,0xba,0x98, // H2\r
+ 0x76,0x54,0x32,0x10, // H3\r
+ 0xf0,0xe1,0xd2,0xc3 // H4\r
+};\r
+\r
+void init(void) {\r
+ memcpy(state.b,sha1InitState,HASH_LENGTH);\r
+ byteCount = 0;\r
+ bufferOffset = 0;\r
+}\r
+\r
+uint32_t rol32(uint32_t number, uint8_t bits) {\r
+ return ((number << bits) | (uint32_t)(number >> (32-bits)));\r
+}\r
+\r
+void hashBlock() {\r
+ uint8_t i;\r
+ uint32_t a,b,c,d,e,t;\r
+\r
+ a=state.w[0];\r
+ b=state.w[1];\r
+ c=state.w[2];\r
+ d=state.w[3];\r
+ e=state.w[4];\r
+ for (i=0; i<80; i++) {\r
+ if (i>=16) {\r
+ t = buffer.w[(i+13)&15] ^ buffer.w[(i+8)&15] ^ buffer.w[(i+2)&15] ^ buffer.w[i&15];\r
+ buffer.w[i&15] = rol32(t,1);\r
+ }\r
+ if (i<20) {\r
+ t = (d ^ (b & (c ^ d))) + SHA1_K0;\r
+ } else if (i<40) {\r
+ t = (b ^ c ^ d) + SHA1_K20;\r
+ } else if (i<60) {\r
+ t = ((b & c) | (d & (b | c))) + SHA1_K40;\r
+ } else {\r
+ t = (b ^ c ^ d) + SHA1_K60;\r
+ }\r
+ t+=rol32(a,5) + e + buffer.w[i&15];\r
+ e=d;\r
+ d=c;\r
+ c=rol32(b,30);\r
+ b=a;\r
+ a=t;\r
+ }\r
+ state.w[0] += a;\r
+ state.w[1] += b;\r
+ state.w[2] += c;\r
+ state.w[3] += d;\r
+ state.w[4] += e;\r
+}\r
+\r
+void addUncounted(uint8_t data) {\r
+ buffer.b[bufferOffset ^ 3] = data;\r
+ bufferOffset++;\r
+ if (bufferOffset == BLOCK_LENGTH) {\r
+ hashBlock();\r
+ bufferOffset = 0;\r
+ }\r
+}\r
+\r
+void write(uint8_t data) {\r
+ ++byteCount;\r
+ addUncounted(data);\r
+\r
+ return;\r
+}\r
+\r
+void writeArray(uint8_t *buffer, uint8_t size){\r
+ while (size--) {\r
+ write(*buffer++);\r
+ }\r
+}\r
+\r
+void pad() {\r
+ // Implement SHA-1 padding (fips180-2 \81\985.1.1)\r
+\r
+ // Pad with 0x80 followed by 0x00 until the end of the block\r
+ addUncounted(0x80);\r
+ while (bufferOffset != 56) addUncounted(0x00);\r
+\r
+ // Append length in the last 8 bytes\r
+ addUncounted(0); // We're only using 32 bit lengths\r
+ addUncounted(0); // But SHA-1 supports 64 bit lengths\r
+ addUncounted(0); // So zero pad the top bits\r
+ addUncounted(byteCount >> 29); // Shifting to multiply by 8\r
+ addUncounted(byteCount >> 21); // as SHA-1 supports bitstreams as well as\r
+ addUncounted(byteCount >> 13); // byte.\r
+ addUncounted(byteCount >> 5);\r
+ addUncounted(byteCount << 3);\r
+}\r
+\r
+uint8_t* result(void) {\r
+ // Pad to complete the last block\r
+ pad();\r
+\r
+ // Swap byte order back\r
+ uint8_t i;\r
+ for (i=0; i<5; i++) {\r
+ uint32_t a,b;\r
+ a=state.w[i];\r
+ b=a<<24;\r
+ b|=(a<<8) & 0x00ff0000;\r
+ b|=(a>>8) & 0x0000ff00;\r
+ b|=a>>24;\r
+ state.w[i]=b;\r
+ }\r
+\r
+ // Return pointer to hash (20 characters)\r
+ return state.b;\r
+}\r
+\r
+#define HMAC_IPAD 0x36\r
+#define HMAC_OPAD 0x5c\r
+\r
+void initHmac(const uint8_t* key, uint8_t keyLength) {\r
+ uint8_t i;\r
+ memset(keyBuffer,0,BLOCK_LENGTH);\r
+ if (keyLength > BLOCK_LENGTH) {\r
+ // Hash long keys\r
+ init();\r
+ for (;keyLength--;) write(*key++);\r
+ memcpy(keyBuffer,result(),HASH_LENGTH);\r
+ } else {\r
+ // Block length keys are used as is\r
+ memcpy(keyBuffer,key,keyLength);\r
+ }\r
+ // Start inner hash\r
+ init();\r
+ for (i=0; i<BLOCK_LENGTH; i++) {\r
+ write(keyBuffer[i] ^ HMAC_IPAD);\r
+ }\r
+}\r
+\r
+uint8_t* resultHmac(void) {\r
+ uint8_t i;\r
+ // Complete inner hash\r
+ memcpy(innerHash,result(),HASH_LENGTH);\r
+ // Calculate outer hash\r
+ init();\r
+ for (i=0; i<BLOCK_LENGTH; i++) write(keyBuffer[i] ^ HMAC_OPAD);\r
+ for (i=0; i<HASH_LENGTH; i++) write(innerHash[i]);\r
+ return result();\r
+}\r
--- /dev/null
+#include <inttypes.h>\r
+\r
+#define HASH_LENGTH 20\r
+#define BLOCK_LENGTH 64\r
+\r
+union _buffer {\r
+ uint8_t b[BLOCK_LENGTH];\r
+ uint32_t w[BLOCK_LENGTH/4];\r
+} buffer;\r
+union _state {\r
+ uint8_t b[HASH_LENGTH];\r
+ uint32_t w[HASH_LENGTH/4];\r
+} state;\r
+\r
+uint8_t bufferOffset;\r
+uint32_t byteCount;\r
+uint8_t keyBuffer[BLOCK_LENGTH];\r
+uint8_t innerHash[HASH_LENGTH];\r
+\r
+void init(void);\r
+void initHmac(const uint8_t* secret, uint8_t secretLength);\r
+uint8_t* result(void);\r
+uint8_t* resultHmac(void);\r
+void write(uint8_t);\r
+void writeArray(uint8_t *buffer, uint8_t size);\r