1189251Ssam/*
2189251Ssam * WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759
3252726Srpaulo * Copyright (c) 2004-2012, 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"
12189251Ssam#include "sha1.h"
13189251Ssam#include "ms_funcs.h"
14189251Ssam#include "crypto.h"
15189251Ssam
16252726Srpaulo/**
17252726Srpaulo * utf8_to_ucs2 - Convert UTF-8 string to UCS-2 encoding
18252726Srpaulo * @utf8_string: UTF-8 string (IN)
19252726Srpaulo * @utf8_string_len: Length of utf8_string (IN)
20252726Srpaulo * @ucs2_buffer: UCS-2 buffer (OUT)
21252726Srpaulo * @ucs2_buffer_size: Length of UCS-2 buffer (IN)
22252726Srpaulo * @ucs2_string_size: Number of 2-byte words in the resulting UCS-2 string
23252726Srpaulo * Returns: 0 on success, -1 on failure
24252726Srpaulo */
25252726Srpaulostatic int utf8_to_ucs2(const u8 *utf8_string, size_t utf8_string_len,
26252726Srpaulo                        u8 *ucs2_buffer, size_t ucs2_buffer_size,
27252726Srpaulo                        size_t *ucs2_string_size)
28252726Srpaulo{
29252726Srpaulo	size_t i, j;
30189251Ssam
31252726Srpaulo	for (i = 0, j = 0; i < utf8_string_len; i++) {
32252726Srpaulo		u8 c = utf8_string[i];
33252726Srpaulo		if (j >= ucs2_buffer_size) {
34252726Srpaulo			/* input too long */
35252726Srpaulo			return -1;
36252726Srpaulo		}
37252726Srpaulo		if (c <= 0x7F) {
38252726Srpaulo			WPA_PUT_LE16(ucs2_buffer + j, c);
39252726Srpaulo			j += 2;
40252726Srpaulo		} else if (i == utf8_string_len - 1 ||
41252726Srpaulo			   j >= ucs2_buffer_size - 1) {
42252726Srpaulo			/* incomplete surrogate */
43252726Srpaulo			return -1;
44252726Srpaulo		} else {
45252726Srpaulo			u8 c2 = utf8_string[++i];
46252726Srpaulo			if ((c & 0xE0) == 0xC0) {
47252726Srpaulo				/* two-byte encoding */
48252726Srpaulo				WPA_PUT_LE16(ucs2_buffer + j,
49252726Srpaulo					     ((c & 0x1F) << 6) | (c2 & 0x3F));
50252726Srpaulo				j += 2;
51252726Srpaulo			} else if (i == utf8_string_len ||
52252726Srpaulo				   j >= ucs2_buffer_size - 1) {
53252726Srpaulo				/* incomplete surrogate */
54252726Srpaulo				return -1;
55252726Srpaulo			} else {
56252726Srpaulo				/* three-byte encoding */
57252726Srpaulo				u8 c3 = utf8_string[++i];
58252726Srpaulo				WPA_PUT_LE16(ucs2_buffer + j,
59252726Srpaulo					     ((c & 0xF) << 12) |
60252726Srpaulo					     ((c2 & 0x3F) << 6) | (c3 & 0x3F));
61252726Srpaulo			}
62252726Srpaulo		}
63252726Srpaulo	}
64252726Srpaulo
65252726Srpaulo	if (ucs2_string_size)
66252726Srpaulo		*ucs2_string_size = j / 2;
67252726Srpaulo	return 0;
68252726Srpaulo}
69252726Srpaulo
70252726Srpaulo
71189251Ssam/**
72189251Ssam * challenge_hash - ChallengeHash() - RFC 2759, Sect. 8.2
73189251Ssam * @peer_challenge: 16-octet PeerChallenge (IN)
74189251Ssam * @auth_challenge: 16-octet AuthenticatorChallenge (IN)
75189251Ssam * @username: 0-to-256-char UserName (IN)
76189251Ssam * @username_len: Length of username
77189251Ssam * @challenge: 8-octet Challenge (OUT)
78214734Srpaulo * Returns: 0 on success, -1 on failure
79189251Ssam */
80214734Srpaulostatic int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge,
81214734Srpaulo			  const u8 *username, size_t username_len,
82214734Srpaulo			  u8 *challenge)
83189251Ssam{
84189251Ssam	u8 hash[SHA1_MAC_LEN];
85189251Ssam	const unsigned char *addr[3];
86189251Ssam	size_t len[3];
87189251Ssam
88189251Ssam	addr[0] = peer_challenge;
89189251Ssam	len[0] = 16;
90189251Ssam	addr[1] = auth_challenge;
91189251Ssam	len[1] = 16;
92189251Ssam	addr[2] = username;
93189251Ssam	len[2] = username_len;
94189251Ssam
95214734Srpaulo	if (sha1_vector(3, addr, len, hash))
96214734Srpaulo		return -1;
97189251Ssam	os_memcpy(challenge, hash, 8);
98214734Srpaulo	return 0;
99189251Ssam}
100189251Ssam
101189251Ssam
102189251Ssam/**
103189251Ssam * nt_password_hash - NtPasswordHash() - RFC 2759, Sect. 8.3
104252726Srpaulo * @password: 0-to-256-unicode-char Password (IN; UTF-8)
105189251Ssam * @password_len: Length of password
106189251Ssam * @password_hash: 16-octet PasswordHash (OUT)
107214734Srpaulo * Returns: 0 on success, -1 on failure
108189251Ssam */
109214734Srpauloint nt_password_hash(const u8 *password, size_t password_len,
110189251Ssam		      u8 *password_hash)
111189251Ssam{
112189251Ssam	u8 buf[512], *pos;
113252726Srpaulo	size_t len, max_len;
114189251Ssam
115252726Srpaulo	max_len = sizeof(buf);
116252726Srpaulo	if (utf8_to_ucs2(password, password_len, buf, max_len, &len) < 0)
117252726Srpaulo		return -1;
118189251Ssam
119252726Srpaulo	len *= 2;
120189251Ssam	pos = buf;
121214734Srpaulo	return md4_vector(1, (const u8 **) &pos, &len, password_hash);
122189251Ssam}
123189251Ssam
124189251Ssam
125189251Ssam/**
126189251Ssam * hash_nt_password_hash - HashNtPasswordHash() - RFC 2759, Sect. 8.4
127189251Ssam * @password_hash: 16-octet PasswordHash (IN)
128189251Ssam * @password_hash_hash: 16-octet PasswordHashHash (OUT)
129214734Srpaulo * Returns: 0 on success, -1 on failure
130189251Ssam */
131214734Srpauloint hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash)
132189251Ssam{
133189251Ssam	size_t len = 16;
134214734Srpaulo	return md4_vector(1, &password_hash, &len, password_hash_hash);
135189251Ssam}
136189251Ssam
137189251Ssam
138189251Ssam/**
139189251Ssam * challenge_response - ChallengeResponse() - RFC 2759, Sect. 8.5
140189251Ssam * @challenge: 8-octet Challenge (IN)
141189251Ssam * @password_hash: 16-octet PasswordHash (IN)
142189251Ssam * @response: 24-octet Response (OUT)
143189251Ssam */
144189251Ssamvoid challenge_response(const u8 *challenge, const u8 *password_hash,
145189251Ssam			u8 *response)
146189251Ssam{
147189251Ssam	u8 zpwd[7];
148189251Ssam	des_encrypt(challenge, password_hash, response);
149189251Ssam	des_encrypt(challenge, password_hash + 7, response + 8);
150189251Ssam	zpwd[0] = password_hash[14];
151189251Ssam	zpwd[1] = password_hash[15];
152189251Ssam	os_memset(zpwd + 2, 0, 5);
153189251Ssam	des_encrypt(challenge, zpwd, response + 16);
154189251Ssam}
155189251Ssam
156189251Ssam
157189251Ssam/**
158189251Ssam * generate_nt_response - GenerateNTResponse() - RFC 2759, Sect. 8.1
159189251Ssam * @auth_challenge: 16-octet AuthenticatorChallenge (IN)
160189251Ssam * @peer_challenge: 16-octet PeerChallenge (IN)
161189251Ssam * @username: 0-to-256-char UserName (IN)
162189251Ssam * @username_len: Length of username
163252726Srpaulo * @password: 0-to-256-unicode-char Password (IN; UTF-8)
164189251Ssam * @password_len: Length of password
165189251Ssam * @response: 24-octet Response (OUT)
166214734Srpaulo * Returns: 0 on success, -1 on failure
167189251Ssam */
168214734Srpauloint generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge,
169214734Srpaulo			 const u8 *username, size_t username_len,
170214734Srpaulo			 const u8 *password, size_t password_len,
171214734Srpaulo			 u8 *response)
172189251Ssam{
173189251Ssam	u8 challenge[8];
174189251Ssam	u8 password_hash[16];
175189251Ssam
176252726Srpaulo	if (challenge_hash(peer_challenge, auth_challenge, username,
177252726Srpaulo			   username_len, challenge))
178252726Srpaulo		return -1;
179214734Srpaulo	if (nt_password_hash(password, password_len, password_hash))
180214734Srpaulo		return -1;
181189251Ssam	challenge_response(challenge, password_hash, response);
182214734Srpaulo	return 0;
183189251Ssam}
184189251Ssam
185189251Ssam
186189251Ssam/**
187189251Ssam * generate_nt_response_pwhash - GenerateNTResponse() - RFC 2759, Sect. 8.1
188189251Ssam * @auth_challenge: 16-octet AuthenticatorChallenge (IN)
189189251Ssam * @peer_challenge: 16-octet PeerChallenge (IN)
190189251Ssam * @username: 0-to-256-char UserName (IN)
191189251Ssam * @username_len: Length of username
192189251Ssam * @password_hash: 16-octet PasswordHash (IN)
193189251Ssam * @response: 24-octet Response (OUT)
194214734Srpaulo * Returns: 0 on success, -1 on failure
195189251Ssam */
196214734Srpauloint generate_nt_response_pwhash(const u8 *auth_challenge,
197214734Srpaulo				const u8 *peer_challenge,
198214734Srpaulo				const u8 *username, size_t username_len,
199214734Srpaulo				const u8 *password_hash,
200214734Srpaulo				u8 *response)
201189251Ssam{
202189251Ssam	u8 challenge[8];
203189251Ssam
204214734Srpaulo	if (challenge_hash(peer_challenge, auth_challenge,
205214734Srpaulo			   username, username_len,
206214734Srpaulo			   challenge))
207214734Srpaulo		return -1;
208189251Ssam	challenge_response(challenge, password_hash, response);
209214734Srpaulo	return 0;
210189251Ssam}
211189251Ssam
212189251Ssam
213189251Ssam/**
214189251Ssam * generate_authenticator_response_pwhash - GenerateAuthenticatorResponse() - RFC 2759, Sect. 8.7
215189251Ssam * @password_hash: 16-octet PasswordHash (IN)
216189251Ssam * @nt_response: 24-octet NT-Response (IN)
217189251Ssam * @peer_challenge: 16-octet PeerChallenge (IN)
218189251Ssam * @auth_challenge: 16-octet AuthenticatorChallenge (IN)
219189251Ssam * @username: 0-to-256-char UserName (IN)
220189251Ssam * @username_len: Length of username
221189251Ssam * @response: 20-octet AuthenticatorResponse (OUT) (note: this value is usually
222189251Ssam * encoded as a 42-octet ASCII string (S=hexdump_of_response)
223214734Srpaulo * Returns: 0 on success, -1 on failure
224189251Ssam */
225214734Srpauloint generate_authenticator_response_pwhash(
226189251Ssam	const u8 *password_hash,
227189251Ssam	const u8 *peer_challenge, const u8 *auth_challenge,
228189251Ssam	const u8 *username, size_t username_len,
229189251Ssam	const u8 *nt_response, u8 *response)
230189251Ssam{
231189251Ssam	static const u8 magic1[39] = {
232189251Ssam		0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
233189251Ssam		0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
234189251Ssam		0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
235189251Ssam		0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74
236189251Ssam	};
237189251Ssam	static const u8 magic2[41] = {
238189251Ssam		0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
239189251Ssam		0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
240189251Ssam		0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
241189251Ssam		0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
242189251Ssam		0x6E
243189251Ssam	};
244189251Ssam
245189251Ssam	u8 password_hash_hash[16], challenge[8];
246189251Ssam	const unsigned char *addr1[3];
247189251Ssam	const size_t len1[3] = { 16, 24, sizeof(magic1) };
248189251Ssam	const unsigned char *addr2[3];
249189251Ssam	const size_t len2[3] = { SHA1_MAC_LEN, 8, sizeof(magic2) };
250189251Ssam
251189251Ssam	addr1[0] = password_hash_hash;
252189251Ssam	addr1[1] = nt_response;
253189251Ssam	addr1[2] = magic1;
254189251Ssam
255189251Ssam	addr2[0] = response;
256189251Ssam	addr2[1] = challenge;
257189251Ssam	addr2[2] = magic2;
258189251Ssam
259214734Srpaulo	if (hash_nt_password_hash(password_hash, password_hash_hash))
260214734Srpaulo		return -1;
261214734Srpaulo	if (sha1_vector(3, addr1, len1, response))
262214734Srpaulo		return -1;
263189251Ssam
264252726Srpaulo	if (challenge_hash(peer_challenge, auth_challenge, username,
265252726Srpaulo			   username_len, challenge))
266252726Srpaulo		return -1;
267214734Srpaulo	return sha1_vector(3, addr2, len2, response);
268189251Ssam}
269189251Ssam
270189251Ssam
271189251Ssam/**
272189251Ssam * generate_authenticator_response - GenerateAuthenticatorResponse() - RFC 2759, Sect. 8.7
273252726Srpaulo * @password: 0-to-256-unicode-char Password (IN; UTF-8)
274189251Ssam * @password_len: Length of password
275189251Ssam * @nt_response: 24-octet NT-Response (IN)
276189251Ssam * @peer_challenge: 16-octet PeerChallenge (IN)
277189251Ssam * @auth_challenge: 16-octet AuthenticatorChallenge (IN)
278189251Ssam * @username: 0-to-256-char UserName (IN)
279189251Ssam * @username_len: Length of username
280189251Ssam * @response: 20-octet AuthenticatorResponse (OUT) (note: this value is usually
281189251Ssam * encoded as a 42-octet ASCII string (S=hexdump_of_response)
282214734Srpaulo * Returns: 0 on success, -1 on failure
283189251Ssam */
284214734Srpauloint generate_authenticator_response(const u8 *password, size_t password_len,
285214734Srpaulo				    const u8 *peer_challenge,
286214734Srpaulo				    const u8 *auth_challenge,
287214734Srpaulo				    const u8 *username, size_t username_len,
288214734Srpaulo				    const u8 *nt_response, u8 *response)
289189251Ssam{
290189251Ssam	u8 password_hash[16];
291214734Srpaulo	if (nt_password_hash(password, password_len, password_hash))
292214734Srpaulo		return -1;
293214734Srpaulo	return generate_authenticator_response_pwhash(
294214734Srpaulo		password_hash, peer_challenge, auth_challenge,
295214734Srpaulo		username, username_len, nt_response, response);
296189251Ssam}
297189251Ssam
298189251Ssam
299189251Ssam/**
300189251Ssam * nt_challenge_response - NtChallengeResponse() - RFC 2433, Sect. A.5
301189251Ssam * @challenge: 8-octet Challenge (IN)
302252726Srpaulo * @password: 0-to-256-unicode-char Password (IN; UTF-8)
303189251Ssam * @password_len: Length of password
304189251Ssam * @response: 24-octet Response (OUT)
305214734Srpaulo * Returns: 0 on success, -1 on failure
306189251Ssam */
307214734Srpauloint nt_challenge_response(const u8 *challenge, const u8 *password,
308214734Srpaulo			  size_t password_len, u8 *response)
309189251Ssam{
310189251Ssam	u8 password_hash[16];
311214734Srpaulo	if (nt_password_hash(password, password_len, password_hash))
312214734Srpaulo		return -1;
313189251Ssam	challenge_response(challenge, password_hash, response);
314214734Srpaulo	return 0;
315189251Ssam}
316189251Ssam
317189251Ssam
318189251Ssam/**
319189251Ssam * get_master_key - GetMasterKey() - RFC 3079, Sect. 3.4
320189251Ssam * @password_hash_hash: 16-octet PasswordHashHash (IN)
321189251Ssam * @nt_response: 24-octet NTResponse (IN)
322189251Ssam * @master_key: 16-octet MasterKey (OUT)
323214734Srpaulo * Returns: 0 on success, -1 on failure
324189251Ssam */
325214734Srpauloint get_master_key(const u8 *password_hash_hash, const u8 *nt_response,
326214734Srpaulo		   u8 *master_key)
327189251Ssam{
328189251Ssam	static const u8 magic1[27] = {
329189251Ssam		0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
330189251Ssam		0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
331189251Ssam		0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79
332189251Ssam	};
333189251Ssam	const unsigned char *addr[3];
334189251Ssam	const size_t len[3] = { 16, 24, sizeof(magic1) };
335189251Ssam	u8 hash[SHA1_MAC_LEN];
336189251Ssam
337189251Ssam	addr[0] = password_hash_hash;
338189251Ssam	addr[1] = nt_response;
339189251Ssam	addr[2] = magic1;
340189251Ssam
341214734Srpaulo	if (sha1_vector(3, addr, len, hash))
342214734Srpaulo		return -1;
343189251Ssam	os_memcpy(master_key, hash, 16);
344214734Srpaulo	return 0;
345189251Ssam}
346189251Ssam
347189251Ssam
348189251Ssam/**
349189251Ssam * get_asymetric_start_key - GetAsymetricStartKey() - RFC 3079, Sect. 3.4
350189251Ssam * @master_key: 16-octet MasterKey (IN)
351189251Ssam * @session_key: 8-to-16 octet SessionKey (OUT)
352189251Ssam * @session_key_len: SessionKeyLength (Length of session_key) (IN)
353189251Ssam * @is_send: IsSend (IN, BOOLEAN)
354189251Ssam * @is_server: IsServer (IN, BOOLEAN)
355214734Srpaulo * Returns: 0 on success, -1 on failure
356189251Ssam */
357214734Srpauloint get_asymetric_start_key(const u8 *master_key, u8 *session_key,
358214734Srpaulo			    size_t session_key_len, int is_send,
359214734Srpaulo			    int is_server)
360189251Ssam{
361189251Ssam	static const u8 magic2[84] = {
362189251Ssam		0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
363189251Ssam		0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
364189251Ssam		0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
365189251Ssam		0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
366189251Ssam		0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
367189251Ssam		0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
368189251Ssam		0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
369189251Ssam		0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
370189251Ssam		0x6b, 0x65, 0x79, 0x2e
371189251Ssam	};
372189251Ssam	static const u8 magic3[84] = {
373189251Ssam		0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
374189251Ssam		0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
375189251Ssam		0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
376189251Ssam		0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
377189251Ssam		0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
378189251Ssam		0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
379189251Ssam		0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
380189251Ssam		0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
381189251Ssam		0x6b, 0x65, 0x79, 0x2e
382189251Ssam	};
383189251Ssam	static const u8 shs_pad1[40] = {
384189251Ssam		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
385189251Ssam		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
386189251Ssam		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
387189251Ssam		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
388189251Ssam	};
389189251Ssam
390189251Ssam	static const u8 shs_pad2[40] = {
391189251Ssam		0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
392189251Ssam		0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
393189251Ssam		0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
394189251Ssam		0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2
395189251Ssam	};
396189251Ssam	u8 digest[SHA1_MAC_LEN];
397189251Ssam	const unsigned char *addr[4];
398189251Ssam	const size_t len[4] = { 16, 40, 84, 40 };
399189251Ssam
400189251Ssam	addr[0] = master_key;
401189251Ssam	addr[1] = shs_pad1;
402189251Ssam	if (is_send) {
403189251Ssam		addr[2] = is_server ? magic3 : magic2;
404189251Ssam	} else {
405189251Ssam		addr[2] = is_server ? magic2 : magic3;
406189251Ssam	}
407189251Ssam	addr[3] = shs_pad2;
408189251Ssam
409214734Srpaulo	if (sha1_vector(4, addr, len, digest))
410214734Srpaulo		return -1;
411189251Ssam
412189251Ssam	if (session_key_len > SHA1_MAC_LEN)
413189251Ssam		session_key_len = SHA1_MAC_LEN;
414189251Ssam	os_memcpy(session_key, digest, session_key_len);
415214734Srpaulo	return 0;
416189251Ssam}
417189251Ssam
418189251Ssam
419189251Ssam#define PWBLOCK_LEN 516
420189251Ssam
421189251Ssam/**
422189251Ssam * encrypt_pw_block_with_password_hash - EncryptPwBlockWithPasswordHash() - RFC 2759, Sect. 8.10
423252726Srpaulo * @password: 0-to-256-unicode-char Password (IN; UTF-8)
424189251Ssam * @password_len: Length of password
425189251Ssam * @password_hash: 16-octet PasswordHash (IN)
426189251Ssam * @pw_block: 516-byte PwBlock (OUT)
427189251Ssam * Returns: 0 on success, -1 on failure
428189251Ssam */
429189251Ssamint encrypt_pw_block_with_password_hash(
430189251Ssam	const u8 *password, size_t password_len,
431189251Ssam	const u8 *password_hash, u8 *pw_block)
432189251Ssam{
433252726Srpaulo	size_t ucs2_len, offset;
434189251Ssam	u8 *pos;
435189251Ssam
436252726Srpaulo	os_memset(pw_block, 0, PWBLOCK_LEN);
437252726Srpaulo
438252726Srpaulo	if (utf8_to_ucs2(password, password_len, pw_block, 512, &ucs2_len) < 0)
439189251Ssam		return -1;
440189251Ssam
441252726Srpaulo	if (ucs2_len > 256)
442189251Ssam		return -1;
443252726Srpaulo
444252726Srpaulo	offset = (256 - ucs2_len) * 2;
445252726Srpaulo	if (offset != 0) {
446252726Srpaulo		os_memmove(pw_block + offset, pw_block, ucs2_len * 2);
447252726Srpaulo		if (os_get_random(pw_block, offset) < 0)
448252726Srpaulo			return -1;
449252726Srpaulo	}
450189251Ssam	/*
451189251Ssam	 * PasswordLength is 4 octets, but since the maximum password length is
452189251Ssam	 * 256, only first two (in little endian byte order) can be non-zero.
453189251Ssam	 */
454189251Ssam	pos = &pw_block[2 * 256];
455189251Ssam	WPA_PUT_LE16(pos, password_len * 2);
456209158Srpaulo	rc4_skip(password_hash, 16, 0, pw_block, PWBLOCK_LEN);
457189251Ssam	return 0;
458189251Ssam}
459189251Ssam
460189251Ssam
461189251Ssam/**
462189251Ssam * new_password_encrypted_with_old_nt_password_hash - NewPasswordEncryptedWithOldNtPasswordHash() - RFC 2759, Sect. 8.9
463252726Srpaulo * @new_password: 0-to-256-unicode-char NewPassword (IN; UTF-8)
464189251Ssam * @new_password_len: Length of new_password
465252726Srpaulo * @old_password: 0-to-256-unicode-char OldPassword (IN; UTF-8)
466189251Ssam * @old_password_len: Length of old_password
467189251Ssam * @encrypted_pw_block: 516-octet EncryptedPwBlock (OUT)
468189251Ssam * Returns: 0 on success, -1 on failure
469189251Ssam */
470189251Ssamint new_password_encrypted_with_old_nt_password_hash(
471189251Ssam	const u8 *new_password, size_t new_password_len,
472189251Ssam	const u8 *old_password, size_t old_password_len,
473189251Ssam	u8 *encrypted_pw_block)
474189251Ssam{
475189251Ssam	u8 password_hash[16];
476189251Ssam
477214734Srpaulo	if (nt_password_hash(old_password, old_password_len, password_hash))
478214734Srpaulo		return -1;
479189251Ssam	if (encrypt_pw_block_with_password_hash(new_password, new_password_len,
480189251Ssam						password_hash,
481189251Ssam						encrypted_pw_block))
482189251Ssam		return -1;
483189251Ssam	return 0;
484189251Ssam}
485189251Ssam
486189251Ssam
487189251Ssam/**
488189251Ssam * nt_password_hash_encrypted_with_block - NtPasswordHashEncryptedWithBlock() - RFC 2759, Sect 8.13
489189251Ssam * @password_hash: 16-octer PasswordHash (IN)
490189251Ssam * @block: 16-octet Block (IN)
491189251Ssam * @cypher: 16-octer Cypher (OUT)
492189251Ssam */
493189251Ssamvoid nt_password_hash_encrypted_with_block(const u8 *password_hash,
494189251Ssam					   const u8 *block, u8 *cypher)
495189251Ssam{
496189251Ssam	des_encrypt(password_hash, block, cypher);
497189251Ssam	des_encrypt(password_hash + 8, block + 7, cypher + 8);
498189251Ssam}
499189251Ssam
500189251Ssam
501189251Ssam/**
502189251Ssam * old_nt_password_hash_encrypted_with_new_nt_password_hash - OldNtPasswordHashEncryptedWithNewNtPasswordHash() - RFC 2759, Sect. 8.12
503252726Srpaulo * @new_password: 0-to-256-unicode-char NewPassword (IN; UTF-8)
504189251Ssam * @new_password_len: Length of new_password
505252726Srpaulo * @old_password: 0-to-256-unicode-char OldPassword (IN; UTF-8)
506189251Ssam * @old_password_len: Length of old_password
507189251Ssam * @encrypted_password_hash: 16-octet EncryptedPasswordHash (OUT)
508214734Srpaulo * Returns: 0 on success, -1 on failure
509189251Ssam */
510214734Srpauloint old_nt_password_hash_encrypted_with_new_nt_password_hash(
511189251Ssam	const u8 *new_password, size_t new_password_len,
512189251Ssam	const u8 *old_password, size_t old_password_len,
513189251Ssam	u8 *encrypted_password_hash)
514189251Ssam{
515189251Ssam	u8 old_password_hash[16], new_password_hash[16];
516189251Ssam
517214734Srpaulo	if (nt_password_hash(old_password, old_password_len,
518214734Srpaulo			     old_password_hash) ||
519214734Srpaulo	    nt_password_hash(new_password, new_password_len,
520214734Srpaulo			     new_password_hash))
521214734Srpaulo		return -1;
522189251Ssam	nt_password_hash_encrypted_with_block(old_password_hash,
523189251Ssam					      new_password_hash,
524189251Ssam					      encrypted_password_hash);
525214734Srpaulo	return 0;
526189251Ssam}
527