From b7ed9adb6c41089da75d258d7148fa87d2f76d20 Mon Sep 17 00:00:00 2001 From: Wesley Ellis Date: Mon, 22 Nov 2021 21:20:57 -0500 Subject: Add initial TOTP watch face impl Vendor code from https://github.com/Netthaw/TOTP-MCU to do the heavy lifting of computing SHA-1 and HMAC and the rest of TOTP Also implement a date_time to unix timestamp method --- movement/lib/TOTP-MCU | 1 + movement/make/Makefile | 4 + movement/movement_config.h | 1 + movement/watch_faces/complications/totp_face.c | 137 +++++++++++++++++++++++++ movement/watch_faces/complications/totp_face.h | 19 ++++ 5 files changed, 162 insertions(+) create mode 160000 movement/lib/TOTP-MCU create mode 100644 movement/watch_faces/complications/totp_face.c create mode 100644 movement/watch_faces/complications/totp_face.h diff --git a/movement/lib/TOTP-MCU b/movement/lib/TOTP-MCU new file mode 160000 index 00000000..646474a8 --- /dev/null +++ b/movement/lib/TOTP-MCU @@ -0,0 +1 @@ +Subproject commit 646474a8757e1fca490792e81082b2ad89b966a3 diff --git a/movement/make/Makefile b/movement/make/Makefile index b595d2dd..4c895322 100755 --- a/movement/make/Makefile +++ b/movement/make/Makefile @@ -16,6 +16,7 @@ INCLUDES += \ -I../watch_faces/complications/ \ -I../watch_faces/thermistor/ \ -I../watch_faces/demos/ \ + -I../lib/TOTP-MCU/ \ # If you add any other source files you wish to compile, add them after ../app.c # Note that you will need to add a backslash at the end of any line you wish to continue, i.e. @@ -24,6 +25,8 @@ INCLUDES += \ # ../drivers/lis2dh.c \ # ../watch_faces/fitness/step_count_face.c SRCS += \ + ../lib/TOTP-MCU/sha1.c \ + ../lib/TOTP-MCU/TOTP.c \ ../movement.c \ ../watch_faces/clock/simple_clock_face.c \ ../watch_faces/settings/preferences_face.c \ @@ -37,6 +40,7 @@ SRCS += \ ../watch_faces/complications/beats_face.c \ ../watch_faces/complications/day_one_face.c \ ../watch_faces/complications/stopwatch_face.c \ + ../watch_faces/complications/totp_face.c \ # Leave this line at the bottom of the file; it has all the targets for making your project. include $(TOP)/rules.mk diff --git a/movement/movement_config.h b/movement/movement_config.h index fa5ddf18..8802883d 100644 --- a/movement/movement_config.h +++ b/movement/movement_config.h @@ -12,6 +12,7 @@ #include "day_one_face.h" #include "voltage_face.h" #include "stopwatch_face.h" +#include "totp_face.h" const watch_face_t watch_faces[] = { simple_clock_face, diff --git a/movement/watch_faces/complications/totp_face.c b/movement/watch_faces/complications/totp_face.c new file mode 100644 index 00000000..2e8b5f66 --- /dev/null +++ b/movement/watch_faces/complications/totp_face.c @@ -0,0 +1,137 @@ +/** + * + * TODO: + * - this ONLY works if watch is set to UTC, probably worth including a TZ offset setting since it can be used by beats as well + * - show how long code is valid for in upper right corner of LCD + * - optimize code so that we don't calculating a new unix timestamp every second AND a new TOTP code + * - Support for multiple codes + */ +#include +#include +#include "totp_face.h" +#include "watch.h" +#include "TOTP.h" + +// test key: JBSWY3DPEHPK3PXP +// Use https://cryptii.com/pipes/base32-to-hex to convert base32 to hex +// Use https://totp.danhersam.com/ to generate test codes for verification +uint8_t hmacKey[] = {0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x21, 0xde, 0xad, 0xbe, 0xef}; // Secret key + +void totp_face_setup(movement_settings_t *settings, void ** context_ptr) { + (void) settings; + (void) context_ptr; + TOTP(hmacKey, sizeof(hmacKey), 30); +} + +void totp_face_activate(movement_settings_t *settings, void *context) { + (void) settings; + (void) context; +} + +/** + * @brief Get unix timestamp from component parts + * + * @param year + * @param month + * @param day + * @param hour + * @param minute + * @param second + * @return uint32_t + * + * Based on code by Josh Haberman for upb + * from https://blog.reverberate.org/2020/05/12/optimizing-date-algorithms.html + * + * Essentially we need to calculate how many days have occured since year 0 + * including leap years! The following code does some clever calculations + * of the number of february's then offsets based on how many leap years + * there have been + * + * Once we have the number of days in the year, it's easy enough to add how + * many days have happened in the current year, then convert that to seconds + */ +uint32_t current_unix_time(uint32_t year, uint32_t month, uint32_t day, + uint32_t hour, uint32_t minute, uint32_t second) { + uint16_t DAYS_SO_FAR[] = { + 0, // Jan + 31, // Feb + 59, // March + 90, // April + 120, // May + 151, // June + 181, // July + 212, // August + 243, // September + 273, // October + 304, // November + 334 // December + }; + + + uint32_t year_adj = year + 4800; + uint32_t febs = year_adj - (month <= 2 ? 1 : 0); /* Februaries since base. */ + uint32_t leap_days = 1 + (febs / 4) - (febs / 100) + (febs / 400); + uint32_t days = 365 * year_adj + leap_days + DAYS_SO_FAR[month - 1] + day - 1; + days -= 2472692; /* Adjust to Unix epoch. */ + + uint32_t timestamp = days * 86400; + timestamp += hour * 3600; + timestamp += minute * 60; + timestamp += second; + + return timestamp; +} + +uint32_t current_unix_time_from_rtc() { + watch_date_time date_time = watch_rtc_get_date_time(); + return current_unix_time( + date_time.unit.year + 2020, // year is stored starting in 2020 + date_time.unit.month, + date_time.unit.day, + date_time.unit.hour, + date_time.unit.minute, + date_time.unit.second + ); +} + +bool totp_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { + (void) settings; + (void) context; + + char buf[14]; + watch_date_time date_time = watch_rtc_get_date_time(); + + uint32_t ts; + uint32_t code; + switch (event.event_type) { + case EVENT_ACTIVATE: + case EVENT_TICK: + ts = current_unix_time_from_rtc(); + code = getCodeFromTimestamp(ts); + sprintf(buf, "2f %lu", code); + + watch_display_string(buf, 0); + break; + case EVENT_MODE_BUTTON_UP: + movement_move_to_next_face(); + break; + case EVENT_LIGHT_BUTTON_DOWN: + movement_illuminate_led(); + break; + case EVENT_TIMEOUT: + // go home + break; + case EVENT_ALARM_BUTTON_DOWN: + case EVENT_ALARM_BUTTON_UP: + case EVENT_ALARM_LONG_PRESS: + default: + break; + } + + return true; +} + +void totp_face_resign(movement_settings_t *settings, void *context) { + (void) settings; + (void) context; +} \ No newline at end of file diff --git a/movement/watch_faces/complications/totp_face.h b/movement/watch_faces/complications/totp_face.h new file mode 100644 index 00000000..1fecb82a --- /dev/null +++ b/movement/watch_faces/complications/totp_face.h @@ -0,0 +1,19 @@ +#ifndef TOTP_FACE_H_ +#define TOTP_FACE_H_ + +#include "movement.h" + +void totp_face_setup(movement_settings_t *settings, void ** context_ptr); +void totp_face_activate(movement_settings_t *settings, void *context); +bool totp_face_loop(movement_event_t event, movement_settings_t *settings, void *context); +void totp_face_resign(movement_settings_t *settings, void *context); + +static const watch_face_t totp_face = { + totp_face_setup, + totp_face_activate, + totp_face_loop, + totp_face_resign, + NULL +}; + +#endif // TOTP_FACE_H_ \ No newline at end of file -- cgit v1.2.3