summaryrefslogtreecommitdiffstats
path: root/movement/watch_faces/complications/totp_face.c
blob: 2e8b5f668d9d692d3ca42db6e095b78d16b9a38a (plain)
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
/**
 * 
 * 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;
}