1189251Ssam/*
2189251Ssam * MSCHAPV2 (RFC 2759)
3189251Ssam * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
4189251Ssam *
5252726Srpaulo * This software may be distributed under the terms of the BSD license.
6252726Srpaulo * See README for more details.
7189251Ssam */
8189251Ssam
9189251Ssam#include "includes.h"
10189251Ssam
11189251Ssam#include "common.h"
12214734Srpaulo#include "crypto/ms_funcs.h"
13189251Ssam#include "mschapv2.h"
14189251Ssam
15189251Ssamconst u8 * mschapv2_remove_domain(const u8 *username, size_t *len)
16189251Ssam{
17189251Ssam	size_t i;
18189251Ssam
19189251Ssam	/*
20189251Ssam	 * MSCHAPv2 does not include optional domain name in the
21189251Ssam	 * challenge-response calculation, so remove domain prefix
22189251Ssam	 * (if present).
23189251Ssam	 */
24189251Ssam
25189251Ssam	for (i = 0; i < *len; i++) {
26189251Ssam		if (username[i] == '\\') {
27189251Ssam			*len -= i + 1;
28189251Ssam			return username + i + 1;
29189251Ssam		}
30189251Ssam	}
31189251Ssam
32189251Ssam	return username;
33189251Ssam}
34189251Ssam
35189251Ssam
36214734Srpauloint mschapv2_derive_response(const u8 *identity, size_t identity_len,
37214734Srpaulo			     const u8 *password, size_t password_len,
38214734Srpaulo			     int pwhash,
39214734Srpaulo			     const u8 *auth_challenge,
40214734Srpaulo			     const u8 *peer_challenge,
41214734Srpaulo			     u8 *nt_response, u8 *auth_response,
42214734Srpaulo			     u8 *master_key)
43189251Ssam{
44189251Ssam	const u8 *username;
45189251Ssam	size_t username_len;
46189251Ssam	u8 password_hash[16], password_hash_hash[16];
47189251Ssam
48189251Ssam	wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: Identity",
49189251Ssam			  identity, identity_len);
50189251Ssam	username_len = identity_len;
51189251Ssam	username = mschapv2_remove_domain(identity, &username_len);
52189251Ssam	wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: Username",
53189251Ssam			  username, username_len);
54189251Ssam
55189251Ssam	wpa_hexdump(MSG_DEBUG, "MSCHAPV2: auth_challenge",
56189251Ssam		    auth_challenge, MSCHAPV2_CHAL_LEN);
57189251Ssam	wpa_hexdump(MSG_DEBUG, "MSCHAPV2: peer_challenge",
58189251Ssam		    peer_challenge, MSCHAPV2_CHAL_LEN);
59189251Ssam	wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: username",
60189251Ssam			  username, username_len);
61189251Ssam	/* Authenticator response is not really needed yet, but calculate it
62189251Ssam	 * here so that challenges need not be saved. */
63189251Ssam	if (pwhash) {
64189251Ssam		wpa_hexdump_key(MSG_DEBUG, "MSCHAPV2: password hash",
65189251Ssam				password, password_len);
66252726Srpaulo		if (generate_nt_response_pwhash(auth_challenge, peer_challenge,
67252726Srpaulo						username, username_len,
68252726Srpaulo						password, nt_response) ||
69252726Srpaulo		    generate_authenticator_response_pwhash(
70252726Srpaulo			    password, peer_challenge, auth_challenge,
71252726Srpaulo			    username, username_len, nt_response,
72252726Srpaulo			    auth_response))
73252726Srpaulo			return -1;
74189251Ssam	} else {
75189251Ssam		wpa_hexdump_ascii_key(MSG_DEBUG, "MSCHAPV2: password",
76189251Ssam				      password, password_len);
77252726Srpaulo		if (generate_nt_response(auth_challenge, peer_challenge,
78252726Srpaulo					 username, username_len,
79252726Srpaulo					 password, password_len,
80252726Srpaulo					 nt_response) ||
81252726Srpaulo		    generate_authenticator_response(password, password_len,
82252726Srpaulo						    peer_challenge,
83252726Srpaulo						    auth_challenge,
84252726Srpaulo						    username, username_len,
85252726Srpaulo						    nt_response,
86252726Srpaulo						    auth_response))
87252726Srpaulo			return -1;
88189251Ssam	}
89189251Ssam	wpa_hexdump(MSG_DEBUG, "MSCHAPV2: NT Response",
90189251Ssam		    nt_response, MSCHAPV2_NT_RESPONSE_LEN);
91189251Ssam	wpa_hexdump(MSG_DEBUG, "MSCHAPV2: Auth Response",
92189251Ssam		    auth_response, MSCHAPV2_AUTH_RESPONSE_LEN);
93189251Ssam
94189251Ssam	/* Generate master_key here since we have the needed data available. */
95189251Ssam	if (pwhash) {
96214734Srpaulo		if (hash_nt_password_hash(password, password_hash_hash))
97214734Srpaulo			return -1;
98189251Ssam	} else {
99214734Srpaulo		if (nt_password_hash(password, password_len, password_hash) ||
100214734Srpaulo		    hash_nt_password_hash(password_hash, password_hash_hash))
101214734Srpaulo			return -1;
102189251Ssam	}
103252726Srpaulo	if (get_master_key(password_hash_hash, nt_response, master_key))
104252726Srpaulo		return -1;
105189251Ssam	wpa_hexdump_key(MSG_DEBUG, "MSCHAPV2: Master Key",
106189251Ssam			master_key, MSCHAPV2_MASTER_KEY_LEN);
107214734Srpaulo
108214734Srpaulo	return 0;
109189251Ssam}
110189251Ssam
111189251Ssam
112189251Ssamint mschapv2_verify_auth_response(const u8 *auth_response,
113189251Ssam				  const u8 *buf, size_t buf_len)
114189251Ssam{
115189251Ssam	u8 recv_response[MSCHAPV2_AUTH_RESPONSE_LEN];
116189251Ssam	if (buf_len < 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN ||
117189251Ssam	    buf[0] != 'S' || buf[1] != '=' ||
118189251Ssam	    hexstr2bin((char *) (buf + 2), recv_response,
119189251Ssam		       MSCHAPV2_AUTH_RESPONSE_LEN) ||
120189251Ssam	    os_memcmp(auth_response, recv_response,
121189251Ssam		      MSCHAPV2_AUTH_RESPONSE_LEN) != 0)
122189251Ssam		return -1;
123189251Ssam	return 0;
124189251Ssam}
125