]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Add support for deriving triplets from quintuplets
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Fri, 13 May 2016 23:12:25 +0000 (19:12 -0400)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Fri, 13 May 2016 23:12:25 +0000 (19:12 -0400)
src/modules/rlm_eap/libeap/eap_sim.h
src/modules/rlm_eap/libeap/eapcrypto.c
src/modules/rlm_eap/radeapclient.c
src/modules/rlm_eap/types/rlm_eap_sim/rlm_eap_sim.c

index 082cda1b68c5b6ad043f2913e150e91930965a2b..176e2f0de493e494f15b895b38fe51d7c98b7876 100644 (file)
@@ -86,9 +86,19 @@ typedef struct eap_sim_keys {
        uint8_t identity[FR_MAX_STRING_LEN];
        unsigned int  identitylen;
        uint8_t nonce_mt[EAPSIM_NONCEMT_SIZE];
+
        uint8_t rand[3][EAPSIM_RAND_SIZE];
-       uint8_t sres[3][EAPSIM_SRES_SIZE];
-       uint8_t Kc[3][EAPSIM_KC_SIZE];
+
+       union {
+               uint8_t sres[3][EAPSIM_SRES_SIZE];
+               uint32_t sres_uint32[3];
+       };
+
+       union {
+               uint8_t kc[3][EAPSIM_KC_SIZE];
+               uint64_t kc_uint64[3];
+       };
+
        uint8_t versionlist[FR_MAX_STRING_LEN];
        uint8_t versionlistlen;
        uint8_t versionselect[2];
index bd54267bbc649ee426bfdf6d38ab613bbca2b1da..f674dec5022b6e5ff7a40a3a84959d4ca3737b48 100644 (file)
@@ -44,9 +44,9 @@ void eap_sim_calculate_keys(struct eap_sim_keys *ek)
 
        p = buf;
        memcpy(p, ek->identity, ek->identitylen);   p = p+ek->identitylen;
-       memcpy(p, ek->Kc[0], EAPSIM_KC_SIZE);       p = p+EAPSIM_KC_SIZE;
-       memcpy(p, ek->Kc[1], EAPSIM_KC_SIZE);       p = p+EAPSIM_KC_SIZE;
-       memcpy(p, ek->Kc[2], EAPSIM_KC_SIZE);       p = p+EAPSIM_KC_SIZE;
+       memcpy(p, ek->kc[0], EAPSIM_KC_SIZE);       p = p+EAPSIM_KC_SIZE;
+       memcpy(p, ek->kc[1], EAPSIM_KC_SIZE);       p = p+EAPSIM_KC_SIZE;
+       memcpy(p, ek->kc[2], EAPSIM_KC_SIZE);       p = p+EAPSIM_KC_SIZE;
        memcpy(p, ek->nonce_mt, sizeof(ek->nonce_mt)); p=p+sizeof(ek->nonce_mt);
        memcpy(p, ek->versionlist, ek->versionlistlen);p=p+ek->versionlistlen;
        memcpy(p, ek->versionselect, sizeof(ek->versionselect)); p=p+sizeof(ek->versionselect);
@@ -132,7 +132,7 @@ void eap_sim_dump_mk(struct eap_sim_keys *ek)
        for (k = 0; k<3; k++) {
                printf("\n   Kc%u: ", k);
                for (i = 0; i < EAPSIM_KC_SIZE; i++) {
-                       printf("%02x", ek->Kc[k][i]);
+                       printf("%02x", ek->kc[k][i]);
                }
        }
 
index 6ffcda720dda9da5b2edd4da65e5e61cf1fe088b..1e9807da0930c7889655048c75eb4c2247280526 100644 (file)
@@ -1064,9 +1064,9 @@ static int rc_process_eap_challenge(rc_eap_context_t *eap_context,
                ERROR("Need to have Kc 1, 2, and 3 set");
                return 0;
        }
-       memcpy(eap_context->eap.sim.keys.Kc[0], Kc1->vp_strvalue, sizeof(eap_context->eap.sim.keys.Kc[0]));
-       memcpy(eap_context->eap.sim.keys.Kc[1], Kc2->vp_strvalue, sizeof(eap_context->eap.sim.keys.Kc[1]));
-       memcpy(eap_context->eap.sim.keys.Kc[2], Kc3->vp_strvalue, sizeof(eap_context->eap.sim.keys.Kc[2]));
+       memcpy(eap_context->eap.sim.keys.kc[0], Kc1->vp_strvalue, sizeof(eap_context->eap.sim.keys.kc[0]));
+       memcpy(eap_context->eap.sim.keys.kc[1], Kc2->vp_strvalue, sizeof(eap_context->eap.sim.keys.kc[1]));
+       memcpy(eap_context->eap.sim.keys.kc[2], Kc3->vp_strvalue, sizeof(eap_context->eap.sim.keys.kc[2]));
 
        /* all set, calculate keys */
        eap_sim_calculate_keys(&eap_context->eap.sim.keys);
index 75e4c0eb7ba1c126bf07b7a23b20b4498439de86..834b132838cb0e9310c7e96dff045dab06650218 100644 (file)
@@ -43,6 +43,13 @@ typedef struct eap_sim_server_state {
        int                     sim_id;
 } eap_sim_state_t;
 
+typedef enum {
+       EAP_SIM_VECTOR_SRC_AUTO,
+       EAP_SIM_VECTOR_SRC_GSM,
+       EAP_SIM_VECTOR_SRC_UMTS,
+       EAP_SIM_VECTOR_SRC_KI
+} eap_sim_vector_src_t;
+
 /*
  *     build a reply to be sent.
  */
@@ -113,137 +120,297 @@ static int eap_sim_send_state(eap_session_t *eap_session)
        return 1;
 }
 
-static int eap_sim_get_challenge(eap_session_t *eap_session, VALUE_PAIR *vps, int idx, eap_sim_state_t *ess)
+static int eap_sim_vector_from_ki(eap_session_t *eap_session, VALUE_PAIR *vps, int idx, eap_sim_state_t *ess)
 {
-       REQUEST *request = eap_session->request;
-       VALUE_PAIR *vp, *ki, *algo_version;
-
-       rad_assert(idx >= 0 && idx < 3);
+       REQUEST *request = eap_session->request;
+       VALUE_PAIR *vp, *version;
+       int i;
 
        /*
         *      Generate a new RAND value, and derive Kc and SRES from Ki
         */
-       ki = fr_pair_find_by_num(vps, 0, PW_EAP_SIM_KI, TAG_ANY);
-       if (ki) {
-               int i;
+       vp = fr_pair_find_by_num(vps, 0, PW_EAP_SIM_KI, TAG_ANY);
+       if (!vp) return -1;
 
-               /*
-                *      Check to see if have a Ki for the IMSI, this allows us to generate the rest
-                *      of the triplets.
-                */
-               algo_version = fr_pair_find_by_num(vps, 0, PW_EAP_SIM_ALGO_VERSION, TAG_ANY);
-               if (!algo_version) {
-                       REDEBUG("Found Ki, but missing EAP-Sim-Algo-Version");
-                       return 0;
-               }
+       /*
+        *      Check to see if have a Ki for the IMSI, this allows us to generate the rest
+        *      of the triplets.
+        */
+       version = fr_pair_find_by_num(vps, 0, PW_EAP_SIM_ALGO_VERSION, TAG_ANY);
+       if (!version) {
+               REDEBUG("Found Ki, but missing EAP-Sim-Algo-Version");
+               return 0;
+       }
 
-               for (i = 0; i < EAPSIM_RAND_SIZE; i++) {
-                       ess->keys.rand[idx][i] = fr_rand();
-               }
+       for (i = 0; i < EAPSIM_RAND_SIZE; i++) {
+               ess->keys.rand[idx][i] = fr_rand();
+       }
 
-               switch (algo_version->vp_integer) {
-               case 1:
-                       comp128v1(ess->keys.sres[idx], ess->keys.Kc[idx], ki->vp_octets, ess->keys.rand[idx]);
-                       break;
-
-               case 2:
-                       comp128v23(ess->keys.sres[idx], ess->keys.Kc[idx], ki->vp_octets, ess->keys.rand[idx],
-                                  true);
-                       break;
-
-               case 3:
-                       comp128v23(ess->keys.sres[idx], ess->keys.Kc[idx], ki->vp_octets, ess->keys.rand[idx],
-                                  false);
-                       break;
-
-               case 4:
-                       REDEBUG("Comp128-4 algorithm is not supported as details have not yet been published. "
-                               "If you have details of this algorithm please contact the FreeRADIUS "
-                               "maintainers");
-                       return 0;
+       switch (version->vp_integer) {
+       case 1:
+               comp128v1(ess->keys.sres[idx], ess->keys.kc[idx], vp->vp_octets, ess->keys.rand[idx]);
+               break;
 
-               default:
-                       REDEBUG("Unknown/unsupported algorithm Comp128-%i", algo_version->vp_integer);
-               }
+       case 2:
+               comp128v23(ess->keys.sres[idx], ess->keys.kc[idx], vp->vp_octets, ess->keys.rand[idx], true);
+               break;
 
-               if (RDEBUG_ENABLED2) {
-                       char buffer[33];        /* 32 hexits (16 bytes) + 1 */
-                       char *p;
+       case 3:
+               comp128v23(ess->keys.sres[idx], ess->keys.kc[idx], vp->vp_octets, ess->keys.rand[idx], false);
+               break;
 
-                       RDEBUG2("Generated following triplets for round %i:", idx);
+       case 4:
+               REDEBUG("Milenage not supported (feel free to implement it)");
+               return 0;
 
-                       RINDENT();
-                       p = buffer;
-                       for (i = 0; i < EAPSIM_RAND_SIZE; i++) {
-                               p += sprintf(p, "%02x", ess->keys.rand[idx][i]);
-                       }
-                       RDEBUG2("RAND : 0x%s", buffer);
+       default:
+               REDEBUG("Unknown/unsupported algorithm Comp128-%i", version->vp_integer);
+       }
 
-                       p = buffer;
-                       for (i = 0; i < EAPSIM_SRES_SIZE; i++) {
-                               p += sprintf(p, "%02x", ess->keys.sres[idx][i]);
-                       }
-                       RDEBUG2("SRES : 0x%s", buffer);
+       if (RDEBUG_ENABLED2) {
+               char buffer[33];        /* 32 hexits (16 bytes) + 1 */
+               char *p;
 
-                       p = buffer;
-                       for (i = 0; i < EAPSIM_KC_SIZE; i++) {
-                               p += sprintf(p, "%02x", ess->keys.Kc[idx][i]);
-                       }
-                       RDEBUG2("Kc   : 0x%s", buffer);
-                       REXDENT();
+               RDEBUG2("Generated following triplets for round %i:", idx);
+
+               RINDENT();
+               p = buffer;
+               for (i = 0; i < EAPSIM_RAND_SIZE; i++) {
+                       p += sprintf(p, "%02x", ess->keys.rand[idx][i]);
                }
-               return 1;
+               RDEBUG2("RAND : 0x%s", buffer);
+
+               p = buffer;
+               for (i = 0; i < EAPSIM_SRES_SIZE; i++) {
+                       p += sprintf(p, "%02x", ess->keys.sres[idx][i]);
+               }
+               RDEBUG2("SRES : 0x%s", buffer);
+
+               p = buffer;
+               for (i = 0; i < EAPSIM_KC_SIZE; i++) {
+                       p += sprintf(p, "%02x", ess->keys.kc[idx][i]);
+               }
+               RDEBUG2("Kc   : 0x%s", buffer);
+               REXDENT();
        }
+       return 1;
+}
+
+static int eap_sim_vector_from_gsm(eap_session_t *eap_session, VALUE_PAIR *vps, int idx, eap_sim_state_t *ess)
+{
+       REQUEST *request = eap_session->request;
+       VALUE_PAIR *vp;
 
        /*
         *      Use known RAND, SRES, and Kc values, these may of been pulled in from an AuC,
         *      or created by sending challenges to the SIM directly.
         */
        vp = fr_pair_find_by_num(vps, 0, PW_EAP_SIM_RAND1 + idx, TAG_ANY);
-       if (!vp) {
-               /* bad, we can't find stuff! */
-               REDEBUG("control:EAP-SIM-RAND%i not found", idx + 1);
-               return 0;
-       }
+       if (!vp) return 1;
+
        if (vp->vp_length != EAPSIM_RAND_SIZE) {
-               REDEBUG("control:EAP-SIM-RAND%i is not " STRINGIFY(EAPSIM_RAND_SIZE) " bytes, got %zu bytes",
+               REDEBUG("&control:EAP-SIM-RAND%i is not " STRINGIFY(EAPSIM_RAND_SIZE) " bytes, got %zu bytes",
                        idx + 1, vp->vp_length);
-               return 0;
+               return -1;
        }
        memcpy(ess->keys.rand[idx], vp->vp_octets, EAPSIM_RAND_SIZE);
 
        vp = fr_pair_find_by_num(vps, 0, PW_EAP_SIM_SRES1 + idx, TAG_ANY);
-       if (!vp) {
-               /* bad, we can't find stuff! */
-               REDEBUG("control:EAP-SIM-SRES%i not found", idx + 1);
-               return 0;
-       }
+       if (!vp) return 1;
+
        if (vp->vp_length != EAPSIM_SRES_SIZE) {
-               REDEBUG("control:EAP-SIM-SRES%i is not " STRINGIFY(EAPSIM_SRES_SIZE) " bytes, got %zu bytes",
+               REDEBUG("&control:EAP-SIM-SRES%i is not " STRINGIFY(EAPSIM_SRES_SIZE) " bytes, got %zu bytes",
                        idx + 1, vp->vp_length);
-               return 0;
+               return -1;
        }
        memcpy(ess->keys.sres[idx], vp->vp_octets, EAPSIM_SRES_SIZE);
 
        vp = fr_pair_find_by_num(vps, 0, PW_EAP_SIM_KC1 + idx, TAG_ANY);
-       if (!vp) {
-               /* bad, we can't find stuff! */
-               REDEBUG("control:EAP-SIM-Kc%i not found", idx + 1);
-               return 0;
-       }
+       if (!vp) return 1;
+
        if (vp->vp_length != EAPSIM_KC_SIZE) {
-               REDEBUG("control:EAP-SIM-Kc%i is not 8 bytes, got %zu bytes", idx + 1, vp->vp_length);
-               return 0;
+               REDEBUG("&control:EAP-SIM-Kc%i is not 8 bytes, got %zu bytes", idx + 1, vp->vp_length);
+               return -1;
        }
-       memcpy(ess->keys.Kc[idx], vp->vp_octets, EAPSIM_KC_SIZE);
+       memcpy(ess->keys.kc[idx], vp->vp_octets, EAPSIM_KC_SIZE);
        if (vp->vp_length != EAPSIM_KC_SIZE) {
-               REDEBUG("control:EAP-SIM-Kc%i is not " STRINGIFY(EAPSIM_KC_SIZE) " bytes, got %zu bytes",
+               REDEBUG("&control:EAP-SIM-Kc%i is not " STRINGIFY(EAPSIM_KC_SIZE) " bytes, got %zu bytes",
                        idx + 1, vp->vp_length);
-               return 0;
+               return -1;
        }
-       memcpy(ess->keys.Kc[idx], vp->vp_strvalue, EAPSIM_KC_SIZE);
+       memcpy(ess->keys.kc[idx], vp->vp_strvalue, EAPSIM_KC_SIZE);
 
-       return 1;
+       return 0;
+}
+
+/** Derive triplets from quintuplets
+ *
+ * c1: RAND[gsm] = RAND
+ * c2: SRES[gsm] = (XRES*[0]...XRES*[31]) ⊕ (XRES*[32]...XRES*[63]) ⊕
+ *                (XRES*[64]...XRES*[95]) ⊕ (XRES*[96]...XRES*[127)
+ * c3:   Kc[gsm] = (CK[0]...CK[63]) ⊕ (CK[64]...CK[127]) ⊕
+ *                (IK[0]...IK[63]) ⊕ (IK[64]...IK[127)
+ */
+static int eap_sim_vector_from_umts(eap_session_t *eap_session, VALUE_PAIR *vps, int idx, eap_sim_state_t *ess)
+{
+       REQUEST         *request = eap_session->request;
+       vp_cursor_t     cursor;
+
+       VALUE_PAIR      *rand, *xres, *ck, *ik;
+       uint8_t         xres_buff[16];
+       uint32_t        *xres_ptr;
+       uint64_t        *ck_ptr;
+       uint64_t        *ik_ptr;
+
+       int             i;
+
+       /*
+        *      Fetch RAND
+        */
+       for (i = 0, fr_cursor_init(&cursor, &vps); i < idx; i++) {
+               rand = fr_cursor_next_by_num(&cursor, 0, PW_EAP_AKA_RAND, TAG_ANY);
+               if (!rand) return 1;
+       }
+       if (rand->vp_length != EAPSIM_RAND_SIZE) {
+               REDEBUG("&control:EAP-AKA-RAND incorrect length.  Expected " STRINGIFY(EAPSIM_RAND_SIZE) " bytes, "
+                       "got %zu bytes", rand->vp_length);
+               return -1;
+       }
+
+       /*
+        *      Fetch XRES
+        */
+       for (i = 0, fr_cursor_init(&cursor, &vps); i < idx; i++) {
+               xres = fr_cursor_next_by_num(&cursor, 0, PW_EAP_AKA_XRES, TAG_ANY);
+               if (!xres) return 1;
+       }
+
+       /*
+        *      Fetch CK
+        */
+       for (i = 0, fr_cursor_init(&cursor, &vps); i < idx; i++) {
+               ck = fr_cursor_next_by_num(&cursor, 0, PW_EAP_AKA_CK, TAG_ANY);
+               if (!ck) return 1;
+       }
+
+       /*
+        *      Fetch KI
+        */
+       for (i = 0, fr_cursor_init(&cursor, &vps); i < idx; i++) {
+               ik = fr_cursor_next_by_num(&cursor, 0, PW_EAP_AKA_IK, TAG_ANY);
+               if (!ik) return 1;
+       }
+
+       memcpy(ess->keys.rand[idx], rand->vp_octets, EAPSIM_RAND_SIZE); /* RAND is 128 bits in both */
+
+       /*
+        *      Have to pad XRES out to 16 octets if it's shorter than that.
+        */
+       if (xres->vp_length < 16) {
+               memset(&xres_buff, 0, sizeof(xres_buff));
+               memcpy(&xres_buff, &xres->vp_octets, xres->vp_length);
+               xres_ptr = (uint32_t *)&xres_buff[0];
+       } else {
+               xres_ptr = (uint32_t *)xres->vp_octets;
+       }
+
+       /*
+        *      Fold xres into itself in 32bit quantities using xor to
+        *      produce sres.
+        */
+       ess->keys.sres_uint32[idx] = ((xres_ptr[0] ^ xres_ptr[1]) ^ xres_ptr[2]) ^ xres_ptr[3];
+
+       /*
+        *      Fold CK and IK in 64bit quantities to produce Kc
+        */
+       ck_ptr = (uint64_t *)ck->vp_octets;
+       ik_ptr = (uint64_t *)ik->vp_octets;
+       ess->keys.kc_uint64[idx] = ((ck_ptr[0] ^ ck_ptr[1]) ^ ik_ptr[0]) ^ ik_ptr[1];
+
+       if (RDEBUG_ENABLED2) {
+               char buffer[33];        /* 32 hexits (16 bytes) + 1 */
+               char *p;
+
+               RDEBUG2("Derived following triplets from UMTS for round %i:", idx);
+
+               RINDENT();
+               p = buffer;
+               for (i = 0; i < EAPSIM_RAND_SIZE; i++) {
+                       p += sprintf(p, "%02x", ess->keys.rand[idx][i]);
+               }
+               RDEBUG2("RAND : 0x%s", buffer);
+
+               p = buffer;
+               for (i = 0; i < EAPSIM_SRES_SIZE; i++) {
+                       p += sprintf(p, "%02x", ess->keys.sres[idx][i]);
+               }
+               RDEBUG2("SRES : 0x%s", buffer);
+
+               p = buffer;
+               for (i = 0; i < EAPSIM_KC_SIZE; i++) {
+                       p += sprintf(p, "%02x", ess->keys.kc[idx][i]);
+               }
+               RDEBUG2("Kc   : 0x%s", buffer);
+               REXDENT();
+       }
+
+       return 0;
+}
+
+/** Retrieve GSM triplets from various locations
+ *
+ * Hunt for a source of SIM triplets
+ *
+ * @param eap_session  The current eap_session.
+ * @param vps          List to hunt for triplets in.
+ * @param ess          EAP session state.
+ * @param src          Forces triplets to be retrieved from a particular src
+ *                     and ensures if multiple triplets are being retrieved
+ *                     that they all come from the same src.
+ * @return
+ *     - 1     Vector could be retrieved from the specified src.
+ *     - 0     Vector was retrieved OK and written to the specified index.
+ *     - -1    Error retrieving vector from the specified src.
+ */
+static int eap_sim_get_challenge(eap_session_t *eap_session, VALUE_PAIR *vps,
+                                int idx, eap_sim_state_t *ess, eap_sim_vector_src_t *src)
+{
+
+       int ret;
+
+       rad_assert(idx >= 0 && idx < 3);
+
+       switch (*src) {
+       default:
+       case EAP_SIM_VECTOR_SRC_KI:
+               ret = eap_sim_vector_from_ki(eap_session, vps, idx, ess);
+               if (ret == 0) {
+                       *src = EAP_SIM_VECTOR_SRC_KI;
+                       return 0;
+               }
+               if (ret < 0) return -1;
+               if (src != EAP_SIM_VECTOR_SRC_AUTO) return 1;
+               /* FALL-THROUGH */
+
+       case EAP_SIM_VECTOR_SRC_GSM:
+               ret = eap_sim_vector_from_gsm(eap_session, vps, idx, ess);
+               if (ret == 0) {
+                       *src = EAP_SIM_VECTOR_SRC_GSM;
+                       return 0;
+               }
+               if (ret < 0) return -1;
+               if (src != EAP_SIM_VECTOR_SRC_AUTO) return 1;
+               /* FALL-THROUGH */
+
+       case EAP_SIM_VECTOR_SRC_UMTS:
+               ret = eap_sim_vector_from_umts(eap_session, vps, idx, ess);
+               if (ret == 0) {
+                       *src = EAP_SIM_VECTOR_SRC_UMTS;
+                       return 0;
+               }
+               if (ret < 0) return -1;
+               return 1;
+       }
 }
 
 /** Send the challenge itself
@@ -455,6 +622,7 @@ static int mod_session_init(UNUSED void *instance, eap_session_t *eap_session)
        REQUEST *request = eap_session->request;
        eap_sim_state_t *ess;
        time_t n;
+       eap_sim_vector_src_t src = EAP_SIM_VECTOR_SRC_AUTO;
 
        ess = talloc_zero(eap_session, eap_sim_state_t);
        if (!ess) {
@@ -467,9 +635,9 @@ static int mod_session_init(UNUSED void *instance, eap_session_t *eap_session)
        /*
         *      Save the keying material, because it could change on a subsequent retrieval.
         */
-       if (!eap_sim_get_challenge(eap_session, request->control, 0, ess) ||
-           !eap_sim_get_challenge(eap_session, request->control, 1, ess) ||
-           !eap_sim_get_challenge(eap_session, request->control, 2, ess)) {
+       if (!eap_sim_get_challenge(eap_session, request->control, 0, ess, &src) ||
+           !eap_sim_get_challenge(eap_session, request->control, 1, ess, &src) ||
+           !eap_sim_get_challenge(eap_session, request->control, 2, ess, &src)) {
                return 0;
        }