1#!/usr/bin/env perl 2 3## 4## Author......: See docs/credits.txt 5## License.....: MIT 6## 7 8use strict; 9use warnings; 10 11use Crypt::PBKDF2; 12use Digest::MD5 qw (md5); 13use Digest::SHA qw (sha1 sha256); 14use Digest::HMAC qw (hmac hmac_hex); 15use Digest::CMAC; 16 17sub module_constraints { [[8, 63], [-1, -1], [-1, -1], [-1, -1], [-1, -1]] } 18 19sub module_generate_hash 20{ 21 my $word = shift; 22 my $salt = shift; 23 my $type = shift // random_number (1, 2); 24 my $macap = shift; 25 my $macsta = shift; 26 my $essid = shift; 27 my $anonce = shift; 28 my $eapol = shift; 29 my $mp = shift; 30 31 my $hash; 32 33 if ($type == 1) 34 { 35 if (!defined ($macap)) 36 { 37 $macap = unpack ("H*", random_bytes (6)); 38 } 39 40 if (!defined ($macsta)) 41 { 42 $macsta = unpack ("H*", random_bytes (6)); 43 } 44 45 if (!defined ($essid)) 46 { 47 $essid = unpack ("H*", random_bytes (random_number (0, 32) & 0x1e)); 48 } 49 50 my $pbkdf2 = Crypt::PBKDF2->new 51 ( 52 hash_class => 'HMACSHA1', 53 iterations => 4096, 54 output_len => 32, 55 ); 56 57 my $essid_bin = pack ("H*", $essid); 58 59 my $pmk = $pbkdf2->PBKDF2 ($essid_bin, $word); 60 61 my $macap_bin = pack ("H*", $macap); 62 my $macsta_bin = pack ("H*", $macsta); 63 64 my $data = "PMK Name" . $macap_bin . $macsta_bin; 65 66 my $pmkid = hmac_hex ($data, $pmk, \&sha1); 67 68 $hash = sprintf ("WPA*%02x*%s*%s*%s*%s***", $type, substr ($pmkid, 0, 32), $macap, $macsta, $essid); 69 } 70 elsif ($type == 2) 71 { 72 if (!defined ($macap)) 73 { 74 $macap = random_bytes (6); 75 } 76 else 77 { 78 $macap = pack ("H*", $macap); 79 } 80 81 if (!defined ($macsta)) 82 { 83 $macsta = random_bytes (6); 84 } 85 else 86 { 87 $macsta = pack ("H*", $macsta); 88 } 89 90 if (!defined ($mp)) 91 { 92 $mp = "\x00"; 93 } 94 else 95 { 96 $mp = pack ("H*", $mp); 97 } 98 99 my $keyver; 100 101 my $snonce; 102 103 if (!defined ($eapol)) 104 { 105 $keyver = random_number (1, 3); # 1, 2 or 3 106 107 $snonce = random_bytes (32); 108 109 $eapol = gen_random_wpa_eapol ($keyver, $snonce); 110 } 111 else 112 { 113 $eapol = pack ("H*", $eapol); 114 115 my $key_info = unpack ("n*", substr ($eapol, 5, 2)); 116 117 $keyver = $key_info & 3; 118 119 $snonce = substr ($eapol, 17, 32); 120 } 121 122 if (!defined ($anonce)) 123 { 124 $anonce = random_bytes (32); 125 } 126 else 127 { 128 $anonce = pack ("H*", $anonce); 129 } 130 131 if (!defined ($essid)) 132 { 133 $essid = unpack ("H*", random_bytes (random_number (0, 32) & 0x1e)); 134 } 135 136 my $pbkdf2 = Crypt::PBKDF2->new 137 ( 138 hash_class => 'HMACSHA1', 139 iterations => 4096, 140 output_len => 32, 141 ); 142 143 my $essid_bin = pack ("H*", $essid); 144 145 my $pmk = $pbkdf2->PBKDF2 ($essid_bin, $word); 146 147 # Pairwise Transient Key (PTK) transformation 148 149 my $ptk = wpa_prf_512 ($keyver, $pmk, $macsta, $macap, $snonce, $anonce); 150 151 # generate the Message Integrity Code (MIC) 152 153 my $mic = ""; 154 155 if ($keyver == 1) # WPA1 => MD5 156 { 157 $mic = hmac ($eapol, $ptk, \&md5); 158 } 159 elsif ($keyver == 2) # WPA2 => SHA1 160 { 161 $mic = hmac ($eapol, $ptk, \&sha1); 162 } 163 elsif ($keyver == 3) # WPA2 => SHA256 + AES-CMAC 164 { 165 my $omac1 = Digest::CMAC->new ($ptk, 'Crypt::Rijndael'); 166 167 $omac1->add ($eapol); 168 169 $mic = $omac1->digest; 170 } 171 172 $mic = substr ($mic, 0, 16); 173 174 $hash = sprintf ("WPA*%02x*%s*%s*%s*%s*%s*%s*%s", $type, unpack ("H*", $mic), unpack ("H*", $macap), unpack ("H*", $macsta), $essid, unpack ("H*", $anonce), unpack ("H*", $eapol), unpack ("H*", $mp)); 175 } 176 177 return $hash; 178} 179 180sub module_verify_hash 181{ 182 my $line = shift; 183 184 my $index1 = index ($line, ":"); 185 186 return if $index1 < 1; 187 188 my $word = substr ($line, $index1 + 1); 189 190 my $hash_in = substr ($line, 0, $index1); 191 192 my @data = split ('\*', $hash_in); 193 194 my ($signature, $type, $pmkidmic, $macap, $macsta, $essid, $anonce, $eapol, $mp) = @data; 195 196 return unless defined $signature; 197 return unless defined $type; 198 return unless defined $pmkidmic; 199 return unless defined $macap; 200 return unless defined $macsta; 201 return unless defined $essid; 202 203 return unless ($signature eq "WPA"); 204 205 my $word_packed = pack_if_HEX_notation ($word); 206 207 my $new_hash = module_generate_hash ($word_packed, undef, $type, $macap, $macsta, $essid, $anonce, $eapol, $mp); 208 209 return ($new_hash, $word); 210} 211 212sub gen_random_wpa_eapol 213{ 214 my $keyver = shift; 215 my $snonce = shift; 216 217 my $ret = ""; 218 219 # version 220 221 my $version = 1; # 802.1X-2001 222 223 $ret .= pack ("C*", $version); 224 225 my $type = 3; # means that this EAPOL frame is used to transfer key information 226 227 $ret .= pack ("C*", $type); 228 229 my $length; # length of remaining data 230 231 if ($keyver == 1) 232 { 233 $length = 119; 234 } 235 else 236 { 237 $length = 117; 238 } 239 240 $ret .= pack ("n*", $length); 241 242 my $descriptor_type; 243 244 if ($keyver == 1) 245 { 246 $descriptor_type = 254; # EAPOL WPA key 247 } 248 else 249 { 250 $descriptor_type = 1; # EAPOL RSN key 251 } 252 253 $ret .= pack ("C*", $descriptor_type); 254 255 # key_info is a bit vector: 256 # generated from these 13 bits: encrypted key data, request, error, secure, key mic, key ack, install, key index (2), key type, key descriptor (3) 257 258 my $key_info = 0; 259 260 $key_info |= 1 << 8; # set key MIC 261 $key_info |= 1 << 3; # set if it is a pairwise key 262 263 if ($keyver == 1) 264 { 265 $key_info |= 1; # RC4 Cipher, HMAC-MD5 MIC 266 } 267 elsif ($keyver == 2) 268 { 269 $key_info |= 2; # AES Cipher, HMAC-SHA1 MIC 270 } 271 elsif ($keyver == 3) 272 { 273 $key_info |= 3; # AES-CMAC 274 } 275 276 $ret .= pack ("n*", $key_info); 277 278 my $key_length; 279 280 if ($keyver == 1) 281 { 282 $key_length = 32; 283 } 284 else 285 { 286 $key_length = 0; 287 } 288 289 $ret .= pack ("n*", $key_length); 290 291 my $replay_counter = 1; 292 293 $ret .= pack ("Q>*", $replay_counter); 294 295 $ret .= $snonce; 296 297 my $key_iv = "\x00" x 16; 298 299 $ret .= $key_iv; 300 301 my $key_rsc = "\x00" x 8; 302 303 $ret .= $key_rsc; 304 305 my $key_id = "\x00" x 8; 306 307 $ret .= $key_id; 308 309 my $key_mic = "\x00" x 16; 310 311 $ret .= $key_mic; 312 313 my $key_data_len; 314 315 if ($keyver == 1) 316 { 317 $key_data_len = 24; # length of the key_data (== WPA info) 318 } 319 else 320 { 321 $key_data_len = 22; # length of the key_data (== RSN info) 322 } 323 324 $ret .= pack ("n*", $key_data_len); 325 326 my $key_data = ""; 327 328 if ($keyver == 1) 329 { 330 # wpa info 331 332 my $wpa_info = ""; 333 334 my $vendor_specific_data = ""; 335 336 my $tag_number = 221; # means it is a vendor specific tag 337 338 $vendor_specific_data .= pack ("C*", $tag_number); 339 340 my $tag_len = 22; # length of the remaining "tag data" 341 342 $vendor_specific_data .= pack ("C*", $tag_len); 343 344 my $vendor_specific_oui = pack ("H*", "0050f2"); # microsoft 345 346 $vendor_specific_data .= $vendor_specific_oui; 347 348 my $vendor_specific_oui_type = 1; # WPA Information Element 349 350 $vendor_specific_data .= pack ("C*", $vendor_specific_oui_type); 351 352 my $vendor_specific_wpa_version = 1; 353 354 $vendor_specific_data .= pack ("v*", $vendor_specific_wpa_version); 355 356 # multicast 357 358 my $vendor_specific_multicast_oui = pack ("H*", "0050f2"); 359 360 $vendor_specific_data .= $vendor_specific_multicast_oui; 361 362 my $vendor_specific_multicast_type = 2; # TKIP 363 364 $vendor_specific_data .= pack ("C*", $vendor_specific_multicast_type); 365 366 # unicast 367 368 my $vendor_specific_unicast_count = 1; 369 370 $vendor_specific_data .= pack ("v*", $vendor_specific_unicast_count); 371 372 my $vendor_specific_unicast_oui = pack ("H*", "0050f2"); 373 374 $vendor_specific_data .= $vendor_specific_unicast_oui; 375 376 my $vendor_specific_unicast_type = 2; # TKIP 377 378 $vendor_specific_data .= pack ("C*", $vendor_specific_unicast_type); 379 380 # Auth Key Management (AKM) 381 382 my $auth_key_management_count = 1; 383 384 $vendor_specific_data .= pack ("v*", $auth_key_management_count); 385 386 my $auth_key_management_oui = pack ("H*", "0050f2"); 387 388 $vendor_specific_data .= $auth_key_management_oui; 389 390 my $auth_key_management_type = 2; # Pre-Shared Key (PSK) 391 392 $vendor_specific_data .= pack ("C*", $auth_key_management_type); 393 394 $wpa_info = $vendor_specific_data; 395 396 $key_data = $wpa_info; 397 } 398 else 399 { 400 # rsn info 401 402 my $rsn_info = ""; 403 404 my $tag_number = 48; # RSN info 405 406 $rsn_info .= pack ("C*", $tag_number); 407 408 my $tag_len = 20; # length of the remaining "tag_data" 409 410 $rsn_info .= pack ("C*", $tag_len); 411 412 my $rsn_version = 1; 413 414 $rsn_info .= pack ("v*", $rsn_version); 415 416 # group cipher suite 417 418 my $group_cipher_suite_oui = pack ("H*", "000fac"); # Ieee8021 419 420 $rsn_info .= $group_cipher_suite_oui; 421 422 my $group_cipher_suite_type = 4; # AES (CCM) 423 424 $rsn_info .= pack ("C*", $group_cipher_suite_type); 425 426 # pairwise cipher suite 427 428 my $pairwise_cipher_suite_count = 1; 429 430 $rsn_info .= pack ("v*", $pairwise_cipher_suite_count); 431 432 my $pairwise_cipher_suite_oui = pack ("H*", "000fac"); # Ieee8021 433 434 $rsn_info .= $pairwise_cipher_suite_oui; 435 436 my $pairwise_cipher_suite_type = 4; # AES (CCM) 437 438 $rsn_info .= pack ("C*", $pairwise_cipher_suite_type); 439 440 # Auth Key Management (AKM) 441 442 my $auth_key_management_count = 1; 443 444 $rsn_info .= pack ("v*", $auth_key_management_count); 445 446 my $auth_key_management_oui = pack ("H*", "000fac"); # Ieee8021 447 448 $rsn_info .= $auth_key_management_oui; 449 450 my $auth_key_management_type = 2; # Pre-Shared Key (PSK) 451 452 $rsn_info .= pack ("C*", $auth_key_management_type); 453 454 # RSN Capabilities 455 456 # bit vector of these 9 bits: peerkey enabled, management frame protection (MFP) capable, MFP required, 457 # RSN GTKSA Capabilities (2), RSN PTKSA Capabilities (2), no pairwise Capabilities, Pre-Auth Capabilities 458 459 my $rsn_capabilities = pack ("H*", "0000"); 460 461 $rsn_info .= $rsn_capabilities; 462 463 $key_data = $rsn_info; 464 } 465 466 $ret .= $key_data; 467 468 return $ret; 469} 470 471sub wpa_prf_512 472{ 473 my $keyver = shift; 474 my $pmk = shift; 475 my $macsta = shift; 476 my $macap = shift; 477 my $snonce = shift; 478 my $anonce = shift; 479 480 my $data = "Pairwise key expansion"; 481 482 if (($keyver == 1) || ($keyver == 2)) 483 { 484 $data .= "\x00"; 485 } 486 487 # 488 # Min(AA, SPA) || Max(AA, SPA) 489 # 490 491 # compare if greater: Min()/Max() on the MACs (6 bytes) 492 493 if (memcmp ($macsta, $macap, 6) < 0) 494 { 495 $data .= $macsta; 496 $data .= $macap; 497 } 498 else 499 { 500 $data .= $macap; 501 $data .= $macsta; 502 } 503 504 # 505 # Min(ANonce,SNonce) || Max(ANonce,SNonce) 506 # 507 508 # compare if greater: Min()/Max() on the nonces (32 bytes) 509 510 if (memcmp ($snonce, $anonce, 32) < 0) 511 { 512 $data .= $snonce; 513 $data .= $anonce; 514 } 515 else 516 { 517 $data .= $anonce; 518 $data .= $snonce; 519 } 520 521 my $prf_buf; 522 523 if (($keyver == 1) || ($keyver == 2)) 524 { 525 $data .= "\x00"; 526 527 $prf_buf = hmac ($data, $pmk, \&sha1); 528 } 529 else 530 { 531 my $data3 = "\x01\x00" . $data . "\x80\x01"; 532 533 $prf_buf = hmac ($data3, $pmk, \&sha256); 534 } 535 536 $prf_buf = substr ($prf_buf, 0, 16); 537 538 return $prf_buf; 539} 540 541sub memcmp 542{ 543 my $str1 = shift; 544 my $str2 = shift; 545 my $len = shift; 546 547 my $len_str1 = length ($str1); 548 my $len_str2 = length ($str2); 549 550 if (($len > $len_str1) || ($len > $len_str2)) 551 { 552 print "ERROR: memcmp () lengths wrong"; 553 554 exit (1); 555 } 556 557 for (my $i = 0; $i < $len; $i++) 558 { 559 my $c_1 = ord (substr ($str1, $i, 1)); 560 my $c_2 = ord (substr ($str2, $i, 1)); 561 562 return -1 if ($c_1 < $c_2); 563 return 1 if ($c_1 > $c_2); 564 } 565 566 return 0; 567} 568 5691; 570