[PATCH] rsn_supp: Don't encrypt EAPOL-Key 4/4.

Nicolas Cavallari Nicolas.Cavallari
Wed Feb 8 09:33:37 PST 2012

On 08/02/2012 17:18, Andreas Hartmann wrote:
> Nicolas Cavallari schrieb:
>> On 08/02/2012 12:31, Andreas Hartmann wrote:
>>> Nicolas Cavallari schrieb:
>>>> On 06/02/2012 21:05, Jouni Malinen wrote:
>>>>> On Mon, Feb 06, 2012 at 06:39:02PM +0100, Nicolas Cavallari wrote:
>>>>>> This is just a proposed solution to a problem that i'm having.  I don't
>>>>>> think it is the best nor it does not break something else, so i'm asking
>>>>>> what would be the right approach here.  I was also thinking about
>>>>>> reusing hostapd's eapol_send.
>>>>> Well, the proper approach would be to implement setprotection rather
>>>>> than this workaround..
>>>> If i implement setprotection, there are still drivers that won't or
>>>> can't support it... that mean different code path depending on if the
>>>> driver support it or not, because if the driver does not support it but
>>>> we assume it does, we would set a key for rx and tx ... before sending
>>>> 4/4.  I already fell into that trap ;)
>>>> I'll start experimenting a setprotection implementation anyway...
>>>>>> I'm currently experimenting with a IBSS RSN network of 4 station, but
>>>>>> while testing, there are always two or more handshakes that fails,
>>>>>> because of a lost EAPOL-Key 4/4 frame.  In IBSS mode, the two station
>>>>>> will not retry association, so the network will not recover and will
>>>>>> eventually split.
>>>>>> Also, between the time where 3/4 was received by the supplicant and 4/4
>>>>>> was received by the authenticator, the opposite four way handshake is
>>>>>> stalled for the same reason.
>>>>> These are bit problematic to fix in any other way than by adding support
>>>>> for unidirectional (RX only) key operations, i.e., MLME-SETPROTECTION
>>>>> primitives. The AP (or well, Authenticator to include IBSS case) side
>>>>> may start transmitting encrypted frames immediately after receiving
>>>>> EAPOL-Key 4/4 and trying to remove/add keys on the STA/Supplicant side
>>>>> is opening a race condition here.
>>>> This race condition already exists; the supplicant currently set the key
>>>> after sending 4/4, this patch does not change anything about that. The
>>>> same race also exist in the authenticator case, after receiving 4/4, and
>>>> we can't do much to protect against that one. Both are
>>>> equally feasible in IBSS mode, because the opposite four way hs is
>>>> occurring at the same time.
>>> Please, may I ask you a question?
>>> I do encounter here a similar (?) problem since ages. hostapd sends 3/4
>>> to supplicant, supplicant sends 4/4 and is ready and all is fine.
>>> But there is one problem: it only works, as long as there is no data
>>> stream (payload) active between supplicant and hostapd.
>>> If there is payload at the same time (e.g. created with netperf), 4/4 is
>>> sent by supplicant (can be seen in wireshark), but isn't seen by
>>> hostapd. hostapd therefore retries 3/4, but supplicant doesn't see these
>>> packages any more: connection is broken, because hostapd closes the
>>> connection because of missing 4/4!
>> Which driver(s) do you use ?
> I'm using rt2800pci as AP and rt5572sta as STA (or rt2800usb or ath9k
> - the latter even as AP, too).
>> It could be the same problem or a different one. Do you have a trace
>> made from a separate interface in monitor mode ? that way we could see
>> which packets are encrypted or not, and which are acked... logs from the
>> both side might be useful too.
> I could provide them if you really need them, but I fear, they are
> incomplete.
> Rekeying behaviour without payload:
> I compared the 4 packets of the original and the patched wpa_supplicant
> (gathered on the supplicant itself with wireshark) but couldn't see any
> difference between them.
> If I trace it with an external interface, all of the 4 packets are 
> encrypted and shown as "QoS Data". IEEE 802.11 QoS Data / Frame
> Control / Flags / Protected flag is set (Data is protected). Each of
> the frame is acked.
> I expected that the 4/4 frame should have been unencrypted with your
> patch. But this seems not to be.

Then my patch does not work... Or the kernel/driver does something
completely strange. I should check my patch more in infrastructure mode
to see if my 4/4 are encrypted ...

> BTW: the first 4 way handshake, directly after the initialisation of the
> connection, is always sent completely unencrypted.
>> But the fact that the supplicant cannot receive the duplicated 3/4 frame
>> might indicate something. You are seeing this at key renegotiation time,
>> right ?
> Correctly - PTK rekeying.
> I checked something more now: I disabled hw encryption on the AP and
> inserted debug output in net/mac80211/wpa.c
> (ieee80211_crypto_ccmp_decrypt()) to see, where and when the frames are 
> dropped during rekeying with payload.
> The dropping of the frames starts directly after 2/4 has been done and
> 3/4 has been sent the first time (long before the first 4/4 timeout). I
> could see, that the first few frames (mostly data frames I suppose) are
> dropped here:
>         if (!(status->flag & RX_FLAG_DECRYPTED)) {
>                 u8 scratch[6 * AES_BLOCK_SIZE];
>                 /* hardware didn't decrypt/verify MIC */
>                 ccmp_special_blocks(skb, pn, scratch, 1);
>                 if (ieee80211_aes_ccm_decrypt(
>                             key->u.ccmp.tfm, scratch,
>                             skb->data + hdrlen + CCMP_HDR_LEN, data_len,
>                             skb->data + skb->len - CCMP_MIC_LEN,
>                             skb->data + hdrlen + CCMP_HDR_LEN)) {
>                         printk(KERN_INFO "ieee80211_crypto_ccmp_decrypt 10 RX_DROP_UNUSABLE\n");
>                         return RX_DROP_UNUSABLE;
>                         }
>         }

So the replay counter was acceptable but the key was wrong, strange
indeed. I would proceed to debug the ieee80211_tx_h_select_key() on the
supplicant, if it's possible.

> All other frames are even dropped before:
>         if (memcmp(pn, key->u.ccmp.rx_pn[queue], CCMP_PN_LEN) <= 0) {
>                 key->u.ccmp.replays++;
>                 printk(KERN_INFO "ieee80211_crypto_ccmp_decrypt 9 RX_DROP_UNUSABLE\n");
>                 return RX_DROP_UNUSABLE;
>         }

During my test, when either side changes the key, it resets the replay
counter to 0, so if one has still the old key, it detects the frame as a
replay, but really is a key conflict.

More information about the Hostap mailing list