Halt import on control characters or a ':'/'/' inside an issuer or account (they collide with the OATH issuer:account separator and period/ prefix), on a composed name over 64 bytes with a clearer per-field message, and on a digit count outside 6-8 or a non-positive period.
75 lines
3.0 KiB
Markdown
75 lines
3.0 KiB
Markdown
# 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
|
|
```
|