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
This commit is contained in:
parent
7817e6696e
commit
b7ed9adb6c
1
movement/lib/TOTP-MCU
Submodule
1
movement/lib/TOTP-MCU
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 646474a8757e1fca490792e81082b2ad89b966a3
|
@ -16,6 +16,7 @@ INCLUDES += \
|
|||||||
-I../watch_faces/complications/ \
|
-I../watch_faces/complications/ \
|
||||||
-I../watch_faces/thermistor/ \
|
-I../watch_faces/thermistor/ \
|
||||||
-I../watch_faces/demos/ \
|
-I../watch_faces/demos/ \
|
||||||
|
-I../lib/TOTP-MCU/ \
|
||||||
|
|
||||||
# If you add any other source files you wish to compile, add them after ../app.c
|
# 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.
|
# 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 \
|
# ../drivers/lis2dh.c \
|
||||||
# ../watch_faces/fitness/step_count_face.c
|
# ../watch_faces/fitness/step_count_face.c
|
||||||
SRCS += \
|
SRCS += \
|
||||||
|
../lib/TOTP-MCU/sha1.c \
|
||||||
|
../lib/TOTP-MCU/TOTP.c \
|
||||||
../movement.c \
|
../movement.c \
|
||||||
../watch_faces/clock/simple_clock_face.c \
|
../watch_faces/clock/simple_clock_face.c \
|
||||||
../watch_faces/settings/preferences_face.c \
|
../watch_faces/settings/preferences_face.c \
|
||||||
@ -37,6 +40,7 @@ SRCS += \
|
|||||||
../watch_faces/complications/beats_face.c \
|
../watch_faces/complications/beats_face.c \
|
||||||
../watch_faces/complications/day_one_face.c \
|
../watch_faces/complications/day_one_face.c \
|
||||||
../watch_faces/complications/stopwatch_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.
|
# Leave this line at the bottom of the file; it has all the targets for making your project.
|
||||||
include $(TOP)/rules.mk
|
include $(TOP)/rules.mk
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include "day_one_face.h"
|
#include "day_one_face.h"
|
||||||
#include "voltage_face.h"
|
#include "voltage_face.h"
|
||||||
#include "stopwatch_face.h"
|
#include "stopwatch_face.h"
|
||||||
|
#include "totp_face.h"
|
||||||
|
|
||||||
const watch_face_t watch_faces[] = {
|
const watch_face_t watch_faces[] = {
|
||||||
simple_clock_face,
|
simple_clock_face,
|
||||||
|
137
movement/watch_faces/complications/totp_face.c
Normal file
137
movement/watch_faces/complications/totp_face.c
Normal file
@ -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 <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#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;
|
||||||
|
}
|
19
movement/watch_faces/complications/totp_face.h
Normal file
19
movement/watch_faces/complications/totp_face.h
Normal file
@ -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_
|
Loading…
x
Reference in New Issue
Block a user