diff options
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/PERFORMANCE.md | 13 | ||||
| -rw-r--r-- | docs/SECURITY.md | 3 | ||||
| -rw-r--r-- | docs/TESTING.md | 20 |
3 files changed, 34 insertions, 2 deletions
diff --git a/docs/PERFORMANCE.md b/docs/PERFORMANCE.md index 9a5bcbf..9c9a31a 100644 --- a/docs/PERFORMANCE.md +++ b/docs/PERFORMANCE.md @@ -25,6 +25,19 @@ The targets are in [README section 18](../README.md#18-performance-budgets). Thi ## Results +### M6, 2026-10-20 (barcodes, in 0.6.0) + +- **Linear codes** encode in milliseconds while the screen opens; nothing is cached. +- **QR is encoded as a job** in steps sized for the Forerunner 255S's watchdog: at most 2,500 modules to place, 500 to score as rows, 1,000 as columns, or one Reed-Solomon block. Simulator compute time and steps: + + | QR | Compute | Steps | At 50 ms per step on a watch | + | --- | --- | --- | --- | + | Version 3 (29 x 29) | 81 ms | 28 | at least 1.4 s | + | Version 10 (57 x 57) | 312 ms | 105 | at least 5.3 s | + | Version 26 (121 x 121) | 1.4 s | 407 | at least 20 s | + + Version 10 is over README 18's 3 s target. Most of the time is the pauses between steps, and mask scoring (eight masks, every module) is most of the steps. The cache makes every later opening instant. M8 can cut the steps, for example by scoring masks on packed rows. The demo card (version 6) encodes in 2.8 s in the simulated watches (`make screenshots` prints it). + ### M5, 2026-10-20 (version 0.5.0, import) - **Compute.** An Aegis export with 100 entries (19 KB, compact) takes 332 ms of compute in the unit-test benchmark, spread over 159 job steps. diff --git a/docs/SECURITY.md b/docs/SECURITY.md index 45faba6..31723d5 100644 --- a/docs/SECURITY.md +++ b/docs/SECURITY.md @@ -18,13 +18,12 @@ Everything lives in the app's private `Application.Storage`: | `lk` | dictionary | Wrong-PIN state: `f` failures, `d` delay in seconds, `t` trusted time of the last attempt; absent after a success | `Lockout` | | `e.<id>` | bytes | One entry record (format below) | `Vault` | | `p.<id>` | bytes | Pending locked entry: a record sealed with the device key | `Vault` | +| `x.<id>` | bytes | Encoded 2D barcode: a SealedBox (aad `OTPW-X-1` + ID) with the entry's own key (device key, or the master key for locked entries) of width, height, quiet zone and the packed modules. Deleted when the entry is updated, removed or erased | `Vault` | | `imp`, `imp.<n>` | dictionary, bytes | Waiting multi-part import: `imp` = `{"n": chunks, "t": saved at}`, `imp.<n>` = chunks of one SealedBox (device key, aad `OTPW-IMP-1`) of the text | `ImportBuffer` | | `res` | dictionary | Latest settings results (`l` lines, `u` unseen) | `Results` | | `fh` | array | Hashes (SHA-256, first 8 bytes) of the last 32 applied settings forms | `SettingsProcessor` | | `st` | array | Version and day of the last passed self-test | `AppState` | -`x.<id>` (barcode cache) arrives with M6. - The PIN wrap and the lockout state have their own keys instead of living in `m` as README 6.2 first planned. Each wrong PIN rewrites the lockout state before the check, and a small separate value makes that write cheap and keeps it from risking the entry index. ## Keys diff --git a/docs/TESTING.md b/docs/TESTING.md index 29cec49..ca72af3 100644 --- a/docs/TESTING.md +++ b/docs/TESTING.md @@ -26,6 +26,25 @@ Test devices (`TEST_DEVICES`): Forerunner 255S (weakest: 218 px round MIP, butto ## Results +### M6b, 2026-10-20 (QR, in 0.6.0) + +Monkey C unit tests: 111 per device. + +1. **Reference comparison with segno** (`make codes`). + - **31 matrices identical module for module.** They cover versions 1 to 40 and levels L, M, Q and H, numeric, alphanumeric and UTF-8 byte data, forced versions and masks, and automatic choices. The watch picks the same version and mask as segno with `boost_error=False`. + - **Two deliberate differences**, listed in README 12.4: + - segno pads a stream that already ends on a codeword boundary with an extra zero byte; the watch follows ISO 18004. + - segno uses a single mode per symbol; the watch segments optimally. +2. **Decoding.** 30 QR codes from the test run are decoded with zxing-cpp at 2 and 3 px per module. They include mixed segmented content, Unicode, emoji, a Wi-Fi string and all four levels. +3. **Screenshots.** The UI flow opens the fixture's QR card (version 6, its digit run in numeric mode), encoded on the watch as a job the first time (2.8 s in the simulated watches). Its screenshot decodes on all seven watches, as does the EAN-13 card's. + +Also tested: + +- **The encrypted matrix cache:** round trip; tampering ignored; cleared by update and remove; a locked entry's cache unreadable while locked. +- **Capacity errors.** + +What the UI flow found: scoring the eight masks tripped the watchdog, first on the Forerunner 70 (2,500 modules per step) and then on the 255S (1,200). Each job step now works on at most 500 modules when scoring rows and 1,000 when scoring columns. + ### M6a, 2026-10-20 (linear barcodes, in 0.6.0) Monkey C unit tests: 105 per device, with the checks of README 12.4. @@ -147,3 +166,4 @@ Things the simulator can't verify, done by you on the Forerunner 70. The checkli - [ ] M4: check that leaving the app and coming back asks for the PIN again for the locked demo entry. Check that the glance in the glance loop shows the number of entries. - [ ] M5: in Garmin Connect, paste a few `otpauth://` links of test accounts (not your real ones) into the Import fields, turn "Import now" on and save. Check the result on the watch and in the Result field. Also check that the text fields are emptied afterwards: this answers part of the M1 question. - [ ] M6a: with "Debug: demo entries", open "Gym card" (an EAN-13). Scan it with your phone's camera, or a shop scanner if you can, indoors and outdoors. Tell me whether it reads. +- [ ] M6b: open "Member QR" (a demo entry) and scan it with your phone's camera. The first opening encodes it; note roughly how long that takes. The second opening should be instant (cache). |
