Skip to content

ncry v3: latch session failure on transport errors, cap response length - #64

Merged
doc-hex merged 2 commits into
Coldcard:masterfrom
scgbckbone:ncry-v3-review-fixes
Aug 18, 2026
Merged

ncry v3: latch session failure on transport errors, cap response length#64
doc-hex merged 2 commits into
Coldcard:masterfrom
scgbckbone:ncry-v3-review-fixes

Conversation

@scgbckbone

Copy link
Copy Markdown
Contributor

Follow-up fixes from the cross-repository review of the ncry v3 protocol change (firmware PR Coldcard/firmware#759 x ckcc-protocol PR #63), incl. findings confirmed by two independent reviewers.

Changes

  • send_recv: latch _v3_failed on any post-encryption transport failure (Medium). A failed HID write, disconnect, or response timeout previously escaped as a plain AssertionError without poisoning the v3 session. Since encrypt_request already advanced tx_seq and the CTR stream, a caller catching the error and reusing the object could receive a stale — but authentic, sequence-valid — response misattributed to the next command, contradicting the documented "any v3 failure is terminal" guarantee. Now any exception after request encryption latches _v3_failed. Application-level error replies (err_/CCProtoError) deliberately do not poison the session: they arrive as complete, validly-tagged responses and both sides' streams stay synchronized. Verified live against the firmware simulator. v1/v2 behavior unchanged.
  • send_recv: cap accumulated response length (Low). Abort with CCFramingError once the reassembled response exceeds USB_V3_MAX_WIRE_MSG_LEN (v3) / MAX_MSG_LEN (legacy), instead of buffering unbounded data from a malfunctioning or malicious device.
  • PROTOCOL.md: state HMAC key/message argument order and the HKDF key layout explicitly; document that transport failures are terminal.
  • Tests: 6 new regression tests (FakeHID-based): read-timeout and write-failure latching, stale-response rejection after timeout, v3 + legacy overlong-response rejection, and app-error-does-not-poison boundary.

Verification

  • Client unit tests: 13/13 (hardware-dependent tests excluded)
  • Independent from-scratch reference implementation cross-check: 41/41
  • Firmware simulator suite (ncry_tests.py) run against this client: 12/12
  • Live simulator reproduction of the timeout/stale-response scenario: fails safe after the fix

- send_recv: any exception after encrypt_request (USB write failure,
  read timeout, over-long response) now latches _v3_failed for v3
  sessions. Previously a caller catching the error could reuse the
  object and receive a stale (authentic, sequence-valid) response
  misattributed to the next command, contradicting the documented
  guarantee that any v3 failure is terminal.
- send_recv: abort with CCFramingError once the accumulated response
  exceeds USB_V3_MAX_WIRE_MSG_LEN (v3) / MAX_MSG_LEN (legacy) instead
  of buffering unbounded data from a malfunctioning device.
- PROTOCOL.md: state HMAC key/message argument order and HKDF key
  layout explicitly; document that transport failures are terminal.
- tests: regression coverage for timeout/write-failure latching,
  stale-response rejection, and overlong responses.
Locks in the boundary of the transport-failure latching: a complete,
validly-tagged b'err_' reply raises CCProtoError but leaves the v3
session usable (streams stay synchronized), per review triage.
@doc-hex
doc-hex merged commit 2d4b22a into Coldcard:master Aug 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants