Skip to content

Commissioning fails with context deadline exceeded at AttestationRequest on real commercial devices #43

@p0fi

Description

@p0fi

Summary

Commissioning a real commercial Matter device fails with a 30-second timeout at the AttestationRequest step. All prior steps succeed (PASE, ArmFailsafe, ReadCommissioningInfo, ReadBasicInfo).

● Commissioning device with code 0449-450-5895 as node 9
● Parsing setup code  0ms
● Discovering device  4.7s
● Establishing PASE session  2.3s
● Arming failsafe timer  120ms
● Reading commissioning info  89ms
● Reading basic information  94ms
⠴ Requesting attestation✗ Commissioning failed: commissioning: requesting attestation: invoking AttestationRequest: interaction: receiving response: context deadline exceeded

The device is a real, commercially-shipped Matter product. It commissions successfully with Apple Home, chip-tool, and other controllers.

Investigation

The timeout originates in internal/interaction/client.go:255exchange.Receive(recvCtx) hits the 30-second invokeResponseTimeout without receiving a message. This means the device's AttestationResponse either:

  1. Was received by the UDP layer but dropped before reaching the exchange, or
  2. Was never sent by the device because the request was malformed.

Confirmed bugs found during investigation

Bug 1 – AttestationResponse TLV not parsed (flow.go:884-888)

info := AttestationInfo{
    Elements:  resp,
    Signature: resp, // TODO: parse AttestationResponse TLV properly
    Nonce:     nonce,
}

The raw response bytes are used for both Elements and Signature. The spec defines AttestationResponse (cluster 0x003E, command 0x01) as:

Tag Field Type
0 AttestationElements octet-string (TLV blob)
1 AttestationSignature octet-string (64 bytes)

These two fields must be extracted from the response TLV. As-is, attestation validation will always fail once the request step is fixed (the validator would compare a 64-byte signature against resp which is the full raw response).

Bug 2 – Wrong attestation challenge in ValidateAttestation call (flow.go:347)

if err := c.Attestation.ValidateAttestation(attestation, dacChain.DAC, attestation.Nonce); err != nil {

The third argument is attestation.Nonce (the 32-byte random nonce we sent), but the signature covers AttestationElements || AttestationChallenge, where AttestationChallenge is the last 16 bytes of the PASE session's S2RKey — not the nonce. The correct value is paseSession.AttestationChallenge (already stored in the PASE protocol.Session).

Per Matter spec §6.4.5.3:

The attestation signature SHALL be computed over the concatenation of the AttestationElements and the AttestationChallenge derived from the commissioning session keys.

Bug 3 – No MRP retransmission for sent messages

sendMessage in controller.go calls c.conn.Send directly, bypassing the transport.MRP retransmission machinery. While the ExFlagReliable bit is set on outgoing messages (correctly requesting reliable delivery from the peer), our own outgoing messages are never retransmitted if a UDP packet is lost.

This currently works because prior commissioning steps succeed. But if the AttestationRequest UDP packet is silently dropped once (possible on some network paths), we simply never get a response.

Bug 4 – Silent packet drops with no debug logging

In runMessagePump (controller.go:237), decode errors cause a continue with only a Warn log. If the device's AttestationResponse has a subtly different nonce format or includes a source node ID we don't expect, decryption silently fails and the packet is dropped — leaving exchange.Receive to time out 30 seconds later with no indication of what happened.

Steps to reproduce

matter commission code <manual-pairing-code-of-real-commercial-device>

Expected behavior

Commissioning should progress past attestation (attestation errors should report a specific failure, not a timeout).

Actual behavior

30-second hang then context deadline exceeded.

Proposed fixes

  1. Fix AttestationResponse parsing — extract tag 0 (AttestationElements) and tag 1 (AttestationSignature) from the response TLV in requestAttestation.
  2. Fix AttestationChallenge in validation call — pass paseSession.AttestationChallenge (from protocol.Session.AttestationChallenge) instead of attestation.Nonce.
  3. Add debug logging for dropped packets — log the full hex dump at debug level when codec.Decode fails, so matter --verbose shows exactly what the device sent.
  4. Investigate and fix MRP retransmission for the sender path.

Related code locations

Location Issue
internal/commissioning/flow.go:847-896 requestAttestation — response not parsed
internal/commissioning/flow.go:344-350 Wrong attestation challenge passed to validator
internal/controller/controller.go:192-264 Message pump — silent drops
internal/interaction/client.go:162-173 30s hard-coded response timeout

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingcommissioningPASE/CASE, BLE, on-network, attestationprotocolInteraction model, TLV, transport, crypto

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions