Skip to content

fix(protocol): validate the CSRResponse in NocsrValidator, including the CSRNonce - #4938

Open
Apollon77 wants to merge 1 commit into
mainfrom
fix/commissioner-csr-nonce
Open

Apollon77 wants to merge 1 commit into
mainfrom
fix/commissioner-csr-nonce

Conversation

@Apollon77

Copy link
Copy Markdown
Collaborator

Type of change

  • 🐛 Fix — corrects a defect or wrong behavior
  • ✨ Feature — adds new functionality or capability

Description

The commissioner sent a random CSRNonce in CSRRequest but never compared it with the CSRNonce in the NOCSR elements of the CSRResponse. Core § 6.4.6.1.1 says: "The CSR Nonce in NOCSR Information SHALL match the Commissioner's CSR Nonce." Without the check, a CSRResponse to an earlier CSRRequest in the same session was accepted, and the commissioner built the NOC from that older key. A response from another session already failed, because the DAC signature covers the session's attestation challenge.

Why a validator. The commissioning flow checked the CSRResponse inline. The checks for the AttestationResponse live in DeviceAttestationValidator, with a named check per failure and their own unit tests. NocsrValidator does the same for the CSRResponse: empty response, the DAC signature, decoding the NOCSR elements, the CSRNonce (new), and the CSR itself. A failed check throws NocsrError with the NocsrCheck that failed. Before, an empty or undecodable response threw UnexpectedDataError or a TLV error, and the cause of a failed signature check was dropped. Like DeviceAttestationValidator, the validator passes InternalError and ImplementationError through and reports any other error as a failure of the device's data; both validators now use one function for that rule.

All these checks are hard failures. Unlike device attestation findings, the attestation policy cannot accept them. The flow now throws InternalError if the DAC public key is missing at this step; before, it skipped the signature check without notice. The attestation step always sets the key or fails, so this does not happen today.

When a check fails, the commissioner tries to end the device's fail-safe, as it does when other errors stop commissioning, unless the device stopped responding.

Tests: unit tests for each check, and a commissioning test with a device that signs its NOCSR elements over a changed nonce. Removing any single NocsrCheck check makes its test fail.

Checklist

  • I understand the code I am submitting and can explain how it works (AI policy)
  • Tests added or updated to cover the change
  • npm test passes
  • npm run format-verify and npm run lint pass
  • CHANGELOG updated

🤖 Generated with Claude Code

…the CSRNonce

The commissioner did not compare the CSRNonce in the NOCSR elements with
the nonce it sent in CSRRequest, which Core § 6.4.6.1.1 requires.
Without the check, a CSRResponse to an earlier CSRRequest in the same
session was accepted, and the commissioner built the NOC from that older
key.

The CSRResponse checks move from the commissioning flow into
NocsrValidator, next to DeviceAttestationValidator: empty response, DAC
signature, NOCSR decoding and the CSR itself. The CSRNonce check is new.
Each failure throws NocsrError with the check that failed. No
attestation policy can accept these failures. Both validators use one
rule to tell a defect of our code from bad device data.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings November 10, 2026 19:18
@mergify

mergify Bot commented Nov 10, 2026

Copy link
Copy Markdown
Contributor

Tick the box to add this pull request to the merge queue (same as @mergifyio queue). If it does not match the queue conditions yet, it enters the queue once it does.

  • Queue this pull request

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The validator throws a raw TypeError when mandatory fields are omitted and accepts signed NOCSRElements exceeding the 900-byte limit, requiring explicit validation and regression tests.

2 open issues
What changed in this PR

This PR closes a same-session CSR replay gap by checking the returned CSRNonce before the commissioner issues operational credentials.

Changes:

  • Adds and exports NocsrValidator with classified validation failures.
  • Integrates validation into commissioning and shares local-defect detection with device attestation.
  • Adds unit tests, a commissioning regression test, and changelog entries.

Build, lint, formatting, and tests were not run in this review environment.

File Description
packages/​protocol/​test/​certificate/​NocsrValidatorTest.ts Tests valid responses, validation failures, and local-error propagation.
packages/​protocol/​src/​peer/​ControllerCommissioningFlow.ts Retains the request nonce and delegates CSRResponse validation.
packages/​protocol/​src/​certificate/​PeerData.ts Adds shared local-defect classification.
packages/​protocol/​src/​certificate/​NocsrValidator.ts Implements CSRResponse validation and classified errors.
packages/​protocol/​src/​certificate/​index.ts Exports the new validator API.
packages/​protocol/​src/​certificate/​DeviceAttestationValidator.ts Uses the shared error-classification helper.
packages/​node/​test/​node/​CsrNonceCommissioningTest.ts Tests commissioning rejection of a signed mismatched nonce.
CHANGELOG.md Documents the validator and commissioning behavior changes.

Review effort: Balanced

{ crypto, attestationChallenge, csrNonce, dacPublicKey }: Context,
{ nocsrElements, attestationSignature }: Response,
): Promise<Bytes> {
if (nocsrElements.byteLength === 0 || attestationSignature.byteLength === 0) {
{ crypto, attestationChallenge, csrNonce, dacPublicKey }: Context,
{ nocsrElements, attestationSignature }: Response,
): Promise<Bytes> {
if (nocsrElements.byteLength === 0 || attestationSignature.byteLength === 0) {
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