eap_gpsk_common.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549
  1. /*
  2. * EAP server/peer: EAP-GPSK shared routines
  3. * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
  4. *
  5. * This software may be distributed under the terms of the BSD license.
  6. * See README for more details.
  7. */
  8. #include "includes.h"
  9. #include "common.h"
  10. #include "crypto/aes_wrap.h"
  11. #include "crypto/sha256.h"
  12. #include "eap_defs.h"
  13. #include "eap_gpsk_common.h"
  14. /**
  15. * eap_gpsk_supported_ciphersuite - Check whether ciphersuite is supported
  16. * @vendor: CSuite/Vendor
  17. * @specifier: CSuite/Specifier
  18. * Returns: 1 if ciphersuite is support, or 0 if not
  19. */
  20. int eap_gpsk_supported_ciphersuite(int vendor, int specifier)
  21. {
  22. if (vendor == EAP_GPSK_VENDOR_IETF &&
  23. specifier == EAP_GPSK_CIPHER_AES)
  24. return 1;
  25. #ifdef EAP_GPSK_SHA256
  26. if (vendor == EAP_GPSK_VENDOR_IETF &&
  27. specifier == EAP_GPSK_CIPHER_SHA256)
  28. return 1;
  29. #endif /* EAP_GPSK_SHA256 */
  30. return 0;
  31. }
  32. static int eap_gpsk_gkdf_cmac(const u8 *psk /* Y */,
  33. const u8 *data /* Z */, size_t data_len,
  34. u8 *buf, size_t len /* X */)
  35. {
  36. u8 *opos;
  37. size_t i, n, hashlen, left, clen;
  38. u8 ibuf[2], hash[16];
  39. const u8 *addr[2];
  40. size_t vlen[2];
  41. hashlen = sizeof(hash);
  42. /* M_i = MAC_Y (i || Z); (MAC = AES-CMAC-128) */
  43. addr[0] = ibuf;
  44. vlen[0] = sizeof(ibuf);
  45. addr[1] = data;
  46. vlen[1] = data_len;
  47. opos = buf;
  48. left = len;
  49. n = (len + hashlen - 1) / hashlen;
  50. for (i = 1; i <= n; i++) {
  51. WPA_PUT_BE16(ibuf, i);
  52. if (omac1_aes_128_vector(psk, 2, addr, vlen, hash))
  53. return -1;
  54. clen = left > hashlen ? hashlen : left;
  55. os_memcpy(opos, hash, clen);
  56. opos += clen;
  57. left -= clen;
  58. }
  59. return 0;
  60. }
  61. #ifdef EAP_GPSK_SHA256
  62. static int eap_gpsk_gkdf_sha256(const u8 *psk /* Y */,
  63. const u8 *data /* Z */, size_t data_len,
  64. u8 *buf, size_t len /* X */)
  65. {
  66. u8 *opos;
  67. size_t i, n, hashlen, left, clen;
  68. u8 ibuf[2], hash[SHA256_MAC_LEN];
  69. const u8 *addr[2];
  70. size_t vlen[2];
  71. hashlen = SHA256_MAC_LEN;
  72. /* M_i = MAC_Y (i || Z); (MAC = HMAC-SHA256) */
  73. addr[0] = ibuf;
  74. vlen[0] = sizeof(ibuf);
  75. addr[1] = data;
  76. vlen[1] = data_len;
  77. opos = buf;
  78. left = len;
  79. n = (len + hashlen - 1) / hashlen;
  80. for (i = 1; i <= n; i++) {
  81. WPA_PUT_BE16(ibuf, i);
  82. hmac_sha256_vector(psk, 32, 2, addr, vlen, hash);
  83. clen = left > hashlen ? hashlen : left;
  84. os_memcpy(opos, hash, clen);
  85. opos += clen;
  86. left -= clen;
  87. }
  88. return 0;
  89. }
  90. #endif /* EAP_GPSK_SHA256 */
  91. static int eap_gpsk_derive_keys_helper(u32 csuite_specifier,
  92. u8 *kdf_out, size_t kdf_out_len,
  93. const u8 *psk, size_t psk_len,
  94. const u8 *seed, size_t seed_len,
  95. u8 *msk, u8 *emsk,
  96. u8 *sk, size_t sk_len,
  97. u8 *pk, size_t pk_len)
  98. {
  99. u8 mk[32], *pos, *data;
  100. size_t data_len, mk_len;
  101. int (*gkdf)(const u8 *_psk, const u8 *_data, size_t _data_len,
  102. u8 *buf, size_t len);
  103. gkdf = NULL;
  104. switch (csuite_specifier) {
  105. case EAP_GPSK_CIPHER_AES:
  106. gkdf = eap_gpsk_gkdf_cmac;
  107. mk_len = 16;
  108. break;
  109. #ifdef EAP_GPSK_SHA256
  110. case EAP_GPSK_CIPHER_SHA256:
  111. gkdf = eap_gpsk_gkdf_sha256;
  112. mk_len = SHA256_MAC_LEN;
  113. break;
  114. #endif /* EAP_GPSK_SHA256 */
  115. default:
  116. return -1;
  117. }
  118. if (psk_len < mk_len)
  119. return -1;
  120. data_len = 2 + psk_len + 6 + seed_len;
  121. data = os_malloc(data_len);
  122. if (data == NULL)
  123. return -1;
  124. pos = data;
  125. WPA_PUT_BE16(pos, psk_len);
  126. pos += 2;
  127. os_memcpy(pos, psk, psk_len);
  128. pos += psk_len;
  129. WPA_PUT_BE32(pos, EAP_GPSK_VENDOR_IETF); /* CSuite/Vendor = IETF */
  130. pos += 4;
  131. WPA_PUT_BE16(pos, csuite_specifier); /* CSuite/Specifier */
  132. pos += 2;
  133. os_memcpy(pos, seed, seed_len); /* inputString */
  134. wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: Data to MK derivation",
  135. data, data_len);
  136. if (gkdf(psk, data, data_len, mk, mk_len) < 0) {
  137. os_free(data);
  138. return -1;
  139. }
  140. os_free(data);
  141. wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: MK", mk, mk_len);
  142. if (gkdf(mk, seed, seed_len, kdf_out, kdf_out_len) < 0)
  143. return -1;
  144. pos = kdf_out;
  145. wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: MSK", pos, EAP_MSK_LEN);
  146. os_memcpy(msk, pos, EAP_MSK_LEN);
  147. pos += EAP_MSK_LEN;
  148. wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: EMSK", pos, EAP_EMSK_LEN);
  149. os_memcpy(emsk, pos, EAP_EMSK_LEN);
  150. pos += EAP_EMSK_LEN;
  151. wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: SK", pos, sk_len);
  152. os_memcpy(sk, pos, sk_len);
  153. pos += sk_len;
  154. if (pk) {
  155. wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PK", pos, pk_len);
  156. os_memcpy(pk, pos, pk_len);
  157. }
  158. return 0;
  159. }
  160. static int eap_gpsk_derive_keys_aes(const u8 *psk, size_t psk_len,
  161. const u8 *seed, size_t seed_len,
  162. u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len,
  163. u8 *pk, size_t *pk_len)
  164. {
  165. #define EAP_GPSK_SK_LEN_AES 16
  166. #define EAP_GPSK_PK_LEN_AES 16
  167. u8 kdf_out[EAP_MSK_LEN + EAP_EMSK_LEN + EAP_GPSK_SK_LEN_AES +
  168. EAP_GPSK_PK_LEN_AES];
  169. /*
  170. * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server
  171. * (= seed)
  172. * KS = 16, PL = psk_len, CSuite_Sel = 0x00000000 0x0001
  173. * MK = GKDF-16 (PSK[0..15], PL || PSK || CSuite_Sel || inputString)
  174. * MSK = GKDF-160 (MK, inputString)[0..63]
  175. * EMSK = GKDF-160 (MK, inputString)[64..127]
  176. * SK = GKDF-160 (MK, inputString)[128..143]
  177. * PK = GKDF-160 (MK, inputString)[144..159]
  178. * zero = 0x00 || 0x00 || ... || 0x00 (16 times)
  179. * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type ||
  180. * CSuite_Sel || inputString)
  181. */
  182. *sk_len = EAP_GPSK_SK_LEN_AES;
  183. *pk_len = EAP_GPSK_PK_LEN_AES;
  184. return eap_gpsk_derive_keys_helper(EAP_GPSK_CIPHER_AES,
  185. kdf_out, sizeof(kdf_out),
  186. psk, psk_len, seed, seed_len,
  187. msk, emsk, sk, *sk_len,
  188. pk, *pk_len);
  189. }
  190. #ifdef EAP_GPSK_SHA256
  191. static int eap_gpsk_derive_keys_sha256(const u8 *psk, size_t psk_len,
  192. const u8 *seed, size_t seed_len,
  193. u8 *msk, u8 *emsk,
  194. u8 *sk, size_t *sk_len)
  195. {
  196. #define EAP_GPSK_SK_LEN_SHA256 SHA256_MAC_LEN
  197. #define EAP_GPSK_PK_LEN_SHA256 SHA256_MAC_LEN
  198. u8 kdf_out[EAP_MSK_LEN + EAP_EMSK_LEN + EAP_GPSK_SK_LEN_SHA256 +
  199. EAP_GPSK_PK_LEN_SHA256];
  200. /*
  201. * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server
  202. * (= seed)
  203. * KS = 32, PL = psk_len, CSuite_Sel = 0x00000000 0x0002
  204. * MK = GKDF-32 (PSK[0..31], PL || PSK || CSuite_Sel || inputString)
  205. * MSK = GKDF-160 (MK, inputString)[0..63]
  206. * EMSK = GKDF-160 (MK, inputString)[64..127]
  207. * SK = GKDF-160 (MK, inputString)[128..159]
  208. * zero = 0x00 || 0x00 || ... || 0x00 (32 times)
  209. * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type ||
  210. * CSuite_Sel || inputString)
  211. */
  212. *sk_len = EAP_GPSK_SK_LEN_SHA256;
  213. return eap_gpsk_derive_keys_helper(EAP_GPSK_CIPHER_SHA256,
  214. kdf_out, sizeof(kdf_out),
  215. psk, psk_len, seed, seed_len,
  216. msk, emsk, sk, *sk_len,
  217. NULL, 0);
  218. }
  219. #endif /* EAP_GPSK_SHA256 */
  220. /**
  221. * eap_gpsk_derive_keys - Derive EAP-GPSK keys
  222. * @psk: Pre-shared key
  223. * @psk_len: Length of psk in bytes
  224. * @vendor: CSuite/Vendor
  225. * @specifier: CSuite/Specifier
  226. * @rand_peer: 32-byte RAND_Peer
  227. * @rand_server: 32-byte RAND_Server
  228. * @id_peer: ID_Peer
  229. * @id_peer_len: Length of ID_Peer
  230. * @id_server: ID_Server
  231. * @id_server_len: Length of ID_Server
  232. * @msk: Buffer for 64-byte MSK
  233. * @emsk: Buffer for 64-byte EMSK
  234. * @sk: Buffer for SK (at least EAP_GPSK_MAX_SK_LEN bytes)
  235. * @sk_len: Buffer for returning length of SK
  236. * @pk: Buffer for PK (at least EAP_GPSK_MAX_PK_LEN bytes)
  237. * @pk_len: Buffer for returning length of PK
  238. * Returns: 0 on success, -1 on failure
  239. */
  240. int eap_gpsk_derive_keys(const u8 *psk, size_t psk_len, int vendor,
  241. int specifier,
  242. const u8 *rand_peer, const u8 *rand_server,
  243. const u8 *id_peer, size_t id_peer_len,
  244. const u8 *id_server, size_t id_server_len,
  245. u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len,
  246. u8 *pk, size_t *pk_len)
  247. {
  248. u8 *seed, *pos;
  249. int ret;
  250. wpa_printf(MSG_DEBUG, "EAP-GPSK: Deriving keys (%d:%d)",
  251. vendor, specifier);
  252. if (vendor != EAP_GPSK_VENDOR_IETF)
  253. return -1;
  254. wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PSK", psk, psk_len);
  255. /* Seed = RAND_Peer || ID_Peer || RAND_Server || ID_Server */
  256. seed = os_malloc(2 * EAP_GPSK_RAND_LEN + id_server_len + id_peer_len);
  257. if (seed == NULL) {
  258. wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to allocate memory "
  259. "for key derivation");
  260. return -1;
  261. }
  262. pos = seed;
  263. os_memcpy(pos, rand_peer, EAP_GPSK_RAND_LEN);
  264. pos += EAP_GPSK_RAND_LEN;
  265. os_memcpy(pos, id_peer, id_peer_len);
  266. pos += id_peer_len;
  267. os_memcpy(pos, rand_server, EAP_GPSK_RAND_LEN);
  268. pos += EAP_GPSK_RAND_LEN;
  269. os_memcpy(pos, id_server, id_server_len);
  270. pos += id_server_len;
  271. wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Seed", seed, pos - seed);
  272. switch (specifier) {
  273. case EAP_GPSK_CIPHER_AES:
  274. ret = eap_gpsk_derive_keys_aes(psk, psk_len, seed, pos - seed,
  275. msk, emsk, sk, sk_len,
  276. pk, pk_len);
  277. break;
  278. #ifdef EAP_GPSK_SHA256
  279. case EAP_GPSK_CIPHER_SHA256:
  280. ret = eap_gpsk_derive_keys_sha256(psk, psk_len, seed,
  281. pos - seed,
  282. msk, emsk, sk, sk_len);
  283. break;
  284. #endif /* EAP_GPSK_SHA256 */
  285. default:
  286. wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d:%d used in "
  287. "key derivation", vendor, specifier);
  288. ret = -1;
  289. break;
  290. }
  291. os_free(seed);
  292. return ret;
  293. }
  294. static int eap_gpsk_derive_mid_helper(u32 csuite_specifier,
  295. u8 *kdf_out, size_t kdf_out_len,
  296. const u8 *psk, const u8 *seed,
  297. size_t seed_len, u8 method_type)
  298. {
  299. u8 *pos, *data;
  300. size_t data_len;
  301. int (*gkdf)(const u8 *_psk, const u8 *_data, size_t _data_len,
  302. u8 *buf, size_t len);
  303. gkdf = NULL;
  304. switch (csuite_specifier) {
  305. case EAP_GPSK_CIPHER_AES:
  306. gkdf = eap_gpsk_gkdf_cmac;
  307. break;
  308. #ifdef EAP_GPSK_SHA256
  309. case EAP_GPSK_CIPHER_SHA256:
  310. gkdf = eap_gpsk_gkdf_sha256;
  311. break;
  312. #endif /* EAP_GPSK_SHA256 */
  313. default:
  314. wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d used in "
  315. "Session-Id derivation", csuite_specifier);
  316. return -1;
  317. }
  318. #define SID_LABEL "Method ID"
  319. /* "Method ID" || EAP_Method_Type || CSuite_Sel || inputString */
  320. data_len = strlen(SID_LABEL) + 1 + 6 + seed_len;
  321. data = os_malloc(data_len);
  322. if (data == NULL)
  323. return -1;
  324. pos = data;
  325. os_memcpy(pos, SID_LABEL, strlen(SID_LABEL));
  326. pos += strlen(SID_LABEL);
  327. #undef SID_LABEL
  328. os_memcpy(pos, &method_type, 1);
  329. pos += 1;
  330. WPA_PUT_BE32(pos, EAP_GPSK_VENDOR_IETF); /* CSuite/Vendor = IETF */
  331. pos += 4;
  332. WPA_PUT_BE16(pos, csuite_specifier); /* CSuite/Specifier */
  333. pos += 2;
  334. os_memcpy(pos, seed, seed_len); /* inputString */
  335. wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Data to Method ID derivation",
  336. data, data_len);
  337. if (gkdf(psk, data, data_len, kdf_out, kdf_out_len) < 0) {
  338. os_free(data);
  339. return -1;
  340. }
  341. os_free(data);
  342. wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Method ID", kdf_out, kdf_out_len);
  343. return 0;
  344. }
  345. /**
  346. * eap_gpsk_session_id - Derive EAP-GPSK Session ID
  347. * @psk: Pre-shared key
  348. * @psk_len: Length of psk in bytes
  349. * @vendor: CSuite/Vendor
  350. * @specifier: CSuite/Specifier
  351. * @rand_peer: 32-byte RAND_Peer
  352. * @rand_server: 32-byte RAND_Server
  353. * @id_peer: ID_Peer
  354. * @id_peer_len: Length of ID_Peer
  355. * @id_server: ID_Server
  356. * @id_server_len: Length of ID_Server
  357. * @method_type: EAP Authentication Method Type
  358. * @sid: Buffer for 17-byte Session ID
  359. * @sid_len: Buffer for returning length of Session ID
  360. * Returns: 0 on success, -1 on failure
  361. */
  362. int eap_gpsk_derive_session_id(const u8 *psk, size_t psk_len, int vendor,
  363. int specifier,
  364. const u8 *rand_peer, const u8 *rand_server,
  365. const u8 *id_peer, size_t id_peer_len,
  366. const u8 *id_server, size_t id_server_len,
  367. u8 method_type, u8 *sid, size_t *sid_len)
  368. {
  369. u8 *seed, *pos;
  370. u8 kdf_out[16];
  371. int ret;
  372. wpa_printf(MSG_DEBUG, "EAP-GPSK: Deriving Session ID(%d:%d)",
  373. vendor, specifier);
  374. if (vendor != EAP_GPSK_VENDOR_IETF)
  375. return -1;
  376. wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PSK", psk, psk_len);
  377. /*
  378. * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server
  379. * (= seed)
  380. * KS = 16, CSuite_Sel = 0x00000000 0x0001
  381. * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type ||
  382. * CSuite_Sel || inputString)
  383. */
  384. seed = os_malloc(2 * EAP_GPSK_RAND_LEN + id_server_len + id_peer_len);
  385. if (seed == NULL) {
  386. wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to allocate memory "
  387. "for Session-Id derivation");
  388. return -1;
  389. }
  390. pos = seed;
  391. os_memcpy(pos, rand_peer, EAP_GPSK_RAND_LEN);
  392. pos += EAP_GPSK_RAND_LEN;
  393. os_memcpy(pos, id_peer, id_peer_len);
  394. pos += id_peer_len;
  395. os_memcpy(pos, rand_server, EAP_GPSK_RAND_LEN);
  396. pos += EAP_GPSK_RAND_LEN;
  397. os_memcpy(pos, id_server, id_server_len);
  398. pos += id_server_len;
  399. wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Seed", seed, pos - seed);
  400. ret = eap_gpsk_derive_mid_helper(specifier,
  401. kdf_out, sizeof(kdf_out),
  402. psk, seed, pos - seed,
  403. method_type);
  404. sid[0] = method_type;
  405. os_memcpy(sid + 1, kdf_out, sizeof(kdf_out));
  406. *sid_len = 1 + sizeof(kdf_out);
  407. os_free(seed);
  408. return ret;
  409. }
  410. /**
  411. * eap_gpsk_mic_len - Get the length of the MIC
  412. * @vendor: CSuite/Vendor
  413. * @specifier: CSuite/Specifier
  414. * Returns: MIC length in bytes
  415. */
  416. size_t eap_gpsk_mic_len(int vendor, int specifier)
  417. {
  418. if (vendor != EAP_GPSK_VENDOR_IETF)
  419. return 0;
  420. switch (specifier) {
  421. case EAP_GPSK_CIPHER_AES:
  422. return 16;
  423. #ifdef EAP_GPSK_SHA256
  424. case EAP_GPSK_CIPHER_SHA256:
  425. return 32;
  426. #endif /* EAP_GPSK_SHA256 */
  427. default:
  428. return 0;
  429. }
  430. }
  431. static int eap_gpsk_compute_mic_aes(const u8 *sk, size_t sk_len,
  432. const u8 *data, size_t len, u8 *mic)
  433. {
  434. if (sk_len != 16) {
  435. wpa_printf(MSG_DEBUG, "EAP-GPSK: Invalid SK length %lu for "
  436. "AES-CMAC MIC", (unsigned long) sk_len);
  437. return -1;
  438. }
  439. return omac1_aes_128(sk, data, len, mic);
  440. }
  441. /**
  442. * eap_gpsk_compute_mic - Compute EAP-GPSK MIC for an EAP packet
  443. * @sk: Session key SK from eap_gpsk_derive_keys()
  444. * @sk_len: SK length in bytes from eap_gpsk_derive_keys()
  445. * @vendor: CSuite/Vendor
  446. * @specifier: CSuite/Specifier
  447. * @data: Input data to MIC
  448. * @len: Input data length in bytes
  449. * @mic: Buffer for the computed MIC, eap_gpsk_mic_len(cipher) bytes
  450. * Returns: 0 on success, -1 on failure
  451. */
  452. int eap_gpsk_compute_mic(const u8 *sk, size_t sk_len, int vendor,
  453. int specifier, const u8 *data, size_t len, u8 *mic)
  454. {
  455. int ret;
  456. if (vendor != EAP_GPSK_VENDOR_IETF)
  457. return -1;
  458. switch (specifier) {
  459. case EAP_GPSK_CIPHER_AES:
  460. ret = eap_gpsk_compute_mic_aes(sk, sk_len, data, len, mic);
  461. break;
  462. #ifdef EAP_GPSK_SHA256
  463. case EAP_GPSK_CIPHER_SHA256:
  464. hmac_sha256(sk, sk_len, data, len, mic);
  465. ret = 0;
  466. break;
  467. #endif /* EAP_GPSK_SHA256 */
  468. default:
  469. wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d:%d used in "
  470. "MIC computation", vendor, specifier);
  471. ret = -1;
  472. break;
  473. }
  474. return ret;
  475. }