16d49e1aeSJan Lentfer /*
26d49e1aeSJan Lentfer * WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759
33ff40c12SJohn Marino * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
46d49e1aeSJan Lentfer *
53ff40c12SJohn Marino * This software may be distributed under the terms of the BSD license.
63ff40c12SJohn Marino * See README for more details.
76d49e1aeSJan Lentfer */
86d49e1aeSJan Lentfer
96d49e1aeSJan Lentfer #include "includes.h"
106d49e1aeSJan Lentfer
116d49e1aeSJan Lentfer #include "common.h"
126d49e1aeSJan Lentfer #include "sha1.h"
136d49e1aeSJan Lentfer #include "ms_funcs.h"
146d49e1aeSJan Lentfer #include "crypto.h"
153ff40c12SJohn Marino
163ff40c12SJohn Marino /**
173ff40c12SJohn Marino * utf8_to_ucs2 - Convert UTF-8 string to UCS-2 encoding
183ff40c12SJohn Marino * @utf8_string: UTF-8 string (IN)
193ff40c12SJohn Marino * @utf8_string_len: Length of utf8_string (IN)
203ff40c12SJohn Marino * @ucs2_buffer: UCS-2 buffer (OUT)
213ff40c12SJohn Marino * @ucs2_buffer_size: Length of UCS-2 buffer (IN)
223ff40c12SJohn Marino * @ucs2_string_size: Number of 2-byte words in the resulting UCS-2 string
233ff40c12SJohn Marino * Returns: 0 on success, -1 on failure
243ff40c12SJohn Marino */
utf8_to_ucs2(const u8 * utf8_string,size_t utf8_string_len,u8 * ucs2_buffer,size_t ucs2_buffer_size,size_t * ucs2_string_size)253ff40c12SJohn Marino static int utf8_to_ucs2(const u8 *utf8_string, size_t utf8_string_len,
263ff40c12SJohn Marino u8 *ucs2_buffer, size_t ucs2_buffer_size,
273ff40c12SJohn Marino size_t *ucs2_string_size)
283ff40c12SJohn Marino {
293ff40c12SJohn Marino size_t i, j;
303ff40c12SJohn Marino
313ff40c12SJohn Marino for (i = 0, j = 0; i < utf8_string_len; i++) {
323ff40c12SJohn Marino u8 c = utf8_string[i];
333ff40c12SJohn Marino if (j >= ucs2_buffer_size) {
343ff40c12SJohn Marino /* input too long */
353ff40c12SJohn Marino return -1;
363ff40c12SJohn Marino }
373ff40c12SJohn Marino if (c <= 0x7F) {
383ff40c12SJohn Marino WPA_PUT_LE16(ucs2_buffer + j, c);
393ff40c12SJohn Marino j += 2;
403ff40c12SJohn Marino } else if (i == utf8_string_len - 1 ||
413ff40c12SJohn Marino j >= ucs2_buffer_size - 1) {
423ff40c12SJohn Marino /* incomplete surrogate */
433ff40c12SJohn Marino return -1;
443ff40c12SJohn Marino } else {
453ff40c12SJohn Marino u8 c2 = utf8_string[++i];
463ff40c12SJohn Marino if ((c & 0xE0) == 0xC0) {
473ff40c12SJohn Marino /* two-byte encoding */
483ff40c12SJohn Marino WPA_PUT_LE16(ucs2_buffer + j,
493ff40c12SJohn Marino ((c & 0x1F) << 6) | (c2 & 0x3F));
503ff40c12SJohn Marino j += 2;
51*a1157835SDaniel Fojt } else if (i == utf8_string_len - 1 ||
523ff40c12SJohn Marino j >= ucs2_buffer_size - 1) {
533ff40c12SJohn Marino /* incomplete surrogate */
543ff40c12SJohn Marino return -1;
553ff40c12SJohn Marino } else {
563ff40c12SJohn Marino /* three-byte encoding */
573ff40c12SJohn Marino u8 c3 = utf8_string[++i];
583ff40c12SJohn Marino WPA_PUT_LE16(ucs2_buffer + j,
593ff40c12SJohn Marino ((c & 0xF) << 12) |
603ff40c12SJohn Marino ((c2 & 0x3F) << 6) | (c3 & 0x3F));
61*a1157835SDaniel Fojt j += 2;
623ff40c12SJohn Marino }
633ff40c12SJohn Marino }
643ff40c12SJohn Marino }
653ff40c12SJohn Marino
663ff40c12SJohn Marino if (ucs2_string_size)
673ff40c12SJohn Marino *ucs2_string_size = j / 2;
683ff40c12SJohn Marino return 0;
693ff40c12SJohn Marino }
706d49e1aeSJan Lentfer
716d49e1aeSJan Lentfer
726d49e1aeSJan Lentfer /**
736d49e1aeSJan Lentfer * challenge_hash - ChallengeHash() - RFC 2759, Sect. 8.2
746d49e1aeSJan Lentfer * @peer_challenge: 16-octet PeerChallenge (IN)
756d49e1aeSJan Lentfer * @auth_challenge: 16-octet AuthenticatorChallenge (IN)
766d49e1aeSJan Lentfer * @username: 0-to-256-char UserName (IN)
776d49e1aeSJan Lentfer * @username_len: Length of username
786d49e1aeSJan Lentfer * @challenge: 8-octet Challenge (OUT)
793ff40c12SJohn Marino * Returns: 0 on success, -1 on failure
806d49e1aeSJan Lentfer */
challenge_hash(const u8 * peer_challenge,const u8 * auth_challenge,const u8 * username,size_t username_len,u8 * challenge)81*a1157835SDaniel Fojt int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge,
82*a1157835SDaniel Fojt const u8 *username, size_t username_len, u8 *challenge)
836d49e1aeSJan Lentfer {
846d49e1aeSJan Lentfer u8 hash[SHA1_MAC_LEN];
856d49e1aeSJan Lentfer const unsigned char *addr[3];
866d49e1aeSJan Lentfer size_t len[3];
876d49e1aeSJan Lentfer
886d49e1aeSJan Lentfer addr[0] = peer_challenge;
896d49e1aeSJan Lentfer len[0] = 16;
906d49e1aeSJan Lentfer addr[1] = auth_challenge;
916d49e1aeSJan Lentfer len[1] = 16;
926d49e1aeSJan Lentfer addr[2] = username;
936d49e1aeSJan Lentfer len[2] = username_len;
946d49e1aeSJan Lentfer
953ff40c12SJohn Marino if (sha1_vector(3, addr, len, hash))
963ff40c12SJohn Marino return -1;
976d49e1aeSJan Lentfer os_memcpy(challenge, hash, 8);
983ff40c12SJohn Marino return 0;
996d49e1aeSJan Lentfer }
1006d49e1aeSJan Lentfer
1016d49e1aeSJan Lentfer
1026d49e1aeSJan Lentfer /**
1036d49e1aeSJan Lentfer * nt_password_hash - NtPasswordHash() - RFC 2759, Sect. 8.3
1043ff40c12SJohn Marino * @password: 0-to-256-unicode-char Password (IN; UTF-8)
1056d49e1aeSJan Lentfer * @password_len: Length of password
1066d49e1aeSJan Lentfer * @password_hash: 16-octet PasswordHash (OUT)
1073ff40c12SJohn Marino * Returns: 0 on success, -1 on failure
1086d49e1aeSJan Lentfer */
nt_password_hash(const u8 * password,size_t password_len,u8 * password_hash)1093ff40c12SJohn Marino int nt_password_hash(const u8 *password, size_t password_len,
1106d49e1aeSJan Lentfer u8 *password_hash)
1116d49e1aeSJan Lentfer {
1126d49e1aeSJan Lentfer u8 buf[512], *pos;
1133ff40c12SJohn Marino size_t len, max_len;
1146d49e1aeSJan Lentfer
1153ff40c12SJohn Marino max_len = sizeof(buf);
1163ff40c12SJohn Marino if (utf8_to_ucs2(password, password_len, buf, max_len, &len) < 0)
1173ff40c12SJohn Marino return -1;
1186d49e1aeSJan Lentfer
1193ff40c12SJohn Marino len *= 2;
1206d49e1aeSJan Lentfer pos = buf;
1213ff40c12SJohn Marino return md4_vector(1, (const u8 **) &pos, &len, password_hash);
1226d49e1aeSJan Lentfer }
1236d49e1aeSJan Lentfer
1246d49e1aeSJan Lentfer
1256d49e1aeSJan Lentfer /**
1266d49e1aeSJan Lentfer * hash_nt_password_hash - HashNtPasswordHash() - RFC 2759, Sect. 8.4
1276d49e1aeSJan Lentfer * @password_hash: 16-octet PasswordHash (IN)
1286d49e1aeSJan Lentfer * @password_hash_hash: 16-octet PasswordHashHash (OUT)
1293ff40c12SJohn Marino * Returns: 0 on success, -1 on failure
1306d49e1aeSJan Lentfer */
hash_nt_password_hash(const u8 * password_hash,u8 * password_hash_hash)1313ff40c12SJohn Marino int hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash)
1326d49e1aeSJan Lentfer {
1336d49e1aeSJan Lentfer size_t len = 16;
1343ff40c12SJohn Marino return md4_vector(1, &password_hash, &len, password_hash_hash);
1356d49e1aeSJan Lentfer }
1366d49e1aeSJan Lentfer
1376d49e1aeSJan Lentfer
1386d49e1aeSJan Lentfer /**
1396d49e1aeSJan Lentfer * challenge_response - ChallengeResponse() - RFC 2759, Sect. 8.5
1406d49e1aeSJan Lentfer * @challenge: 8-octet Challenge (IN)
1416d49e1aeSJan Lentfer * @password_hash: 16-octet PasswordHash (IN)
1426d49e1aeSJan Lentfer * @response: 24-octet Response (OUT)
143*a1157835SDaniel Fojt * Returns: 0 on success, -1 on failure
1446d49e1aeSJan Lentfer */
challenge_response(const u8 * challenge,const u8 * password_hash,u8 * response)145*a1157835SDaniel Fojt int challenge_response(const u8 *challenge, const u8 *password_hash,
1466d49e1aeSJan Lentfer u8 *response)
1476d49e1aeSJan Lentfer {
1486d49e1aeSJan Lentfer u8 zpwd[7];
149*a1157835SDaniel Fojt
150*a1157835SDaniel Fojt if (des_encrypt(challenge, password_hash, response) < 0 ||
151*a1157835SDaniel Fojt des_encrypt(challenge, password_hash + 7, response + 8) < 0)
152*a1157835SDaniel Fojt return -1;
1536d49e1aeSJan Lentfer zpwd[0] = password_hash[14];
1546d49e1aeSJan Lentfer zpwd[1] = password_hash[15];
1556d49e1aeSJan Lentfer os_memset(zpwd + 2, 0, 5);
156*a1157835SDaniel Fojt return des_encrypt(challenge, zpwd, response + 16);
1576d49e1aeSJan Lentfer }
1586d49e1aeSJan Lentfer
1596d49e1aeSJan Lentfer
1606d49e1aeSJan Lentfer /**
1616d49e1aeSJan Lentfer * generate_nt_response - GenerateNTResponse() - RFC 2759, Sect. 8.1
1626d49e1aeSJan Lentfer * @auth_challenge: 16-octet AuthenticatorChallenge (IN)
1636d49e1aeSJan Lentfer * @peer_challenge: 16-octet PeerChallenge (IN)
1646d49e1aeSJan Lentfer * @username: 0-to-256-char UserName (IN)
1656d49e1aeSJan Lentfer * @username_len: Length of username
1663ff40c12SJohn Marino * @password: 0-to-256-unicode-char Password (IN; UTF-8)
1676d49e1aeSJan Lentfer * @password_len: Length of password
1686d49e1aeSJan Lentfer * @response: 24-octet Response (OUT)
1693ff40c12SJohn Marino * Returns: 0 on success, -1 on failure
1706d49e1aeSJan Lentfer */
generate_nt_response(const u8 * auth_challenge,const u8 * peer_challenge,const u8 * username,size_t username_len,const u8 * password,size_t password_len,u8 * response)1713ff40c12SJohn Marino int generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge,
1726d49e1aeSJan Lentfer const u8 *username, size_t username_len,
1736d49e1aeSJan Lentfer const u8 *password, size_t password_len,
1746d49e1aeSJan Lentfer u8 *response)
1756d49e1aeSJan Lentfer {
1766d49e1aeSJan Lentfer u8 challenge[8];
1776d49e1aeSJan Lentfer u8 password_hash[16];
1786d49e1aeSJan Lentfer
1793ff40c12SJohn Marino if (challenge_hash(peer_challenge, auth_challenge, username,
180*a1157835SDaniel Fojt username_len, challenge) ||
181*a1157835SDaniel Fojt nt_password_hash(password, password_len, password_hash) ||
182*a1157835SDaniel Fojt challenge_response(challenge, password_hash, response))
1833ff40c12SJohn Marino return -1;
1843ff40c12SJohn Marino return 0;
1856d49e1aeSJan Lentfer }
1866d49e1aeSJan Lentfer
1876d49e1aeSJan Lentfer
1886d49e1aeSJan Lentfer /**
1896d49e1aeSJan Lentfer * generate_nt_response_pwhash - GenerateNTResponse() - RFC 2759, Sect. 8.1
1906d49e1aeSJan Lentfer * @auth_challenge: 16-octet AuthenticatorChallenge (IN)
1916d49e1aeSJan Lentfer * @peer_challenge: 16-octet PeerChallenge (IN)
1926d49e1aeSJan Lentfer * @username: 0-to-256-char UserName (IN)
1936d49e1aeSJan Lentfer * @username_len: Length of username
1946d49e1aeSJan Lentfer * @password_hash: 16-octet PasswordHash (IN)
1956d49e1aeSJan Lentfer * @response: 24-octet Response (OUT)
1963ff40c12SJohn Marino * Returns: 0 on success, -1 on failure
1976d49e1aeSJan Lentfer */
generate_nt_response_pwhash(const u8 * auth_challenge,const u8 * peer_challenge,const u8 * username,size_t username_len,const u8 * password_hash,u8 * response)1983ff40c12SJohn Marino int generate_nt_response_pwhash(const u8 *auth_challenge,
1996d49e1aeSJan Lentfer const u8 *peer_challenge,
2006d49e1aeSJan Lentfer const u8 *username, size_t username_len,
2016d49e1aeSJan Lentfer const u8 *password_hash,
2026d49e1aeSJan Lentfer u8 *response)
2036d49e1aeSJan Lentfer {
2046d49e1aeSJan Lentfer u8 challenge[8];
2056d49e1aeSJan Lentfer
2063ff40c12SJohn Marino if (challenge_hash(peer_challenge, auth_challenge,
2073ff40c12SJohn Marino username, username_len,
208*a1157835SDaniel Fojt challenge) ||
209*a1157835SDaniel Fojt challenge_response(challenge, password_hash, response))
2103ff40c12SJohn Marino return -1;
2113ff40c12SJohn Marino return 0;
2126d49e1aeSJan Lentfer }
2136d49e1aeSJan Lentfer
2146d49e1aeSJan Lentfer
2156d49e1aeSJan Lentfer /**
2166d49e1aeSJan Lentfer * generate_authenticator_response_pwhash - GenerateAuthenticatorResponse() - RFC 2759, Sect. 8.7
2176d49e1aeSJan Lentfer * @password_hash: 16-octet PasswordHash (IN)
2186d49e1aeSJan Lentfer * @nt_response: 24-octet NT-Response (IN)
2196d49e1aeSJan Lentfer * @peer_challenge: 16-octet PeerChallenge (IN)
2206d49e1aeSJan Lentfer * @auth_challenge: 16-octet AuthenticatorChallenge (IN)
2216d49e1aeSJan Lentfer * @username: 0-to-256-char UserName (IN)
2226d49e1aeSJan Lentfer * @username_len: Length of username
2236d49e1aeSJan Lentfer * @response: 20-octet AuthenticatorResponse (OUT) (note: this value is usually
2246d49e1aeSJan Lentfer * encoded as a 42-octet ASCII string (S=hexdump_of_response)
2253ff40c12SJohn Marino * Returns: 0 on success, -1 on failure
2266d49e1aeSJan Lentfer */
generate_authenticator_response_pwhash(const u8 * password_hash,const u8 * peer_challenge,const u8 * auth_challenge,const u8 * username,size_t username_len,const u8 * nt_response,u8 * response)2273ff40c12SJohn Marino int generate_authenticator_response_pwhash(
2286d49e1aeSJan Lentfer const u8 *password_hash,
2296d49e1aeSJan Lentfer const u8 *peer_challenge, const u8 *auth_challenge,
2306d49e1aeSJan Lentfer const u8 *username, size_t username_len,
2316d49e1aeSJan Lentfer const u8 *nt_response, u8 *response)
2326d49e1aeSJan Lentfer {
2336d49e1aeSJan Lentfer static const u8 magic1[39] = {
2346d49e1aeSJan Lentfer 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
2356d49e1aeSJan Lentfer 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
2366d49e1aeSJan Lentfer 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
2376d49e1aeSJan Lentfer 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74
2386d49e1aeSJan Lentfer };
2396d49e1aeSJan Lentfer static const u8 magic2[41] = {
2406d49e1aeSJan Lentfer 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
2416d49e1aeSJan Lentfer 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
2426d49e1aeSJan Lentfer 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
2436d49e1aeSJan Lentfer 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
2446d49e1aeSJan Lentfer 0x6E
2456d49e1aeSJan Lentfer };
2466d49e1aeSJan Lentfer
2476d49e1aeSJan Lentfer u8 password_hash_hash[16], challenge[8];
2486d49e1aeSJan Lentfer const unsigned char *addr1[3];
2496d49e1aeSJan Lentfer const size_t len1[3] = { 16, 24, sizeof(magic1) };
2506d49e1aeSJan Lentfer const unsigned char *addr2[3];
2516d49e1aeSJan Lentfer const size_t len2[3] = { SHA1_MAC_LEN, 8, sizeof(magic2) };
2526d49e1aeSJan Lentfer
2536d49e1aeSJan Lentfer addr1[0] = password_hash_hash;
2546d49e1aeSJan Lentfer addr1[1] = nt_response;
2556d49e1aeSJan Lentfer addr1[2] = magic1;
2566d49e1aeSJan Lentfer
2576d49e1aeSJan Lentfer addr2[0] = response;
2586d49e1aeSJan Lentfer addr2[1] = challenge;
2596d49e1aeSJan Lentfer addr2[2] = magic2;
2606d49e1aeSJan Lentfer
261*a1157835SDaniel Fojt if (hash_nt_password_hash(password_hash, password_hash_hash) ||
262*a1157835SDaniel Fojt sha1_vector(3, addr1, len1, response) ||
263*a1157835SDaniel Fojt challenge_hash(peer_challenge, auth_challenge, username,
2643ff40c12SJohn Marino username_len, challenge))
2653ff40c12SJohn Marino return -1;
2663ff40c12SJohn Marino return sha1_vector(3, addr2, len2, response);
2676d49e1aeSJan Lentfer }
2686d49e1aeSJan Lentfer
2696d49e1aeSJan Lentfer
2706d49e1aeSJan Lentfer /**
2716d49e1aeSJan Lentfer * generate_authenticator_response - GenerateAuthenticatorResponse() - RFC 2759, Sect. 8.7
2723ff40c12SJohn Marino * @password: 0-to-256-unicode-char Password (IN; UTF-8)
2736d49e1aeSJan Lentfer * @password_len: Length of password
2746d49e1aeSJan Lentfer * @nt_response: 24-octet NT-Response (IN)
2756d49e1aeSJan Lentfer * @peer_challenge: 16-octet PeerChallenge (IN)
2766d49e1aeSJan Lentfer * @auth_challenge: 16-octet AuthenticatorChallenge (IN)
2776d49e1aeSJan Lentfer * @username: 0-to-256-char UserName (IN)
2786d49e1aeSJan Lentfer * @username_len: Length of username
2796d49e1aeSJan Lentfer * @response: 20-octet AuthenticatorResponse (OUT) (note: this value is usually
2806d49e1aeSJan Lentfer * encoded as a 42-octet ASCII string (S=hexdump_of_response)
2813ff40c12SJohn Marino * Returns: 0 on success, -1 on failure
2826d49e1aeSJan Lentfer */
generate_authenticator_response(const u8 * password,size_t password_len,const u8 * peer_challenge,const u8 * auth_challenge,const u8 * username,size_t username_len,const u8 * nt_response,u8 * response)2833ff40c12SJohn Marino int generate_authenticator_response(const u8 *password, size_t password_len,
2846d49e1aeSJan Lentfer const u8 *peer_challenge,
2856d49e1aeSJan Lentfer const u8 *auth_challenge,
2866d49e1aeSJan Lentfer const u8 *username, size_t username_len,
2876d49e1aeSJan Lentfer const u8 *nt_response, u8 *response)
2886d49e1aeSJan Lentfer {
2896d49e1aeSJan Lentfer u8 password_hash[16];
2903ff40c12SJohn Marino if (nt_password_hash(password, password_len, password_hash))
2913ff40c12SJohn Marino return -1;
2923ff40c12SJohn Marino return generate_authenticator_response_pwhash(
2933ff40c12SJohn Marino password_hash, peer_challenge, auth_challenge,
2943ff40c12SJohn Marino username, username_len, nt_response, response);
2956d49e1aeSJan Lentfer }
2966d49e1aeSJan Lentfer
2976d49e1aeSJan Lentfer
2986d49e1aeSJan Lentfer /**
2996d49e1aeSJan Lentfer * nt_challenge_response - NtChallengeResponse() - RFC 2433, Sect. A.5
3006d49e1aeSJan Lentfer * @challenge: 8-octet Challenge (IN)
3013ff40c12SJohn Marino * @password: 0-to-256-unicode-char Password (IN; UTF-8)
3026d49e1aeSJan Lentfer * @password_len: Length of password
3036d49e1aeSJan Lentfer * @response: 24-octet Response (OUT)
3043ff40c12SJohn Marino * Returns: 0 on success, -1 on failure
3056d49e1aeSJan Lentfer */
nt_challenge_response(const u8 * challenge,const u8 * password,size_t password_len,u8 * response)3063ff40c12SJohn Marino int nt_challenge_response(const u8 *challenge, const u8 *password,
3076d49e1aeSJan Lentfer size_t password_len, u8 *response)
3086d49e1aeSJan Lentfer {
3096d49e1aeSJan Lentfer u8 password_hash[16];
310*a1157835SDaniel Fojt
311*a1157835SDaniel Fojt if (nt_password_hash(password, password_len, password_hash) ||
312*a1157835SDaniel Fojt challenge_response(challenge, password_hash, response))
3133ff40c12SJohn Marino return -1;
3143ff40c12SJohn Marino return 0;
3156d49e1aeSJan Lentfer }
3166d49e1aeSJan Lentfer
3176d49e1aeSJan Lentfer
3186d49e1aeSJan Lentfer /**
3196d49e1aeSJan Lentfer * get_master_key - GetMasterKey() - RFC 3079, Sect. 3.4
3206d49e1aeSJan Lentfer * @password_hash_hash: 16-octet PasswordHashHash (IN)
3216d49e1aeSJan Lentfer * @nt_response: 24-octet NTResponse (IN)
3226d49e1aeSJan Lentfer * @master_key: 16-octet MasterKey (OUT)
3233ff40c12SJohn Marino * Returns: 0 on success, -1 on failure
3246d49e1aeSJan Lentfer */
get_master_key(const u8 * password_hash_hash,const u8 * nt_response,u8 * master_key)3253ff40c12SJohn Marino int get_master_key(const u8 *password_hash_hash, const u8 *nt_response,
3266d49e1aeSJan Lentfer u8 *master_key)
3276d49e1aeSJan Lentfer {
3286d49e1aeSJan Lentfer static const u8 magic1[27] = {
3296d49e1aeSJan Lentfer 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
3306d49e1aeSJan Lentfer 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
3316d49e1aeSJan Lentfer 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79
3326d49e1aeSJan Lentfer };
3336d49e1aeSJan Lentfer const unsigned char *addr[3];
3346d49e1aeSJan Lentfer const size_t len[3] = { 16, 24, sizeof(magic1) };
3356d49e1aeSJan Lentfer u8 hash[SHA1_MAC_LEN];
3366d49e1aeSJan Lentfer
3376d49e1aeSJan Lentfer addr[0] = password_hash_hash;
3386d49e1aeSJan Lentfer addr[1] = nt_response;
3396d49e1aeSJan Lentfer addr[2] = magic1;
3406d49e1aeSJan Lentfer
3413ff40c12SJohn Marino if (sha1_vector(3, addr, len, hash))
3423ff40c12SJohn Marino return -1;
3436d49e1aeSJan Lentfer os_memcpy(master_key, hash, 16);
3443ff40c12SJohn Marino return 0;
3456d49e1aeSJan Lentfer }
3466d49e1aeSJan Lentfer
3476d49e1aeSJan Lentfer
3486d49e1aeSJan Lentfer /**
3496d49e1aeSJan Lentfer * get_asymetric_start_key - GetAsymetricStartKey() - RFC 3079, Sect. 3.4
3506d49e1aeSJan Lentfer * @master_key: 16-octet MasterKey (IN)
3516d49e1aeSJan Lentfer * @session_key: 8-to-16 octet SessionKey (OUT)
3526d49e1aeSJan Lentfer * @session_key_len: SessionKeyLength (Length of session_key) (IN)
3536d49e1aeSJan Lentfer * @is_send: IsSend (IN, BOOLEAN)
3546d49e1aeSJan Lentfer * @is_server: IsServer (IN, BOOLEAN)
3553ff40c12SJohn Marino * Returns: 0 on success, -1 on failure
3566d49e1aeSJan Lentfer */
get_asymetric_start_key(const u8 * master_key,u8 * session_key,size_t session_key_len,int is_send,int is_server)3573ff40c12SJohn Marino int get_asymetric_start_key(const u8 *master_key, u8 *session_key,
3586d49e1aeSJan Lentfer size_t session_key_len, int is_send,
3596d49e1aeSJan Lentfer int is_server)
3606d49e1aeSJan Lentfer {
3616d49e1aeSJan Lentfer static const u8 magic2[84] = {
3626d49e1aeSJan Lentfer 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
3636d49e1aeSJan Lentfer 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
3646d49e1aeSJan Lentfer 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
3656d49e1aeSJan Lentfer 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
3666d49e1aeSJan Lentfer 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
3676d49e1aeSJan Lentfer 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
3686d49e1aeSJan Lentfer 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
3696d49e1aeSJan Lentfer 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
3706d49e1aeSJan Lentfer 0x6b, 0x65, 0x79, 0x2e
3716d49e1aeSJan Lentfer };
3726d49e1aeSJan Lentfer static const u8 magic3[84] = {
3736d49e1aeSJan Lentfer 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
3746d49e1aeSJan Lentfer 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
3756d49e1aeSJan Lentfer 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
3766d49e1aeSJan Lentfer 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
3776d49e1aeSJan Lentfer 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
3786d49e1aeSJan Lentfer 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
3796d49e1aeSJan Lentfer 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
3806d49e1aeSJan Lentfer 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
3816d49e1aeSJan Lentfer 0x6b, 0x65, 0x79, 0x2e
3826d49e1aeSJan Lentfer };
3836d49e1aeSJan Lentfer static const u8 shs_pad1[40] = {
3846d49e1aeSJan Lentfer 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3856d49e1aeSJan Lentfer 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3866d49e1aeSJan Lentfer 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3876d49e1aeSJan Lentfer 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
3886d49e1aeSJan Lentfer };
3896d49e1aeSJan Lentfer
3906d49e1aeSJan Lentfer static const u8 shs_pad2[40] = {
3916d49e1aeSJan Lentfer 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
3926d49e1aeSJan Lentfer 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
3936d49e1aeSJan Lentfer 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
3946d49e1aeSJan Lentfer 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2
3956d49e1aeSJan Lentfer };
3966d49e1aeSJan Lentfer u8 digest[SHA1_MAC_LEN];
3976d49e1aeSJan Lentfer const unsigned char *addr[4];
3986d49e1aeSJan Lentfer const size_t len[4] = { 16, 40, 84, 40 };
3996d49e1aeSJan Lentfer
4006d49e1aeSJan Lentfer addr[0] = master_key;
4016d49e1aeSJan Lentfer addr[1] = shs_pad1;
4026d49e1aeSJan Lentfer if (is_send) {
4036d49e1aeSJan Lentfer addr[2] = is_server ? magic3 : magic2;
4046d49e1aeSJan Lentfer } else {
4056d49e1aeSJan Lentfer addr[2] = is_server ? magic2 : magic3;
4066d49e1aeSJan Lentfer }
4076d49e1aeSJan Lentfer addr[3] = shs_pad2;
4086d49e1aeSJan Lentfer
4093ff40c12SJohn Marino if (sha1_vector(4, addr, len, digest))
4103ff40c12SJohn Marino return -1;
4116d49e1aeSJan Lentfer
4126d49e1aeSJan Lentfer if (session_key_len > SHA1_MAC_LEN)
4136d49e1aeSJan Lentfer session_key_len = SHA1_MAC_LEN;
4146d49e1aeSJan Lentfer os_memcpy(session_key, digest, session_key_len);
4153ff40c12SJohn Marino return 0;
4166d49e1aeSJan Lentfer }
4176d49e1aeSJan Lentfer
4186d49e1aeSJan Lentfer
419*a1157835SDaniel Fojt #ifndef CONFIG_NO_RC4
420*a1157835SDaniel Fojt
4216d49e1aeSJan Lentfer #define PWBLOCK_LEN 516
4226d49e1aeSJan Lentfer
4236d49e1aeSJan Lentfer /**
4246d49e1aeSJan Lentfer * encrypt_pw_block_with_password_hash - EncryptPwBlockWithPasswordHash() - RFC 2759, Sect. 8.10
4253ff40c12SJohn Marino * @password: 0-to-256-unicode-char Password (IN; UTF-8)
4266d49e1aeSJan Lentfer * @password_len: Length of password
4276d49e1aeSJan Lentfer * @password_hash: 16-octet PasswordHash (IN)
4286d49e1aeSJan Lentfer * @pw_block: 516-byte PwBlock (OUT)
4296d49e1aeSJan Lentfer * Returns: 0 on success, -1 on failure
4306d49e1aeSJan Lentfer */
encrypt_pw_block_with_password_hash(const u8 * password,size_t password_len,const u8 * password_hash,u8 * pw_block)4316d49e1aeSJan Lentfer int encrypt_pw_block_with_password_hash(
4326d49e1aeSJan Lentfer const u8 *password, size_t password_len,
4336d49e1aeSJan Lentfer const u8 *password_hash, u8 *pw_block)
4346d49e1aeSJan Lentfer {
4353ff40c12SJohn Marino size_t ucs2_len, offset;
4366d49e1aeSJan Lentfer u8 *pos;
4376d49e1aeSJan Lentfer
4383ff40c12SJohn Marino os_memset(pw_block, 0, PWBLOCK_LEN);
4393ff40c12SJohn Marino
440*a1157835SDaniel Fojt if (utf8_to_ucs2(password, password_len, pw_block, 512, &ucs2_len) < 0
441*a1157835SDaniel Fojt || ucs2_len > 256)
4423ff40c12SJohn Marino return -1;
4433ff40c12SJohn Marino
4443ff40c12SJohn Marino offset = (256 - ucs2_len) * 2;
4453ff40c12SJohn Marino if (offset != 0) {
4463ff40c12SJohn Marino os_memmove(pw_block + offset, pw_block, ucs2_len * 2);
4476d49e1aeSJan Lentfer if (os_get_random(pw_block, offset) < 0)
4486d49e1aeSJan Lentfer return -1;
4493ff40c12SJohn Marino }
4506d49e1aeSJan Lentfer /*
4516d49e1aeSJan Lentfer * PasswordLength is 4 octets, but since the maximum password length is
4526d49e1aeSJan Lentfer * 256, only first two (in little endian byte order) can be non-zero.
4536d49e1aeSJan Lentfer */
4546d49e1aeSJan Lentfer pos = &pw_block[2 * 256];
4556d49e1aeSJan Lentfer WPA_PUT_LE16(pos, password_len * 2);
4566d49e1aeSJan Lentfer rc4_skip(password_hash, 16, 0, pw_block, PWBLOCK_LEN);
4576d49e1aeSJan Lentfer return 0;
4586d49e1aeSJan Lentfer }
4596d49e1aeSJan Lentfer
4606d49e1aeSJan Lentfer
4616d49e1aeSJan Lentfer /**
4626d49e1aeSJan Lentfer * new_password_encrypted_with_old_nt_password_hash - NewPasswordEncryptedWithOldNtPasswordHash() - RFC 2759, Sect. 8.9
4633ff40c12SJohn Marino * @new_password: 0-to-256-unicode-char NewPassword (IN; UTF-8)
4646d49e1aeSJan Lentfer * @new_password_len: Length of new_password
4653ff40c12SJohn Marino * @old_password: 0-to-256-unicode-char OldPassword (IN; UTF-8)
4666d49e1aeSJan Lentfer * @old_password_len: Length of old_password
4676d49e1aeSJan Lentfer * @encrypted_pw_block: 516-octet EncryptedPwBlock (OUT)
4686d49e1aeSJan Lentfer * Returns: 0 on success, -1 on failure
4696d49e1aeSJan Lentfer */
new_password_encrypted_with_old_nt_password_hash(const u8 * new_password,size_t new_password_len,const u8 * old_password,size_t old_password_len,u8 * encrypted_pw_block)4706d49e1aeSJan Lentfer int new_password_encrypted_with_old_nt_password_hash(
4716d49e1aeSJan Lentfer const u8 *new_password, size_t new_password_len,
4726d49e1aeSJan Lentfer const u8 *old_password, size_t old_password_len,
4736d49e1aeSJan Lentfer u8 *encrypted_pw_block)
4746d49e1aeSJan Lentfer {
4756d49e1aeSJan Lentfer u8 password_hash[16];
4766d49e1aeSJan Lentfer
4773ff40c12SJohn Marino if (nt_password_hash(old_password, old_password_len, password_hash))
4783ff40c12SJohn Marino return -1;
4796d49e1aeSJan Lentfer if (encrypt_pw_block_with_password_hash(new_password, new_password_len,
4806d49e1aeSJan Lentfer password_hash,
4816d49e1aeSJan Lentfer encrypted_pw_block))
4826d49e1aeSJan Lentfer return -1;
4836d49e1aeSJan Lentfer return 0;
4846d49e1aeSJan Lentfer }
4856d49e1aeSJan Lentfer
486*a1157835SDaniel Fojt #endif /* CONFIG_NO_RC4 */
487*a1157835SDaniel Fojt
4886d49e1aeSJan Lentfer
4896d49e1aeSJan Lentfer /**
4906d49e1aeSJan Lentfer * nt_password_hash_encrypted_with_block - NtPasswordHashEncryptedWithBlock() - RFC 2759, Sect 8.13
4916d49e1aeSJan Lentfer * @password_hash: 16-octer PasswordHash (IN)
4926d49e1aeSJan Lentfer * @block: 16-octet Block (IN)
4936d49e1aeSJan Lentfer * @cypher: 16-octer Cypher (OUT)
494*a1157835SDaniel Fojt * Returns: 0 on success, -1 on failure
4956d49e1aeSJan Lentfer */
nt_password_hash_encrypted_with_block(const u8 * password_hash,const u8 * block,u8 * cypher)496*a1157835SDaniel Fojt int nt_password_hash_encrypted_with_block(const u8 *password_hash,
4976d49e1aeSJan Lentfer const u8 *block, u8 *cypher)
4986d49e1aeSJan Lentfer {
499*a1157835SDaniel Fojt if (des_encrypt(password_hash, block, cypher) < 0 ||
500*a1157835SDaniel Fojt des_encrypt(password_hash + 8, block + 7, cypher + 8) < 0)
501*a1157835SDaniel Fojt return -1;
502*a1157835SDaniel Fojt return 0;
5036d49e1aeSJan Lentfer }
5046d49e1aeSJan Lentfer
5056d49e1aeSJan Lentfer
5066d49e1aeSJan Lentfer /**
5076d49e1aeSJan Lentfer * old_nt_password_hash_encrypted_with_new_nt_password_hash - OldNtPasswordHashEncryptedWithNewNtPasswordHash() - RFC 2759, Sect. 8.12
5083ff40c12SJohn Marino * @new_password: 0-to-256-unicode-char NewPassword (IN; UTF-8)
5096d49e1aeSJan Lentfer * @new_password_len: Length of new_password
5103ff40c12SJohn Marino * @old_password: 0-to-256-unicode-char OldPassword (IN; UTF-8)
5116d49e1aeSJan Lentfer * @old_password_len: Length of old_password
5126d49e1aeSJan Lentfer * @encrypted_password_hash: 16-octet EncryptedPasswordHash (OUT)
5133ff40c12SJohn Marino * Returns: 0 on success, -1 on failure
5146d49e1aeSJan Lentfer */
old_nt_password_hash_encrypted_with_new_nt_password_hash(const u8 * new_password,size_t new_password_len,const u8 * old_password,size_t old_password_len,u8 * encrypted_password_hash)5153ff40c12SJohn Marino int old_nt_password_hash_encrypted_with_new_nt_password_hash(
5166d49e1aeSJan Lentfer const u8 *new_password, size_t new_password_len,
5176d49e1aeSJan Lentfer const u8 *old_password, size_t old_password_len,
5186d49e1aeSJan Lentfer u8 *encrypted_password_hash)
5196d49e1aeSJan Lentfer {
5206d49e1aeSJan Lentfer u8 old_password_hash[16], new_password_hash[16];
5216d49e1aeSJan Lentfer
5223ff40c12SJohn Marino if (nt_password_hash(old_password, old_password_len,
5233ff40c12SJohn Marino old_password_hash) ||
5243ff40c12SJohn Marino nt_password_hash(new_password, new_password_len,
525*a1157835SDaniel Fojt new_password_hash) ||
5266d49e1aeSJan Lentfer nt_password_hash_encrypted_with_block(old_password_hash,
5276d49e1aeSJan Lentfer new_password_hash,
528*a1157835SDaniel Fojt encrypted_password_hash))
529*a1157835SDaniel Fojt return -1;
5303ff40c12SJohn Marino return 0;
5316d49e1aeSJan Lentfer }
532