# otp-import Import TOTP credentials from an exported spreadsheet into a VivoKey Apex OTP (ykneo-oath) applet over PC/SC. The importer reads the curated columns of an export spreadsheet (`otp-keys-export.xlsx`: Issuer, Account, Secret, Type, Algorithm, Digits, Period) and writes each credential to the applet using the standard YKOATH `PUT` command. It authenticates first if the applet is password-protected, asks before overwriting credentials that already exist, and can optionally profile per-credential on-chip memory consumption via the `javacard-memory` applet. The protocol layer is verified against the applet sources (`com.vivokey.otp.YkneoOath` / `OathObj`) and `doc/Protocol.txt`. See [docs/design.md](docs/design.md) for the full design and protocol notes. ## Security This tool handles live TOTP secrets. **Keep your secrets out of this repository.** The spreadsheet, any CSV, and QR screenshots are excluded by `.gitignore`; keep the actual data in a private location (e.g. a synced personal folder) and point the importer at it with `--input`. ## Requirements - Python 3.9+ - `pyscard`, `cryptography`, `openpyxl` - A PC/SC reader with the Apex (or a JavaCard carrying the applet) presented ## Usage ``` # 1. Validate the data and preview the APDUs (no card needed) python apex_otp_import.py --input /path/to/otp-keys-export.xlsx --dry-run # 2. Read-only: list what is already on the applet python apex_otp_import.py --input /path/to/otp-keys-export.xlsx --list # 3. Import (asks before overwriting existing credentials) python apex_otp_import.py --input /path/to/otp-keys-export.xlsx # Non-interactive conflict handling python apex_otp_import.py --input ... --overwrite-all python apex_otp_import.py --input ... --skip-existing # Target a specific reader, supply a password, and profile memory python apex_otp_import.py --input ... --reader VivoKey --password '...' --profile-memory # Import, then set an access password (only if the applet has none yet) python apex_otp_import.py --input ... --set-password 'yourpassword' # Reset: wipe all credentials and any password (standalone; no input needed) python apex_otp_import.py --reset ``` Authenticating to a password-protected applet uses the standard YKOATH scheme (PBKDF2-HMAC-SHA1 of the password with the device id as salt, then mutual challenge-response). `--set-password` only sets a password on an applet that has none; it does not change or remove an existing one. `--reset` clears everything. The input is validated before any reader is touched. The run halts with a per-row report if it finds: duplicate credential names; invalid Base32; over-long names or keys; an unsupported algorithm; control characters or a `:`/`/` inside an issuer or account (they break the OATH name convention); or a digit count outside 6-8 / a non-positive period. Fix the source data and re-run. ## Tests Hardware-free unit tests cover the protocol encoding, password derivation, input validation, and response reassembly: ``` python test_oath.py ```