eap_gpsk_common.c 14 KB

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