# 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.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.