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::ECB qw (encrypt); 12use Digest::MD4 qw (md4); 13use Digest::SHA qw (sha1 hmac_sha1); 14use Encode; 15 16sub module_constraints { [[0, 256], [-1, -1], [-1, -1], [-1, -1], [-1, -1]] } 17 18sub get_random_dpapimk_salt 19{ 20 my $version = shift; 21 22 my $salt_buf = ""; 23 24 my $context = random_number (1, 2); 25 26 my $cipher_algo = ""; 27 28 my $hash_algo = ""; 29 30 my $iterations; 31 32 my $SID = sprintf ('S-15-21-%d-%d-%d-%d', 33 random_number (400000000,490000000), 34 random_number (400000000,490000000), 35 random_number (400000000,490000000), 36 random_number (1000,1999)); 37 38 my $cipher_len = 0; 39 40 if ($version == 1) 41 { 42 $iterations = random_number (4000, 24000); 43 44 $cipher_algo = "des3"; 45 46 $hash_algo = "sha1"; 47 48 $cipher_len = 208; 49 } 50 elsif ($version == 2) 51 { 52 $iterations = random_number (8000, 17000); 53 54 $cipher_algo = "aes256"; 55 56 $hash_algo = "sha512"; 57 58 $cipher_len = 288; 59 } 60 61 my $iv = random_bytes (16); 62 $iv = unpack ("H*", $iv); 63 64 $salt_buf = $version . '*' . 65 $context . '*' . 66 $SID . '*' . 67 $cipher_algo . '*' . 68 $hash_algo . '*' . 69 $iterations . '*' . 70 $iv . '*' . 71 $cipher_len . '*'; 72 73 return $salt_buf; 74} 75 76# Thanks to Jochen Hoenicke <hoenicke@gmail.com> 77# (one of the authors of Palm Keyring) 78# for these next two subs. 79 80sub dpapi_pbkdf2 81{ 82 my ($password, $salt, $iter, $keylen, $prf) = @_; 83 my ($k, $t, $u, $ui, $i); 84 85 $t = ""; 86 87 for ($k = 1; length ($t) < $keylen; $k++) 88 { 89 $u = $ui = &$prf ($salt . pack ('N', $k), $password); 90 91 for ($i = 1; $i < $iter; $i++) 92 { 93 # modification to fit Microsoft 94 # weird pbkdf2 implementation... 95 96 $ui = &$prf ($u, $password); 97 $u ^= $ui; 98 } 99 100 $t .= $u; 101 } 102 return substr ($t, 0, $keylen); 103} 104 105sub module_generate_hash 106{ 107 my $word_buf = shift; 108 my $salt_buf = shift; 109 my $dpapimk_salt = shift // get_random_dpapimk_salt (1); 110 my $cipher = shift; 111 112 my @salt_arr = split ('\*', $dpapimk_salt); 113 114 my $version = $salt_arr[0]; 115 my $context = $salt_arr[1]; 116 my $SID = $salt_arr[2]; 117 my $cipher_algorithm = $salt_arr[3]; 118 my $hash_algorithm = $salt_arr[4]; 119 my $iterations = $salt_arr[5]; 120 my $salt = pack ("H*", $salt_arr[6]); 121 my $cipher_len = $salt_arr[7]; 122 123 # intermediate values 124 125 my $user_hash; 126 my $user_derivationKey; 127 my $encKey; 128 my $expected_hmac; 129 my $cleartext; 130 131 if ($context == 1) 132 { 133 $user_hash = sha1 (encode ("UTF-16LE", $word_buf)); 134 } 135 elsif ($context == 2) 136 { 137 $user_hash = md4 (encode ("UTF-16LE", $word_buf)); 138 } 139 140 $user_derivationKey = hmac_sha1 (encode ("UTF-16LE", $SID . "\x00"), $user_hash); 141 142 my $hmacSalt = random_bytes (16); 143 my $last_key = random_bytes (64); 144 145 if ($version == 1) 146 { 147 $encKey = hmac_sha1 ($hmacSalt, $user_derivationKey); 148 $expected_hmac = hmac_sha1 ($last_key, $encKey); 149 150 # need padding because keyLen is 24 and hashLen 20 151 $expected_hmac = $expected_hmac . random_bytes (4); 152 } 153 elsif ($version == 2) 154 { 155 $encKey = hmac_sha512 ($hmacSalt, $user_derivationKey); 156 $expected_hmac = hmac_sha512 ($last_key, $encKey); 157 } 158 159 $cleartext = $hmacSalt . $expected_hmac . $last_key; 160 161 my $derived_key; 162 my $key; 163 my $iv; 164 165 my $pbkdf2; 166 167 if ($version == 1) 168 { 169 $derived_key = dpapi_pbkdf2 ($user_derivationKey, $salt, $iterations, 32, \&hmac_sha1); 170 } 171 elsif ($version == 2) 172 { 173 $derived_key = dpapi_pbkdf2 ($user_derivationKey, $salt, $iterations, 48, \&hmac_sha512); 174 } 175 176 if (defined $cipher) 177 { 178 $cipher = pack ("H*", $cipher); 179 180 my $computed_hmac = ""; 181 182 if ($version == 1) 183 { 184 $key = substr ($derived_key, 0, 24); 185 $iv = substr ($derived_key, 24, 8); 186 187 my $p1 = Crypt::ECB->new ({ 188 key => substr ($key, 0, 8), 189 cipher => "DES", 190 literal_key => 1, 191 header => "none", 192 keysize => 8, 193 padding => "none", 194 }); 195 196 my $p2 = Crypt::ECB->new ({ 197 key => substr ($key, 8, 8), 198 cipher => "DES", 199 literal_key => 1, 200 header => "none", 201 keysize => 8, 202 padding => "none", 203 }); 204 205 my $p3 = Crypt::ECB->new ({ 206 key => substr ($key, 16, 8), 207 cipher => "DES", 208 literal_key => 1, 209 header => "none", 210 keysize => 8, 211 padding => "none", 212 }); 213 214 # let's compute a 3DES-EDE-CBC decryption 215 216 my $out1; 217 my $out2; 218 my $out3; 219 my $expected_cleartext = ""; 220 221 # size of cipherlen is 104 bytes 222 for (my $k = 0; $k < 13; $k++) 223 { 224 $out1 = $p3->decrypt (substr ($cipher, $k * 8, 8)); 225 $out2 = $p2->encrypt ($out1); 226 $out3 = $p1->decrypt ($out2); 227 228 $expected_cleartext .= substr ($out3, 0, 8) ^ $iv; 229 230 $iv = substr ($cipher, $k * 8, 8); 231 } 232 233 $last_key = substr ($expected_cleartext, length ($expected_cleartext) - 64, 64); 234 $hmacSalt = substr ($expected_cleartext, 0, 16); 235 $expected_hmac = substr ($expected_cleartext, 16, 20); 236 237 $encKey = hmac_sha1 ($hmacSalt, $user_derivationKey); 238 $computed_hmac = hmac_sha1 ($last_key, $encKey); 239 240 $cleartext = $expected_cleartext; 241 242 if (unpack ("H*", $expected_hmac) ne unpack ("H*", $computed_hmac)) 243 { 244 $cleartext = "0" x 104; 245 } 246 } 247 elsif ($version == 2) 248 { 249 $key = substr ($derived_key, 0, 32); 250 $iv = substr ($derived_key, 32, 16); 251 252 my $aes = Crypt::CBC->new ({ 253 key => $key, 254 cipher => "Crypt::Rijndael", 255 iv => $iv, 256 literal_key => 1, 257 header => "none", 258 keysize => 32, 259 padding => "none", 260 }); 261 262 my $expected_cleartext = $aes->decrypt ($cipher); 263 264 $last_key = substr ($expected_cleartext, length ($expected_cleartext) - 64, 64); 265 $hmacSalt = substr ($expected_cleartext, 0, 16); 266 $expected_hmac = substr ($expected_cleartext, 16, 64); 267 268 $encKey = hmac_sha512 ($hmacSalt, $user_derivationKey); 269 $computed_hmac = hmac_sha512 ($last_key, $encKey); 270 271 $cleartext = $expected_cleartext; 272 273 if (unpack ("H*", $expected_hmac) ne unpack ("H*", $computed_hmac)) 274 { 275 $cleartext = "0" x 144; 276 } 277 } 278 } 279 280 if ($version == 1) 281 { 282 $key = substr ($derived_key, 0, 24); 283 $iv = substr ($derived_key, 24, 8); 284 285 my $p1 = Crypt::ECB->new ({ 286 key => substr ($key, 0, 8), 287 cipher => "DES", 288 literal_key => 1, 289 header => "none", 290 keysize => 8, 291 padding => "none", 292 }); 293 294 my $p2 = Crypt::ECB->new ({ 295 key => substr ($key, 8, 8), 296 cipher => "DES", 297 literal_key => 1, 298 header => "none", 299 keysize => 8, 300 padding => "none", 301 }); 302 303 my $p3 = Crypt::ECB->new ({ 304 key => substr ($key, 16, 8), 305 cipher => "DES", 306 literal_key => 1, 307 header => "none", 308 keysize => 8, 309 padding => "none", 310 }); 311 312 # let's compute a 3DES-EDE-CBC encryption 313 314 # compute first block 315 my $out1 = $p1->encrypt (substr ($cleartext, 0, 8) ^ $iv); 316 my $out2 = $p2->decrypt ($out1); 317 my $out3 = $p3->encrypt ($out2); 318 319 $cipher = substr ($out3, 0, 8); 320 321 # size of cipherlen is 104 bytes 322 for (my $k = 1; $k < 13; $k++) 323 { 324 $iv = $out3; 325 326 $out1 = $p1->encrypt (substr ($cleartext, $k * 8, 8) ^ $iv); 327 $out2 = $p2->decrypt ($out1); 328 $out3 = $p3->encrypt ($out2); 329 330 $cipher .= substr ($out3, 0, 8); 331 } 332 } 333 else 334 { 335 $key = substr ($derived_key, 0, 32); 336 $iv = substr ($derived_key, 32, 16); 337 338 my $aes = Crypt::CBC->new ({ 339 key => $key, 340 cipher => "Crypt::Rijndael", 341 iv => $iv, 342 literal_key => 1, 343 header => "none", 344 keysize => 32, 345 padding => "none", 346 }); 347 348 $cipher = $aes->encrypt ($cleartext); 349 } 350 351 my $tmp_hash = sprintf ('$DPAPImk$%d*%d*%s*%s*%s*%d*%s*%d*%s', 352 $version, 353 $context, 354 $SID, 355 $cipher_algorithm, 356 $hash_algorithm, 357 $iterations, 358 unpack ("H*", $salt), 359 $cipher_len, 360 unpack ("H*", $cipher)); 361 362 return $tmp_hash; 363} 364 365sub module_verify_hash 366{ 367 my $line = shift; 368 369 my ($hash, $word) = split (':', $line); 370 371 return unless defined $hash; 372 return unless defined $word; 373 374 my @tmp_data = split ('\$', $hash); 375 376 my $signature = $tmp_data[1]; 377 378 next unless ($signature eq 'DPAPImk'); 379 380 my @data = split ('\*', $tmp_data[2]); 381 382 next unless (scalar @data == 9); 383 384 my $version = shift @data; 385 386 next unless ($version == 1 || $version == 2); 387 388 my $context = shift @data; 389 my $SID = shift @data; 390 my $cipher_algorithm = shift @data; 391 my $hash_algorithm = shift @data; 392 my $iteration = shift @data; 393 my $iv = shift @data; 394 my $cipher_len = shift @data; 395 my $cipher = shift @data; 396 397 next unless (length ($cipher) == $cipher_len); 398 399 if ($version == 1) 400 { 401 next unless ($cipher_len == 208); 402 } 403 elsif ($version == 2) 404 { 405 next unless ($cipher_len == 288); 406 } 407 408 my $dpapimk_salt = substr ($hash, length ('$DPAPImk$')); 409 410 my $word_packed = pack_if_HEX_notation ($word); 411 412 my $new_hash = module_generate_hash ($word_packed, undef, $dpapimk_salt, $cipher); 413 414 return ($new_hash, $word); 415} 416 4171; 418