eap_gpsk_common.c
Go to the documentation of this file.
00001 /*
00002  * EAP server/peer: EAP-GPSK shared routines
00003  * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
00004  *
00005  * This program is free software; you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License version 2 as
00007  * published by the Free Software Foundation.
00008  *
00009  * Alternatively, this software may be distributed under the terms of BSD
00010  * license.
00011  *
00012  * See README and COPYING for more details.
00013  */
00014 
00015 #include "includes.h"
00016 
00017 #include "common.h"
00018 #include "crypto/aes_wrap.h"
00019 #include "crypto/sha256.h"
00020 #include "eap_defs.h"
00021 #include "eap_gpsk_common.h"
00022 
00023 
00030 int eap_gpsk_supported_ciphersuite(int vendor, int specifier)
00031 {
00032         if (vendor == EAP_GPSK_VENDOR_IETF &&
00033             specifier == EAP_GPSK_CIPHER_AES)
00034                 return 1;
00035 #ifdef EAP_GPSK_SHA256
00036         if (vendor == EAP_GPSK_VENDOR_IETF &&
00037             specifier == EAP_GPSK_CIPHER_SHA256)
00038                 return 1;
00039 #endif /* EAP_GPSK_SHA256 */
00040         return 0;
00041 }
00042 
00043 
00044 static int eap_gpsk_gkdf_cmac(const u8 *psk /* Y */,
00045                               const u8 *data /* Z */, size_t data_len,
00046                               u8 *buf, size_t len /* X */)
00047 {
00048         u8 *opos;
00049         size_t i, n, hashlen, left, clen;
00050         u8 ibuf[2], hash[16];
00051         const u8 *addr[2];
00052         size_t vlen[2];
00053 
00054         hashlen = sizeof(hash);
00055         /* M_i = MAC_Y (i || Z); (MAC = AES-CMAC-128) */
00056         addr[0] = ibuf;
00057         vlen[0] = sizeof(ibuf);
00058         addr[1] = data;
00059         vlen[1] = data_len;
00060 
00061         opos = buf;
00062         left = len;
00063         n = (len + hashlen - 1) / hashlen;
00064         for (i = 1; i <= n; i++) {
00065                 WPA_PUT_BE16(ibuf, i);
00066                 if (omac1_aes_128_vector(psk, 2, addr, vlen, hash))
00067                         return -1;
00068                 clen = left > hashlen ? hashlen : left;
00069                 os_memcpy(opos, hash, clen);
00070                 opos += clen;
00071                 left -= clen;
00072         }
00073 
00074         return 0;
00075 }
00076 
00077 
00078 #ifdef EAP_GPSK_SHA256
00079 static int eap_gpsk_gkdf_sha256(const u8 *psk /* Y */,
00080                                 const u8 *data /* Z */, size_t data_len,
00081                                 u8 *buf, size_t len /* X */)
00082 {
00083         u8 *opos;
00084         size_t i, n, hashlen, left, clen;
00085         u8 ibuf[2], hash[SHA256_MAC_LEN];
00086         const u8 *addr[2];
00087         size_t vlen[2];
00088 
00089         hashlen = SHA256_MAC_LEN;
00090         /* M_i = MAC_Y (i || Z); (MAC = HMAC-SHA256) */
00091         addr[0] = ibuf;
00092         vlen[0] = sizeof(ibuf);
00093         addr[1] = data;
00094         vlen[1] = data_len;
00095 
00096         opos = buf;
00097         left = len;
00098         n = (len + hashlen - 1) / hashlen;
00099         for (i = 1; i <= n; i++) {
00100                 WPA_PUT_BE16(ibuf, i);
00101                 hmac_sha256_vector(psk, 32, 2, addr, vlen, hash);
00102                 clen = left > hashlen ? hashlen : left;
00103                 os_memcpy(opos, hash, clen);
00104                 opos += clen;
00105                 left -= clen;
00106         }
00107 
00108         return 0;
00109 }
00110 #endif /* EAP_GPSK_SHA256 */
00111 
00112 
00113 static int eap_gpsk_derive_keys_helper(u32 csuite_specifier,
00114                                        u8 *kdf_out, size_t kdf_out_len,
00115                                        const u8 *psk, size_t psk_len,
00116                                        const u8 *seed, size_t seed_len,
00117                                        u8 *msk, u8 *emsk,
00118                                        u8 *sk, size_t sk_len,
00119                                        u8 *pk, size_t pk_len)
00120 {
00121         u8 mk[32], *pos, *data;
00122         size_t data_len, mk_len;
00123         int (*gkdf)(const u8 *_psk, const u8 *_data, size_t _data_len,
00124                     u8 *buf, size_t len);
00125 
00126         gkdf = NULL;
00127         switch (csuite_specifier) {
00128         case EAP_GPSK_CIPHER_AES:
00129                 gkdf = eap_gpsk_gkdf_cmac;
00130                 mk_len = 16;
00131                 break;
00132 #ifdef EAP_GPSK_SHA256
00133         case EAP_GPSK_CIPHER_SHA256:
00134                 gkdf = eap_gpsk_gkdf_sha256;
00135                 mk_len = SHA256_MAC_LEN;
00136                 break;
00137 #endif /* EAP_GPSK_SHA256 */
00138         default:
00139                 return -1;
00140         }
00141 
00142         if (psk_len < mk_len)
00143                 return -1;
00144 
00145         data_len = 2 + psk_len + 6 + seed_len;
00146         data = os_malloc(data_len);
00147         if (data == NULL)
00148                 return -1;
00149         pos = data;
00150         WPA_PUT_BE16(pos, psk_len);
00151         pos += 2;
00152         os_memcpy(pos, psk, psk_len);
00153         pos += psk_len;
00154         WPA_PUT_BE32(pos, EAP_GPSK_VENDOR_IETF); /* CSuite/Vendor = IETF */
00155         pos += 4;
00156         WPA_PUT_BE16(pos, csuite_specifier); /* CSuite/Specifier */
00157         pos += 2;
00158         os_memcpy(pos, seed, seed_len); /* inputString */
00159         wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: Data to MK derivation",
00160                         data, data_len);
00161 
00162         if (gkdf(psk, data, data_len, mk, mk_len) < 0) {
00163                 os_free(data);
00164                 return -1;
00165         }
00166         os_free(data);
00167         wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: MK", mk, mk_len);
00168 
00169         if (gkdf(mk, seed, seed_len, kdf_out, kdf_out_len) < 0)
00170                 return -1;
00171 
00172         pos = kdf_out;
00173         wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: MSK", pos, EAP_MSK_LEN);
00174         os_memcpy(msk, pos, EAP_MSK_LEN);
00175         pos += EAP_MSK_LEN;
00176 
00177         wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: EMSK", pos, EAP_EMSK_LEN);
00178         os_memcpy(emsk, pos, EAP_EMSK_LEN);
00179         pos += EAP_EMSK_LEN;
00180 
00181         wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: SK", pos, sk_len);
00182         os_memcpy(sk, pos, sk_len);
00183         pos += sk_len;
00184 
00185         if (pk) {
00186                 wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PK", pos, pk_len);
00187                 os_memcpy(pk, pos, pk_len);
00188         }
00189 
00190         return 0;
00191 }
00192 
00193 
00194 static int eap_gpsk_derive_keys_aes(const u8 *psk, size_t psk_len,
00195                                     const u8 *seed, size_t seed_len,
00196                                     u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len,
00197                                     u8 *pk, size_t *pk_len)
00198 {
00199 #define EAP_GPSK_SK_LEN_AES 16
00200 #define EAP_GPSK_PK_LEN_AES 16
00201         u8 kdf_out[EAP_MSK_LEN + EAP_EMSK_LEN + EAP_GPSK_SK_LEN_AES +
00202                    EAP_GPSK_PK_LEN_AES];
00203 
00204         /*
00205          * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server
00206          *            (= seed)
00207          * KS = 16, PL = psk_len, CSuite_Sel = 0x00000000 0x0001
00208          * MK = GKDF-16 (PSK[0..15], PL || PSK || CSuite_Sel || inputString)
00209          * MSK = GKDF-160 (MK, inputString)[0..63]
00210          * EMSK = GKDF-160 (MK, inputString)[64..127]
00211          * SK = GKDF-160 (MK, inputString)[128..143]
00212          * PK = GKDF-160 (MK, inputString)[144..159]
00213          * zero = 0x00 || 0x00 || ... || 0x00 (16 times)
00214          * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type ||
00215          *                      CSuite_Sel || inputString)
00216          */
00217 
00218         *sk_len = EAP_GPSK_SK_LEN_AES;
00219         *pk_len = EAP_GPSK_PK_LEN_AES;
00220 
00221         return eap_gpsk_derive_keys_helper(EAP_GPSK_CIPHER_AES,
00222                                            kdf_out, sizeof(kdf_out),
00223                                            psk, psk_len, seed, seed_len,
00224                                            msk, emsk, sk, *sk_len,
00225                                            pk, *pk_len);
00226 }
00227 
00228 
00229 #ifdef EAP_GPSK_SHA256
00230 static int eap_gpsk_derive_keys_sha256(const u8 *psk, size_t psk_len,
00231                                        const u8 *seed, size_t seed_len,
00232                                        u8 *msk, u8 *emsk,
00233                                        u8 *sk, size_t *sk_len)
00234 {
00235 #define EAP_GPSK_SK_LEN_SHA256 SHA256_MAC_LEN
00236 #define EAP_GPSK_PK_LEN_SHA256 SHA256_MAC_LEN
00237         u8 kdf_out[EAP_MSK_LEN + EAP_EMSK_LEN + EAP_GPSK_SK_LEN_SHA256 +
00238                    EAP_GPSK_PK_LEN_SHA256];
00239 
00240         /*
00241          * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server
00242          *            (= seed)
00243          * KS = 32, PL = psk_len, CSuite_Sel = 0x00000000 0x0002
00244          * MK = GKDF-32 (PSK[0..31], PL || PSK || CSuite_Sel || inputString)
00245          * MSK = GKDF-160 (MK, inputString)[0..63]
00246          * EMSK = GKDF-160 (MK, inputString)[64..127]
00247          * SK = GKDF-160 (MK, inputString)[128..159]
00248          * zero = 0x00 || 0x00 || ... || 0x00 (32 times)
00249          * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type ||
00250          *                      CSuite_Sel || inputString)
00251          */
00252 
00253         *sk_len = EAP_GPSK_SK_LEN_SHA256;
00254 
00255         return eap_gpsk_derive_keys_helper(EAP_GPSK_CIPHER_SHA256,
00256                                            kdf_out, sizeof(kdf_out),
00257                                            psk, psk_len, seed, seed_len,
00258                                            msk, emsk, sk, *sk_len,
00259                                            NULL, 0);
00260 }
00261 #endif /* EAP_GPSK_SHA256 */
00262 
00263 
00284 int eap_gpsk_derive_keys(const u8 *psk, size_t psk_len, int vendor,
00285                          int specifier,
00286                          const u8 *rand_peer, const u8 *rand_server,
00287                          const u8 *id_peer, size_t id_peer_len,
00288                          const u8 *id_server, size_t id_server_len,
00289                          u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len,
00290                          u8 *pk, size_t *pk_len)
00291 {
00292         u8 *seed, *pos;
00293         size_t seed_len;
00294         int ret;
00295 
00296         wpa_printf(MSG_DEBUG, "EAP-GPSK: Deriving keys (%d:%d)",
00297                    vendor, specifier);
00298 
00299         if (vendor != EAP_GPSK_VENDOR_IETF)
00300                 return -1;
00301 
00302         wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PSK", psk, psk_len);
00303 
00304         /* Seed = RAND_Peer || ID_Peer || RAND_Server || ID_Server */
00305         seed_len = 2 * EAP_GPSK_RAND_LEN + id_server_len + id_peer_len;
00306         seed = os_malloc(seed_len);
00307         if (seed == NULL) {
00308                 wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to allocate memory "
00309                            "for key derivation");
00310                 return -1;
00311         }
00312 
00313         pos = seed;
00314         os_memcpy(pos, rand_peer, EAP_GPSK_RAND_LEN);
00315         pos += EAP_GPSK_RAND_LEN;
00316         os_memcpy(pos, id_peer, id_peer_len);
00317         pos += id_peer_len;
00318         os_memcpy(pos, rand_server, EAP_GPSK_RAND_LEN);
00319         pos += EAP_GPSK_RAND_LEN;
00320         os_memcpy(pos, id_server, id_server_len);
00321         pos += id_server_len;
00322         wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Seed", seed, seed_len);
00323 
00324         switch (specifier) {
00325         case EAP_GPSK_CIPHER_AES:
00326                 ret = eap_gpsk_derive_keys_aes(psk, psk_len, seed, seed_len,
00327                                                msk, emsk, sk, sk_len,
00328                                                pk, pk_len);
00329                 break;
00330 #ifdef EAP_GPSK_SHA256
00331         case EAP_GPSK_CIPHER_SHA256:
00332                 ret = eap_gpsk_derive_keys_sha256(psk, psk_len, seed, seed_len,
00333                                                   msk, emsk, sk, sk_len);
00334                 break;
00335 #endif /* EAP_GPSK_SHA256 */
00336         default:
00337                 wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d:%d used in "
00338                            "key derivation", vendor, specifier);
00339                 ret = -1;
00340                 break;
00341         }
00342 
00343         os_free(seed);
00344 
00345         return ret;
00346 }
00347 
00348 
00355 size_t eap_gpsk_mic_len(int vendor, int specifier)
00356 {
00357         if (vendor != EAP_GPSK_VENDOR_IETF)
00358                 return 0;
00359 
00360         switch (specifier) {
00361         case EAP_GPSK_CIPHER_AES:
00362                 return 16;
00363 #ifdef EAP_GPSK_SHA256
00364         case EAP_GPSK_CIPHER_SHA256:
00365                 return 32;
00366 #endif /* EAP_GPSK_SHA256 */
00367         default:
00368                 return 0;
00369         }
00370 }
00371 
00372 
00373 static int eap_gpsk_compute_mic_aes(const u8 *sk, size_t sk_len,
00374                                     const u8 *data, size_t len, u8 *mic)
00375 {
00376         if (sk_len != 16) {
00377                 wpa_printf(MSG_DEBUG, "EAP-GPSK: Invalid SK length %lu for "
00378                            "AES-CMAC MIC", (unsigned long) sk_len);
00379                 return -1;
00380         }
00381 
00382         return omac1_aes_128(sk, data, len, mic);
00383 }
00384 
00385 
00397 int eap_gpsk_compute_mic(const u8 *sk, size_t sk_len, int vendor,
00398                          int specifier, const u8 *data, size_t len, u8 *mic)
00399 {
00400         int ret;
00401 
00402         if (vendor != EAP_GPSK_VENDOR_IETF)
00403                 return -1;
00404 
00405         switch (specifier) {
00406         case EAP_GPSK_CIPHER_AES:
00407                 ret = eap_gpsk_compute_mic_aes(sk, sk_len, data, len, mic);
00408                 break;
00409 #ifdef EAP_GPSK_SHA256
00410         case EAP_GPSK_CIPHER_SHA256:
00411                 hmac_sha256(sk, sk_len, data, len, mic);
00412                 ret = 0;
00413                 break;
00414 #endif /* EAP_GPSK_SHA256 */
00415         default:
00416                 wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d:%d used in "
00417                            "MIC computation", vendor, specifier);
00418                 ret = -1;
00419                 break;
00420         }
00421 
00422         return ret;
00423 }


wpa_supplicant
Author(s): Package maintained by Blaise Gassend
autogenerated on Thu Jan 2 2014 11:26:37