Skip to content

fix(panel): a PIN below a stricter policy still identifies, and the too-short line says the minimum - #256

Merged
angeloINTJ merged 4 commits into
mainfrom
fix/pin-entry-below-policy
Oct 5, 2026
Merged

angeloINTJ merged 4 commits into
mainfrom
fix/pin-entry-below-policy

Conversation

@angeloINTJ

@angeloINTJ angeloINTJ commented Oct 5, 2026 •

Copy link
Copy Markdown
Owner

Two defects of the panel's PIN, found while #255 was checked on the rig.

  1. Raising the PIN minimum locked shorter PINs out of the panel. A stricter policy marks every account holding a PIN to choose a new one, so that an account whose PIN is now too short identifies with it and goes to the change. But the keypad refused that entry as too short, against the new minimum, before Core 0 saw it.
  2. "PIN too short (min 4)" said 4 under any policy. The number was written into the line, in the firmware and in both packs.

One commit per defect, then the flash record.

What changes

main 8388f32 this branch
Proving a PIN shorter than the policy's minimum refused, "PIN muito curto (mín. 4)"; the account is out of the panel Core 0 checks it; if right, the account goes straight to Novo PIN
A PIN the web set below the minimum (the web does not check it, finding 7) refused the same way, the one thing that held it to the policy identifies and goes to Novo PIN: a tap is a character, so the entry's length is the PIN's
The too-short line "(mín. 4)" whatever the minimum the screen's real minimum, through a {n} marker
A pack line that drops the marker not checked check_lang_packs.py (before every build) fails

PinKb::entryMinLen( ) is the shortest entry a screen takes: 4 to prove a PIN, the policy's minimum to choose one. PinKb::changeAfterProof( ) decides the forced change: the mark, or a PIN shorter than the policy. display/CountInText.h fills the marker. It never hands the pack line to printf: packs are uploaded, so a %s in a line prints as %s.

What gets worse: an entry of 4 characters or more is now a real attempt, so a wrong one counts on the account's ladder. One shorter than the policy used to cost nothing.

What stays: narrowing the alphabet or lowering the length ceiling still strands an old PIN with a letter that left the cards, or longer than the ceiling. The cards cannot offer the first, and Core 0 cannot resolve the second in time. Manual chapters 8 and 30 now say so, with the way out (a new PIN from the panel's Users, the web or the console).

Tests first

  • Native suite (287 of 287, plainly and under ASan and UBSan; the other eight suites pass too). Each new case first failed against main's behaviour:
    • test_identification_takes_a_pin_shorter_than_the_policy: "Expected 4 Was 5. identifying";
    • test_a_pin_below_the_policy_is_changed_once_it_proves: "shorter than the policy, unmarked";
    • test_count_in_text_puts_the_number_where_the_line_says: "Expected 'PIN too short (min 5)' Was 'PIN too short (min {n})'". It also covers a pack older than the marker, a %s%n in a line, and a short buffer.
  • The language gate: with {n} in the firmware's English, it failed on both packs until their lines carried it.
  • The gates job's scripts and build_manual.py --check pass.
  • Every commit builds pico_w_test.

On the rig

pico_w_test of each tree, from the S1 bench dump, restored and read back byte for byte before each image. The policy went from 4/3/0 to 5/3/0 over pshort (PIN 1357) and plong (24680). The web then gave pweb 2468, unmarked.

main this branch
pshort, 1357 "PIN muito curto (mín. 4)", no SEC_PIN_OK identified, Novo PIN
pweb, 2468 the same identified, Novo PIN
plong, 24680 identified, Novo PIN identified, Novo PIN
three digits on Novo PIN "PIN muito curto (mín. 4)" "PIN muito curto (mín. 5)", with the new pt-BR pack uploaded

On this branch, pshort chose 13579 and got the menu, and from then on its new PIN opened the menu directly. On the first try after the restart that loaded the pack, pshort's taps landed on the dashboard. That was the instrument, not the device; the second try is the one above. The rig is back in its arrival state (R30).

Flash

image main this branch budget
release 1,020,940 1,021,092 (+152) 1,021,012 → 1,021,164
test 1,025,516 1,025,668 (+152) 1,025,660 → 1,025,812
test_https 1,030,492 1,030,652 (+160) 1,030,492 → 1,030,652
asserts 1,023,236 1,023,388 (+152) 1,023,268 → 1,023,420
alpha, Air unchanged unchanged hold
Pico 2 W 997,000 997,160 (+160) 997,128 → 997,288

The signed release .bin has 7,019 B left under the OTA ceiling.

Docs

  • Manual chapter 8 (the web PIN, wrong PINs, the PIN messages, the policy) and chapter 30 (two troubleshooting rows) are updated; MANUAL.pt-BR.html is regenerated.
  • The READMEs count 639 cases, and quote the release at 97.8 % of its slot. The last commit fixes that number: CI caught it, because I had run check_readme_numbers.py before the flash record moved.
  • tools/flash_budget.json and §3.1 of PLANO_STABLE.md carry the record.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BniVDEDSHGQgcoqmCRn5um

Ângelo Moisés Alves and others added 3 commits October 4, 2026 23:39
Raising the PIN minimum marks every account holding a PIN to choose a new
one (markPinsBelowPolicy), so that an account whose PIN is now too short
identifies with it and goes to the change. The keypad refused that entry as
too short, against the new minimum, before Core 0 saw it, and the account
was out of the panel. Core 0 resolves any length from PIN_LEN_MIN.

PinKb::entryMinLen( ) is the shortest entry a screen takes: PIN_LEN_MIN to
prove a PIN, the policy's minimum to choose one. pinSubmit( ) and the row
of dots use it.

That refusal was also the one thing that held a PIN the web set below the
minimum to the policy: the web does not check it (finding 7 of
docs/analysis/PLANO_REVISAO_EXTERNA.md), and no tightening marks such a
PIN. So PinKb::changeAfterProof( ) sends a PIN that proves shorter than the
policy to the change, marked or not. A tap is a character, so the entry's
length is the PIN's.

An entry of 4 characters or more is now a real attempt, and a wrong one
counts on the account's ladder; one shorter than the policy used to cost
nothing. Narrowing the alphabet or lowering the length ceiling still
strands a PIN with a letter that left the cards, or longer than the
ceiling: the cards cannot offer the one, and Core 0 cannot resolve the
other in time. Manual chapters 8 and 30 say so.

Tests first, in the native suite (286 of 286, under ASan and UBSan too):
- test_identification_takes_a_pin_shorter_than_the_policy failed with
  main's rule ("Expected 4 Was 5. identifying");
- test_a_pin_below_the_policy_is_changed_once_it_proves failed with the
  mark alone ("shorter than the policy, unmarked").

On the rig, pico_w_test from the S1 dump. The policy went from 4/3/0 to
5/3/0 over pshort (PIN 1357) and plong (24680); the web then gave pweb
2468:
- main 8388f32: pshort and pweb got "PIN muito curto (mín. 4)" and no
  SEC_PIN_OK, so they stayed out; plong identified and went to Novo PIN;
- this branch: all three identified and went to Novo PIN. pshort chose
  13579 and got the menu, and from then on its new PIN opened the menu
  directly.

Manual chapters 8 and 30 describe identification and the policy as they
are now; MANUAL.pt-BR.html regenerated. The READMEs count the two cases.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BniVDEDSHGQgcoqmCRn5um
"PIN too short (min 4)" said 4 under any policy: the number was written
into the line, in the firmware and in both packs. On the rig, with the
minimum at 5, Novo PIN answered three digits with "PIN muito curto
(mín. 4)".

The line carries "{n}" now, and display/CountInText.h puts the screen's
shortest entry there: the policy's minimum to choose a PIN, 4 to prove one.
Not printf on the line: a pack is uploaded, and a pack line is never a
format string, so a "%s" in it prints as "%s". A pack older than the marker
shows its line as it is.

check_lang_packs.py, which runs before every build, now refuses a pack line
that drops a marker the firmware's line has, or carries one it does not.

Tests first:
- test_count_in_text_puts_the_number_where_the_line_says, in the native
  suite, failed while the helper copied the line ("Expected 'PIN too short
  (min 5)' Was 'PIN too short (min {n})'"); 287 of 287 now;
- with "{n}" in the firmware's English, the gate failed on both packs
  until their lines carried it.

On the rig, this branch with the new pt-BR pack uploaded: "PIN muito curto
(mín. 5)" (screenshot).

Manual chapter 8's table says N; MANUAL.pt-BR.html regenerated.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BniVDEDSHGQgcoqmCRn5um
All seven images built from this branch, on main 8388f32:
- release, test and asserts: 'measured' and the .bin +152 B;
- test_https and pico2_w_release: +160 B;
- alpha and Air: unchanged, as neither has a panel PIN.
Each budget that moves moves by its image's growth, which keeps its
margin: release 1,021,012 -> 1,021,164, test 1,025,660 -> 1,025,812,
test_https 1,030,492 -> 1,030,652, asserts 1,023,268 -> 1,023,420,
pico2_w_release 997,128 -> 997,288.

On release: drawPinDotsInto( ) +104 B, the marker filled in; the new
pinEntryMinLen( ) +28 B; panelIdentify( ) +8 B.

The OTA headroom table of docs/analysis/PLANO_STABLE.md section 3.1
follows: release 7,019 B, test 2,443 B (already under one sector, and
warned), asserts 4,723 B, pico2_w_release 571,343 B against its slot.
check_flash_budget.py passes on the seven builds, and --table passes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BniVDEDSHGQgcoqmCRn5um
@github-actions github-actions Bot added documentation Improvements or additions to documentation tests Testing infrastructure and test coverage display Display, TFT, touchscreen, and themes i18n Internationalization and translations tools Build tools, scripts, and developer tooling code Firmware source changes (src/, WebUI.h) labels Oct 5, 2026
The +152 B of this PR take pico_w_release from 1,020,940 to 1,021,092
used bytes of the 1,044,480 B slot: 97.75 % to 97.76 %, which rounds up.
check_readme_numbers.py failed in CI on the three READMEs; it passed
locally only because it ran before the flash record moved.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BniVDEDSHGQgcoqmCRn5um
@angeloINTJ
angeloINTJ merged commit de13562 into main Oct 5, 2026
11 checks passed
@angeloINTJ angeloINTJ mentioned this pull request Oct 5, 2026
@angeloINTJ
angeloINTJ deleted the fix/pin-entry-below-policy branch October 15, 2026 22:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

code Firmware source changes (src/, WebUI.h) display Display, TFT, touchscreen, and themes documentation Improvements or additions to documentation i18n Internationalization and translations tests Testing infrastructure and test coverage tools Build tools, scripts, and developer tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant