├── LICENSE ├── README.md ├── doc ├── pebbleauth_qr.png ├── pebbleauth_watch1_50.png ├── pebbleauth_watch2_50.png ├── pebbleauth_watch3_50.png └── pebbleauth_watch4_50.png ├── html ├── v20 │ ├── assets │ │ ├── Thumbs.db │ │ ├── font_style_1.png │ │ ├── font_style_2.png │ │ ├── font_style_3.png │ │ └── font_style_4.png │ ├── index.htm │ └── script.js ├── v21 │ ├── assets │ │ ├── Thumbs.db │ │ ├── font_style_1.png │ │ ├── font_style_2.png │ │ ├── font_style_3.png │ │ └── font_style_4.png │ ├── index.htm │ └── script.js ├── v22 │ ├── assets │ │ ├── Thumbs.db │ │ ├── font_style_1.png │ │ ├── font_style_2.png │ │ ├── font_style_3.png │ │ └── font_style_4.png │ ├── index.htm │ └── script.js ├── v23 │ ├── assets │ │ ├── Thumbs.db │ │ ├── og │ │ │ ├── font_style_1.png │ │ │ ├── font_style_2.png │ │ │ ├── font_style_3.png │ │ │ ├── font_style_4.png │ │ │ └── multi_code_layout.png │ │ └── time │ │ │ ├── font_style_1.png │ │ │ ├── font_style_2.png │ │ │ ├── font_style_3.png │ │ │ ├── font_style_4.png │ │ │ └── multi_code_layout.png │ ├── index.htm │ ├── pebble-og.css │ ├── pebble-time.css │ └── script.js ├── v24 │ ├── OLD │ │ ├── assets │ │ │ ├── Thumbs.db │ │ │ ├── og │ │ │ │ ├── font_style_1.png │ │ │ │ ├── font_style_2.png │ │ │ │ ├── font_style_3.png │ │ │ │ ├── font_style_4.png │ │ │ │ └── multi_code_layout.png │ │ │ └── time │ │ │ │ ├── font_style_1.png │ │ │ │ ├── font_style_2.png │ │ │ │ ├── font_style_3.png │ │ │ │ ├── font_style_4.png │ │ │ │ └── multi_code_layout.png │ │ ├── index.htm │ │ ├── pebble-og.css │ │ ├── pebble-time.css │ │ └── script.js │ ├── assets │ │ ├── Thumbs.db │ │ ├── og │ │ │ ├── font_style_1.png │ │ │ ├── font_style_2.png │ │ │ ├── font_style_3.png │ │ │ ├── font_style_4.png │ │ │ ├── multi_code_layout.png │ │ │ ├── screenshot_holder_1.png │ │ │ └── screenshot_holder_2.png │ │ └── time │ │ │ ├── font_style_1.png │ │ │ ├── font_style_2.png │ │ │ ├── font_style_3.png │ │ │ ├── font_style_4.png │ │ │ ├── multi_code_layout.png │ │ │ └── screenshot_holder_3.png │ ├── index.htm │ ├── pebble-1.css │ ├── pebble-2.css │ ├── pebble-3.css │ └── script.js ├── v25 │ ├── OLD │ │ ├── assets │ │ │ ├── Thumbs.db │ │ │ ├── og │ │ │ │ ├── font_style_1.png │ │ │ │ ├── font_style_2.png │ │ │ │ ├── font_style_3.png │ │ │ │ ├── font_style_4.png │ │ │ │ └── multi_code_layout.png │ │ │ └── time │ │ │ │ ├── font_style_1.png │ │ │ │ ├── font_style_2.png │ │ │ │ ├── font_style_3.png │ │ │ │ ├── font_style_4.png │ │ │ │ └── multi_code_layout.png │ │ ├── index.htm │ │ ├── pebble-og.css │ │ ├── pebble-time.css │ │ └── script.js │ ├── assets │ │ ├── Thumbs.db │ │ ├── og │ │ │ ├── font_style_1.png │ │ │ ├── font_style_2.png │ │ │ ├── font_style_3.png │ │ │ ├── font_style_4.png │ │ │ ├── multi_code_layout.png │ │ │ ├── screenshot_holder_1.png │ │ │ └── screenshot_holder_2.png │ │ └── time │ │ │ ├── font_style_1.png │ │ │ ├── font_style_2.png │ │ │ ├── font_style_3.png │ │ │ ├── font_style_4.png │ │ │ ├── multi_code_layout.png │ │ │ └── screenshot_holder_3.png │ ├── index.htm │ ├── pebble-1.css │ ├── pebble-2.css │ ├── pebble-3.css │ └── script.js ├── v4 │ ├── assets │ │ ├── Thumbs.db │ │ ├── font_style_1.png │ │ ├── font_style_2.png │ │ ├── font_style_3.png │ │ └── font_style_4.png │ ├── index.htm │ └── script.js ├── v5 │ ├── assets │ │ ├── Thumbs.db │ │ ├── font_style_1.png │ │ ├── font_style_2.png │ │ ├── font_style_3.png │ │ └── font_style_4.png │ ├── index.htm │ └── script.js ├── v6 │ ├── assets │ │ ├── Thumbs.db │ │ ├── font_style_1.png │ │ ├── font_style_2.png │ │ ├── font_style_3.png │ │ └── font_style_4.png │ └── index.htm └── v7 │ ├── assets │ ├── Thumbs.db │ ├── font_style_1.png │ ├── font_style_2.png │ ├── font_style_3.png │ └── font_style_4.png │ ├── index.htm │ └── script.js ├── package.json ├── resources ├── fonts │ ├── BD_Cartoon_Shout.ttf │ ├── BITWISE_32.ttf │ ├── DS-DIGIB.TTF │ ├── ORBITRON_28.ttf │ └── UNISPACE_20.ttf └── images │ ├── Padlock_aplite.png │ ├── Padlock_aplite~color.png │ ├── Pebble_Bin.png │ ├── Pebble_Bin~color.png │ ├── Pebble_Star2.png │ ├── Pebble_Star2~color.png │ ├── SimpleFadeBottom.png │ ├── SimpleFadeTop.png │ └── auth_icon.png ├── src ├── c │ ├── base32.c │ ├── base32.h │ ├── display.h │ ├── display_round.c │ ├── display_square.c │ ├── dod_window.c │ ├── dod_window.h │ ├── google-authenticator.c │ ├── google-authenticator.h │ ├── hmac.c │ ├── hmac.h │ ├── main.c │ ├── main.h │ ├── multi_code_window.c │ ├── multi_code_window.h │ ├── select_window.c │ ├── select_window.h │ ├── sha1.c │ ├── sha1.h │ ├── single_code_window.c │ └── single_code_window.h └── pkjs │ ├── app.js │ └── config.js └── wscript /README.md: -------------------------------------------------------------------------------- 1 | QuickAuth 2 | ========== 3 | 4 | Google Authenticator for the Pebble smart watch! 5 | 6 | ![](doc/pebbleauth_watch1_50.png) ![](doc/pebbleauth_watch2_50.png) ![](doc/pebbleauth_watch3_50.png) ![](doc/pebbleauth_watch4_50.png) 7 | 8 | The app can store up to 16 keys. You must use 20 or less characters for the label although not all fonts can display 20 characters on the tiny Pebble screen. It's been tested with 128 character keys which should cover all uses! When adding any keys, known incorrect characters are corrected and spaces are removed. Once the keys have been uploaded they'll be saved on the watch and will only be removed during an upgrade. But don't worry the keys are also stored in the Pebble phone app and reloaded in the event of an upgrade or factory reset of the watch. 9 | 10 | The authentication codes are based on time and timezones are also automatically handled. (In case of time zone errors, just reboot your phone and watch.) 11 | 12 | There's a [great guide by Adam Zeis](http://www.connectedly.com/how-get-your-two-step-verification-codes-your-pebble) that explains how to setup the app to work with your Google account. 13 | 14 | Happy two-factoring! 15 | 16 | 17 | ### GET IT HERE 18 | 19 | ![](doc/pebbleauth_qr.png) 20 | 21 | [![](http://pblweb.com/badge/53131df8bb31cf87cd00019a/white/medium/)](http://pblweb.com/appstore/53131df8bb31cf87cd00019a) -------------------------------------------------------------------------------- /doc/pebbleauth_qr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/doc/pebbleauth_qr.png -------------------------------------------------------------------------------- /doc/pebbleauth_watch1_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/doc/pebbleauth_watch1_50.png -------------------------------------------------------------------------------- /doc/pebbleauth_watch2_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/doc/pebbleauth_watch2_50.png -------------------------------------------------------------------------------- /doc/pebbleauth_watch3_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/doc/pebbleauth_watch3_50.png -------------------------------------------------------------------------------- /doc/pebbleauth_watch4_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/doc/pebbleauth_watch4_50.png -------------------------------------------------------------------------------- /html/v20/assets/Thumbs.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v20/assets/Thumbs.db -------------------------------------------------------------------------------- /html/v20/assets/font_style_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v20/assets/font_style_1.png -------------------------------------------------------------------------------- /html/v20/assets/font_style_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v20/assets/font_style_2.png -------------------------------------------------------------------------------- /html/v20/assets/font_style_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v20/assets/font_style_3.png -------------------------------------------------------------------------------- /html/v20/assets/font_style_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v20/assets/font_style_4.png -------------------------------------------------------------------------------- /html/v20/script.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | $('#foreground-color').on('click', function (event) { 3 | changeForeground = true; 4 | highlightColorBox($(this)); 5 | }); 6 | }); 7 | 8 | $(function () { 9 | $('#background-color').on('click', function (event) { 10 | changeForeground = false; 11 | highlightColorBox($(this)); 12 | }); 13 | }); 14 | 15 | $(function () { 16 | $('#color-picker .color-box').on('click', function (event) { 17 | var selected_color = rgb2hex($(this).css("background-color")); 18 | 19 | if (changeForeground) 20 | $('#foreground-color').css('background-color', selected_color); 21 | else 22 | $('#background-color').css('background-color', selected_color); 23 | $( "#color-picker" ).popup( "close" ); 24 | }); 25 | }); 26 | 27 | $(function () { 28 | $("#confirm_delete_all").click(function() { 29 | console.log("Deleting all"); 30 | var options = {}; 31 | options.delete_all = 1; 32 | var location = "pebblejs://close#" + encodeURIComponent(JSON.stringify(options)); 33 | document.location = location; 34 | }); 35 | }); 36 | 37 | $(function () { 38 | $("#cancel").click(function() { 39 | console.log("Cancelling"); 40 | document.location = "pebblejs://close"; 41 | }); 42 | }); 43 | 44 | $(function () { 45 | $("#save").click(function() { 46 | console.log("Submit"); 47 | 48 | var options = saveOptions(); 49 | 50 | if (options.secret && base32length(options.secret) < 10) { 51 | alert("Secret key value is too short"); 52 | } 53 | else if (options.secret && options.label.length === 0) { 54 | alert("Please enter a label"); 55 | } 56 | else { 57 | var return_to = getQueryParam('return_to', 'pebblejs://close#'); 58 | var location = return_to + encodeURIComponent(JSON.stringify(options)); 59 | console.log("Warping to:"); 60 | console.log(location); 61 | document.location = location; 62 | } 63 | }); 64 | }); 65 | 66 | function highlightColorBox($colorButton) { 67 | selectedColor = rgb2hex($colorButton.css("background-color")); 68 | 69 | var i; 70 | for (i = 1; i <= 13; i++) 71 | { 72 | $('#color-row'+i).children().removeClass("selected-color-box"); 73 | } 74 | $('#color-box-'+selectedColor.substring(1).toUpperCase()).addClass("selected-color-box"); 75 | } 76 | 77 | //Function to convert hex format to a rgb color 78 | function rgb2hex(rgb){ 79 | rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/); 80 | return ("#" + 81 | ("0" + parseInt(rgb[1],10).toString(16)).slice(-2) + 82 | ("0" + parseInt(rgb[2],10).toString(16)).slice(-2) + 83 | ("0" + parseInt(rgb[3],10).toString(16)).slice(-2)).toUpperCase(); 84 | } 85 | 86 | function queryObj() { 87 | var result = {}, keyValuePairs = location.search.slice(1).split('&'); 88 | 89 | keyValuePairs.forEach(function(keyValuePair) { 90 | keyValuePair = keyValuePair.split('='); 91 | result[keyValuePair[0]] = keyValuePair[1] || ''; 92 | }); 93 | 94 | return result; 95 | } 96 | 97 | function getQueryParam(variable, default_) { 98 | var query = location.search.substring(1); 99 | var vars = query.split('&'); 100 | for (var i = 0; i < vars.length; i++) { 101 | var pair = vars[i].split('='); 102 | if (pair[0] == variable) 103 | return decodeURIComponent(pair[1]); 104 | } 105 | return default_ || false; 106 | } 107 | 108 | function isNumber(n) { 109 | return !isNaN(parseFloat(n)) && isFinite(n); 110 | } 111 | 112 | // Base32 decoder from https://github.com/agnoster/base32-js/blob/master/lib/base32.js by Isaac Wolkerstorfer 113 | var alphabet = '0123456789abcdefghjkmnpqrtuvwxyz' 114 | var alias = { o:0, i:1, l:1, s:5 } 115 | 116 | var lookup = function() { 117 | var table = {} 118 | // Invert 'alphabet' 119 | for (var i = 0; i < alphabet.length; i++) { 120 | table[alphabet[i]] = i 121 | } 122 | // Splice in 'alias' 123 | for (var key in alias) { 124 | if (!alias.hasOwnProperty(key)) continue 125 | table[key] = table['' + alias[key]] 126 | } 127 | lookup = function() { return table } 128 | return table 129 | } 130 | 131 | function Decoder() { 132 | var skip = 0 // how many bits we have from the previous character 133 | var byte = 0 // current byte we're producing 134 | 135 | this.output = '' 136 | 137 | // Consume a character from the stream, store 138 | // the output in this.output. As before, better 139 | // to use update(). 140 | this.readChar = function(char) { 141 | if (typeof char != 'string'){ 142 | if (typeof char == 'number') { 143 | char = String.fromCharCode(char) 144 | } 145 | } 146 | char = char.toLowerCase() 147 | var val = lookup()[char] 148 | if (typeof val == 'undefined') { 149 | // character does not exist in our lookup table 150 | return // skip silently. An alternative would be: 151 | // throw Error('Could not find character "' + char + '" in lookup table.') 152 | } 153 | val <<= 3 // move to the high bits 154 | byte |= val >>> skip 155 | skip += 5 156 | if (skip >= 8) { 157 | // we have enough to preduce output 158 | this.output += String.fromCharCode(byte) 159 | skip -= 8 160 | if (skip > 0) byte = (val << (5 - skip)) & 255 161 | else byte = 0 162 | } 163 | 164 | } 165 | 166 | this.finish = function(check) { 167 | var output = this.output + (skip < 0 ? alphabet[bits >> 3] : '') + (check ? '$' : '') 168 | this.output = '' 169 | return output 170 | } 171 | } 172 | 173 | Decoder.prototype.update = function(input, flush) { 174 | for (var i = 0; i < input.length; i++) { 175 | this.readChar(input[i]) 176 | } 177 | var output = this.output 178 | this.output = '' 179 | if (flush) { 180 | output += this.finish() 181 | } 182 | return output 183 | } 184 | 185 | // Base32-encoded string goes in, decoded data comes out. 186 | function decode(input) { 187 | var decoder = new Decoder() 188 | var output = decoder.update(input, true) 189 | return output 190 | } 191 | 192 | function base32length(secret) { 193 | return decode(secret).length; 194 | } 195 | 196 | function saveOptions() { 197 | 198 | var options = {}; 199 | 200 | options.theme = parseInt($("input[name=theme]:checked").val(), 10); 201 | options.font_style = parseInt($("input[name=font_style]:checked").val(), 10); 202 | options.idle_timeout = parseInt($("#idle_timeout").val(), 10); 203 | 204 | var colors = rgb2hex($('#background-color').css("background-color")).replace("#", ''); 205 | colors = colors + rgb2hex($('#foreground-color').css("background-color")).replace("#", ''); 206 | options.basalt_colors = colors; 207 | 208 | if ($("#keysecret").val()) { 209 | options.label = $("#keyname").val(); 210 | options.secret = $("#keysecret").val().replace(/\s/g, ''); 211 | } 212 | 213 | return options; 214 | } 215 | 216 | $("document").ready(function() { 217 | var watch_version = getQueryParam("watch_version", 1); 218 | 219 | if (watch_version >= 3) { 220 | var basalt_colors = getQueryParam("basalt_colors", 0); 221 | basalt_colors = basalt_colors.length == 12 ? basalt_colors : "00AAFFFFFFFF"; 222 | $('#background-color').css('background-color', "#"+basalt_colors.substring(0,6)); 223 | $('#foreground-color').css('background-color', "#"+basalt_colors.substring(6,12)); 224 | $('#time-foreground').removeClass("hidden"); 225 | $('#time-background').removeClass("hidden"); 226 | } else { 227 | var theme = getQueryParam("theme", 0); 228 | theme = parseInt(isNumber(theme) && theme <= 1 ? theme : 0); 229 | $('#theme').removeClass("hidden"); 230 | $('#theme_'+theme).prop( "checked", true ).checkboxradio("refresh"); 231 | } 232 | 233 | var otp_count = getQueryParam("otp_count", 0); 234 | var font_style = getQueryParam("font_style", 0); 235 | var idle_timeout = getQueryParam("idle_timeout", 300); 236 | 237 | font_style = parseInt(isNumber(font_style) && font_style <= 3 ? font_style : 0); 238 | idle_timeout = parseInt(isNumber(idle_timeout) && (idle_timeout >= 0 || idle_timeout == 60 || idle_timeout == 120 || idle_timeout == 300 || idle_timeout == 600) ? idle_timeout : 300); 239 | 240 | $('#font_style_'+font_style).prop( "checked", true ).checkboxradio("refresh"); 241 | $("#idle_timeout").val(idle_timeout).selectmenu("refresh"); 242 | 243 | otp_count = parseInt(isNumber(otp_count) ? otp_count : 0); 244 | var slots_remaining = 16 - otp_count; 245 | 246 | if (slots_remaining === 0) { 247 | document.getElementById("keyname").disabled=true; 248 | document.getElementById("keysecret").disabled=true; 249 | } 250 | document.getElementById("footer").innerHTML=document.getElementById("footer").innerHTML.replace("NUM_SPACES", slots_remaining); 251 | }); -------------------------------------------------------------------------------- /html/v21/assets/Thumbs.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v21/assets/Thumbs.db -------------------------------------------------------------------------------- /html/v21/assets/font_style_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v21/assets/font_style_1.png -------------------------------------------------------------------------------- /html/v21/assets/font_style_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v21/assets/font_style_2.png -------------------------------------------------------------------------------- /html/v21/assets/font_style_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v21/assets/font_style_3.png -------------------------------------------------------------------------------- /html/v21/assets/font_style_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v21/assets/font_style_4.png -------------------------------------------------------------------------------- /html/v21/script.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | $('#foreground-color').on('click', function (event) { 3 | changeForeground = true; 4 | highlightColorBox($(this)); 5 | }); 6 | }); 7 | 8 | $(function () { 9 | $('#background-color').on('click', function (event) { 10 | changeForeground = false; 11 | highlightColorBox($(this)); 12 | }); 13 | }); 14 | 15 | $(function () { 16 | $('#color-picker .color-box').on('click', function (event) { 17 | var selected_color = rgb2hex($(this).css("background-color")); 18 | 19 | if (changeForeground) 20 | $('#foreground-color').css('background-color', selected_color); 21 | else 22 | $('#background-color').css('background-color', selected_color); 23 | $( "#color-picker" ).popup( "close" ); 24 | }); 25 | }); 26 | 27 | $(function () { 28 | $("#confirm_delete_all").click(function() { 29 | console.log("Deleting all"); 30 | var options = {}; 31 | options.delete_all = 1; 32 | var location = "pebblejs://close#" + encodeURIComponent(JSON.stringify(options)); 33 | document.location = location; 34 | }); 35 | }); 36 | 37 | $(function () { 38 | $("#cancel").click(function() { 39 | console.log("Cancelling"); 40 | document.location = "pebblejs://close"; 41 | }); 42 | }); 43 | 44 | $(function () { 45 | $("#save").click(function() { 46 | console.log("Submit"); 47 | 48 | var options = saveOptions(); 49 | 50 | if (options.secret && base32length(options.secret) < 10) { 51 | alert("Secret key value is too short"); 52 | } 53 | else if (options.secret && options.label.length === 0) { 54 | alert("Please enter a label"); 55 | } 56 | else { 57 | var return_to = getQueryParam('return_to', 'pebblejs://close#'); 58 | var location = return_to + encodeURIComponent(JSON.stringify(options)); 59 | console.log("Warping to:"); 60 | console.log(location); 61 | document.location = location; 62 | } 63 | }); 64 | }); 65 | 66 | function highlightColorBox($colorButton) { 67 | selectedColor = rgb2hex($colorButton.css("background-color")); 68 | 69 | var i; 70 | for (i = 1; i <= 13; i++) 71 | { 72 | $('#color-row'+i).children().removeClass("selected-color-box"); 73 | } 74 | $('#color-box-'+selectedColor.substring(1).toUpperCase()).addClass("selected-color-box"); 75 | } 76 | 77 | //Function to convert hex format to a rgb color 78 | function rgb2hex(rgb){ 79 | rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/); 80 | return ("#" + 81 | ("0" + parseInt(rgb[1],10).toString(16)).slice(-2) + 82 | ("0" + parseInt(rgb[2],10).toString(16)).slice(-2) + 83 | ("0" + parseInt(rgb[3],10).toString(16)).slice(-2)).toUpperCase(); 84 | } 85 | 86 | function queryObj() { 87 | var result = {}, keyValuePairs = location.search.slice(1).split('&'); 88 | 89 | keyValuePairs.forEach(function(keyValuePair) { 90 | keyValuePair = keyValuePair.split('='); 91 | result[keyValuePair[0]] = keyValuePair[1] || ''; 92 | }); 93 | 94 | return result; 95 | } 96 | 97 | function getQueryParam(variable, default_) { 98 | var query = location.search.substring(1); 99 | var vars = query.split('&'); 100 | for (var i = 0; i < vars.length; i++) { 101 | var pair = vars[i].split('='); 102 | if (pair[0] == variable) 103 | return decodeURIComponent(pair[1]); 104 | } 105 | return default_ || false; 106 | } 107 | 108 | function isNumber(n) { 109 | return !isNaN(parseFloat(n)) && isFinite(n); 110 | } 111 | 112 | // Base32 decoder from https://github.com/agnoster/base32-js/blob/master/lib/base32.js by Isaac Wolkerstorfer 113 | var alphabet = '0123456789abcdefghjkmnpqrtuvwxyz' 114 | var alias = { o:0, i:1, l:1, s:5 } 115 | 116 | var lookup = function() { 117 | var table = {} 118 | // Invert 'alphabet' 119 | for (var i = 0; i < alphabet.length; i++) { 120 | table[alphabet[i]] = i 121 | } 122 | // Splice in 'alias' 123 | for (var key in alias) { 124 | if (!alias.hasOwnProperty(key)) continue 125 | table[key] = table['' + alias[key]] 126 | } 127 | lookup = function() { return table } 128 | return table 129 | } 130 | 131 | function Decoder() { 132 | var skip = 0 // how many bits we have from the previous character 133 | var byte = 0 // current byte we're producing 134 | 135 | this.output = '' 136 | 137 | // Consume a character from the stream, store 138 | // the output in this.output. As before, better 139 | // to use update(). 140 | this.readChar = function(char) { 141 | if (typeof char != 'string'){ 142 | if (typeof char == 'number') { 143 | char = String.fromCharCode(char) 144 | } 145 | } 146 | char = char.toLowerCase() 147 | var val = lookup()[char] 148 | if (typeof val == 'undefined') { 149 | // character does not exist in our lookup table 150 | return // skip silently. An alternative would be: 151 | // throw Error('Could not find character "' + char + '" in lookup table.') 152 | } 153 | val <<= 3 // move to the high bits 154 | byte |= val >>> skip 155 | skip += 5 156 | if (skip >= 8) { 157 | // we have enough to preduce output 158 | this.output += String.fromCharCode(byte) 159 | skip -= 8 160 | if (skip > 0) byte = (val << (5 - skip)) & 255 161 | else byte = 0 162 | } 163 | 164 | } 165 | 166 | this.finish = function(check) { 167 | var output = this.output + (skip < 0 ? alphabet[bits >> 3] : '') + (check ? '$' : '') 168 | this.output = '' 169 | return output 170 | } 171 | } 172 | 173 | Decoder.prototype.update = function(input, flush) { 174 | for (var i = 0; i < input.length; i++) { 175 | this.readChar(input[i]) 176 | } 177 | var output = this.output 178 | this.output = '' 179 | if (flush) { 180 | output += this.finish() 181 | } 182 | return output 183 | } 184 | 185 | // Base32-encoded string goes in, decoded data comes out. 186 | function decode(input) { 187 | var decoder = new Decoder() 188 | var output = decoder.update(input, true) 189 | return output 190 | } 191 | 192 | function base32length(secret) { 193 | return decode(secret).length; 194 | } 195 | 196 | function saveOptions() { 197 | 198 | var options = {}; 199 | 200 | options.theme = parseInt($("input[name=theme]:checked").val(), 10); 201 | options.font_style = parseInt($("input[name=font_style]:checked").val(), 10); 202 | options.idle_timeout = parseInt($("#idle_timeout").val(), 10); 203 | 204 | var colors = rgb2hex($('#background-color').css("background-color")).replace("#", ''); 205 | colors = colors + rgb2hex($('#foreground-color').css("background-color")).replace("#", ''); 206 | options.basalt_colors = colors; 207 | 208 | if ($("#keysecret").val()) { 209 | options.label = $("#keyname").val(); 210 | options.secret = $("#keysecret").val().replace(/\s/g, ''); 211 | } 212 | 213 | return options; 214 | } 215 | 216 | $("document").ready(function() { 217 | var watch_version = getQueryParam("watch_version", 1); 218 | 219 | if (watch_version >= 3) { 220 | var basalt_colors = getQueryParam("basalt_colors", 0); 221 | basalt_colors = basalt_colors.length == 12 ? basalt_colors : "00AAFFFFFFFF"; 222 | $('#background-color').css('background-color', "#"+basalt_colors.substring(0,6)); 223 | $('#foreground-color').css('background-color', "#"+basalt_colors.substring(6,12)); 224 | $('#time-foreground').removeClass("hidden"); 225 | $('#time-background').removeClass("hidden"); 226 | } else { 227 | var theme = getQueryParam("theme", 0); 228 | theme = parseInt(isNumber(theme) && theme <= 1 ? theme : 0); 229 | $('#theme').removeClass("hidden"); 230 | $('#theme_'+theme).prop( "checked", true ).checkboxradio("refresh"); 231 | } 232 | 233 | var otp_count = getQueryParam("otp_count", 0); 234 | var font_style = getQueryParam("font_style", 0); 235 | var idle_timeout = getQueryParam("idle_timeout", 300); 236 | 237 | font_style = parseInt(isNumber(font_style) && font_style <= 3 ? font_style : 0); 238 | idle_timeout = parseInt(isNumber(idle_timeout) && (idle_timeout >= 0 || idle_timeout == 60 || idle_timeout == 120 || idle_timeout == 300 || idle_timeout == 600) ? idle_timeout : 300); 239 | 240 | $('#font_style_'+font_style).prop( "checked", true ).checkboxradio("refresh"); 241 | $("#idle_timeout").val(idle_timeout).selectmenu("refresh"); 242 | 243 | otp_count = parseInt(isNumber(otp_count) ? otp_count : 0); 244 | var slots_remaining = 16 - otp_count; 245 | 246 | if (slots_remaining === 0) { 247 | document.getElementById("keyname").disabled=true; 248 | document.getElementById("keysecret").disabled=true; 249 | } 250 | document.getElementById("footer").innerHTML=document.getElementById("footer").innerHTML.replace("NUM_SPACES", slots_remaining); 251 | }); -------------------------------------------------------------------------------- /html/v22/assets/Thumbs.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v22/assets/Thumbs.db -------------------------------------------------------------------------------- /html/v22/assets/font_style_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v22/assets/font_style_1.png -------------------------------------------------------------------------------- /html/v22/assets/font_style_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v22/assets/font_style_2.png -------------------------------------------------------------------------------- /html/v22/assets/font_style_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v22/assets/font_style_3.png -------------------------------------------------------------------------------- /html/v22/assets/font_style_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v22/assets/font_style_4.png -------------------------------------------------------------------------------- /html/v22/script.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | $('#foreground-color').on('click', function (event) { 3 | changeForeground = true; 4 | highlightColorBox($(this)); 5 | }); 6 | }); 7 | 8 | $(function () { 9 | $('#background-color').on('click', function (event) { 10 | changeForeground = false; 11 | highlightColorBox($(this)); 12 | }); 13 | }); 14 | 15 | $(function () { 16 | $('#color-picker .color-box').on('click', function (event) { 17 | var selected_color = rgb2hex($(this).css("background-color")); 18 | 19 | if (changeForeground) 20 | $('#foreground-color').css('background-color', selected_color); 21 | else 22 | $('#background-color').css('background-color', selected_color); 23 | $( "#color-picker" ).popup( "close" ); 24 | }); 25 | }); 26 | 27 | $(function () { 28 | $("#confirm_delete_all").click(function() { 29 | console.log("Deleting all"); 30 | var options = {}; 31 | options.delete_all = 1; 32 | var location = "pebblejs://close#" + encodeURIComponent(JSON.stringify(options)); 33 | document.location = location; 34 | }); 35 | }); 36 | 37 | $(function () { 38 | $("#cancel").click(function() { 39 | console.log("Cancelling"); 40 | document.location = "pebblejs://close"; 41 | }); 42 | }); 43 | 44 | $(function () { 45 | $("#save").click(function() { 46 | console.log("Submit"); 47 | 48 | var options = saveOptions(); 49 | 50 | if (options.secret && base32length(options.secret) < 10) { 51 | alert("Secret key value is too short"); 52 | } 53 | else if (options.secret && options.label.length === 0) { 54 | alert("Please enter a label"); 55 | } 56 | else { 57 | var return_to = getQueryParam('return_to', 'pebblejs://close#'); 58 | var location = return_to + encodeURIComponent(JSON.stringify(options)); 59 | console.log("Warping to:"); 60 | console.log(location); 61 | document.location = location; 62 | } 63 | }); 64 | }); 65 | 66 | function highlightColorBox($colorButton) { 67 | selectedColor = rgb2hex($colorButton.css("background-color")); 68 | 69 | var i; 70 | for (i = 1; i <= 13; i++) 71 | { 72 | $('#color-row'+i).children().removeClass("selected-color-box"); 73 | } 74 | $('#color-box-'+selectedColor.substring(1).toUpperCase()).addClass("selected-color-box"); 75 | } 76 | 77 | //Function to convert hex format to a rgb color 78 | function rgb2hex(rgb){ 79 | rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/); 80 | return ("#" + 81 | ("0" + parseInt(rgb[1],10).toString(16)).slice(-2) + 82 | ("0" + parseInt(rgb[2],10).toString(16)).slice(-2) + 83 | ("0" + parseInt(rgb[3],10).toString(16)).slice(-2)).toUpperCase(); 84 | } 85 | 86 | function queryObj() { 87 | var result = {}, keyValuePairs = location.search.slice(1).split('&'); 88 | 89 | keyValuePairs.forEach(function(keyValuePair) { 90 | keyValuePair = keyValuePair.split('='); 91 | result[keyValuePair[0]] = keyValuePair[1] || ''; 92 | }); 93 | 94 | return result; 95 | } 96 | 97 | function getQueryParam(variable, default_) { 98 | var query = location.search.substring(1); 99 | var vars = query.split('&'); 100 | for (var i = 0; i < vars.length; i++) { 101 | var pair = vars[i].split('='); 102 | if (pair[0] == variable) 103 | return decodeURIComponent(pair[1]); 104 | } 105 | return default_ || false; 106 | } 107 | 108 | function isNumber(n) { 109 | return !isNaN(parseFloat(n)) && isFinite(n); 110 | } 111 | 112 | // Base32 decoder from https://github.com/agnoster/base32-js/blob/master/lib/base32.js by Isaac Wolkerstorfer 113 | var alphabet = '0123456789abcdefghjkmnpqrtuvwxyz' 114 | var alias = { o:0, i:1, l:1, s:5 } 115 | 116 | var lookup = function() { 117 | var table = {} 118 | // Invert 'alphabet' 119 | for (var i = 0; i < alphabet.length; i++) { 120 | table[alphabet[i]] = i 121 | } 122 | // Splice in 'alias' 123 | for (var key in alias) { 124 | if (!alias.hasOwnProperty(key)) continue 125 | table[key] = table['' + alias[key]] 126 | } 127 | lookup = function() { return table } 128 | return table 129 | } 130 | 131 | function Decoder() { 132 | var skip = 0 // how many bits we have from the previous character 133 | var byte = 0 // current byte we're producing 134 | 135 | this.output = '' 136 | 137 | // Consume a character from the stream, store 138 | // the output in this.output. As before, better 139 | // to use update(). 140 | this.readChar = function(char) { 141 | if (typeof char != 'string'){ 142 | if (typeof char == 'number') { 143 | char = String.fromCharCode(char) 144 | } 145 | } 146 | char = char.toLowerCase() 147 | var val = lookup()[char] 148 | if (typeof val == 'undefined') { 149 | // character does not exist in our lookup table 150 | return // skip silently. An alternative would be: 151 | // throw Error('Could not find character "' + char + '" in lookup table.') 152 | } 153 | val <<= 3 // move to the high bits 154 | byte |= val >>> skip 155 | skip += 5 156 | if (skip >= 8) { 157 | // we have enough to preduce output 158 | this.output += String.fromCharCode(byte) 159 | skip -= 8 160 | if (skip > 0) byte = (val << (5 - skip)) & 255 161 | else byte = 0 162 | } 163 | 164 | } 165 | 166 | this.finish = function(check) { 167 | var output = this.output + (skip < 0 ? alphabet[bits >> 3] : '') + (check ? '$' : '') 168 | this.output = '' 169 | return output 170 | } 171 | } 172 | 173 | Decoder.prototype.update = function(input, flush) { 174 | for (var i = 0; i < input.length; i++) { 175 | this.readChar(input[i]) 176 | } 177 | var output = this.output 178 | this.output = '' 179 | if (flush) { 180 | output += this.finish() 181 | } 182 | return output 183 | } 184 | 185 | // Base32-encoded string goes in, decoded data comes out. 186 | function decode(input) { 187 | var decoder = new Decoder() 188 | var output = decoder.update(input, true) 189 | return output 190 | } 191 | 192 | function base32length(secret) { 193 | return decode(secret).length; 194 | } 195 | 196 | function saveOptions() { 197 | 198 | var options = {}; 199 | 200 | options.theme = parseInt($("input[name=theme]:checked").val(), 10); 201 | options.font_style = parseInt($("input[name=font_style]:checked").val(), 10); 202 | options.idle_timeout = parseInt($("#idle_timeout").val(), 10); 203 | 204 | var colors = rgb2hex($('#background-color').css("background-color")).replace("#", ''); 205 | colors = colors + rgb2hex($('#foreground-color').css("background-color")).replace("#", ''); 206 | options.basalt_colors = colors; 207 | 208 | if ($("#keysecret").val()) { 209 | options.label = $("#keyname").val(); 210 | options.secret = $("#keysecret").val().replace(/\s/g, ''); 211 | } 212 | 213 | return options; 214 | } 215 | 216 | $("document").ready(function() { 217 | var watch_version = getQueryParam("watch_version", 1); 218 | 219 | if (watch_version >= 3) { 220 | var basalt_colors = getQueryParam("basalt_colors", 0); 221 | basalt_colors = basalt_colors.length == 12 ? basalt_colors : "00AAFFFFFFFF"; 222 | $('#background-color').css('background-color', "#"+basalt_colors.substring(0,6)); 223 | $('#foreground-color').css('background-color', "#"+basalt_colors.substring(6,12)); 224 | $('#time-foreground').removeClass("hidden"); 225 | $('#time-background').removeClass("hidden"); 226 | } else { 227 | var theme = getQueryParam("theme", 0); 228 | theme = parseInt(isNumber(theme) && theme <= 1 ? theme : 0); 229 | $('#theme').removeClass("hidden"); 230 | $('#theme_'+theme).prop( "checked", true ).checkboxradio("refresh"); 231 | } 232 | 233 | var otp_count = getQueryParam("otp_count", 0); 234 | var font_style = getQueryParam("font_style", 0); 235 | var idle_timeout = getQueryParam("idle_timeout", 300); 236 | 237 | font_style = parseInt(isNumber(font_style) && font_style <= 3 ? font_style : 0); 238 | idle_timeout = parseInt(isNumber(idle_timeout) && (idle_timeout >= 0 || idle_timeout == 60 || idle_timeout == 120 || idle_timeout == 300 || idle_timeout == 600) ? idle_timeout : 300); 239 | 240 | $('#font_style_'+font_style).prop( "checked", true ).checkboxradio("refresh"); 241 | $("#idle_timeout").val(idle_timeout).selectmenu("refresh"); 242 | 243 | otp_count = parseInt(isNumber(otp_count) ? otp_count : 0); 244 | var slots_remaining = 16 - otp_count; 245 | 246 | if (slots_remaining === 0) { 247 | document.getElementById("keyname").disabled=true; 248 | document.getElementById("keysecret").disabled=true; 249 | } 250 | document.getElementById("footer").innerHTML=document.getElementById("footer").innerHTML.replace("NUM_SPACES", slots_remaining); 251 | }); -------------------------------------------------------------------------------- /html/v23/assets/Thumbs.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v23/assets/Thumbs.db -------------------------------------------------------------------------------- /html/v23/assets/og/font_style_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v23/assets/og/font_style_1.png -------------------------------------------------------------------------------- /html/v23/assets/og/font_style_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v23/assets/og/font_style_2.png -------------------------------------------------------------------------------- /html/v23/assets/og/font_style_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v23/assets/og/font_style_3.png -------------------------------------------------------------------------------- /html/v23/assets/og/font_style_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v23/assets/og/font_style_4.png -------------------------------------------------------------------------------- /html/v23/assets/og/multi_code_layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v23/assets/og/multi_code_layout.png -------------------------------------------------------------------------------- /html/v23/assets/time/font_style_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v23/assets/time/font_style_1.png -------------------------------------------------------------------------------- /html/v23/assets/time/font_style_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v23/assets/time/font_style_2.png -------------------------------------------------------------------------------- /html/v23/assets/time/font_style_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v23/assets/time/font_style_3.png -------------------------------------------------------------------------------- /html/v23/assets/time/font_style_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v23/assets/time/font_style_4.png -------------------------------------------------------------------------------- /html/v23/assets/time/multi_code_layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v23/assets/time/multi_code_layout.png -------------------------------------------------------------------------------- /html/v23/pebble-og.css: -------------------------------------------------------------------------------- 1 | .font_1_screenshot { 2 | background-image: url(assets/og/font_style_1.png); 3 | height: 168px; 4 | width: 144px; 5 | } 6 | .font_2_screenshot { 7 | background-image: url(assets/og/font_style_2.png); 8 | height: 168px; 9 | width: 144px; 10 | } 11 | .font_3_screenshot { 12 | background-image: url(assets/og/font_style_3.png); 13 | height: 168px; 14 | width: 144px; 15 | } 16 | .font_4_screenshot { 17 | background-image: url(assets/og/font_style_4.png); 18 | height: 168px; 19 | width: 144px; 20 | } 21 | .multi_code_layout_screenshot { 22 | background-image: url(assets/og/multi_code_layout.png); 23 | height: 168px; 24 | width: 144px; 25 | } -------------------------------------------------------------------------------- /html/v23/pebble-time.css: -------------------------------------------------------------------------------- 1 | .font_1_screenshot { 2 | background-image: url(assets/time/font_style_1.png); 3 | height: 168px; 4 | width: 144px; 5 | } 6 | .font_2_screenshot { 7 | background-image: url(assets/time/font_style_2.png); 8 | height: 168px; 9 | width: 144px; 10 | } 11 | .font_3_screenshot { 12 | background-image: url(assets/time/font_style_3.png); 13 | height: 168px; 14 | width: 144px; 15 | } 16 | .font_4_screenshot { 17 | background-image: url(assets/time/font_style_4.png); 18 | height: 168px; 19 | width: 144px; 20 | } 21 | .multi_code_layout_screenshot { 22 | background-image: url(assets/time/multi_code_layout.png); 23 | height: 168px; 24 | width: 144px; 25 | } -------------------------------------------------------------------------------- /html/v23/script.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | $('#foreground-color').on('click', function (event) { 3 | changeForeground = true; 4 | highlightColorBox($(this)); 5 | }); 6 | }); 7 | 8 | $(function () { 9 | $('#background-color').on('click', function (event) { 10 | changeForeground = false; 11 | highlightColorBox($(this)); 12 | }); 13 | }); 14 | 15 | $(function () { 16 | $('#color-picker .color-box').on('click', function (event) { 17 | var selected_color = rgb2hex($(this).css("background-color")); 18 | 19 | if (changeForeground) 20 | $('#foreground-color').css('background-color', selected_color); 21 | else 22 | $('#background-color').css('background-color', selected_color); 23 | $( "#color-picker" ).popup( "close" ); 24 | }); 25 | }); 26 | 27 | $(function () { 28 | $("#confirm_delete_all").click(function() { 29 | console.log("Deleting all"); 30 | var options = {}; 31 | options.delete_all = 1; 32 | var location = "pebblejs://close#" + encodeURIComponent(JSON.stringify(options)); 33 | document.location = location; 34 | }); 35 | }); 36 | 37 | $(function () { 38 | $("#cancel").click(function() { 39 | console.log("Cancelling"); 40 | document.location = "pebblejs://close"; 41 | }); 42 | }); 43 | 44 | $(function () { 45 | $("#save").click(function() { 46 | console.log("Submit"); 47 | 48 | var options = saveOptions(); 49 | 50 | if (options.secret && base32length(options.secret) < 10) { 51 | alert("Secret key value is too short"); 52 | } 53 | else if (options.secret && options.label.length === 0) { 54 | alert("Please enter a label"); 55 | } 56 | else { 57 | var return_to = getQueryParam('return_to', 'pebblejs://close#'); 58 | var location = return_to + encodeURIComponent(JSON.stringify(options)); 59 | console.log("Warping to:"); 60 | console.log(location); 61 | document.location = location; 62 | } 63 | }); 64 | }); 65 | 66 | function highlightColorBox($colorButton) { 67 | selectedColor = rgb2hex($colorButton.css("background-color")); 68 | 69 | var i; 70 | for (i = 1; i <= 13; i++) 71 | { 72 | $('#color-row'+i).children().removeClass("selected-color-box"); 73 | } 74 | $('#color-box-'+selectedColor.substring(1).toUpperCase()).addClass("selected-color-box"); 75 | } 76 | 77 | //Function to convert hex format to a rgb color 78 | function rgb2hex(rgb){ 79 | rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/); 80 | return ("#" + 81 | ("0" + parseInt(rgb[1],10).toString(16)).slice(-2) + 82 | ("0" + parseInt(rgb[2],10).toString(16)).slice(-2) + 83 | ("0" + parseInt(rgb[3],10).toString(16)).slice(-2)).toUpperCase(); 84 | } 85 | 86 | function queryObj() { 87 | var result = {}, keyValuePairs = location.search.slice(1).split('&'); 88 | 89 | keyValuePairs.forEach(function(keyValuePair) { 90 | keyValuePair = keyValuePair.split('='); 91 | result[keyValuePair[0]] = keyValuePair[1] || ''; 92 | }); 93 | 94 | return result; 95 | } 96 | 97 | function getQueryParam(variable, default_) { 98 | var query = location.search.substring(1); 99 | var vars = query.split('&'); 100 | for (var i = 0; i < vars.length; i++) { 101 | var pair = vars[i].split('='); 102 | if (pair[0] == variable) 103 | return decodeURIComponent(pair[1]); 104 | } 105 | return default_ || false; 106 | } 107 | 108 | function isNumber(n) { 109 | return !isNaN(parseFloat(n)) && isFinite(n); 110 | } 111 | 112 | // Base32 decoder from https://github.com/agnoster/base32-js/blob/master/lib/base32.js by Isaac Wolkerstorfer 113 | var alphabet = '0123456789abcdefghjkmnpqrtuvwxyz' 114 | var alias = { o:0, i:1, l:1, s:5 } 115 | 116 | var lookup = function() { 117 | var table = {} 118 | // Invert 'alphabet' 119 | for (var i = 0; i < alphabet.length; i++) { 120 | table[alphabet[i]] = i 121 | } 122 | // Splice in 'alias' 123 | for (var key in alias) { 124 | if (!alias.hasOwnProperty(key)) continue 125 | table[key] = table['' + alias[key]] 126 | } 127 | lookup = function() { return table } 128 | return table 129 | } 130 | 131 | function Decoder() { 132 | var skip = 0 // how many bits we have from the previous character 133 | var byte = 0 // current byte we're producing 134 | 135 | this.output = '' 136 | 137 | // Consume a character from the stream, store 138 | // the output in this.output. As before, better 139 | // to use update(). 140 | this.readChar = function(char) { 141 | if (typeof char != 'string'){ 142 | if (typeof char == 'number') { 143 | char = String.fromCharCode(char) 144 | } 145 | } 146 | char = char.toLowerCase() 147 | var val = lookup()[char] 148 | if (typeof val == 'undefined') { 149 | // character does not exist in our lookup table 150 | return // skip silently. An alternative would be: 151 | // throw Error('Could not find character "' + char + '" in lookup table.') 152 | } 153 | val <<= 3 // move to the high bits 154 | byte |= val >>> skip 155 | skip += 5 156 | if (skip >= 8) { 157 | // we have enough to preduce output 158 | this.output += String.fromCharCode(byte) 159 | skip -= 8 160 | if (skip > 0) byte = (val << (5 - skip)) & 255 161 | else byte = 0 162 | } 163 | 164 | } 165 | 166 | this.finish = function(check) { 167 | var output = this.output + (skip < 0 ? alphabet[bits >> 3] : '') + (check ? '$' : '') 168 | this.output = '' 169 | return output 170 | } 171 | } 172 | 173 | Decoder.prototype.update = function(input, flush) { 174 | for (var i = 0; i < input.length; i++) { 175 | this.readChar(input[i]) 176 | } 177 | var output = this.output 178 | this.output = '' 179 | if (flush) { 180 | output += this.finish() 181 | } 182 | return output 183 | } 184 | 185 | // Base32-encoded string goes in, decoded data comes out. 186 | function decode(input) { 187 | var decoder = new Decoder() 188 | var output = decoder.update(input, true) 189 | return output 190 | } 191 | 192 | function base32length(secret) { 193 | return decode(secret).length; 194 | } 195 | 196 | function saveOptions() { 197 | 198 | var options = {}; 199 | 200 | options.theme = parseInt($("input[name=theme]:checked").val(), 10); 201 | options.font_style = parseInt($("input[name=font_style]:checked").val(), 10); 202 | options.idle_timeout = parseInt($("#idle_timeout").val(), 10); 203 | options.window_layout = parseInt($("input[name=window_layout]:checked").val(), 10); 204 | 205 | var colors = rgb2hex($('#background-color').css("background-color")).replace("#", ''); 206 | colors = colors + rgb2hex($('#foreground-color').css("background-color")).replace("#", ''); 207 | options.basalt_colors = colors; 208 | 209 | if ($("#keysecret").val()) { 210 | options.label = $("#keyname").val(); 211 | options.secret = $("#keysecret").val().replace(/\s/g, ''); 212 | } 213 | 214 | return options; 215 | } 216 | 217 | $("document").ready(function() { 218 | var watch_version = getQueryParam("watch_version", 1); 219 | 220 | if (watch_version >= 3) { 221 | document.getElementById('pebble-stylesheet').setAttribute('href', 'pebble-time.css'); 222 | var basalt_colors = getQueryParam("basalt_colors", 0); 223 | basalt_colors = basalt_colors.length == 12 ? basalt_colors : "00AAFFFFFFFF"; 224 | $('#background-color').css('background-color', "#"+basalt_colors.substring(0,6)); 225 | $('#foreground-color').css('background-color', "#"+basalt_colors.substring(6,12)); 226 | $('#time-foreground').removeClass("hidden"); 227 | $('#time-background').removeClass("hidden"); 228 | } else { 229 | var theme = getQueryParam("theme", 0); 230 | theme = parseInt(isNumber(theme) && theme <= 1 ? theme : 0); 231 | $('#theme').removeClass("hidden"); 232 | $('#theme_'+theme).prop( "checked", true ).checkboxradio("refresh"); 233 | } 234 | 235 | var otp_count = getQueryParam("otp_count", 0); 236 | var font_style = getQueryParam("font_style", 0); 237 | var idle_timeout = getQueryParam("idle_timeout", 300); 238 | var window_layout = getQueryParam("window_layout", 0); 239 | 240 | font_style = parseInt(isNumber(font_style) && font_style <= 3 ? font_style : 0); 241 | idle_timeout = parseInt(isNumber(idle_timeout) && (idle_timeout >= 0 || idle_timeout == 60 || idle_timeout == 120 || idle_timeout == 300 || idle_timeout == 600) ? idle_timeout : 300); 242 | window_layout = parseInt(isNumber(window_layout) && window_layout <= 3 ? window_layout : 0); 243 | 244 | $('#font_style_'+font_style).prop( "checked", true ).checkboxradio("refresh"); 245 | $("#idle_timeout").val(idle_timeout).selectmenu("refresh"); 246 | $('#window_layout_'+window_layout).prop( "checked", true ).checkboxradio("refresh"); 247 | 248 | otp_count = parseInt(isNumber(otp_count) ? otp_count : 0); 249 | var slots_remaining = 16 - otp_count; 250 | 251 | if (slots_remaining === 0) { 252 | document.getElementById("keyname").disabled=true; 253 | document.getElementById("keysecret").disabled=true; 254 | } 255 | document.getElementById("footer").innerHTML=document.getElementById("footer").innerHTML.replace("NUM_SPACES", slots_remaining); 256 | }); -------------------------------------------------------------------------------- /html/v24/OLD/assets/Thumbs.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v24/OLD/assets/Thumbs.db -------------------------------------------------------------------------------- /html/v24/OLD/assets/og/font_style_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v24/OLD/assets/og/font_style_1.png -------------------------------------------------------------------------------- /html/v24/OLD/assets/og/font_style_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v24/OLD/assets/og/font_style_2.png -------------------------------------------------------------------------------- /html/v24/OLD/assets/og/font_style_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v24/OLD/assets/og/font_style_3.png -------------------------------------------------------------------------------- /html/v24/OLD/assets/og/font_style_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v24/OLD/assets/og/font_style_4.png -------------------------------------------------------------------------------- /html/v24/OLD/assets/og/multi_code_layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v24/OLD/assets/og/multi_code_layout.png -------------------------------------------------------------------------------- /html/v24/OLD/assets/time/font_style_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v24/OLD/assets/time/font_style_1.png -------------------------------------------------------------------------------- /html/v24/OLD/assets/time/font_style_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v24/OLD/assets/time/font_style_2.png -------------------------------------------------------------------------------- /html/v24/OLD/assets/time/font_style_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v24/OLD/assets/time/font_style_3.png -------------------------------------------------------------------------------- /html/v24/OLD/assets/time/font_style_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v24/OLD/assets/time/font_style_4.png -------------------------------------------------------------------------------- /html/v24/OLD/assets/time/multi_code_layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v24/OLD/assets/time/multi_code_layout.png -------------------------------------------------------------------------------- /html/v24/OLD/pebble-og.css: -------------------------------------------------------------------------------- 1 | .font_1_screenshot { 2 | background-image: url(assets/og/font_style_1.png); 3 | height: 168px; 4 | width: 144px; 5 | } 6 | .font_2_screenshot { 7 | background-image: url(assets/og/font_style_2.png); 8 | height: 168px; 9 | width: 144px; 10 | } 11 | .font_3_screenshot { 12 | background-image: url(assets/og/font_style_3.png); 13 | height: 168px; 14 | width: 144px; 15 | } 16 | .font_4_screenshot { 17 | background-image: url(assets/og/font_style_4.png); 18 | height: 168px; 19 | width: 144px; 20 | } 21 | .multi_code_layout_screenshot { 22 | background-image: url(assets/og/multi_code_layout.png); 23 | height: 168px; 24 | width: 144px; 25 | } -------------------------------------------------------------------------------- /html/v24/OLD/pebble-time.css: -------------------------------------------------------------------------------- 1 | .font_1_screenshot { 2 | background-image: url(assets/time/font_style_1.png); 3 | height: 168px; 4 | width: 144px; 5 | } 6 | .font_2_screenshot { 7 | background-image: url(assets/time/font_style_2.png); 8 | height: 168px; 9 | width: 144px; 10 | } 11 | .font_3_screenshot { 12 | background-image: url(assets/time/font_style_3.png); 13 | height: 168px; 14 | width: 144px; 15 | } 16 | .font_4_screenshot { 17 | background-image: url(assets/time/font_style_4.png); 18 | height: 168px; 19 | width: 144px; 20 | } 21 | .multi_code_layout_screenshot { 22 | background-image: url(assets/time/multi_code_layout.png); 23 | height: 168px; 24 | width: 144px; 25 | } -------------------------------------------------------------------------------- /html/v24/OLD/script.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | $('#foreground-color').on('click', function (event) { 3 | changeForeground = true; 4 | highlightColorBox($(this)); 5 | }); 6 | }); 7 | 8 | $(function () { 9 | $('#background-color').on('click', function (event) { 10 | changeForeground = false; 11 | highlightColorBox($(this)); 12 | }); 13 | }); 14 | 15 | $(function () { 16 | $('#color-picker .color-box').on('click', function (event) { 17 | var selected_color = rgb2hex($(this).css("background-color")); 18 | 19 | if (changeForeground) 20 | $('#foreground-color').css('background-color', selected_color); 21 | else 22 | $('#background-color').css('background-color', selected_color); 23 | $( "#color-picker" ).popup( "close" ); 24 | }); 25 | }); 26 | 27 | $(function () { 28 | $("#confirm_delete_all").click(function() { 29 | console.log("Deleting all"); 30 | var options = {}; 31 | options.delete_all = 1; 32 | var location = "pebblejs://close#" + encodeURIComponent(JSON.stringify(options)); 33 | document.location = location; 34 | }); 35 | }); 36 | 37 | $(function () { 38 | $("#cancel").click(function() { 39 | console.log("Cancelling"); 40 | document.location = "pebblejs://close"; 41 | }); 42 | }); 43 | 44 | $(function () { 45 | $("#save").click(function() { 46 | console.log("Submit"); 47 | 48 | var options = saveOptions(); 49 | 50 | if (options.secret && base32length(options.secret) < 10) { 51 | alert("Secret key value is too short"); 52 | } 53 | else if (options.secret && options.label.length === 0) { 54 | alert("Please enter a label"); 55 | } 56 | else { 57 | var return_to = getQueryParam('return_to', 'pebblejs://close#'); 58 | var location = return_to + encodeURIComponent(JSON.stringify(options)); 59 | console.log("Warping to:"); 60 | console.log(location); 61 | document.location = location; 62 | } 63 | }); 64 | }); 65 | 66 | function highlightColorBox($colorButton) { 67 | selectedColor = rgb2hex($colorButton.css("background-color")); 68 | 69 | var i; 70 | for (i = 1; i <= 13; i++) 71 | { 72 | $('#color-row'+i).children().removeClass("selected-color-box"); 73 | } 74 | $('#color-box-'+selectedColor.substring(1).toUpperCase()).addClass("selected-color-box"); 75 | } 76 | 77 | //Function to convert hex format to a rgb color 78 | function rgb2hex(rgb){ 79 | rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/); 80 | return ("#" + 81 | ("0" + parseInt(rgb[1],10).toString(16)).slice(-2) + 82 | ("0" + parseInt(rgb[2],10).toString(16)).slice(-2) + 83 | ("0" + parseInt(rgb[3],10).toString(16)).slice(-2)).toUpperCase(); 84 | } 85 | 86 | function queryObj() { 87 | var result = {}, keyValuePairs = location.search.slice(1).split('&'); 88 | 89 | keyValuePairs.forEach(function(keyValuePair) { 90 | keyValuePair = keyValuePair.split('='); 91 | result[keyValuePair[0]] = keyValuePair[1] || ''; 92 | }); 93 | 94 | return result; 95 | } 96 | 97 | function getQueryParam(variable, default_) { 98 | var query = location.search.substring(1); 99 | var vars = query.split('&'); 100 | for (var i = 0; i < vars.length; i++) { 101 | var pair = vars[i].split('='); 102 | if (pair[0] == variable) 103 | return decodeURIComponent(pair[1]); 104 | } 105 | return default_ || false; 106 | } 107 | 108 | function isNumber(n) { 109 | return !isNaN(parseFloat(n)) && isFinite(n); 110 | } 111 | 112 | // Base32 decoder from https://github.com/agnoster/base32-js/blob/master/lib/base32.js by Isaac Wolkerstorfer 113 | var alphabet = '0123456789abcdefghjkmnpqrtuvwxyz' 114 | var alias = { o:0, i:1, l:1, s:5 } 115 | 116 | var lookup = function() { 117 | var table = {} 118 | // Invert 'alphabet' 119 | for (var i = 0; i < alphabet.length; i++) { 120 | table[alphabet[i]] = i 121 | } 122 | // Splice in 'alias' 123 | for (var key in alias) { 124 | if (!alias.hasOwnProperty(key)) continue 125 | table[key] = table['' + alias[key]] 126 | } 127 | lookup = function() { return table } 128 | return table 129 | } 130 | 131 | function Decoder() { 132 | var skip = 0 // how many bits we have from the previous character 133 | var byte = 0 // current byte we're producing 134 | 135 | this.output = '' 136 | 137 | // Consume a character from the stream, store 138 | // the output in this.output. As before, better 139 | // to use update(). 140 | this.readChar = function(char) { 141 | if (typeof char != 'string'){ 142 | if (typeof char == 'number') { 143 | char = String.fromCharCode(char) 144 | } 145 | } 146 | char = char.toLowerCase() 147 | var val = lookup()[char] 148 | if (typeof val == 'undefined') { 149 | // character does not exist in our lookup table 150 | return // skip silently. An alternative would be: 151 | // throw Error('Could not find character "' + char + '" in lookup table.') 152 | } 153 | val <<= 3 // move to the high bits 154 | byte |= val >>> skip 155 | skip += 5 156 | if (skip >= 8) { 157 | // we have enough to preduce output 158 | this.output += String.fromCharCode(byte) 159 | skip -= 8 160 | if (skip > 0) byte = (val << (5 - skip)) & 255 161 | else byte = 0 162 | } 163 | 164 | } 165 | 166 | this.finish = function(check) { 167 | var output = this.output + (skip < 0 ? alphabet[bits >> 3] : '') + (check ? '$' : '') 168 | this.output = '' 169 | return output 170 | } 171 | } 172 | 173 | Decoder.prototype.update = function(input, flush) { 174 | for (var i = 0; i < input.length; i++) { 175 | this.readChar(input[i]) 176 | } 177 | var output = this.output 178 | this.output = '' 179 | if (flush) { 180 | output += this.finish() 181 | } 182 | return output 183 | } 184 | 185 | // Base32-encoded string goes in, decoded data comes out. 186 | function decode(input) { 187 | var decoder = new Decoder() 188 | var output = decoder.update(input, true) 189 | return output 190 | } 191 | 192 | function base32length(secret) { 193 | return decode(secret).length; 194 | } 195 | 196 | function saveOptions() { 197 | 198 | var options = {}; 199 | 200 | options.theme = parseInt($("input[name=theme]:checked").val(), 10); 201 | options.font_style = parseInt($("input[name=font_style]:checked").val(), 10); 202 | options.idle_timeout = parseInt($("#idle_timeout").val(), 10); 203 | options.window_layout = parseInt($("input[name=window_layout]:checked").val(), 10); 204 | 205 | var colors = rgb2hex($('#background-color').css("background-color")).replace("#", ''); 206 | colors = colors + rgb2hex($('#foreground-color').css("background-color")).replace("#", ''); 207 | options.basalt_colors = colors; 208 | 209 | if ($("#keysecret").val()) { 210 | options.label = $("#keyname").val(); 211 | options.secret = $("#keysecret").val().replace(/\s/g, ''); 212 | } 213 | 214 | return options; 215 | } 216 | 217 | $("document").ready(function() { 218 | var watch_version = getQueryParam("watch_version", 1); 219 | 220 | if (watch_version >= 3) { 221 | document.getElementById('pebble-stylesheet').setAttribute('href', 'pebble-time.css'); 222 | var basalt_colors = getQueryParam("basalt_colors", 0); 223 | basalt_colors = basalt_colors.length == 12 ? basalt_colors : "00AAFFFFFFFF"; 224 | $('#background-color').css('background-color', "#"+basalt_colors.substring(0,6)); 225 | $('#foreground-color').css('background-color', "#"+basalt_colors.substring(6,12)); 226 | $('#time-foreground').removeClass("hidden"); 227 | $('#time-background').removeClass("hidden"); 228 | } else { 229 | var theme = getQueryParam("theme", 0); 230 | theme = parseInt(isNumber(theme) && theme <= 1 ? theme : 0); 231 | $('#theme').removeClass("hidden"); 232 | $('#theme_'+theme).prop( "checked", true ).checkboxradio("refresh"); 233 | } 234 | 235 | var otp_count = getQueryParam("otp_count", 0); 236 | var font_style = getQueryParam("font_style", 0); 237 | var idle_timeout = getQueryParam("idle_timeout", 300); 238 | var window_layout = getQueryParam("window_layout", 0); 239 | 240 | font_style = parseInt(isNumber(font_style) && font_style <= 3 ? font_style : 0); 241 | idle_timeout = parseInt(isNumber(idle_timeout) && (idle_timeout >= 0 || idle_timeout == 60 || idle_timeout == 120 || idle_timeout == 300 || idle_timeout == 600) ? idle_timeout : 300); 242 | window_layout = parseInt(isNumber(window_layout) && window_layout <= 3 ? window_layout : 0); 243 | 244 | $('#font_style_'+font_style).prop( "checked", true ).checkboxradio("refresh"); 245 | $("#idle_timeout").val(idle_timeout).selectmenu("refresh"); 246 | $('#window_layout_'+window_layout).prop( "checked", true ).checkboxradio("refresh"); 247 | 248 | otp_count = parseInt(isNumber(otp_count) ? otp_count : 0); 249 | var slots_remaining = 16 - otp_count; 250 | 251 | if (slots_remaining === 0) { 252 | document.getElementById("keyname").disabled=true; 253 | document.getElementById("keysecret").disabled=true; 254 | } 255 | document.getElementById("footer").innerHTML=document.getElementById("footer").innerHTML.replace("NUM_SPACES", slots_remaining); 256 | }); -------------------------------------------------------------------------------- /html/v24/assets/Thumbs.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v24/assets/Thumbs.db -------------------------------------------------------------------------------- /html/v24/assets/og/font_style_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v24/assets/og/font_style_1.png -------------------------------------------------------------------------------- /html/v24/assets/og/font_style_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v24/assets/og/font_style_2.png -------------------------------------------------------------------------------- /html/v24/assets/og/font_style_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v24/assets/og/font_style_3.png -------------------------------------------------------------------------------- /html/v24/assets/og/font_style_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v24/assets/og/font_style_4.png -------------------------------------------------------------------------------- /html/v24/assets/og/multi_code_layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v24/assets/og/multi_code_layout.png -------------------------------------------------------------------------------- /html/v24/assets/og/screenshot_holder_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v24/assets/og/screenshot_holder_1.png -------------------------------------------------------------------------------- /html/v24/assets/og/screenshot_holder_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v24/assets/og/screenshot_holder_2.png -------------------------------------------------------------------------------- /html/v24/assets/time/font_style_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v24/assets/time/font_style_1.png -------------------------------------------------------------------------------- /html/v24/assets/time/font_style_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v24/assets/time/font_style_2.png -------------------------------------------------------------------------------- /html/v24/assets/time/font_style_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v24/assets/time/font_style_3.png -------------------------------------------------------------------------------- /html/v24/assets/time/font_style_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v24/assets/time/font_style_4.png -------------------------------------------------------------------------------- /html/v24/assets/time/multi_code_layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v24/assets/time/multi_code_layout.png -------------------------------------------------------------------------------- /html/v24/assets/time/screenshot_holder_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v24/assets/time/screenshot_holder_3.png -------------------------------------------------------------------------------- /html/v24/pebble-1.css: -------------------------------------------------------------------------------- 1 | .screenshot_holder { 2 | background-image: url(assets/og/screenshot_holder_1.png); 3 | padding-top: 104px; 4 | height: 372px; 5 | width: 236px; 6 | background-repeat: no-repeat; 7 | } 8 | 9 | .font_1_screenshot { 10 | background-image: url(assets/og/font_style_1.png); 11 | height: 168px; 12 | width: 144px; 13 | margin-left: auto; 14 | margin-right: auto; 15 | } 16 | 17 | .font_2_screenshot { 18 | background-image: url(assets/og/font_style_2.png); 19 | height: 168px; 20 | width: 144px; 21 | margin-left: auto; 22 | margin-right: auto; 23 | } 24 | .font_3_screenshot { 25 | background-image: url(assets/og/font_style_3.png); 26 | height: 168px; 27 | width: 144px; 28 | margin-left: auto; 29 | margin-right: auto; 30 | } 31 | .font_4_screenshot { 32 | background-image: url(assets/og/font_style_4.png); 33 | height: 168px; 34 | width: 144px; 35 | margin-left: auto; 36 | margin-right: auto; 37 | } 38 | 39 | .multi_code_layout_screenshot { 40 | background-image: url(assets/og/multi_code_layout.png); 41 | height: 168px; 42 | width: 144px; 43 | margin-left: auto; 44 | margin-right: auto; 45 | } -------------------------------------------------------------------------------- /html/v24/pebble-2.css: -------------------------------------------------------------------------------- 1 | .screenshot_holder { 2 | background-image: url(assets/og/screenshot_holder_2.png); 3 | padding-top: 104px; 4 | height: 372px; 5 | width: 236px; 6 | background-repeat: no-repeat; 7 | } 8 | 9 | .font_1_screenshot { 10 | background-image: url(assets/og/font_style_1.png); 11 | height: 168px; 12 | width: 144px; 13 | margin-left: auto; 14 | margin-right: auto; 15 | } 16 | 17 | .font_2_screenshot { 18 | background-image: url(assets/og/font_style_2.png); 19 | height: 168px; 20 | width: 144px; 21 | margin-left: auto; 22 | margin-right: auto; 23 | } 24 | .font_3_screenshot { 25 | background-image: url(assets/og/font_style_3.png); 26 | height: 168px; 27 | width: 144px; 28 | margin-left: auto; 29 | margin-right: auto; 30 | } 31 | .font_4_screenshot { 32 | background-image: url(assets/og/font_style_4.png); 33 | height: 168px; 34 | width: 144px; 35 | margin-left: auto; 36 | margin-right: auto; 37 | } 38 | 39 | .multi_code_layout_screenshot { 40 | background-image: url(assets/og/multi_code_layout.png); 41 | height: 168px; 42 | width: 144px; 43 | margin-left: auto; 44 | margin-right: auto; 45 | } -------------------------------------------------------------------------------- /html/v24/pebble-3.css: -------------------------------------------------------------------------------- 1 | .screenshot_holder { 2 | background-image: url(assets/time/screenshot_holder_3.png); 3 | padding-top: 91px; 4 | height: 244px; 5 | width: 244px; 6 | background-repeat: no-repeat; 7 | } 8 | 9 | .font_1_screenshot { 10 | background-image: url(assets/time/font_style_1.png); 11 | height: 168px; 12 | width: 144px; 13 | margin-left: auto; 14 | margin-right: auto; 15 | } 16 | 17 | .font_2_screenshot { 18 | background-image: url(assets/time/font_style_2.png); 19 | height: 168px; 20 | width: 144px; 21 | margin-left: auto; 22 | margin-right: auto; 23 | } 24 | .font_3_screenshot { 25 | background-image: url(assets/time/font_style_3.png); 26 | height: 168px; 27 | width: 144px; 28 | margin-left: auto; 29 | margin-right: auto; 30 | } 31 | .font_4_screenshot { 32 | background-image: url(assets/time/font_style_4.png); 33 | height: 168px; 34 | width: 144px; 35 | margin-left: auto; 36 | margin-right: auto; 37 | } 38 | 39 | .multi_code_layout_screenshot { 40 | background-image: url(assets/time/multi_code_layout.png); 41 | height: 168px; 42 | width: 144px; 43 | margin-left: auto; 44 | margin-right: auto; 45 | } -------------------------------------------------------------------------------- /html/v24/script.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | $('#foreground-color').on('click', function (event) { 3 | changeForeground = true; 4 | highlightColorBox($(this)); 5 | }); 6 | }); 7 | 8 | $(function () { 9 | $('#background-color').on('click', function (event) { 10 | changeForeground = false; 11 | highlightColorBox($(this)); 12 | }); 13 | }); 14 | 15 | $(function () { 16 | $('#color-picker .color-box').on('click', function (event) { 17 | var selected_color = rgb2hex($(this).css("background-color")); 18 | 19 | if (changeForeground) 20 | $('#foreground-color').css('background-color', selected_color); 21 | else 22 | $('#background-color').css('background-color', selected_color); 23 | $( "#color-picker" ).popup( "close" ); 24 | }); 25 | }); 26 | 27 | $(function () { 28 | $("#confirm_delete_all").click(function() { 29 | console.log("Deleting all"); 30 | var options = {}; 31 | options.delete_all = 1; 32 | var location = "pebblejs://close#" + encodeURIComponent(JSON.stringify(options)); 33 | document.location = location; 34 | }); 35 | }); 36 | 37 | $(function () { 38 | $("#cancel").click(function() { 39 | console.log("Cancelling"); 40 | document.location = "pebblejs://close"; 41 | }); 42 | }); 43 | 44 | $(function () { 45 | $("#save").click(function() { 46 | console.log("Submit"); 47 | 48 | var options = saveOptions(); 49 | 50 | if (options.secret && base32length(options.secret) < 10) { 51 | alert("Secret key value is too short"); 52 | } 53 | else if (options.secret && options.label.length === 0) { 54 | alert("Please enter a label"); 55 | } 56 | else { 57 | var return_to = getQueryParam('return_to', 'pebblejs://close#'); 58 | var location = return_to + encodeURIComponent(JSON.stringify(options)); 59 | console.log("Warping to:"); 60 | console.log(location); 61 | document.location = location; 62 | } 63 | }); 64 | }); 65 | 66 | function highlightColorBox($colorButton) { 67 | selectedColor = rgb2hex($colorButton.css("background-color")); 68 | 69 | var i; 70 | for (i = 1; i <= 13; i++) 71 | { 72 | $('#color-row'+i).children().removeClass("selected-color-box"); 73 | } 74 | $('#color-box-'+selectedColor.substring(1).toUpperCase()).addClass("selected-color-box"); 75 | } 76 | 77 | //Function to convert hex format to a rgb color 78 | function rgb2hex(rgb){ 79 | rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/); 80 | return ("#" + 81 | ("0" + parseInt(rgb[1],10).toString(16)).slice(-2) + 82 | ("0" + parseInt(rgb[2],10).toString(16)).slice(-2) + 83 | ("0" + parseInt(rgb[3],10).toString(16)).slice(-2)).toUpperCase(); 84 | } 85 | 86 | function queryObj() { 87 | var result = {}, keyValuePairs = location.search.slice(1).split('&'); 88 | 89 | keyValuePairs.forEach(function(keyValuePair) { 90 | keyValuePair = keyValuePair.split('='); 91 | result[keyValuePair[0]] = keyValuePair[1] || ''; 92 | }); 93 | 94 | return result; 95 | } 96 | 97 | function getQueryParam(variable, default_) { 98 | var query = location.search.substring(1); 99 | var vars = query.split('&'); 100 | for (var i = 0; i < vars.length; i++) { 101 | var pair = vars[i].split('='); 102 | if (pair[0] == variable) 103 | return decodeURIComponent(pair[1]); 104 | } 105 | return default_ || false; 106 | } 107 | 108 | function isNumber(n) { 109 | return !isNaN(parseFloat(n)) && isFinite(n); 110 | } 111 | 112 | // Base32 decoder from https://github.com/agnoster/base32-js/blob/master/lib/base32.js by Isaac Wolkerstorfer 113 | var alphabet = '0123456789abcdefghjkmnpqrtuvwxyz' 114 | var alias = { o:0, i:1, l:1, s:5 } 115 | 116 | var lookup = function() { 117 | var table = {} 118 | // Invert 'alphabet' 119 | for (var i = 0; i < alphabet.length; i++) { 120 | table[alphabet[i]] = i 121 | } 122 | // Splice in 'alias' 123 | for (var key in alias) { 124 | if (!alias.hasOwnProperty(key)) continue 125 | table[key] = table['' + alias[key]] 126 | } 127 | lookup = function() { return table } 128 | return table 129 | } 130 | 131 | function Decoder() { 132 | var skip = 0 // how many bits we have from the previous character 133 | var byte = 0 // current byte we're producing 134 | 135 | this.output = '' 136 | 137 | // Consume a character from the stream, store 138 | // the output in this.output. As before, better 139 | // to use update(). 140 | this.readChar = function(char) { 141 | if (typeof char != 'string'){ 142 | if (typeof char == 'number') { 143 | char = String.fromCharCode(char) 144 | } 145 | } 146 | char = char.toLowerCase() 147 | var val = lookup()[char] 148 | if (typeof val == 'undefined') { 149 | // character does not exist in our lookup table 150 | return // skip silently. An alternative would be: 151 | // throw Error('Could not find character "' + char + '" in lookup table.') 152 | } 153 | val <<= 3 // move to the high bits 154 | byte |= val >>> skip 155 | skip += 5 156 | if (skip >= 8) { 157 | // we have enough to preduce output 158 | this.output += String.fromCharCode(byte) 159 | skip -= 8 160 | if (skip > 0) byte = (val << (5 - skip)) & 255 161 | else byte = 0 162 | } 163 | 164 | } 165 | 166 | this.finish = function(check) { 167 | var output = this.output + (skip < 0 ? alphabet[bits >> 3] : '') + (check ? '$' : '') 168 | this.output = '' 169 | return output 170 | } 171 | } 172 | 173 | Decoder.prototype.update = function(input, flush) { 174 | for (var i = 0; i < input.length; i++) { 175 | this.readChar(input[i]) 176 | } 177 | var output = this.output 178 | this.output = '' 179 | if (flush) { 180 | output += this.finish() 181 | } 182 | return output 183 | } 184 | 185 | // Base32-encoded string goes in, decoded data comes out. 186 | function decode(input) { 187 | var decoder = new Decoder() 188 | var output = decoder.update(input, true) 189 | return output 190 | } 191 | 192 | function base32length(secret) { 193 | return decode(secret).length; 194 | } 195 | 196 | function saveOptions() { 197 | 198 | var options = {}; 199 | 200 | options.theme = parseInt($("input[name=theme]:checked").val(), 10); 201 | options.font_style = parseInt($("input[name=font_style]:checked").val(), 10); 202 | options.idle_timeout = parseInt($("#idle_timeout").val(), 10); 203 | options.window_layout = parseInt($("input[name=window_layout]:checked").val(), 10); 204 | 205 | var colors = rgb2hex($('#background-color').css("background-color")).replace("#", ''); 206 | colors = colors + rgb2hex($('#foreground-color').css("background-color")).replace("#", ''); 207 | options.basalt_colors = colors; 208 | 209 | if ($("#keysecret").val()) { 210 | options.label = $("#keyname").val(); 211 | options.secret = $("#keysecret").val().replace(/\W/g, ''); 212 | } 213 | 214 | return options; 215 | } 216 | 217 | $("document").ready(function() { 218 | var watch_version = getQueryParam("watch_version", 1); 219 | 220 | if (watch_version >= 3) { 221 | var basalt_colors = getQueryParam("basalt_colors", 0); 222 | basalt_colors = basalt_colors.length == 12 ? basalt_colors : "00AAFFFFFFFF"; 223 | $('#background-color').css('background-color', "#"+basalt_colors.substring(0,6)); 224 | $('#foreground-color').css('background-color', "#"+basalt_colors.substring(6,12)); 225 | $('#time-foreground').removeClass("hidden"); 226 | $('#time-background').removeClass("hidden"); 227 | } else { 228 | var theme = getQueryParam("theme", 0); 229 | theme = parseInt(isNumber(theme) && theme <= 1 ? theme : 0); 230 | $('#theme').removeClass("hidden"); 231 | $('#theme_'+theme).prop( "checked", true ).checkboxradio("refresh"); 232 | } 233 | 234 | if (watch_version >= 2 && watch_version <= 3) { 235 | document.getElementById('pebble-stylesheet').setAttribute('href', 'pebble-'+watch_version+'.css'); 236 | } else if (watch_version > 3) { 237 | document.getElementById('pebble-stylesheet').setAttribute('href', 'pebble-3.css'); 238 | } else { 239 | document.getElementById('pebble-stylesheet').setAttribute('href', 'pebble-1.css'); 240 | } 241 | 242 | var otp_count = getQueryParam("otp_count", 0); 243 | var font_style = getQueryParam("font_style", 0); 244 | var idle_timeout = getQueryParam("idle_timeout", 300); 245 | var window_layout = getQueryParam("window_layout", 0); 246 | 247 | font_style = parseInt(isNumber(font_style) && font_style <= 3 ? font_style : 0); 248 | idle_timeout = parseInt(isNumber(idle_timeout) && (idle_timeout >= 0 || idle_timeout == 60 || idle_timeout == 120 || idle_timeout == 300 || idle_timeout == 600) ? idle_timeout : 300); 249 | window_layout = parseInt(isNumber(window_layout) && window_layout <= 3 ? window_layout : 0); 250 | 251 | $('#font_style_'+font_style).prop( "checked", true ).checkboxradio("refresh"); 252 | $("#idle_timeout").val(idle_timeout).selectmenu("refresh"); 253 | $('#window_layout_'+window_layout).prop( "checked", true ).checkboxradio("refresh"); 254 | 255 | otp_count = parseInt(isNumber(otp_count) ? otp_count : 0); 256 | var slots_remaining = 16 - otp_count; 257 | 258 | if (slots_remaining === 0) { 259 | document.getElementById("keyname").disabled=true; 260 | document.getElementById("keysecret").disabled=true; 261 | } 262 | document.getElementById("footer").innerHTML=document.getElementById("footer").innerHTML.replace("NUM_SPACES", slots_remaining); 263 | }); -------------------------------------------------------------------------------- /html/v25/OLD/assets/Thumbs.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v25/OLD/assets/Thumbs.db -------------------------------------------------------------------------------- /html/v25/OLD/assets/og/font_style_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v25/OLD/assets/og/font_style_1.png -------------------------------------------------------------------------------- /html/v25/OLD/assets/og/font_style_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v25/OLD/assets/og/font_style_2.png -------------------------------------------------------------------------------- /html/v25/OLD/assets/og/font_style_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v25/OLD/assets/og/font_style_3.png -------------------------------------------------------------------------------- /html/v25/OLD/assets/og/font_style_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v25/OLD/assets/og/font_style_4.png -------------------------------------------------------------------------------- /html/v25/OLD/assets/og/multi_code_layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v25/OLD/assets/og/multi_code_layout.png -------------------------------------------------------------------------------- /html/v25/OLD/assets/time/font_style_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v25/OLD/assets/time/font_style_1.png -------------------------------------------------------------------------------- /html/v25/OLD/assets/time/font_style_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v25/OLD/assets/time/font_style_2.png -------------------------------------------------------------------------------- /html/v25/OLD/assets/time/font_style_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v25/OLD/assets/time/font_style_3.png -------------------------------------------------------------------------------- /html/v25/OLD/assets/time/font_style_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v25/OLD/assets/time/font_style_4.png -------------------------------------------------------------------------------- /html/v25/OLD/assets/time/multi_code_layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v25/OLD/assets/time/multi_code_layout.png -------------------------------------------------------------------------------- /html/v25/OLD/pebble-og.css: -------------------------------------------------------------------------------- 1 | .font_1_screenshot { 2 | background-image: url(assets/og/font_style_1.png); 3 | height: 168px; 4 | width: 144px; 5 | } 6 | .font_2_screenshot { 7 | background-image: url(assets/og/font_style_2.png); 8 | height: 168px; 9 | width: 144px; 10 | } 11 | .font_3_screenshot { 12 | background-image: url(assets/og/font_style_3.png); 13 | height: 168px; 14 | width: 144px; 15 | } 16 | .font_4_screenshot { 17 | background-image: url(assets/og/font_style_4.png); 18 | height: 168px; 19 | width: 144px; 20 | } 21 | .multi_code_layout_screenshot { 22 | background-image: url(assets/og/multi_code_layout.png); 23 | height: 168px; 24 | width: 144px; 25 | } -------------------------------------------------------------------------------- /html/v25/OLD/pebble-time.css: -------------------------------------------------------------------------------- 1 | .font_1_screenshot { 2 | background-image: url(assets/time/font_style_1.png); 3 | height: 168px; 4 | width: 144px; 5 | } 6 | .font_2_screenshot { 7 | background-image: url(assets/time/font_style_2.png); 8 | height: 168px; 9 | width: 144px; 10 | } 11 | .font_3_screenshot { 12 | background-image: url(assets/time/font_style_3.png); 13 | height: 168px; 14 | width: 144px; 15 | } 16 | .font_4_screenshot { 17 | background-image: url(assets/time/font_style_4.png); 18 | height: 168px; 19 | width: 144px; 20 | } 21 | .multi_code_layout_screenshot { 22 | background-image: url(assets/time/multi_code_layout.png); 23 | height: 168px; 24 | width: 144px; 25 | } -------------------------------------------------------------------------------- /html/v25/OLD/script.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | $('#foreground-color').on('click', function (event) { 3 | changeForeground = true; 4 | highlightColorBox($(this)); 5 | }); 6 | }); 7 | 8 | $(function () { 9 | $('#background-color').on('click', function (event) { 10 | changeForeground = false; 11 | highlightColorBox($(this)); 12 | }); 13 | }); 14 | 15 | $(function () { 16 | $('#color-picker .color-box').on('click', function (event) { 17 | var selected_color = rgb2hex($(this).css("background-color")); 18 | 19 | if (changeForeground) 20 | $('#foreground-color').css('background-color', selected_color); 21 | else 22 | $('#background-color').css('background-color', selected_color); 23 | $( "#color-picker" ).popup( "close" ); 24 | }); 25 | }); 26 | 27 | $(function () { 28 | $("#confirm_delete_all").click(function() { 29 | console.log("Deleting all"); 30 | var options = {}; 31 | options.delete_all = 1; 32 | var location = "pebblejs://close#" + encodeURIComponent(JSON.stringify(options)); 33 | document.location = location; 34 | }); 35 | }); 36 | 37 | $(function () { 38 | $("#cancel").click(function() { 39 | console.log("Cancelling"); 40 | document.location = "pebblejs://close"; 41 | }); 42 | }); 43 | 44 | $(function () { 45 | $("#save").click(function() { 46 | console.log("Submit"); 47 | 48 | var options = saveOptions(); 49 | 50 | if (options.secret && base32length(options.secret) < 10) { 51 | alert("Secret key value is too short"); 52 | } 53 | else if (options.secret && options.label.length === 0) { 54 | alert("Please enter a label"); 55 | } 56 | else { 57 | var return_to = getQueryParam('return_to', 'pebblejs://close#'); 58 | var location = return_to + encodeURIComponent(JSON.stringify(options)); 59 | console.log("Warping to:"); 60 | console.log(location); 61 | document.location = location; 62 | } 63 | }); 64 | }); 65 | 66 | function highlightColorBox($colorButton) { 67 | selectedColor = rgb2hex($colorButton.css("background-color")); 68 | 69 | var i; 70 | for (i = 1; i <= 13; i++) 71 | { 72 | $('#color-row'+i).children().removeClass("selected-color-box"); 73 | } 74 | $('#color-box-'+selectedColor.substring(1).toUpperCase()).addClass("selected-color-box"); 75 | } 76 | 77 | //Function to convert hex format to a rgb color 78 | function rgb2hex(rgb){ 79 | rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/); 80 | return ("#" + 81 | ("0" + parseInt(rgb[1],10).toString(16)).slice(-2) + 82 | ("0" + parseInt(rgb[2],10).toString(16)).slice(-2) + 83 | ("0" + parseInt(rgb[3],10).toString(16)).slice(-2)).toUpperCase(); 84 | } 85 | 86 | function queryObj() { 87 | var result = {}, keyValuePairs = location.search.slice(1).split('&'); 88 | 89 | keyValuePairs.forEach(function(keyValuePair) { 90 | keyValuePair = keyValuePair.split('='); 91 | result[keyValuePair[0]] = keyValuePair[1] || ''; 92 | }); 93 | 94 | return result; 95 | } 96 | 97 | function getQueryParam(variable, default_) { 98 | var query = location.search.substring(1); 99 | var vars = query.split('&'); 100 | for (var i = 0; i < vars.length; i++) { 101 | var pair = vars[i].split('='); 102 | if (pair[0] == variable) 103 | return decodeURIComponent(pair[1]); 104 | } 105 | return default_ || false; 106 | } 107 | 108 | function isNumber(n) { 109 | return !isNaN(parseFloat(n)) && isFinite(n); 110 | } 111 | 112 | // Base32 decoder from https://github.com/agnoster/base32-js/blob/master/lib/base32.js by Isaac Wolkerstorfer 113 | var alphabet = '0123456789abcdefghjkmnpqrtuvwxyz' 114 | var alias = { o:0, i:1, l:1, s:5 } 115 | 116 | var lookup = function() { 117 | var table = {} 118 | // Invert 'alphabet' 119 | for (var i = 0; i < alphabet.length; i++) { 120 | table[alphabet[i]] = i 121 | } 122 | // Splice in 'alias' 123 | for (var key in alias) { 124 | if (!alias.hasOwnProperty(key)) continue 125 | table[key] = table['' + alias[key]] 126 | } 127 | lookup = function() { return table } 128 | return table 129 | } 130 | 131 | function Decoder() { 132 | var skip = 0 // how many bits we have from the previous character 133 | var byte = 0 // current byte we're producing 134 | 135 | this.output = '' 136 | 137 | // Consume a character from the stream, store 138 | // the output in this.output. As before, better 139 | // to use update(). 140 | this.readChar = function(char) { 141 | if (typeof char != 'string'){ 142 | if (typeof char == 'number') { 143 | char = String.fromCharCode(char) 144 | } 145 | } 146 | char = char.toLowerCase() 147 | var val = lookup()[char] 148 | if (typeof val == 'undefined') { 149 | // character does not exist in our lookup table 150 | return // skip silently. An alternative would be: 151 | // throw Error('Could not find character "' + char + '" in lookup table.') 152 | } 153 | val <<= 3 // move to the high bits 154 | byte |= val >>> skip 155 | skip += 5 156 | if (skip >= 8) { 157 | // we have enough to preduce output 158 | this.output += String.fromCharCode(byte) 159 | skip -= 8 160 | if (skip > 0) byte = (val << (5 - skip)) & 255 161 | else byte = 0 162 | } 163 | 164 | } 165 | 166 | this.finish = function(check) { 167 | var output = this.output + (skip < 0 ? alphabet[bits >> 3] : '') + (check ? '$' : '') 168 | this.output = '' 169 | return output 170 | } 171 | } 172 | 173 | Decoder.prototype.update = function(input, flush) { 174 | for (var i = 0; i < input.length; i++) { 175 | this.readChar(input[i]) 176 | } 177 | var output = this.output 178 | this.output = '' 179 | if (flush) { 180 | output += this.finish() 181 | } 182 | return output 183 | } 184 | 185 | // Base32-encoded string goes in, decoded data comes out. 186 | function decode(input) { 187 | var decoder = new Decoder() 188 | var output = decoder.update(input, true) 189 | return output 190 | } 191 | 192 | function base32length(secret) { 193 | return decode(secret).length; 194 | } 195 | 196 | function saveOptions() { 197 | 198 | var options = {}; 199 | 200 | options.theme = parseInt($("input[name=theme]:checked").val(), 10); 201 | options.font_style = parseInt($("input[name=font_style]:checked").val(), 10); 202 | options.idle_timeout = parseInt($("#idle_timeout").val(), 10); 203 | options.window_layout = parseInt($("input[name=window_layout]:checked").val(), 10); 204 | 205 | var colors = rgb2hex($('#background-color').css("background-color")).replace("#", ''); 206 | colors = colors + rgb2hex($('#foreground-color').css("background-color")).replace("#", ''); 207 | options.basalt_colors = colors; 208 | 209 | if ($("#keysecret").val()) { 210 | options.label = $("#keyname").val(); 211 | options.secret = $("#keysecret").val().replace(/\s/g, ''); 212 | } 213 | 214 | return options; 215 | } 216 | 217 | $("document").ready(function() { 218 | var watch_version = getQueryParam("watch_version", 1); 219 | 220 | if (watch_version >= 3) { 221 | document.getElementById('pebble-stylesheet').setAttribute('href', 'pebble-time.css'); 222 | var basalt_colors = getQueryParam("basalt_colors", 0); 223 | basalt_colors = basalt_colors.length == 12 ? basalt_colors : "00AAFFFFFFFF"; 224 | $('#background-color').css('background-color', "#"+basalt_colors.substring(0,6)); 225 | $('#foreground-color').css('background-color', "#"+basalt_colors.substring(6,12)); 226 | $('#time-foreground').removeClass("hidden"); 227 | $('#time-background').removeClass("hidden"); 228 | } else { 229 | var theme = getQueryParam("theme", 0); 230 | theme = parseInt(isNumber(theme) && theme <= 1 ? theme : 0); 231 | $('#theme').removeClass("hidden"); 232 | $('#theme_'+theme).prop( "checked", true ).checkboxradio("refresh"); 233 | } 234 | 235 | var otp_count = getQueryParam("otp_count", 0); 236 | var font_style = getQueryParam("font_style", 0); 237 | var idle_timeout = getQueryParam("idle_timeout", 300); 238 | var window_layout = getQueryParam("window_layout", 0); 239 | 240 | font_style = parseInt(isNumber(font_style) && font_style <= 3 ? font_style : 0); 241 | idle_timeout = parseInt(isNumber(idle_timeout) && (idle_timeout >= 0 || idle_timeout == 60 || idle_timeout == 120 || idle_timeout == 300 || idle_timeout == 600) ? idle_timeout : 300); 242 | window_layout = parseInt(isNumber(window_layout) && window_layout <= 3 ? window_layout : 0); 243 | 244 | $('#font_style_'+font_style).prop( "checked", true ).checkboxradio("refresh"); 245 | $("#idle_timeout").val(idle_timeout).selectmenu("refresh"); 246 | $('#window_layout_'+window_layout).prop( "checked", true ).checkboxradio("refresh"); 247 | 248 | otp_count = parseInt(isNumber(otp_count) ? otp_count : 0); 249 | var slots_remaining = 16 - otp_count; 250 | 251 | if (slots_remaining === 0) { 252 | document.getElementById("keyname").disabled=true; 253 | document.getElementById("keysecret").disabled=true; 254 | } 255 | document.getElementById("footer").innerHTML=document.getElementById("footer").innerHTML.replace("NUM_SPACES", slots_remaining); 256 | }); -------------------------------------------------------------------------------- /html/v25/assets/Thumbs.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v25/assets/Thumbs.db -------------------------------------------------------------------------------- /html/v25/assets/og/font_style_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v25/assets/og/font_style_1.png -------------------------------------------------------------------------------- /html/v25/assets/og/font_style_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v25/assets/og/font_style_2.png -------------------------------------------------------------------------------- /html/v25/assets/og/font_style_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v25/assets/og/font_style_3.png -------------------------------------------------------------------------------- /html/v25/assets/og/font_style_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v25/assets/og/font_style_4.png -------------------------------------------------------------------------------- /html/v25/assets/og/multi_code_layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v25/assets/og/multi_code_layout.png -------------------------------------------------------------------------------- /html/v25/assets/og/screenshot_holder_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v25/assets/og/screenshot_holder_1.png -------------------------------------------------------------------------------- /html/v25/assets/og/screenshot_holder_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v25/assets/og/screenshot_holder_2.png -------------------------------------------------------------------------------- /html/v25/assets/time/font_style_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v25/assets/time/font_style_1.png -------------------------------------------------------------------------------- /html/v25/assets/time/font_style_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v25/assets/time/font_style_2.png -------------------------------------------------------------------------------- /html/v25/assets/time/font_style_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v25/assets/time/font_style_3.png -------------------------------------------------------------------------------- /html/v25/assets/time/font_style_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v25/assets/time/font_style_4.png -------------------------------------------------------------------------------- /html/v25/assets/time/multi_code_layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v25/assets/time/multi_code_layout.png -------------------------------------------------------------------------------- /html/v25/assets/time/screenshot_holder_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v25/assets/time/screenshot_holder_3.png -------------------------------------------------------------------------------- /html/v25/pebble-1.css: -------------------------------------------------------------------------------- 1 | .screenshot_holder { 2 | background-image: url(assets/og/screenshot_holder_1.png); 3 | padding-top: 104px; 4 | height: 372px; 5 | width: 236px; 6 | background-repeat: no-repeat; 7 | } 8 | 9 | .font_1_screenshot { 10 | background-image: url(assets/og/font_style_1.png); 11 | height: 168px; 12 | width: 144px; 13 | margin-left: auto; 14 | margin-right: auto; 15 | } 16 | 17 | .font_2_screenshot { 18 | background-image: url(assets/og/font_style_2.png); 19 | height: 168px; 20 | width: 144px; 21 | margin-left: auto; 22 | margin-right: auto; 23 | } 24 | .font_3_screenshot { 25 | background-image: url(assets/og/font_style_3.png); 26 | height: 168px; 27 | width: 144px; 28 | margin-left: auto; 29 | margin-right: auto; 30 | } 31 | .font_4_screenshot { 32 | background-image: url(assets/og/font_style_4.png); 33 | height: 168px; 34 | width: 144px; 35 | margin-left: auto; 36 | margin-right: auto; 37 | } 38 | 39 | .multi_code_layout_screenshot { 40 | background-image: url(assets/og/multi_code_layout.png); 41 | height: 168px; 42 | width: 144px; 43 | margin-left: auto; 44 | margin-right: auto; 45 | } -------------------------------------------------------------------------------- /html/v25/pebble-2.css: -------------------------------------------------------------------------------- 1 | .screenshot_holder { 2 | background-image: url(assets/og/screenshot_holder_2.png); 3 | padding-top: 104px; 4 | height: 372px; 5 | width: 236px; 6 | background-repeat: no-repeat; 7 | } 8 | 9 | .font_1_screenshot { 10 | background-image: url(assets/og/font_style_1.png); 11 | height: 168px; 12 | width: 144px; 13 | margin-left: auto; 14 | margin-right: auto; 15 | } 16 | 17 | .font_2_screenshot { 18 | background-image: url(assets/og/font_style_2.png); 19 | height: 168px; 20 | width: 144px; 21 | margin-left: auto; 22 | margin-right: auto; 23 | } 24 | .font_3_screenshot { 25 | background-image: url(assets/og/font_style_3.png); 26 | height: 168px; 27 | width: 144px; 28 | margin-left: auto; 29 | margin-right: auto; 30 | } 31 | .font_4_screenshot { 32 | background-image: url(assets/og/font_style_4.png); 33 | height: 168px; 34 | width: 144px; 35 | margin-left: auto; 36 | margin-right: auto; 37 | } 38 | 39 | .multi_code_layout_screenshot { 40 | background-image: url(assets/og/multi_code_layout.png); 41 | height: 168px; 42 | width: 144px; 43 | margin-left: auto; 44 | margin-right: auto; 45 | } -------------------------------------------------------------------------------- /html/v25/pebble-3.css: -------------------------------------------------------------------------------- 1 | .screenshot_holder { 2 | background-image: url(assets/time/screenshot_holder_3.png); 3 | padding-top: 91px; 4 | height: 244px; 5 | width: 244px; 6 | background-repeat: no-repeat; 7 | } 8 | 9 | .font_1_screenshot { 10 | background-image: url(assets/time/font_style_1.png); 11 | height: 168px; 12 | width: 144px; 13 | margin-left: auto; 14 | margin-right: auto; 15 | } 16 | 17 | .font_2_screenshot { 18 | background-image: url(assets/time/font_style_2.png); 19 | height: 168px; 20 | width: 144px; 21 | margin-left: auto; 22 | margin-right: auto; 23 | } 24 | .font_3_screenshot { 25 | background-image: url(assets/time/font_style_3.png); 26 | height: 168px; 27 | width: 144px; 28 | margin-left: auto; 29 | margin-right: auto; 30 | } 31 | .font_4_screenshot { 32 | background-image: url(assets/time/font_style_4.png); 33 | height: 168px; 34 | width: 144px; 35 | margin-left: auto; 36 | margin-right: auto; 37 | } 38 | 39 | .multi_code_layout_screenshot { 40 | background-image: url(assets/time/multi_code_layout.png); 41 | height: 168px; 42 | width: 144px; 43 | margin-left: auto; 44 | margin-right: auto; 45 | } -------------------------------------------------------------------------------- /html/v25/script.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | $('#foreground-color').on('click', function (event) { 3 | changeForeground = true; 4 | highlightColorBox($(this)); 5 | }); 6 | }); 7 | 8 | $(function () { 9 | $('#background-color').on('click', function (event) { 10 | changeForeground = false; 11 | highlightColorBox($(this)); 12 | }); 13 | }); 14 | 15 | $(function () { 16 | $('#color-picker .color-box').on('click', function (event) { 17 | var selected_color = rgb2hex($(this).css("background-color")); 18 | 19 | if (changeForeground) 20 | $('#foreground-color').css('background-color', selected_color); 21 | else 22 | $('#background-color').css('background-color', selected_color); 23 | $( "#color-picker" ).popup( "close" ); 24 | }); 25 | }); 26 | 27 | $(function () { 28 | $("#confirm_delete_all").click(function() { 29 | console.log("Deleting all"); 30 | var options = {}; 31 | options.delete_all = 1; 32 | var location = "pebblejs://close#" + encodeURIComponent(JSON.stringify(options)); 33 | document.location = location; 34 | }); 35 | }); 36 | 37 | $(function () { 38 | $("#cancel").click(function() { 39 | console.log("Cancelling"); 40 | document.location = "pebblejs://close"; 41 | }); 42 | }); 43 | 44 | $(function () { 45 | $("#save").click(function() { 46 | console.log("Submit"); 47 | 48 | var options = saveOptions(); 49 | 50 | if (options.secret && base32length(options.secret) < 10) { 51 | alert("Secret key value is too short"); 52 | } 53 | else if (options.secret && options.label.length === 0) { 54 | alert("Please enter a label"); 55 | } 56 | else { 57 | var return_to = getQueryParam('return_to', 'pebblejs://close#'); 58 | var location = return_to + encodeURIComponent(JSON.stringify(options)); 59 | console.log("Warping to:"); 60 | console.log(location); 61 | document.location = location; 62 | } 63 | }); 64 | }); 65 | 66 | function highlightColorBox($colorButton) { 67 | selectedColor = rgb2hex($colorButton.css("background-color")); 68 | 69 | var i; 70 | for (i = 1; i <= 13; i++) 71 | { 72 | $('#color-row'+i).children().removeClass("selected-color-box"); 73 | } 74 | $('#color-box-'+selectedColor.substring(1).toUpperCase()).addClass("selected-color-box"); 75 | } 76 | 77 | //Function to convert hex format to a rgb color 78 | function rgb2hex(rgb){ 79 | rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/); 80 | return ("#" + 81 | ("0" + parseInt(rgb[1],10).toString(16)).slice(-2) + 82 | ("0" + parseInt(rgb[2],10).toString(16)).slice(-2) + 83 | ("0" + parseInt(rgb[3],10).toString(16)).slice(-2)).toUpperCase(); 84 | } 85 | 86 | function queryObj() { 87 | var result = {}, keyValuePairs = location.search.slice(1).split('&'); 88 | 89 | keyValuePairs.forEach(function(keyValuePair) { 90 | keyValuePair = keyValuePair.split('='); 91 | result[keyValuePair[0]] = keyValuePair[1] || ''; 92 | }); 93 | 94 | return result; 95 | } 96 | 97 | function getQueryParam(variable, default_) { 98 | var query = location.search.substring(1); 99 | var vars = query.split('&'); 100 | for (var i = 0; i < vars.length; i++) { 101 | var pair = vars[i].split('='); 102 | if (pair[0] == variable) 103 | return decodeURIComponent(pair[1]); 104 | } 105 | return default_ || false; 106 | } 107 | 108 | function isNumber(n) { 109 | return !isNaN(parseFloat(n)) && isFinite(n); 110 | } 111 | 112 | // Base32 decoder from https://github.com/agnoster/base32-js/blob/master/lib/base32.js by Isaac Wolkerstorfer 113 | var alphabet = '0123456789abcdefghjkmnpqrtuvwxyz' 114 | var alias = { o:0, i:1, l:1, s:5 } 115 | 116 | var lookup = function() { 117 | var table = {} 118 | // Invert 'alphabet' 119 | for (var i = 0; i < alphabet.length; i++) { 120 | table[alphabet[i]] = i 121 | } 122 | // Splice in 'alias' 123 | for (var key in alias) { 124 | if (!alias.hasOwnProperty(key)) continue 125 | table[key] = table['' + alias[key]] 126 | } 127 | lookup = function() { return table } 128 | return table 129 | } 130 | 131 | function Decoder() { 132 | var skip = 0 // how many bits we have from the previous character 133 | var byte = 0 // current byte we're producing 134 | 135 | this.output = '' 136 | 137 | // Consume a character from the stream, store 138 | // the output in this.output. As before, better 139 | // to use update(). 140 | this.readChar = function(char) { 141 | if (typeof char != 'string'){ 142 | if (typeof char == 'number') { 143 | char = String.fromCharCode(char) 144 | } 145 | } 146 | char = char.toLowerCase() 147 | var val = lookup()[char] 148 | if (typeof val == 'undefined') { 149 | // character does not exist in our lookup table 150 | return // skip silently. An alternative would be: 151 | // throw Error('Could not find character "' + char + '" in lookup table.') 152 | } 153 | val <<= 3 // move to the high bits 154 | byte |= val >>> skip 155 | skip += 5 156 | if (skip >= 8) { 157 | // we have enough to preduce output 158 | this.output += String.fromCharCode(byte) 159 | skip -= 8 160 | if (skip > 0) byte = (val << (5 - skip)) & 255 161 | else byte = 0 162 | } 163 | 164 | } 165 | 166 | this.finish = function(check) { 167 | var output = this.output + (skip < 0 ? alphabet[bits >> 3] : '') + (check ? '$' : '') 168 | this.output = '' 169 | return output 170 | } 171 | } 172 | 173 | Decoder.prototype.update = function(input, flush) { 174 | for (var i = 0; i < input.length; i++) { 175 | this.readChar(input[i]) 176 | } 177 | var output = this.output 178 | this.output = '' 179 | if (flush) { 180 | output += this.finish() 181 | } 182 | return output 183 | } 184 | 185 | // Base32-encoded string goes in, decoded data comes out. 186 | function decode(input) { 187 | var decoder = new Decoder() 188 | var output = decoder.update(input, true) 189 | return output 190 | } 191 | 192 | function base32length(secret) { 193 | return decode(secret).length; 194 | } 195 | 196 | function saveOptions() { 197 | 198 | var options = {}; 199 | 200 | options.theme = parseInt($("input[name=theme]:checked").val(), 10); 201 | options.font_style = parseInt($("input[name=font_style]:checked").val(), 10); 202 | options.idle_timeout = parseInt($("#idle_timeout").val(), 10); 203 | options.window_layout = parseInt($("input[name=window_layout]:checked").val(), 10); 204 | 205 | var colors = rgb2hex($('#background-color').css("background-color")).replace("#", ''); 206 | colors = colors + rgb2hex($('#foreground-color').css("background-color")).replace("#", ''); 207 | options.basalt_colors = colors; 208 | 209 | if ($("#keysecret").val()) { 210 | options.label = $("#keyname").val(); 211 | options.secret = $("#keysecret").val().replace(/\W/g, ''); 212 | } 213 | 214 | return options; 215 | } 216 | 217 | $("document").ready(function() { 218 | var watch_version = getQueryParam("watch_version", 1); 219 | 220 | if (watch_version >= 3) { 221 | var basalt_colors = getQueryParam("basalt_colors", 0); 222 | basalt_colors = basalt_colors.length == 12 ? basalt_colors : "00AAFFFFFFFF"; 223 | $('#background-color').css('background-color', "#"+basalt_colors.substring(0,6)); 224 | $('#foreground-color').css('background-color', "#"+basalt_colors.substring(6,12)); 225 | $('#time-foreground').removeClass("hidden"); 226 | $('#time-background').removeClass("hidden"); 227 | } else { 228 | var theme = getQueryParam("theme", 0); 229 | theme = parseInt(isNumber(theme) && theme <= 1 ? theme : 0); 230 | $('#theme').removeClass("hidden"); 231 | $('#theme_'+theme).prop( "checked", true ).checkboxradio("refresh"); 232 | } 233 | 234 | if (watch_version >= 2 && watch_version <= 3) { 235 | document.getElementById('pebble-stylesheet').setAttribute('href', 'pebble-'+watch_version+'.css'); 236 | } else if (watch_version > 3) { 237 | document.getElementById('pebble-stylesheet').setAttribute('href', 'pebble-3.css'); 238 | } else { 239 | document.getElementById('pebble-stylesheet').setAttribute('href', 'pebble-1.css'); 240 | } 241 | 242 | var otp_count = getQueryParam("otp_count", 0); 243 | var font_style = getQueryParam("font_style", 0); 244 | var idle_timeout = getQueryParam("idle_timeout", 300); 245 | var window_layout = getQueryParam("window_layout", 0); 246 | 247 | font_style = parseInt(isNumber(font_style) && font_style <= 3 ? font_style : 0); 248 | idle_timeout = parseInt(isNumber(idle_timeout) && (idle_timeout >= 0 || idle_timeout == 60 || idle_timeout == 120 || idle_timeout == 300 || idle_timeout == 600) ? idle_timeout : 300); 249 | window_layout = parseInt(isNumber(window_layout) && window_layout <= 3 ? window_layout : 0); 250 | 251 | $('#font_style_'+font_style).prop( "checked", true ).checkboxradio("refresh"); 252 | $("#idle_timeout").val(idle_timeout).selectmenu("refresh"); 253 | $('#window_layout_'+window_layout).prop( "checked", true ).checkboxradio("refresh"); 254 | 255 | otp_count = parseInt(isNumber(otp_count) ? otp_count : 0); 256 | var slots_remaining = 16 - otp_count; 257 | 258 | if (slots_remaining === 0) { 259 | document.getElementById("keyname").disabled=true; 260 | document.getElementById("keysecret").disabled=true; 261 | } 262 | document.getElementById("footer").innerHTML=document.getElementById("footer").innerHTML.replace("NUM_SPACES", slots_remaining); 263 | }); -------------------------------------------------------------------------------- /html/v4/assets/Thumbs.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v4/assets/Thumbs.db -------------------------------------------------------------------------------- /html/v4/assets/font_style_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v4/assets/font_style_1.png -------------------------------------------------------------------------------- /html/v4/assets/font_style_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v4/assets/font_style_2.png -------------------------------------------------------------------------------- /html/v4/assets/font_style_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v4/assets/font_style_3.png -------------------------------------------------------------------------------- /html/v4/assets/font_style_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v4/assets/font_style_4.png -------------------------------------------------------------------------------- /html/v4/index.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | PebbleAuth 5 | 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
17 | 21 |
22 | 23 | NEW VERSION RELEASED - CLICK HERE 24 | 25 |
26 | 27 |
28 | 29 |
30 | 31 |
32 | 33 |
34 |
35 | Global Theme 36 | 37 | 38 | 39 | 40 |
41 |
42 | 43 |
44 |
45 |
46 |
47 | Global Font Style 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 |
57 |
58 |
59 |
60 |
61 |
62 | HIDDEN 63 | Preview 64 | Preview 65 | Preview 66 | Preview 67 |
68 |
69 |
70 |
71 | 72 |
73 |
74 | 75 |
76 |
77 | 78 |
79 |
80 |
81 | 86 | 87 | 88 |
89 | Close 90 |

Welcome to PebbleAuth!

91 |

This application allows you to get your one time code codes straight from your Pebble Smartwatch.

92 |

Many websites support two-factor authentication to enhance your security including Facebook, Dropbox, AWS, and all of Google's services.

93 |

If you don't already have Google Authenticator installed on your smartphone I would recommend you install it as a backup. To get your codes using PebbleAuth simply go through the setup process on the website such as Facebook. You will either get a QR code to scan or be given a key which is a random 14-64 character long string. Scan the barcode or enter the key into your Google Authenticator app. If you were given a key you can enter this straight into the configuration page here. If you were given a QR code there'll usually be a link to get a key for "other devices". Enter this key into the the configuration page and you're good to go!

94 |

The font styles and theme are global and can be changed without entering a new key. The Name field can be up to 20 characters long although only Style 2 supports showing that many characters.

95 |

Happy Two-Factoring!

96 |
97 |
98 | Close 99 |

This version if out of date

100 |

A new version of PebbleAuth has been released. To update please unload PebbleAuth from the Pebble App, then load it again from your locker.

101 |

Don't worry. Your keys will not be removed in the process.

102 |
103 |
104 | Photo landscape 105 |
106 |
107 | Photo landscape 108 |
109 |
110 | Photo landscape 111 |
112 |
113 | Photo landscape 114 |
115 | 116 |
117 | 118 | 127 | 128 | -------------------------------------------------------------------------------- /html/v4/script.js: -------------------------------------------------------------------------------- 1 | 2 | function queryObj() { 3 | var result = {}, keyValuePairs = location.search.slice(1).split('&'); 4 | 5 | keyValuePairs.forEach(function(keyValuePair) { 6 | keyValuePair = keyValuePair.split('='); 7 | result[keyValuePair[0]] = keyValuePair[1] || ''; 8 | }); 9 | 10 | return result; 11 | } 12 | 13 | function isNumber(n) { 14 | return !isNaN(parseFloat(n)) && isFinite(n); 15 | } 16 | 17 | // Base32 decoder from https://github.com/agnoster/base32-js/blob/master/lib/base32.js by Isaac Wolkerstorfer 18 | var alphabet = '0123456789abcdefghjkmnpqrtuvwxyz' 19 | var alias = { o:0, i:1, l:1, s:5 } 20 | 21 | var lookup = function() { 22 | var table = {} 23 | // Invert 'alphabet' 24 | for (var i = 0; i < alphabet.length; i++) { 25 | table[alphabet[i]] = i 26 | } 27 | // Splice in 'alias' 28 | for (var key in alias) { 29 | if (!alias.hasOwnProperty(key)) continue 30 | table[key] = table['' + alias[key]] 31 | } 32 | lookup = function() { return table } 33 | return table 34 | } 35 | 36 | function Decoder() { 37 | var skip = 0 // how many bits we have from the previous character 38 | var byte = 0 // current byte we're producing 39 | 40 | this.output = '' 41 | 42 | // Consume a character from the stream, store 43 | // the output in this.output. As before, better 44 | // to use update(). 45 | this.readChar = function(char) { 46 | if (typeof char != 'string'){ 47 | if (typeof char == 'number') { 48 | char = String.fromCharCode(char) 49 | } 50 | } 51 | char = char.toLowerCase() 52 | var val = lookup()[char] 53 | if (typeof val == 'undefined') { 54 | // character does not exist in our lookup table 55 | return // skip silently. An alternative would be: 56 | // throw Error('Could not find character "' + char + '" in lookup table.') 57 | } 58 | val <<= 3 // move to the high bits 59 | byte |= val >>> skip 60 | skip += 5 61 | if (skip >= 8) { 62 | // we have enough to preduce output 63 | this.output += String.fromCharCode(byte) 64 | skip -= 8 65 | if (skip > 0) byte = (val << (5 - skip)) & 255 66 | else byte = 0 67 | } 68 | 69 | } 70 | 71 | this.finish = function(check) { 72 | var output = this.output + (skip < 0 ? alphabet[bits >> 3] : '') + (check ? '$' : '') 73 | this.output = '' 74 | return output 75 | } 76 | } 77 | 78 | Decoder.prototype.update = function(input, flush) { 79 | for (var i = 0; i < input.length; i++) { 80 | this.readChar(input[i]) 81 | } 82 | var output = this.output 83 | this.output = '' 84 | if (flush) { 85 | output += this.finish() 86 | } 87 | return output 88 | } 89 | 90 | // Base32-encoded string goes in, decoded data comes out. 91 | function decode(input) { 92 | var decoder = new Decoder() 93 | var output = decoder.update(input, true) 94 | return output 95 | } 96 | 97 | function base32length(secret) { 98 | return decode(secret).length; 99 | } 100 | 101 | function saveOptions() { 102 | 103 | var options = {}; 104 | 105 | options.theme = parseInt($("input[name=theme]:checked").val(), 10); 106 | options.font_style = parseInt($("input[name=font_style]:checked").val(), 10); 107 | 108 | if ($("#keysecret").val()) { 109 | options.label = $("#keyname").val(); 110 | options.secret = $("#keysecret").val().replace(/\s/g, ''); 111 | } 112 | 113 | return options; 114 | } 115 | 116 | $("document").ready(function() { 117 | $("#cancel").click(function() { 118 | console.log("Cancel"); 119 | document.location = "pebblejs://close"; 120 | }); 121 | 122 | $("#save").click(function() { 123 | console.log("Submit"); 124 | 125 | var options = saveOptions(); 126 | 127 | if (options.secret && base32length(options.secret) < 10) { 128 | alert("Secret key value is too short"); 129 | } 130 | else if (options.secret && options.label.length === 0) { 131 | alert("Please enter a label"); 132 | } 133 | else { 134 | var location = "pebblejs://close#" + encodeURIComponent(JSON.stringify(options)); 135 | console.log("Warping to:"); 136 | console.log(location); 137 | document.location = location; 138 | } 139 | }); 140 | 141 | var otp_count = queryObj()["otp_count"]; 142 | var slots_remaining = 0; 143 | var theme = queryObj()["theme"]; 144 | var font_style = queryObj()["font_style"]; 145 | 146 | theme = parseInt(isNumber(theme) && theme <= 1 ? theme : 0); 147 | font_style = parseInt(isNumber(font_style) && font_style <= 3 ? font_style : 0); 148 | 149 | $('#theme_'+theme).prop( "checked", true ).checkboxradio( "refresh" ); 150 | $('#font_style_'+font_style).prop( "checked", true ).checkboxradio( "refresh" ); 151 | 152 | otp_count = parseInt(isNumber(otp_count) ? otp_count : 0); 153 | slots_remaining = 16 - otp_count; 154 | 155 | if (slots_remaining === 0) { 156 | document.getElementById("keyname").disabled=true; 157 | document.getElementById("keysecret").disabled=true; 158 | } 159 | document.getElementById("footer").innerHTML=document.getElementById("footer").innerHTML.replace("NUM_SPACES", slots_remaining); 160 | }); -------------------------------------------------------------------------------- /html/v5/assets/Thumbs.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v5/assets/Thumbs.db -------------------------------------------------------------------------------- /html/v5/assets/font_style_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v5/assets/font_style_1.png -------------------------------------------------------------------------------- /html/v5/assets/font_style_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v5/assets/font_style_2.png -------------------------------------------------------------------------------- /html/v5/assets/font_style_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v5/assets/font_style_3.png -------------------------------------------------------------------------------- /html/v5/assets/font_style_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v5/assets/font_style_4.png -------------------------------------------------------------------------------- /html/v5/index.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | PebbleAuth 5 | 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
17 | 21 |
22 | 23 | 24 | 25 |
26 | 27 |
28 | 29 |
30 | 31 |
32 | 33 |
34 |
35 | Global Theme 36 | 37 | 38 | 39 | 40 |
41 |
42 | 43 |
44 |
45 |
46 |
47 | Global Font Style 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 |
57 |
58 |
59 |
60 |
61 |
62 | HIDDEN 63 | Preview 64 | Preview 65 | Preview 66 | Preview 67 |
68 |
69 |
70 |
71 | 72 |
73 |
74 | 75 |
76 |
77 | 78 |
79 |
80 |
81 | 86 | 87 | 88 |
89 | Close 90 |

Welcome to PebbleAuth!

91 |

This application allows you to get your one time code codes straight from your Pebble Smartwatch.

92 |

Many websites support two-factor authentication to enhance your security including Facebook, Dropbox, AWS, and all of Google's services.

93 |

If you don't already have Google Authenticator installed on your smartphone I would recommend you install it as a backup. To get your codes using PebbleAuth simply go through the setup process on the website such as Facebook. You will either get a QR code to scan or be given a key which is a random 14-64 character long string. Scan the barcode or enter the key into your Google Authenticator app. If you were given a key you can enter this straight into the configuration page here. If you were given a QR code there'll usually be a link to get a key for "other devices". Enter this key into the the configuration page and you're good to go!

94 |

The font styles and theme are global and can be changed without entering a new key. The Name field can be up to 20 characters long although only Style 2 supports showing that many characters.

95 |

Happy Two-Factoring!

96 |
97 |
98 | Close 99 |

This version if out of date

100 |

A new version of PebbleAuth has been released. To update please unload PebbleAuth from the Pebble App, then load it again from your locker.

101 |

Don't worry. Your keys will not be removed in the process.

102 |
103 |
104 | Photo landscape 105 |
106 |
107 | Photo landscape 108 |
109 |
110 | Photo landscape 111 |
112 |
113 | Photo landscape 114 |
115 | 116 |
117 | 118 | 127 | 128 | -------------------------------------------------------------------------------- /html/v5/script.js: -------------------------------------------------------------------------------- 1 | 2 | function queryObj() { 3 | var result = {}, keyValuePairs = location.search.slice(1).split('&'); 4 | 5 | keyValuePairs.forEach(function(keyValuePair) { 6 | keyValuePair = keyValuePair.split('='); 7 | result[keyValuePair[0]] = keyValuePair[1] || ''; 8 | }); 9 | 10 | return result; 11 | } 12 | 13 | function isNumber(n) { 14 | return !isNaN(parseFloat(n)) && isFinite(n); 15 | } 16 | 17 | // Base32 decoder from https://github.com/agnoster/base32-js/blob/master/lib/base32.js by Isaac Wolkerstorfer 18 | var alphabet = '0123456789abcdefghjkmnpqrtuvwxyz' 19 | var alias = { o:0, i:1, l:1, s:5 } 20 | 21 | var lookup = function() { 22 | var table = {} 23 | // Invert 'alphabet' 24 | for (var i = 0; i < alphabet.length; i++) { 25 | table[alphabet[i]] = i 26 | } 27 | // Splice in 'alias' 28 | for (var key in alias) { 29 | if (!alias.hasOwnProperty(key)) continue 30 | table[key] = table['' + alias[key]] 31 | } 32 | lookup = function() { return table } 33 | return table 34 | } 35 | 36 | function Decoder() { 37 | var skip = 0 // how many bits we have from the previous character 38 | var byte = 0 // current byte we're producing 39 | 40 | this.output = '' 41 | 42 | // Consume a character from the stream, store 43 | // the output in this.output. As before, better 44 | // to use update(). 45 | this.readChar = function(char) { 46 | if (typeof char != 'string'){ 47 | if (typeof char == 'number') { 48 | char = String.fromCharCode(char) 49 | } 50 | } 51 | char = char.toLowerCase() 52 | var val = lookup()[char] 53 | if (typeof val == 'undefined') { 54 | // character does not exist in our lookup table 55 | return // skip silently. An alternative would be: 56 | // throw Error('Could not find character "' + char + '" in lookup table.') 57 | } 58 | val <<= 3 // move to the high bits 59 | byte |= val >>> skip 60 | skip += 5 61 | if (skip >= 8) { 62 | // we have enough to preduce output 63 | this.output += String.fromCharCode(byte) 64 | skip -= 8 65 | if (skip > 0) byte = (val << (5 - skip)) & 255 66 | else byte = 0 67 | } 68 | 69 | } 70 | 71 | this.finish = function(check) { 72 | var output = this.output + (skip < 0 ? alphabet[bits >> 3] : '') + (check ? '$' : '') 73 | this.output = '' 74 | return output 75 | } 76 | } 77 | 78 | Decoder.prototype.update = function(input, flush) { 79 | for (var i = 0; i < input.length; i++) { 80 | this.readChar(input[i]) 81 | } 82 | var output = this.output 83 | this.output = '' 84 | if (flush) { 85 | output += this.finish() 86 | } 87 | return output 88 | } 89 | 90 | // Base32-encoded string goes in, decoded data comes out. 91 | function decode(input) { 92 | var decoder = new Decoder() 93 | var output = decoder.update(input, true) 94 | return output 95 | } 96 | 97 | function base32length(secret) { 98 | return decode(secret).length; 99 | } 100 | 101 | function saveOptions() { 102 | 103 | var options = {}; 104 | 105 | options.theme = parseInt($("input[name=theme]:checked").val(), 10); 106 | options.font_style = parseInt($("input[name=font_style]:checked").val(), 10); 107 | 108 | if ($("#keysecret").val()) { 109 | options.label = $("#keyname").val(); 110 | options.secret = $("#keysecret").val().replace(/\s/g, ''); 111 | } 112 | 113 | return options; 114 | } 115 | 116 | $("document").ready(function() { 117 | $("#cancel").click(function() { 118 | console.log("Cancel"); 119 | document.location = "pebblejs://close"; 120 | }); 121 | 122 | $("#save").click(function() { 123 | console.log("Submit"); 124 | 125 | var options = saveOptions(); 126 | 127 | if (options.secret && base32length(options.secret) < 10) { 128 | alert("Secret key value is too short"); 129 | } 130 | else if (options.secret && options.label.length === 0) { 131 | alert("Please enter a label"); 132 | } 133 | else { 134 | var location = "pebblejs://close#" + encodeURIComponent(JSON.stringify(options)); 135 | console.log("Warping to:"); 136 | console.log(location); 137 | document.location = location; 138 | } 139 | }); 140 | 141 | var otp_count = queryObj()["otp_count"]; 142 | var slots_remaining = 0; 143 | var theme = queryObj()["theme"]; 144 | var font_style = queryObj()["font_style"]; 145 | 146 | theme = parseInt(isNumber(theme) && theme <= 1 ? theme : 0); 147 | font_style = parseInt(isNumber(font_style) && font_style <= 3 ? font_style : 0); 148 | 149 | $('#theme_'+theme).prop( "checked", true ).checkboxradio( "refresh" ); 150 | $('#font_style_'+font_style).prop( "checked", true ).checkboxradio( "refresh" ); 151 | 152 | otp_count = parseInt(isNumber(otp_count) ? otp_count : 0); 153 | slots_remaining = 16 - otp_count; 154 | 155 | if (slots_remaining === 0) { 156 | document.getElementById("keyname").disabled=true; 157 | document.getElementById("keysecret").disabled=true; 158 | } 159 | document.getElementById("footer").innerHTML=document.getElementById("footer").innerHTML.replace("NUM_SPACES", slots_remaining); 160 | }); -------------------------------------------------------------------------------- /html/v6/assets/Thumbs.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v6/assets/Thumbs.db -------------------------------------------------------------------------------- /html/v6/assets/font_style_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v6/assets/font_style_1.png -------------------------------------------------------------------------------- /html/v6/assets/font_style_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v6/assets/font_style_2.png -------------------------------------------------------------------------------- /html/v6/assets/font_style_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v6/assets/font_style_3.png -------------------------------------------------------------------------------- /html/v6/assets/font_style_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v6/assets/font_style_4.png -------------------------------------------------------------------------------- /html/v6/index.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | PebbleAuth 5 | 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
17 | 21 |
22 | 23 | 26 |
27 | 28 |
29 | 30 |
31 | 32 |
33 | 34 |
35 |
36 | Global Theme 37 | 38 | 39 | 40 | 41 |
42 |
43 |
44 | 45 | 52 |
53 | 54 |
55 |
56 |
57 |
58 | Global Font Style 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 |
68 |
69 |
70 |
71 |
72 |
73 | HIDDEN 74 | Preview 75 | Preview 76 | Preview 77 | Preview 78 |
79 |
80 |
81 |
82 |
83 |
84 | 85 |
86 |
87 | 88 |
89 |
90 | DELETE ALL 91 |
92 | 97 | 98 | 99 |
100 | Close 101 |

Welcome to PebbleAuth!

102 |

This application allows you to get your one time code codes straight from your Pebble Smartwatch.

103 |

Many websites support two-factor authentication to enhance your security including Facebook, Dropbox, AWS, and all of Google's services.

104 |

If you don't already have Google Authenticator installed on your smartphone I would recommend you install it as a backup. To get your codes using PebbleAuth simply go through the setup process on the website such as Facebook. You will either get a QR code to scan or be given a key which is a random 14-64 character long string. Scan the barcode or enter the key into your Google Authenticator app. If you were given a key you can enter this straight into the configuration page here. If you were given a QR code there'll usually be a link to get a key for "other devices". Enter this key into the the configuration page and you're good to go!

105 |

The font styles and theme are global and can be changed without entering a new key. The Name field can be up to 20 characters long although only Style 2 supports showing that many characters.

106 |

Happy Two-Factoring!

107 |
108 |
109 | Close 110 |

This version is out of date

111 |

A new version of PebbleAuth has been released. To update please unload PebbleAuth from the Pebble App, then load it again from your locker.

112 |

Don't worry. Your keys will not be removed in the process.

113 |
114 |
115 | Photo landscape 116 |
117 |
118 | Photo landscape 119 |
120 |
121 | Photo landscape 122 |
123 |
124 | Photo landscape 125 |
126 |
127 |

ARE YOU SURE?

128 |

This cannot be undone!

129 |
130 |
131 | Cancel 132 |
133 |
134 | 135 |
136 |
137 |
138 |
139 | 140 | 141 | 142 | 151 | 152 | -------------------------------------------------------------------------------- /html/v7/assets/Thumbs.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v7/assets/Thumbs.db -------------------------------------------------------------------------------- /html/v7/assets/font_style_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v7/assets/font_style_1.png -------------------------------------------------------------------------------- /html/v7/assets/font_style_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v7/assets/font_style_2.png -------------------------------------------------------------------------------- /html/v7/assets/font_style_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v7/assets/font_style_3.png -------------------------------------------------------------------------------- /html/v7/assets/font_style_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/html/v7/assets/font_style_4.png -------------------------------------------------------------------------------- /html/v7/index.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | PebbleAuth 5 | 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
17 | 21 |
22 | 23 | 26 |
27 | 28 |
29 | 30 |
31 | 32 |
33 | 34 |
35 |
36 | Global Theme 37 | 38 | 39 | 40 | 41 |
42 |
43 |
44 | 45 | 52 |
53 | 54 |
55 |
56 |
57 |
58 | Global Font Style 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 |
68 |
69 |
70 |
71 |
72 |
73 | HIDDEN 74 | Preview 75 | Preview 76 | Preview 77 | Preview 78 |
79 |
80 |
81 |
82 |
83 |
84 | 85 |
86 |
87 | 88 |
89 |
90 | DELETE ALL 91 |
92 | 97 | 98 | 99 |
100 | Close 101 |

Welcome to PebbleAuth!

102 |

This application allows you to get your one time code codes straight from your Pebble Smartwatch.

103 |

Many websites support two-factor authentication to enhance your security including Facebook, Dropbox, AWS, and all of Google's services.

104 |

If you don't already have Google Authenticator installed on your smartphone I would recommend you install it as a backup. To get your codes using PebbleAuth simply go through the setup process on the website such as Facebook. You will either get a QR code to scan or be given a key which is a random 14-64 character long string. Scan the barcode or enter the key into your Google Authenticator app. If you were given a key you can enter this straight into the configuration page here. If you were given a QR code there'll usually be a link to get a key for "other devices". Enter this key into the the configuration page and you're good to go!

105 |

The font styles and theme are global and can be changed without entering a new key. The Name field can be up to 20 characters long although only Style 2 supports showing that many characters.

106 |

Happy Two-Factoring!

107 |
108 |
109 | Close 110 |

This version is out of date

111 |

A new version of PebbleAuth has been released. To update please unload PebbleAuth from the Pebble App, then load it again from your locker.

112 |

Don't worry. Your keys will not be removed in the process.

113 |
114 |
115 | Photo landscape 116 |
117 |
118 | Photo landscape 119 |
120 |
121 | Photo landscape 122 |
123 |
124 | Photo landscape 125 |
126 |
127 |

ARE YOU SURE?

128 |

This cannot be undone!

129 |
130 |
131 | Cancel 132 |
133 |
134 | 135 |
136 |
137 |
138 |
139 | 140 | 141 | 142 | 151 | 152 | -------------------------------------------------------------------------------- /html/v7/script.js: -------------------------------------------------------------------------------- 1 | 2 | function queryObj() { 3 | var result = {}, keyValuePairs = location.search.slice(1).split('&'); 4 | 5 | keyValuePairs.forEach(function(keyValuePair) { 6 | keyValuePair = keyValuePair.split('='); 7 | result[keyValuePair[0]] = keyValuePair[1] || ''; 8 | }); 9 | 10 | return result; 11 | } 12 | 13 | function isNumber(n) { 14 | return !isNaN(parseFloat(n)) && isFinite(n); 15 | } 16 | 17 | // Base32 decoder from https://github.com/agnoster/base32-js/blob/master/lib/base32.js by Isaac Wolkerstorfer 18 | var alphabet = '0123456789abcdefghjkmnpqrtuvwxyz' 19 | var alias = { o:0, i:1, l:1, s:5 } 20 | 21 | var lookup = function() { 22 | var table = {} 23 | // Invert 'alphabet' 24 | for (var i = 0; i < alphabet.length; i++) { 25 | table[alphabet[i]] = i 26 | } 27 | // Splice in 'alias' 28 | for (var key in alias) { 29 | if (!alias.hasOwnProperty(key)) continue 30 | table[key] = table['' + alias[key]] 31 | } 32 | lookup = function() { return table } 33 | return table 34 | } 35 | 36 | function Decoder() { 37 | var skip = 0 // how many bits we have from the previous character 38 | var byte = 0 // current byte we're producing 39 | 40 | this.output = '' 41 | 42 | // Consume a character from the stream, store 43 | // the output in this.output. As before, better 44 | // to use update(). 45 | this.readChar = function(char) { 46 | if (typeof char != 'string'){ 47 | if (typeof char == 'number') { 48 | char = String.fromCharCode(char) 49 | } 50 | } 51 | char = char.toLowerCase() 52 | var val = lookup()[char] 53 | if (typeof val == 'undefined') { 54 | // character does not exist in our lookup table 55 | return // skip silently. An alternative would be: 56 | // throw Error('Could not find character "' + char + '" in lookup table.') 57 | } 58 | val <<= 3 // move to the high bits 59 | byte |= val >>> skip 60 | skip += 5 61 | if (skip >= 8) { 62 | // we have enough to preduce output 63 | this.output += String.fromCharCode(byte) 64 | skip -= 8 65 | if (skip > 0) byte = (val << (5 - skip)) & 255 66 | else byte = 0 67 | } 68 | 69 | } 70 | 71 | this.finish = function(check) { 72 | var output = this.output + (skip < 0 ? alphabet[bits >> 3] : '') + (check ? '$' : '') 73 | this.output = '' 74 | return output 75 | } 76 | } 77 | 78 | Decoder.prototype.update = function(input, flush) { 79 | for (var i = 0; i < input.length; i++) { 80 | this.readChar(input[i]) 81 | } 82 | var output = this.output 83 | this.output = '' 84 | if (flush) { 85 | output += this.finish() 86 | } 87 | return output 88 | } 89 | 90 | // Base32-encoded string goes in, decoded data comes out. 91 | function decode(input) { 92 | var decoder = new Decoder() 93 | var output = decoder.update(input, true) 94 | return output 95 | } 96 | 97 | function base32length(secret) { 98 | return decode(secret).length; 99 | } 100 | 101 | function saveOptions() { 102 | 103 | var options = {}; 104 | 105 | options.theme = parseInt($("input[name=theme]:checked").val(), 10); 106 | options.font_style = parseInt($("input[name=font_style]:checked").val(), 10); 107 | options.idle_timeout = parseInt($("#idle_timeout").val(), 10); 108 | console.log(options.idle_timeout); 109 | 110 | if ($("#keysecret").val()) { 111 | options.label = $("#keyname").val(); 112 | options.secret = $("#keysecret").val().replace(/\s/g, ''); 113 | } 114 | 115 | return options; 116 | } 117 | 118 | $("document").ready(function() { 119 | 120 | $("#delete_all").click(function() { 121 | var options = {}; 122 | options.delete_all = 1; 123 | var location = "pebblejs://close#" + encodeURIComponent(JSON.stringify(options)); 124 | document.location = location; 125 | }); 126 | 127 | $("#cancel").click(function() { 128 | console.log("Cancel"); 129 | document.location = "pebblejs://close"; 130 | }); 131 | 132 | $("#save").click(function() { 133 | console.log("Submit"); 134 | 135 | var options = saveOptions(); 136 | 137 | if (options.secret && base32length(options.secret) < 10) { 138 | alert("Secret key value is too short"); 139 | } 140 | else if (options.secret && options.label.length === 0) { 141 | alert("Please enter a label"); 142 | } 143 | else { 144 | var location = "pebblejs://close#" + encodeURIComponent(JSON.stringify(options)); 145 | console.log("Warping to:"); 146 | console.log(location); 147 | document.location = location; 148 | } 149 | }); 150 | 151 | var otp_count = queryObj()["otp_count"]; 152 | var slots_remaining = 0; 153 | var theme = queryObj()["theme"]; 154 | var font_style = queryObj()["font_style"]; 155 | var idle_timeout = queryObj()["idle_timeout"]; 156 | 157 | theme = parseInt(isNumber(theme) && theme <= 1 ? theme : 0); 158 | font_style = parseInt(isNumber(font_style) && font_style <= 3 ? font_style : 0); 159 | idle_timeout = parseInt(isNumber(idle_timeout) && (idle_timeout >= 0 || idle_timeout == 60 || idle_timeout == 120 || idle_timeout == 300 || idle_timeout == 600) ? idle_timeout : 300); 160 | 161 | $('#theme_'+theme).prop( "checked", true ).checkboxradio("refresh"); 162 | $('#font_style_'+font_style).prop( "checked", true ).checkboxradio("refresh"); 163 | $("#idle_timeout").val(idle_timeout).selectmenu("refresh"); 164 | 165 | otp_count = parseInt(isNumber(otp_count) ? otp_count : 0); 166 | slots_remaining = 16 - otp_count; 167 | 168 | if (slots_remaining === 0) { 169 | document.getElementById("keyname").disabled=true; 170 | document.getElementById("keysecret").disabled=true; 171 | } 172 | document.getElementById("footer").innerHTML=document.getElementById("footer").innerHTML.replace("NUM_SPACES", slots_remaining); 173 | }); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Kevin Cooper", 3 | "dependencies": { 4 | "pebble-clay": "^1.0.0" 5 | }, 6 | "keywords": [], 7 | "name": "quickauth", 8 | "pebble": { 9 | "capabilities": [ 10 | "configurable" 11 | ], 12 | "displayName": "QuickAuth", 13 | "enableMultiJS": true, 14 | "messageKeys": [ 15 | "key_count", 16 | "request_key", 17 | "transmit_key", 18 | "timezone", 19 | "show_message", 20 | "theme", 21 | "delete_key", 22 | "font", 23 | "idle_timeout", 24 | "theme_colors", 25 | "window_layout", 26 | "background_color", 27 | "foreground_color", 28 | "auth_name", 29 | "auth_key" 30 | ], 31 | "projectType": "native", 32 | "resources": { 33 | "media": [ 34 | { 35 | "file": "images/auth_icon.png", 36 | "menuIcon": true, 37 | "name": "IMAGE_ICON", 38 | "type": "png" 39 | }, 40 | { 41 | "file": "images/SimpleFadeTop.png", 42 | "name": "IMAGE_SIMPLE_FADE_TOP", 43 | "targetPlatforms": null, 44 | "type": "bitmap" 45 | }, 46 | { 47 | "file": "images/SimpleFadeBottom.png", 48 | "name": "IMAGE_SIMPLE_FADE_BOTTOM", 49 | "targetPlatforms": null, 50 | "type": "bitmap" 51 | }, 52 | { 53 | "file": "images/Pebble_Star2.png", 54 | "name": "PNG_IMAGE_ICON_STAR", 55 | "type": "png" 56 | }, 57 | { 58 | "file": "images/Pebble_Bin.png", 59 | "name": "PNG_IMAGE_ICON_TRASH", 60 | "type": "png" 61 | }, 62 | { 63 | "file": "images/Padlock_aplite.png", 64 | "name": "IMAGE_PADLOCK", 65 | "type": "png" 66 | }, 67 | { 68 | "file": "fonts/UNISPACE_20.ttf", 69 | "name": "FONT_UNISPACE_20", 70 | "type": "font" 71 | }, 72 | { 73 | "file": "fonts/ORBITRON_28.ttf", 74 | "name": "FONT_ORBITRON_28", 75 | "type": "font" 76 | }, 77 | { 78 | "file": "fonts/DS-DIGIB.TTF", 79 | "name": "FONT_DIGITAL_30", 80 | "type": "font" 81 | }, 82 | { 83 | "characterRegex": "[0-9]", 84 | "file": "fonts/DS-DIGIB.TTF", 85 | "name": "FONT_DIGITAL_42", 86 | "type": "font" 87 | }, 88 | { 89 | "file": "fonts/BITWISE_32.ttf", 90 | "name": "FONT_BITWISE_32", 91 | "type": "font" 92 | }, 93 | { 94 | "file": "fonts/BD_Cartoon_Shout.ttf", 95 | "name": "FONT_BD_CARTOON_20", 96 | "type": "font" 97 | }, 98 | { 99 | "characterRegex": "[0-9]", 100 | "file": "fonts/BD_Cartoon_Shout.ttf", 101 | "name": "FONT_BD_CARTOON_30", 102 | "type": "font" 103 | } 104 | ] 105 | }, 106 | "sdkVersion": "3", 107 | "targetPlatforms": [ 108 | "aplite", 109 | "basalt", 110 | "chalk", 111 | "diorite" 112 | ], 113 | "uuid": "1f4d9835-3b9a-4ddd-907e-41a25d06f19c", 114 | "watchapp": { 115 | "watchface": false 116 | } 117 | }, 118 | "version": "3.3.0" 119 | } 120 | -------------------------------------------------------------------------------- /resources/fonts/BD_Cartoon_Shout.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/resources/fonts/BD_Cartoon_Shout.ttf -------------------------------------------------------------------------------- /resources/fonts/BITWISE_32.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/resources/fonts/BITWISE_32.ttf -------------------------------------------------------------------------------- /resources/fonts/DS-DIGIB.TTF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/resources/fonts/DS-DIGIB.TTF -------------------------------------------------------------------------------- /resources/fonts/ORBITRON_28.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/resources/fonts/ORBITRON_28.ttf -------------------------------------------------------------------------------- /resources/fonts/UNISPACE_20.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/resources/fonts/UNISPACE_20.ttf -------------------------------------------------------------------------------- /resources/images/Padlock_aplite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/resources/images/Padlock_aplite.png -------------------------------------------------------------------------------- /resources/images/Padlock_aplite~color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/resources/images/Padlock_aplite~color.png -------------------------------------------------------------------------------- /resources/images/Pebble_Bin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/resources/images/Pebble_Bin.png -------------------------------------------------------------------------------- /resources/images/Pebble_Bin~color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/resources/images/Pebble_Bin~color.png -------------------------------------------------------------------------------- /resources/images/Pebble_Star2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/resources/images/Pebble_Star2.png -------------------------------------------------------------------------------- /resources/images/Pebble_Star2~color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/resources/images/Pebble_Star2~color.png -------------------------------------------------------------------------------- /resources/images/SimpleFadeBottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/resources/images/SimpleFadeBottom.png -------------------------------------------------------------------------------- /resources/images/SimpleFadeTop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/resources/images/SimpleFadeTop.png -------------------------------------------------------------------------------- /resources/images/auth_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JumpMaster/QuickAuth/1ec47bfd593959b251436e7481226f7913915a8c/resources/images/auth_icon.png -------------------------------------------------------------------------------- /src/c/base32.c: -------------------------------------------------------------------------------- 1 | // Base32 implementation 2 | // 3 | // Copyright 2010 Google Inc. 4 | // Author: Markus Gutschke 5 | // 6 | // Adapted for the Pebble Smartwatch 7 | // Author: Kevin Cooper 8 | // https://github.com/JumpMaster/PebbleAuth 9 | // 10 | // Licensed under the Apache License, Version 2.0 (the "License"); 11 | // you may not use this file except in compliance with the License. 12 | // You may obtain a copy of the License at 13 | // 14 | // http://www.apache.org/licenses/LICENSE-2.0 15 | // 16 | // Unless required by applicable law or agreed to in writing, software 17 | // distributed under the License is distributed on an "AS IS" BASIS, 18 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | // See the License for the specific language governing permissions and 20 | // limitations under the License. 21 | 22 | #include 23 | #include "base32.h" 24 | 25 | int base32_decode(const uint8_t *encoded, uint8_t *result, int bufSize) { 26 | int buffer = 0; 27 | int bitsLeft = 0; 28 | int count = 0; 29 | for (const uint8_t *ptr = encoded; count < bufSize && *ptr; ++ptr) { 30 | uint8_t ch = *ptr; 31 | if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == '-') { 32 | continue; 33 | } 34 | buffer <<= 5; 35 | 36 | // Deal with commonly mistyped characters 37 | if (ch == '0') { 38 | ch = 'O'; 39 | } else if (ch == '1') { 40 | ch = 'L'; 41 | } else if (ch == '8') { 42 | ch = 'B'; 43 | } 44 | 45 | // Look up one base32 digit 46 | if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')) { 47 | ch = (ch & 0x1F) - 1; 48 | } else if (ch >= '2' && ch <= '7') { 49 | ch -= '2' - 26; 50 | } else { 51 | return -1; 52 | } 53 | 54 | buffer |= ch; 55 | bitsLeft += 5; 56 | if (bitsLeft >= 8) { 57 | result[count++] = buffer >> (bitsLeft - 8); 58 | bitsLeft -= 8; 59 | } 60 | } 61 | if (count < bufSize) { 62 | result[count] = '\000'; 63 | } 64 | return count; 65 | } 66 | -------------------------------------------------------------------------------- /src/c/base32.h: -------------------------------------------------------------------------------- 1 | // Base32 implementation 2 | // 3 | // Copyright 2010 Google Inc. 4 | // Author: Markus Gutschke 5 | // 6 | // Adapted for the Pebble Smartwatch 7 | // Author: Kevin Cooper 8 | // https://github.com/JumpMaster/PebbleAuth 9 | // 10 | // Licensed under the Apache License, Version 2.0 (the "License"); 11 | // you may not use this file except in compliance with the License. 12 | // You may obtain a copy of the License at 13 | // 14 | // http://www.apache.org/licenses/LICENSE-2.0 15 | // 16 | // Unless required by applicable law or agreed to in writing, software 17 | // distributed under the License is distributed on an "AS IS" BASIS, 18 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | // See the License for the specific language governing permissions and 20 | // limitations under the License. 21 | // 22 | // Encode and decode from base32 encoding using the following alphabet: 23 | // ABCDEFGHIJKLMNOPQRSTUVWXYZ234567 24 | // This alphabet is documented in RFC 4668/3548 25 | // 26 | // We allow white-space and hyphens, but all other characters are considered 27 | // invalid. 28 | // 29 | // All functions return the number of output bytes or -1 on error. If the 30 | // output buffer is too small, the result will silently be truncated. 31 | 32 | #pragma once 33 | #include 34 | 35 | int base32_decode(const uint8_t *encoded, uint8_t *result, int bufSize) 36 | __attribute__((visibility("hidden"))); 37 | int base32_encode(const uint8_t *data, int length, uint8_t *result, 38 | int bufSize) 39 | __attribute__((visibility("hidden"))); -------------------------------------------------------------------------------- /src/c/display.h: -------------------------------------------------------------------------------- 1 | #include 2 | void draw_countdown_graphic(Layer **layer, GContext **ctx, bool on_screen); 3 | void create_single_code_screen_elements(Layer **layer, GRect *text_label_rect, GRect *text_pin_rect, TextLayer **text_label_layer); 4 | void set_textlayer_positions(int font, GRect *text_label_rect, GRect *text_pin_rect); -------------------------------------------------------------------------------- /src/c/display_round.c: -------------------------------------------------------------------------------- 1 | #if defined(PBL_ROUND) 2 | 3 | #include 4 | #include "display.h" 5 | #include "main.h" 6 | 7 | #define TIME_ANGLE(time) time * (TRIG_MAX_ANGLE / 30000) 8 | 9 | uint16_t thickness = 0; 10 | uint16_t c_angle = 0; 11 | 12 | void draw_countdown_graphic(Layer **layer, GContext **ctx, bool on_screen) { 13 | time_t temp = time(NULL); 14 | struct tm *tick_time = localtime(&temp); 15 | int seconds = ((tick_time->tm_sec % 30)*1000) + time_ms(NULL, NULL); 16 | 17 | if (on_screen && thickness != 7) 18 | thickness++; 19 | else if (!on_screen && thickness > 0) 20 | thickness--; 21 | 22 | uint16_t target = (seconds*((TRIG_MAX_ANGLE*100)/30000))/100; 23 | 24 | if (target >= c_angle) 25 | c_angle += ((target-c_angle)/6)+1; 26 | else 27 | c_angle -= ((c_angle-target)/6)+1; 28 | 29 | graphics_context_set_fill_color(*ctx, fg_color); 30 | graphics_fill_radial(*ctx, layer_get_bounds(*layer), GOvalScaleModeFitCircle, thickness, 0, c_angle); 31 | } 32 | 33 | void create_single_code_screen_elements(Layer **layer, GRect *text_label_rect, GRect *text_pin_rect, TextLayer **text_label_layer) { 34 | GRect display_bounds = layer_get_frame(*layer); 35 | 36 | text_label_rect->origin.x = 0; 37 | text_label_rect->origin.y = 150; 38 | text_label_rect->size.w = display_bounds.size.w; 39 | text_label_rect->size.h = 40; 40 | 41 | text_pin_rect->origin.x = 0; 42 | text_pin_rect->origin.y = 180; 43 | text_pin_rect->size.w = display_bounds.size.w; 44 | text_pin_rect->size.h = 50; 45 | 46 | GRect text_label_start = *text_label_rect; 47 | text_label_start.origin.x = display_bounds.size.w; 48 | *text_label_layer = text_layer_create(text_label_start); 49 | text_layer_set_background_color(*text_label_layer, GColorClear); 50 | text_layer_set_overflow_mode(*text_label_layer, GTextOverflowModeWordWrap); 51 | text_layer_set_text_alignment(*text_label_layer, GTextAlignmentCenter); 52 | } 53 | 54 | void set_textlayer_positions(int font, GRect *text_label_rect, GRect *text_pin_rect) { 55 | switch(font) 56 | { 57 | case 1 : 58 | text_label_rect->origin.y = 60; 59 | text_label_rect->size.h = 30; 60 | text_pin_rect->origin.y = 79; 61 | text_pin_rect->size.h = 50; 62 | break; 63 | case 2 : 64 | text_label_rect->origin.y = 57; 65 | text_label_rect->size.h = 40; 66 | text_pin_rect->origin.y = 79; 67 | text_pin_rect->size.h = 50; 68 | break; 69 | case 3 : 70 | text_label_rect->origin.y = 59; 71 | text_label_rect->size.h = 22; 72 | text_pin_rect->origin.y = 83; 73 | text_pin_rect->size.h = 50; 74 | break; 75 | default : 76 | text_label_rect->origin.y = 60; 77 | text_label_rect->size.h = 36; 78 | text_pin_rect->origin.y = 90; 79 | text_pin_rect->size.h = 85; 80 | break; 81 | } 82 | } 83 | 84 | 85 | #endif -------------------------------------------------------------------------------- /src/c/display_square.c: -------------------------------------------------------------------------------- 1 | #if defined(PBL_RECT) // && PBL_DISPLAY_WIDTH == 144 2 | 3 | #include 4 | #include "display.h" 5 | #include "main.h" 6 | 7 | uint16_t thickness = 0; 8 | uint16_t c_size = 0; 9 | 10 | void draw_countdown_graphic(Layer **layer, GContext **ctx, bool on_screen) { 11 | GRect display_bounds = layer_get_frame(*layer); 12 | time_t temp = time(NULL); 13 | struct tm *tick_time = localtime(&temp); 14 | int seconds = ((30 - (tick_time->tm_sec % 30))*1000) - time_ms(NULL, NULL); 15 | int target_width = (((display_bounds.size.w*100) / 30) * seconds) / 100000; 16 | 17 | if (target_width >= c_size) 18 | c_size += ((target_width-c_size)/6)+1; 19 | else 20 | c_size -= ((c_size-target_width)/6)+1; 21 | 22 | if (on_screen && thickness != 10) 23 | thickness++; 24 | else if (!on_screen && thickness > 0) 25 | thickness--; 26 | 27 | GRect countdown_rect = GRect(0, display_bounds.size.h-thickness, c_size, thickness); 28 | graphics_context_set_fill_color(*ctx, fg_color); 29 | graphics_fill_rect(*ctx, countdown_rect, 0, GCornerNone); 30 | } 31 | 32 | void create_single_code_screen_elements(Layer **layer, GRect *text_label_rect, GRect *text_pin_rect, TextLayer **text_label_layer) { 33 | GRect display_bounds = layer_get_frame(*layer); 34 | 35 | text_label_rect->origin.x = 2; 36 | text_label_rect->origin.y = 50; 37 | text_label_rect->size.w = display_bounds.size.w-2; 38 | text_label_rect->size.h = 40; 39 | 40 | text_pin_rect->origin.x = 0; 41 | text_pin_rect->origin.y = 80; 42 | text_pin_rect->size.w = display_bounds.size.w; 43 | text_pin_rect->size.h = 50; 44 | 45 | GRect text_label_start = *text_label_rect; 46 | text_label_start.origin.x = display_bounds.size.w; 47 | *text_label_layer = text_layer_create(text_label_start); 48 | text_layer_set_background_color(*text_label_layer, GColorClear); 49 | text_layer_set_overflow_mode(*text_label_layer, GTextOverflowModeWordWrap); 50 | text_layer_set_text_alignment(*text_label_layer, GTextAlignmentLeft); 51 | } 52 | 53 | void set_textlayer_positions(int font, GRect *text_label_rect, GRect *text_pin_rect) { 54 | switch(font) 55 | { 56 | case 1 : 57 | text_label_rect->origin.y = 55; 58 | text_label_rect->size.h = 30; 59 | text_pin_rect->origin.y = 76; 60 | text_pin_rect->size.h = 50; 61 | break; 62 | case 2 : 63 | text_label_rect->origin.y = 52; 64 | text_label_rect->size.h = 40; 65 | text_pin_rect->origin.y = 74; 66 | text_pin_rect->size.h = 50; 67 | break; 68 | case 3 : 69 | text_label_rect->origin.y = 54; 70 | text_label_rect->size.h = 22; 71 | text_pin_rect->origin.y = 78; 72 | text_pin_rect->size.h = 50; 73 | break; 74 | default : 75 | text_label_rect->origin.y = 50; 76 | text_label_rect->size.h = 36; 77 | text_pin_rect->origin.y = 80; 78 | text_pin_rect->size.h = 50; 79 | break; 80 | } 81 | } 82 | 83 | 84 | #endif -------------------------------------------------------------------------------- /src/c/dod_window.c: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2015 3 | // PebbAuth for the Pebble Smartwatch 4 | // Author: Kevin Cooper 5 | // https://github.com/JumpMaster/PebbleAuth 6 | // 7 | 8 | #include "pebble.h" 9 | #include "main.h" 10 | #include "select_window.h" 11 | #include "dod_window.h" 12 | 13 | static Window *dod_main_window; 14 | static TextLayer *dod_label_layer; 15 | static BitmapLayer *dod_icon_layer; 16 | static ActionBarLayer *dod_action_bar_layer; 17 | 18 | static GBitmap *dod_icon_bitmap, *dod_fav_bitmap, *dod_del_bitmap; 19 | 20 | static int s_key_id; 21 | 22 | void dod_actionbar_up_click_handler(ClickRecognizerRef recognizer, void *context) { 23 | resetIdleTime(); 24 | 25 | set_default_key(s_key_id, true); 26 | 27 | close_select_window(); 28 | window_stack_pop(true); 29 | } 30 | 31 | void dod_actionbar_down_click_handler(ClickRecognizerRef recognizer, void *context) { 32 | resetIdleTime(); 33 | 34 | request_delete(s_key_id); 35 | 36 | close_select_window(); 37 | window_stack_pop(true); 38 | } 39 | 40 | void dod_actionbar_config_provider(void *context) { 41 | window_single_click_subscribe(BUTTON_ID_UP, (ClickHandler) dod_actionbar_up_click_handler); 42 | window_single_click_subscribe(BUTTON_ID_DOWN, (ClickHandler) dod_actionbar_down_click_handler); 43 | } 44 | 45 | static void window_load(Window *window) { 46 | Layer *window_layer = window_get_root_layer(window); 47 | GRect bounds = layer_get_bounds(window_layer); 48 | 49 | dod_icon_bitmap = gbitmap_create_with_resource(RESOURCE_ID_IMAGE_PADLOCK); 50 | GRect bitmap_bounds = gbitmap_get_bounds(dod_icon_bitmap); 51 | dod_icon_layer = bitmap_layer_create(GRect((bounds.size.w / 2) - (bitmap_bounds.size.w / 2) - (ACTION_BAR_WIDTH / 2), 15, bitmap_bounds.size.w, bitmap_bounds.size.h)); 52 | 53 | 54 | bitmap_layer_set_bitmap(dod_icon_layer, dod_icon_bitmap); 55 | bitmap_layer_set_compositing_mode(dod_icon_layer, GCompOpSet); 56 | 57 | layer_add_child(window_layer, bitmap_layer_get_layer(dod_icon_layer)); 58 | 59 | dod_label_layer = text_layer_create(GRect(10, 15 + bitmap_bounds.size.h + 5, 124 - ACTION_BAR_WIDTH, bounds.size.h - (10 + bitmap_bounds.size.h + 15))); 60 | text_layer_set_text(dod_label_layer, DIALOG_CHOICE_WINDOW_MESSAGE); 61 | 62 | text_layer_set_text_color(dod_label_layer, COLOR_FALLBACK(fg_color, GColorBlack)); 63 | 64 | text_layer_set_background_color(dod_label_layer, GColorClear); 65 | text_layer_set_text_alignment(dod_label_layer, GTextAlignmentCenter); 66 | text_layer_set_font(dod_label_layer, fonts_get_system_font(FONT_KEY_GOTHIC_24_BOLD)); 67 | layer_add_child(window_layer, text_layer_get_layer(dod_label_layer)); 68 | 69 | dod_fav_bitmap = gbitmap_create_with_resource(RESOURCE_ID_PNG_IMAGE_ICON_STAR); 70 | dod_del_bitmap = gbitmap_create_with_resource(RESOURCE_ID_PNG_IMAGE_ICON_TRASH); 71 | 72 | dod_action_bar_layer = action_bar_layer_create(); 73 | action_bar_layer_set_icon(dod_action_bar_layer, BUTTON_ID_UP, dod_fav_bitmap); 74 | action_bar_layer_set_icon(dod_action_bar_layer, BUTTON_ID_DOWN, dod_del_bitmap); 75 | action_bar_layer_set_click_config_provider(dod_action_bar_layer, dod_actionbar_config_provider); 76 | action_bar_layer_add_to_window(dod_action_bar_layer, window); 77 | } 78 | 79 | static void window_unload(Window *window) { 80 | text_layer_destroy(dod_label_layer); 81 | action_bar_layer_destroy(dod_action_bar_layer); 82 | bitmap_layer_destroy(dod_icon_layer); 83 | 84 | gbitmap_destroy(dod_icon_bitmap); 85 | gbitmap_destroy(dod_fav_bitmap); 86 | gbitmap_destroy(dod_del_bitmap); 87 | 88 | window_destroy(window); 89 | dod_main_window = NULL; 90 | } 91 | 92 | void dod_window_push(int key_id) { 93 | if(!dod_main_window) { 94 | s_key_id = key_id; 95 | dod_main_window = window_create(); 96 | 97 | window_set_background_color(dod_main_window, COLOR_FALLBACK(bg_color, GColorWhite)); 98 | 99 | window_set_window_handlers(dod_main_window, (WindowHandlers) { 100 | .load = window_load, 101 | .unload = window_unload, 102 | }); 103 | } 104 | window_stack_push(dod_main_window, true); 105 | } -------------------------------------------------------------------------------- /src/c/dod_window.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2015 3 | // PebbAuth for the Pebble Smartwatch 4 | // Author: Kevin Cooper 5 | // https://github.com/JumpMaster/PebbleAuth 6 | // 7 | 8 | #pragma once 9 | 10 | #define DIALOG_CHOICE_WINDOW_MESSAGE "Default or delete?" 11 | 12 | void dod_window_push(int key_id); -------------------------------------------------------------------------------- /src/c/google-authenticator.c: -------------------------------------------------------------------------------- 1 | // Helper program to generate a new secret for use in two-factor 2 | // authentication. 3 | // 4 | // Copyright 2010 Google Inc. 5 | // Author: Markus Gutschke 6 | // 7 | // Adapted for the Pebble Smartwatch 8 | // Author: Kevin Cooper 9 | // https://github.com/JumpMaster/PebbleAuth 10 | // 11 | // Licensed under the Apache License, Version 2.0 (the "License"); 12 | // you may not use this file except in compliance with the License. 13 | // You may obtain a copy of the License at 14 | // 15 | // http://www.apache.org/licenses/LICENSE-2.0 16 | // 17 | // Unless required by applicable law or agreed to in writing, software 18 | // distributed under the License is distributed on an "AS IS" BASIS, 19 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | // See the License for the specific language governing permissions and 21 | // limitations under the License. 22 | 23 | #include "pebble.h" 24 | #include "base32.h" 25 | #include "hmac.h" 26 | #include "sha1.h" 27 | #include "google-authenticator.h" 28 | 29 | char *generateCode(const char *key, int timezone_offset) { 30 | 31 | #ifdef PBL_SDK_2 32 | long tm = (time(NULL) + (timezone_offset*60))/30; 33 | #else 34 | long tm = time(NULL)/30; 35 | #endif 36 | 37 | uint8_t challenge[8]; 38 | for (int i = 8; i--; tm >>= 8) { 39 | challenge[i] = tm; 40 | } 41 | 42 | // Estimated number of bytes needed to represent the decoded secret. Because 43 | // of white-space and separators, this is an upper bound of the real number, 44 | // which we later get as a return-value from base32_decode() 45 | int secretLen = (strlen(key) + 7)/8*BITS_PER_BASE32_CHAR; 46 | 47 | // Sanity check, that our secret will fixed into a reasonably-sized static 48 | // array. 49 | if (secretLen < 0 || secretLen > 100) { 50 | return "000000"; 51 | } 52 | 53 | // Decode secret from Base32 to a binary representation, and check that we 54 | // have at least one byte's worth of secret data. 55 | uint8_t secret[100]; 56 | if ((secretLen = base32_decode((const uint8_t *)key, secret, secretLen))<1) { 57 | return "000000"; 58 | } 59 | 60 | // Compute the HMAC_SHA1 of the secret and the challenge. 61 | uint8_t hash[SHA1_DIGEST_LENGTH]; 62 | hmac_sha1(secret, secretLen, challenge, 8, hash, SHA1_DIGEST_LENGTH); 63 | 64 | // Pick the offset where to sample our hash value for the actual verification 65 | // code. 66 | int offset = hash[SHA1_DIGEST_LENGTH - 1] & 0xF; 67 | 68 | // Compute the truncated hash in a byte-order independent loop. 69 | unsigned int truncatedHash = 0; 70 | for (int i = 0; i < 4; ++i) { 71 | truncatedHash <<= 8; 72 | truncatedHash |= hash[offset + i]; 73 | } 74 | 75 | // Truncate to a smaller number of digits. 76 | truncatedHash &= 0x7FFFFFFF; 77 | truncatedHash %= VERIFICATION_CODE_MODULUS; 78 | 79 | // Convert the truncatedHash int to a Char/String 80 | static char tokenText[7] = "000000"; 81 | 82 | for(int i = 5; i >= 0; i--) 83 | { 84 | tokenText[i] = '0' + (truncatedHash % 10); 85 | truncatedHash /= 10; 86 | } 87 | tokenText[6] = '\0'; 88 | 89 | return tokenText; 90 | } -------------------------------------------------------------------------------- /src/c/google-authenticator.h: -------------------------------------------------------------------------------- 1 | // Helper program to generate a new secret for use in two-factor 2 | // authentication. 3 | // 4 | // Copyright 2010 Google Inc. 5 | // Author: Markus Gutschke 6 | // 7 | // Adapted for the Pebble Smartwatch 8 | // Author: Kevin Cooper 9 | // https://github.com/JumpMaster/PebbleAuth 10 | // 11 | // Licensed under the Apache License, Version 2.0 (the "License"); 12 | // you may not use this file except in compliance with the License. 13 | // You may obtain a copy of the License at 14 | // 15 | // http://www.apache.org/licenses/LICENSE-2.0 16 | // 17 | // Unless required by applicable law or agreed to in writing, software 18 | // distributed under the License is distributed on an "AS IS" BASIS, 19 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | // See the License for the specific language governing permissions and 21 | // limitations under the License. 22 | 23 | #pragma once 24 | 25 | #include 26 | 27 | #define VERIFICATION_CODE_MODULUS (1000*1000) // Six digits 28 | #define BITS_PER_BASE32_CHAR 5 // Base32 expands space by 8/5 29 | 30 | char *generateCode(const char *key, int timezone_offset) 31 | __attribute__((visibility("hidden"))); -------------------------------------------------------------------------------- /src/c/hmac.c: -------------------------------------------------------------------------------- 1 | // HMAC_SHA1 implementation 2 | // 3 | // Copyright 2010 Google Inc. 4 | // Author: Markus Gutschke 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | #include 19 | 20 | #include "hmac.h" 21 | #include "sha1.h" 22 | 23 | void hmac_sha1(const uint8_t *key, int keyLength, 24 | const uint8_t *data, int dataLength, 25 | uint8_t *result, int resultLength) { 26 | SHA1_INFO ctx; 27 | uint8_t hashed_key[SHA1_DIGEST_LENGTH]; 28 | if (keyLength > 64) { 29 | // The key can be no bigger than 64 bytes. If it is, we'll hash it down to 30 | // 20 bytes. 31 | sha1_init(&ctx); 32 | sha1_update(&ctx, key, keyLength); 33 | sha1_final(&ctx, hashed_key); 34 | key = hashed_key; 35 | keyLength = SHA1_DIGEST_LENGTH; 36 | } 37 | 38 | // The key for the inner digest is derived from our key, by padding the key 39 | // the full length of 64 bytes, and then XOR'ing each byte with 0x36. 40 | uint8_t tmp_key[64]; 41 | for (int i = 0; i < keyLength; ++i) { 42 | tmp_key[i] = key[i] ^ 0x36; 43 | } 44 | memset(tmp_key + keyLength, 0x36, 64 - keyLength); 45 | 46 | // Compute inner digest 47 | sha1_init(&ctx); 48 | sha1_update(&ctx, tmp_key, 64); 49 | sha1_update(&ctx, data, dataLength); 50 | uint8_t sha[SHA1_DIGEST_LENGTH]; 51 | sha1_final(&ctx, sha); 52 | 53 | // The key for the outer digest is derived from our key, by padding the key 54 | // the full length of 64 bytes, and then XOR'ing each byte with 0x5C. 55 | for (int i = 0; i < keyLength; ++i) { 56 | tmp_key[i] = key[i] ^ 0x5C; 57 | } 58 | memset(tmp_key + keyLength, 0x5C, 64 - keyLength); 59 | 60 | // Compute outer digest 61 | sha1_init(&ctx); 62 | sha1_update(&ctx, tmp_key, 64); 63 | sha1_update(&ctx, sha, SHA1_DIGEST_LENGTH); 64 | sha1_final(&ctx, sha); 65 | 66 | // Copy result to output buffer and truncate or pad as necessary 67 | memset(result, 0, resultLength); 68 | if (resultLength > SHA1_DIGEST_LENGTH) { 69 | resultLength = SHA1_DIGEST_LENGTH; 70 | } 71 | memcpy(result, sha, resultLength); 72 | 73 | // Zero out all internal data structures 74 | memset(hashed_key, 0, sizeof(hashed_key)); 75 | memset(sha, 0, sizeof(sha)); 76 | memset(tmp_key, 0, sizeof(tmp_key)); 77 | } 78 | -------------------------------------------------------------------------------- /src/c/hmac.h: -------------------------------------------------------------------------------- 1 | // HMAC_SHA1 implementation 2 | // 3 | // Copyright 2010 Google Inc. 4 | // Author: Markus Gutschke 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | #pragma once 19 | #include 20 | 21 | void hmac_sha1(const uint8_t *key, int keyLength, 22 | const uint8_t *data, int dataLength, 23 | uint8_t *result, int resultLength) 24 | __attribute__((visibility("hidden"))); -------------------------------------------------------------------------------- /src/c/main.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2015 3 | // PebbAuth for the Pebble Smartwatch 4 | // Author: Kevin Cooper 5 | // https://github.com/JumpMaster/PebbleAuth 6 | // 7 | 8 | #pragma once 9 | #include "pebble.h" 10 | 11 | typedef struct { 12 | GFont font; 13 | bool isCustom; 14 | } AppFont; 15 | 16 | #define MAX_OTP 30 17 | #define MAX_LABEL_LENGTH 21 // 20 + termination 18 | #define MAX_KEY_LENGTH 129 // 128 + termination 19 | #define MAX_COMBINED_LENGTH MAX_LABEL_LENGTH+MAX_KEY_LENGTH 20 | #define APP_VERSION 33 21 | #define DEBUG false 22 | 23 | #define MyTupletCString(_key, _cstring) \ 24 | ((const Tuplet) { .type = TUPLE_CSTRING, .key = _key, .cstring = { .data = _cstring, .length = strlen(_cstring) + 1 }}) 25 | 26 | // Persistant Storage Keys 27 | enum { 28 | PS_TIMEZONE_KEY, 29 | PS_DEFAULT_KEY, 30 | PS_FONT, 31 | PS_IDLE_TIMEOUT, 32 | PS_FOREGROUND_COLOR, 33 | PS_BACKGROUND_COLOR, 34 | PS_WINDOW_LAYOUT, 35 | PS_SECRET = 0x40 // Needs 30 spaces, should always be last 36 | }; 37 | 38 | 39 | // Animation Directions 40 | enum { 41 | UP, 42 | DOWN, 43 | LEFT, 44 | RIGHT 45 | }; 46 | 47 | extern GColor bg_color; 48 | extern GColor fg_color; 49 | extern AppFont font_pin; 50 | extern AppFont font_label; 51 | 52 | extern char otp_labels[MAX_OTP][MAX_LABEL_LENGTH]; 53 | extern char otp_keys[MAX_OTP][MAX_KEY_LENGTH]; 54 | 55 | extern unsigned int font; 56 | extern unsigned int watch_otp_count; 57 | extern unsigned int otp_selected; 58 | extern unsigned int otp_update_tick; 59 | extern unsigned int otp_updated_at_tick; 60 | extern int timezone_offset; 61 | #if defined(PBL_PLATFORM_APLITE) 62 | static unsigned int countdown_refresh_time = 60; 63 | #else 64 | static unsigned int countdown_refresh_time = 30; 65 | #endif 66 | extern bool loading_complete; 67 | extern bool refresh_required; 68 | extern bool fonts_changed; 69 | extern bool colors_changed; 70 | 71 | void set_default_key(int key_id, bool force_refresh); 72 | void request_delete(int key_id); 73 | void resetIdleTime(); 74 | void switch_window_layout(); 75 | void animate_layer(Layer *layer, AnimationCurve curve, GRect *start, GRect *finish, int duration, AnimationStoppedHandler callback); 76 | void add_countdown_layer(struct Layer *window_layer); 77 | void set_countdown_layer_color(GColor color); 78 | void show_countdown_layer(); 79 | void hide_countdown_layer(); 80 | void move_key_position(unsigned int key_position, unsigned int new_position); 81 | void apply_new_colors(); -------------------------------------------------------------------------------- /src/c/multi_code_window.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "main.h" 3 | #include "multi_code_window.h" 4 | #include "google-authenticator.h" 5 | #include "select_window.h" 6 | #include "display.h" 7 | 8 | static GRect display_bounds; 9 | static MenuLayer *multi_code_menu_layer; 10 | static Layer *multi_code_graphics_layer; 11 | static Window *multi_code_main_window; 12 | int menu_cell_height = 0; 13 | int pin_origin_y = 0; 14 | bool multi_code_exiting = false; 15 | AppTimer *multi_code_graphics_timer; 16 | 17 | void multi_code_refresh_callback(void *data) { 18 | if (!multi_code_exiting) 19 | layer_mark_dirty(multi_code_graphics_layer); 20 | } 21 | 22 | static void update_graphics(Layer *layer, GContext *ctx) { 23 | draw_countdown_graphic(&layer, &ctx, true); 24 | if (!multi_code_exiting) 25 | multi_code_graphics_timer = app_timer_register(countdown_refresh_time, (AppTimerCallback) multi_code_refresh_callback, NULL); 26 | } 27 | 28 | static uint16_t multi_code_menu_get_num_rows_callback(MenuLayer *menu_layer, uint16_t section_index, void *context) { 29 | return watch_otp_count > 1 ? watch_otp_count : 1; 30 | } 31 | 32 | static void multi_code_menu_draw_row_callback(GContext *ctx, Layer *cell_layer, MenuIndex *cell_index, void *context) { 33 | GRect bounds = layer_get_bounds(cell_layer); 34 | 35 | graphics_context_set_fill_color(ctx, bg_color); 36 | graphics_context_set_text_color(ctx, fg_color); 37 | 38 | if (menu_cell_layer_is_highlighted(cell_layer)) { 39 | graphics_context_set_fill_color(ctx, fg_color); 40 | graphics_context_set_text_color(ctx, bg_color); 41 | graphics_fill_rect(ctx, bounds, 0, 0); 42 | } 43 | 44 | if (watch_otp_count >= 1) { 45 | graphics_draw_text(ctx, generateCode(otp_keys[cell_index->row], timezone_offset), font_pin.font, GRect(0, pin_origin_y, bounds.size.w, 30), GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL); 46 | graphics_draw_text(ctx, otp_labels[cell_index->row], fonts_get_system_font(FONT_KEY_GOTHIC_18), GRect(0, 30, bounds.size.w, 25), GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL); 47 | } else { 48 | graphics_draw_text(ctx, "123456", font_pin.font, GRect(0, pin_origin_y, bounds.size.w, 30), GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL); 49 | graphics_draw_text(ctx, "EMPTY", fonts_get_system_font(FONT_KEY_GOTHIC_18), GRect(0, 30, bounds.size.w, 25), GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL); 50 | } 51 | } 52 | 53 | static int16_t multi_code_menu_get_cell_height_callback(struct MenuLayer *menu_layer, MenuIndex *cell_index, void *context) { 54 | return MULTI_CODE_CELL_HEIGHT; 55 | } 56 | 57 | static void multi_code_menu_select_callback(struct MenuLayer *menu_layer, MenuIndex *cell_index, void *context) { 58 | resetIdleTime(); 59 | otp_selected = cell_index->row; 60 | push_select_window(otp_selected); 61 | } 62 | 63 | static void multi_code_menu_select_long_callback(struct MenuLayer *menu_layer, MenuIndex *cell_index, void *context) { 64 | resetIdleTime(); 65 | switch_window_layout(); 66 | } 67 | 68 | static void multi_code_menu_draw_header_callback(GContext *ctx, const Layer *cell_layer, uint16_t section_index, void *context) { 69 | menu_cell_basic_header_draw(ctx, cell_layer, NULL); 70 | } 71 | 72 | static int16_t multi_code_menu_get_header_height_callback(struct MenuLayer *menu_layer, uint16_t section_index, void *context) { 73 | return 0; 74 | } 75 | 76 | static uint16_t multi_code_menu_get_num_sections_callback(struct MenuLayer *menu_layer, void *context) { 77 | return 1; 78 | } 79 | 80 | static void multi_code_menu_selection_changed_callback(struct MenuLayer *menu_layer, MenuIndex new_index, MenuIndex old_index, void *callback_context) { 81 | if (!refresh_required) 82 | otp_selected = new_index.row; 83 | resetIdleTime(); 84 | } 85 | 86 | void multi_code_set_fonts(void) { 87 | fonts_changed = false; 88 | 89 | if (font_pin.isCustom) 90 | fonts_unload_custom_font(font_pin.font); 91 | 92 | switch(font) 93 | { 94 | case 1 : 95 | font_pin.font = fonts_get_system_font(FONT_KEY_BITHAM_34_MEDIUM_NUMBERS); 96 | font_pin.isCustom = false; 97 | pin_origin_y = 0; 98 | break; 99 | case 2 : 100 | font_pin.font = fonts_load_custom_font(resource_get_handle(RESOURCE_ID_FONT_DIGITAL_42)); 101 | font_pin.isCustom = true; 102 | pin_origin_y = -8; 103 | break; 104 | case 3 : 105 | font_pin.font = fonts_load_custom_font(resource_get_handle(RESOURCE_ID_FONT_BD_CARTOON_30)); 106 | font_pin.isCustom = true; 107 | pin_origin_y = 2; 108 | break; 109 | default : 110 | font = 0; 111 | font_pin.font = fonts_load_custom_font(resource_get_handle(RESOURCE_ID_FONT_BITWISE_32)); 112 | font_pin.isCustom = true; 113 | pin_origin_y = 0; 114 | break; 115 | } 116 | } 117 | 118 | 119 | void multi_code_apply_display_colors() { 120 | apply_new_colors(); 121 | window_set_background_color(multi_code_main_window, bg_color); 122 | menu_layer_set_normal_colors(multi_code_menu_layer, bg_color, fg_color); 123 | } 124 | 125 | void multi_code_window_second_tick(int seconds) { 126 | if (refresh_required) { 127 | menu_layer_reload_data(multi_code_menu_layer); 128 | menu_layer_set_selected_index(multi_code_menu_layer, MenuIndex(0, otp_selected), MenuRowAlignCenter, true); 129 | if(fonts_changed) 130 | multi_code_set_fonts(); 131 | if (colors_changed) 132 | multi_code_apply_display_colors(); 133 | refresh_required = false; 134 | } 135 | } 136 | 137 | static void multi_code_window_load(Window *window) { 138 | multi_code_exiting = false; 139 | Layer *window_layer = window_get_root_layer(window); 140 | display_bounds = layer_get_frame(window_layer); 141 | multi_code_set_fonts(); 142 | GRect menu_bounds = layer_get_bounds(window_layer); 143 | menu_bounds.size.h = (display_bounds.size.h - 10) - 2; 144 | multi_code_menu_layer = menu_layer_create(menu_bounds); 145 | multi_code_graphics_layer = layer_create(display_bounds); 146 | layer_set_update_proc(multi_code_graphics_layer, update_graphics); 147 | menu_layer_set_callbacks(multi_code_menu_layer, NULL, (MenuLayerCallbacks) { 148 | .get_num_rows = (MenuLayerGetNumberOfRowsInSectionsCallback)multi_code_menu_get_num_rows_callback, 149 | .draw_row = (MenuLayerDrawRowCallback)multi_code_menu_draw_row_callback, 150 | .get_cell_height = (MenuLayerGetCellHeightCallback)multi_code_menu_get_cell_height_callback, 151 | .select_click = (MenuLayerSelectCallback)multi_code_menu_select_callback, 152 | .select_long_click = (MenuLayerSelectCallback)multi_code_menu_select_long_callback, 153 | .draw_header = (MenuLayerDrawHeaderCallback)multi_code_menu_draw_header_callback, 154 | .get_header_height = (MenuLayerGetHeaderHeightCallback)multi_code_menu_get_header_height_callback, 155 | .get_num_sections = (MenuLayerGetNumberOfSectionsCallback)multi_code_menu_get_num_sections_callback, 156 | .selection_changed = (MenuLayerSelectionChangedCallback)multi_code_menu_selection_changed_callback, 157 | }); 158 | menu_layer_set_click_config_onto_window(multi_code_menu_layer, window); 159 | scroll_layer_set_shadow_hidden(menu_layer_get_scroll_layer(multi_code_menu_layer), true); 160 | layer_add_child(window_layer, menu_layer_get_layer(multi_code_menu_layer)); 161 | layer_add_child(window_layer, multi_code_graphics_layer); 162 | menu_layer_set_selected_index(multi_code_menu_layer, MenuIndex(0, otp_selected), MenuRowAlignCenter, false); 163 | multi_code_apply_display_colors(); 164 | } 165 | 166 | void multi_code_window_unload(Window *window) { 167 | multi_code_exiting = true; 168 | app_timer_cancel(multi_code_graphics_timer); 169 | menu_layer_destroy(multi_code_menu_layer); 170 | layer_destroy(multi_code_graphics_layer); 171 | window_destroy(multi_code_main_window); 172 | multi_code_main_window = NULL; 173 | } 174 | 175 | void multi_code_window_remove(void) { 176 | window_stack_remove(multi_code_main_window, false); 177 | } 178 | 179 | void multi_code_window_push(void) { 180 | if (!multi_code_main_window) { 181 | multi_code_main_window = window_create(); 182 | window_set_window_handlers(multi_code_main_window, (WindowHandlers) { 183 | .load = multi_code_window_load, 184 | .unload = multi_code_window_unload, 185 | }); 186 | } 187 | window_stack_push(multi_code_main_window, true); 188 | } -------------------------------------------------------------------------------- /src/c/multi_code_window.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define MULTI_CODE_CELL_HEIGHT 55 4 | 5 | void multi_code_window_push(void); 6 | void multi_code_window_remove(void); 7 | void multi_code_window_second_tick(int seconds); -------------------------------------------------------------------------------- /src/c/select_window.c: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2015 3 | // PebbAuth for the Pebble Smartwatch 4 | // Author: Kevin Cooper 5 | // https://github.com/JumpMaster/PebbleAuth 6 | // 7 | 8 | #include "pebble.h" 9 | #include "select_window.h" 10 | #include "main.h" 11 | #include "dod_window.h" 12 | 13 | Window *select_window; 14 | static MenuLayer *select_menu_layer; 15 | int selected_index = 0; 16 | int moving_index = 0; 17 | bool reorder_mode; 18 | 19 | static GBitmap *shadow_top; 20 | static GBitmap *shadow_bottom; 21 | 22 | void close_select_window() { 23 | window_stack_remove(select_window, false); 24 | } 25 | 26 | void toggle_reorder_mode(int index) { 27 | reorder_mode = !reorder_mode; 28 | 29 | if (reorder_mode) 30 | moving_index = index; 31 | else 32 | move_key_position(moving_index, index); 33 | menu_layer_reload_data(select_menu_layer); 34 | } 35 | 36 | static uint16_t select_menu_get_num_rows_callback(MenuLayer *menu_layer, uint16_t section_index, void *context) { 37 | return watch_otp_count; 38 | } 39 | 40 | static void select_menu_draw_row_callback(GContext *ctx, Layer *cell_layer, MenuIndex *cell_index, void *context) { 41 | GRect bounds = layer_get_bounds(cell_layer); 42 | 43 | #ifdef PBL_COLOR 44 | if (menu_cell_layer_is_highlighted(cell_layer)) { 45 | graphics_context_set_fill_color(ctx, fg_color); 46 | graphics_context_set_text_color(ctx, bg_color); 47 | graphics_fill_rect(ctx, bounds, 0, 0); 48 | } 49 | #endif 50 | 51 | if (reorder_mode) { 52 | // DRAW TEXT 53 | if (cell_index->row >= moving_index && cell_index->row < selected_index) 54 | menu_cell_basic_draw(ctx, cell_layer, otp_labels[(cell_index->row)+1], NULL, NULL); 55 | else if (cell_index->row <= moving_index && cell_index->row > selected_index) 56 | menu_cell_basic_draw(ctx, cell_layer, otp_labels[(cell_index->row)-1], NULL, NULL); 57 | else if (cell_index->row == selected_index) 58 | menu_cell_basic_draw(ctx, cell_layer, otp_labels[moving_index], NULL, NULL); 59 | else 60 | menu_cell_basic_draw(ctx, cell_layer, otp_labels[cell_index->row], NULL, NULL); 61 | 62 | // #ifdef PBL_PLATFORM_APLITE 63 | // graphics_context_set_compositing_mode(ctx, GCompOpClear); 64 | // #elif PBL_PLATFORM_BASALT 65 | graphics_context_set_compositing_mode(ctx, GCompOpSet); 66 | // #endif 67 | 68 | if (cell_index->row == (selected_index-1)) 69 | graphics_draw_bitmap_in_rect(ctx, shadow_top, GRect(0, bounds.size.h-SHADOW_HEIGHT, 144, SHADOW_HEIGHT)); 70 | else if (cell_index->row == (selected_index+1)) 71 | graphics_draw_bitmap_in_rect(ctx, shadow_bottom, GRect(0, 0, 144, SHADOW_HEIGHT)); 72 | } 73 | else 74 | menu_cell_basic_draw(ctx, cell_layer, otp_labels[cell_index->row], NULL, NULL); 75 | } 76 | 77 | static int16_t select_menu_get_cell_height_callback(struct MenuLayer *menu_layer, MenuIndex *cell_index, void *context) { 78 | return SELECT_WINDOW_CELL_HEIGHT; 79 | } 80 | 81 | static void select_menu_select_callback(struct MenuLayer *menu_layer, MenuIndex *cell_index, void *context) { 82 | resetIdleTime(); 83 | if (reorder_mode) 84 | toggle_reorder_mode(cell_index->row); 85 | else 86 | dod_window_push(cell_index->row); 87 | } 88 | 89 | static void select_menu_select_long_callback(struct MenuLayer *menu_layer, MenuIndex *cell_index, void *context) { 90 | toggle_reorder_mode(cell_index->row); 91 | } 92 | 93 | static void select_menu_draw_header_callback(GContext *ctx, const Layer *cell_layer, uint16_t section_index, void *context) { 94 | menu_cell_basic_header_draw(ctx, cell_layer, NULL); 95 | } 96 | 97 | static int16_t select_menu_get_header_height_callback(struct MenuLayer *menu_layer, uint16_t section_index, void *context) { 98 | return MENU_CELL_BASIC_HEADER_HEIGHT; 99 | } 100 | 101 | static uint16_t select_menu_get_num_sections_callback(struct MenuLayer *menu_layer, void *context) { 102 | return 1; 103 | } 104 | 105 | static void select_menu_selection_changed_callback(struct MenuLayer *menu_layer, MenuIndex new_index, MenuIndex old_index, void *callback_context) { 106 | resetIdleTime(); 107 | selected_index = new_index.row; 108 | } 109 | 110 | static void select_window_load(Window *window) { 111 | reorder_mode = false; 112 | Layer *window_layer = window_get_root_layer(window); 113 | GRect bounds = layer_get_bounds(window_layer); 114 | 115 | select_menu_layer = menu_layer_create(bounds); 116 | 117 | #ifdef PBL_COLOR 118 | menu_layer_set_normal_colors(select_menu_layer, bg_color, fg_color); 119 | #endif 120 | 121 | menu_layer_set_click_config_onto_window(select_menu_layer, window); 122 | menu_layer_set_callbacks(select_menu_layer, NULL, (MenuLayerCallbacks) { 123 | .get_num_rows = (MenuLayerGetNumberOfRowsInSectionsCallback)select_menu_get_num_rows_callback, 124 | .draw_row = (MenuLayerDrawRowCallback)select_menu_draw_row_callback, 125 | .get_cell_height = (MenuLayerGetCellHeightCallback)select_menu_get_cell_height_callback, 126 | .select_click = (MenuLayerSelectCallback)select_menu_select_callback, 127 | .select_long_click = (MenuLayerSelectCallback)select_menu_select_long_callback, 128 | .draw_header = (MenuLayerDrawHeaderCallback)select_menu_draw_header_callback, 129 | .get_header_height = (MenuLayerGetHeaderHeightCallback)select_menu_get_header_height_callback, 130 | .get_num_sections = (MenuLayerGetNumberOfSectionsCallback)select_menu_get_num_sections_callback, 131 | .selection_changed = (MenuLayerSelectionChangedCallback)select_menu_selection_changed_callback, 132 | }); 133 | menu_layer_set_selected_index(select_menu_layer, MenuIndex(0, otp_selected), MenuRowAlignCenter, false); 134 | scroll_layer_set_shadow_hidden(menu_layer_get_scroll_layer(select_menu_layer), true); 135 | layer_add_child(window_layer, menu_layer_get_layer(select_menu_layer)); 136 | 137 | // #ifdef PBL_PLATFORM_APLITE 138 | // shadow_top = gbitmap_create_with_resource(RESOURCE_ID_IMAGE_SIMPLE_FADE_TOP_BLACK); 139 | // shadow_bottom = gbitmap_create_with_resource(RESOURCE_ID_IMAGE_SIMPLE_FADE_BOTTOM_BLACK); 140 | // #elif PBL_PLATFORM_BASALT 141 | shadow_top = gbitmap_create_with_resource(RESOURCE_ID_IMAGE_SIMPLE_FADE_TOP); 142 | shadow_bottom = gbitmap_create_with_resource(RESOURCE_ID_IMAGE_SIMPLE_FADE_BOTTOM); 143 | // #endif 144 | } 145 | 146 | void select_window_unload(Window *window) { 147 | menu_layer_destroy(select_menu_layer); 148 | gbitmap_destroy(shadow_top); 149 | gbitmap_destroy(shadow_bottom); 150 | window_destroy(select_window); 151 | } 152 | 153 | void push_select_window(int key_id) { 154 | selected_index = key_id; 155 | select_window = window_create(); 156 | 157 | // #ifdef PBL_SDK_2 158 | // window_set_fullscreen(select_window, true); 159 | // #endif 160 | 161 | window_set_window_handlers(select_window, (WindowHandlers) { 162 | .load = select_window_load, 163 | .unload = select_window_unload, 164 | }); 165 | 166 | window_stack_push(select_window, true /* Animated */); 167 | } -------------------------------------------------------------------------------- /src/c/select_window.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define SELECT_WINDOW_CELL_HEIGHT 30 4 | #define SHADOW_HEIGHT 15 5 | 6 | void close_select_window(); 7 | void push_select_window(int key_id); -------------------------------------------------------------------------------- /src/c/sha1.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2010 Google Inc. 3 | * Author: Markus Gutschke 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | * 18 | * An earlier version of this file was originally released into the public 19 | * domain by its authors. It has been modified to make the code compile and 20 | * link as part of the Google Authenticator project. These changes are 21 | * copyrighted by Google Inc. and released under the Apache License, 22 | * Version 2.0. 23 | * 24 | * The previous authors' terms are included below: 25 | */ 26 | 27 | /***************************************************************************** 28 | * 29 | * File: sha1.c 30 | * 31 | * Purpose: Implementation of the SHA1 message-digest algorithm. 32 | * 33 | * NIST Secure Hash Algorithm 34 | * Heavily modified by Uwe Hollerbach 35 | * from Peter C. Gutmann's implementation as found in 36 | * Applied Cryptography by Bruce Schneier 37 | * Further modifications to include the "UNRAVEL" stuff, below 38 | * 39 | * This code is in the public domain 40 | * 41 | ***************************************************************************** 42 | */ 43 | #include "string.h" 44 | #include "sha1.h" 45 | 46 | /* SHA f()-functions */ 47 | #define f1(x,y,z) ((x & y) | (~x & z)) 48 | #define f2(x,y,z) (x ^ y ^ z) 49 | #define f3(x,y,z) ((x & y) | (x & z) | (y & z)) 50 | #define f4(x,y,z) (x ^ y ^ z) 51 | 52 | /* SHA constants */ 53 | #define CONST1 0x5a827999L 54 | #define CONST2 0x6ed9eba1L 55 | #define CONST3 0x8f1bbcdcL 56 | #define CONST4 0xca62c1d6L 57 | 58 | /* truncate to 32 bits -- should be a null op on 32-bit machines */ 59 | #define T32(x) ((x) & 0xffffffffL) 60 | 61 | /* 32-bit rotate */ 62 | #define R32(x,n) T32(((x << n) | (x >> (32 - n)))) 63 | 64 | /* the generic case, for when the overall rotation is not unraveled */ 65 | #define FG(n) \ 66 | T = T32(R32(A,5) + f##n(B,C,D) + E + *WP++ + CONST##n); \ 67 | E = D; D = C; C = R32(B,30); B = A; A = T 68 | 69 | /* specific cases, for when the overall rotation is unraveled */ 70 | #define FA(n) \ 71 | T = T32(R32(A,5) + f##n(B,C,D) + E + *WP++ + CONST##n); B = R32(B,30) 72 | 73 | #define FB(n) \ 74 | E = T32(R32(T,5) + f##n(A,B,C) + D + *WP++ + CONST##n); A = R32(A,30) 75 | 76 | #define FC(n) \ 77 | D = T32(R32(E,5) + f##n(T,A,B) + C + *WP++ + CONST##n); T = R32(T,30) 78 | 79 | #define FD(n) \ 80 | C = T32(R32(D,5) + f##n(E,T,A) + B + *WP++ + CONST##n); E = R32(E,30) 81 | 82 | #define FE(n) \ 83 | B = T32(R32(C,5) + f##n(D,E,T) + A + *WP++ + CONST##n); D = R32(D,30) 84 | 85 | #define FT(n) \ 86 | A = T32(R32(B,5) + f##n(C,D,E) + T + *WP++ + CONST##n); C = R32(C,30) 87 | 88 | 89 | static void 90 | sha1_transform(SHA1_INFO *sha1_info) 91 | { 92 | int i; 93 | uint8_t *dp; 94 | uint32_t T, A, B, C, D, E, W[80], *WP; 95 | 96 | dp = sha1_info->data; 97 | 98 | for (i = 0; i < 16; ++i) { 99 | T = *((uint32_t *) dp); 100 | dp += 4; 101 | W[i] = 102 | ((T << 24) & 0xff000000) | 103 | ((T << 8) & 0x00ff0000) | 104 | ((T >> 8) & 0x0000ff00) | ((T >> 24) & 0x000000ff); 105 | } 106 | 107 | for (i = 16; i < 80; ++i) { 108 | W[i] = W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16]; 109 | W[i] = R32(W[i], 1); 110 | } 111 | A = sha1_info->digest[0]; 112 | B = sha1_info->digest[1]; 113 | C = sha1_info->digest[2]; 114 | D = sha1_info->digest[3]; 115 | E = sha1_info->digest[4]; 116 | WP = W; 117 | #ifdef UNRAVEL 118 | FA(1); FB(1); FC(1); FD(1); FE(1); FT(1); FA(1); FB(1); FC(1); FD(1); 119 | FE(1); FT(1); FA(1); FB(1); FC(1); FD(1); FE(1); FT(1); FA(1); FB(1); 120 | FC(2); FD(2); FE(2); FT(2); FA(2); FB(2); FC(2); FD(2); FE(2); FT(2); 121 | FA(2); FB(2); FC(2); FD(2); FE(2); FT(2); FA(2); FB(2); FC(2); FD(2); 122 | FE(3); FT(3); FA(3); FB(3); FC(3); FD(3); FE(3); FT(3); FA(3); FB(3); 123 | FC(3); FD(3); FE(3); FT(3); FA(3); FB(3); FC(3); FD(3); FE(3); FT(3); 124 | FA(4); FB(4); FC(4); FD(4); FE(4); FT(4); FA(4); FB(4); FC(4); FD(4); 125 | FE(4); FT(4); FA(4); FB(4); FC(4); FD(4); FE(4); FT(4); FA(4); FB(4); 126 | sha1_info->digest[0] = T32(sha1_info->digest[0] + E); 127 | sha1_info->digest[1] = T32(sha1_info->digest[1] + T); 128 | sha1_info->digest[2] = T32(sha1_info->digest[2] + A); 129 | sha1_info->digest[3] = T32(sha1_info->digest[3] + B); 130 | sha1_info->digest[4] = T32(sha1_info->digest[4] + C); 131 | #else /* !UNRAVEL */ 132 | #ifdef UNROLL_LOOPS 133 | FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); 134 | FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); 135 | FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); 136 | FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); 137 | FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); 138 | FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); 139 | FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); 140 | FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); 141 | #else /* !UNROLL_LOOPS */ 142 | for (i = 0; i < 20; ++i) { FG(1); } 143 | for (i = 20; i < 40; ++i) { FG(2); } 144 | for (i = 40; i < 60; ++i) { FG(3); } 145 | for (i = 60; i < 80; ++i) { FG(4); } 146 | #endif /* !UNROLL_LOOPS */ 147 | sha1_info->digest[0] = T32(sha1_info->digest[0] + A); 148 | sha1_info->digest[1] = T32(sha1_info->digest[1] + B); 149 | sha1_info->digest[2] = T32(sha1_info->digest[2] + C); 150 | sha1_info->digest[3] = T32(sha1_info->digest[3] + D); 151 | sha1_info->digest[4] = T32(sha1_info->digest[4] + E); 152 | #endif /* !UNRAVEL */ 153 | } 154 | 155 | /* initialize the SHA digest */ 156 | 157 | void 158 | sha1_init(SHA1_INFO *sha1_info) 159 | { 160 | sha1_info->digest[0] = 0x67452301L; 161 | sha1_info->digest[1] = 0xefcdab89L; 162 | sha1_info->digest[2] = 0x98badcfeL; 163 | sha1_info->digest[3] = 0x10325476L; 164 | sha1_info->digest[4] = 0xc3d2e1f0L; 165 | sha1_info->count_lo = 0L; 166 | sha1_info->count_hi = 0L; 167 | sha1_info->local = 0; 168 | } 169 | 170 | /* update the SHA digest */ 171 | 172 | void 173 | sha1_update(SHA1_INFO *sha1_info, const uint8_t *buffer, int count) 174 | { 175 | int i; 176 | uint32_t clo; 177 | 178 | clo = T32(sha1_info->count_lo + ((uint32_t) count << 3)); 179 | if (clo < sha1_info->count_lo) { 180 | ++sha1_info->count_hi; 181 | } 182 | sha1_info->count_lo = clo; 183 | sha1_info->count_hi += (uint32_t) count >> 29; 184 | if (sha1_info->local) { 185 | i = SHA1_BLOCKSIZE - sha1_info->local; 186 | if (i > count) { 187 | i = count; 188 | } 189 | memcpy(((uint8_t *) sha1_info->data) + sha1_info->local, buffer, i); 190 | count -= i; 191 | buffer += i; 192 | sha1_info->local += i; 193 | if (sha1_info->local == SHA1_BLOCKSIZE) { 194 | sha1_transform(sha1_info); 195 | } else { 196 | return; 197 | } 198 | } 199 | while (count >= SHA1_BLOCKSIZE) { 200 | memcpy(sha1_info->data, buffer, SHA1_BLOCKSIZE); 201 | buffer += SHA1_BLOCKSIZE; 202 | count -= SHA1_BLOCKSIZE; 203 | sha1_transform(sha1_info); 204 | } 205 | memcpy(sha1_info->data, buffer, count); 206 | sha1_info->local = count; 207 | } 208 | 209 | 210 | static void 211 | sha1_transform_and_copy(unsigned char digest[20], SHA1_INFO *sha1_info) 212 | { 213 | sha1_transform(sha1_info); 214 | digest[ 0] = (unsigned char) ((sha1_info->digest[0] >> 24) & 0xff); 215 | digest[ 1] = (unsigned char) ((sha1_info->digest[0] >> 16) & 0xff); 216 | digest[ 2] = (unsigned char) ((sha1_info->digest[0] >> 8) & 0xff); 217 | digest[ 3] = (unsigned char) ((sha1_info->digest[0] ) & 0xff); 218 | digest[ 4] = (unsigned char) ((sha1_info->digest[1] >> 24) & 0xff); 219 | digest[ 5] = (unsigned char) ((sha1_info->digest[1] >> 16) & 0xff); 220 | digest[ 6] = (unsigned char) ((sha1_info->digest[1] >> 8) & 0xff); 221 | digest[ 7] = (unsigned char) ((sha1_info->digest[1] ) & 0xff); 222 | digest[ 8] = (unsigned char) ((sha1_info->digest[2] >> 24) & 0xff); 223 | digest[ 9] = (unsigned char) ((sha1_info->digest[2] >> 16) & 0xff); 224 | digest[10] = (unsigned char) ((sha1_info->digest[2] >> 8) & 0xff); 225 | digest[11] = (unsigned char) ((sha1_info->digest[2] ) & 0xff); 226 | digest[12] = (unsigned char) ((sha1_info->digest[3] >> 24) & 0xff); 227 | digest[13] = (unsigned char) ((sha1_info->digest[3] >> 16) & 0xff); 228 | digest[14] = (unsigned char) ((sha1_info->digest[3] >> 8) & 0xff); 229 | digest[15] = (unsigned char) ((sha1_info->digest[3] ) & 0xff); 230 | digest[16] = (unsigned char) ((sha1_info->digest[4] >> 24) & 0xff); 231 | digest[17] = (unsigned char) ((sha1_info->digest[4] >> 16) & 0xff); 232 | digest[18] = (unsigned char) ((sha1_info->digest[4] >> 8) & 0xff); 233 | digest[19] = (unsigned char) ((sha1_info->digest[4] ) & 0xff); 234 | } 235 | 236 | /* finish computing the SHA digest */ 237 | void 238 | sha1_final(SHA1_INFO *sha1_info, uint8_t digest[20]) 239 | { 240 | int count; 241 | uint32_t lo_bit_count, hi_bit_count; 242 | 243 | lo_bit_count = sha1_info->count_lo; 244 | hi_bit_count = sha1_info->count_hi; 245 | count = (int) ((lo_bit_count >> 3) & 0x3f); 246 | ((uint8_t *) sha1_info->data)[count++] = 0x80; 247 | if (count > SHA1_BLOCKSIZE - 8) { 248 | memset(((uint8_t *) sha1_info->data) + count, 0, SHA1_BLOCKSIZE - count); 249 | sha1_transform(sha1_info); 250 | memset((uint8_t *) sha1_info->data, 0, SHA1_BLOCKSIZE - 8); 251 | } else { 252 | memset(((uint8_t *) sha1_info->data) + count, 0, 253 | SHA1_BLOCKSIZE - 8 - count); 254 | } 255 | sha1_info->data[56] = (uint8_t)((hi_bit_count >> 24) & 0xff); 256 | sha1_info->data[57] = (uint8_t)((hi_bit_count >> 16) & 0xff); 257 | sha1_info->data[58] = (uint8_t)((hi_bit_count >> 8) & 0xff); 258 | sha1_info->data[59] = (uint8_t)((hi_bit_count >> 0) & 0xff); 259 | sha1_info->data[60] = (uint8_t)((lo_bit_count >> 24) & 0xff); 260 | sha1_info->data[61] = (uint8_t)((lo_bit_count >> 16) & 0xff); 261 | sha1_info->data[62] = (uint8_t)((lo_bit_count >> 8) & 0xff); 262 | sha1_info->data[63] = (uint8_t)((lo_bit_count >> 0) & 0xff); 263 | sha1_transform_and_copy(digest, sha1_info); 264 | } 265 | 266 | /***EOF***/ 267 | -------------------------------------------------------------------------------- /src/c/sha1.h: -------------------------------------------------------------------------------- 1 | // SHA1 header file 2 | // 3 | // Copyright 2010 Google Inc. 4 | // Author: Markus Gutschke 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | #pragma once 19 | #include "stdint.h" 20 | 21 | #define SHA1_BLOCKSIZE 64 22 | #define SHA1_DIGEST_LENGTH 20 23 | 24 | typedef struct { 25 | uint32_t digest[8]; 26 | uint32_t count_lo, count_hi; 27 | uint8_t data[SHA1_BLOCKSIZE]; 28 | int local; 29 | } SHA1_INFO; 30 | 31 | void sha1_init(SHA1_INFO *sha1_info) 32 | __attribute__((visibility("hidden"))); 33 | void sha1_update(SHA1_INFO *sha1_info, const uint8_t *buffer, int count) 34 | __attribute__((visibility("hidden"))); 35 | void sha1_final(SHA1_INFO *sha1_info, uint8_t digest[20]) 36 | __attribute__((visibility("hidden"))); -------------------------------------------------------------------------------- /src/c/single_code_window.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void single_code_window_push(void); 4 | void single_code_window_remove(void); 5 | void single_code_window_second_tick(int seconds); -------------------------------------------------------------------------------- /src/pkjs/config.js: -------------------------------------------------------------------------------- 1 | module.exports = 2 | [ 3 | { 4 | "type": "heading", 5 | "defaultValue": "QuickAuth" 6 | }, 7 | { 8 | "type": "text", 9 | "defaultValue": "Google Authenticator for Pebble" 10 | }, 11 | { 12 | "type": "section", 13 | "items": [ 14 | { 15 | "type": "heading", 16 | "defaultValue": "Key Settings" 17 | }, 18 | { 19 | "type": "text", 20 | "defaultValue": "Add or edit an existing key." 21 | }, 22 | { 23 | "type": "input", 24 | "messageKey": "auth_name", 25 | "label": "Name", 26 | "attributes": { 27 | "placeholder": "eg: Google", 28 | "limit": 20 29 | } 30 | }, 31 | { 32 | "type": "input", 33 | "messageKey": "auth_key", 34 | "label": "Key", 35 | "attributes": { 36 | "limit": 128 37 | } 38 | }, 39 | { 40 | "type": "text", 41 | "messageKey": "slots_remaining" 42 | } 43 | ] 44 | }, 45 | { 46 | "type": "section", 47 | "items": [ 48 | { 49 | "type": "heading", 50 | "defaultValue": "App Settings" 51 | }, 52 | { 53 | "type": "text", 54 | "defaultValue": "Settings that can be changed without having to add or edit a key." 55 | }, 56 | { 57 | "type": "color", 58 | "messageKey": "foreground_color", 59 | "defaultValue": "ffffff", 60 | "label": "Foreground Color", 61 | "sunlight": false, 62 | }, 63 | { 64 | "type": "color", 65 | "messageKey": "background_color", 66 | "defaultValue": "0000ff", 67 | "label": "Background Color", 68 | "sunlight": false, 69 | }, 70 | { 71 | "type": "select", 72 | "messageKey": "idle_timeout", 73 | "label": "Idle Timeout", 74 | "defaultValue": "300", 75 | "options": [ 76 | { 77 | "label": "Disabled", 78 | "value": "0" 79 | }, 80 | { 81 | "label": "1 Minute", 82 | "value": "60" 83 | }, 84 | { 85 | "label": "2 Minutes", 86 | "value": "120" 87 | }, 88 | { 89 | "label": "5 Minutes", 90 | "value": "300" 91 | }, 92 | { 93 | "label": "10 Minutes", 94 | "value": "600" 95 | } 96 | ] 97 | }, 98 | { 99 | "type": "radiogroup", 100 | "messageKey": "window_layout", 101 | "label": "View Mode", 102 | "defaultValue": "0", 103 | "options": [ 104 | { 105 | "label": "Single Code", 106 | "value": "0" 107 | }, 108 | { 109 | "label": "Multi Code", 110 | "value": "1" 111 | } 112 | ] 113 | }, 114 | { 115 | "type": "select", 116 | "messageKey": "font", 117 | "label": "Font", 118 | "defaultValue": "0", 119 | "options": [ 120 | { 121 | "label": "Style 1", 122 | "value": "0" 123 | }, 124 | { 125 | "label": "Style 2", 126 | "value": "1" 127 | }, 128 | { 129 | "label": "Style 3", 130 | "value": "2" 131 | }, 132 | { 133 | "label": "Style 4", 134 | "value": "3" 135 | } 136 | ] 137 | }, 138 | { 139 | "type": "submit", 140 | "defaultValue": "Save" 141 | } 142 | ] 143 | } 144 | ]; -------------------------------------------------------------------------------- /wscript: -------------------------------------------------------------------------------- 1 | 2 | # 3 | # This file is the default set of rules to compile a Pebble project. 4 | # 5 | # Feel free to customize this to your needs. 6 | # 7 | 8 | try: 9 | from sh import jshint, ErrorReturnCode_2 10 | hint = jshint 11 | except ImportError: 12 | hint = None 13 | 14 | top = '.' 15 | out = 'build' 16 | 17 | def options(ctx): 18 | ctx.load('pebble_sdk') 19 | 20 | def configure(ctx): 21 | ctx.load('pebble_sdk') 22 | global hint 23 | if hint is not None: 24 | hint = hint.bake(['--config', 'pebble-jshintrc']) 25 | 26 | def build(ctx): 27 | if False and hint is not None: 28 | try: 29 | hint("src/js/pebble-js-app.js", _tty_out=False) # no tty because there are none in the cloudpebble sandbox. 30 | except ErrorReturnCode_2 as e: 31 | ctx.fatal("\nJavaScript linting failed (you can disable this in Project Settings):\n" + e.stdout) 32 | 33 | ctx.load('pebble_sdk') 34 | 35 | ctx.pbl_program(source=ctx.path.ant_glob('src/**/*.c'), 36 | target='pebble-app.elf') 37 | 38 | ctx.pbl_bundle(elf='pebble-app.elf', 39 | js=ctx.path.ant_glob('src/js/**/*.js')) 40 | 41 | --------------------------------------------------------------------------------