From bb09e0636d5857f4b5f76ccae2b1ba2abb12cbb7 Mon Sep 17 00:00:00 2001 From: Amal Graafstra Date: Wed, 8 Jul 2026 13:34:18 -0700 Subject: [PATCH] Require auth to reset a locked applet The applet gates RESET behind authentication when a password is set (unlike Yubico OATH, which allows an unauthenticated reset). --reset now authenticates with the applet password (--password or prompt) before resetting a locked applet, and the RESET error includes the status-word meaning. --- apex_otp_import.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/apex_otp_import.py b/apex_otp_import.py index d611d11..73c80a5 100644 --- a/apex_otp_import.py +++ b/apex_otp_import.py @@ -446,10 +446,12 @@ class OathSession: return def reset(self): - """RESET: wipe all credentials and any password (P1P2 = 0xDEAD).""" + """RESET: wipe all credentials and any password (P1P2 = 0xDEAD). + Unlike Yubico's OATH, this applet gates RESET behind authentication when + a password is set, so a locked applet must be VALIDATEd first.""" data, sw = self.ch.send(0x00, INS_RESET, 0xDE, 0xAD) if sw != 0x9000: - raise OathError(sw, "RESET failed") + raise OathError(sw, f"RESET failed ({sw_message(sw)})") return @@ -642,6 +644,10 @@ def main(argv=None): return 1 oath = OathSession(CardChannel(connection)) oath.select() + if oath.locked: + # This applet requires authentication before RESET (unlike Yubico). + pw = args.password or getpass.getpass("Applet password (required to reset): ") + oath.validate(pw) oath.reset() print(f"Applet reset on '{reader}' (all credentials and any password wiped).") return 0