Trammell Hudson avatar Trammell Hudson committed 5a51b2f Merge

merge google authenticator tree

Comments (0)

Files changed (3)

+#include <string.h>
+#include "sha1.h"
+
+#define HASH_LENGTH 20
+#define BLOCK_LENGTH 64
+
+typedef union
+{
+  uint8_t b[BLOCK_LENGTH];
+  uint32_t w[BLOCK_LENGTH/4];
+} _buffer;
+typedef union {
+  uint8_t b[HASH_LENGTH];
+  uint32_t w[HASH_LENGTH/4];
+} _state;
+
+static _buffer buffer;
+static uint8_t bufferOffset;
+static _state state;
+static uint32_t byteCount;
+static uint8_t keyBuffer[BLOCK_LENGTH];
+static uint8_t innerHash[HASH_LENGTH];
+    
+#define SHA1_K0 0x5a827999
+#define SHA1_K20 0x6ed9eba1
+#define SHA1_K40 0x8f1bbcdc
+#define SHA1_K60 0xca62c1d6
+
+static const uint8_t sha1InitState[] = {
+  0x01,0x23,0x45,0x67, // H0
+  0x89,0xab,0xcd,0xef, // H1
+  0xfe,0xdc,0xba,0x98, // H2
+  0x76,0x54,0x32,0x10, // H3
+  0xf0,0xe1,0xd2,0xc3  // H4
+};
+
+static void sha1_init(void) {
+  memcpy(state.b,sha1InitState,HASH_LENGTH);
+  byteCount = 0;
+  bufferOffset = 0;
+}
+
+static uint32_t sha1_rol32(uint32_t number, uint8_t bits) {
+  return ((number << bits) | (number >> (32-bits)));
+}
+
+static void sha1_hashBlock() {
+  uint8_t i;
+  uint32_t a,b,c,d,e,t;
+
+  a=state.w[0];
+  b=state.w[1];
+  c=state.w[2];
+  d=state.w[3];
+  e=state.w[4];
+  for (i=0; i<80; i++) {
+    if (i>=16) {
+      t = buffer.w[(i+13)&15] ^ buffer.w[(i+8)&15] ^ buffer.w[(i+2)&15] ^ buffer.w[i&15];
+      buffer.w[i&15] = sha1_rol32(t,1);
+    }
+    if (i<20) {
+      t = (d ^ (b & (c ^ d))) + SHA1_K0;
+    } else if (i<40) {
+      t = (b ^ c ^ d) + SHA1_K20;
+    } else if (i<60) {
+      t = ((b & c) | (d & (b | c))) + SHA1_K40;
+    } else {
+      t = (b ^ c ^ d) + SHA1_K60;
+    }
+    t+=sha1_rol32(a,5) + e + buffer.w[i&15];
+    e=d;
+    d=c;
+    c=sha1_rol32(b,30);
+    b=a;
+    a=t;
+  }
+  state.w[0] += a;
+  state.w[1] += b;
+  state.w[2] += c;
+  state.w[3] += d;
+  state.w[4] += e;
+}
+
+void sha1_addUncounted(uint8_t data) {
+  buffer.b[bufferOffset ^ 3] = data;
+  bufferOffset++;
+  if (bufferOffset == BLOCK_LENGTH) {
+    sha1_hashBlock();
+    bufferOffset = 0;
+  }
+}
+
+void sha1_write(uint8_t data) {
+  ++byteCount;
+  sha1_addUncounted(data);
+}
+
+void sha1_writebytes(const uint8_t* data, int length) {
+ for (int i=0; i<length; i++)
+ {
+   sha1_write(data[i]);
+ }
+}
+
+void sha1_pad() {
+  // Implement SHA-1 padding (fips180-2 §5.1.1)
+
+  // Pad with 0x80 followed by 0x00 until the end of the block
+  sha1_addUncounted(0x80);
+  while (bufferOffset != 56) sha1_addUncounted(0x00);
+
+  // Append length in the last 8 bytes
+  sha1_addUncounted(0); // We're only using 32 bit lengths
+  sha1_addUncounted(0); // But SHA-1 supports 64 bit lengths
+  sha1_addUncounted(0); // So zero pad the top bits
+  sha1_addUncounted(byteCount >> 29); // Shifting to multiply by 8
+  sha1_addUncounted(byteCount >> 21); // as SHA-1 supports bitstreams as well as
+  sha1_addUncounted(byteCount >> 13); // byte.
+  sha1_addUncounted(byteCount >> 5);
+  sha1_addUncounted(byteCount << 3);
+}
+
+
+uint8_t* sha1_result(void) {
+  // Pad to complete the last block
+  sha1_pad();
+  
+  // Swap byte order back
+  for (int i=0; i<5; i++) {
+    uint32_t a,b;
+    a=state.w[i];
+    b=a<<24;
+    b|=(a<<8) & 0x00ff0000;
+    b|=(a>>8) & 0x0000ff00;
+    b|=a>>24;
+    state.w[i]=b;
+  }
+  
+  // Return pointer to hash (20 characters)
+  return state.b;
+}
+
+#define HMAC_IPAD 0x36
+#define HMAC_OPAD 0x5c
+
+void sha1_initHmac(const uint8_t* key, int keyLength) {
+  uint8_t i;
+  memset(keyBuffer,0,BLOCK_LENGTH);
+  if (keyLength > BLOCK_LENGTH) {
+    // Hash long keys
+    sha1_init();
+    for (;keyLength--;) sha1_write(*key++);
+    memcpy(keyBuffer,sha1_result(),HASH_LENGTH);
+  } else {
+    // Block length keys are used as is
+    memcpy(keyBuffer,key,keyLength);
+  }
+  // Start inner hash
+  sha1_init();
+  for (i=0; i<BLOCK_LENGTH; i++) {
+    sha1_write(keyBuffer[i] ^ HMAC_IPAD);
+  }
+}
+
+uint8_t* sha1_resultHmac(void) {
+  uint8_t i;
+  // Complete inner hash
+  memcpy(innerHash,sha1_result(),HASH_LENGTH);
+  // Calculate outer hash
+  sha1_init();
+  for (i=0; i<BLOCK_LENGTH; i++) sha1_write(keyBuffer[i] ^ HMAC_OPAD);
+  for (i=0; i<HASH_LENGTH; i++) sha1_write(innerHash[i]);
+  return sha1_result();
+}
+
+
+static const uint8_t hmacKey1[] = {
+// Generate your key from the QR code and base32 decoding the value
+#include "otp.do-not-checkin"
+};
+
+
+/** C function to do oauth computation */
+uint32_t
+oauth_calc(
+	uint32_t unix_epoch
+)
+{
+	const uint32_t now = unix_epoch / 30;
+
+	uint8_t byteArray[] = {
+		0,
+		0,
+		0,
+		0,
+		now >> 24,
+		now >> 16,
+		now >>  8,
+		now >>  0,
+	};
+
+	sha1_initHmac(hmacKey1,sizeof(hmacKey1));
+	sha1_writebytes(byteArray, 8);
+	const uint8_t * const hash = sha1_resultHmac();
+  
+	const unsigned offset = hash[20 - 1] & 0xF; 
+	uint32_t truncatedHash = 0;
+	for (int j = 0; j < 4; ++j) {
+		truncatedHash <<= 8;
+		truncatedHash  |= hash[offset + j];
+	}
+    
+	truncatedHash &= 0x7FFFFFFF;
+	truncatedHash %= 1000000;
+
+	return truncatedHash;
+}
+#ifndef Sha1_h
+#define Sha1_h
+
+#include <sys/types.h>
+#include <inttypes.h>
+
+uint32_t oauth_calc(uint32_t unix_epoch);
+
+#endif
+/** \file
+ * Google Two Factor authentication watch face.
+ */
+#include "pebble_os.h"
+#include "pebble_app.h"
+#include "pebble_fonts.h"
+#include "sha1.h"
+
+PBL_APP_INFO(
+	"Two Factor",
+	"hudson",
+	1, // Version
+	INVALID_RESOURCE,
+	APP_INFO_WATCH_FACE
+);
+
+
+static char time_buffer[32];
+static char otp_buffer[32];
+static Window window;
+static TextLayer time_layer;
+static TextLayer otp_layer;
+static Layer bargraph_layer;
+static uint32_t remaining;
+static unsigned old_half;
+
+static void
+bargraph_layer_update(
+	Layer * const me,
+	GContext * ctx
+)
+{
+	// Draw a white rectangle across the entire bottom
+	const unsigned w = me->bounds.size.w;
+	const unsigned h = me->bounds.size.h;
+	graphics_context_set_fill_color(ctx, GColorWhite);
+	graphics_fill_rect(ctx, GRect(0, 0, w, h), 0, GCornersAll);
+
+	graphics_context_set_fill_color(ctx, GColorBlack);
+	graphics_fill_rect(ctx, GRect(0, 0, (w * remaining) / 30, h), 4, GCornersRight);
+}
+
+#define GMT_OFFSET 5
+
+static uint32_t unix_epoch_time(
+	const PblTm * const curr_time
+)
+{
+  long now = 0;
+  now += (curr_time->tm_year-70)*31536000;
+  now += ((curr_time->tm_year-69)/4)*86400;
+  now -= ((curr_time->tm_year-1)/100)*86400;
+  now += ((curr_time->tm_year+299)/400)*86400;
+  now += curr_time->tm_yday*86400;
+  now += curr_time->tm_hour*3600;
+  now += curr_time->tm_min*60;
+  now += curr_time->tm_sec;
+  return now;
+}
+
+static void
+handle_tick(
+	AppContextRef ctx,
+	PebbleTickEvent * const event
+)
+{
+	(void) ctx;
+	const PblTm * const ptm = event->tick_time;
+
+	// Only recompute and update if we have a new time step
+	remaining = 30 - (ptm->tm_sec % 30);
+	layer_mark_dirty(&bargraph_layer);
+
+	unsigned new_half = ptm->tm_sec > 30;
+	if (new_half == old_half)
+		return;
+	old_half = new_half;
+
+	string_format_time(
+		time_buffer,
+		sizeof(time_buffer),
+		"%H:%M",
+		event->tick_time
+	);
+	text_layer_set_text(&time_layer, time_buffer);
+
+	// Convert the time into a unix epoch time based on our local time.
+	// this should be done in a library
+	const uint32_t now = unix_epoch_time(ptm) + 3600 * GMT_OFFSET;
+	uint32_t auth = oauth_calc(now);
+
+	otp_buffer[6] = '\0';
+	otp_buffer[5] = '0' + auth % 10; auth /= 10;
+	otp_buffer[4] = '0' + auth % 10; auth /= 10;
+	otp_buffer[3] = '0' + auth % 10; auth /= 10;
+	otp_buffer[2] = '0' + auth % 10; auth /= 10;
+	otp_buffer[1] = '0' + auth % 10; auth /= 10;
+	otp_buffer[0] = '0' + auth % 10; auth /= 10;
+	text_layer_set_text(&otp_layer, otp_buffer);
+}
+
+
+static void
+handle_init(
+	AppContextRef ctx
+)
+{
+	(void) ctx;
+
+	// Store an invalid value in the old_half so that we will
+	// be forced to recompute the current hash
+	old_half = 999;
+
+	window_init(&window, "RFC 6238");
+	window_stack_push(&window, true);
+
+	text_layer_init(&time_layer, GRect(22,10,144-24,50));
+	time_buffer[0] = '\0';
+	text_layer_set_text(&time_layer, time_buffer);
+	text_layer_set_font(&time_layer, fonts_get_system_font(FONT_KEY_GOTHAM_42_BOLD));
+	layer_add_child(&window.layer, &time_layer.layer);
+
+	text_layer_init(&otp_layer, GRect(13,70,144-13,40));
+	text_layer_set_font(&otp_layer, fonts_get_system_font(FONT_KEY_GOTHAM_30_BLACK));
+	otp_buffer[0] = '\0';
+	text_layer_set_text(&otp_layer, otp_buffer);
+	layer_add_child(&window.layer, &otp_layer.layer);
+
+	// Bargraph layer is across the bottom
+	const unsigned bargraph_height = 20;
+	layer_init(&bargraph_layer, GRect(0,168-bargraph_height-15,144,bargraph_height));
+	bargraph_layer.update_proc = bargraph_layer_update;
+
+	layer_add_child(&window.layer, &bargraph_layer);
+}
+
+
+void
+pbl_main(
+	void * const params
+)
+{
+	PebbleAppHandlers handlers = {
+		.init_handler	= &handle_init,
+		.tick_info	= {
+			.tick_handler = &handle_tick,
+			.tick_units = SECOND_UNIT,
+		},
+	};
+
+	app_event_loop(params, &handlers);
+}
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.