You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Green Power: honour the EZSP status field instead of discarding it #733
Follow-up to #713, deliberately left out of it to keep that PR to one subject.
_handle_gp_frame in #713 unpacks the gpepIncomingMessageHandler callback and discards four of its fields: status, autoCommissioning, bidirectionalInfo and mic. It then forwards the GPD command payload as a ZCL GP Notification.
status is the one that matters. sl_GpStatus (bellows/types/named.py) distinguishes:
So the NCP tells us when it declined to security-process a frame, or when security failed outright, and we currently forward it as an ordinary GP Notification regardless.
That matters because of what a GP Notification means. Per A.3.5.2, a proxy does not send one for a frame whose security processing failed - so a sink is entitled to treat a Notification as already security-processed. And the sink cannot check for itself: the GP Notification (Figures 23/24) carries no MIC and no RxAfterTx bit, and those are part of the CCM* associated data (NWK FC || Ext NWK FC || SrcID || frame counter, A.1.5.4.3.3). There is no way for zigpy to reconstruct the header, so it cannot verify anything.
bellows is the only place this can be fixed properly, because at that point it still holds the mic, autoCommissioning and bidirectionalInfo that zigpy can never recover.
Two options, roughly in increasing order of effort:
Drop frames whose status is AUTH_FAILURE, UNPROCESSED or DROP_FRAME, and forward the rest as now. Small and closes the hole.
Re-emit unprocessed frames as a GP Commissioning Notification with security_failed = 1. That schema does carry the MIC and the extra header bits, which is exactly the route the spec provides for a proxy that could not process security itself. More work, but it is the shape the spec intends and it leaves the sink able to verify.
Option 1 is probably the right first step. Raising it separately rather than growing #713.
Closing this. Looking closer, the framing was wrong.
zigbee-herdsman's ember adapter forwards every gpepIncomingMessageHandler frame as a plain GP Notification and does not filter on the status field either - it logs the status and forwards. So #713 matches the reference implementation here; discarding status is not the deviation I described.
The real difference is on the sink side. herdsman's Green Power controller decrypts the payload itself for FullEncr (0b11) frames: CTR-mode AES-128-CCM* with a nonce of srcID/srcID/frameCounter and a zeroed MIC placeholder - it recovers plaintext without authenticating, since a plain Notification carries no MIC to authenticate against. The header is only needed to verify the MIC, not to decrypt.
That only matters for 0b11 devices. For 0b10 (FullFrameCounterAndMIC) the payload is already cleartext, so forwarding it as-is is correct, and herdsman does not decrypt those either. All the GP hardware captured in these tests is 0b10 (the Busch-Jaeger 6716U), so nothing here exercises the 0b11 path.
So the open question is not "honour the status field in bellows" but "does the sink need to decrypt 0b11 payloads", which lives in the zigpy manager rather than this forwarding code. It needs a capture from an encrypted GPD to settle whether the NCP hands up ciphertext or plaintext when no sink table is programmed. Tracking it where it belongs rather than here.
Activity
nmingam commentedon Jul 23, 2026
Closing this. Looking closer, the framing was wrong.
zigbee-herdsman's ember adapter forwards every
gpepIncomingMessageHandlerframe as a plain GP Notification and does not filter on the status field either - it logs the status and forwards. So #713 matches the reference implementation here; discardingstatusis not the deviation I described.The real difference is on the sink side. herdsman's Green Power controller decrypts the payload itself for FullEncr (0b11) frames: CTR-mode AES-128-CCM* with a nonce of srcID/srcID/frameCounter and a zeroed MIC placeholder - it recovers plaintext without authenticating, since a plain Notification carries no MIC to authenticate against. The header is only needed to verify the MIC, not to decrypt.
That only matters for 0b11 devices. For 0b10 (FullFrameCounterAndMIC) the payload is already cleartext, so forwarding it as-is is correct, and herdsman does not decrypt those either. All the GP hardware captured in these tests is 0b10 (the Busch-Jaeger 6716U), so nothing here exercises the 0b11 path.
So the open question is not "honour the status field in bellows" but "does the sink need to decrypt 0b11 payloads", which lives in the zigpy manager rather than this forwarding code. It needs a capture from an encrypted GPD to settle whether the NCP hands up ciphertext or plaintext when no sink table is programmed. Tracking it where it belongs rather than here.