EAP-AKA': Added processing of AT_KDF and AT_KDF_INPUT attributes
authorJouni Malinen <j@w1.fi>
Thu, 4 Dec 2008 18:29:46 +0000 (20:29 +0200)
committerJouni Malinen <j@w1.fi>
Thu, 4 Dec 2008 18:29:46 +0000 (20:29 +0200)
Network Name is not yet generated and validated based on 3GPP.33.402
(i.e., a hardcoded string is used in server and anything is accepted in
peer).

src/eap_common/eap_sim_common.c
src/eap_common/eap_sim_common.h
src/eap_peer/eap_aka_prime.c
src/eap_server/eap_aka_prime.c

index 58253f9..9d1bf2c 100644 (file)
@@ -788,6 +788,52 @@ int eap_sim_parse_attr(const u8 *start, const u8 *end,
                        wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RESULT_IND");
                        attr->result_ind = 1;
                        break;
+#ifdef EAP_AKA_PRIME
+               case EAP_SIM_AT_KDF_INPUT:
+                       if (aka != 2) {
+                               wpa_printf(MSG_INFO, "EAP-AKA: Unexpected "
+                                          "AT_KDF_INPUT");
+                               return -1;
+                       }
+
+                       wpa_printf(MSG_DEBUG, "EAP-AKA: AT_KDF_INPUT");
+                       plen = WPA_GET_BE16(apos);
+                       apos += 2;
+                       alen -= 2;
+                       if (plen > alen) {
+                               wpa_printf(MSG_INFO, "EAP-AKA': Invalid "
+                                          "AT_KDF_INPUT (Actual Length %lu, "
+                                          "remaining length %lu)",
+                                          (unsigned long) plen,
+                                          (unsigned long) alen);
+                               return -1;
+                       }
+                       attr->kdf_input = apos;
+                       attr->kdf_input_len = plen;
+                       break;
+               case EAP_SIM_AT_KDF:
+                       if (aka != 2) {
+                               wpa_printf(MSG_INFO, "EAP-AKA: Unexpected "
+                                          "AT_KDF");
+                               return -1;
+                       }
+
+                       wpa_printf(MSG_DEBUG, "EAP-AKA: AT_KDF");
+                       if (alen != 2) {
+                               wpa_printf(MSG_INFO, "EAP-AKA': Invalid "
+                                          "AT_KDF (len %lu)",
+                                          (unsigned long) alen);
+                               return -1;
+                       }
+                       if (attr->kdf_count == EAP_AKA_PRIME_KDF_MAX) {
+                               wpa_printf(MSG_DEBUG, "EAP-AKA': Too many "
+                                          "AT_KDF attributes - ignore this");
+                               continue;
+                       }
+                       attr->kdf[attr->kdf_count] = WPA_GET_BE16(apos);
+                       attr->kdf_count++;
+                       break;
+#endif /* EAP_AKA_PRIME */
                default:
                        if (pos[0] < 128) {
                                wpa_printf(MSG_INFO, "EAP-SIM: Unrecognized "
index 15efcb3..c799f2f 100644 (file)
@@ -126,6 +126,8 @@ void eap_sim_add_mac_sha256(const u8 *k_aut, const u8 *msg, size_t msg_len,
 #define EAP_SIM_AT_COUNTER_TOO_SMALL 20 /* only encrypted */
 #define EAP_SIM_AT_NONCE_S 21 /* only encrypted */
 #define EAP_SIM_AT_CLIENT_ERROR_CODE 22 /* only send */
+#define EAP_SIM_AT_KDF_INPUT 23 /* only AKA' */
+#define EAP_SIM_AT_KDF 24 /* only AKA' */
 #define EAP_SIM_AT_IV 129
 #define EAP_SIM_AT_ENCR_DATA 130
 #define EAP_SIM_AT_NEXT_PSEUDONYM 132 /* only encrypted */
@@ -140,6 +142,8 @@ void eap_sim_add_mac_sha256(const u8 *k_aut, const u8 *msg, size_t msg_len,
 #define EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH 16384
 #define EAP_SIM_SUCCESS 32768
 
+/* EAP-AKA' AT_KDF Key Derivation Function values */
+#define EAP_AKA_PRIME_KDF 1
 
 enum eap_sim_id_req {
        NO_ID_REQ, ANY_ID, FULLAUTH_ID, PERMANENT_ID
@@ -151,14 +155,19 @@ struct eap_sim_attrs {
        const u8 *next_pseudonym, *next_reauth_id;
        const u8 *nonce_mt, *identity, *res, *auts;
        const u8 *checkcode;
+       const u8 *kdf_input;
        size_t num_chal, version_list_len, encr_data_len;
        size_t next_pseudonym_len, next_reauth_id_len, identity_len, res_len;
        size_t res_len_bits;
        size_t checkcode_len;
+       size_t kdf_input_len;
        enum eap_sim_id_req id_req;
        int notification, counter, selected_version, client_error_code;
        int counter_too_small;
        int result_ind;
+#define EAP_AKA_PRIME_KDF_MAX 10
+       u16 kdf[EAP_AKA_PRIME_KDF_MAX];
+       size_t kdf_count;
 };
 
 int eap_sim_parse_attr(const u8 *start, const u8 *end,
index b3ff706..243005a 100644 (file)
@@ -57,6 +57,9 @@ struct eap_aka_data {
        int prev_id;
        int result_ind, use_result_ind;
        u8 eap_method;
+       u8 *network_name;
+       size_t network_name_len;
+       u16 kdf;
 };
 
 
@@ -129,6 +132,7 @@ static void eap_aka_deinit(struct eap_sm *sm, void *priv)
                os_free(data->reauth_id);
                os_free(data->last_eap_identity);
                wpabuf_free(data->id_msgs);
+               os_free(data->network_name);
                os_free(data);
        }
 }
@@ -650,6 +654,89 @@ static int eap_aka_verify_mac(struct eap_aka_data *data,
 }
 
 
+#ifdef EAP_AKA_PRIME
+static struct wpabuf * eap_aka_prime_kdf_select(struct eap_aka_data *data,
+                                               u8 id, u16 kdf)
+{
+       struct eap_sim_msg *msg;
+
+       data->kdf = kdf;
+       wpa_printf(MSG_DEBUG, "Generating EAP-AKA Challenge (id=%d) (KDF "
+                  "select)", id);
+       msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
+                              EAP_AKA_SUBTYPE_CHALLENGE);
+       wpa_printf(MSG_DEBUG, "   AT_KDF");
+       eap_sim_msg_add(msg, EAP_SIM_AT_KDF, kdf, NULL, 0);
+       return eap_sim_msg_finish(msg, NULL, NULL, 0);
+}
+
+
+static struct wpabuf * eap_aka_prime_kdf_neg(struct eap_aka_data *data,
+                                            u8 id, struct eap_sim_attrs *attr)
+{
+       size_t i;
+
+       for (i = 0; i < attr->kdf_count; i++) {
+               if (attr->kdf[i] == EAP_AKA_PRIME_KDF)
+                       return eap_aka_prime_kdf_select(data, id,
+                                                       EAP_AKA_PRIME_KDF);
+       }
+
+       /* No matching KDF found - fail authentication as if AUTN had been
+        * incorrect */
+       return eap_aka_authentication_reject(data, id);
+}
+
+
+static int eap_aka_prime_kdf_valid(struct eap_aka_data *data,
+                                  struct eap_sim_attrs *attr)
+{
+       size_t i, j;
+
+       if (attr->kdf_count == 0)
+               return 0;
+
+       /* The only allowed (and required) duplication of a KDF is the addition
+        * of the selected KDF into the beginning of the list. */
+
+       if (data->kdf) {
+               if (attr->kdf[0] != data->kdf) {
+                       wpa_printf(MSG_WARNING, "EAP-AKA': The server did not "
+                                  "accept the selected KDF");
+                       return 0;
+               }
+
+               for (i = 1; i < attr->kdf_count; i++) {
+                       if (attr->kdf[i] == data->kdf)
+                               break;
+               }
+               if (i == attr->kdf_count &&
+                   attr->kdf_count < EAP_AKA_PRIME_KDF_MAX) {
+                       wpa_printf(MSG_WARNING, "EAP-AKA': The server did not "
+                                  "duplicate the selected KDF");
+                       return 0;
+               }
+
+               /* TODO: should check that the list is identical to the one
+                * used in the previous Challenge message apart from the added
+                * entry in the beginning. */
+       }
+
+       for (i = data->kdf ? 1 : 0; i < attr->kdf_count; i++) {
+               for (j = i + 1; j < attr->kdf_count; j++) {
+                       if (attr->kdf[i] == attr->kdf[j]) {
+                               wpa_printf(MSG_WARNING, "EAP-AKA': The server "
+                                          "included a duplicated KDF");
+                               return 0;
+                       }
+               }
+       }
+
+       return 1;
+}
+#endif /* EAP_AKA_PRIME */
+
+
 static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm,
                                                 struct eap_aka_data *data,
                                                 u8 id,
@@ -672,6 +759,40 @@ static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm,
                                            EAP_AKA_UNABLE_TO_PROCESS_PACKET);
        }
 
+#ifdef EAP_AKA_PRIME
+       if (data->eap_method == EAP_TYPE_AKA_PRIME) {
+               if (!attr->kdf_input || attr->kdf_input_len == 0) {
+                       wpa_printf(MSG_WARNING, "EAP-AKA': Challenge message "
+                                  "did not include non-empty AT_KDF_INPUT");
+                       /* Fail authentication as if AUTN had been incorrect */
+                       return eap_aka_authentication_reject(data, id);
+               }
+               os_free(data->network_name);
+               data->network_name = os_malloc(attr->kdf_input_len);
+               if (data->network_name == NULL) {
+                       wpa_printf(MSG_WARNING, "EAP-AKA': No memory for "
+                                  "storing Network Name");
+                       return eap_aka_authentication_reject(data, id);
+               }
+               os_memcpy(data->network_name, attr->kdf_input,
+                         attr->kdf_input_len);
+               data->network_name_len = attr->kdf_input_len;
+               wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA': Network Name "
+                                 "(AT_KDF_INPUT)",
+                                 data->network_name, data->network_name_len);
+               /* TODO: check Network Name per 3GPP.33.402 */
+
+               if (!eap_aka_prime_kdf_valid(data, attr))
+                       return eap_aka_authentication_reject(data, id);
+
+               if (attr->kdf[0] != EAP_AKA_PRIME_KDF)
+                       return eap_aka_prime_kdf_neg(data, id, attr);
+
+               data->kdf = EAP_AKA_PRIME_KDF;
+               wpa_printf(MSG_DEBUG, "EAP-AKA': KDF %d selected", data->kdf);
+       }
+#endif /* EAP_AKA_PRIME */
+
        data->reauth = 0;
        if (!attr->mac || !attr->rand || !attr->autn) {
                wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message "
@@ -1020,7 +1141,8 @@ static struct wpabuf * eap_aka_process(struct eap_sm *sm, void *priv,
        wpa_printf(MSG_DEBUG, "EAP-AKA: Subtype=%d", subtype);
        pos += 2; /* Reserved */
 
-       if (eap_sim_parse_attr(pos, wpabuf_head_u8(reqData) + len, &attr, 1,
+       if (eap_sim_parse_attr(pos, wpabuf_head_u8(reqData) + len, &attr,
+                              data->eap_method == EAP_TYPE_AKA_PRIME ? 2 : 1,
                               0)) {
                res = eap_aka_client_error(data, id,
                                           EAP_AKA_UNABLE_TO_PROCESS_PACKET);
index 1788576..1563086 100644 (file)
@@ -52,6 +52,9 @@ struct eap_aka_data {
        struct wpabuf *id_msgs;
        int pending_id;
        u8 eap_method;
+       u8 *network_name;
+       size_t network_name_len;
+       u16 kdf;
 };
 
 
@@ -130,6 +133,13 @@ static void * eap_aka_prime_init(struct eap_sm *sm)
                return NULL;
 
        data->eap_method = EAP_TYPE_AKA_PRIME;
+       data->network_name = os_malloc(3);
+       if (data->network_name == NULL) {
+               os_free(data);
+               return NULL;
+       }
+       os_memcpy(data->network_name, "FOO", 3); /* FIX: 3GPP.24.302 */
+       data->network_name_len = 3;
 
        data->state = IDENTITY;
        eap_aka_determine_identity(sm, data, 1, 0);
@@ -145,6 +155,7 @@ static void eap_aka_reset(struct eap_sm *sm, void *priv)
        os_free(data->next_pseudonym);
        os_free(data->next_reauth_id);
        wpabuf_free(data->id_msgs);
+       os_free(data->network_name);
        os_free(data);
 }
 
@@ -357,6 +368,18 @@ static struct wpabuf * eap_aka_build_challenge(struct eap_sm *sm,
        wpa_printf(MSG_DEBUG, "   AT_RAND");
        eap_sim_msg_add(msg, EAP_SIM_AT_RAND, 0, data->rand, EAP_AKA_RAND_LEN);
        eap_sim_msg_add(msg, EAP_SIM_AT_AUTN, 0, data->autn, EAP_AKA_AUTN_LEN);
+       if (data->eap_method == EAP_TYPE_AKA_PRIME) {
+               if (data->kdf) {
+                       /* Add the selected KDF into the beginning */
+                       eap_sim_msg_add(msg, EAP_SIM_AT_KDF, data->kdf,
+                                       NULL, 0);
+               }
+               eap_sim_msg_add(msg, EAP_SIM_AT_KDF, EAP_AKA_PRIME_KDF,
+                               NULL, 0);
+               eap_sim_msg_add(msg, EAP_SIM_AT_KDF_INPUT,
+                               data->network_name_len,
+                               data->network_name, data->network_name_len);
+       }
 
        if (eap_aka_build_encr(sm, data, msg, 0, NULL)) {
                eap_sim_msg_free(msg);
@@ -738,6 +761,25 @@ static void eap_aka_process_challenge(struct eap_sm *sm,
 
        wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Challenge");
 
+       if (data->eap_method == EAP_TYPE_AKA_PRIME &&
+           attr->kdf_count == 1 && attr->mac == NULL) {
+               if (attr->kdf[0] != EAP_AKA_PRIME_KDF) {
+                       wpa_printf(MSG_WARNING, "EAP-AKA': Peer selected "
+                                  "unknown KDF");
+                       data->notification =
+                               EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+                       eap_aka_state(data, NOTIFICATION);
+                       return;
+               }
+
+               data->kdf = attr->kdf[0];
+
+               /* Allow negotiation to continue with the selected KDF by
+                * sending another Challenge message */
+               wpa_printf(MSG_DEBUG, "EAP-AKA': KDF %d selected", data->kdf);
+               return;
+       }
+
        if (attr->checkcode &&
            eap_aka_verify_checkcode(data, attr->checkcode,
                                     attr->checkcode_len)) {
@@ -1028,7 +1070,9 @@ static void eap_aka_process(struct eap_sm *sm, void *priv,
                return;
        }
 
-       if (eap_sim_parse_attr(pos, end, &attr, 1, 0)) {
+       if (eap_sim_parse_attr(pos, end, &attr,
+                              data->eap_method == EAP_TYPE_AKA_PRIME ? 2 : 1,
+                              0)) {
                wpa_printf(MSG_DEBUG, "EAP-AKA: Failed to parse attributes");
                data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
                eap_aka_state(data, NOTIFICATION);