diff --git a/include/interface_migrate.h b/include/interface_migrate.h index 2481cde83..b5774d38b 100644 --- a/include/interface_migrate.h +++ b/include/interface_migrate.h @@ -643,17 +643,6 @@ typedef struct keepass_tmp } keepass_tmp_t; -typedef struct dpapimk_tmp_v2 -{ - u64 ipad64[8]; - u64 opad64[8]; - u64 dgst64[16]; - u64 out64[16]; - - u32 userKey[8]; - -} dpapimk_tmp_v2_t; - typedef struct apple_secure_notes_tmp { u32 ipad[8]; @@ -664,7 +653,6 @@ typedef struct apple_secure_notes_tmp } apple_secure_notes_tmp_t; - typedef struct struct_psafe2_hdr { u32 random[2]; @@ -927,7 +915,6 @@ typedef enum kern_type KERN_TYPE_JKS_SHA1 = 15500, KERN_TYPE_ETHEREUM_PBKDF2 = 15600, KERN_TYPE_ETHEREUM_SCRYPT = 15700, - KERN_TYPE_DPAPIMK_V2 = 15900, KERN_TYPE_TRIPCODE = 16000, KERN_TYPE_TACACS_PLUS = 16100, KERN_TYPE_APPLE_SECURE_NOTES = 16200, @@ -1010,7 +997,6 @@ typedef enum rounds_count ROUNDS_ITUNES102_BACKUP = 10000, ROUNDS_ATLASSIAN = 10000, ROUNDS_NETBSD_SHA1CRYPT = 20000, - ROUNDS_DPAPIMK_V2 = 8000 - 1, // from 4000 to 24000 (possibly more) ROUNDS_ETHEREUM_PBKDF2 = 262144 - 1, ROUNDS_APPLE_SECURE_NOTES = 20000, ROUNDS_ETHEREUM_PRESALE = 2000 - 1, diff --git a/src/interface_migrate.c b/src/interface_migrate.c index 07969a248..dd306e851 100644 --- a/src/interface_migrate.c +++ b/src/interface_migrate.c @@ -105,7 +105,6 @@ " 11500 | CRC32 | Checksums", " 1100 | Domain Cached Credentials (DCC), MS Cache | Operating Systems", " 2100 | Domain Cached Credentials 2 (DCC2), MS Cache 2 | Operating Systems", - " 15900 | DPAPI masterkey file v2 | Operating Systems", " 12800 | MS-AzureSync PBKDF2-HMAC-SHA256 | Operating Systems", " 12400 | BSDi Crypt, Extended DES | Operating Systems", " 7400 | sha256crypt $5$, SHA256 (Unix) | Operating Systems", @@ -448,7 +447,6 @@ static const char *ST_HASH_15400 = "$chacha20$*0400000000000003*35*0200000000000 static const char *ST_HASH_15500 = "$jksprivk$*338BD2FBEBA7B3EF198A4CBFC6E18AFF1E229367*5225850113575146134463704406336350011656*D5253EB151EB92DC73E542D8C0A4D7A848A5B0C0E370E625E6547D4E6F23416FC85A27BC295731B8021CDFBD003551C66C434FFBC87DACAD1FDF39022320034A2F86E779F2B1B3325428A666518FA89507AD63E15FD9C57B9E36EF5B642A2F448A9A3F09B79AD93D65F46B8692CD07539FD140146F8F219DC262971AF019E18EDC16C3C240569E1673F4D98BC818CCF28298D5A7BFF038A663DD10FE5E48643C3217C237D342164E2D41EF15075431FBD5B34800E5AE7EB80FAA5AE9982A55F35379AA7B31217E7F1C5F1964A15024A305AE4B3981FE1C80C163BC38ECA5581F11867E5C34C5D124D0367B3737E5E5BB14D2CAB26A698C8DAAB755C82BA6B823BCAECDD4A89C831651ACE5A6029FD0D3515C5D1D53AD8B9062CE8C445373862035CBBF60D490CA2E4975EE6E0358EC32E871FAB15347E3032E21F30F543BAAB01D779BA833CA0B8C7591B42C7C59A8FDD46D7DECEC0E91ADBF331177605E7830ABED62FAD7D5D806D8EFD01C38765940B7F97168FC72C39BF4C98F944FFC310CA8F4EB1D0F960F352CC5E2BB23A1EB221072A5471EDA2CE81C04595B8D37088CFB5C14F6A4A881AD12125DEFBB8154EB4C130AB7FD9933FD36DF1A6A26B51AB169866788678FCED988C8E017CA84354F487A5508210181AFB8B3AD0753E3E28BE674DFBD4E4FBDFD1E30D592F4EA3A77A2F0F5CF9A175DBC590EF5D42971A39918F12B92DCD8BFD56BE9A3459856B5587603C7B53062663A4C8894BBC9894FB1663BF30F32D907664328138B7A50EAC7F8E3183D74562A5C90FE1889AC4C5FE43EBEB8974563B6682F92591ECA4FA0DA72236C3851DA102DB6BA0CC07BFD32F7E962AB0EDCF4A8DEA6525174F5BB5C021E2A9A3F7F761E9CA90B6E27FB7E55CD91DA184FAC5E534E8AD25314C56CE5796506A0CA70881782F9C5147D87705065D68BD67D2B0344205BA6445D562273690004CA5A303274FB283A75F49BA968D7947943AA98F2AF9CB8253B425B86225E7395A331AC4CB1B1700C64D4F458D5D642C54148AE6DA41D9E26657D331B157D76042C2CF3057B83997C23D8BF68FB3C7337CAFB8B324AD0DF7A80B554B4D7F9AD6ED527E7932F1741A573C152A41610F6517E3F4A3BC6B66685871A7CE3795C559BD47CDB8E34CB2C1DFE980518D79E2078C258C54F312EB38609F640E7DC013E0F2A16A25BB5971882B4308D27930CA99FEC231AE927B62215A1B56098C362B7F20593953B29428681875070E84BF5B60BEA3948127151634123DA77C814AAD54CE10905763C8C19BC191C0C40458C809402E1957C4C05C4EAE27576B2D30593F7FDCC9A248DB5DB23CF2FA22A92C016090F611690BF0AB5B8B2866ED25F345EFE85DF3311C9E91C37CEE709CF16E7CB09D01BECD2961D094C02D42EC85BF47FAB1B67A13B9A1741C15F7156D57A71BFFABB03B71E69707913A5C136B3D69CE3F71ABFE376F0A21D723FFA2E60AC180689D3E8AF4348C9F555CD897387327FC8BA2B9C51A7298547E556A11A60441EF5331A1BFB847A3D23DD9F7C50E636A2C6309BC82E1A8852F5A8569B6D93*14*78D6A2424484CF5149932B7EA8BF*test"; static const char *ST_HASH_15600 = "$ethereum$p*1024*38353131353831333338313138363430*a8b4dfe92687dbc0afeb5dae7863f18964241e96b264f09959903c8c924583fc*0a9252861d1e235994ce33dbca91c98231764d8ecb4950015a8ae20d6415b986"; //static const char *ST_HASH_15700 = "$ethereum$s*262144*8*1*3134313837333434333838303231333633373433323633373534333136363537*73da7f80ec3bd4f2a128c3a815cfb4d576ecb1a9b47024c902e62ea926f7795b*910e0f8dc1f7ba41959e1089bb769f3e919109591913cc33ba03953d7a905efd"; -static const char *ST_HASH_15900 = "$DPAPImk$2*1*S-15-21-439882973-489230393-482956683-1522*aes256*sha512*12900*79f7ca399f2626e21aad108c3922af7c*288*c47bc8a985ca6aa708b01c97b004bff20cc52379dc2635b4acf59ce17970a2cb47ace98c7e8de977f265243c5c03d0a97e4b954b494d9e38d9158d0c1e729d16a28ba69e2e7c6c3bc0e3afc9c9b6306b83372ccb35d89b98925728fd36315b8ee95b4d4eccdcb31564769f9a4b9ee10828184e16d4af336675d5e31d987dd87233d34fbbb98880c5e1f64cbb9b043ad8"; static const char *ST_HASH_16000 = "pfaRCwDe0U"; static const char *ST_HASH_16100 = "$tacacs-plus$0$5fde8e68$4e13e8fb33df$c006"; static const char *ST_HASH_16200 = "$ASN$*1*20000*80771171105233481004850004085037*d04b17af7f6b184346aad3efefe8bec0987ee73418291a41"; @@ -610,7 +608,6 @@ static const char *HT_15400 = "ChaCha20"; static const char *HT_15500 = "JKS Java Key Store Private Keys (SHA1)"; static const char *HT_15600 = "Ethereum Wallet, PBKDF2-HMAC-SHA256"; static const char *HT_15700 = "Ethereum Wallet, SCRYPT"; -static const char *HT_15900 = "DPAPI masterkey file v2"; static const char *HT_16000 = "Tripcode"; static const char *HT_16100 = "TACACS+"; static const char *HT_16200 = "Apple Secure Notes"; @@ -15873,8 +15870,6 @@ void hashconfig_benchmark_defaults (hashcat_ctx_t *hashcat_ctx, salt_t *salt, vo break; case 15700: salt->salt_iter = 1; break; - case 15900: salt->salt_iter = ROUNDS_DPAPIMK_V2; - break; case 16200: salt->salt_iter = ROUNDS_APPLE_SECURE_NOTES - 1; break; case 16300: salt->salt_iter = ROUNDS_ETHEREUM_PRESALE; @@ -18346,91 +18341,6 @@ int ascii_digest (hashcat_ctx_t *hashcat_ctx, char *out_buf, const int out_size, digest_buf[7] ); } - else if (hash_mode == 15900) - { - dpapimk_t *dpapimks = (dpapimk_t *) esalts_buf; - - dpapimk_t *dpapimk = &dpapimks[digest_cur]; - - u32 version = 2; - u32 context = dpapimk->context; - u32 rounds = salt.salt_iter + 1; - u32 contents_len = dpapimk->contents_len; - u32 SID_len = dpapimk->SID_len; - u32 iv_len = 32; - - u8 cipher_algorithm[8] = { 0 }; - u8 hash_algorithm[8] = { 0 }; - u8 SID[512] = { 0 }; - u8* SID_tmp; - - u32 *ptr_SID = (u32 *) dpapimk->SID; - u32 *ptr_iv = (u32 *) dpapimk->iv; - u32 *ptr_contents = (u32 *) dpapimk->contents; - - u32 u32_iv[4]; - u8 iv[32 + 1]; - - // convert back SID - - SID_tmp = (u8 *) hcmalloc ((SID_len + 1) * sizeof(u8)); - - for (u32 i = 0; i < (SID_len / 4) + 1; i++) - { - u8 hex[8] = { 0 }; - u32_to_hex (byte_swap_32 (ptr_SID[i]), hex); - - for (u32 j = 0, k = 0; j < 8; j += 2, k++) - { - SID_tmp[i * 4 + k] = hex_to_u8 (&hex[j]); - } - } - // overwrite trailing 0x80 - SID_tmp[SID_len] = 0; - - for (u32 i = 0, j = 0 ; j < SID_len ; i++, j += 2) - { - SID[i] = SID_tmp[j]; - } - - hcfree(SID_tmp); - - for (u32 i = 0; i < iv_len / 8; i++) - { - u32_iv[i] = byte_swap_32 (ptr_iv[i]); - u32_to_hex (u32_iv[i], iv + i * 8); - } - iv[32] = 0; - - u32 u32_contents[36]; - u8 contents[288 + 1]; - - for (u32 i = 0; i < contents_len / 8; i++) - { - u32_contents[i] = byte_swap_32 (ptr_contents[i]); - u32_to_hex (u32_contents[i], contents + i * 8); - } - - contents[288] = 0; - - if (contents_len == 288) - { - memcpy(cipher_algorithm, "aes256", strlen("aes256")); - memcpy(hash_algorithm, "sha512", strlen("sha512")); - } - - snprintf (out_buf, out_size, "%s%d*%d*%s*%s*%s*%d*%s*%d*%s", - SIGNATURE_DPAPIMK, - version, - context, - SID, - cipher_algorithm, - hash_algorithm, - rounds, - iv, - contents_len, - contents); - } else if (hash_mode == 16000) { memset (tmp_buf, 0, sizeof (tmp_buf)); @@ -23018,25 +22928,6 @@ int hashconfig_init (hashcat_ctx_t *hashcat_ctx) hashconfig->st_pass = ST_PASS_HASHCAT_PLAIN; break; - case 15900: hashconfig->hash_type = HASH_TYPE_DPAPIMK; - hashconfig->salt_type = SALT_TYPE_EMBEDDED; - hashconfig->attack_exec = ATTACK_EXEC_OUTSIDE_KERNEL; - hashconfig->opts_type = OPTS_TYPE_PT_GENERATE_LE - | OPTS_TYPE_PREFERED_THREAD; - hashconfig->kern_type = KERN_TYPE_DPAPIMK_V2; - hashconfig->dgst_size = DGST_SIZE_4_4; - hashconfig->parse_func = dpapimk_parse_hash; - hashconfig->opti_type = OPTI_TYPE_ZERO_BYTE - | OPTI_TYPE_USES_BITS_64 - | OPTI_TYPE_SLOW_HASH_SIMD_LOOP; - hashconfig->dgst_pos0 = 0; - hashconfig->dgst_pos1 = 1; - hashconfig->dgst_pos2 = 2; - hashconfig->dgst_pos3 = 3; - hashconfig->st_hash = ST_HASH_15900; - hashconfig->st_pass = ST_PASS_HASHCAT_PLAIN; - break; - case 16000: hashconfig->hash_type = HASH_TYPE_DESCRYPT; hashconfig->salt_type = SALT_TYPE_NONE; hashconfig->attack_exec = ATTACK_EXEC_INSIDE_KERNEL; @@ -23419,7 +23310,6 @@ int hashconfig_init (hashcat_ctx_t *hashcat_ctx) case 15500: hashconfig->esalt_size = sizeof (jks_sha1_t); break; case 15600: hashconfig->esalt_size = sizeof (ethereum_pbkdf2_t); break; case 15700: hashconfig->esalt_size = sizeof (ethereum_scrypt_t); break; - case 15900: hashconfig->esalt_size = sizeof (dpapimk_t); break; case 16100: hashconfig->esalt_size = sizeof (tacacs_plus_t); break; case 16200: hashconfig->esalt_size = sizeof (apple_secure_notes_t); break; case 16300: hashconfig->esalt_size = sizeof (ethereum_presale_t); break; @@ -23522,7 +23412,6 @@ int hashconfig_init (hashcat_ctx_t *hashcat_ctx) case 15100: hashconfig->tmp_size = sizeof (pbkdf1_sha1_tmp_t); break; case 15200: hashconfig->tmp_size = sizeof (mywallet_tmp_t); break; case 15600: hashconfig->tmp_size = sizeof (pbkdf2_sha256_tmp_t); break; - case 15900: hashconfig->tmp_size = sizeof (dpapimk_tmp_v2_t); break; case 16200: hashconfig->tmp_size = sizeof (apple_secure_notes_tmp_t); break; case 16300: hashconfig->tmp_size = sizeof (pbkdf2_sha256_tmp_t); break; case 16700: hashconfig->tmp_size = sizeof (apple_secure_notes_tmp_t); break; @@ -23716,7 +23605,6 @@ u32 default_pw_max (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED co case 15400: pw_max = 32; break; // Underlaying ChaCha20 fixed case 15600: pw_max = PW_MAX; break; case 15700: pw_max = PW_MAX; break; - case 15900: pw_max = PW_MAX; break; case 16000: pw_max = 8; break; // Underlaying DES max case 16900: pw_max = PW_MAX; break; } diff --git a/src/modules/module_15300.c b/src/modules/module_15300.c index 74be86bc3..28f13a178 100644 --- a/src/modules/module_15300.c +++ b/src/modules/module_15300.c @@ -22,9 +22,11 @@ static const char *HASH_NAME = "DPAPI masterkey file v1"; static const u32 HASH_TYPE = HASH_TYPE_GENERIC; static const u64 KERN_TYPE = 15300; static const u32 OPTI_TYPE = OPTI_TYPE_ZERO_BYTE + | OPTI_TYPE_USES_BITS_64 | OPTI_TYPE_SLOW_HASH_SIMD_LOOP; static const u64 OPTS_TYPE = OPTS_TYPE_STATE_BUFFER_LE - | OPTS_TYPE_PT_GENERATE_LE; + | OPTS_TYPE_PT_GENERATE_LE + | OPTS_TYPE_PREFERED_THREAD; static const u32 SALT_TYPE = SALT_TYPE_EMBEDDED; static const char *ST_PASS = "hashcat"; static const char *ST_HASH = "$DPAPImk$1*1*S-15-21-466364039-425773974-453930460-1925*des3*sha1*24000*b038489dee5ad04e3e3cab4d957258b5*208*cb9b5b7d96a0d2a00305ca403d3fd9c47c561e35b4b2cf3aebfd1d3199a6481d56972be7ebd6c291b199e6f1c2ffaee91978706737e9b1209e6c7d3aa3d8c3c3e38ad1ccfa39400d62c2415961c17fd0bd6b0f7bbd49cc1de1a394e64b7237f56244238da8d37d78"; @@ -294,13 +296,15 @@ int module_hash_encode (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSE u8 cipher_algorithm[8] = { 0 }; u8 hash_algorithm[8] = { 0 }; u8 SID[512] = { 0 }; + u8* SID_tmp; - u32 *ptr_SID = (u32 *) dpapimk->SID; - u32 *ptr_iv = (u32 *) dpapimk->iv; - u32 *ptr_contents = (u32 *) dpapimk->contents; + u32 *ptr_SID = (u32 *) dpapimk->SID; + u32 *ptr_iv = (u32 *) dpapimk->iv; + u32 *ptr_contents = (u32 *) dpapimk->contents; u32 u32_iv[4]; + u8 iv[32 + 1]; // convert back SID @@ -310,6 +314,7 @@ int module_hash_encode (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSE for (u32 i = 0; i < (SID_len / 4) + 1; i++) { u8 hex[8] = { 0 }; + u32_to_hex (byte_swap_32 (ptr_SID[i]), hex); for (u32 j = 0, k = 0; j < 8; j += 2, k++) @@ -317,6 +322,7 @@ int module_hash_encode (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSE SID_tmp[i * 4 + k] = hex_to_u8 (&hex[j]); } } + // overwrite trailing 0x80 SID_tmp[SID_len] = 0; @@ -330,17 +336,21 @@ int module_hash_encode (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSE for (u32 i = 0; i < iv_len / 8; i++) { u32_iv[i] = byte_swap_32 (ptr_iv[i]); + u32_to_hex (u32_iv[i], iv + i * 8); } + iv[32] = 0; u32 u32_contents[36]; - u8 contents[288 + 1]; + + u8 contents[288 + 1]; for (u32 i = 0; i < contents_len / 8; i++) { u32_contents[i] = byte_swap_32 (ptr_contents[i]); - u32_to_hex (u32_contents[i], contents + i * 8); + + u32_to_hex (u32_contents[i], contents + i * 8); } contents[208] = 0; @@ -348,7 +358,8 @@ int module_hash_encode (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSE if (contents_len == 208) { memcpy (cipher_algorithm, "des3", strlen("des3")); - memcpy (hash_algorithm, "sha1", strlen("sha1")); + + memcpy (hash_algorithm, "sha1", strlen("sha1")); } const int line_len = snprintf (line_buf, line_size, "%s%u*%u*%s*%s*%s*%u*%s*%u*%s", diff --git a/src/modules/module_15900.c b/src/modules/module_15900.c new file mode 100644 index 000000000..a6cb21c6b --- /dev/null +++ b/src/modules/module_15900.c @@ -0,0 +1,442 @@ +/** + * Author......: See docs/credits.txt + * License.....: MIT + */ + +#include "common.h" +#include "types.h" +#include "modules.h" +#include "bitops.h" +#include "convert.h" +#include "shared.h" +#include "memory.h" + +static const u32 ATTACK_EXEC = ATTACK_EXEC_OUTSIDE_KERNEL; +static const u32 DGST_POS0 = 0; +static const u32 DGST_POS1 = 1; +static const u32 DGST_POS2 = 2; +static const u32 DGST_POS3 = 3; +static const u32 DGST_SIZE = DGST_SIZE_4_4; +static const u32 HASH_CATEGORY = HASH_CATEGORY_OS; +static const char *HASH_NAME = "DPAPI masterkey file v2"; +static const u32 HASH_TYPE = HASH_TYPE_GENERIC; +static const u64 KERN_TYPE = 15900; +static const u32 OPTI_TYPE = OPTI_TYPE_ZERO_BYTE + | OPTI_TYPE_SLOW_HASH_SIMD_LOOP; +static const u64 OPTS_TYPE = OPTS_TYPE_STATE_BUFFER_LE + | OPTS_TYPE_PT_GENERATE_LE; +static const u32 SALT_TYPE = SALT_TYPE_EMBEDDED; +static const char *ST_PASS = "hashcat"; +static const char *ST_HASH = "$DPAPImk$2*1*S-15-21-439882973-489230393-482956683-1522*aes256*sha512*12900*79f7ca399f2626e21aad108c3922af7c*288*c47bc8a985ca6aa708b01c97b004bff20cc52379dc2635b4acf59ce17970a2cb47ace98c7e8de977f265243c5c03d0a97e4b954b494d9e38d9158d0c1e729d16a28ba69e2e7c6c3bc0e3afc9c9b6306b83372ccb35d89b98925728fd36315b8ee95b4d4eccdcb31564769f9a4b9ee10828184e16d4af336675d5e31d987dd87233d34fbbb98880c5e1f64cbb9b043ad8"; + +u32 module_attack_exec (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED const user_options_t *user_options, MAYBE_UNUSED const user_options_extra_t *user_options_extra) { return ATTACK_EXEC; } +u32 module_dgst_pos0 (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED const user_options_t *user_options, MAYBE_UNUSED const user_options_extra_t *user_options_extra) { return DGST_POS0; } +u32 module_dgst_pos1 (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED const user_options_t *user_options, MAYBE_UNUSED const user_options_extra_t *user_options_extra) { return DGST_POS1; } +u32 module_dgst_pos2 (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED const user_options_t *user_options, MAYBE_UNUSED const user_options_extra_t *user_options_extra) { return DGST_POS2; } +u32 module_dgst_pos3 (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED const user_options_t *user_options, MAYBE_UNUSED const user_options_extra_t *user_options_extra) { return DGST_POS3; } +u32 module_dgst_size (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED const user_options_t *user_options, MAYBE_UNUSED const user_options_extra_t *user_options_extra) { return DGST_SIZE; } +u32 module_hash_category (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED const user_options_t *user_options, MAYBE_UNUSED const user_options_extra_t *user_options_extra) { return HASH_CATEGORY; } +const char *module_hash_name (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED const user_options_t *user_options, MAYBE_UNUSED const user_options_extra_t *user_options_extra) { return HASH_NAME; } +u32 module_hash_type (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED const user_options_t *user_options, MAYBE_UNUSED const user_options_extra_t *user_options_extra) { return HASH_TYPE; } +u64 module_kern_type (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED const user_options_t *user_options, MAYBE_UNUSED const user_options_extra_t *user_options_extra) { return KERN_TYPE; } +u32 module_opti_type (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED const user_options_t *user_options, MAYBE_UNUSED const user_options_extra_t *user_options_extra) { return OPTI_TYPE; } +u64 module_opts_type (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED const user_options_t *user_options, MAYBE_UNUSED const user_options_extra_t *user_options_extra) { return OPTS_TYPE; } +u32 module_salt_type (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED const user_options_t *user_options, MAYBE_UNUSED const user_options_extra_t *user_options_extra) { return SALT_TYPE; } +const char *module_st_hash (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED const user_options_t *user_options, MAYBE_UNUSED const user_options_extra_t *user_options_extra) { return ST_HASH; } +const char *module_st_pass (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED const user_options_t *user_options, MAYBE_UNUSED const user_options_extra_t *user_options_extra) { return ST_PASS; } + +typedef struct dpapimk +{ + u32 context; + + u32 SID[32]; + u32 SID_len; + u32 SID_offset; + + /* here only for possible + forward compatibiliy + */ + // u8 cipher_algo[16]; + // u8 hash_algo[16]; + + u32 iv[4]; + u32 contents_len; + u32 contents[128]; + +} dpapimk_t; + +typedef struct dpapimk_tmp_v2 +{ + u64 ipad64[8]; + u64 opad64[8]; + u64 dgst64[16]; + u64 out64[16]; + + u32 userKey[8]; + +} dpapimk_tmp_v2_t; + +static const char *SIGNATURE_DPAPIMK = "$DPAPImk$"; + +u64 module_tmp_size (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED const user_options_t *user_options, MAYBE_UNUSED const user_options_extra_t *user_options_extra) +{ + const u64 tmp_size = (const u64) sizeof (dpapimk_tmp_v2_t); + + return tmp_size; +} + +u64 module_esalt_size (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED const user_options_t *user_options, MAYBE_UNUSED const user_options_extra_t *user_options_extra) +{ + const u64 esalt_size = (const u64) sizeof (dpapimk_t); + + return esalt_size; +} + +u32 module_pw_max (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED const user_options_t *user_options, MAYBE_UNUSED const user_options_extra_t *user_options_extra) +{ + const u32 pw_max = PW_MAX; + + return pw_max; +} + +int module_hash_decode (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED void *digest_buf, MAYBE_UNUSED salt_t *salt, MAYBE_UNUSED void *esalt_buf, MAYBE_UNUSED hashinfo_t *hash_info, const char *line_buf, MAYBE_UNUSED const int line_len) +{ + u32 *digest = (u32 *) digest_buf; + + dpapimk_t *dpapimk = (dpapimk_t *) esalt_buf; + + token_t token; + + token.token_cnt = 10; + + token.signatures_cnt = 1; + token.signatures_buf[0] = SIGNATURE_DPAPIMK; + + // signature + token.len[0] = 9; + token.attr[0] = TOKEN_ATTR_FIXED_LENGTH + | TOKEN_ATTR_VERIFY_SIGNATURE; + + // version + token.len_min[1] = 1; + token.len_max[1] = 1; + token.sep[1] = '*'; + token.attr[1] = TOKEN_ATTR_VERIFY_LENGTH + | TOKEN_ATTR_VERIFY_DIGIT; + + // context + token.len_min[2] = 1; + token.len_max[2] = 1; + token.sep[2] = '*'; + token.attr[2] = TOKEN_ATTR_VERIFY_LENGTH + | TOKEN_ATTR_VERIFY_DIGIT; + + // sid + token.len_min[3] = 10; + token.len_max[3] = 60; + token.sep[3] = '*'; + token.attr[3] = TOKEN_ATTR_VERIFY_LENGTH; + + // cipher + token.len_min[4] = 4; + token.len_max[4] = 6; + token.sep[4] = '*'; + token.attr[4] = TOKEN_ATTR_VERIFY_LENGTH; + + // hash + token.len_min[5] = 4; + token.len_max[5] = 6; + token.sep[5] = '*'; + token.attr[5] = TOKEN_ATTR_VERIFY_LENGTH; + + // iterations + token.len_min[6] = 1; + token.len_max[6] = 6; + token.sep[6] = '*'; + token.attr[6] = TOKEN_ATTR_VERIFY_LENGTH + | TOKEN_ATTR_VERIFY_DIGIT; + + // iv + token.len_min[7] = 32; + token.len_max[7] = 32; + token.sep[7] = '*'; + token.attr[7] = TOKEN_ATTR_VERIFY_LENGTH + | TOKEN_ATTR_VERIFY_HEX; + + // content len + token.len_min[8] = 1; + token.len_max[8] = 6; + token.sep[8] = '*'; + token.attr[8] = TOKEN_ATTR_VERIFY_LENGTH + | TOKEN_ATTR_VERIFY_DIGIT; + + // content + token.len_min[9] = 0; + token.len_max[9] = 1024; + token.attr[9] = TOKEN_ATTR_VERIFY_LENGTH + | TOKEN_ATTR_VERIFY_HEX; + + const int rc_tokenizer = input_tokenizer ((const u8 *) line_buf, line_len, &token); + + if (rc_tokenizer != PARSER_OK) return (rc_tokenizer); + + const u8 *version_pos = token.buf[1]; + const u8 *context_pos = token.buf[2]; + const u8 *SID_pos = token.buf[3]; + const u8 *rounds_pos = token.buf[6]; + const u8 *iv_pos = token.buf[7]; + const u8 *contents_len_pos = token.buf[8]; + const u8 *contents_pos = token.buf[9]; + + /** + * content verification + */ + + const int version = hc_strtoul ((const char *) version_pos, NULL, 10); + const int contents_len = hc_strtoul ((const char *) contents_len_pos, NULL, 10); + + if (version == 1) + { + if (contents_len != 208) return (PARSER_SALT_LENGTH); + } + else if (version == 2) + { + if (contents_len != 288) return (PARSER_SALT_LENGTH); + } + else + { + return (PARSER_SALT_VALUE); + } + + if (contents_len != token.len[9]) return (PARSER_SALT_LENGTH); + + dpapimk->contents_len = contents_len; + + dpapimk->context = hc_strtoul ((const char *) context_pos, NULL, 10); + + // division by 4 should be fine because contents_len is either 208 or 288 + + for (u32 i = 0; i < dpapimk->contents_len / 4; i++) + { + dpapimk->contents[i] = hex_to_u32 ((const u8 *) &contents_pos[i * 8]); + + dpapimk->contents[i] = byte_swap_32 (dpapimk->contents[i]); + } + + // SID + + const int SID_len = token.len[3]; + + u8 SID_utf16le[128] = { 0 }; + + for (int i = 0; i < SID_len; i++) + { + SID_utf16le[i * 2] = SID_pos[i]; + } + + /* Specific to DPAPI: needs trailing '\0' while computing hash */ + + dpapimk->SID_len = (SID_len + 1) * 2; + + SID_utf16le[dpapimk->SID_len] = 0x80; + + memcpy ((u8 *) dpapimk->SID, SID_utf16le, sizeof (SID_utf16le)); + + for (u32 i = 0; i < 32; i++) + { + dpapimk->SID[i] = byte_swap_32 (dpapimk->SID[i]); + } + + // iv + + dpapimk->iv[0] = hex_to_u32 ((const u8 *) &iv_pos[ 0]); + dpapimk->iv[1] = hex_to_u32 ((const u8 *) &iv_pos[ 8]); + dpapimk->iv[2] = hex_to_u32 ((const u8 *) &iv_pos[16]); + dpapimk->iv[3] = hex_to_u32 ((const u8 *) &iv_pos[24]); + + dpapimk->iv[0] = byte_swap_32 (dpapimk->iv[0]); + dpapimk->iv[1] = byte_swap_32 (dpapimk->iv[1]); + dpapimk->iv[2] = byte_swap_32 (dpapimk->iv[2]); + dpapimk->iv[3] = byte_swap_32 (dpapimk->iv[3]); + + digest[0] = dpapimk->iv[0]; + digest[1] = dpapimk->iv[1]; + digest[2] = dpapimk->iv[2]; + digest[3] = dpapimk->iv[3]; + + salt->salt_buf[0] = dpapimk->iv[0]; + salt->salt_buf[1] = dpapimk->iv[1]; + salt->salt_buf[2] = dpapimk->iv[2]; + salt->salt_buf[3] = dpapimk->iv[3]; + + salt->salt_len = 16; + + // iter + + salt->salt_iter = hc_strtoul ((const char *) rounds_pos, NULL, 10) - 1; + + return (PARSER_OK); +} + +int module_hash_encode (MAYBE_UNUSED const hashconfig_t *hashconfig, MAYBE_UNUSED const void *digest_buf, MAYBE_UNUSED const salt_t *salt, MAYBE_UNUSED const void *esalt_buf, MAYBE_UNUSED const hashinfo_t *hash_info, char *line_buf, MAYBE_UNUSED const int line_size) +{ + const u32 *digest = (const u32 *) digest_buf; + + const dpapimk_t *dpapimk = (const dpapimk_t *) esalt_buf; + + u32 version = 2; + u32 context = dpapimk->context; + u32 rounds = salt->salt_iter + 1; + u32 contents_len = dpapimk->contents_len; + u32 SID_len = dpapimk->SID_len; + u32 iv_len = 32; + + u8 cipher_algorithm[8] = { 0 }; + u8 hash_algorithm[8] = { 0 }; + u8 SID[512] = { 0 }; + u8* SID_tmp; + + u32 *ptr_SID = (u32 *) dpapimk->SID; + u32 *ptr_iv = (u32 *) dpapimk->iv; + u32 *ptr_contents = (u32 *) dpapimk->contents; + + u32 u32_iv[4]; + u8 iv[32 + 1]; + + // convert back SID + + SID_tmp = (u8 *) hcmalloc ((SID_len + 1) * sizeof(u8)); + + for (u32 i = 0; i < (SID_len / 4) + 1; i++) + { + u8 hex[8] = { 0 }; + + u32_to_hex (byte_swap_32 (ptr_SID[i]), hex); + + for (u32 j = 0, k = 0; j < 8; j += 2, k++) + { + SID_tmp[i * 4 + k] = hex_to_u8 (&hex[j]); + } + } + + // overwrite trailing 0x80 + SID_tmp[SID_len] = 0; + + for (u32 i = 0, j = 0 ; j < SID_len ; i++, j += 2) + { + SID[i] = SID_tmp[j]; + } + + hcfree (SID_tmp); + + for (u32 i = 0; i < iv_len / 8; i++) + { + u32_iv[i] = byte_swap_32 (ptr_iv[i]); + + u32_to_hex (u32_iv[i], iv + i * 8); + } + + iv[32] = 0; + + u32 u32_contents[36]; + + u8 contents[288 + 1]; + + for (u32 i = 0; i < contents_len / 8; i++) + { + u32_contents[i] = byte_swap_32 (ptr_contents[i]); + + u32_to_hex (u32_contents[i], contents + i * 8); + } + + contents[288] = 0; + + if (contents_len == 288) + { + memcpy (cipher_algorithm, "aes256", strlen ("aes256")); + + memcpy (hash_algorithm, "sha512", strlen ("sha512")); + } + + const int line_len = snprintf (line_buf, line_size, "%s%d*%d*%s*%s*%s*%d*%s*%d*%s", + SIGNATURE_DPAPIMK, + version, + context, + SID, + cipher_algorithm, + hash_algorithm, + rounds, + iv, + contents_len, + contents); + + return line_len; +} + +void module_init (module_ctx_t *module_ctx) +{ + module_ctx->module_context_size = MODULE_CONTEXT_SIZE_CURRENT; + module_ctx->module_interface_version = MODULE_INTERFACE_VERSION_CURRENT; + + module_ctx->module_attack_exec = module_attack_exec; + module_ctx->module_benchmark_esalt = MODULE_DEFAULT; + module_ctx->module_benchmark_hook_salt = MODULE_DEFAULT; + module_ctx->module_benchmark_mask = MODULE_DEFAULT; + module_ctx->module_benchmark_salt = MODULE_DEFAULT; + module_ctx->module_build_plain_postprocess = MODULE_DEFAULT; + module_ctx->module_deep_comp_kernel = MODULE_DEFAULT; + module_ctx->module_dgst_pos0 = module_dgst_pos0; + module_ctx->module_dgst_pos1 = module_dgst_pos1; + module_ctx->module_dgst_pos2 = module_dgst_pos2; + module_ctx->module_dgst_pos3 = module_dgst_pos3; + module_ctx->module_dgst_size = module_dgst_size; + module_ctx->module_dictstat_disable = MODULE_DEFAULT; + module_ctx->module_esalt_size = module_esalt_size; + module_ctx->module_extra_buffer_size = MODULE_DEFAULT; + module_ctx->module_forced_outfile_format = MODULE_DEFAULT; + module_ctx->module_hash_binary_count = MODULE_DEFAULT; + module_ctx->module_hash_binary_parse = MODULE_DEFAULT; + module_ctx->module_hash_binary_save = MODULE_DEFAULT; + module_ctx->module_hash_binary_verify = MODULE_DEFAULT; + module_ctx->module_hash_decode_outfile = MODULE_DEFAULT; + module_ctx->module_hash_decode_zero_hash = MODULE_DEFAULT; + module_ctx->module_hash_decode = module_hash_decode; + module_ctx->module_hash_encode_status = MODULE_DEFAULT; + module_ctx->module_hash_encode = module_hash_encode; + module_ctx->module_hash_init_selftest = MODULE_DEFAULT; + module_ctx->module_hash_mode = MODULE_DEFAULT; + module_ctx->module_hash_category = module_hash_category; + module_ctx->module_hash_name = module_hash_name; + module_ctx->module_hash_type = module_hash_type; + module_ctx->module_hlfmt_disable = MODULE_DEFAULT; + module_ctx->module_hook12 = MODULE_DEFAULT; + module_ctx->module_hook23 = MODULE_DEFAULT; + module_ctx->module_hook_salt_size = MODULE_DEFAULT; + module_ctx->module_hook_size = MODULE_DEFAULT; + module_ctx->module_jit_build_options = MODULE_DEFAULT; + module_ctx->module_kernel_accel_max = MODULE_DEFAULT; + module_ctx->module_kernel_accel_min = MODULE_DEFAULT; + module_ctx->module_kernel_loops_max = MODULE_DEFAULT; + module_ctx->module_kernel_loops_min = MODULE_DEFAULT; + module_ctx->module_kernel_threads_max = MODULE_DEFAULT; + module_ctx->module_kernel_threads_min = MODULE_DEFAULT; + module_ctx->module_kern_type = module_kern_type; + module_ctx->module_opti_type = module_opti_type; + module_ctx->module_opts_type = module_opts_type; + module_ctx->module_outfile_check_disable = MODULE_DEFAULT; + module_ctx->module_outfile_check_nocomp = MODULE_DEFAULT; + module_ctx->module_potfile_disable = MODULE_DEFAULT; + module_ctx->module_potfile_keep_all_hashes = MODULE_DEFAULT; + module_ctx->module_pwdump_column = MODULE_DEFAULT; + module_ctx->module_pw_max = module_pw_max; + module_ctx->module_pw_min = MODULE_DEFAULT; + module_ctx->module_salt_max = MODULE_DEFAULT; + module_ctx->module_salt_min = MODULE_DEFAULT; + module_ctx->module_salt_type = module_salt_type; + module_ctx->module_separator = MODULE_DEFAULT; + module_ctx->module_st_hash = module_st_hash; + module_ctx->module_st_pass = module_st_pass; + module_ctx->module_tmp_size = module_tmp_size; + module_ctx->module_unstable_warning = MODULE_DEFAULT; + module_ctx->module_warmup_disable = MODULE_DEFAULT; +} diff --git a/tools/test_modules/m15300.pm b/tools/test_modules/m15300.pm index 917bc12f7..a7b892136 100644 --- a/tools/test_modules/m15300.pm +++ b/tools/test_modules/m15300.pm @@ -8,10 +8,10 @@ use strict; use warnings; -use Encode; +use Crypt::ECB qw (encrypt); use Digest::MD4 qw (md4); use Digest::SHA qw (sha1 hmac_sha1); -use Crypt::ECB qw (encrypt); +use Encode; sub module_constraints { [[0, 255], [-1, -1], [0, 27], [-1, -1], [-1, -1]] } diff --git a/tools/test_modules/m15900.pm b/tools/test_modules/m15900.pm new file mode 100644 index 000000000..d0a2a55c0 --- /dev/null +++ b/tools/test_modules/m15900.pm @@ -0,0 +1,412 @@ +#!/usr/bin/env perl + +## +## Author......: See docs/credits.txt +## License.....: MIT +## + +use strict; +use warnings; + +use Crypt::CBC; +use Crypt::ECB qw (encrypt); +use Digest::MD4 qw (md4); +use Digest::SHA qw (sha1 hmac_sha1 hmac_sha512); +use Encode; + +sub module_constraints { [[0, 255], [-1, -1], [0, 27], [-1, -1], [-1, -1]] } + +sub get_random_dpapimk_salt +{ + my $version = shift; + + my $salt_buf = ""; + + my $context = random_number (1, 2); + + my $cipher_algo = ""; + + my $hash_algo = ""; + + my $iterations; + + my $SID = sprintf ('S-15-21-%d-%d-%d-%d', + random_number (400000000,490000000), + random_number (400000000,490000000), + random_number (400000000,490000000), + random_number (1000,1999)); + + my $cipher_len = 0; + + if ($version == 1) + { + $iterations = random_number (4000, 24000); + + $cipher_algo = "des3"; + + $hash_algo = "sha1"; + + $cipher_len = 208; + } + elsif ($version == 2) + { + $iterations = random_number (8000, 17000); + + $cipher_algo = "aes256"; + + $hash_algo = "sha512"; + + $cipher_len = 288; + } + + my $iv = random_bytes (16); + $iv = unpack ("H*", $iv); + + $salt_buf = $version . '*' . + $context . '*' . + $SID . '*' . + $cipher_algo . '*' . + $hash_algo . '*' . + $iterations . '*' . + $iv . '*' . + $cipher_len . '*'; + + return $salt_buf; +} + +#Thanks to Jochen Hoenicke +# (one of the authors of Palm Keyring) +# for these next two subs. +sub dpapi_pbkdf2 +{ + my ($password, $salt, $iter, $keylen, $prf) = @_; + my ($k, $t, $u, $ui, $i); + $t = ""; + for ($k = 1; length ($t) < $keylen; $k++) + { + $u = $ui = &$prf ($salt.pack ('N', $k), $password); + for ($i = 1; $i < $iter; $i++) + { + # modification to fit Microsoft + # weird pbkdf2 implementation... + $ui = &$prf ($u, $password); + $u ^= $ui; + } + $t .= $u; + } + return substr ($t, 0, $keylen); +} + +sub module_generate_hash +{ + my $word_buf = shift; + my $salt_buf = shift; + my $dpapimk_salt = shift // get_random_dpapimk_salt (2); + my $cipher = shift; + + my @salt_arr = split ('\*', $dpapimk_salt); + + my $version = $salt_arr[0]; + my $context = $salt_arr[1]; + my $SID = $salt_arr[2]; + my $cipher_algorithm = $salt_arr[3]; + my $hash_algorithm = $salt_arr[4]; + my $iterations = $salt_arr[5]; + my $salt = pack ("H*", $salt_arr[6]); + my $cipher_len = $salt_arr[7]; + + # intermediate values + + my $user_hash; + my $user_derivationKey; + my $encKey; + my $expected_hmac; + my $cleartext; + + if ($context == 1) + { + $user_hash = sha1 (encode ("UTF-16LE", $word_buf)); + } + elsif ($context == 2) + { + $user_hash = md4 (encode ("UTF-16LE", $word_buf)); + } + + $user_derivationKey = hmac_sha1 (encode ("UTF-16LE", $SID . "\x00"), $user_hash); + + my $hmacSalt = random_bytes (16); + my $last_key = random_bytes (64); + + if ($version == 1) + { + $encKey = hmac_sha1 ($hmacSalt, $user_derivationKey); + $expected_hmac = hmac_sha1 ($last_key, $encKey); + + # need padding because keyLen is 24 and hashLen 20 + $expected_hmac = $expected_hmac . random_bytes (4); + } + elsif ($version == 2) + { + $encKey = hmac_sha512 ($hmacSalt, $user_derivationKey); + $expected_hmac = hmac_sha512 ($last_key, $encKey); + } + + $cleartext = $hmacSalt . $expected_hmac . $last_key; + + my $derived_key; + my $key; + my $iv; + + my $pbkdf2; + + if ($version == 1) + { + $derived_key = dpapi_pbkdf2 ($user_derivationKey, $salt, $iterations, 32, \&hmac_sha1); + } + elsif ($version == 2) + { + $derived_key = dpapi_pbkdf2 ($user_derivationKey, $salt, $iterations, 48, \&hmac_sha512); + } + + if (defined $cipher) + { + $cipher = pack ("H*", $cipher); + + my $computed_hmac = ""; + + if ($version == 1) + { + $key = substr ($derived_key, 0, 24); + $iv = substr ($derived_key, 24, 8); + + my $p1 = Crypt::ECB->new ({ + key => substr ($key, 0, 8), + cipher => "DES", + literal_key => 1, + header => "none", + keysize => 8, + padding => "null", + }); + + my $p2 = Crypt::ECB->new ({ + key => substr ($key, 8, 8), + cipher => "DES", + literal_key => 1, + header => "none", + keysize => 8, + padding => "null", + }); + + my $p3 = Crypt::ECB->new ({ + key => substr ($key, 16, 8), + cipher => "DES", + literal_key => 1, + header => "none", + keysize => 8, + padding => "null", + }); + + # let's compute a 3DES-EDE-CBC decryption + + my $out1; + my $out2; + my $out3; + my $expected_cleartext = ""; + + # size of cipherlen is 104 bytes + for (my $k = 0; $k < 13; $k++) + { + $out1 = $p3->decrypt (substr ($cipher, $k * 8, 8)); + $out2 = $p2->encrypt ($out1); + $out3 = $p1->decrypt ($out2); + + $expected_cleartext .= substr ($out3, 0, 8) ^ $iv; + + $iv = substr ($cipher, $k * 8, 8); + } + + $last_key = substr ($expected_cleartext, length ($expected_cleartext) - 64, 64); + $hmacSalt = substr ($expected_cleartext, 0, 16); + $expected_hmac = substr ($expected_cleartext, 16, 20); + + $encKey = hmac_sha1 ($hmacSalt, $user_derivationKey); + $computed_hmac = hmac_sha1 ($last_key, $encKey); + + $cleartext = $expected_cleartext; + + if (unpack ("H*", $expected_hmac) ne unpack ("H*", $computed_hmac)) + { + $cleartext = "0" x 104; + } + } + elsif ($version == 2) + { + $key = substr ($derived_key, 0, 32); + $iv = substr ($derived_key, 32, 16); + + my $aes = Crypt::CBC->new ({ + key => $key, + cipher => "Crypt::Rijndael", + iv => $iv, + literal_key => 1, + header => "none", + keysize => 32, + padding => "null", + }); + + my $expected_cleartext = $aes->decrypt ($cipher); + + $last_key = substr ($expected_cleartext, length ($expected_cleartext) - 64, 64); + $hmacSalt = substr ($expected_cleartext, 0, 16); + $expected_hmac = substr ($expected_cleartext, 16, 64); + + $encKey = hmac_sha512 ($hmacSalt, $user_derivationKey); + $computed_hmac = hmac_sha512 ($last_key, $encKey); + + $cleartext = $expected_cleartext; + + if (unpack ("H*", $expected_hmac) ne unpack ("H*", $computed_hmac)) + { + $cleartext = "0" x 144; + } + } + } + + if ($version == 1) + { + $key = substr ($derived_key, 0, 24); + $iv = substr ($derived_key, 24, 8); + + my $p1 = Crypt::ECB->new ({ + key => substr ($key, 0, 8), + cipher => "DES", + literal_key => 1, + header => "none", + keysize => 8, + padding => "null", + }); + + my $p2 = Crypt::ECB->new ({ + key => substr ($key, 8, 8), + cipher => "DES", + literal_key => 1, + header => "none", + keysize => 8, + padding => "null", + }); + + my $p3 = Crypt::ECB->new ({ + key => substr ($key, 16, 8), + cipher => "DES", + literal_key => 1, + header => "none", + keysize => 8, + padding => "null", + }); + + # let's compute a 3DES-EDE-CBC encryption + + # compute first block + my $out1 = $p1->encrypt (substr ($cleartext, 0, 8) ^ $iv); + my $out2 = $p2->decrypt ($out1); + my $out3 = $p3->encrypt ($out2); + + $cipher = substr ($out3, 0, 8); + + # size of cipherlen is 104 bytes + for (my $k = 1; $k < 13; $k++) + { + $iv = $out3; + + $out1 = $p1->encrypt (substr ($cleartext, $k * 8, 8) ^ $iv); + $out2 = $p2->decrypt ($out1); + $out3 = $p3->encrypt ($out2); + + $cipher .= substr ($out3, 0, 8); + } + } + else + { + $key = substr ($derived_key, 0, 32); + $iv = substr ($derived_key, 32, 16); + + my $aes = Crypt::CBC->new ({ + key => $key, + cipher => "Crypt::Rijndael", + iv => $iv, + literal_key => 1, + header => "none", + keysize => 32, + padding => "null", + }); + + $cipher = $aes->encrypt ($cleartext); + } + + my $tmp_hash = sprintf ('$DPAPImk$%d*%d*%s*%s*%s*%d*%s*%d*%s', + $version, + $context, + $SID, + $cipher_algorithm, + $hash_algorithm, + $iterations, + unpack ("H*", $salt), + $cipher_len, + unpack ("H*", $cipher)); + + return $tmp_hash; +} + +sub module_verify_hash +{ + my $line = shift; + + my ($hash, $word) = split (':', $line); + + return unless defined $hash; + return unless defined $word; + + my @tmp_data = split ('\$', $hash); + + my $signature = $tmp_data[1]; + + next unless ($signature eq 'DPAPImk'); + + my @data = split ('\*', $tmp_data[2]); + + next unless (scalar @data == 9); + + my $version = shift @data; + + next unless ($version == 1 || $version == 2); + + my $context = shift @data; + my $SID = shift @data; + my $cipher_algorithm = shift @data; + my $hash_algorithm = shift @data; + my $iteration = shift @data; + my $iv = shift @data; + my $cipher_len = shift @data; + my $cipher = shift @data; + + next unless (length ($cipher) == $cipher_len); + + if ($version == 1) + { + next unless ($cipher_len == 208); + } + elsif ($version == 2) + { + next unless ($cipher_len == 288); + } + + my $dpapimk_salt = substr ($hash, length ('$DPAPImk$')); + + my $word_packed = pack_if_HEX_notation ($word); + + my $new_hash = module_generate_hash ($word_packed, undef, $dpapimk_salt, $cipher); + + return ($new_hash, $word); +} + +1;