2 * Copyright (c) 2011 Rustam Kovhaev. All rights reserved.
3 * Copyright (c) 2021 Eivind Næss. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
17 * 3. The name(s) of the authors of this software must not be used to
18 * endorse or promote products derived from this software without
19 * prior written permission.
21 * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
22 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
23 * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
24 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
25 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
26 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
27 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
32 * 1 - Outer EAP, where TLS session gets established
33 * 2 - Inner EAP, where inside TLS session with EAP MSCHAPV2 auth, or any other auth
35 * And so protocols encapsulation looks like this:
36 * Outer EAP -> TLS -> Inner EAP -> MSCHAPV2
37 * PEAP can compress an inner EAP packet prior to encapsulating it within
38 * the Data field of a PEAP packet by removing its Code, Identifier,
39 * and Length fields, and Microsoft PEAP server/client always does that
41 * Current implementation does not support:
43 * b) Inner EAP fragmentation
44 * c) Any other auth other than MSCHAPV2
46 * For details on the PEAP protocol, look to Microsoft:
47 * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-peap
58 #include <openssl/opensslv.h>
59 #include <openssl/ssl.h>
60 #include <openssl/hmac.h>
61 #include <openssl/rand.h>
62 #include <openssl/err.h>
64 #include "pppd-private.h"
87 u_char ipmk[PEAP_TLV_IPMK_LEN];
88 u_char tk[PEAP_TLV_TK_LEN];
89 u_char nonce[PEAP_TLV_NONCE_LEN];
90 struct tls_info *info;
91 #ifdef PPP_WITH_CHAPMS
92 struct chap_digest_type *chap;
97 * K = Key, S = Seed, LEN = output length
98 * PRF+(K, S, LEN) = T1 | T2 | ... |Tn
100 * T1 = HMAC-SHA1 (K, S | 0x01 | 0x00 | 0x00)
101 * T2 = HMAC-SHA1 (K, T1 | S | 0x02 | 0x00 | 0x00)
103 * Tn = HMAC-SHA1 (K, Tn-1 | S | n | 0x00 | 0x00)
104 * As shown, PRF+ is computed in iterations. The number of iterations (n)
105 * depends on the output length (LEN).
107 static void peap_prfplus(u_char *seed, size_t seed_len, u_char *key, size_t key_len, u_char *out_buf, size_t pfr_len)
111 size_t max_iter, i, j, k;
114 max_iter = (pfr_len + SHA_DIGEST_LENGTH - 1) / SHA_DIGEST_LENGTH;
115 buf = malloc(seed_len + max_iter * SHA_DIGEST_LENGTH);
118 hash = malloc(pfr_len + SHA_DIGEST_LENGTH);
122 for (i = 0; i < max_iter; i++) {
127 j = SHA_DIGEST_LENGTH;
128 for (k = 0; k < seed_len; k++)
129 buf[j + k] = seed[k];
137 if (!HMAC(EVP_sha1(), key, key_len, buf, pos, (hash + i * SHA_DIGEST_LENGTH), &len))
138 fatal("HMAC() failed");
139 for (j = 0; j < SHA_DIGEST_LENGTH; j++)
140 buf[j] = hash[i * SHA_DIGEST_LENGTH + j];
142 BCOPY(hash, out_buf, pfr_len);
147 static void generate_cmk(u_char *ipmk, u_char *tempkey, u_char *nonce, u_char *tlv_response_out, int client)
149 const char *label = PEAP_TLV_IPMK_SEED_LABEL;
150 u_char data_tlv[PEAP_TLV_DATA_LEN] = {0};
151 u_char isk[PEAP_TLV_ISK_LEN] = {0};
152 u_char ipmkseed[PEAP_TLV_IPMKSEED_LEN] = {0};
153 u_char cmk[PEAP_TLV_CMK_LEN] = {0};
154 u_char buf[PEAP_TLV_CMK_LEN + PEAP_TLV_IPMK_LEN] = {0};
155 u_char compound_mac[PEAP_TLV_COMP_MAC_LEN] = {0};
158 /* format outgoing CB TLV response packet */
159 data_tlv[1] = PEAP_TLV_TYPE;
160 data_tlv[3] = PEAP_TLV_LENGTH_FIELD;
162 data_tlv[7] = PEAP_TLV_SUBTYPE_RESPONSE;
164 data_tlv[7] = PEAP_TLV_SUBTYPE_REQUEST;
165 BCOPY(nonce, (data_tlv + PEAP_TLV_HEADERLEN), PEAP_TLV_NONCE_LEN);
166 data_tlv[60] = EAPT_PEAP;
169 mppe_get_send_key(isk, MPPE_MAX_KEY_LEN);
170 mppe_get_recv_key(isk + MPPE_MAX_KEY_LEN, MPPE_MAX_KEY_LEN);
173 BCOPY(label, ipmkseed, strlen(label));
174 BCOPY(isk, ipmkseed + strlen(label), PEAP_TLV_ISK_LEN);
175 peap_prfplus(ipmkseed, PEAP_TLV_IPMKSEED_LEN,
176 tempkey, PEAP_TLV_TEMPKEY_LEN, buf, PEAP_TLV_CMK_LEN + PEAP_TLV_IPMK_LEN);
178 BCOPY(buf, ipmk, PEAP_TLV_IPMK_LEN);
179 BCOPY(buf + PEAP_TLV_IPMK_LEN, cmk, PEAP_TLV_CMK_LEN);
180 if (!HMAC(EVP_sha1(), cmk, PEAP_TLV_CMK_LEN, data_tlv, PEAP_TLV_DATA_LEN, compound_mac, &len))
181 fatal("HMAC() failed");
182 BCOPY(compound_mac, data_tlv + PEAP_TLV_HEADERLEN + PEAP_TLV_NONCE_LEN, PEAP_TLV_COMP_MAC_LEN);
183 /* do not copy last byte to response packet */
184 BCOPY(data_tlv, tlv_response_out, PEAP_TLV_DATA_LEN - 1);
187 static void verify_compound_mac(struct peap_state *psm, u_char *in_buf)
189 u_char nonce[PEAP_TLV_NONCE_LEN] = {0};
190 u_char out_buf[PEAP_TLV_LEN] = {0};
192 BCOPY(in_buf, nonce, PEAP_TLV_NONCE_LEN);
193 generate_cmk(psm->ipmk, psm->tk, nonce, out_buf, 0);
194 if (memcmp((in_buf + PEAP_TLV_NONCE_LEN), (out_buf + PEAP_TLV_HEADERLEN + PEAP_TLV_NONCE_LEN), PEAP_TLV_CMK_LEN))
195 fatal("server's CMK does not match client's CMK, potential MiTM");
199 #define PEAP_MPPE_KEY_LEN 32
201 static void generate_mppe_keys(u_char *ipmk, int client)
203 const char *label = PEAP_TLV_CSK_SEED_LABEL;
204 u_char csk[PEAP_TLV_CSK_LEN] = {0};
207 dbglog("PEAP CB: generate mppe keys");
209 len++; /* CSK requires NULL byte in seed */
210 peap_prfplus((u_char *)label, len, ipmk, PEAP_TLV_IPMK_LEN, csk, PEAP_TLV_CSK_LEN);
213 * The first 64 bytes of the CSK are split into two MPPE keys, as follows.
215 * +-----------------------+------------------------+
216 * | First 32 bytes of CSK | Second 32 bytes of CSK |
217 * +-----------------------+------------------------+
218 * | MS-MPPE-Send-Key | MS-MPPE-Recv-Key |
219 * +-----------------------+------------------------+
222 mppe_set_keys(csk, csk + PEAP_MPPE_KEY_LEN, PEAP_MPPE_KEY_LEN);
224 mppe_set_keys(csk + PEAP_MPPE_KEY_LEN, csk, PEAP_MPPE_KEY_LEN);
232 static void peap_ack(eap_state *esp, u_char id)
236 outp = outpacket_buf;
237 MAKEHEADER(outp, PPP_EAP);
238 PUTCHAR(EAP_RESPONSE, outp);
240 esp->es_client.ea_id = id;
241 PUTSHORT(PEAP_HEADERLEN, outp);
242 PUTCHAR(EAPT_PEAP, outp);
243 PUTCHAR(PEAP_FLAGS_ACK, outp);
244 output(esp->es_unit, outpacket_buf, PPP_HDRLEN + PEAP_HEADERLEN);
247 static void peap_response(eap_state *esp, u_char id, u_char *buf, int len)
249 struct peap_state *psm = esp->ea_peap;
253 outp = outpacket_buf;
254 MAKEHEADER(outp, PPP_EAP);
255 PUTCHAR(EAP_RESPONSE, outp);
257 esp->es_client.ea_id = id;
259 if (psm->phase == PEAP_PHASE_1)
260 peap_len = PEAP_HEADERLEN + PEAP_FRAGMENT_LENGTH_FIELD + len;
262 peap_len = PEAP_HEADERLEN + len;
264 PUTSHORT(peap_len, outp);
265 PUTCHAR(EAPT_PEAP, outp);
267 if (psm->phase == PEAP_PHASE_1) {
268 PUTCHAR(PEAP_L_FLAG_SET, outp);
271 PUTCHAR(PEAP_NO_FLAGS, outp);
273 BCOPY(buf, outp, len);
274 output(esp->es_unit, outpacket_buf, PPP_HDRLEN + peap_len);
277 void peap_do_inner_eap(u_char *in_buf, int in_len, eap_state *esp, int id,
278 u_char *out_buf, int *out_len)
280 struct peap_state *psm = esp->ea_peap;
284 char secret[MAXSECRETLEN + 1];
285 char rhostname[MAXWORDLEN];
286 u_char *outp = out_buf;
288 dbglog("PEAP: EAP (in): %.*B", in_len, in_buf);
290 if (*(in_buf + EAP_HEADERLEN) == PEAP_CAPABILITIES_TYPE &&
291 in_len == (EAP_HEADERLEN + PEAP_CAPABILITIES_LEN)) {
292 /* use original packet as template for response */
293 BCOPY(in_buf, outp, EAP_HEADERLEN + PEAP_CAPABILITIES_LEN);
294 PUTCHAR(EAP_RESPONSE, outp);
296 /* change last byte to 0 to disable fragmentation */
297 *(outp + PEAP_CAPABILITIES_LEN + 1) = 0x00;
298 used = EAP_HEADERLEN + PEAP_CAPABILITIES_LEN;
301 if (*(in_buf + EAP_HEADERLEN + PEAP_TLV_HEADERLEN) == PEAP_TLV_TYPE &&
302 in_len == PEAP_TLV_LEN) {
303 /* PEAP TLV message, do cryptobinding */
304 SSL_export_keying_material(psm->ssl, psm->tk, PEAP_TLV_TK_LEN,
305 PEAP_TLV_TK_SEED_LABEL, strlen(PEAP_TLV_TK_SEED_LABEL), NULL, 0, 0);
306 /* verify server's CMK */
307 verify_compound_mac(psm, in_buf + EAP_HEADERLEN + PEAP_TLV_RESULT_LEN + PEAP_TLV_HEADERLEN);
308 /* generate client's CMK with new nonce */
309 PUTCHAR(EAP_RESPONSE, outp);
311 PUTSHORT(PEAP_TLV_LEN, outp);
312 BCOPY(in_buf + EAP_HEADERLEN, outp, PEAP_TLV_RESULT_LEN);
313 outp = outp + PEAP_TLV_RESULT_LEN;
314 RAND_bytes(psm->nonce, PEAP_TLV_NONCE_LEN);
315 generate_cmk(psm->ipmk, psm->tk, psm->nonce, outp, 1);
318 generate_mppe_keys(psm->ipmk, 1);
324 GETCHAR(typenum, in_buf);
329 /* Respond with our identity to the peer */
330 PUTCHAR(EAPT_IDENTITY, outp);
331 BCOPY(esp->es_client.ea_name, outp,
332 esp->es_client.ea_namelen);
333 used += (esp->es_client.ea_namelen + 1);
337 /* Send NAK to EAP_TLS request */
338 PUTCHAR(EAPT_NAK, outp);
339 PUTCHAR(EAPT_MSCHAPV2, outp);
344 case EAPT_MSCHAPV2: {
346 // Must have at least 4 more bytes to process CHAP header
348 error("PEAP: received invalid MSCHAPv2 packet, too short");
353 GETCHAR(opcode, in_buf);
356 GETCHAR(chap_id, in_buf);
359 GETSHORT(mssize, in_buf);
361 // Validate the CHAP packet (including header)
362 if (in_len != mssize) {
363 error("PEAP: received invalid MSCHAPv2 packet, invalid length");
369 case CHAP_CHALLENGE: {
371 u_char *challenge = in_buf; // VLEN + VALUE
374 GETCHAR(vsize, in_buf);
377 if (vsize != MS_CHAP2_PEER_CHAL_LEN || in_len < MS_CHAP2_PEER_CHAL_LEN) {
378 error("PEAP: received invalid MSCHAPv2 packet, invalid value-length: %d", vsize);
382 INCPTR(MS_CHAP2_PEER_CHAL_LEN, in_buf);
383 in_len -= MS_CHAP2_PEER_CHAL_LEN;
385 // Copy the provided remote host name
388 if (in_len >= sizeof(rhostname)) {
389 dbglog("PEAP: trimming really long peer name down");
390 in_len = sizeof(rhostname) - 1;
392 BCOPY(in_buf, rhostname, in_len);
393 rhostname[in_len] = '\0';
396 // In case the remote doesn't give us his name, or user explictly specified remotename is config
397 if (explicit_remote || (remote_name[0] != '\0' && in_len == 0))
398 strlcpy(rhostname, remote_name, sizeof(rhostname));
400 // Get the scrert for authenticating ourselves with the specified host
401 if (get_secret(esp->es_unit, esp->es_client.ea_name,
402 rhostname, secret, &secret_len, 0)) {
404 u_char response[MS_CHAP2_RESPONSE_LEN+1];
405 u_char user_len = esp->es_client.ea_namelen;
406 char *user = esp->es_client.ea_name;
408 psm->chap->make_response(response, chap_id, user,
409 challenge, secret, secret_len, NULL);
411 PUTCHAR(EAPT_MSCHAPV2, outp);
412 PUTCHAR(CHAP_RESPONSE, outp);
413 PUTCHAR(chap_id, outp);
415 PUTCHAR(5 + user_len + MS_CHAP2_RESPONSE_LEN, outp);
416 BCOPY(response, outp, MS_CHAP2_RESPONSE_LEN+1); // VLEN + VALUE
417 INCPTR(MS_CHAP2_RESPONSE_LEN+1, outp);
418 BCOPY(user, outp, user_len);
419 used = 5 + user_len + MS_CHAP2_RESPONSE_LEN + 1;
422 dbglog("PEAP: no CHAP secret for auth to %q", rhostname);
423 PUTCHAR(EAPT_NAK, outp);
430 u_char status = CHAP_FAILURE;
431 if (psm->chap->check_success(chap_id, in_buf, in_len)) {
432 info("Chap authentication succeeded! %.*v", in_len, in_buf);
433 status = CHAP_SUCCESS;
436 PUTCHAR(EAPT_MSCHAPV2, outp);
437 PUTCHAR(status, outp);
443 u_char status = CHAP_FAILURE;
444 psm->chap->handle_failure(in_buf, in_len);
445 PUTCHAR(EAPT_MSCHAPV2, outp);
446 PUTCHAR(status, outp);
458 /* send compressed EAP NAK for any unknown packet */
459 PUTCHAR(EAPT_NAK, outp);
465 dbglog("PEAP: EAP (out): %.*B", used, psm->out_buf);
469 int peap_init(struct peap_state **ctx, const char *rhostname)
471 const SSL_METHOD *method;
478 struct peap_state *psm = malloc(sizeof(*psm));
480 novm("peap psm struct");
481 psm->in_buf = malloc(TLS_RECORD_MAX_SIZE);
483 novm("peap tls buffer");
484 psm->out_buf = malloc(TLS_RECORD_MAX_SIZE);
486 novm("peap tls buffer");
487 method = tls_method();
489 novm("TLS_method() failed");
490 psm->ctx = SSL_CTX_new(method);
492 novm("SSL_CTX_new() failed");
494 /* Configure the default options */
495 tls_set_opts(psm->ctx);
497 /* Configure the max TLS version */
498 tls_set_version(psm->ctx, max_tls_version);
500 /* Configure the peer certificate callback */
501 tls_set_verify(psm->ctx, 5);
503 /* Configure CA locations */
504 if (tls_set_ca(psm->ctx, ca_path, cacert_file)) {
505 fatal("Could not set CA verify locations");
508 /* Configure CRL check (if any) */
509 if (tls_set_crl(psm->ctx, crl_dir, crl_file)) {
510 fatal("Could not set CRL verify locations");
513 psm->out_bio = BIO_new(BIO_s_mem());
514 psm->in_bio = BIO_new(BIO_s_mem());
515 BIO_set_mem_eof_return(psm->out_bio, -1);
516 BIO_set_mem_eof_return(psm->in_bio, -1);
517 psm->ssl = SSL_new(psm->ctx);
518 SSL_set_bio(psm->ssl, psm->in_bio, psm->out_bio);
519 SSL_set_connect_state(psm->ssl);
520 psm->phase = PEAP_PHASE_1;
521 tls_set_verify_info(psm->ssl, explicit_remote ? rhostname : NULL, NULL, 1, &psm->info);
522 psm->chap = chap_find_digest(CHAP_MICROSOFT_V2);
527 void peap_finish(struct peap_state **psm) {
530 struct peap_state *tmp = *psm;
536 SSL_CTX_free(tmp->ctx);
539 tls_free_verify_info(&tmp->info);
541 // NOTE: BIO and memory is freed as a part of SSL_free()
548 int peap_process(eap_state *esp, u_char id, u_char *inp, int len)
553 struct peap_state *psm = esp->ea_peap;
555 if (esp->es_client.ea_id == id) {
556 info("PEAP: retransmits are not supported..");
561 case PEAP_S_FLAG_SET:
562 dbglog("PEAP: S bit is set, starting PEAP phase 1");
563 ret = SSL_do_handshake(psm->ssl);
565 ret = SSL_get_error(psm->ssl, ret);
566 if (ret != SSL_ERROR_WANT_READ && ret != SSL_ERROR_WANT_WRITE)
567 fatal("SSL_do_handshake(): %s", ERR_error_string(ret, NULL));
570 psm->read = BIO_read(psm->out_bio, psm->out_buf, TLS_RECORD_MAX_SIZE);
571 peap_response(esp, id, psm->out_buf, psm->read);
574 case PEAP_LM_FLAG_SET:
575 dbglog("PEAP TLS: LM bits are set, need to get more TLS fragments");
576 inp = inp + PEAP_FRAGMENT_LENGTH_FIELD + PEAP_FLAGS_FIELD;
577 psm->written = BIO_write(psm->in_bio, inp, len - PEAP_FRAGMENT_LENGTH_FIELD - PEAP_FLAGS_FIELD);
581 case PEAP_M_FLAG_SET:
582 dbglog("PEAP TLS: M bit is set, need to get more TLS fragments");
583 inp = inp + PEAP_FLAGS_FIELD;
584 psm->written = BIO_write(psm->in_bio, inp, len - PEAP_FLAGS_FIELD);
588 case PEAP_L_FLAG_SET:
590 if (*inp == PEAP_L_FLAG_SET) {
591 dbglog("PEAP TLS: L bit is set");
592 inp = inp + PEAP_FRAGMENT_LENGTH_FIELD + PEAP_FLAGS_FIELD;
593 psm->written = BIO_write(psm->in_bio, inp, len - PEAP_FRAGMENT_LENGTH_FIELD - PEAP_FLAGS_FIELD);
595 dbglog("PEAP TLS: all bits are off");
596 inp = inp + PEAP_FLAGS_FIELD;
597 psm->written = BIO_write(psm->in_bio, inp, len - PEAP_FLAGS_FIELD);
600 if (psm->phase == PEAP_PHASE_1) {
601 dbglog("PEAP TLS: continue handshake");
602 ret = SSL_do_handshake(psm->ssl);
604 ret = SSL_get_error(psm->ssl, ret);
605 if (ret != SSL_ERROR_WANT_READ && ret != SSL_ERROR_WANT_WRITE)
606 fatal("SSL_do_handshake(): %s", ERR_error_string(ret, NULL));
608 if (SSL_is_init_finished(psm->ssl))
609 psm->phase = PEAP_PHASE_2;
610 if (BIO_ctrl_pending(psm->out_bio) == 0) {
615 psm->read = BIO_read(psm->out_bio, psm->out_buf,
616 TLS_RECORD_MAX_SIZE);
617 peap_response(esp, id, psm->out_buf, psm->read);
620 psm->read = SSL_read(psm->ssl, psm->in_buf,
621 TLS_RECORD_MAX_SIZE);
622 out_len = TLS_RECORD_MAX_SIZE;
623 peap_do_inner_eap(psm->in_buf, psm->read, esp, id,
624 psm->out_buf, &out_len);
626 psm->written = SSL_write(psm->ssl, psm->out_buf, out_len);
627 psm->read = BIO_read(psm->out_bio, psm->out_buf,
628 TLS_RECORD_MAX_SIZE);
629 peap_response(esp, id, psm->out_buf, psm->read);
638 u_char outpacket_buf[255];
644 * Using the example in MS-PEAP, section 4.4.1.
645 * see https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-peap/5308642b-90c9-4cc4-beec-fb367325c0f9
647 int test_cmk(u_char *ipmk) {
648 u_char nonce[PEAP_TLV_NONCE_LEN] = {
649 0x6C, 0x6B, 0xA3, 0x87, 0x84, 0x23, 0x74, 0x57,
650 0xCC, 0xC9, 0x0B, 0x1A, 0x90, 0x8C, 0xBD, 0xF4,
651 0x71, 0x1B, 0x69, 0x99, 0x4D, 0x0C, 0xFE, 0x8D,
652 0x3D, 0xB4, 0x4E, 0xCB, 0xCD, 0xAD, 0x37, 0xE9
655 u_char tmpkey[PEAP_TLV_TEMPKEY_LEN] = {
656 0x73, 0x8B, 0xB5, 0xF4, 0x62, 0xD5, 0x8E, 0x7E,
657 0xD8, 0x44, 0xE1, 0xF0, 0x0D, 0x0E, 0xBE, 0x50,
658 0xC5, 0x0A, 0x20, 0x50, 0xDE, 0x11, 0x99, 0x77,
659 0x10, 0xD6, 0x5F, 0x45, 0xFB, 0x5F, 0xBA, 0xB7,
660 0xE3, 0x18, 0x1E, 0x92, 0x4F, 0x42, 0x97, 0x38,
661 // 0xDE, 0x40, 0xC8, 0x46, 0xCD, 0xF5, 0x0B, 0xCB,
662 // 0xF9, 0xCE, 0xDB, 0x1E, 0x85, 0x1D, 0x22, 0x52,
663 // 0x45, 0x3B, 0xDF, 0x63
666 u_char expected[60] = {
667 0x00, 0x0C, 0x00, 0x38, 0x00, 0x00, 0x00, 0x01,
668 0x6C, 0x6B, 0xA3, 0x87, 0x84, 0x23, 0x74, 0x57,
669 0xCC, 0xC9, 0x0B, 0x1A, 0x90, 0x8C, 0xBD, 0xF4,
670 0x71, 0x1B, 0x69, 0x99, 0x4D, 0x0C, 0xFE, 0x8D,
671 0x3D, 0xB4, 0x4E, 0xCB, 0xCD, 0xAD, 0x37, 0xE9,
672 0x42, 0xE0, 0x86, 0x07, 0x1D, 0x1C, 0x8B, 0x8C,
673 0x8E, 0x45, 0x8F, 0x70, 0x21, 0xF0, 0x6A, 0x6E,
674 0xAB, 0x16, 0xB6, 0x46
677 u_char inner_mppe_keys[32] = {
678 0x67, 0x3E, 0x96, 0x14, 0x01, 0xBE, 0xFB, 0xA5,
679 0x60, 0x71, 0x7B, 0x3B, 0x5D, 0xDD, 0x40, 0x38,
680 0x65, 0x67, 0xF9, 0xF4, 0x16, 0xFD, 0x3E, 0x9D,
681 0xFC, 0x71, 0x16, 0x3B, 0xDF, 0xF2, 0xFA, 0x95
684 u_char response[60] = {};
686 // Set the inner MPPE keys (e.g. from CHAPv2)
687 mppe_set_keys(inner_mppe_keys, inner_mppe_keys + 16, 16);
689 // Generate and compare the response
690 generate_cmk(ipmk, tmpkey, nonce, response, 1);
691 if (memcmp(expected, response, sizeof(response)) != 0) {
692 dbglog("Failed CMK key generation\n");
693 dbglog("%.*B", sizeof(response), response);
694 dbglog("%.*B", sizeof(expected), expected);
701 int test_mppe(u_char *ipmk) {
702 u_char outer_mppe_send_key[MPPE_MAX_KEY_SIZE] = {
703 0x6A, 0x02, 0xD7, 0x82, 0x20, 0x1B, 0xC7, 0x13,
704 0x8B, 0xF8, 0xEF, 0xF7, 0x33, 0xB4, 0x96, 0x97,
705 0x0D, 0x7C, 0xAB, 0x30, 0x0A, 0xC9, 0x57, 0x72,
706 0x78, 0xE1, 0xDD, 0xD5, 0xAE, 0xF7, 0x66, 0x97
709 u_char outer_mppe_recv_key[MPPE_MAX_KEY_SIZE] = {
710 0x17, 0x52, 0xD4, 0xE5, 0x84, 0xA1, 0xC8, 0x95,
711 0x03, 0x9B, 0x4D, 0x05, 0xE3, 0xBC, 0x9A, 0x84,
712 0x84, 0xDD, 0xC2, 0xAA, 0x6E, 0x2C, 0xE1, 0x62,
713 0x76, 0x5C, 0x40, 0x68, 0xBF, 0xF6, 0x5A, 0x45
716 u_char result[MPPE_MAX_KEY_SIZE];
721 generate_mppe_keys(ipmk, 1);
723 len = mppe_get_recv_key(result, sizeof(result));
724 if (len != sizeof(result)) {
725 dbglog("Invalid length of resulting MPPE recv key");
729 if (memcmp(result, outer_mppe_recv_key, len) != 0) {
730 dbglog("Invalid result for outer mppe recv key");
734 len = mppe_get_send_key(result, sizeof(result));
735 if (len != sizeof(result)) {
736 dbglog("Invalid length of resulting MPPE send key");
740 if (memcmp(result, outer_mppe_send_key, len) != 0) {
741 dbglog("Invalid result for outer mppe send key");
748 int main(int argc, char *argv[])
750 u_char ipmk[PEAP_TLV_IPMK_LEN] = {
751 0x3A, 0x91, 0x1C, 0x25, 0x54, 0x73, 0xE8, 0x3E,
752 0x9A, 0x0C, 0xC3, 0x33, 0xAE, 0x1F, 0x8A, 0x35,
753 0xCD, 0xC7, 0x41, 0x63, 0xE7, 0xF6, 0x0F, 0x6C,
754 0x65, 0xEF, 0x71, 0xC2, 0x64, 0x42, 0xAA, 0xAC,
755 0xA2, 0xB6, 0xF1, 0xEB, 0x4F, 0x25, 0xEC, 0xA3,
759 ret = test_cmk(ipmk);
764 ret = test_mppe(ipmk);