1- # PyPortal TOTP Authenticator
21import time
32
4- import adafruit_hashlib as hashlib
5- import adafruit_imageload
6- import adafruit_touchscreen
73import board
84import busio
5+ from digitalio import DigitalInOut
6+
7+ import adafruit_hashlib as hashlib
8+ import adafruit_touchscreen
99import displayio
1010import neopixel
1111import terminalio
1818from adafruit_esp32spi import adafruit_esp32spi
1919from adafruit_ntp import NTP
2020from adafruit_pyportal import PyPortal
21- from digitalio import DigitalInOut
2221
2322# Background/Images
2423BACKGROUND = 0x059ACE
3433 print ("WiFi secrets are kept in secrets.py, please add them there!" )
3534 raise
3635
36+
3737# Initialize PyPortal Display
3838display = board .DISPLAY
3939
4747 ),
4848 size = (WIDTH , HEIGHT ))
4949
50+
5051# Create a SHA1 Object
5152SHA1 = hashlib .sha1
5253
5960spi = busio .SPI (board .SCK , board .MOSI , board .MISO )
6061esp = adafruit_esp32spi .ESP_SPIcontrol (spi , esp32_cs , esp32_ready , esp32_reset )
6162
63+
6264# HMAC implementation, as hashlib/hmac wouldn't fit
6365# From https://en.wikipedia.org/wiki/Hash-based_message_authentication_code
6466def HMAC (k , m ):
@@ -114,6 +116,7 @@ def int_to_bytestring(i, padding=8):
114116
115117# HMAC -> OTP generator, pretty much same as
116118# https://github.com/pyotp/pyotp/blob/master/src/pyotp/otp.py
119+
117120def generate_otp (int_input , secret_key , digits = 6 ):
118121 if int_input < 0 :
119122 raise ValueError ('input must be positive integer' )
@@ -129,6 +132,7 @@ def generate_otp(int_input, secret_key, digits=6):
129132 str_code = str (code % 10 ** digits )
130133 while len (str_code ) < digits :
131134 str_code = '0' + str_code
135+
132136 return str_code
133137
134138
@@ -154,7 +158,7 @@ def generate_otp(int_input, secret_key, digits=6):
154158bg_palette [0 ] = BACKGROUND
155159background = displayio .TileGrid (bg_bitmap , pixel_shader = bg_palette )
156160
157- # Text DisplayIO
161+ # Create a new DisplayIO group
158162splash = displayio .Group (max_size = 100 )
159163
160164splash .append (background )
@@ -163,20 +167,46 @@ def generate_otp(int_input, secret_key, digits=6):
163167# We'll use a default text placeholder for this label
164168label_key = Label (font , text = "000 000" )
165169label_key .x = (display .width // 2 ) // 13
166- label_key .y = 17
170+ label_key .y = 20
167171key_group .append (label_key )
168172
169173label_title = Label (font , max_glyphs = 14 )
170- label_title .text = "loading.."
171- label_title .x = (display .width // 2 ) // 13
174+ label_title .text = "loading... "
175+ label_title .x = (display .width // 2 ) // 10
172176label_title .y = 5
173177key_group .append (label_title )
174178
179+ # append key_group to splash
175180splash .append (key_group )
176-
177181# Show the group
178182display .show (splash )
179183
184+ # Add buttons to the interface
185+ assert len (secrets ['totp_keys' ]) < 6 , "This code can only render 5 keys at a time"
186+
187+ # generate buttons
188+ buttons = []
189+
190+ btn_x = 5
191+ for i in secrets ['totp_keys' ]:
192+ print (i )
193+ try :
194+ if i [2 ]:
195+ color = i [2 ]
196+ except IndexError :
197+ color = 0x00FF00
198+ button = Button (name = i [0 ], x = btn_x , y = 175 ,
199+ width = 60 , height = 60 ,
200+ label = i [0 ], label_font = font , label_color = 0xFFFFFF ,
201+ fill_color = color , style = Button .ROUNDRECT )
202+ buttons .append (button )
203+ btn_x += 63
204+
205+ # append buttons to splash group
206+ for b in buttons :
207+ splash .append (b .group )
208+
209+
180210print ("Connecting to AP..." )
181211while not esp .is_connected :
182212 try :
@@ -207,45 +237,28 @@ def generate_otp(int_input, secret_key, digits=6):
207237print ("Monotonic time" , mono_time )
208238
209239
210- def display_otp (unix_time , name , secret ):
211- """Updates text objects to display the OTP and name.
212-
213- :param int unix_time: Current unix time
214- :param str name: OTP name
215- :param str secret: OTP Secret
216- """
217- otp = generate_otp (unix_time // 30 , secret )
218- print (name + " OTP output: " , otp )
219- # display the key's name
220- label_title .text = name
221- # format and display the OTP
222- label_key .text = "{} {}" .format (str (otp )[0 :3 ],str (otp )[3 :6 ])
223-
224- def get_unix_time ():
225- """Calculate current time based on NTP + monotonic
226-
227- """
228- unix_time = t - mono_time + int (time .monotonic ())
229- return unix_time
230-
231- # how long to stay on if not in always_on mode
232- countdown = ON_SECONDS
233- cur_otp = 0
234- max_otp = len (secrets ['totp_keys' ])
235-
236- display_otp (get_unix_time (), secrets ['totp_keys' ][cur_otp ][0 ],
237- secrets ['totp_keys' ][cur_otp ][1 ])
238-
240+ countdown = ON_SECONDS # how long to stay on if not in always_on mode
239241while ALWAYS_ON or (countdown > 0 ):
242+ # Calculate current time based on NTP + monotonic
243+ unix_time = t - mono_time + int (time .monotonic ())
240244 p = ts .touch_point
241245 if p :
242- if cur_otp < max_otp :
243- display_otp (get_unix_time (), secrets ['totp_keys' ][cur_otp ][0 ],
244- secrets ['totp_keys' ][cur_otp ][1 ])
245- cur_otp += 1
246- else :
247- # reset the current otp display
248- cur_otp = 0
246+ for i , b in enumerate (buttons ):
247+ if b .contains (p ):
248+ b .selected = True
249+ for name , secret in secrets ['totp_keys' ]:
250+ if b .name == name :
251+ # generate OTP
252+ #print('Background color: ', background_color)
253+ otp = generate_otp (unix_time // 30 , secret )
254+ print ('{} selected: ' .format (name ))
255+ print (name + " OTP output: " , otp )
256+ # display the key's name
257+ label_title .text = name
258+ # format and display the OTP
259+ label_key .text = "{} {}" .format (str (otp )[0 :3 ],str (otp )[3 :6 ])
260+ else :
261+ b .selected = False
249262 # We'll update every 1/4 second, we can hash very fast so its no biggie!
250263 countdown -= 0.25
251264 time .sleep (0.25 )
0 commit comments