STATIC void jswP1ReqPending(struct jsw_session *js, jsw_event_t event) { struct ieee802_1x_hdr *eapolHdr; wpa_printf(MSG_DEBUG, "state jswP1ReqPending: event %d\n", event); switch (event) { case JSW_EVENT_JDS_REQ: eapolHdr = js->sm.event_arg; /* XXX Divy. Need to code this */ /* Found a JS guy, so turn off legacy failover timer */ if (jswGetTlvs(js, eapolHdr, JSW_OP_JDS_REQUEST) != 0) { goto failed; } if (jswDeriveSecret(js) != 0) { goto failed; } inc_byte_array(js->key_replay_counter, WPA_REPLAY_COUNTER_LEN); if ((jswSendJdsAck(js)) != 0) { goto failed; } jswTimerStart(js, JSW_DH_TIMEOUT); smSetState(js, jswP1JdsAckPending); break; case JSW_EVENT_TIMEOUT: if (!jswDoRetry(js)) { goto failed; } inc_byte_array(js->key_replay_counter, WPA_REPLAY_COUNTER_LEN); jswTimerStart(js, JSW_DEFAULT_TIMEOUT); if ((jswP1SendJdsOffer(js)) != 0) { goto failed; } break; default: goto failed; } return; failed: jswP1Failed(js, event); }
static int wpa_supplicant_send_smk_error(struct wpa_sm *sm, const u8 *dst, const u8 *peer, u16 mui, u16 error_type, int ver) { size_t rlen; struct wpa_eapol_key *err; struct rsn_error_kde error; u8 *rbuf, *pos; size_t kde_len; u16 key_info; kde_len = 2 + RSN_SELECTOR_LEN + sizeof(error); if (peer) kde_len += 2 + RSN_SELECTOR_LEN + ETH_ALEN; rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, sizeof(*err) + kde_len, &rlen, (void *) &err); if (rbuf == NULL) return -1; err->type = EAPOL_KEY_TYPE_RSN; key_info = ver | WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE | WPA_KEY_INFO_ERROR | WPA_KEY_INFO_REQUEST; WPA_PUT_BE16(err->key_info, key_info); WPA_PUT_BE16(err->key_length, 0); os_memcpy(err->replay_counter, sm->request_counter, WPA_REPLAY_COUNTER_LEN); inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN); WPA_PUT_BE16(err->key_data_length, (u16) kde_len); pos = (u8 *) (err + 1); if (peer) { /* Peer MAC Address KDE */ pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN); } /* Error KDE */ error.mui = host_to_be16(mui); error.error_type = host_to_be16(error_type); wpa_add_kde(pos, RSN_KEY_DATA_ERROR, (u8 *) &error, sizeof(error)); if (peer) { wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK Error (peer " MACSTR " mui %d error_type %d)", MAC2STR(peer), mui, error_type); } else { wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK Error " "(mui %d error_type %d)", mui, error_type); } wpa_eapol_key_send(sm, sm->ptk.kck, ver, dst, ETH_P_EAPOL, rbuf, rlen, err->key_mic); return 0; }
static void wpa_supplicant_send_stk_1_of_4(struct wpa_sm *sm, struct wpa_peerkey *peerkey) { size_t mlen; struct wpa_eapol_key *msg; u8 *mbuf; size_t kde_len; u16 key_info, ver; kde_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN; mbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, sizeof(*msg) + kde_len, &mlen, (void *) &msg); if (mbuf == NULL) return; msg->type = EAPOL_KEY_TYPE_RSN; if (peerkey->cipher == WPA_CIPHER_CCMP) ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; else ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; key_info = ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_ACK; WPA_PUT_BE16(msg->key_info, key_info); if (peerkey->cipher == WPA_CIPHER_CCMP) WPA_PUT_BE16(msg->key_length, 16); else WPA_PUT_BE16(msg->key_length, 32); os_memcpy(msg->replay_counter, peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN); inc_byte_array(peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN); WPA_PUT_BE16(msg->key_data_length, kde_len); wpa_add_kde((u8 *) (msg + 1), RSN_KEY_DATA_PMKID, peerkey->smkid, PMKID_LEN); if (random_get_bytes(peerkey->inonce, WPA_NONCE_LEN)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "RSN: Failed to get random data for INonce (STK)"); os_free(mbuf); return; } wpa_hexdump(MSG_DEBUG, "RSN: INonce for STK 4-Way Handshake", peerkey->inonce, WPA_NONCE_LEN); os_memcpy(msg->key_nonce, peerkey->inonce, WPA_NONCE_LEN); wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key STK 1/4 to " MACSTR, MAC2STR(peerkey->addr)); wpa_eapol_key_send(sm, NULL, ver, peerkey->addr, ETH_P_EAPOL, mbuf, mlen, NULL); }
static void wpa_supplicant_send_stk_3_of_4(struct wpa_sm *sm, struct wpa_peerkey *peerkey) { size_t mlen; struct wpa_eapol_key *msg; u8 *mbuf, *pos; size_t kde_len; u16 key_info, ver; be32 lifetime; kde_len = peerkey->rsnie_i_len + 2 + RSN_SELECTOR_LEN + sizeof(lifetime); mbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, sizeof(*msg) + kde_len, &mlen, (void *) &msg); if (mbuf == NULL) return; msg->type = EAPOL_KEY_TYPE_RSN; if (peerkey->cipher == WPA_CIPHER_CCMP) ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; else ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; key_info = ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_ACK | WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE; WPA_PUT_BE16(msg->key_info, key_info); if (peerkey->cipher == WPA_CIPHER_CCMP) WPA_PUT_BE16(msg->key_length, 16); else WPA_PUT_BE16(msg->key_length, 32); os_memcpy(msg->replay_counter, peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN); inc_byte_array(peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN); WPA_PUT_BE16(msg->key_data_length, kde_len); pos = (u8 *) (msg + 1); pos = wpa_add_ie(pos, peerkey->rsnie_i, peerkey->rsnie_i_len); lifetime = host_to_be32(peerkey->lifetime); wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME, (u8 *) &lifetime, sizeof(lifetime)); os_memcpy(msg->key_nonce, peerkey->inonce, WPA_NONCE_LEN); wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key STK 3/4 to " MACSTR, MAC2STR(peerkey->addr)); wpa_eapol_key_send(sm, peerkey->stk.kck, ver, peerkey->addr, ETH_P_EAPOL, mbuf, mlen, msg->key_mic); }
/** * wpa_sm_key_request - Send EAPOL-Key Request * @sm: Pointer to WPA state machine data from wpa_sm_init() * @error: Indicate whether this is an Michael MIC error report * @pairwise: 1 = error report for pairwise packet, 0 = for group packet * * Send an EAPOL-Key Request to the current authenticator. This function is * used to request rekeying and it is usually called when a local Michael MIC * failure is detected. */ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise) { size_t rlen; struct wpa_eapol_key *reply; int key_info, ver; u8 bssid[ETH_ALEN], *rbuf; if (wpa_key_mgmt_ft(sm->key_mgmt) || wpa_key_mgmt_sha256(sm->key_mgmt)) ver = WPA_KEY_INFO_TYPE_AES_128_CMAC; else if (sm->pairwise_cipher == WPA_CIPHER_CCMP) ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; else ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; if (wpa_sm_get_bssid(sm, bssid) < 0) { wpa_printf(MSG_WARNING, "Failed to read BSSID for EAPOL-Key " "request"); return; } rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, sizeof(*reply), &rlen, (void *) &reply); if (rbuf == NULL) return; reply->type = sm->proto == WPA_PROTO_RSN ? EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; key_info = WPA_KEY_INFO_REQUEST | ver; if (sm->ptk_set) key_info |= WPA_KEY_INFO_MIC; if (error) key_info |= WPA_KEY_INFO_ERROR; if (pairwise) key_info |= WPA_KEY_INFO_KEY_TYPE; WPA_PUT_BE16(reply->key_info, key_info); WPA_PUT_BE16(reply->key_length, 0); os_memcpy(reply->replay_counter, sm->request_counter, WPA_REPLAY_COUNTER_LEN); inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN); WPA_PUT_BE16(reply->key_data_length, 0); wpa_printf(MSG_INFO, "WPA: Sending EAPOL-Key Request (error=%d " "pairwise=%d ptk_set=%d len=%lu)", error, pairwise, sm->ptk_set, (unsigned long) rlen); wpa_eapol_key_send(sm, sm->ptk.kck, ver, bssid, ETH_P_EAPOL, rbuf, rlen, key_info & WPA_KEY_INFO_MIC ? reply->key_mic : NULL); }
STATIC int jswHandleJdsAck(struct jsw_session *js, struct ieee802_1x_hdr *eapolHdr) { if ((jswVerifyMic(js, eapolHdr, js->secret.micKey)) != 0) { return -1; } if (jswGetTlvs(js, eapolHdr, JSW_OP_JDS_ACK) != 0) { return -1; } inc_byte_array(js->key_replay_counter, WPA_REPLAY_COUNTER_LEN); jswTimerStart(js, JSW_PASSWORD_RESP_TIMEOUT); return(jswSendPassReq(js)); }
STATIC void jswP1PassPending(struct jsw_session *js, jsw_event_t event) { struct ieee802_1x_hdr *eapolHdr; int i; wpa_printf(MSG_DEBUG, "state jswP1PassPending: event %d\n", event); switch (event) { case JSW_EVENT_JSS_PW_RESP: eapolHdr = js->sm.event_arg; if (jswGetTlvs(js, eapolHdr, JSW_OP_JSS_PASSWORD_RESP) != 0) { goto failed; } inc_byte_array(js->key_replay_counter, WPA_REPLAY_COUNTER_LEN); /* * Just send a burst and hope that at least one * makes it to the station. */ if ((jswSendJdsPassAck(js)) != 0) { goto failed; } for( i = 0; i < JSW_LAST_FRAME_RETRY; i++) { jswSendJdsPassAck(js); } jswP1Successful(js); break; case JSW_EVENT_JDS_ACK: eapolHdr = js->sm.event_arg; if (!jswDoRetry(js)) { goto failed; } if ((jswHandleJdsAck(js, eapolHdr)) != 0) { goto failed; } break; default: goto failed; } return; failed: jswP1Failed(js, event); }
STATIC void jswP1JdsAckPending(struct jsw_session *js, jsw_event_t event) { struct ieee802_1x_hdr *eapolHdr; wpa_printf(MSG_DEBUG, "state jswP1JdsAckPending: event %d\n", event); switch (event) { case JSW_EVENT_JDS_ACK: eapolHdr = js->sm.event_arg; if ((jswHandleJdsAck(js, eapolHdr)) != 0) { goto failed; } smSetState(js, jswP1PassPending); jsw_led_stop(js->hapd); jsw_set_led_pattern(js->hapd, &jsw_led_success); break; case JSW_EVENT_TIMEOUT: if (!jswDoRetry(js)) { goto failed; } inc_byte_array(js->key_replay_counter, WPA_REPLAY_COUNTER_LEN); if ((jswSendJdsAck(js)) != 0) { goto failed; } jswTimerStart(js, JSW_DH_TIMEOUT); break; default: goto failed; } return; failed: jswP1Failed(js, event); }
static void eap_fast_write_crypto_binding( struct eap_tlv_crypto_binding_tlv *rbind, struct eap_tlv_crypto_binding_tlv *_bind, const u8 *cmk) { rbind->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | EAP_TLV_CRYPTO_BINDING_TLV); rbind->length = host_to_be16(sizeof(*rbind) - sizeof(struct eap_tlv_hdr)); rbind->version = EAP_FAST_VERSION; rbind->received_version = _bind->version; rbind->subtype = EAP_TLV_CRYPTO_BINDING_SUBTYPE_RESPONSE; os_memcpy(rbind->nonce, _bind->nonce, sizeof(_bind->nonce)); inc_byte_array(rbind->nonce, sizeof(rbind->nonce)); hmac_sha1(cmk, EAP_FAST_CMK_LEN, (u8 *) rbind, sizeof(*rbind), rbind->compound_mac); wpa_printf(MSG_DEBUG, "EAP-FAST: Reply Crypto-Binding TLV: Version %d " "Received Version %d SubType %d", rbind->version, rbind->received_version, rbind->subtype); wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: NONCE", rbind->nonce, sizeof(rbind->nonce)); wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Compound MAC", rbind->compound_mac, sizeof(rbind->compound_mac)); }
/** * wpa_sm_stkstart - Send EAPOL-Key Request for STK handshake (STK M1) * @sm: Pointer to WPA state machine data from wpa_sm_init() * @peer: MAC address of the peer STA * Returns: 0 on success, or -1 on failure * * Send an EAPOL-Key Request to the current authenticator to start STK * handshake with the peer. */ int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer) { size_t rlen, kde_len; struct wpa_eapol_key *req; int key_info, ver; u8 bssid[ETH_ALEN], *rbuf, *pos, *count_pos; u16 count; struct rsn_ie_hdr *hdr; struct wpa_peerkey *peerkey; struct wpa_ie_data ie; if (sm->proto != WPA_PROTO_RSN || !sm->ptk_set || !sm->peerkey_enabled) return -1; if (sm->ap_rsn_ie && wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &ie) == 0 && !(ie.capabilities & WPA_CAPABILITY_PEERKEY_ENABLED)) { wpa_printf(MSG_DEBUG, "RSN: Current AP does not support STK"); return -1; } if (sm->pairwise_cipher == WPA_CIPHER_CCMP) ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; else ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; if (wpa_sm_get_bssid(sm, bssid) < 0) { wpa_printf(MSG_WARNING, "Failed to read BSSID for EAPOL-Key " "SMK M1"); return -1; } /* TODO: find existing entry and if found, use that instead of adding * a new one */ peerkey = os_zalloc(sizeof(*peerkey)); if (peerkey == NULL) return -1; peerkey->initiator = 1; os_memcpy(peerkey->addr, peer, ETH_ALEN); #ifdef CONFIG_IEEE80211W if (wpa_key_mgmt_sha256(sm->key_mgmt)) peerkey->use_sha256 = 1; #endif /* CONFIG_IEEE80211W */ /* SMK M1: * EAPOL-Key(S=1, M=1, A=0, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce, * MIC=MIC, DataKDs=(RSNIE_I, MAC_P KDE)) */ hdr = (struct rsn_ie_hdr *) peerkey->rsnie_i; hdr->elem_id = WLAN_EID_RSN; WPA_PUT_LE16(hdr->version, RSN_VERSION); pos = (u8 *) (hdr + 1); /* Group Suite can be anything for SMK RSN IE; receiver will just * ignore it. */ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); pos += RSN_SELECTOR_LEN; count_pos = pos; pos += 2; count = 0; if (sm->allowed_pairwise_cipher & WPA_CIPHER_CCMP) { RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); pos += RSN_SELECTOR_LEN; count++; } if (sm->allowed_pairwise_cipher & WPA_CIPHER_TKIP) { RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); pos += RSN_SELECTOR_LEN; count++; } WPA_PUT_LE16(count_pos, count); hdr->len = (pos - peerkey->rsnie_i) - 2; peerkey->rsnie_i_len = pos - peerkey->rsnie_i; wpa_hexdump(MSG_DEBUG, "WPA: RSN IE for SMK handshake", peerkey->rsnie_i, peerkey->rsnie_i_len); kde_len = peerkey->rsnie_i_len + 2 + RSN_SELECTOR_LEN + ETH_ALEN; rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, sizeof(*req) + kde_len, &rlen, (void *) &req); if (rbuf == NULL) { wpa_supplicant_peerkey_free(sm, peerkey); return -1; } req->type = EAPOL_KEY_TYPE_RSN; key_info = WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE | WPA_KEY_INFO_REQUEST | ver; WPA_PUT_BE16(req->key_info, key_info); WPA_PUT_BE16(req->key_length, 0); os_memcpy(req->replay_counter, sm->request_counter, WPA_REPLAY_COUNTER_LEN); inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN); if (random_get_bytes(peerkey->inonce, WPA_NONCE_LEN)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to get random data for INonce"); os_free(rbuf); wpa_supplicant_peerkey_free(sm, peerkey); return -1; } os_memcpy(req->key_nonce, peerkey->inonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "WPA: INonce for SMK handshake", req->key_nonce, WPA_NONCE_LEN); WPA_PUT_BE16(req->key_data_length, (u16) kde_len); pos = (u8 *) (req + 1); /* Initiator RSN IE */ pos = wpa_add_ie(pos, peerkey->rsnie_i, peerkey->rsnie_i_len); /* Peer MAC address KDE */ wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN); wpa_printf(MSG_INFO, "RSN: Sending EAPOL-Key SMK M1 Request (peer " MACSTR ")", MAC2STR(peer)); wpa_eapol_key_send(sm, sm->ptk.kck, ver, bssid, ETH_P_EAPOL, rbuf, rlen, req->key_mic); peerkey->next = sm->peerkey; sm->peerkey = peerkey; return 0; }
VOID WpaMicFailureReportFrame( IN PRTMP_ADAPTER pAd, IN MLME_QUEUE_ELEM *Elem) { PUCHAR pOutBuffer = NULL; UCHAR Header802_3[14]; ULONG FrameLen = 0; EAPOL_PACKET Packet; UCHAR Mic[16]; BOOLEAN bUnicast; DBGPRINT(RT_DEBUG_TRACE, ("WpaMicFailureReportFrame ----->\n")); bUnicast = (Elem->Msg[0] == 1 ? TRUE:FALSE); pAd->Sequence = ((pAd->Sequence) + 1) & (MAX_SEQ_NUMBER); // init 802.3 header and Fill Packet MAKE_802_3_HEADER(Header802_3, pAd->CommonCfg.Bssid, pAd->CurrentAddress, EAPOL); NdisZeroMemory(&Packet, sizeof(Packet)); Packet.ProVer = EAPOL_VER; Packet.ProType = EAPOLKey; Packet.KeyDesc.Type = WPA1_KEY_DESC; // Request field presented Packet.KeyDesc.KeyInfo.Request = 1; if(pAd->StaCfg.WepStatus == Ndis802_11Encryption3Enabled) { Packet.KeyDesc.KeyInfo.KeyDescVer = 2; } else // TKIP { Packet.KeyDesc.KeyInfo.KeyDescVer = 1; } Packet.KeyDesc.KeyInfo.KeyType = (bUnicast ? PAIRWISEKEY : GROUPKEY); // KeyMic field presented Packet.KeyDesc.KeyInfo.KeyMic = 1; // Error field presented Packet.KeyDesc.KeyInfo.Error = 1; // Update packet length after decide Key data payload SET_UINT16_TO_ARRARY(Packet.Body_Len, LEN_EAPOL_KEY_MSG) // Key Replay Count NdisMoveMemory(Packet.KeyDesc.ReplayCounter, pAd->StaCfg.ReplayCounter, LEN_KEY_DESC_REPLAY); inc_byte_array(pAd->StaCfg.ReplayCounter, 8); // Convert to little-endian format. *((USHORT *)&Packet.KeyDesc.KeyInfo) = cpu2le16(*((USHORT *)&Packet.KeyDesc.KeyInfo)); MlmeAllocateMemory(pAd, (PUCHAR *)&pOutBuffer); // allocate memory if(pOutBuffer == NULL) { return; } // Prepare EAPOL frame for MIC calculation // Be careful, only EAPOL frame is counted for MIC calculation MakeOutgoingFrame(pOutBuffer, &FrameLen, CONV_ARRARY_TO_UINT16(Packet.Body_Len) + 4, &Packet, END_OF_ARGS); // Prepare and Fill MIC value NdisZeroMemory(Mic, sizeof(Mic)); if(pAd->StaCfg.WepStatus == Ndis802_11Encryption3Enabled) { // AES UCHAR digest[20] = {0}; HMAC_SHA1(pAd->StaCfg.PTK, LEN_EAP_MICK, pOutBuffer, FrameLen, digest, SHA1_DIGEST_SIZE); NdisMoveMemory(Mic, digest, LEN_KEY_DESC_MIC); } else { // TKIP HMAC_MD5(pAd->StaCfg.PTK, LEN_EAP_MICK, pOutBuffer, FrameLen, Mic, MD5_DIGEST_SIZE); } NdisMoveMemory(Packet.KeyDesc.KeyMic, Mic, LEN_KEY_DESC_MIC); // copy frame to Tx ring and send MIC failure report frame to authenticator RTMPToWirelessSta(pAd, &pAd->MacTab.Content[BSSID_WCID], Header802_3, LENGTH_802_3, (PUCHAR)&Packet, CONV_ARRARY_TO_UINT16(Packet.Body_Len) + 4, FALSE); MlmeFreeMemory(pAd, (PUCHAR)pOutBuffer); DBGPRINT(RT_DEBUG_TRACE, ("WpaMicFailureReportFrame <-----\n")); }
VOID WpaMicFailureReportFrame( IN PRTMP_ADAPTER pAd, IN MLME_QUEUE_ELEM *Elem) { PUCHAR pOutBuffer = NULL; UCHAR Header802_3[14]; ULONG FrameLen = 0; UCHAR *mpool; PEAPOL_PACKET pPacket; UCHAR Mic[16]; BOOLEAN bUnicast; DBGPRINT(RT_DEBUG_TRACE, ("WpaMicFailureReportFrame ----->\n")); bUnicast = (Elem->Msg[0] == 1 ? TRUE:FALSE); pAd->Sequence = ((pAd->Sequence) + 1) & (MAX_SEQ_NUMBER); /* init 802.3 header and Fill Packet */ MAKE_802_3_HEADER(Header802_3, pAd->CommonCfg.Bssid, pAd->CurrentAddress, EAPOL); /* Allocate memory for output */ os_alloc_mem(NULL, (PUCHAR *)&mpool, TX_EAPOL_BUFFER); if (mpool == NULL) { DBGPRINT(RT_DEBUG_ERROR, ("!!!%s : no memory!!!\n", __FUNCTION__)); return; } pPacket = (PEAPOL_PACKET)mpool; NdisZeroMemory(pPacket, TX_EAPOL_BUFFER); pPacket->ProVer = EAPOL_VER; pPacket->ProType = EAPOLKey; pPacket->KeyDesc.Type = WPA1_KEY_DESC; /* Request field presented */ pPacket->KeyDesc.KeyInfo.Request = 1; if(pAd->StaCfg.WepStatus == Ndis802_11Encryption3Enabled) { pPacket->KeyDesc.KeyInfo.KeyDescVer = 2; } else /* TKIP */ { pPacket->KeyDesc.KeyInfo.KeyDescVer = 1; } pPacket->KeyDesc.KeyInfo.KeyType = (bUnicast ? PAIRWISEKEY : GROUPKEY); /* KeyMic field presented */ pPacket->KeyDesc.KeyInfo.KeyMic = 1; /* Error field presented */ pPacket->KeyDesc.KeyInfo.Error = 1; /* Update packet length after decide Key data payload */ SET_UINT16_TO_ARRARY(pPacket->Body_Len, MIN_LEN_OF_EAPOL_KEY_MSG) /* Key Replay Count */ NdisMoveMemory(pPacket->KeyDesc.ReplayCounter, pAd->StaCfg.ReplayCounter, LEN_KEY_DESC_REPLAY); inc_byte_array(pAd->StaCfg.ReplayCounter, 8); /* Convert to little-endian format. */ *((USHORT *)&pPacket->KeyDesc.KeyInfo) = cpu2le16(*((USHORT *)&pPacket->KeyDesc.KeyInfo)); MlmeAllocateMemory(pAd, (PUCHAR *)&pOutBuffer); /* allocate memory */ if(pOutBuffer == NULL) { os_free_mem(NULL, mpool); return; } /* Prepare EAPOL frame for MIC calculation Be careful, only EAPOL frame is counted for MIC calculation */ MakeOutgoingFrame(pOutBuffer, &FrameLen, CONV_ARRARY_TO_UINT16(pPacket->Body_Len) + 4, pPacket, END_OF_ARGS); /* Prepare and Fill MIC value */ NdisZeroMemory(Mic, sizeof(Mic)); if(pAd->StaCfg.WepStatus == Ndis802_11Encryption3Enabled) { /* AES */ UCHAR digest[20] = {0}; RT_HMAC_SHA1(pAd->StaCfg.PTK, LEN_PTK_KCK, pOutBuffer, FrameLen, digest, SHA1_DIGEST_SIZE); NdisMoveMemory(Mic, digest, LEN_KEY_DESC_MIC); } else { /* TKIP */ RT_HMAC_MD5(pAd->StaCfg.PTK, LEN_PTK_KCK, pOutBuffer, FrameLen, Mic, MD5_DIGEST_SIZE); } NdisMoveMemory(pPacket->KeyDesc.KeyMic, Mic, LEN_KEY_DESC_MIC); /* copy frame to Tx ring and send MIC failure report frame to authenticator */ RTMPToWirelessSta(pAd, &pAd->MacTab.Content[BSSID_WCID], Header802_3, LENGTH_802_3, (PUCHAR)pPacket, CONV_ARRARY_TO_UINT16(pPacket->Body_Len) + 4, FALSE); MlmeFreeMemory(pAd, (PUCHAR)pOutBuffer); os_free_mem(NULL, mpool); DBGPRINT(RT_DEBUG_TRACE, ("WpaMicFailureReportFrame <-----\n")); }
static struct wpabuf * eap_psk_process_3(struct eap_psk_data *data, struct eap_method_ret *ret, const struct wpabuf *reqData) { const struct eap_psk_hdr_3 *hdr3; struct eap_psk_hdr_4 *hdr4; struct wpabuf *resp; u8 *buf, *rpchannel, nonce[16], *decrypted; const u8 *pchannel, *tag, *msg; u8 mac[EAP_PSK_MAC_LEN]; size_t buflen, left, data_len, len, plen; int failed = 0; const u8 *pos; wpa_printf(MSG_DEBUG, "EAP-PSK: in MAC_SENT state"); pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, reqData, &len); hdr3 = (const struct eap_psk_hdr_3 *) pos; if (pos == NULL || len < sizeof(*hdr3)) { wpa_printf(MSG_INFO, "EAP-PSK: Invalid third message " "length (%lu; expected %lu or more)", (unsigned long) len, (unsigned long) sizeof(*hdr3)); ret->ignore = TRUE; return NULL; } left = len - sizeof(*hdr3); pchannel = (const u8 *) (hdr3 + 1); wpa_printf(MSG_DEBUG, "EAP-PSK: Flags=0x%x", hdr3->flags); if (EAP_PSK_FLAGS_GET_T(hdr3->flags) != 2) { wpa_printf(MSG_INFO, "EAP-PSK: Unexpected T=%d (expected 2)", EAP_PSK_FLAGS_GET_T(hdr3->flags)); ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; return NULL; } wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_S", hdr3->rand_s, EAP_PSK_RAND_LEN); wpa_hexdump(MSG_DEBUG, "EAP-PSK: MAC_S", hdr3->mac_s, EAP_PSK_MAC_LEN); wpa_hexdump(MSG_DEBUG, "EAP-PSK: PCHANNEL", pchannel, left); if (left < 4 + 16 + 1) { wpa_printf(MSG_INFO, "EAP-PSK: Too short PCHANNEL data in " "third message (len=%lu, expected 21)", (unsigned long) left); ret->ignore = TRUE; return NULL; } /* MAC_S = OMAC1-AES-128(AK, ID_S||RAND_P) */ buflen = data->id_s_len + EAP_PSK_RAND_LEN; buf = os_malloc(buflen); if (buf == NULL) return NULL; os_memcpy(buf, data->id_s, data->id_s_len); os_memcpy(buf + data->id_s_len, data->rand_p, EAP_PSK_RAND_LEN); if (omac1_aes_128(data->ak, buf, buflen, mac)) { os_free(buf); return NULL; } os_free(buf); if (os_memcmp(mac, hdr3->mac_s, EAP_PSK_MAC_LEN) != 0) { wpa_printf(MSG_WARNING, "EAP-PSK: Invalid MAC_S in third " "message"); ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; return NULL; } wpa_printf(MSG_DEBUG, "EAP-PSK: MAC_S verified successfully"); if (eap_psk_derive_keys(data->kdk, data->rand_p, data->tek, data->msk, data->emsk)) { ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; return NULL; } wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: TEK", data->tek, EAP_PSK_TEK_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: MSK", data->msk, EAP_MSK_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: EMSK", data->emsk, EAP_EMSK_LEN); os_memset(nonce, 0, 12); os_memcpy(nonce + 12, pchannel, 4); pchannel += 4; left -= 4; tag = pchannel; pchannel += 16; left -= 16; msg = pchannel; wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - nonce", nonce, sizeof(nonce)); wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - hdr", wpabuf_head(reqData), 5); wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - cipher msg", msg, left); decrypted = os_malloc(left); if (decrypted == NULL) { ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; return NULL; } os_memcpy(decrypted, msg, left); if (aes_128_eax_decrypt(data->tek, nonce, sizeof(nonce), wpabuf_head(reqData), sizeof(struct eap_hdr) + 1 + sizeof(*hdr3) - EAP_PSK_MAC_LEN, decrypted, left, tag)) { wpa_printf(MSG_WARNING, "EAP-PSK: PCHANNEL decryption failed"); os_free(decrypted); return NULL; } wpa_hexdump(MSG_DEBUG, "EAP-PSK: Decrypted PCHANNEL message", decrypted, left); /* Verify R flag */ switch (decrypted[0] >> 6) { case EAP_PSK_R_FLAG_CONT: wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - CONT - unsupported"); failed = 1; break; case EAP_PSK_R_FLAG_DONE_SUCCESS: wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_SUCCESS"); break; case EAP_PSK_R_FLAG_DONE_FAILURE: wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_FAILURE"); wpa_printf(MSG_INFO, "EAP-PSK: Authentication server rejected " "authentication"); failed = 1; break; } data_len = 1; if ((decrypted[0] & EAP_PSK_E_FLAG) && left > 1) data_len++; plen = sizeof(*hdr4) + 4 + 16 + data_len; resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PSK, plen, EAP_CODE_RESPONSE, eap_get_id(reqData)); if (resp == NULL) { os_free(decrypted); return NULL; } hdr4 = wpabuf_put(resp, sizeof(*hdr4)); hdr4->flags = EAP_PSK_FLAGS_SET_T(3); /* T=3 */ os_memcpy(hdr4->rand_s, hdr3->rand_s, EAP_PSK_RAND_LEN); rpchannel = wpabuf_put(resp, 4 + 16 + data_len); /* nonce++ */ inc_byte_array(nonce, sizeof(nonce)); os_memcpy(rpchannel, nonce + 12, 4); if (decrypted[0] & EAP_PSK_E_FLAG) { wpa_printf(MSG_DEBUG, "EAP-PSK: Unsupported E (Ext) flag"); failed = 1; rpchannel[4 + 16] = (EAP_PSK_R_FLAG_DONE_FAILURE << 6) | EAP_PSK_E_FLAG; if (left > 1) { /* Add empty EXT_Payload with same EXT_Type */ rpchannel[4 + 16 + 1] = decrypted[1]; } } else if (failed) rpchannel[4 + 16] = EAP_PSK_R_FLAG_DONE_FAILURE << 6; else rpchannel[4 + 16] = EAP_PSK_R_FLAG_DONE_SUCCESS << 6; wpa_hexdump(MSG_DEBUG, "EAP-PSK: reply message (plaintext)", rpchannel + 4 + 16, data_len); if (aes_128_eax_encrypt(data->tek, nonce, sizeof(nonce), wpabuf_head(resp), sizeof(struct eap_hdr) + 1 + sizeof(*hdr4), rpchannel + 4 + 16, data_len, rpchannel + 4)) { os_free(decrypted); wpabuf_free(resp); return NULL; } wpa_hexdump(MSG_DEBUG, "EAP-PSK: reply message (PCHANNEL)", rpchannel, 4 + 16 + data_len); wpa_printf(MSG_DEBUG, "EAP-PSK: Completed %ssuccessfully", failed ? "un" : ""); data->state = PSK_DONE; ret->methodState = METHOD_DONE; ret->decision = failed ? DECISION_FAIL : DECISION_UNCOND_SUCC; os_free(decrypted); return resp; }
/** * tlsv1_record_receive - TLS record layer: Process a received message * @rl: Pointer to TLS record layer data * @in_data: Received data * @in_len: Length of the received data * @out_data: Buffer for output data (must be at least as long as in_data) * @out_len: Set to maximum out_data length by caller; used to return the * length of the used data * @alert: Buffer for returning an alert value on failure * Returns: 0 on success, -1 on failure * * This function decrypts the received message, verifies HMAC and TLS record * layer header. */ int tlsv1_record_receive(struct tlsv1_record_layer *rl, const u8 *in_data, size_t in_len, u8 *out_data, size_t *out_len, u8 *alert) { size_t i, rlen, hlen; u8 padlen; struct crypto_hash *hmac; u8 len[2], hash[100]; int force_mac_error = 0; wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received", in_data, in_len); if (in_len < TLS_RECORD_HEADER_LEN) { wpa_printf(MSG_DEBUG, "TLSv1: Too short record (in_len=%lu)", (unsigned long) in_len); *alert = TLS_ALERT_DECODE_ERROR; return -1; } wpa_printf(MSG_DEBUG, "TLSv1: Received content type %d version %d.%d " "length %d", in_data[0], in_data[1], in_data[2], WPA_GET_BE16(in_data + 3)); if (in_data[0] != TLS_CONTENT_TYPE_HANDSHAKE && in_data[0] != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC && in_data[0] != TLS_CONTENT_TYPE_ALERT && in_data[0] != TLS_CONTENT_TYPE_APPLICATION_DATA) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type 0x%x", in_data[0]); *alert = TLS_ALERT_UNEXPECTED_MESSAGE; return -1; } /* * TLS v1.0 and v1.1 RFCs were not exactly clear on the use of the * protocol version in record layer. As such, accept any {03,xx} value * to remain compatible with existing implementations. */ if (in_data[1] != 0x03) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version " "%u.%u", in_data[1], in_data[2]); *alert = TLS_ALERT_PROTOCOL_VERSION; return -1; } rlen = WPA_GET_BE16(in_data + 3); /* TLSCiphertext must not be more than 2^14+2048 bytes */ if (TLS_RECORD_HEADER_LEN + rlen > 18432) { wpa_printf(MSG_DEBUG, "TLSv1: Record overflow (len=%lu)", (unsigned long) (TLS_RECORD_HEADER_LEN + rlen)); *alert = TLS_ALERT_RECORD_OVERFLOW; return -1; } in_data += TLS_RECORD_HEADER_LEN; in_len -= TLS_RECORD_HEADER_LEN; if (rlen > in_len) { wpa_printf(MSG_DEBUG, "TLSv1: Not all record data included " "(rlen=%lu > in_len=%lu)", (unsigned long) rlen, (unsigned long) in_len); *alert = TLS_ALERT_DECODE_ERROR; return -1; } in_len = rlen; if (*out_len < in_len) { wpa_printf(MSG_DEBUG, "TLSv1: Not enough output buffer for " "processing received record"); *alert = TLS_ALERT_INTERNAL_ERROR; return -1; } if (rl->read_cipher_suite != TLS_NULL_WITH_NULL_NULL) { size_t plen; if (crypto_cipher_decrypt(rl->read_cbc, in_data, out_data, in_len) < 0) { *alert = TLS_ALERT_DECRYPTION_FAILED; return -1; } plen = in_len; wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Record Layer - Decrypted " "data", out_data, plen); if (rl->iv_size) { /* * TLS v1.0 defines different alert values for various * failures. That may information to aid in attacks, so * use the same bad_record_mac alert regardless of the * issues. * * In addition, instead of returning immediately on * error, run through the MAC check to make timing * attacks more difficult. */ if (rl->tls_version == TLS_VERSION_1_1) { /* Remove opaque IV[Cipherspec.block_length] */ if (plen < rl->iv_size) { wpa_printf(MSG_DEBUG, "TLSv1.1: Not " "enough room for IV"); force_mac_error = 1; goto check_mac; } os_memmove(out_data, out_data + rl->iv_size, plen - rl->iv_size); plen -= rl->iv_size; } /* Verify and remove padding */ if (plen == 0) { wpa_printf(MSG_DEBUG, "TLSv1: Too short record" " (no pad)"); force_mac_error = 1; goto check_mac; } padlen = out_data[plen - 1]; if (padlen >= plen) { wpa_printf(MSG_DEBUG, "TLSv1: Incorrect pad " "length (%u, plen=%lu) in " "received record", padlen, (unsigned long) plen); force_mac_error = 1; goto check_mac; } for (i = plen - padlen; i < plen; i++) { if (out_data[i] != padlen) { wpa_hexdump(MSG_DEBUG, "TLSv1: Invalid pad in " "received record", out_data + plen - padlen, padlen); force_mac_error = 1; goto check_mac; } } plen -= padlen + 1; } check_mac: wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Record Layer - Decrypted " "data with IV and padding removed", out_data, plen); if (plen < rl->hash_size) { wpa_printf(MSG_DEBUG, "TLSv1: Too short record; no " "hash value"); *alert = TLS_ALERT_BAD_RECORD_MAC; return -1; } plen -= rl->hash_size; hmac = crypto_hash_init(rl->hash_alg, rl->read_mac_secret, rl->hash_size); if (hmac == NULL) { wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed " "to initialize HMAC"); *alert = TLS_ALERT_INTERNAL_ERROR; return -1; } crypto_hash_update(hmac, rl->read_seq_num, TLS_SEQ_NUM_LEN); /* type + version + length + fragment */ crypto_hash_update(hmac, in_data - TLS_RECORD_HEADER_LEN, 3); WPA_PUT_BE16(len, plen); crypto_hash_update(hmac, len, 2); crypto_hash_update(hmac, out_data, plen); hlen = sizeof(hash); if (crypto_hash_finish(hmac, hash, &hlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed " "to calculate HMAC"); *alert = TLS_ALERT_INTERNAL_ERROR; return -1; } if (hlen != rl->hash_size || os_memcmp(hash, out_data + plen, hlen) != 0 || force_mac_error) { wpa_printf(MSG_DEBUG, "TLSv1: Invalid HMAC value in " "received message (force_mac_error=%d)", force_mac_error); *alert = TLS_ALERT_BAD_RECORD_MAC; return -1; } *out_len = plen; } else { os_memcpy(out_data, in_data, in_len); *out_len = in_len; } /* TLSCompressed must not be more than 2^14+1024 bytes */ if (TLS_RECORD_HEADER_LEN + *out_len > 17408) { wpa_printf(MSG_DEBUG, "TLSv1: Record overflow (len=%lu)", (unsigned long) (TLS_RECORD_HEADER_LEN + *out_len)); *alert = TLS_ALERT_RECORD_OVERFLOW; return -1; } inc_byte_array(rl->read_seq_num, TLS_SEQ_NUM_LEN); return 0; }
/** * tlsv1_record_send - TLS record layer: Send a message * @rl: Pointer to TLS record layer data * @content_type: Content type (TLS_CONTENT_TYPE_*) * @buf: Buffer to send (with TLS_RECORD_HEADER_LEN octets reserved in the * beginning for record layer to fill in; payload filled in after this and * extra space in the end for HMAC). * @buf_size: Maximum buf size * @payload_len: Length of the payload * @out_len: Buffer for returning the used buf length * Returns: 0 on success, -1 on failure * * This function fills in the TLS record layer header, adds HMAC, and encrypts * the data using the current write cipher. */ int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf, size_t buf_size, size_t payload_len, size_t *out_len) { u8 *pos, *ct_start, *length, *payload; struct crypto_hash *hmac; size_t clen; pos = buf; /* ContentType type */ ct_start = pos; *pos++ = content_type; /* ProtocolVersion version */ WPA_PUT_BE16(pos, TLS_VERSION); pos += 2; /* uint16 length */ length = pos; WPA_PUT_BE16(length, payload_len); pos += 2; /* opaque fragment[TLSPlaintext.length] */ payload = pos; pos += payload_len; if (rl->write_cipher_suite != TLS_NULL_WITH_NULL_NULL) { hmac = crypto_hash_init(rl->hash_alg, rl->write_mac_secret, rl->hash_size); if (hmac == NULL) { wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed " "to initialize HMAC"); return -1; } crypto_hash_update(hmac, rl->write_seq_num, TLS_SEQ_NUM_LEN); /* type + version + length + fragment */ crypto_hash_update(hmac, ct_start, pos - ct_start); clen = buf + buf_size - pos; if (clen < rl->hash_size) { wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Not " "enough room for MAC"); crypto_hash_finish(hmac, NULL, NULL); return -1; } if (crypto_hash_finish(hmac, pos, &clen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed " "to calculate HMAC"); return -1; } wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Write HMAC", pos, clen); pos += clen; if (rl->iv_size) { size_t len = pos - payload; size_t pad; pad = (len + 1) % rl->iv_size; if (pad) pad = rl->iv_size - pad; if (pos + pad + 1 > buf + buf_size) { wpa_printf(MSG_DEBUG, "TLSv1: No room for " "block cipher padding"); return -1; } os_memset(pos, pad, pad + 1); pos += pad + 1; } if (crypto_cipher_encrypt(rl->write_cbc, payload, payload, pos - payload) < 0) return -1; } WPA_PUT_BE16(length, pos - length - 2); inc_byte_array(rl->write_seq_num, TLS_SEQ_NUM_LEN); *out_len = pos - buf; return 0; }
/** * tlsv1_record_receive - TLS record layer: Process a received message * @rl: Pointer to TLS record layer data * @in_data: Received data * @in_len: Length of the received data * @out_data: Buffer for output data (must be at least as long as in_data) * @out_len: Set to maximum out_data length by caller; used to return the * length of the used data * @alert: Buffer for returning an alert value on failure * Returns: 0 on success, -1 on failure * * This function decrypts the received message, verifies HMAC and TLS record * layer header. */ int tlsv1_record_receive(struct tlsv1_record_layer *rl, const u8 *in_data, size_t in_len, u8 *out_data, size_t *out_len, u8 *alert) { size_t i, rlen, hlen; u8 padlen; struct crypto_hash *hmac; u8 len[2], hash[100]; wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received", in_data, in_len); if (in_len < TLS_RECORD_HEADER_LEN) { wpa_printf(MSG_DEBUG, "TLSv1: Too short record (in_len=%lu)", (unsigned long) in_len); *alert = TLS_ALERT_DECODE_ERROR; return -1; } wpa_printf(MSG_DEBUG, "TLSv1: Received content type %d version %d.%d " "length %d", in_data[0], in_data[1], in_data[2], WPA_GET_BE16(in_data + 3)); if (in_data[0] != TLS_CONTENT_TYPE_HANDSHAKE && in_data[0] != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC && in_data[0] != TLS_CONTENT_TYPE_APPLICATION_DATA) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type 0x%x", in_data[0]); *alert = TLS_ALERT_UNEXPECTED_MESSAGE; return -1; } if (WPA_GET_BE16(in_data + 1) != TLS_VERSION) { wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version " "%d.%d", in_data[1], in_data[2]); *alert = TLS_ALERT_PROTOCOL_VERSION; return -1; } rlen = WPA_GET_BE16(in_data + 3); /* TLSCiphertext must not be more than 2^14+2048 bytes */ if (TLS_RECORD_HEADER_LEN + rlen > 18432) { wpa_printf(MSG_DEBUG, "TLSv1: Record overflow (len=%lu)", (unsigned long) (TLS_RECORD_HEADER_LEN + rlen)); *alert = TLS_ALERT_RECORD_OVERFLOW; return -1; } in_data += TLS_RECORD_HEADER_LEN; in_len -= TLS_RECORD_HEADER_LEN; if (rlen > in_len) { wpa_printf(MSG_DEBUG, "TLSv1: Not all record data included " "(rlen=%lu > in_len=%lu)", (unsigned long) rlen, (unsigned long) in_len); *alert = TLS_ALERT_DECODE_ERROR; return -1; } in_len = rlen; if (*out_len < in_len) { wpa_printf(MSG_DEBUG, "TLSv1: Not enough output buffer for " "processing received record"); *alert = TLS_ALERT_INTERNAL_ERROR; return -1; } os_memcpy(out_data, in_data, in_len); *out_len = in_len; if (rl->read_cipher_suite != TLS_NULL_WITH_NULL_NULL) { if (crypto_cipher_decrypt(rl->read_cbc, out_data, out_data, in_len) < 0) { *alert = TLS_ALERT_DECRYPTION_FAILED; return -1; } if (rl->iv_size) { if (in_len == 0) { wpa_printf(MSG_DEBUG, "TLSv1: Too short record" " (no pad)"); *alert = TLS_ALERT_DECODE_ERROR; return -1; } padlen = out_data[in_len - 1]; if (padlen >= in_len) { wpa_printf(MSG_DEBUG, "TLSv1: Incorrect pad " "length (%u, in_len=%lu) in " "received record", padlen, (unsigned long) in_len); *alert = TLS_ALERT_DECRYPTION_FAILED; return -1; } for (i = in_len - padlen; i < in_len; i++) { if (out_data[i] != padlen) { wpa_hexdump(MSG_DEBUG, "TLSv1: Invalid pad in " "received record", out_data + in_len - padlen, padlen); *alert = TLS_ALERT_DECRYPTION_FAILED; return -1; } } *out_len -= padlen + 1; } wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Decrypted data", out_data, in_len); if (*out_len < rl->hash_size) { wpa_printf(MSG_DEBUG, "TLSv1: Too short record; no " "hash value"); *alert = TLS_ALERT_INTERNAL_ERROR; return -1; } *out_len -= rl->hash_size; hmac = crypto_hash_init(rl->hash_alg, rl->read_mac_secret, rl->hash_size); if (hmac == NULL) { wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed " "to initialize HMAC"); *alert = TLS_ALERT_INTERNAL_ERROR; return -1; } crypto_hash_update(hmac, rl->read_seq_num, TLS_SEQ_NUM_LEN); /* type + version + length + fragment */ crypto_hash_update(hmac, in_data - TLS_RECORD_HEADER_LEN, 3); WPA_PUT_BE16(len, *out_len); crypto_hash_update(hmac, len, 2); crypto_hash_update(hmac, out_data, *out_len); hlen = sizeof(hash); if (crypto_hash_finish(hmac, hash, &hlen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed " "to calculate HMAC"); return -1; } if (hlen != rl->hash_size || os_memcmp(hash, out_data + *out_len, hlen) != 0) { wpa_printf(MSG_DEBUG, "TLSv1: Invalid HMAC value in " "received message"); *alert = TLS_ALERT_BAD_RECORD_MAC; return -1; } } /* TLSCompressed must not be more than 2^14+1024 bytes */ if (TLS_RECORD_HEADER_LEN + *out_len > 17408) { wpa_printf(MSG_DEBUG, "TLSv1: Record overflow (len=%lu)", (unsigned long) (TLS_RECORD_HEADER_LEN + *out_len)); *alert = TLS_ALERT_RECORD_OVERFLOW; return -1; } inc_byte_array(rl->read_seq_num, TLS_SEQ_NUM_LEN); return 0; }
/** * tlsv1_record_send - TLS record layer: Send a message * @rl: Pointer to TLS record layer data * @content_type: Content type (TLS_CONTENT_TYPE_*) * @buf: Buffer for the generated TLS message (needs to have extra space for * header, IV (TLS v1.1), and HMAC) * @buf_size: Maximum buf size * @payload: Payload to be sent * @payload_len: Length of the payload * @out_len: Buffer for returning the used buf length * Returns: 0 on success, -1 on failure * * This function fills in the TLS record layer header, adds HMAC, and encrypts * the data using the current write cipher. */ int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf, size_t buf_size, const u8 *payload, size_t payload_len, size_t *out_len) { u8 *pos, *ct_start, *length, *cpayload; struct crypto_hash *hmac; size_t clen; int explicit_iv; pos = buf; if (pos + TLS_RECORD_HEADER_LEN > buf + buf_size) return -1; /* ContentType type */ ct_start = pos; *pos++ = content_type; /* ProtocolVersion version */ WPA_PUT_BE16(pos, rl->tls_version); pos += 2; /* uint16 length */ length = pos; WPA_PUT_BE16(length, payload_len); pos += 2; cpayload = pos; explicit_iv = rl->write_cipher_suite != TLS_NULL_WITH_NULL_NULL && rl->iv_size && rl->tls_version == TLS_VERSION_1_1; if (explicit_iv) { /* opaque IV[Cipherspec.block_length] */ if (pos + rl->iv_size > buf + buf_size) return -1; /* * Use random number R per the RFC 4346, 6.2.3.2 CBC Block * Cipher option 2a. */ if (os_get_random(pos, rl->iv_size)) return -1; pos += rl->iv_size; } /* * opaque fragment[TLSPlaintext.length] * (opaque content[TLSCompressed.length] in GenericBlockCipher) */ if (pos + payload_len > buf + buf_size) return -1; os_memmove(pos, payload, payload_len); pos += payload_len; if (rl->write_cipher_suite != TLS_NULL_WITH_NULL_NULL) { /* * MAC calculated over seq_num + TLSCompressed.type + * TLSCompressed.version + TLSCompressed.length + * TLSCompressed.fragment */ hmac = crypto_hash_init(rl->hash_alg, rl->write_mac_secret, rl->hash_size); if (hmac == NULL) { wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed " "to initialize HMAC"); return -1; } crypto_hash_update(hmac, rl->write_seq_num, TLS_SEQ_NUM_LEN); /* type + version + length + fragment */ crypto_hash_update(hmac, ct_start, TLS_RECORD_HEADER_LEN); crypto_hash_update(hmac, payload, payload_len); clen = buf + buf_size - pos; if (clen < rl->hash_size) { wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Not " "enough room for MAC"); crypto_hash_finish(hmac, NULL, NULL); return -1; } if (crypto_hash_finish(hmac, pos, &clen) < 0) { wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed " "to calculate HMAC"); return -1; } wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Write HMAC", pos, clen); pos += clen; if (rl->iv_size) { size_t len = pos - cpayload; size_t pad; pad = (len + 1) % rl->iv_size; if (pad) pad = rl->iv_size - pad; if (pos + pad + 1 > buf + buf_size) { wpa_printf(MSG_DEBUG, "TLSv1: No room for " "block cipher padding"); return -1; } os_memset(pos, pad, pad + 1); pos += pad + 1; } if (crypto_cipher_encrypt(rl->write_cbc, cpayload, cpayload, pos - cpayload) < 0) return -1; } WPA_PUT_BE16(length, pos - length - 2); inc_byte_array(rl->write_seq_num, TLS_SEQ_NUM_LEN); *out_len = pos - buf; return 0; }