~skv: 1 Update TOTP codes when they are expired 2 files changed, 25 insertions(+), 14 deletions(-)
Copy & paste the following snippet into your terminal to import this patchset into git:
curl -s https://lists.sr.ht/~martijnbraam/public-inbox/patches/39319/mbox | git am -3Learn more about email & git
From: Andrey Skvortsov <andrej.skvortzov@gmail.com> If system is suspended, then after resume TOTP are not updated until 'period' seconds are left in the wakeup state. During this time shown TOTP codes are wrong, because they are calculated base on the timestamp before system was suspended. --- numberstation/otpurl.py | 15 ++++++++++++++- numberstation/window.py | 24 +++++++++++------------- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/numberstation/otpurl.py b/numberstation/otpurl.py index 628e3f0..701d100 100644 --- a/numberstation/otpurl.py +++ b/numberstation/otpurl.py @@ -46,7 +46,9 @@ class OTPUrl: def get_token(self): if self.type == 'totp': totp = pyotp.TOTP(self.secret, interval=self.period, digits=self.digits, digest=self.digest) - return totp.now(), totp.interval - datetime.now().timestamp() % totp.interval + base_timestamp = totp.interval * int(datetime.now().timestamp() / totp.interval) + valid_until = datetime.fromtimestamp(base_timestamp + totp.interval) + return totp.now(), valid_until elif self.type == 'hotp': hotp = pyotp.HOTP(self.secret, digits=self.digits, digest=self.digest, initial_count=self.initial_count) return hotp.at(0), None @@ -54,6 +56,17 @@ class OTPUrl: sys.stderr.write("Unknown key format '{}'\n".format(self.type)) return None, None + def get_validity(self): + """Returns floating value in the range 0.0-1.0 + described how long generated code will be valid + relative to update period + """ + if self.type == 'totp': + secs_left = self.period - datetime.now().timestamp() % self.period + return 1.0 * secs_left / self.period + else: + return None + def get_url(self): properties = { 'secret': self.secret, diff --git a/numberstation/window.py b/numberstation/window.py index 5dda4c9..eb14310 100644 --- a/numberstation/window.py +++ b/numberstation/window.py @@ -1,4 +1,5 @@ from base64 import b32decode +from datetime import datetime from collections import namedtuple import keyring @@ -128,7 +129,7 @@ class NumberstationWindow: self.save_keyring() def update_code_label(self, label, progressbar, token): - code, validity = token.get_token() + code, valid_until = token.get_token() if code is None: return user_code = code @@ -144,23 +145,20 @@ class NumberstationWindow: else: label.set_label(user_code) label.paste_code = paste_code - if validity: - progressbar.validity = int(validity) + if valid_until: + progressbar.valid_until = valid_until else: if progressbar: - progressbar.validity = None + progressbar.valid_until = None def update_codes(self): GLib.timeout_add(1000, self.update_codes) for timer in self.timers: - if timer.validity is None: + if timer.valid_until is None: continue - timer.validity -= 1 - if timer.validity < 1: - timer.validity = int(timer.period) + if timer.valid_until < datetime.now(): self.update_code_label(timer.display, timer, timer.token) - - timer.set_fraction(1.0 / timer.period * timer.validity) + timer.set_fraction(timer.token.get_validity()) def on_long_press(self, gesture, x, y, *args): eb = gesture.eb @@ -249,12 +247,12 @@ class NumberstationWindow: timer.token = token timer.display = code - if not hasattr(timer, 'validity'): + if not hasattr(timer, 'valid_until'): continue - if timer.validity is not None: + if timer.valid_until is not None: timer.show() timer.set_no_show_all(False) - timer.set_fraction(1.0 / token.period * timer.validity) + timer.set_fraction(token.get_validity()) else: timer.hide() timer.set_no_show_all(True) -- 2.34.7
Hi Martijn, do you have any feedback on the patch? Is there anything I could possibly do to get it merged?