diff options
| author | cirakg <cirakg@cirakg.xyz> | 2026-10-25 12:46:42 +0100 |
|---|---|---|
| committer | cirakg <cirakg@cirakg.xyz> | 2026-10-25 12:46:42 +0100 |
| commit | e0384274bbeab999279bdc35bdddbd8a3a451aeb (patch) | |
| tree | 206abf2390f6bd30d1ddbcc02717f1009b4cc659 /docs | |
| parent | 6b17954f654d83c754cbdaae37f98689b9a4fcb7 (diff) | |
docs: record the settings spike and the Forerunner 70 benchmark
Spike (2026-10-21, Android): 8000 characters per field and 64,000 in
total arrive; URIs with & save; the mixed special-character string is
refused, so the test strings now split it into one group each plus a
fake Aegis export. Benchmark: SHA-1 TOTP 18 ms, SHA-512 732 ms, PBKDF2
2,000 iterations per second.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/PERFORMANCE.md | 17 | ||||
| -rw-r--r-- | docs/SETTINGS_SPIKE.md | 42 |
2 files changed, 45 insertions, 14 deletions
diff --git a/docs/PERFORMANCE.md b/docs/PERFORMANCE.md index 4c35be6..0433457 100644 --- a/docs/PERFORMANCE.md +++ b/docs/PERFORMANCE.md @@ -27,6 +27,23 @@ The targets are in [README section 18](../README.md#18-performance-budgets). Thi ## Results +### Forerunner 70, 2026-10-21 (real watch, debug build 0.8.0) + +The first numbers from the real watch, read from "Debug: benchmark" on the sideloaded debug build (docs/YOUR_STEPS.md part 4.2): + +| Value | Forerunner 70 | Simulator (M4/M8) | Note | +| --- | --- | --- | --- | +| TOTP, SHA-1 | 18 ms | under 1 ms | One code per timer event is far inside the watchdog | +| TOTP, SHA-512 | 732 ms | 17.5 ms | About 40 times SHA-1, as the simulator ratio predicted. A list with several SHA-512 entries fills in about one per second; the detail screen shows the code within a second | +| PBKDF2 | 2,000 iterations per second | about 12,000 | Exactly the lower bound in README 18, so setting a PIN calibrates to the 2,000-iteration floor and unlocking takes about 1 s | +| Header reading | 0 entries in 1 ms | | The benchmark ran before the demo entries were added, so this says nothing yet | +| Self-test | 292 ms | | The crypto known-answer tests at start-up of the debug build | +| Memory at the benchmark | 179 of 763 KB (23 %) | 21 % at the first frame | Well under the 70 % budget | + +Other timings reported from the same session: settings saved on the phone reach the watch in about 1 second (settings spike, test G). The Member QR demo card took noticeably long on its first opening and about 1 second the second time; its progress display jumped to 90 % almost at once, which is fixed in the next version (the percentage now follows the work, not the phase count). + +What this means: unlocking stays at about 1 s because 2,000 iterations is also the minimum the app accepts. An attacker who copies the watch's storage still has to run those 2,000 iterations per guess, but on a PC that is microseconds, so the PIN protects mainly against someone holding the watch (lockout and the optional erase after 10 wrong PINs), not against an offline attack on a copied file system (docs/SECURITY.md). + ### M8, 2026-10-21 (polish and release candidate, version 0.8.0) - **Memory, Forerunner 255S (512 KB):** diff --git a/docs/SETTINGS_SPIKE.md b/docs/SETTINGS_SPIKE.md index 340670a..a3e2328 100644 --- a/docs/SETTINGS_SPIKE.md +++ b/docs/SETTINGS_SPIKE.md @@ -61,23 +61,37 @@ Keep the spike app open on the watch for tests A to H, except where a test says ## Results -Fill this in (or tell me the values and I'll record them). Date, phone model, Garmin Connect app version (Settings, About) and the watch firmware help explain later differences. +Run on 2026-10-21 by the project owner with the Store beta of Settings Spike on the Forerunner 70 and an Android phone. The two version numbers were reported as "2.50" and "18.32" without labels; the phone model wasn't recorded. | Item | Result | | --- | --- | -| Date, phone, Garmin Connect version, watch firmware | | -| A. Longest single field that arrives complete | | -| A. What happens above that (refused / error / cut to N) | | -| B. Largest total across the eight fields | | -| C. Characters that didn't arrive or blocked saving | | -| D. Status written by the watch visible on the phone (yes/no) | | -| D. Password field empty after the watch cleared it (yes/no) | | -| E. Changes arrive while the app is open (yes/no) | | -| E. Changes arrive while the app is closed, seen at next start (yes/no) | | -| F. Password field: arrives on the watch, masked on the phone (yes/no each) | | -| G. Time from Save to the watch updating (short / longest) | | -| H. Phone-side validation of the number fields (message) | | -| Other observations | | +| Date, phone, Garmin Connect version, watch firmware | 2026-10-21, Android phone, versions 2.50 and 18.32 (which is the app and which the firmware is to be confirmed) | +| A. Longest single field that arrives complete | 8000 characters, the longest string tested | +| A. What happens above that (refused / error / cut to N) | Not tested: no string longer than 8000 was tried | +| B. Largest total across the eight fields | 64,000 characters (8 fields of 8000) saved and arrived complete | +| C. Characters that didn't arrive or blocked saving | The phone **refused to save** the `special` string. An `otpauth://` URI with `&`, `?`, `=`, `:` and `@` saved and arrived. Which of `< > \ " { } %`, the accented letters, `€` or the emoji blocks saving is not known yet: see the follow-up below | +| D. Status written by the watch visible on the phone (yes/no) | Yes: the phone shows the "seen ... change #N" line and its number goes up | +| D. Password field empty after the watch cleared it (yes/no) | Yes | +| E. Changes arrive while the app is open (yes/no) | Yes | +| E. Changes arrive while the app is closed, seen at next start (yes/no) | Yes | +| F. Password field: arrives on the watch, masked on the phone (yes/no each) | When the settings were opened again, the field was empty, because the watch clears it after every save (as in D). The "got N chars" line on the watch wasn't reported. The OTP Wallet secret fields, which use the same field type, worked in the real-watch test (YOUR_STEPS 5.2), so the field is usable | +| G. Time from Save to the watch updating (short / longest) | About 1 second | +| H. Phone-side validation of the number fields (message) | 301 is rejected by the phone and the settings can't be saved. The switch, list (option C, value 2) and the long number 9999999999 arrive (`switchField: true`, `listField: 2`) | +| Other observations | None | + +### Follow-up for test C + +The `special` string mixes many characters, so its failure doesn't say which one the phone rejects. The test-string page now has eight short strings, one per character group (`C1` percent, `C2` angle brackets, `C3` backslash, `C4` double quote, `C5` braces, `C6` accented letters and `€`, `C7` emoji, `C8` a JSON fragment), and `aegis-json`, a complete fake unencrypted Aegis export with one entry. Paste each into Text 2 and save; note which ones the phone refuses. Regenerate the page with `python3 spike/settings/test-strings/make_test_strings.py`. + +This decides whether Aegis JSON can be pasted into the import fields at all (it needs `"`, `{`, `}` and sometimes `\`). If the phone refuses those characters, Aegis import goes back to the design table: the fallback is pasting `otpauth://` URIs, which are confirmed to work. + +### What the results change + +- **Import field size (A, B).** The import design assumed fields of a few hundred characters. One field takes at least 8000 characters and the eight together at least 64,000, so Garmin Connect is not the limit: the watch's 32 KB import buffer is (README 11.2). About 160 compact Aegis entries, or 60 to 80 pretty-printed ones, fit in one sync. The eight `impText` fields and multi-part import stay as they are. +- **`otpauth://` URIs (C).** They paste as they are, so the parser doesn't need the `;` fallback for `&`. +- **Write-back (D).** Status lines written by the watch (the forms' **Result** fields) and cleared secret fields show on the phone after reopening the settings. +- **Processing (E, G).** Changes arrive within about a second while the app is open, and at the next start when it is closed. The app already processes forms on both paths. +- **Number validation (H).** The phone enforces `min` and `max` itself, so the watch-side range checks are a second line of defence only. ## How the results feed the design |
