diff --git a/.gitignore b/.gitignore index 560ecae..8569585 100644 --- a/.gitignore +++ b/.gitignore @@ -8,9 +8,15 @@ build .lock-waf* +# Ignore xcode project +Pebble Authenticator + +# Ignore vi backups +*~ + # Ignore linked files/directories waf -wscript +resources/wscript tools include lib diff --git a/README.md b/README.md index a22b9b7..22b0101 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ Authenticator ============= -Forked off 'authenticator' by IEF, which was forked off 'twostep' by pokey9000, this is Authenticator for Pebble, with patches from rigel314 +Forked off 'authenticator' by IEF, which was forked off 'twostep' by pokey9000, this is Authenticator for Pebble, with patches from rigel314, updated to PebbleSDK-2.0 by Didier Arenzana. generating multiple Time-based One-Time Passwords, much like Google Authenticator. @@ -21,11 +21,11 @@ label:secret 5. repeat this for all your keys (don't forget to remove the example) -6. Generate the config by running ./configuration.py +6. build the application by running pebble build -7. Build and install the application with ./waf build && python httpserver as usual +7. install by running pebble install 8. Done, you can find 'Authenticator' in your app menu for your Pebble. -The above is assuming you have the Pebble SDK installed and configured to compile watch apps. -If not, review: http://developer.getpebble.com/1/01_GetStarted/01_Step_2 +The above is assuming you have the Pebble SDK 2.0 installed and configured to compile watch apps. +If not, review: https://developer.getpebble.com/2/ diff --git a/appinfo.json b/appinfo.json new file mode 100644 index 0000000..95fd2b9 --- /dev/null +++ b/appinfo.json @@ -0,0 +1,22 @@ +{ + "uuid": "5b59ec4a-8a6b-4ff0-bd80-0038a151cd86", + "shortName": "Authenticator", + "longName": "Authenticator", + "companyName": "pokey9000/IEF/rigel314", + "versionCode": 1, + "versionLabel": "1.1.0", + "watchapp": { + "watchface": false + }, + "appKeys": {}, + "resources": { + "media": [ + { + "menuIcon": true, + "type": "png", + "name": "IMAGE_MENU_ICON", + "file": "images/twostep_icon.png" + } + ] + } +} diff --git a/resources/src/images/twostep_icon.png b/resources/images/twostep_icon.png similarity index 100% rename from resources/src/images/twostep_icon.png rename to resources/images/twostep_icon.png diff --git a/resources/src/resource_map.json b/resources/src/resource_map.json deleted file mode 100644 index 0da7e90..0000000 --- a/resources/src/resource_map.json +++ /dev/null @@ -1,10 +0,0 @@ - -{"friendlyVersion": "VERSION", - "versionDefName": "VERSION", - "media": [ - { "type": "png", - "defName": "IMAGE_MENU_ICON", - "file": "images/twostep_icon.png" - } - ] -} diff --git a/src/authenticator.c b/src/authenticator.c index ed912db..3105119 100644 --- a/src/authenticator.c +++ b/src/authenticator.c @@ -1,36 +1,35 @@ -#include "pebble_os.h" -#include "pebble_app.h" -#include "pebble_fonts.h" +#include #include "configuration.h" // defined in editTzone.c extern void showEditTimeZone(); +extern void destroyEditTimeZone(); // Truncate n decimal digits to 2^n for 6 digits #define DIGITS_TRUNCATE 1000000 #define SHA1_SIZE 20 -#define MY_UUID { 0xA4, 0xA6, 0x13, 0xB5, 0x8A, 0x6B, 0x4F, 0xF0, 0xBD, 0x80, 0x00, 0x38, 0xA1, 0x51, 0xCD, 0x86 } -PBL_APP_INFO(MY_UUID, - "Authenticator", "pokey9000/IEF/rigel314", - 1, 1, /* App version */ - RESOURCE_ID_IMAGE_MENU_ICON, - APP_INFO_STANDARD_APP); +Window *window; -Window window; +TextLayer *label; +TextLayer *token; +TextLayer *ticker; -TextLayer label; -TextLayer token; -TextLayer ticker; -int curToken = 0; +#define KEY_CURTOKEN 1 +int curToken; +int curToken_orig; + +#define KEY_TZONE 2 int tZone; +int tZone_orig; //used to track config changes + bool changed; /* from sha1.c from liboauth */ -/* This code is public-domain - it is based on libcrypt +/* This code is public-domain - it is based on libcrypt * placed in the public domain by Wei Dai and other contributors. */ @@ -63,13 +62,13 @@ typedef struct sha1nfo { /* public API - prototypes - TODO: doxygen*/ /* -void sha1_init(sha1nfo *s); -void sha1_writebyte(sha1nfo *s, uint8_t data); -void sha1_write(sha1nfo *s, const char *data, size_t len); -uint8_t* sha1_result(sha1nfo *s); -void sha1_initHmac(sha1nfo *s, const uint8_t* key, int keyLength); -uint8_t* sha1_resultHmac(sha1nfo *s); -*/ + void sha1_init(sha1nfo *s); + void sha1_writebyte(sha1nfo *s, uint8_t data); + void sha1_write(sha1nfo *s, const char *data, size_t len); + uint8_t* sha1_result(sha1nfo *s); + void sha1_initHmac(sha1nfo *s, const uint8_t* key, int keyLength); + uint8_t* sha1_resultHmac(sha1nfo *s); + */ char* itoa(int val, int base){ static char buf[32] = {0}; @@ -106,7 +105,7 @@ uint32_t sha1_rol32(uint32_t number, uint8_t bits) { void sha1_hashBlock(sha1nfo *s) { uint8_t i; uint32_t a,b,c,d,e,t; - + a=s->state.w[0]; b=s->state.w[1]; c=s->state.w[2]; @@ -160,11 +159,11 @@ void sha1_write(sha1nfo *s, const char *data, size_t len) { void sha1_pad(sha1nfo *s) { // Implement SHA-1 padding (fips180-2 ยง5.1.1) - + // Pad with 0x80 followed by 0x00 until the end of the block sha1_addUncounted(s, 0x80); while (s->bufferOffset != 56) sha1_addUncounted(s, 0x00); - + // Append length in the last 8 bytes sha1_addUncounted(s, 0); // We're only using 32 bit lengths sha1_addUncounted(s, 0); // But SHA-1 supports 64 bit lengths @@ -180,7 +179,7 @@ uint8_t* sha1_result(sha1nfo *s) { int i; // Pad to complete the last block sha1_pad(s); - + // Swap byte order back for (i=0; i<5; i++) { uint32_t a,b; @@ -191,7 +190,7 @@ uint8_t* sha1_result(sha1nfo *s) { b|=a>>24; s->state.w[i]=b; } - + // Return pointer to hash (20 characters) return s->state.b; } @@ -229,39 +228,11 @@ uint8_t* sha1_resultHmac(sha1nfo *s) { return sha1_result(s); } - /* end sha1.c */ -// return seconds since epoch compensating for Pebble's lack of location -// independent GMT - -int curSeconds=0; - -uint32_t get_epoch_seconds() { - PblTm current_time; - uint32_t unix_time; - get_time(¤t_time); - -// shamelessly stolen from WhyIsThisOpen's Unix Time source: http://forums.getpebble.com/discussion/4324/watch-face-unix-time - /* Convert time to seconds since epoch. */ - //curSeconds=current_time.tm_sec; - unix_time = ((0-tZone)*3600) + /* time zone offset */ /* 0-tZone+current_time.tm_isdst if it ever starts working. */ - + current_time.tm_sec /* start with seconds */ - + current_time.tm_min*60 /* add minutes */ - + current_time.tm_hour*3600 /* add hours */ - + current_time.tm_yday*86400 /* add days */ - + (current_time.tm_year-70)*31536000 /* add years since 1970 */ - + ((current_time.tm_year-69)/4)*86400 /* add a day after leap years, starting in 1973 */ - ((current_time.tm_year-1)/100)*86400 /* remove a leap day every 100 years, starting in 2001 */ + ((current_time.tm_year+299)/400)*86400; /* add a leap day back every 400 years, starting in 2001*/ - unix_time /= 30; - return unix_time; -} - - -void handle_second_tick(AppContextRef ctx, PebbleTickEvent *t) { - - (void)t; - (void)ctx; +void handle_second_tick(struct tm *tick_time, TimeUnits units_changed) { + (void) units_changed; static char tokenText[] = "RYRYRY"; // Needs to be static because it's used by the system later. sha1nfo s; @@ -269,11 +240,17 @@ void handle_second_tick(AppContextRef ctx, PebbleTickEvent *t) { uint32_t otp; int i; uint32_t unix_time; + time_t current_time ; char sha1_time[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + int curSeconds; - PblTm curTime; - get_time(&curTime); - curSeconds = curTime.tm_sec; + current_time=time(NULL); + unix_time=current_time + ((0-tZone)*3600) ; //still needed because time() is not GMT + unix_time /= 30; + if (tick_time == NULL) { + tick_time = localtime(¤t_time); + } + curSeconds = tick_time->tm_sec; if(curSeconds == 0 || curSeconds == 30 || changed) { @@ -282,7 +259,6 @@ void handle_second_tick(AppContextRef ctx, PebbleTickEvent *t) { // TOTP uses seconds since epoch in the upper half of an 8 byte payload // TOTP is HOTP with a time based payload // HOTP is HMAC with a truncation function to get a short decimal key - unix_time = get_epoch_seconds(); sha1_time[4] = (unix_time >> 24) & 0xFF; sha1_time[5] = (unix_time >> 16) & 0xFF; sha1_time[6] = (unix_time >> 8) & 0xFF; @@ -313,102 +289,127 @@ void handle_second_tick(AppContextRef ctx, PebbleTickEvent *t) { char *labelText = otplabels[curToken]; - text_layer_set_text(&label, labelText); - text_layer_set_text(&token, tokenText); + text_layer_set_text(label, labelText); + text_layer_set_text(token, tokenText); } if ((curSeconds>=0) && (curSeconds<30)) { - text_layer_set_text(&ticker, itoa((30-curSeconds),10)); + text_layer_set_text(ticker, itoa((30-curSeconds),10)); } else { - text_layer_set_text(&ticker, itoa((60-curSeconds),10)); + text_layer_set_text(ticker, itoa((60-curSeconds),10)); } } -void up_single_click_handler(ClickRecognizerRef recognizer, Window *window) { +void up_single_click_handler(ClickRecognizerRef recognizer, void *context) { + (void)recognizer ; + (void)context ; + if (curToken==0) { curToken=NUM_SECRETS-1; } else { curToken--; }; changed = true; - handle_second_tick(NULL,NULL); + handle_second_tick(NULL,SECOND_UNIT); } -void down_single_click_handler(ClickRecognizerRef recognizer, Window *window) { - (void)recognizer; - (void)window; - if ((curToken+1)==NUM_SECRETS) { - curToken=0; - } else { - curToken++; - }; +void down_single_click_handler(ClickRecognizerRef recognizer, void *context) { + (void)recognizer; + (void)context; + + curToken = (curToken + 1 ) % NUM_SECRETS ; changed = true; - handle_second_tick(NULL,NULL); + handle_second_tick(NULL,SECOND_UNIT); } -void select_single_click_handler(ClickRecognizerRef recognizer, Window *window) { +void select_single_click_handler(ClickRecognizerRef recognizer, void *context) { (void)recognizer; - (void)window; + (void)context; showEditTimeZone(); } -void click_config_provider(ClickConfig **config, Window *window) { - (void)window; - - config[BUTTON_ID_UP]->click.handler = (ClickHandler) up_single_click_handler; - config[BUTTON_ID_UP]->click.repeat_interval_ms = 100; - - config[BUTTON_ID_DOWN]->click.handler = (ClickHandler) down_single_click_handler; - config[BUTTON_ID_DOWN]->click.repeat_interval_ms = 100; +void click_config_provider(void *context) { - config[BUTTON_ID_SELECT]->click.handler = (ClickHandler) select_single_click_handler; + // window_set_click_context(BUTTON_ID_UP, context); + window_single_repeating_click_subscribe(BUTTON_ID_UP, 100, up_single_click_handler); + window_single_repeating_click_subscribe(BUTTON_ID_DOWN, 100, down_single_click_handler); + window_single_click_subscribe(BUTTON_ID_SELECT, select_single_click_handler); } -void handle_init(AppContextRef ctx) { - (void)ctx; - tZone = DEFAULT_TIME_ZONE; +void handle_init(void) { + + // get timezone from persistent storage, if found + tZone = persist_exists(KEY_TZONE) ? persist_read_int(KEY_TZONE) : DEFAULT_TIME_ZONE; + tZone_orig=tZone ; + + // get saved current token + curToken = persist_exists(KEY_CURTOKEN) ? persist_read_int(KEY_CURTOKEN) : 0 ; + curToken_orig = curToken; + changed = true; - window_init(&window, "auth"); - window_stack_push(&window, true /* Animated */); - window_set_background_color(&window, GColorBlack); + window = window_create(); + window_stack_push(window, true /* Animated */); + window_set_background_color(window, GColorBlack); // Init the identifier label - text_layer_init(&label, GRect(5, 30, 144-4, 168-44)); - text_layer_set_text_color(&label, GColorWhite); - text_layer_set_background_color(&label, GColorClear); - text_layer_set_font(&label, fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD)); + label=text_layer_create(GRect(5, 30, 144-4, 168-44)); + + text_layer_set_text_color(label, GColorWhite); + text_layer_set_background_color(label, GColorClear); + text_layer_set_font(label, fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD)); + // Init the token label - text_layer_init(&token, GRect(10, 60, 144-4 /* width */, 168-44 /* height */)); - text_layer_set_text_color(&token, GColorWhite); - text_layer_set_background_color(&token, GColorClear); - text_layer_set_font(&token, fonts_get_system_font(FONT_KEY_BITHAM_34_MEDIUM_NUMBERS)); + token=text_layer_create(GRect(10, 60, 144-4 /* width */, 168-44 /* height */)); + text_layer_set_text_color(token, GColorWhite); + text_layer_set_background_color(token, GColorClear); + text_layer_set_font(token, fonts_get_system_font(FONT_KEY_BITHAM_34_MEDIUM_NUMBERS)); // Init the second ticker - text_layer_init(&ticker, GRect(60, 120, 144-4 /* width */, 168-44 /* height */)); - text_layer_set_text_color(&ticker, GColorWhite); - text_layer_set_background_color(&ticker, GColorClear); - text_layer_set_font(&ticker, fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD)); + ticker=text_layer_create(GRect(60, 120, 144-4 /* width */, 168-44 /* height */)); + text_layer_set_text_color(ticker, GColorWhite); + text_layer_set_background_color(ticker, GColorClear); + text_layer_set_font(ticker, fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD)); + + handle_second_tick(NULL, SECOND_UNIT); - handle_second_tick(ctx, NULL); - layer_add_child(&window.layer, &label.layer); - layer_add_child(&window.layer, &token.layer); - layer_add_child(&window.layer, &ticker.layer); + Layer *window_layer=window_get_root_layer(window); + layer_add_child(window_layer, text_layer_get_layer(label)); + layer_add_child(window_layer, text_layer_get_layer(token)); + layer_add_child(window_layer, text_layer_get_layer(ticker)); - window_set_click_config_provider(&window, (ClickConfigProvider) click_config_provider); + window_set_click_config_provider(window, (ClickConfigProvider) click_config_provider); } +void handle_deinit(void) { + APP_LOG(APP_LOG_LEVEL_DEBUG, "deinit called"); + + //store timezone in persistence storage if needed + if (tZone != tZone_orig) { + if (tZone == DEFAULT_TIME_ZONE) persist_delete(KEY_TZONE) ; + else persist_write_int(KEY_TZONE, tZone) ; + } + + //save selected token + if (curToken != curToken_orig) { + if (curToken == 0) persist_delete(KEY_CURTOKEN); + else persist_write_int(KEY_CURTOKEN, curToken); + } + + destroyEditTimeZone(); + tick_timer_service_unsubscribe(); + text_layer_destroy(label) ; + text_layer_destroy(token) ; + text_layer_destroy(ticker) ; + window_destroy(window); +} -void pbl_main(void *params) { - PebbleAppHandlers handlers = { - .init_handler = &handle_init, - .tick_info = { - .tick_handler = &handle_second_tick, - .tick_units = SECOND_UNIT - } - }; - app_event_loop(params, &handlers); +int main() { + handle_init(); + tick_timer_service_subscribe(SECOND_UNIT, handle_second_tick); + app_event_loop() ; + handle_deinit() ; } diff --git a/src/editTzone.c b/src/editTzone.c index 6d6ccf7..380483d 100644 --- a/src/editTzone.c +++ b/src/editTzone.c @@ -1,14 +1,11 @@ -#include "pebble_os.h" -#include "pebble_app.h" -#include "pebble_fonts.h" +#include extern int tZone; extern bool changed; -Window setZoneW; -TextLayer setZoneW_zone; -TextLayer setZoneW_label; -TextLayer setZoneW_disclaim; +Window *setZoneW=NULL; +TextLayer *setZoneW_zone; +TextLayer *setZoneW_label; char gmt[7]; @@ -29,72 +26,83 @@ char* itoa2(int valIN, int base){ // 2 in the morning hack } -void zone_up(ClickRecognizerRef recognizer, Window *window) { +void zone_up(ClickRecognizerRef recognizer, void *context) { (void)recognizer; - (void)window; + (void)context; if(tZone<24) tZone++; strcpy(gmt+3, itoa2(tZone,10)); - text_layer_set_text(&setZoneW_zone, gmt); + text_layer_set_text(setZoneW_zone, gmt); changed = true; } -void zone_down(ClickRecognizerRef recognizer, Window *window) { +void zone_down(ClickRecognizerRef recognizer, void *context) { (void)recognizer; - (void)window; + (void)context; if(tZone>-24) tZone--; strcpy(gmt+3, itoa2(tZone,10)); - text_layer_set_text(&setZoneW_zone, gmt); + text_layer_set_text(setZoneW_zone, gmt); changed = true; } -void zone_click_config_provider(ClickConfig **config, Window *window) { - (void)window; +void zone_click_config_provider(void *context) { - config[BUTTON_ID_UP]->click.handler = (ClickHandler) zone_up; - config[BUTTON_ID_UP]->click.repeat_interval_ms = 100; - - config[BUTTON_ID_DOWN]->click.handler = (ClickHandler) zone_down; - config[BUTTON_ID_DOWN]->click.repeat_interval_ms = 100; + window_single_repeating_click_subscribe(BUTTON_ID_UP, 100, zone_up); + window_single_repeating_click_subscribe(BUTTON_ID_DOWN, 100, zone_down); + } -void showEditTimeZone() -{ - window_init(&setZoneW, "Set Time Zone"); - window_set_background_color(&setZoneW, GColorBlack); - +void create_setZoneW() { + setZoneW=window_create(); + Layer *setZoneW_layer=window_get_root_layer(setZoneW); + window_set_background_color(setZoneW, GColorBlack); + strcpy(gmt, "UTC"); strcpy(gmt+3, itoa2(tZone,10)); - - text_layer_init(&setZoneW_zone, GRect(0,50,144,48)); - text_layer_set_text(&setZoneW_zone, gmt); - text_layer_set_font(&setZoneW_zone, fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD)); - text_layer_set_text_alignment(&setZoneW_zone, GTextAlignmentCenter); - setZoneW_zone.text_color = GColorWhite; - setZoneW_zone.background_color = GColorBlack; - layer_add_child(&setZoneW.layer, &setZoneW_zone.layer); + + setZoneW_zone=text_layer_create(GRect(0,50,144,48)); - text_layer_init(&setZoneW_label, GRect(0,5,144,48)); - text_layer_set_text(&setZoneW_label, "Change Time Zone"); - text_layer_set_font(&setZoneW_label, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); - text_layer_set_text_alignment(&setZoneW_label, GTextAlignmentCenter); - setZoneW_label.text_color = GColorWhite; - setZoneW_label.background_color = GColorBlack; - layer_add_child(&setZoneW.layer, &setZoneW_label.layer); - - text_layer_init(&setZoneW_disclaim, GRect(0,168-31,144,30)); - text_layer_set_text(&setZoneW_disclaim, "Not Persistant"); - text_layer_set_font(&setZoneW_disclaim, fonts_get_system_font(FONT_KEY_GOTHIC_14_BOLD)); - text_layer_set_text_alignment(&setZoneW_disclaim, GTextAlignmentCenter); - setZoneW_disclaim.text_color = GColorWhite; - setZoneW_disclaim.background_color = GColorBlack; - layer_add_child(&setZoneW.layer, &setZoneW_disclaim.layer); - - window_set_click_config_provider(&setZoneW, (ClickConfigProvider) zone_click_config_provider); - window_stack_push(&setZoneW, true); + text_layer_set_text( setZoneW_zone, gmt); + text_layer_set_font( setZoneW_zone, + fonts_get_system_font(FONT_KEY_GOTHIC_28_BOLD)); + text_layer_set_text_alignment( setZoneW_zone, GTextAlignmentCenter); + text_layer_set_text_color( setZoneW_zone, GColorWhite); + text_layer_set_background_color(setZoneW_zone, GColorBlack); + + layer_add_child(setZoneW_layer, text_layer_get_layer(setZoneW_zone)); + + setZoneW_label=text_layer_create(GRect(0,5,144,48)); + + text_layer_set_text( setZoneW_label, "Change Time Zone"); + text_layer_set_font( setZoneW_label, + fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); + text_layer_set_text_alignment( setZoneW_label, GTextAlignmentCenter); + text_layer_set_text_color( setZoneW_label, GColorWhite); + text_layer_set_background_color(setZoneW_label, GColorBlack); + + layer_add_child(setZoneW_layer, text_layer_get_layer(setZoneW_label)); + + window_set_click_config_provider(setZoneW, (ClickConfigProvider) zone_click_config_provider); +} + +void destroyEditTimeZone() { + if (setZoneW == NULL) return ; + + text_layer_destroy(setZoneW_label); + text_layer_destroy(setZoneW_zone); + window_destroy(setZoneW); + setZoneW=NULL ; +} + +void showEditTimeZone() +{ + APP_LOG(APP_LOG_LEVEL_DEBUG, "showEditTimeZone: setZoneW==%p", setZoneW); + if (setZoneW == NULL) create_setZoneW(); + window_stack_push(setZoneW, true); changed = true; } + diff --git a/wscript b/wscript new file mode 100644 index 0000000..0554dc8 --- /dev/null +++ b/wscript @@ -0,0 +1,24 @@ + +# +# This file is the default set of rules to compile a Pebble project. +# +# Feel free to customize this to your needs. +# + +top = '.' +out = 'build' + +def options(ctx): + ctx.load('pebble_sdk') + +def configure(ctx): + ctx.load('pebble_sdk') + +def build(ctx): + ctx.load('pebble_sdk') + + ctx.pbl_program(source=ctx.path.ant_glob('src/**/*.c'), + target='pebble-app.elf') + + ctx.pbl_bundle(elf='pebble-app.elf', + js=ctx.path.ant_glob('src/js/**/*.js'))