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