mirror of https://github.com/ARMmbed/mbed-os.git
299 lines
9.9 KiB
C
299 lines
9.9 KiB
C
/*
|
|
* Copyright (c) 2018-2019, Arm Limited and affiliates.
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
|
|
|
|
#include "nsconfig.h"
|
|
|
|
#include "ns_types.h"
|
|
#include "eventOS_event.h"
|
|
#include "ns_trace.h"
|
|
#include "string.h"
|
|
#include "common_functions.h"
|
|
#include "Security/PANA/pana_eap_header.h"
|
|
#include "Security/eapol/eapol_helper.h"
|
|
|
|
#ifdef HAVE_WS
|
|
|
|
#define KEY_INFO_VERSION_BIT_MASK 0x0007
|
|
#define KEY_INFO_VERSION_BIT_SHIFT 0
|
|
#define KEY_INFO_KEY_TYPE_BIT_MASK 0x0008
|
|
#define KEY_INFO_KEY_TYPE_BIT_SHIFT 3
|
|
#define KEY_INFO_INSTALL_BIT_MASK 0x0040
|
|
#define KEY_INFO_INSTALL_BIT_SHIFT 6
|
|
#define KEY_INFO_ACK_BIT_MASK 0x0080
|
|
#define KEY_INFO_ACK_BIT_SHIFT 7
|
|
#define KEY_INFO_MIC_MASK 0x0100
|
|
#define KEY_INFO_MIC_SHIFT 8
|
|
#define KEY_INFO_SECURE_MASK 0x0200
|
|
#define KEY_INFO_SECURE_SHIFT 9
|
|
#define KEY_INFO_ERROR_MASK 0x0400
|
|
#define KEY_INFO_ERROR_SHIFT 10
|
|
#define KEY_INFO_REQUEST_MASK 0x0800
|
|
#define KEY_INFO_REQUEST_SHIFT 11
|
|
#define KEY_INFO_ENC_KEY_DATA_MASK 0x1000
|
|
#define KEY_INFO_ENC_KEY_DATA_SHIFT 12
|
|
#define KEY_INFO_SMK_MASK 0x2000
|
|
#define KEY_INFO_SMK_SHIFT 13
|
|
|
|
static uint8_t *eapol_key_information_write(eapol_key_information_t *key_information, uint8_t *ptr)
|
|
{
|
|
uint16_t key_info = 0;
|
|
key_info |= (key_information->description_version << KEY_INFO_VERSION_BIT_SHIFT);
|
|
key_info |= (key_information->pairwise_key << KEY_INFO_KEY_TYPE_BIT_SHIFT);
|
|
key_info |= (key_information->install << KEY_INFO_INSTALL_BIT_SHIFT);
|
|
key_info |= (key_information->key_ack << KEY_INFO_ACK_BIT_SHIFT);
|
|
key_info |= (key_information->key_mic << KEY_INFO_MIC_SHIFT);
|
|
key_info |= (key_information->secured_key_frame << KEY_INFO_SECURE_SHIFT);
|
|
key_info |= (key_information->error << KEY_INFO_ERROR_SHIFT);
|
|
key_info |= (key_information->request << KEY_INFO_REQUEST_SHIFT);
|
|
key_info |= (key_information->encrypted_key_data << KEY_INFO_ENC_KEY_DATA_SHIFT);
|
|
key_info |= (key_information->smk_handshake << KEY_INFO_SMK_SHIFT);
|
|
return common_write_16_bit(key_info, ptr);
|
|
}
|
|
|
|
static uint8_t *eapol_key_information_read(eapol_key_information_t *key_information, uint8_t *ptr)
|
|
{
|
|
uint16_t key_info = common_read_16_bit(ptr);
|
|
key_information->description_version = ((key_info & KEY_INFO_VERSION_BIT_MASK) >> KEY_INFO_VERSION_BIT_SHIFT);
|
|
key_information->pairwise_key = ((key_info & KEY_INFO_KEY_TYPE_BIT_MASK) >> KEY_INFO_KEY_TYPE_BIT_SHIFT);
|
|
key_information->install = ((key_info & KEY_INFO_INSTALL_BIT_MASK) >> KEY_INFO_INSTALL_BIT_SHIFT);
|
|
key_information->key_ack = ((key_info & KEY_INFO_ACK_BIT_MASK) >> KEY_INFO_ACK_BIT_SHIFT);
|
|
key_information->key_mic = ((key_info & KEY_INFO_MIC_MASK) >> KEY_INFO_MIC_SHIFT);
|
|
key_information->secured_key_frame = ((key_info & KEY_INFO_SECURE_MASK) >> KEY_INFO_SECURE_SHIFT);
|
|
key_information->error = ((key_info & KEY_INFO_ERROR_MASK) >> KEY_INFO_ERROR_SHIFT);
|
|
key_information->request = ((key_info & KEY_INFO_REQUEST_MASK) >> KEY_INFO_REQUEST_SHIFT);
|
|
key_information->encrypted_key_data = ((key_info & KEY_INFO_ENC_KEY_DATA_MASK) >> KEY_INFO_ENC_KEY_DATA_SHIFT);
|
|
key_information->smk_handshake = ((key_info & KEY_INFO_SMK_MASK) >> KEY_INFO_SMK_SHIFT);
|
|
return ptr + 2;
|
|
}
|
|
|
|
|
|
static bool eapol_parse_eap_packet(eapol_pdu_t *eapol_pdu)
|
|
{
|
|
return eap_header_parse(eapol_pdu->packet_body, eapol_pdu->packet_length, &eapol_pdu->msg.eap);
|
|
}
|
|
|
|
|
|
static bool eapol_parse_key_packet(eapol_pdu_t *eapol_pdu)
|
|
{
|
|
if (eapol_pdu->packet_length < EAPOL_KEY_FRAME_BASE_SIZE) {
|
|
return false;
|
|
}
|
|
uint8_t *ptr = eapol_pdu->packet_body;
|
|
eapol_key_frame_t *key_frame = &eapol_pdu->msg.key;
|
|
key_frame->key_description = *ptr++;
|
|
if (key_frame->key_description != EAPOL_RSN_KEY_DESCRIPTION) {
|
|
return false;
|
|
}
|
|
ptr = eapol_key_information_read(&key_frame->key_information, ptr);
|
|
if (key_frame->key_information.description_version != KEY_DESCRIPTION_HMAC_SHA1_MIC_AES_ENC) {
|
|
return false;
|
|
}
|
|
key_frame->key_length = common_read_16_bit(ptr);
|
|
ptr += 2;
|
|
|
|
key_frame->replay_counter = common_read_64_bit(ptr);
|
|
ptr += 8;
|
|
|
|
key_frame->key_nonce = ptr;
|
|
ptr += 32;
|
|
|
|
key_frame->key_iv = ptr;
|
|
ptr += 16;
|
|
|
|
key_frame->key_rsc = ptr;
|
|
ptr += 16; //Skip 8 byte RSC + RESERVED 8
|
|
|
|
key_frame->key_mic = ptr;
|
|
ptr += 16;
|
|
|
|
key_frame->key_data_length = common_read_16_bit(ptr);
|
|
ptr += 2;
|
|
key_frame->key_data = ptr;
|
|
if (key_frame->key_data_length > (eapol_pdu->packet_length - EAPOL_KEY_FRAME_BASE_SIZE)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
void eapol_write_key_packet_mic(uint8_t *eapol_pdu, uint8_t *mic)
|
|
{
|
|
if (mic) {
|
|
memcpy(&eapol_pdu[81], mic, 16);
|
|
} else {
|
|
memset(&eapol_pdu[81], 0, 16);
|
|
}
|
|
}
|
|
|
|
bool eapol_parse_pdu_header(uint8_t *ptr, uint16_t data_length, eapol_pdu_t *eapol_pdu)
|
|
{
|
|
//Validate MIN length
|
|
if (data_length < EAPOL_BASE_LENGTH) {
|
|
return false;
|
|
}
|
|
//Validate Protocol version
|
|
uint8_t protocol = *ptr++;
|
|
if (protocol != EAPOL_PROTOCOL_VERSION) {
|
|
return false;
|
|
}
|
|
eapol_pdu->packet_type = *ptr++;
|
|
eapol_pdu->packet_length = common_read_16_bit(ptr);
|
|
ptr += 2;
|
|
//Validate Body Length
|
|
if (eapol_pdu->packet_length > data_length - EAPOL_BASE_LENGTH) {
|
|
return false;
|
|
}
|
|
eapol_pdu->packet_body = ptr;
|
|
|
|
if (eapol_pdu->packet_type == EAPOL_EAP_TYPE) {
|
|
return eapol_parse_eap_packet(eapol_pdu);
|
|
} else if (eapol_pdu->packet_type == EAPOL_KEY_TYPE) {
|
|
return eapol_parse_key_packet(eapol_pdu);
|
|
} else {
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
uint8_t *eapol_write_pdu_frame(uint8_t *ptr, eapol_pdu_t *eapol_pdu)
|
|
{
|
|
*ptr++ = EAPOL_PROTOCOL_VERSION;
|
|
*ptr++ = eapol_pdu->packet_type;
|
|
ptr = common_write_16_bit(eapol_pdu->packet_length, ptr);
|
|
eapol_pdu->packet_body = ptr;
|
|
|
|
if (eapol_pdu->packet_type == EAPOL_EAP_TYPE) {
|
|
eap_header_t *eap_header = &eapol_pdu->msg.eap;
|
|
ptr = eap_header_build(ptr, eap_header->length, eap_header->eap_code, eap_header->id_seq, eap_header->type);
|
|
memcpy(ptr, eap_header->data_ptr, eap_header->length - (ptr - eapol_pdu->packet_body));
|
|
ptr += eap_header->length - (ptr - eapol_pdu->packet_body);
|
|
|
|
} else if (eapol_pdu->packet_type == EAPOL_KEY_TYPE) {
|
|
eapol_key_frame_t *key_frame = &eapol_pdu->msg.key;
|
|
*ptr++ = key_frame->key_description;
|
|
ptr = eapol_key_information_write(&key_frame->key_information, ptr);
|
|
ptr = common_write_16_bit(key_frame->key_length, ptr);
|
|
ptr = common_write_64_bit(key_frame->replay_counter, ptr);
|
|
|
|
if (key_frame->key_nonce) {
|
|
memcpy(ptr, key_frame->key_nonce, 32);
|
|
} else {
|
|
memset(ptr, 0, 32);
|
|
}
|
|
ptr += 32;
|
|
|
|
if (key_frame->key_iv) {
|
|
memcpy(ptr, key_frame->key_iv, 16);
|
|
} else {
|
|
memset(ptr, 0, 16);
|
|
}
|
|
ptr += 16;
|
|
|
|
if (key_frame->key_rsc) {
|
|
memcpy(ptr, key_frame->key_rsc, 8);
|
|
} else {
|
|
memset(ptr, 0, 8);
|
|
}
|
|
ptr += 8;
|
|
|
|
//Reserved 8bytes
|
|
memset(ptr, 0, 8);
|
|
ptr += 8;
|
|
|
|
if (key_frame->key_mic && key_frame->key_information.key_mic) {
|
|
memcpy(ptr, key_frame->key_mic, 16);
|
|
} else {
|
|
memset(ptr, 0, 16);
|
|
}
|
|
ptr += 16;
|
|
ptr = common_write_16_bit(key_frame->key_data_length, ptr);
|
|
if (key_frame->key_data_length && key_frame->key_data) {
|
|
memcpy(ptr, key_frame->key_data, key_frame->key_data_length);
|
|
ptr += key_frame->key_data_length;
|
|
}
|
|
}
|
|
|
|
return ptr;
|
|
}
|
|
|
|
|
|
|
|
uint16_t eapol_pdu_eap_frame_init(eapol_pdu_t *eapol_pdu, uint8_t eap_code, uint8_t id_seq, uint8_t type, uint16_t data_length, uint8_t *data_ptr)
|
|
{
|
|
memset(eapol_pdu, 0, sizeof(eapol_pdu_t));
|
|
|
|
eapol_pdu->packet_type = EAPOL_EAP_TYPE;
|
|
eapol_pdu->packet_length = data_length;
|
|
eapol_pdu->msg.eap.eap_code = eap_code;
|
|
eapol_pdu->msg.eap.data_ptr = data_ptr;
|
|
eapol_pdu->msg.eap.length = data_length;
|
|
eapol_pdu->msg.eap.id_seq = id_seq;
|
|
eapol_pdu->msg.eap.type = type;
|
|
|
|
if (eap_code == EAP_REQ || eap_code == EAP_RESPONSE) {
|
|
eapol_pdu->packet_body += 5;
|
|
eapol_pdu->msg.eap.length++; // Add space for type
|
|
eapol_pdu->packet_length++;
|
|
} else {
|
|
eapol_pdu->packet_body += 4;
|
|
}
|
|
|
|
return eapol_pdu_total_length(eapol_pdu);
|
|
|
|
}
|
|
|
|
uint16_t eapol_pdu_key_frame_init(eapol_pdu_t *eapol_pdu, uint16_t data_length, uint8_t *data_ptr)
|
|
{
|
|
memset(eapol_pdu, 0, sizeof(eapol_pdu_t));
|
|
|
|
eapol_pdu->packet_type = EAPOL_KEY_TYPE;
|
|
eapol_pdu->packet_length = data_length + EAPOL_KEY_FRAME_BASE_SIZE;
|
|
eapol_pdu->msg.key.key_data = data_ptr;
|
|
eapol_pdu->msg.key.key_description = EAPOL_RSN_KEY_DESCRIPTION;
|
|
eapol_pdu->msg.key.key_data_length = data_length;
|
|
eapol_pdu->msg.key.key_information.description_version = KEY_DESCRIPTION_HMAC_SHA1_MIC_AES_ENC;
|
|
|
|
return eapol_pdu_total_length(eapol_pdu);
|
|
|
|
}
|
|
|
|
uint8_t eapol_pdu_key_mask_get(eapol_pdu_t *eapol_pdu)
|
|
{
|
|
uint8_t key_mask = 0;
|
|
|
|
if (eapol_pdu->msg.key.key_information.install) {
|
|
key_mask |= KEY_INFO_INSTALL;
|
|
}
|
|
if (eapol_pdu->msg.key.key_information.key_ack) {
|
|
key_mask |= KEY_INFO_KEY_ACK;
|
|
}
|
|
if (eapol_pdu->msg.key.key_information.key_mic) {
|
|
key_mask |= KEY_INFO_KEY_MIC;
|
|
}
|
|
if (eapol_pdu->msg.key.key_information.secured_key_frame) {
|
|
key_mask |= KEY_INFO_SECURED_KEY_FRAME;
|
|
}
|
|
|
|
return key_mask;
|
|
}
|
|
|
|
#endif
|
|
|