# Changelog All notable changes to this project are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Versions stay at 0.x until milestone M8; each milestone raises the minor version. ## [Unreleased] ## [0.7.0] - 2026-10-20 Milestone M7: the phone settings forms. Entries can now be added and changed from Garmin Connect on the phone, one form at a time. ### Added - The full settings form (`resources/settings/`): Add OTP, Add code, Manage entry, Import, Options and a read-only About group, with every property of README 10.2 and its strings. - Processing (`source/settings/SettingsJob.mc`) in README 10.3 order: Import, Add OTP, Add code, Manage, then the About fields. Each form is applied only when its switch is on. On success the form is cleared (secret first) and its keyed hash remembered, so the same form sent again does nothing. After a problem only the secret is cleared, so the rest can be corrected. Every form writes a short result into its Result field and the results screen. - Add OTP (`OtpForm.mc`): TOTP, HOTP and Steam, validated as README 10.4 says (Base32 errors name the character and position, a warning below 80 bits). - Add code (`CodeForm.mc`): all 12 symbologies, validated by the real encoder (`CodeCheck`, a job for 2D codes), with a warning when the code doesn't fit this screen. 2D codes are cached encoded on the way in, so the first opening is instant. - Manage entry (`ManageForm.mc`): edit details, move, require or drop the PIN, replace the secret or contents, set a HOTP counter, delete. Entry IDs are case-insensitive. - Changes to locked entries wait for the PIN (README 7.9, `ChangeQueue.mc`): they are sealed with the device key under `q`, listed under "Phone changes" in the menu and applied only after the PIN is entered on the watch, or rejected. - About fields (`AboutScan.mc`): the entry catalog (IDs and names, at most 800 characters; IDs and types only with "Encrypt names"), the version and the storage use, rewritten only when something changed. - "Encrypt names" now works: new entries follow it, and existing ones are re-sealed (locked ones after the next PIN entry). - Options are applied as soon as the phone saves them. - `tools/check_resources.py` (in `make lint`): every resource file validates against the SDK's `resources.xsd`, and settings, properties and strings agree. The dev image gains `xmlschema` for it. - Tests: 34 new Monkey C tests (154 per device), including every symbology valid and invalid, every Manage action, the queue, idempotency and a check that the form defaults match `properties.xml`. The UI flow sends the forms the way the phone does and checks the results: a TOTP with its known code, a Code 128 card that must decode, and a queued change confirmed with the PIN. ### Changed - Form hashes are keyed (HMAC-SHA256 with a key derived from the device key) instead of plain SHA-256 of the content, because the content includes secrets. Fields are fed to the native HMAC one by one: copying a large import form into one buffer and zeroing it tripped the watchdog on the Forerunner 255S during M7. ## [0.6.0] - 2026-10-20 Milestone M6: barcodes. Code entries now show their barcode: all 12 symbologies of README 12.1, encoded on the watch. ### Added - M6a, linear barcodes (`source/codes/`): EAN-13, EAN-8, UPC-A and UPC-E, with check digits added, verified, fixed or off, and canonical UPC-E zero suppression; Code 128 (sets B and C); Code 39 with an optional mod-43 check character; ITF with an optional check digit; Codabar with start and stop characters. - The code screen: black on white, the largest whole module size, human- readable text, and a full-backlight request. - Reference rows from zxing-cpp (`tools/gen_code_vectors.py`, `make codes`). Barcodes from the unit tests are decoded with zxing-cpp in every test run (`tools/check_codes.py`), and the UI flow decodes a screenshot of the code screen. - M6b, QR Code (`source/codes/Qr.mc`, `ReedSolomon.mc`, `QrTables.mc`): - Versions 1-40 and levels L-H, with optimal numeric, alphanumeric and byte segmentation and all eight masks scored as segno does. - Encoded as a watchdog-safe job with progress, and cached sealed with the entry's key (`x.`). - Drawn in the largest centred square at 2 px per module or more. - Checked against segno (`tools/gen_qr_tables.py`, `make codes`), decoded with zxing-cpp, and decoded from a screenshot of a version 6 card. - M6c, Data Matrix (`DataMatrix.mc`): - ECC 200 square sizes 10x10 to 144x144, ASCII with digit pairs, and Base256 or Upper Shift for UTF-8. - Encoded as a job; identical to ppf.datamatrix in ASCII mode. - M6c, PDF417 (`Pdf417.mc`): - Text, byte and numeric compaction as pdf417gen does it, with automatic (ISO) or fixed error-correction levels. - Columns and module size chosen for the screen. - The pattern tables are generated JSON resources, loaded only while encoding (`tools/gen_pdf417_tables.py`). - Identical to pdf417gen. - 2D codes now share a matrix type with a row height, a fit rule for round screens, deferred cache saving and 8-at-a-time packing. - Dev image: pdf417gen, ppf-datamatrix and aztec-code-generator as reference encoders (pure Python, MIT). - M6d, Aztec (`Aztec.mc`): - Compact 1-4 and full range 1-32 layers, error correction of 10, 23, 36 or 50 %, Upper, Lower and Digit modes with Binary Shift. - The geometry is ported from aztec_code_generator, and symbols match it for single-class text. - Decoded with zxing-cpp, also from a screenshot. ## [0.5.0] - 2026-10-20 Milestone M5: import. You can now add entries by pasting `otpauth://` links or an unencrypted Aegis JSON export into the new Import group of the app settings. ### Added - `source/import/`: - A streaming JSON tokenizer: strict, with depth and length limits; skips icons natively; reports incomplete input. - The Aegis reader: database versions 1-3, groups, encrypted exports refused, duplicate fields rejected. - The `otpauth://` parser: run-together URIs, `;` separators, Steam forms, issuer mismatch notes. - The import job: Replace mode, the duplicate and replacement checks, a multi-part buffer sealed with the device key (32 KB, 24 h), and a report. - `source/settings/`: - The settings processor: the Import form, applied after the first frame and on every settings change. It clears the fields, writes the status, remembers form hashes and opens the results screen. - The results store; the list shows "Results ready" until they're read. - The Import group in the phone settings (8 text fields, mode, "Require PIN", "Import now", result). - An 8-byte keyed secret fingerprint in every record header, to find duplicates of locked entries while locked (docs/SECURITY.md). - Seeded fuzz corpus (`tools/gen_fuzz_corpus.py`, `make corpus`); 29 Monkey C and 5 Python tests. The UI flow imports 100 entries through the settings form and checks the peak memory. ### Changed - Info screens space out their items; the results screen is titled "Results". ### Deferred - Google Authenticator `otpauth-migration://` exports (README 11.1): the importer explains that they aren't supported yet. ## [0.4.0] - 2026-10-19 Milestone M4: the screens. The app is usable in the simulator with demo entries; adding real entries comes with M5 (import) and M7 (settings). ### Added - List of entries with live codes, countdowns, lock and barcode icons, and groups. Rows adapt to narrow screens, and headers are read only for the visible rows. - OTP detail: large grouped code, countdown ring (bar on rectangular screens), next code in the last 5 seconds, HOTP "next" with the counter, entry ID. - PIN screen: touch keypad or button digit selector. It covers create (twice, with a warning below 6 digits), unlock and change. Wrong PINs vibrate, and the lockout wait is shown. Pending locked entries are promoted after unlocking. - Menu: lock now, set/change/reset PIN, settings results, entry IDs, clock status, about. Debug builds add demo entries, a performance overlay and an on-watch benchmark. - Glance with the number of entries; the crypto self-test on first start and daily; auto-lock while a locked entry is open. - Option properties with defaults (`resources/settings/properties.xml`). - UI-test build with fake entries, PIN and a frozen clock. A scripted UI flow (`tools/ui_flow.py`) checks 10 screens on each of seven simulated watches. Also new: `tools/gen_fixture_wrap.py`, and 7 Monkey C and 6 Python tests. ### Changed - PBKDF2 XORs 32-bit words: 2.3× faster, and 100 iterations per job step, because 200 tripped the watchdog on the Forerunner 255S. - Codes are computed in their own timer events, at most one SHA-512 code per event. - `make screenshots` runs the scripted flow; goldens are now `test/golden//.png`. Start-up budgets are set from the M4 measurements. ## [0.3.0] - 2026-10-19 Milestone M3: storage, keys and PIN logic. Nothing user-visible yet; the screens come with M4. ### Added - Storage (`source/storage/`): the vault (ordered entries, add, update, move, remove, HOTP counter), the deterministic TLV codec, the record format (readable header plus an encrypt-then-MAC box bound to the entry ID), the device key and the PIN-wrapped master key, resumable PIN derivation, the wrong-PIN lockout (trusted clock with an uptime fallback, optional erase after 10 failures), the session with auto-lock and key wiping, the pending queue for locked entries, schema migrations, the 150-entry cap and clean store-full errors. - `Bytes.toUtf8` with strict UTF-8 validation. - `docs/SECURITY.md`: the stored byte formats and what each protection is tested by. - 34 Monkey C tests (65 in total per device) and storage benchmarks. ### Changed - The PIN wrap and the lockout state are stored under their own keys (`w`, `lk`) instead of inside the metadata (README 6.2 updated). ## [0.2.0] - 2026-10-18 Milestones M1 (prepared; your phone test pending) and M2: crypto and OTP core. Nothing user-visible yet; the home screen still shows only name and version. ### Added - Crypto (`source/crypto/`): SHA-512 in Monkey C (FIPS 180-4), generic HMAC (RFC 2104) for SHA-1/SHA-256/SHA-512 plus native HMAC-SHA256, resumable PBKDF2-HMAC-SHA256 with calibration, HKDF-SHA256, AES-CBC with PKCS#7 (AES-256 chosen), SealedBox encrypt-then-MAC records, and a start-up self-test. - OTP (`source/otp/`): Base32 decoding with exact error positions, HOTP, TOTP (64-bit time, correct after 2038) and Steam Guard codes. - Utilities: constant-time comparison and wiping (`Bytes`), the 2038-safe clock and trusted-clock check (`Clock`), resumable jobs with a timer runner (`Jobs`). - `tools/gen_vectors.py` and `make vectors`: official RFC/FIPS/NIST vectors, each recomputed with Python's standard library or openssl before use, plus seeded cases (SHA padding boundaries, 2038, 2100, all digit counts). - 31 Monkey C unit tests and a benchmark, passing on all four test devices. - M1 settings spike: a separate test app (`spike/settings/`), `make spike`, `make spike-sim`, test strings and `docs/SETTINGS_SPIKE.md`. ### Changed - Screenshot checks are now strict: at most 0.01 % of pixels may differ (one changed character used to pass under the old 0.5 % tolerance). - SHA-512 and PBKDF2 inner loops optimised (37 % and 42 % faster in the simulator) without changing results. ## [0.1.0] - 2026-10-18 Milestone M0: scaffolding. Not useful on a watch yet; the app shows only its name and version. ### Added - Project plan (`README.md`), Phase 0 feasibility findings with what M0 verified (`docs/FEASIBILITY.md`), developer guide, testing and performance documents. - Docker images with pinned versions: `garmin-otp/dev` (Ubuntu 22.04 snapshot, Java 17, headless simulator stack, hash-locked Python tools, checksum-verified SDK manager CLI) and `garmin-otp/docs` (Markdown lint and Mermaid rendering). - `Makefile` running every step in hardened containers: no capabilities, read-only root filesystem, only the project folder mounted, no network outside image builds and `make sdk`, serial even under `make -j`. - `make sdk` (agreements, login, SDK 9.2.0, device files for 72 watches, token deleted afterwards) and `make key` (RSA-4096 developer key). - Build pipeline with strict type checking and warnings as errors, per-size launcher icons for all 72 products, unit tests in the headless simulator on four devices, golden-image screenshot tests, start-up and memory budgets, and the signed `.iq` package. - Python tools with 44 tests: test-log judge, screenshot checker, budget checker, product rule checker, Markdown link checker, project linter (code rules and the Author/Summary header rule) and icon generator. - A minimal watch app (home screen with name and version) and a smoke test.