From b0cf3b7958be819b9f06bc8dc217b42aef7e4f5a Mon Sep 17 00:00:00 2001 From: Dhiru Kholia Date: Thu, 13 Aug 2026 13:30:28 +0530 Subject: [PATCH] dmg: support AES-encrypted v2 key blobs Recent macOS releases can wrap v2 DMG key blobs with AES-192-CBC (CSSM_ALGID_AES) instead of 3DES. Teach both extractors to recognize this parameter set and emit a compact $dmg$3 hash, while rejecting unknown blob encryption combinations. Add CPU and OpenCL verification by decrypting the final key-blob block and checking the CKIE marker and PKCS#7 padding. Include a known-password test vector and retain existing v1/v2 behavior. --- run/dmg2john.py | 29 +++++- run/opencl/dmg_kernel.cl | 35 ++++++- src/dmg2john.c | 38 ++++++++ src/dmg_common.h | 7 ++ src/dmg_common_plug.c | 158 ++++++++++++++++++++++++++++++- src/dmg_fmt_plug.c | 191 ++++++++++++++------------------------ src/opencl_dmg_fmt_plug.c | 152 +++++++----------------------- 7 files changed, 365 insertions(+), 245 deletions(-) diff --git a/run/dmg2john.py b/run/dmg2john.py index f3a61a0adb6..539b9e9ceb0 100755 --- a/run/dmg2john.py +++ b/run/dmg2john.py @@ -92,8 +92,8 @@ def process_file(filename): return data = struct.unpack(v2_header_fmt, data) - (sig, version, enc_iv_size, _, _, _, _, - unk5, uuid, blocksize, datasize, dataoffset, filler1, + (sig, version, enc_iv_size, _, _, data_enc_key_bits, _, + hmac_key_bits, uuid, blocksize, datasize, dataoffset, filler1, kdf_algorithm, kdf_prng_algorithm, kdf_iteration_count, kdf_salt_len, kdf_salt, blob_enc_iv_size, blob_enc_iv, blob_enc_key_bits, blob_enc_algorithm, blob_enc_padding, @@ -111,6 +111,31 @@ def process_file(filename): "is too long!\n" % filename) return + legacy_blob = (blob_enc_algorithm == 17 and blob_enc_key_bits == 192 and + blob_enc_mode == 6 and blob_enc_padding == 7) + aes_blob = (blob_enc_algorithm == 0x80000001 and + blob_enc_key_bits == 192 and blob_enc_mode == 6 and + blob_enc_padding == 7 and encrypted_keyblob_size == 64 and + kdf_salt_len <= 20 and + data_enc_key_bits == 256 and hmac_key_bits == 160 and + kdf_algorithm == 103 and kdf_prng_algorithm == 0 and + kdf_iteration_count > 0) + if not legacy_blob and not aes_blob: + sys.stderr.write("%s uses unsupported blob encryption parameters " \ + "algorithm=%d key_bits=%d mode=%d padding=%d\n" % + (filename, blob_enc_algorithm, blob_enc_key_bits, + blob_enc_mode, blob_enc_padding)) + return + + if aes_blob: + sys.stdout.write("%s:$dmg$3*%d*%s*%d*%d*%s*%d::::%s\n" % + (os.path.basename(filename), kdf_salt_len, + hexlify(kdf_salt)[0:kdf_salt_len*2].decode("ascii"), + blob_enc_key_bits, encrypted_keyblob_size, + hexlify(encrypted_keyblob)[0:encrypted_keyblob_size*2].decode("ascii"), + kdf_iteration_count, filename)) + return + # read starting chunk(s) fd.seek(dataoffset + int(cno * 4096), 0) chunk1 = fd.read(data_size) diff --git a/run/opencl/dmg_kernel.cl b/run/opencl/dmg_kernel.cl index 25c82e65fc0..92ceed41a62 100644 --- a/run/opencl/dmg_kernel.cl +++ b/run/opencl/dmg_kernel.cl @@ -24,7 +24,8 @@ typedef struct { uint ivlen; uchar iv[32]; uint32_t encrypted_keyblob_size; - uint8_t encrypted_keyblob[32]; + uint8_t encrypted_keyblob[64]; + uint blob_enc_keybits; uint len_wrapped_aes_key; uchar wrapped_aes_key[296]; uint len_hmac_sha1_key; @@ -136,6 +137,25 @@ INLINE int check_v2hash(const uchar *derived_key, return 0; } +INLINE int check_v3hash(const uchar *derived_key, + MAYBE_CONSTANT dmg_salt *salt, __local aes_local_t *lt) +{ + AES_KEY aes_decrypt_key; aes_decrypt_key.lt = lt; + uchar last_block[16]; + int i; + + AES_set_decrypt_key(derived_key, salt->blob_enc_keybits, &aes_decrypt_key); + memcpy_mcp(last_block, &salt->encrypted_keyblob[48], sizeof(last_block)); + AES_decrypt(last_block, last_block, &aes_decrypt_key); + for (i = 0; i < 16; i++) + last_block[i] ^= salt->encrypted_keyblob[32 + i]; + + return last_block[4] == 'C' && last_block[5] == 'K' && + last_block[6] == 'I' && last_block[7] == 'E' && + /* 9 = 4-byte prefix + "CKIE" (4) + null (1); remaining 7 bytes are PKCS#7 padding */ + !last_block[8] && check_pkcs_pad(last_block, 16, 16) == 9; +} + __kernel void dmg_final_v1(MAYBE_CONSTANT dmg_salt *salt, __global dmg_out *out) @@ -160,3 +180,16 @@ void dmg_final_v2(MAYBE_CONSTANT dmg_salt *salt, out[gid].cracked = check_v2hash((uchar*)dk, salt, <); } + +__kernel +void dmg_final_v3(MAYBE_CONSTANT dmg_salt *salt, + __global dmg_out *out) +{ + __local aes_local_t lt; + uint gid = get_global_id(0); + uint dk[OUTLEN / 4]; + + memcpy_gp(dk, out[gid].dk, OUTLEN); + + out[gid].cracked = check_v3hash((uchar*)dk, salt, <); +} diff --git a/src/dmg2john.c b/src/dmg2john.c index 3ec16b755be..c4d25aa8f2d 100644 --- a/src/dmg2john.c +++ b/src/dmg2john.c @@ -270,6 +270,7 @@ static void hash_plugin_parse_hash(char *in_filepath) printf("*%u::::%s\n", header.kdf_iteration_count, filename); } else { cencrypted_v2_key_header_pointer header_pointer; + int aes_blob = 0; int password_header_found = 0; if (lseek(fd, 0, SEEK_SET) < 0) { @@ -342,6 +343,31 @@ static void hash_plugin_parse_hash(char *in_filepath) v2_password_header_byteorder_fix(&v2_password_header); + aes_blob = v2_password_header.blob_enc_algo == 0x80000001 && + v2_password_header.blob_enc_keybits == 192 && + v2_password_header.blob_enc_mode == 6 && + v2_password_header.blob_enc_padding == 7 && + v2_password_header.keyblobsize == 64 && + v2_password_header.salt_size <= 20 && + v2_password_header.algorithm == 103 && + v2_password_header.prngalgo == 0 && + v2_password_header.itercount > 0 && + header2.keyBits == 256 && + header2.prngkeysize == 160; + + if (!aes_blob && (v2_password_header.blob_enc_algo != 17 || + v2_password_header.blob_enc_keybits != 192 || + v2_password_header.blob_enc_mode != 6 || + v2_password_header.blob_enc_padding != 7)) { + fprintf(stderr, "%s uses unsupported blob encryption parameters " + "algorithm=%u key_bits=%u mode=%u padding=%u\n", filename, + v2_password_header.blob_enc_algo, + v2_password_header.blob_enc_keybits, + v2_password_header.blob_enc_mode, + v2_password_header.blob_enc_padding); + goto bailout; + } + // Allocate the keyblob memory if (v2_password_header.keyblobsize > 1024) { fprintf(stderr, "Unusual keyblobsize found in %s\n", filename); @@ -386,6 +412,18 @@ static void hash_plugin_parse_hash(char *in_filepath) goto bailout; } + if (aes_blob) { + replace(name, ':', ' '); + printf("%s:$dmg$3*%u*", name, v2_password_header.salt_size); + print_hex(v2_password_header.salt, v2_password_header.salt_size); + printf("*%u*%u*", v2_password_header.blob_enc_keybits, + v2_password_header.keyblobsize); + print_hex(v2_password_header.keyblob, v2_password_header.keyblobsize); + printf("*%u::::%s\n", v2_password_header.itercount, filename); + free(v2_password_header.keyblob); + goto bailout; + } + fprintf(stderr, "%s (DMG v%d) successfully parsed, iterations " "count %u\n", name, headerver, v2_password_header.itercount); diff --git a/src/dmg_common.h b/src/dmg_common.h index 3890d7393db..05ccaafd16d 100644 --- a/src/dmg_common.h +++ b/src/dmg_common.h @@ -5,4 +5,11 @@ * modification, are permitted. */ +#include "formats.h" + +#define FORMAT_TAG "$dmg$" +#define FORMAT_TAG_LEN (sizeof(FORMAT_TAG) - 1) + extern struct fmt_tests dmg_tests[]; + +int dmg_valid(char *ciphertext, struct fmt_main *self); diff --git a/src/dmg_common_plug.c b/src/dmg_common_plug.c index cfd77455d00..450cb0fac27 100644 --- a/src/dmg_common_plug.c +++ b/src/dmg_common_plug.c @@ -5,7 +5,10 @@ * modification, are permitted. */ -#include "formats.h" +#include "arch.h" +#include "misc.h" +#include "common.h" +#include "dmg_common.h" struct fmt_tests dmg_tests[] = { // testimage.AES-256.64k.header_v2.dmg @@ -100,6 +103,8 @@ struct fmt_tests dmg_tests[] = { "1261fe5720eff029a9c1a0341951247f2a35266e7dedf0d669be2a5de6b3eeccb4069317ce6b40bfda453818adf9c66bbbdd0e93f76d352cdc080ef11ffc39d7b4437f78d6501b89b47575b3040c4b7e430f9772a3b1f92de2ac2c918e28e165cf7d5133e4797e6dcca5fbe56579ca79ce47cd355bf6c389c51d02203d7c1784f175eff854fbec80fb85db5ffb899921da97ac07e911321d679e23bd11c083c1136b59ba1c2e8a5126ba32979116fa7e724f79c34e3b31130c2a04857d5a9d8f0ec5c7b75d529381163635b295eb31b429f062bfe714bb5b671f6834e76769a5d0cbd101b6f9d27354a711f85e7066e817fcbeeef9e6ed6e8c23c243335e54cd43a27c74f9256825b21ceaf2d23bdc8f79e18d8dc32011413c54eb9e78a9bfd7b97fe1d3bf73c880ed58c387a3bcd8db31d7d03f9c298b24d9d0bea71a644c8b27402c94fc3d52e68971a4d7657dc5b6b06cffdf8590e909c4215d6e9644954577179e089e3a966b2295e7da1a877eb5b51d0deae6113da8217e735b835e6ef8c0fe0711c8cbe96f4d02f16d689ee6309d3a78dd4d81dfa83d2ba62777217862b598f70b8809e35a7745523eaa182c347225b8bb05f91fd099ed303ab5ee798534f331a12abeed4fea91b75342fec2a82030667037e03a910e97e8afb8845a81dcf401c1cb7e6aa8646dba60682bbf779d2386292f5c3e97f958a48d2ec20a6da1ea5b42f8c726705a20ed2029e6ac05c1a6b9bfa746df0a259c7dd74170ae37a1965560ef20779f09a37acc10eceaf6cd055" "31f19386a0cc043cc7051dbe1adb24f2911242d3a08861eae5f97be39df33f608d857063a9e5f997703ab11fe29e3a5c9ebefa4f3529fb49c1056e6eca75d3cc11b2bd8fa66908c662819bfae2ae02ca5f38a1586abda26dffcd474544180c33678dbb483dc5253b59120e83d3326195541566334f44588a644addb472b0f6ab746995be1d4caae8aae55258371fae4609a12f550145ff28a1a2b34d66af8a06e9927d6059765f22b309664c809f557e71627e0777cc9343d9c5a882aac3979929d0ed48d2591d8656159fb9adc2daa501d104f9b7361ee2b9451565ce235a04f7ceb9d4ecf081f019912147bf6f0eb19b4dcf2af20121c2be6b475395fcaaeac4d71c664e29b7918da2dc306aa6cc6b680b7bb3dc5aafae0a68dfdbd1b1860a4797996bf6af4077e1f7c6104775e5b36960641982f401d12ef8da50385fbb2e8137d2a1513e154f227bc7f8349206fa02de6557623b455136bf1f09a0acf83836af40b2578db3ccb75b8b22741e30d4e69b5afe0572656ce0fef67dd7b151c3b23207e86d3ba05fb1a63d1034b992da26f8072624cad1690d2d87a18ea9d80e6432c5b69ebbe08848b1f96bd60fa29b357f8a27aa393ea9cbe34d020e7c1067dccf0382d417d9b0fe4367737e1b51ad56cdb7604b82f31a7433f6aa92e7478e92a17ab435f03ff7f2ac7e35c41db1a4e1e4ad74a9cc00600f5abe0fcaba89cb0a1dc50b3c6a75926b9032a0f42c700f088b74b6ba4b0c97931d0db524a145c5535a086a52d3741e7af701d68009aa5df513c" "fdc4f8d423cb3d954da66f650435016bc4ad0f2f0c4a00e8bd2bde44e98f4424c8247cdb3c8f11d18fea5fdcacdf05f62815e478ec4e95d1bed4d2e8fe99c23ad46fedf531713b88d81b02c47f348c8c2d2dfba17c2fab7f98518f6c3331b6d3d3c7684d43a9e6d99b0a6b19630eb5fb92c33412a386e8cd5dc85421ae1e06eadcb421c747c3e491d9ee978c14697e623fad52a92e6d686e652efaebd79f562ab6ccffcf035b47949931d6f7892627e6522aa349517061b621ec1e13efaaf79cd97ec00e64b8da52769a2513446dcce8c47a513f9414e7ab1b1b886c95cd718c472eea30f413b4c0b1591f26c8cb42c9eb27f87a9f5a97e5970df8f4466a53bd410bf2ad4f92d92682d302482fd1bee67e963f5f373813feccb9f4ff468e46972a708bf6925501defde950acc917f27da3a0257af8e2701918390e3d701f83f32f91f7be893b6ffaa7e2d78247e4ef7849f5d75102e37f4c5686ac8b73dfb1bd6207dc70054b6b54090bbb2db8446626ae54e9d08fa0b888fcf7bb6b72423c834fd5f74984a38d9da7b1820d2d6616*181818","1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij"}, + // $dmg$3: macOS DMG v2 disk image whose key blob is AES-192-CBC wrapped (recent macOS) + {"$dmg$3*20*dda90b3cadb0cfa4e6b17b935c7f415ecb3d4947*192*64*89669cb0994d0bba4d7bdd5fb8551ae0d641a9427cb6903b9546f38c0053c2456799d990a76007f6a7aa3c59fae69889d79df480230d21faaf7a65ab26e45151*400000", "d3fc0n"}, #if DEBUG {"$dmg$2*20*a631b96b5fcb8047cb84e3b5dab3f0a0f305eed6*32*52a7e3147d29e3a2000000000000000000000000000000000000000000000000*64*bd454b86fccfb4971cd5ae9ca8a828ba2a03f17bfdc9b4155b3089298ce82a778ed21ad311596a99a682d867adf9e5ca06074a59ad8044b505e269e13092facd*1*8192*0eb194415c5cb6a0a82867714f20147a208e651ee5514d3f4416b9fb421b3bef969df766105e472bb41b43844abbc4bfabf773108b9748808181256eda4d9f7f6397cc0ed5ced194ec0d96cf30a9277cc81e1a2385e87d0b2138625b38209e6d8ec04f36f4c2be64d966f1c39cb7979b78a6678a651415b8199d09ce720291c0420f3822372de7005f99e185c4712f3d4f0f3c1366988857ccc457664cc2f43d8562e7c8712f1eba325740bea5f0ec1be2b4596c623bfe14ca38024e7e0be394b101d373219287c8028af0ce6ffcf9e50d0d400502c821a4a7aa943d74df01e50ca660e867937842c8e643fd847e3b22a1b3779ea6edbda497cc81a311bf0375ea44dbd88f3e2b0c70205588713eb1818af205ea313a8c760af4712101ba6433cbc4f7ac290db1a7fa7ec6813e678515e652a5eb8ca7a275a7fdec478ffbb9235d73738681d1891a25df504ea484e514a6c57410484ccf90086f4ffdc61d6bee3be4aa819e9affd6be8a8ccef8289ef7191e9b159d22b5dc5fad09d12bb715466d1fb311201928034778507275717c52794b1c145181890060102dd72977b1b19a53ad98b07feb27d95cfb9911cf0a653eb6f2eae9a3cad2f2a9b26e05a3f73e357a4bd4808b8a0690ed343f1355eef156b8b241e14acd6d6b00e6f7d780c1580f2df02db34a8b368557767d06386bebf792d25ba862d2489de3e6443540929f2637ee396cb7bbdd0cd57a5597b3bf2ebc963daddce7836cd21c6a812d728faa44c127886ba79ca6998b6b669eb2ea4a11f75cda2972d8b8e9a5ea5106eaccb9a75e47a4c869513f4ef3c81769cd286fdd37155d1ff9d3deed7ae1960e95b2af6456aa87355823bc698ec0600155f361ba9bb904b7230e43d74d83da414a2e52a291a9bca5058a0fb65dd831ed4374f998b82849039bbafcd73895ec63809caf503649c56494bb9b01185c6d4bc88cde35aaab25df3e6e7327f8f77a3365b173e9a4c96f0df2dee033cc3d5b0adea30dd90f59b75a08a9ca31f73ecef48bba0c2870addcab061974d88d3ed46f4b2c22588e55f1b0413b2cf5605e0e37c2246e1394d0c2e7e3a8b412d531c012f26274cafae07ca18f65fe10dd61e54c31edfcfa61f3ae1ee3b4b4de318b5dc43849c732ebeaf86a63eddb5ec5a81aa08f95c2d97dcb46c9a25d5b4a1c37ece800164737f91aaab4ed5fd5f63de974dd51775840db0be361772199118e53b001608ae389573cf8678a41e5cd7c781e6326bbdde034982e728d1c2c3d3c3ec1a3726d24c81817fffc69353bee17e443ce55fba840f8c114e300e73742d96346093994bafa4293ea1911755dce34fffea6b4b5248be56687d5a9b84adecbd46302fea488a48e22536f999156309d11ec008dc8886ad0f3661b7e7d9ef59e89bc9787c789176d42c01a8a780d6fdf8dc151bfa4ec6b56d530a769a3365e47c333fa4930dbcb0ea2e3187c41c31587ba623a24608da90aeab55be6a014af29bfdc552c4a514b648e964e4eb5e6cd4c171edafa4c478768ffffdadab15ac55563f53c88d0bcfb5d28aa6a32af946fcc842fc4f275eb41edc36781bffb1295f49436ed96b594ebceaf41cf1149082565896fc66385dd725e518061acb904ff2064f3a2055611c45cf102335f0756272835f7e250b351e9020e22d90740658769e6c69b2ff2cd9cde4c6c8f8c6902b5d66d85a8706fecccb4ddc62801b994eaa7de38717b5fad8abd90d59b0dcf554b4ed705b680fb777eb02b67d1e0a0d52b04b896f8b71d4d2b22bc7ea99835b9dfe62597fe86ae89b72c58da87f26edec3180d0103ad5ba2586b3b7517739a1b3de0852a75473f35683e4d978245823a614e1c56ae901f6f89ac0bd44558cd3cca777a1948ef0425965dc0b6105fbf71b2354bd8f5101f150f76e6738b86842f253a29ba652e0a37269d67a877910ec7d435d18044566cceaa796c878566ff0c348a988e691fefddbe109c07f9e3b0b8b80c32165fe10f1c1ff91cd93f1ef7de5fd31df8762be797cb26851e4704f002be4ef23f79f0a081226fce49163e42f58abfc114a50f5973ca516093b8660466d750cc525a088bed3fa2f98955ceb6ecfcd94e079559a8a3af3d51816494d33cbdf8d7b91036ad0fd03a28ab98ccd60de76ffe20de4d6363f61777252e46a53af5d6d639cb2b2d78ab89b0b9ed3dcd54874b06d1c4704eedbb43069e7084b7cc5eed226cd8646af87563df3e0077738f9232d6483692aebba65a8f600ea7467718661c550efc21ce159f40d54b64f10ad6ab5ec0bd6453e7a422764494525d6bc95ff67fd0964a1b15f171127343a2a61b0fc63e93012f616b4382b29f70c954eddce6221e7fd3ef25d9271bad10ff14655ce9b62db39d4c2c5259df50c0ebef895095746bdaac2aaf294dad27985d6f220f6edf14b3e85fffa90e7463b546d32268a01afc212c87836f43caa73c5203b9a607a97a09270b1a1b2a4333f0d4c754a3260e5116cbdc9760dea1b1519a4785e398b5888b21b64ef2677bfdb6d96250439c7302136ff7c17d4e2d976d221caec7ca811f7cb73751e8437aa6fdcf68b6374d928100469a69eadc42bc1e2f1f93c8e12f875dbb1063b0e959e4ece77bbff44b49206e39a5866965b221d8fc78d1cff7e787579894c8c843b0ef9df919679f0ee364cf956cf875739931ad9f95a9eb67a39c789372d9074305e9c51298122b8a1cce7939cb3b89417160e2df62da72d0f9a2cbe372e9368f4204d1fea0296abbc05fdc97a212a97c84a60471b57e77ddd551c389fc3b78ee0bb4a2f4ee2267a043b2accb6117f589a247e45cce0e50f5281a19157139133c644499df06a9ffa2d9ef0068e109fa54d0c525f06ce9ab0c27cd8a0c777ef8a81fa7aab4de7992e35628d267232c621c287258513ad9faac8911d3c4c28d493faf1f96a500c92418195de6b5c767de6ff126e7164bc7f9adffe4fe2c4a1acae5406d6f719c34160f4f5139b54585defdc5bbabbdcff728490ec409f3d48bee0e646a31a90f0e38a7bfa4adfaf364bd1b948011a7a851b78c6ef353befc6b419ca10421b3f8d1bb0816e970087ed732370fec195bfd4c9c3d8fa779630c9a4f823390090313a69af6554c19bae30940428241147a068ddcde22efd04d80265f94735d5ca2559f59da33b485b8850ab50a1578ba4b912cefc655c6963c366fd6d7e6c1ce4bf1371dae0aa6535d81a03f0cbbfc27dae247b1eafd2eee627590500fa9cb794eda2b13206781de86ca89913b9f94677d539209f549fa75f03f1638c3935d48f09707dd164879bf343de79d772d959f65ba92c498d73bb9b3eb1680f7c2054b2b01b5553355f5ad807f1dd1d4b9dec069ee5362ba5a3331034e1cbec2f0090a53c6e342213e52bb66a2380c1a7f95ba469850f5962b9bd0b01d5182c099a05258373604739dcdfff3e46b0e74e7ba019dc307fd5e95aa6d9a9b77a804c3a911d4258c3e4b3eaefbd611b7028b7ca658cb2dd4204b33754e9cfc394e4bb32dd6a29cc2c93c70def987a0e8455c28ab1aad61b19c3eb151b8a7f3c1ca21f13743e4b8682066c274825a43d3e7df2495cb7557cb5a01c8d6ab9f554f9670c60f2da4279bee4bf7c965aedfec1b051069666681f07d6cb41dee47aece39f750eb15708cc59a687dce83f0cd8b60b7ea237096fc6b7c4a45f8ff1e0e220fe410414972a884c27035e53d75b55ef539873868797e6401c734096353ff7813b91c54bc051c36f123ba7111848fe8ed827e404066de065aa851e5afbf744c973434c929ac65e0365bcc150ef31207a75a496b51bf418eae69fd5263f233afcac59bd5b024bd4f3ce91cf78b7ee5da2948b5690410e85ea1d76c4ff55c8d2213ad4f3f9f08fffd6ed1ab9cc4eb99e28eb499deb7f5652d3335d0bfb0c580409e8b619458cfcfe028bc584deb95c0f8acd32d0130d35781c2b16c92b2cf1f02d97bac4aa5a93105452eef52946728e6a00d8c5cb19aaf6e86501fa6c83e692199e836e7f77afe1baeaa4bc5e2a7f39d33e8fdbbff2b438958de3c8321c59ee6920ca1bfa599d5bdacd4a87b41a66f269b46f455eae99b98d3b9ccda1956a4c9e6a94ab1f008562d5e348017a3ec3a5e4e47ad8a27ece0aa4d07ae969d185d135ab7ddf403ae2a34ce2a2a0af2ab1e70aa450cda6cc91617357321aac686e31db8a82b4b7f6ded3e41a5ed92ddb2c16bca680367dcd0131b888f300e585c86dad7270739b2b565de167890fab272c128f22ba90493db164f3e514413d06879f000834f153502026cb6c2802fca4614761ea363b80802a7daf1cff4b761bbafc3b3501853e5bc71789c10e7bcf2bdc3d5f9afa3582a482cd7ef96545bd266e28bdb74ec380bdcf9273433a02cdab3b84711b2e9e6de7450dd03178d85f96301a5a879de31c2b3afbf464b0d98511c426618f67f3f1f35024d3474fed2c3bdeaeea5cc325233e99e97d102968d0ce74c073fe280f848275bfaac2bd4c74a7aeff9d7969a2aabda99773869972f426f8f0e4c6770a598ab8762ca00b6b7bd9fb6ec0b61aed807cd934f5f9daa3ac457f04dc819802e0aea20e36d3bf8dce1bc7282d7691a6879e69f3c0e8229c1f6f07019da3520f3fa57104806fd7757ff3eacbcfdb57643170300df009ddf0875f47da7a32d5e5d1a5097e318759fab34ffb54b5796c32fb44e67517c6206d7fc8f8f4a2d0651431e2489ba435d7444d05312b0ef84cc349056e1d4f9ac096b7e164001749accad9849d800a44b7e30d4abd6116edf7395f96904944c4df949c8ddd7ea9c30c3359e80139fed92d0766bbe720302a348c98aa1a15ee28e3934ad0b6c70668d18126b9b0e390bb011c35090c5bcc4cab9d050cadf39e5de606184f1ce51bbaf6dd91ec76e7e4c5188966a01f24f91ac14acd88f1bd7339e3948874856dcbade686ae6c6a952565dd627631e0966a547aa0ee3dc2d88ad0d89b049e8c14782ed139510abafafa9cdd5e44b60e78b8e45c5daaaa92f1189f92ee489676ccf31e0669db58612282575ed6d670df4e79a2b5f9c28466eda9d1fb0da124b125324a4b356f4871131d33d3695e8e5d8d57ae88f8753bc4504395a10775d02068da7d1c95716920ceffa35d4546e045aac7858f9f2690bb6d7d1170db2c4536573ebe1d628ec68a33bd10a53593269c885444fc8a01f96263400128e66a8fbccd7d57882da607eec53f76da448ba5ce43ce8694087aad3294e381a9866ce1419484625175f1bbcc38be679d1e51b276025f835f1bcddc02beb1d08e79d27c6fabfb09c9a13bfb6b51f215a387853fd65d4c4db2291310d868762944d3b93fff741998b7308706de150b73e3a6c53b0697e414a0e5f1390d53f485c32733ab4216fc5035ef6ff5803366c0de98f6cfeb207b7a1392a2abb1520c9eb509a5bfe5949ef88ff9cbc5a730af9fcd496f01b48533af60601869d1f47496378ee3bce360d82acb70ba6789d89708acea75e5b19609e97b8e9133270ad4a729dd31d6487e64c771b565983223b438d8839b38d6caa27f7991689cfddb6201d3280b8ee3f12aaf1392d66a7b5b5c317a23904b5aa50dbe3a2cde9418ec853c145aa9739b813cf245e4ab0a4a07784d12d7bcb2f914ee71c80c45286373d5d79f4c215d1db275d37a8be8490737ab6e597df97dedf53c04387f5b53dce70c9b22191d0c6f5478ea9e1305660a1e363b22ff83d57ba00af4654e12d0839418e968464d876f9d6944b49c171d0b045be0971b5681d322dd8790e3fe04b2d0a6dd9aa976ff0b6fd7934a78742dda5df9da4e19c95b60082ad9f60db959ca1989a50951ab6c7ad00dbfc6130ca270b2284aa58ee9ab4e6db03049dfcafea27ab59f72c5db1589a8a032cfff44ba7546c506ac5f679f4123a8099824d724253d214087cb23347c2422a2b5f94c21bffdd5087875ab9bead8d3eefbf465df7e94f58d0e362c4774412754b030b4e4135b89f5f3d94e46822f834e31d29bbffa41868b08d7a3a368fdb73c5b26e2f73948dc3c1b006a74c047579ca1fa7b74eea439c8da90f5d4ed424911b513c8e8bdfdffa0adbbb2b44a3dcca3b50e2e12744b943051cc931252e70885693be70a02ce6f57b8fecb36f42b1f6661a0120498205833d87828e5d24768ccd17a3549a3ddb8a76e0121a1707170d897cedc7364ba5cb92a781c50221ae7ad5aa982b6a85547782fb440422f72e31e6d8f38c721e13ccc1da73ed96dd05f701c34132e04c6477a12bdb0048429cc2053aaff5ae498a89dd268b7d7b08142772471d41f15f8cef60c02d89014565b3e033e28cde445c436ebce3ff35c4774edbefa02aa5bb6666b3f32b94b600ca11c8db9b07e6caa23baf3e7ec40c76d0ce0bd7f60ad2c66c8cef7409b04edcbeb9acf18e7e10a7db47c648ee44ee1e7746ad923b855770d674ed1defcc43f3baaa3096d6e8f9700398549f7e996f72ead1d293a778427af0ba2057737691f5778f1377b3a81fa9f5b9376c5ff38e270b4ffa04b07f2b92b33b9a29ce71b8eadca0e321a1885f163f1ed7b63634ea5b9df05dd26fe8430898b111e88eab3487da01d591873be35ade54eb5bf088b77e8c33ac51c11262f3039c34fc4651635ea5b57f64b2af54aa1d0ea347b68d365f70b48c4fc7d0303bceafd3bccde9895e32b199921fc2673c4cce6d97cc1521f38f845138cf779d7d868e7539b2ad4a6a045f89059f17ed3b5d3c1829fa40001d9f17524e9e40f8e6f858e72df1a22deae05402175b15bcf08875bc66828fbd758d027633e9868b488414272ba4a847372976756b8673a7909f0011836955e387e9f08e055672b5a914af44e6c8afb567ace0ceb03b57372836f4479aa914b89fd7523701d15c9a9711a9a997e6c65663e36011e134b13cd266a1dbf3d0a5c27ffa08960bab2bfb4a8fcc115cb5e77906e18c1c2c05b9137581c53fa8640a1cef303937190047eb2ab5ba54fd6fc104d09d4a3b4fa6c1f0914988e3637a36d115d9725bec40aa8c52ca4c352d673b8f8701861d5dff7cecc8dab5e7b2e4c103baeb35eaef838d3bf97e2a13274376bcdf3ddfc5e4993a84ce49af65644438b26b80f207bf6205946c73d1b89f3df3189fd90733e0e75a3ad2f85ec31fa825b23a6dcf2f317cc2c14074716fb361ee0a536dabcd36023d171fd72ee1303faf144888432c436be335651ff6654472a9449199c2d0e0d6f7c50a0b083be6689abe149efa9986bbc3f377cdb63fc3e05636f809c3c72dd597971cf3c0410615cb01a41ac899989ce215aab96c8ed3bdf1cb42ba7cb2b5c0375f505b9a83bdc80c5d4411d8d737eca6625e6a334e78b7d4cb2937dbb9a5bbd6e0203252eedbe7a8a2bf3e20b54c42d61394fc1ab5337ed20969c7264daf7fc643d7e1616b30e4b70751a382444d076127717797b0c5745b811271f582df4ca11e91ba44cb52d6af7e92db0d028e577c4b8d86afd03661ccb6f27786cab1cc08a3abf9d5c9281540bf23ab52e1e44e44b08a6901e563764de6702537d2bd1bbdfa1d9a783ab370ff2bc8eacc9c435d800d7a4e53d422d3125b74584d7c1785b51eacb9f458023d23a971941600a9ca18de6eeb99f52062ffdca1fa850b8118cab3949707e545df8bcd3f1a64ea242d11bbd6ac65e9078891a43cdbb565907a0fa17a24302b278cd4a735afbdbbb256f1f8a32af4a7bc50398d80d512ec3acb8f55e92d14f01d542798db6b4f5de54d207d253bf1ffe2587d2a91acc83c9b65a0aed947148c3b6f36a2be0488e06823e88b17f264146b613b19e42135c99eca45295722a366804b57a5ef7864225418edeb27766b0ad4f96ede5e33a5bb49101dffd7543b8f1a61317616749e07cfd2aa3c4e9bb9d8c8d1a9958b6891b059287137b0381b719916c14768520e8b08aada1133d6a2ef481b97e3692dfa85ffe008622dd4eac58b942dd123174f9b872cef33bccfd99148bfea3e81ed68db19c45f62312d83b33f957a65bacd835766ed6ff238b801f40b0d38aaa52ff50d17a59846fd00bea075dc466f7997be4b613a4797b7ba2d55c497eb0f91a024fecca08cea4f287c2c8e14202d23a68d7d8e0ff56d08d59a131059880b331f39741aa9ddd1a7becbe314920dd370b3a5707f723e50722ab5b10ef58171d501f4032de0950660e04ff4f9ec4147edf249d9d26acbb1b30b03152317b48c5f6091aeab6918443fff8b57c50b0c5c32cf3669fbac11774b8ae4c3b8e0d9ae92fe66eed788b455c5f0426b19830adb5f5ef91a6674ad6f5c1c2a1b9f3f2f0de079bdf0cba52b0d54b710e437c1d3d53b4b9c79c61efb091646ffd132349887081967482b89aed32dbebd13b337c8dc0c8e74b925cc8449451e0f5d3d067b9750e40744203409c53db81ac9ba683ef0ee54131f45802783b2e54eefce453572f9530f0cee6a2e7efd3887a7a293fefbf7d40ebead39273ad4f19caf4ad227ac78ba5919494f2ed9abaf1eb4b947353a055dba8ce48c3f307419b515370b827e9eb8dd87d14bbba4ae264ba8725ea53ef786204e7dbf5f1adc080acb901b7223d026923f2ce2907fd2f5b0f8de553c3390c5db0258043def1c3f77566b2b99c44dbdc6c0576ed2a4c37599894b2deec40630b41127d40a773a8aa61a2f1067b14f88d1418f23d020311aa61d1792193b4ef9489ded443342bb7b396fc23dba3e64944a80e442de84a185b5a2c4061377485537c1ec2d32a86ed241b497de2f3a55e7bbc3e7093b79a06ed10307e12d8f93024850f1fd5641da7f02492f30c497d4eeba2fe463ff55dd315a0a7bcb22436a8cd25d784613ccb35e6687c96db3d0b93bf4a7a8816ef0b240f90b99c62dd21a11794330eb6a4a17e57ebd0b2820f59d098e63a655f013a5dfabb9eb2075211de8b6cf5d0b6a5420b277829695d5d5a79d7a11be26e1fcde2eab9defe7a6f0ac2bb14199f735aa61d7292b0de4e42b6bf17c5940424b79632325cdadaa1ca8722592eefb4cddcc93584cc7370eedbcc4e9d8b6acdcbd9ef1450242f222ab0d0310b674bd946cb5f2d2eeef7ac89d16412a0330aa079191329d4d62472491090c9416d7939cbd2514547698482e15abd3c2dc60fe69ed1a63700fccb08952861d2f881d5073a447c1b431bc984b385ea2199f6944e4f852a545c30d9f97b0686ab456b25bc0fd783d65131c78cc4b1cada8c0acba7457940409b99c69a5a542df3898d0d142b71a2198f2254ceb2bb8940e3c44cecd57823edd508e89b2c0f885d1059ff3d16fff4712f37013caacedbaadc160094d359d5c9de4f815b74b903d1deeb1b1f8ccff3faf32bd924b77dbdadbc6fa0fa90fe8a54749f371f95552382fac7248ced80634ae09a6efda178d49f19a189cf2931667240e583cad2c583f053fbf936d3328646e6de5c7b7f021c6248f3a5deb0a05e935274c7aeb286f367c97665e5e56fce48e2d68ea26f9cf86b28d9362a75ee99ca24a91b181112c78acaea300bc3611cdf418436f447b01e1102b5103d61e14d47da425854b244a494ec6cd3020e720fa65c35a59a4a0732009f7e500d553e0607e1495c7bc3184fd1bd8a219b39656cc52bb6cca8c7030eb279c232ea2bfdcae56bdc0f44ac2a69ab6d91cf475c8986e651635a507880e6730cb8697489a5d8bec420229a0da40b7b2097a4285f28c55d89a149a65eb90f3f149adef3a97e59cea7da4312237be4e315e66d9d7713230994bf2e1e1525cead992ce4258fa0f99786b37bc0dbce5157fafcc565d2a95d40f370fd1a971ca45c42607c210358e70d4b7da992c98e6fd80afc94be96600be97e8f0896e83c509119c4f366cc6f529da7ecc1695f8f5adf8ba4fd9efe119f2e5051086ff9fb61a869c647e225f08969b7851798f58058c4224b03de83e608d3efb52da2aa21fbe7040c6319a774c42beeca0368c1bf3f3deaa8a321421fe458e77b700f492458c64e108b30ef98fcb096e98385b23e95eba1d0f8e5eb33d6cb6239536d28f3ac991e0f59a4e72384ce3575eb49197c71bc368abb259aca55186d3169f6b5401d3beae1515018106b9caf1b97fc03b05f666918fd9f148b6afd9ed7b61abef2603b6b8a1c905b5b9ff8f7495dee66f15fb69639f8ab934037fc01a002695f987d86925070f0ba1dcc59420bffc36ab871d40b70555f9eecbfc956da992f11640a5b156b0a6e9714274110e40b40edd3c853e26f35539abc0324c35f69d76afc0eb52d42b825745ea902fe84e5df6d25895941e52f5680d60b65f2a56d191f177c7acd8be07bf07ddceae5a0704ee3077375bfd43b9545505210bdbb3aa0e982db4e7736e76caaf64b4a6cb4e1b77e4be1838cb95094fe4f24db7a804844ab6681311366296f5def2257205dd1f715cd1f6904b099962d10d3d26b6e90f5b6af43fc1b117f0a19625e667ae12710690cb8b7cb62ebb5fb541ba69883cb9d6e8349ee608103feaff24a7adf75b522c4753c40c0bec2d9d1c36ff668be6f17311df1c3da0fc047c67bd5a05afb68cd15d60ed96ae03a4945a722c8c893c435ed3a221206f2eedc1c7624ae0611b07cff9450043213264bef727e3e1bfe9917db9fae21f7d6a8f604bd6ea4a05d2e0fb129e2abab951e0ae9a7a4ea7f4157e35eaa5b8571a79216d6eb90b036ae34def1a0fbf327d835703a053ad3b57a37701cab3c5d282903544c0e18270db2fcae73a51686ca1f0a839debc8729c229fbf48bca35a3fa5d2e2ae09f6b4d403306ee7ce2e31da0499b862ff93d359ec97637a614160d32195087a3ab852844ce123838fa68ed3a1765d4cb07f77a2ae86afe9fe6155f7e88fffc53f40e3ca83dc7c1d1aaa527508155805d478f41bbdfd618498dc92c8c0cc329717d8065a8fa8e5303acc505f4b95361217748c55eb3d4e74089d2566fa152c5032d0bbe2c80add4be2c30a49d5564dbf89bff86aba910e4165e22df0cca9dc7c28bd7f8e0ac5eb315002f6da3d660eefc53b92e32075db7e847f44a281892706dde3ab42d406e59caa848890c5c8bc07f7e7735ac1b907c9fc0264e59077ccab96057802501a4c6540a85d12f4a28aaef5c260088beca0b359743e920ae108c6e586141081e61d1122b59e897ae2ffe3770d731637e1b995b69d1ef8db21d9c0c1407201c9a8fdc4da192ad907ceb0de4f6240e0258af921e749fbf5b55c22679df7f1f677ed815ef32f53d9a5b8888a4f5befc4311513958c826d8cc00e7bafbece759f727063d155afb88df0e857a68fa9be1a7cdeea6d60c807da80aeb76880ff056da69cd82e79cc1530e959564f22639f26481817b61b3a29bf526809a4a87b24c9af5a7f7db9d248a52cf6f39c5552547ef3c8c6dd6431edd12f6b80b7c62e71d86f64dbad4892324cc89b9cb990fd75bebfa744ad3b2cbbb2ec15aed0dae0e66fd46d6522f27f7efeddbfb46d6aa42b07310e087dc7a730eb42b7c560ae16208e96bb4dcbd82ae16aa7bbc599525dd8eab0c1c14de1cb219f26d06f2f20e52c7333ea6fd369292f8542be34d97605bb32c12a3148e89f0*1*9d2142d7e19d9308e91d4d8622042cebe1d84f92e16d45d949e50d362e8fde6742518f5bfb29a27875261f3d7285c3e42b1aa8b9346bc2b82715b4eab108588675f31a9c5939d0902fcd2c4702f226f3e01acea9741148a477ce73e5f48579405396f91c9af429ccc2d5f9ec2bede1a34e0753a45033615c703ed3b80b078e345a225d2cdfef90d30f12d73400f07b6fe4379bacb3f50ef49d0bcb0e616002e1396b9e4928cea21c93c165fb68eefb54da5c0161e865ad1925c25a9f49f6ec1a0c90d900dd3db86dc3f6d040382fd36cd6563cad9f75dea4c70672d44d0276bf600c5e2df5be6647d8c479ab43313ce75b7a88069c4295a876fd57f828db6bcde7d6292ff3eb3d56f860c1f1c4d84026e78f36943caaf805234cdbb60d75eca16785dcd86edcdb6bd9e6056eb3214c9e139d67cef0d02ad2702a5622ccbccd76c324693c87e27e9bbbec8a649845f6837a356de305f06805124110b0d844a0a47867aaa4e5c8b017edf1ba9dd1d52524f9243f9799b532d01dd9d7663f29b34d88270cea294dc034da869bace73cebeda348fa1bf6171c73f594b49eaa9e7ffe128b84f570a564ff986f0ea152f18a0a0a64a97640da68d23275f9da67f79be9b4819b694c6dc1a9c61dc06da50c9f319b8ab0cf68d27b6c781dee1394edd7cd206eb5fd680ad0b420e4f4c939a7e6b806e5f2e9d8f3600b5279ff3a84194447a8731ac6a295db227889a9c6fcf7a81a8ef3728b1ef064c6fb0729a5737212c0152b03de1ab65bf666db4b3cfa0ba9a8eec4f5240327647981de02684909e49c1db4f986c1021ea8de6527f43653268353553b52e2d4f0a835ff9c1259065f2120e02df5273515066cf6bc5dbdf0e62bbeb68f56bcfbaed1c9a4f8da566b255cd042f3cdccee6962f19f47adca456a4d661817a26611ff8a9ade59623fd3939452848020dce810f91125cc5615da7542ba948d179d80db62037b2fefa296643bf848f546d13e220be629f7fbb6b0d855f106b4614f224e388eb533cea3b1002d92f95283d35ce98b5958a956c0edbb5f4ddd9660c7f241af3a02a00495da1481b320c5d7d16447872644f29e5e845ae71303c1aab5e81c0d1a524b3ba87c341c0130c1a552526551a675f31001e06ccba407475334d997027f5562fc2d6a8410f1a0821aa87e013403b45df354ab793b87400e6440befc16287742aea2f16db7d89de126cf38918c1f0463880817c33f89833ab636da152b3b78cda7025fc294dd494a849ea078048a8edde4a65c21353e08a501caab6d5c76cc1db5ba17424c32ab91a55fc26cef226a4c2e03afd12b64b3956d8e1fa61f3b0729b077ffc022b73cf222744771f5d16090304845d9f3625fbe3904138266035b18d2481e9a410cd184ccdff30210c80497caf2fccd9ed96b86f14c896893ac494b8706c3ed3e56d7a8ef387e0e2690462d369e456ce1a221f9c0d9a7d9e473a63dadfa96e7f7a59455c5b00d91b24b4aa63b19aae347091fbd035521ace84622ed5dc91c61d325a3f04a681aae7104fd48b55af72bd1000627b219ae045e2e4229a2268d0b07ce57c36a4feee9c70779f39a75154c30de575ec35b43917ffb2b575e506c069aff87e60b821810fd86c75ded9af9a47964eed7a381e6a3b714f1d9e0d911781f5b5dfdbd257704a48afbc0f900497d0b6fa340add5c6254793a1b9bfd2f1504c2599102ad22b73f9b6005732d78fef6ece362c6458e6aff8a40a37437a6122ddb4d320214001dd8e0eefbd0cdfe4f20798c3b5f3b6830b69988a04c76bd48614e41f8a00d5120f120e4b567e8ee8e4640c12b75dab5bd4c28b631683d58067916ca99e11d7dd1a13365f27ef9933ed36905fa0567baba1aa321343123da55eda825775dc244ad5a3e30f683803a20ececf968ab1bb8c2739974317a43fcdbf8efd13bf4416afd932de1e67efaeec4b9a2b592adee0dc96befee8b47ad55982d47f3dc235318949c22d340c49b212f88fc0c205c947a0485fa1275c48e4f942b144cfc8c8e9baab07081786ee958eb11dbd46735f86cc49c7d38ba53bbe6531ae19c2aecbca16c8ccecc384e507bc2a6141602c87d23b5230279570ec4714ed44419ecedd606d423900c6515c601100b8f096ce5b1f5033516cf14738ad047768e0cb8621cd47e0b9e570d7bd11fd68a0f80d4f7933e1f373b5fbce33bca8ef886844cb38d4f0707d08b729faa4d1023c11ad9dfba49d13a991c82e583dbd68936b1481850337134f5e990364350205b40df24b11ad1e06c3586376d34afda0f0707d86a4e5fe4e2fb46834c1ced6be611990d96bc68ac157fdfd1a265ad8e011af4c84235a564a8b8558c63265d6e1254b0c786baaf62472a50d3ad222be27fbed030c6386dc050c25a93774e74f6c8b5f186768b674fe8c011f968fbca15d43338b861b80080312a071f29b8f1f22492595d2e4ef06166577119aec71689462a265b2073a116724fca5327fded43e829da39c0f06efe9858893615e191192df34e1fbb14a11d4878726ed5e94c16be94c5980598e43acf903b9e30b61a236a21e44670376a7bec0884853c287f00e563730ccc20942337b60046f2c03b1dd65e370c11fdcb2280a63324c19ab15d1b157e155df97243cb0684db2ef5eba09bbda73baba37dd78e6c2210b4a6ab4f42e74a0be3c78240f9f381c8be12f98cab7698593c8404e481a331bf83b7809b4240723d1a4a0c5bb293affcf9e043bd5897a934f03dd2ba1b2044d9f1a355a15b9583f140e0b8205ddee57563171aa8bc47ef950f2efaf3e2bbf56fed75fcc628cd6ccfd86285f696248bc22d20b056276977b345b4558ddca64d34dbb7f349234d229349ad95f75464f0de2b3367272c346039601c4b4e13159706a77a714d931cfefa6e8f57307d024b328f111088af16dc4d08b3f65d4f751d63d029a5c172945a4c24912bbf70a2530e170dfacbb45d8a46f3d895bc34e53ed4ce291a686570ce826b32fa213aa1801a4fb7f930631b21e7c2b0ef13259a108f89c4f683d1af52ccb7a1fb375ed22a6e4cde314281534ba9141206b7c265e4beeed31d712e6fa489fded11c0558320a072eecb7da817dcda1e768b3670e7f31efe3227ce5357b0f965947d542e48ccaa59a571305c827430f248e94d3aef096ae55651cbd243c84d806330f65f2f098ee485eab2fe741196597cae28edfba0143e6cdd667a2a30d7e24297c7a261935338f346e2497ec6d62884a26878219e5f1f8dc15e9571f61c7d824ec6a968364876156b96b14bba20b92f07669e755478d60a51a8eb75ab64534b2b3f37e014b80b014166fe3c4da4dd23725458570fa090ee294f0b84518de70e450b8c5baca23635b93d55cfea77521d03b3fc4326e89112aa59cc6de0b8b413a3c15d19d8fb2af86f920444f194bad9c339d1022b74c261f27b8243e3d41f1ad3bd0964ed38de807da56eb0358bf94591558aab954499b56ba84de009bff1e4cd4ba82f648ed9a262332abe185b1870a56074a2f206c48400526a06c6c3dd185c700ea71c6e14ea36bf94667882e34d42044c99a46650709a34b2f9f0e754b1d53803ab1a390f591ef02fc6305d2c10d3703421b2b0533fbec8226318573fd1c0fdefba20b91f79c3df5ef4493c0da4927368b8533a3f93d301d028eed71214dcfb97a8cd4fe6fe5a5ef915db34d0bd7f6541fba7c6af256898f9042e2fb34e94ffd6e11f96a1382acecbe445ecafb22ba147f32ec0e95d906d3a01312e2effe1c4b1a3a2b3e52fe1f4d9486cf38251de5a7cecf4e90d650a96f6f320e1a3be13684ae32ebb424a7cec95634242330338273ed02a54d1cbdd5f885561eb6c1e786aaf2406bf93dee7aea287f3ab4d24e2430efe73274d94fccce89f98faecf44a1edd34dc4f74451641f4693c8911641575c65ab49031e0d7bd5f77ee653723510f5430ad962cd20108f140fb5a860f39f1e5e1fb7f1eb7b38c59410dec1fd5c1d7171d845951164cfbb54d7679e276c898bb2c0339a57a88e564d755c27fd2b2aa94e6c40087d37a674b7d5a247c600267d664552660d5dbdc91c719ed45693b86e5f760cdd7eeac0c0a7b5ba86e862a3d682c3546b90ad113f64b26d2613fb729e25c89720ed99bc85ddf096c65b79b003317e8b215f54d2dd3a1341e03fd3f4d6ca45224cc242e7be8d22b9f19de1f85a51ec20d0b5c847ba1efb0f75ed6f07ddcc550fb1d40688a2b505767a1322cd30b6974d98c0fbe24cf51832f17a570b5992b2b966b5b9fb081ae56969e1507a382794d99f71657cddd7a113446b2c5f7ba3995a731e48d134f92dc013f980d1264379b6cb28310abc7e3289640681c7159d24cf116d828f32dba45b897a6bb0b79d56c0042716d04e0e0147746d84bb32b5d046a647641204cf4b95826e53d7c940225fbea5f4b2ad02aa55b49e612d3182d5a4f783b5f533f4e89dd335d2e19adc338d94b067507193dddd08a87d64bb08c4ae2e848b0c2619cbe64243228f4e6a7422a9dc0ccb0d2d66aad027b839109ba71afe5e9cf49e4feb986f292060371730a7b75025c3ff787b96d0b0ff762f0034924f0467499335d8a3453bfcc41db59a9419750bd0c619b8799691ac1007b63d52db3c0b6d9f406f9c9bc72f7426eb6c4550f32f7d963516b8ebe1402727ca3197b1850c620dec284545f4b599c08ba9199e74d40e05c08cb0e77e3baa3fddd521cf1cdd78db807cbba8269a13165961df02a8469ded07cb05abb74c28f5943804fc1d1253f686aeb0cfe22e686426b61b00104f64546f0888f67f66c4671b098f956c68fc75bed93c8bea61293316af9d5240c9a764ab5893bc0bcac135587b650b3caee695c090a2a5fdceaf4a62961dbb2096e8374d51fc8439101ff2b71374328878ed0eb20786682fbaf686c7e236508d5ab8a0575d5ad75109fdd8f37e3608ae8b8ce6146257653fe55968e0ea7b67ea33856e969ea44bbeb3b72150e5b46ee7c86458f039a401ca1fd1feedb13ec9065406c1a69c496671a2a3d7e1dc76340416b723a7bb2326616c7f634963f633ccfa57e9773090e2be3d1827795df14903b642cba977c7b9d3787584901ff96f5f57fc33a598667edd54e23cbc83913f72f93b6f6a9615736e653e2bb14efaf8f7d53d2579573b65851d3ace6eb383c1b1e0fd6024b3eaf44115fe17984a61ba4390c3a9049fddf4ed4384e21128c460fd76538f40001e72dc879f95e95d4be60d7e0c6ada6cb463419c2f8f0337c426967a1fa76e71a661e8a81e0d537a8df6e864e70e5a8fe2691386d819c945b805ff111d8ef72b2058e66129d4e368e709b318bdeb73c8bde83f027c62d21795e4c2f37738675fa40666ad380c05b31951614b7aa3be6c778c96f794a13af3b2ba700992bd6a0098fa23de0e67ee92883f1ce1cb17ac5424a52e8a3da6aa294ca206d380213818b752b8a219b6581e11a6bfe5f6e55fac85bab7fcc1bfcc69b128ac05224f6b3e97ec70e562261082bdfcf9461576b83cefc791ed24145be30e8236dcf94bb7bc109086ed44be49a2e85bb206d2e9c8aa79593853370bed7e24497fe47f54e7722b6a969461858cb477f2168ec5587cdc35f2d89e3536d2a4cedfbd5709c68b863ea8cf2d27e7352271267b094ee7c2d15af0838a2711b2f7e74101ebc970a3b820964430e370485ace134564f2b8335523b811fa623d7906610f860673b8c242405529abda1e05919096fe8da1da96cb978cbfd7ff57e201e1f05bf5845646*166666","1234567890098765432112345678900987654321123456789009876543211234567890098765432112345678900987654321"}, {"$dmg$2*20*58f110c5659f71a773cfee15fce57bd39a9c3071*32*67a0f064560064ad000000000000000000000000000000000000000000000000*64*a888055ffd98f46c27ed27ef34abde2d46808cf5a0da4a1ace11c116ae28febaebb282fc4d5aeb0338eb5a16e7bb81975767a83a4c20ac341f1af24b6e012e32*24418*4608*ec2592165f1a11d333edcf97edd17413b8ff2bb314081d1faf267fce7ec264052fd38920ae5fd5b6ad5b6bb21d1cc9afe7b689c21889d5648725167f10bcca9972ba84a7132fe23217ac40963f0d02977d7b7089934e9e95eb38498fb050e984ea7feed162c5392d03711251dca4177cc7893e32fd1e590d44bb8dae0c2a280a30b2f553ab80ada24bc32d19700176301da131abe2551d785d0ec09f68459e82c58102f5364955947f3a78362eb0c5cddfcc49eec109001d536740b2e0fab0554e2408e98ed20605ec8588c73e7e259f74b77e7b89d2b38282a5c95311460150d8419625c621a9bb02561c73bf364f1c5147cea67b4882672613f0d7f425ac976ba712e8485c1fe69176c6574c75ae686314ad04f8c3489db5fa506967f6d9b8f67f98a63297f62aa6f4a1f6d5db5917d0f54b5580df4ef831eef8ee962387e77e0396bb098b0029e953af3e5e2f15477db0a79e1d143533c373afce8c01e64bbbdfd3b1a2ed81bf7debe4f0dcf1f2a5e6dc15d7d434d825170f2a9fc4f5621f05900b65416fc027487c348e547e8b66a63b7974949bfa7d89d4253cff69931b460f3310b738058485092c2602d7b190c469c71372c6ebb1c7a6ee70505a4f200030f076be667a358264179be591b5cd0cfb983aaa1668a6d8b5a268fcaeeb333690254866dde8ff83f9ab97e03f11dc0455031e8364da38152736460153cd65aae181a5ef4e66f3e36c5492182631c9ba4829597f709067a50f7b1aa0109df25e66a79955d5dcefa36b0f425be6b2e4986e9a44cf382ca80f428489cd01e5b347e1d3c9b0e111af29fa3e7ea15999f16134188ae4cf7e3d574790373fb5590117aa73df99af0478639da1e3cf9fb0674f096d6f93363f3e8e4692d66ff73d979391a02323943020eea247060ac96c6d8d9646cf75eb9abcc5aae4f629ad47a9e2f4fae66901a363aa32fb65f0910a3f044d4f8bc8052e6b96ce7e2891c177f7b9a71928f4c15d4b74d4631edf8d5723108c427018f7296a7ce4aad04e127f02d9cf416b2486bd5603480c312e1fdb40659e774f44eaaed1c73568f67c0717a95184f40a0d1d2e23c9a5068990a6c528e7fe026127509196e1fcefde88df364755e21be8c98858946f4552991b3b74dcfcca6cad8d3a8755775138c95690280a5cfd0b71ffb58683b7406d30e3e3a798afad5dfb1d54b85c428e693f9532ecbb49e162fd0b714142c5546fbebbf4110586eb45a4374e144d0f2c776dbd1bd527a354a0509b64dfdaa2ac782a7c6beaeb731497942e407163bc99b11015d6127524832d06197259f72b37f8a0e47622c72e1d8d63b2dd34f04e6bf8fcfda8c6a747ad0cd27df2324e4eebae1e4658fef14f61d0bf5dc2ed4fe39b7cc1747391cdb2461e29e48418b20464865b879d3b9de6a072957d65d3a5cd5a35bcefb7c7cd58dc9d4a31a3b1616f8d9a7fd5c5ed9485cbb59e8d1fe9f8d683346c1adb1ce3f2db53a1330e0a165c8150a96620ffe88350af30344c77f7ae5d2831d5b1093d55c34051b48f45deb5823903704173c9f74f80d3c7240bca291e9271b560cd05c9d7e32df80ba1c396a553ba0ea9cfd920779fee5ecf27d8abd9a06b14851739cdd5038496fb783cecc1b8b8d688abc91fb2181d0131f61e2baed1e030acca664873cc753110c952c3d881ac09629e87e8fba18d5ea74673de3b7215af86501ed69d44d1efbe0d1950341c0a6fcd51f7af1f7d068087992eeae41c587916b66b66f92659aada75c396d93947944bcafa3dd71cdadbf9c26358c75dd0517260931fc0f2b78d9fdb03cb332e05e3d3a78f92a35a0a218581e3f2d44122ee7074da80b59c187982005bb65663d8af556257a5dac5c65b672f48e4be26c7b720305f2d186d43dcb967eceb2991e2c63cebb35220e69e6dce1f7229272876a32aa0e86ac1706cc223991bc5511c67ae78dc4b30e4d3d27ae59a274f0cd4e8f263d84f23880b575164cdc9a31bba7d62fce3a8ffca6b48c13bb6af518dcbedb9223feac77249a0012ca6bda8f3edf81f3ac2826673e67ac85bea8d9859c821c40a472723581073ce8e3574172b2e2ef1391f859824698c2b3e502f17a9e43593d5123e4e4ca7c4db08ce72ecbee12d089ccfba930c7bb1f848a73e054e3b370cfaaf73a42c30b5a063ac59c7c2daf87bce49ab1cc5e4e7f3f65774512dcbbc577f3e4ec369199a1298ccbf8eb7cbd2bc09471cd641bb0a0d585b9bafa48a6a50b0766c7fdbd7787cb1124d141b714cc6d698f50dc3971ef56424d5fbd197ea3488ac8492673e5cd034d5c9ea0241df80bccb77ec55807d9cab5736233ad915552387ba74dd140d78cb08ee4934aad13432baf526a8f9a2f59f0f941e9f15bd65ff50b3587de446e555f39e43bc510bfcdc993fe6a4c2008140aa7c066f6a80c8820b0e8a09a24cc10062472e57953bae27a087a3b5fe5cb3026c8bd45149d00e3469627c5d5f5e48a9c06b899c9d808964a1c83ae41dbe6d26b57059cb24db722ceeef2fabefb8155e987fc382f65b41f064cd180738667ea10c9dfd1e350978283059203513a027a8f15cd524628ed21d5f1ed167547b00f84951d0e15ab17c48e534b8c75bd25907c1caea668f4065cc6523f5f67021071bcf4bd145a93f4fe5ec7ffe02811ce100b2fc8a926f8512a72440b5270f27b6102ee4ac51c0c5c3bfe0e0f4f8371d1eefee7f67e7268b979340b243d35f4873ca3a38f7a692f2010298d2dd87e861391d4d9999a6bb704d3d200dd512368f6b34dc5a13c46f113aa5fb1120092f441b21e2aa0a6b605a749c0297a5b6a9d507a39833fcb863c097328b11b3edf97d4bb94f2a52c2fb061ce5750293d869954e5d7dd1370dd7126bc6d03fd5eb7ab3ac94055873084597cabacc283513c8455fc7761254b635c29f0f5480893ce9db5bac75418d4225ea088b606e1aa96a1658b21331afd01eebb9d9e8db0d89286fc90603fd67ed8687af21965542bfe269024ea89fd027ab17b2e2ce329d5b7dbbd8ebc26feedc3247e475a74b0104b71a555d6f0d65e7166296fb2e9270228a8895f681eb73a6baed53499e6e1b136bd0fc0144bdcc464f52614d5efb179231a22aaae813bc9e2842028df92b7554d3350bf748a688c869341c13142d3d990993386b24daed9ab71887cd2291f1e7de05c837f99df599f37b8a6fef40e668890b795659d481efac8f79d087a51b15495e0839e68d14587e49be645326ac755f9bee0b6f2d3f674709a58a7487091a6f6924e22dfa3183aaba232b08892c18e21283582c6cb2ad9b3bb1079a4ed7e5228283058b13555cb6207a92085ffc40935e6042b103576bceb71e48f000fa8133ed046dec8f840b78bc17a7cc2382d5959ea1378b937872ec4fea3144df391e0e80d28cf8b9eb661e46ddac4e84f4398d9ec733ea1e7b37951e00cf8051e648a725cf5b7f271950bbeacce4a2a682b77653d3cf7e302e402cfbe87a257031f00fde184545b30861ce2794c4b4a667535962a7873f505ba11bee33d5eaae7f434f8788cbf41ee68738357a6ea14a94057f2b602a675be0e0522a23408b236cfa3c6438d028cb711e49ae12c9e83bdcc0b118cef50bc2268ca4ef4eb82197d7787a98e8ae7a19b57a1494d08028d86ad501f81ec4794d674e6bb9aa28826a9ab5aa3634d4707be0a85eab37dc400e05553cab8d833e2dc7bfe05c5c5ca150e918568fb84ee7097881679afa05121db9e59f4ca71733c03361cf34d3f2a35254d199039c814409c19bdccbda99cc069e380712f4094b2a78830700228af6471f3460beef6425f9b667a96a81918a050e5f9b6e8fca53501f6d85430835fe05b093d4e5843402b4bfd51cbb0eeeb7f7469e29042f8cfd0b5162a24ba99ac038bfe93dd4480d6df37f581c1fd3695601cdf8e25c79d82d1b7a173e17efb4663559f633ef05b87585afbf991261648119aa9f72cf6b725bbb5ff1d9157a4a255138861468e3e9b4451114a2f3983c20c4f566662b81fe72845ee3b49b8f78908ea79e9ea09e5d018372314660af414f641ffbd6b898e457a7f60bb61e8922e797b4257cf5dd6b7186d35279cbfecea6dfb6b028c0ee5c4fda01e6451eda511a221a3e95c01e21e8730cdcba65aa0e4e8123454eb0c33afab666456e60caf5bf849a4331985359a4e2f0207cf18b44eecc0a5d38791d1f24a7c1c3d73bfe233c954df30ac38f787adbf2466e87c8f17cf5a9527d8496720c98b4be3fdd5d2ede8654cec0dbf82ff46d4619f477b272764308d046a11c551e1a22d0ec26b374a50430f3373037bed7bad6758d24bace152ac8ef748f98509e2faf028bdae70509503c72e16d8abe76cd5a5e93af8e622f45f18f1590a07fa0e5ce005462899a97ebbd9fab12f1bc513e2d66850dda06e3c4fedd6f12bf8627806bf3c0989431cd7f83fc01d89e485c6b7458f2b0e01c46c3db4e395b8b8aeb61cb346fd55e83d0aa4d6f45ea9ff6756ef23d59efa72157bfb51df6a1b6b2763959f53aa9b9a9fb725c2a4402c749d2b205e29ba46ef85c15f404c3743a8d86c8606308dad5220fbc4d9e41b918f0519ec49b6ea1dc443ff166537ce90926daca68f58db9b63f33616b97ea0f68ed28dda96443250d69afee43b20459c218600c0f8740827bf42825535773a247439ac5ef3d60a85206d68d7d267ef5758351aafd094e9455c8b04e147942704d396213066a36867b29c91e435db1d775643edd9ff9c2cf45412c6bec5eb0a76a966e5349f007fb6c7bf8cc73cb476394b5c7eaa326bdd8efd2c199a721fbfc3da0aa1f663a45487b250e8f2efab1f0995e0d2db8f77dc688f4d693f7dfbad3c7375a350fe36f2798ac1064f2fe741a3772ee081c24345dfefab7457019cb4fdfde3d6199e1f6da8002cb3e767bf61a521a2425024f34f607a8268b389b497e31a56c6f1f853390657baa1f89645488ce5472da8c66201c4e63d771209367c5f5a74488d96d213ed56ca8166459a65483459149b9c1126cc8d9750e051816cb47af7512291d992e44236a806bcd1cae67a647883fd3f36b40c032052dbf9e35bb1ecdfa6db69e640bb7763ff276cef30c2dc1bd5c132dee26d9ea772910f6d2176cfb3a81e4300af90c0fb4857d718507591e852f2da21842c49bd66a23838cc431da245b1ea9d1f9d869d7b9d5bc0e923446760a73e0f12992e3eae20d613b5169494a74493fbfb0d13674fba7a63f03ffacd463973955b7d48da28d624e6fa6d278bca034930654737ed1fd97698ef3882900025b3a3734403a93925830383d00b322477630ff439b141deb90996ca7fc6df5f2f5caec1b67e56c4a5dab27f107f6bc6f516670a47ef01171e53307fcf9a450636a7167ce6d4fb3e3ccd3498d0028d7f3ac0fe9db57814cc5a0811747778921cc0fd40cf2668dc07980a2bcc1c24b56984ae742ee77d32e8e0a47fa68acb81a8bb077c03d20024dc340d26b23199ca02ed9344aa019b39b7e0e97f3e817a044bb12e7cdb08bb5f59b10bb673499ff7f72b3e5550faa2cc0366a98fd40816a3dd16af30d937e2ba5ca9cb2977c780432f0d47f3246b00976f6a25325883b394a0cc85eb9a25948d8bf935d59a949ed88de7478cb6dc4c74d79b34f625256701469999c3ec6b80caab260dfecb6dedc458cfbb2d45f952580c0952284bfa5030996f616ddb082dd2424e1db2f35eeb85dabd76bf926a1c3491a7e2df3b4fe86c8d7f963433232168a2689e7d61ef946b0996c440b1732988e782ba6f6a8172a5718691ed5b277e9f81af6453433b14ea6ad486852976c3b990a0536c22c9d1f1fa6fead482e96c5b3a360b5a6c9002a699554d63bba89a05249fff3df391519fd89c967b2a298d80981026f007d9ed932ba2ac87228b06a912162a4294c543589514f5a6c5dbf457cb7c7af1bbe464f451c342ecae77f62abad2f0514073b83cb9bc5d9b975e9278a67462960ff0af6204361e501b243f006b3aac61e47148c306b1c2ee7bf1b230600354df5daa5de14d5385051a7c4c7eac0e0ac0c1220add6484d35acf57c646952cc42fe6ae5cf0f72513dfd02a221d093223f9b3062e6c57ac962ea12b076f77ef61462381f661e6db7b80c377f605cf91b81d63f771d460c6fadfaf81b735603a0f1bf1b7bf1c31ff0a7faa8325fdc31925a9a7f7c216894961e9b17abacaaa1571c4c2c642e2312517f865bebd23063d06e7508a72993772c308a30aa9fa4154543853138171d2df029fe31b15793298fa2c930ad6ccf4d95d91374fbb1ba1de67e8a94f41cc75cbf856e227f685c2d84a72e5921e88c96c2be5bd6d37871ff74fd0ef2530b443004b029f421b3fdf1b6242b80dfa68345746ae15377fb9c122b711c339b2a2576d4b18d8877b7285e09c5c37d50ac29a345f8aad56e29a93340012076f8fbf9c5af568fbf627772a51099920ee2586b97fa3087b6ccc75f9ae1284b6fe99150f0f2b2e76fe34547*1*f14af4ebf9764fbb42b8d2c6ae6d44a1a48c72d58720d5d6fbdb7116b308bf72df33d882e5c7aa85fe201737cc1010dff1ffcb2ba26bb0dd673515b741f7fc50405ebe80b58358690fa052ff11331bd632cb9cf2c3922cf6fa6a3dcefddf641431573cb9cd35aaf7ba977baf7c32c8619780215f32384c544ae9136536010e4bbbc1eed94eb5e4648fa536db11dae8af9eaa8c23f754ee6b34635f20236f16fbb918e52a63e25856f33f9334b5c5ec7019a2a74d22b2f4b9359d8174b7cdafbffa49465f6a4d63cad0d9c287b854c6a4f3516989f02b5b4283afca81a6205352369d71dc4a7eb18218f4426e222dce78695bf093f7296b44fbba208ce8afc6f347078739e733dd2fce99947c66489274fac5ef87c2b186f4584cccb4ecf2a6bc87adeac1f7d8c8261116139880d8e69d913dbe3082876351639576eb7c303413777505ebd1f0ad300ec6fd0962f2636c0eb94ae28cd355230758492c328476b2475ae6ba49689fac88e410d943315a63e17bcea442535430914ecc017e9928e4ed9a4b428a12e4123787ba9ca4b7ebd1ce107b41eec41913d9532a8fd37eaa2a5f16ab341ecffd14e57abe53be6a477af8a092b136aa282f6cb3619aa61a55bfc40a9394f4f001be24f5c3a9041ffe52cfa6dfc1638a469c9901fc9ca2489c7cf6f1934f3cdeba2dd928ec5a88c95565c3e657b0edec785e77c5b2df79a91c9efaaacb275e799981ee37362acbff72da7768a9f450bfc2c90fcfdf9cb2033d65fb99bb50c3bbff4f79cd782a405a049b9b207cd403797246cc843e46241210452ef81e7e84f5033f7bf49a79952c4836fbcc222f72ddcf3c4400e5ae68a653c30e45f7fdf5cfd26ae1f9654c1f5800fc3d0ad0bb26b5b7e314ee153599bb7aafbae5193fba8be251982a227ccbd497a8a95e6f2130cac8c54d41a3e9053aedef90202ccfbeafb5573ea057d1fe3ef9842d38312927fe13aa7ef22110f40a291e018f537d81a5222019080b843c919a61fd00a42308a5f8a2b3c24776aa4bf818010036164409e63622b5f47f15ac83f839362543c32c9e64c1f184dd894f546646aa9f677bfc714a7d6d0343117bdf71dee2cebc751e9cf7ab10a8a12fab1febf08232de817e66566169d255c96c27a1fba6e43418bdb9bb9c2465856669cc5223541323627d2d167fbb25b34b6dca4b3e44cec7f80a534a1230107681de00e65251a3129b79d0babe4739737e90cd7d7ba0baf8a8c99154b575a1a9f44fa97cd12cefa3bc250e4e888e4ba57080be70ffea4b1013414d8b2ff943a368d86f90baa72e2129598e5db7c3b47a3f1e84ad78ed1f0d96f23f7e5cd16e09e821a4d40ee17dfcb2d58d1755ad5945d4a5f7c8b29649362598291f30303d20ee47d440ae2b46c5db1a9a4980daf080d23536d4ab55b7112254545fa6cc1ad4f28683031b278a7af4a2ce5df587d7ecf09a0bda6d509b6442386f6b35be07628c6c584ae23fceaa891014a215a9e4511d7ac65bd9269763ac0ddc2d8498d34fc027f28848169bb64df63298bf91399bcb2df9f9a1553f4af7c5db0075fad98777d03c90124e362a733b9ea8d49310b994c0e2b085c2b5ed686519342c1b1bc612eb4ca510817f15954cd3952db4efadf5735a8bbb44f1d3417c8af742878eee524e6e28158d70dfa8c280372c34e3cf0ce2105b16aa72fc4b298d612d0de722a1852014b8ac79cb4794d0de4f259a572f322be9898a25efa8cc65fc44bdd3217473bb12b9ac98a78b887683a4932844eedc2cd3dccea625452e92df96e4183ab059a3b867317f2ca6896f596814fe56b0dcdb8a488e797b0e79246a8b39853ccd9d0c1f9817a1639e3536c46b57c78943c540c8c3e7d615051a55a97612691a430e2d041a3f9ca3991b4282c5581e2ac4ce15a880ab733ca088e38b4f96848aec0ae8fe99fa799e630c7e64c478c7883be33bc57257e86a4fe319c3880a7480c13689058a8f7235174409ca0d4ee2c35621684b65eef14d6a51e7dc00cb6c2b109308800058618ccaa46cd0dfbba8cf50b58c2ba73aa25457cf7de3f289c8cfa4cbdf920f966737ae7723e9a56ceabe563a7bba7a5980d4d01b86cc95c75220ba3253634973792a59dd481c7371cde517c12d2507b66176fca41884c3fc4da937179dabed275047442765436be017712c71cdee7b358043883656f9c0391d50d0c691ab154bcc3dcf207594ee9dd5f879864d08f611cb03621953a67ac4ab1f8fcaea681697302402a57d50cafab1b95a4f01c1dcca08a7bca329381fd82483649a6488323dba047c182fdbe0525ef7091a211c0a17ad7d42787cabcdf8ce699991435e0663c114cc5dbfd4e6f0609fc0826f36c23c51e351b4c22e2254a35e837711a528bc9eb855dc8b53e4a0c063a35003f8dbb7560b0f1cdc76b3bd1c11cbf749b67713e4d8302b37dc30201848e9c3367482388009aff2e0940d5ff41f37bb50d5858cceb97ab22bd28af6e3273b4ebae11f3587662bf410f4ce66107d0f746b96f98f12e422c12b3097a7cfaa817396ae4822f38570d86fc5944b05dc273166e85d949d6e2a41adba07ddc652740ceaba3830d67bd400a5801f6dc1f87116f66ebe4a2e4937c8783a60776be98e37123e758d8b55fa34b0eb3639410842ed8a57267b9c015aba4c92a4ea93bf2e56ca29d07c7fdd1ff837d954e984b41a6e38df8e429fa2d2fe6bf8b49e5a9547d660bbf82643e6e44455cd25703960a0348dfba345802047bbd3ce4c62ccd915e58fff056f21230acbc2cb1e7398b51700634c76d1db64f8ead09023f8dd5225243b6ee8629f2a9f1b673b0195f52be6b989abeb11e4436c4b6e0e1552aff6a0f82094ab2e9c1349b2b460c43c95d64f1591bb698c7212c087c2e6eed64c3632d199e5e026f771996fdf05f82cc00f28855e6667cb54bde77af99dddb20b207efa9b3a0f05cd28fc24f119f222bf67235c5f0835dfc89e91ac916f0ddb05d0d699403720bfc761673d4d3590c8c9427abf3ec43560453e2e2fafde09253d6c938ee8484a65386a7fe033dca74b785888d1525632bf8d3549deebc3c552cb719dee079172285d38c7d9e5d48796c62d046c1b7ea8b79d1d54635b62283bcb86ee8be1ab06dac90e304048e29fd877f5c84f9ed5baed795d404580ac39819d2118583156b538d7d80a0bb3724036bb1d1bd19791090546c9ea73039402efb01f6a9283a59133cc78edbe787b172aa51d7e3461bf81792c140f10690286778e18851c8ec22c921fa41f885f844fed8cb3f28b8b8a92f4d464e222b212702c6e21e394b0d9a285b4e32b6eebb6d2078d022245979c138dd3b364972326be4f6827d6df4da44bb4f27b2382f314e3a880ce783b3d5e66bfd7806ce3da4d66dc4a884e6416a601a1d19e8917fb57252fe733e32e91443b93480b4aa0f01f5c70697530a9e477dee402f02de3df643c36cc6740ee6fae62e83f61f12e27323bce31ce3484567e1be903e281854aa0574769138393d2c22543886ce49acaac37f4814f69423926a67ebea70c89e768996db3aeba0c60331235f6077ccf7efc5bb3fd6402d79b88992f25ef15671f6a05f2af4c223264be455287dd3973930de4b444a97230c1cb6c73b930381210097bdd0df7a46fab4789a14ecb6bf565744cb4f0c6871770db9db41990b90abc9e42679bcc2d75b880a33ec714a157c7c86059b2fed89936494fd26adfe526bd1d58974ee2fb6bad5d537e13fa3e19bd6e5f853b3f64c04a565829f0a4769e9f034a981e245f8bddc1753ecde5441be84aca659b7ba3487f4e0e9277f7f8c234d7fd3e6fd69ab7a4887b6929391a189fb92a56e8dff37a43f357637e2851261a62a614a0a97f38677264919b3b734b45dcfdcc0f4a24ce909a3ed0094d19323688808c8e4c7f37b046ce18130ed44d7ae7b28e561567577461b10b42315162c111b0c420d61c847845ded62a63dd1cdc3606d1d0f91bfdb393fe67a0a4886046f65a2d78df577bff39cabf6d6bb71e8cf97e523f3289b2ba3fdbee597fd6cf3d9faa3ad41b7d43813a71ab9adc4f30bedb918bdd6f86e8f9bd7f016d667c3498f15d73cdf3f466ec4df4b7b52463acc8a9d373941c6793653a507db2a6c26c135a7c6a7bfb3a8e61e2ed006349b3c681bc24e6b8bd358d7030b4190aee61679b42188f9d4362c72075cfb8110656b007ee348e33915ddb47a923afbbd0c0444a95a2a956bdc736f6191ecbf5ec7d7a5981c720b9739d1c95fe5faa499129ee9ee9bc037153caf2ed434a16aec6f0041984e9cf2aa5b6ad422c2f8f3f42811b044feede18ac7fede199cae7278b2c392e59f65cd3e1fcb1876a01b599be255e253a24713cc60d2f303c856265d9903c5eba39cbe1be4648e20195e6e3549f8da6aee096181c3a745f59ad12323fc3da745d497ed65e39d2d339ea435d1c66bb5ee8270e1337b0e9d04107ac82c9c10d6869040ce0942c2bab93718b41d50b37f2dceea5f2b62a30268176ffe51912ff581ef846f08465511cfc59bc6f412eb5244d7e03f6514a6eedc0fd345fe574fafa84b83ca0e2ee5001c44ddb964b6a4fac0bbd0de7215d5ff0e75e594d0b79c1b587236aff314d8589d7d8ae2268ebd4aa17b5a5230b458c2147856510daa574345c34c16b5afb11109cdd1f6189e2603a6a068f39862ec892e830a2a8ee4903167c6d42a747930cbde302b2f51b567132d7011302463e09f72a77b2a01a6e5dac3bec1a6f0d2f7b247a6a07012e013a0cb248478c1b289dce931ff48381e72ca9a1bff6906f464008385578fd68f74f530232e4bcf8df8121979724d0812e4ec64bfe8230e4815ec7d148d21151fc7624b7e5ef325dcda7db3ec8a0ed8c28cbb53b18f1380bf2b7cf6885d608d3ced7c490dd91ea960dbdd960870872856fb23f1153155d29e75eff97efb4cb666ffb246754b9a52ea34518961ab0084a283a904b3d8256bbb1540c78700439f38a1e3aa80f9cb79353971731cb1dadd2dc7ca293fe48ac6e0cfa0be2922eab378c46dcf9096e79acb85f6bd9efb76a7d6f1e47f10f5a76e58b2acfc48a786b2ba7775e8734e4f55676a369e2622e8e62bc8cda50f02d5f2eecd7a8060ed435e388dcb972cc9c4efbda9c5a3e56619af53ac0ca17de9df442adb04c9f9b18d1f391d0b90b84694abba6def4f38c394de32a11790e53c73cf742d09d5eda3a5b78e0baeefdd6ee20760f42e1a221a550ecc0c16e25c9c9f2ee8ef45267a552ab0c56d4da75a16847b17f4ffa07bb2444a0b9b495cbd299ca988fa28ec0e8a4b00b6937205ce48e09d9b75aa7db5c3acb5f1b63a3ab1f525e7b733fdc92895236fa5e97c3abdf822d2f1fa8cbd8e1cd858f59af8b814efb5575365aa87eead322113672604db792148899dfa8e0bc752f6850dc1df414cde39244d6261b65ef3aeae1c662dabe6212201294293d74fefa147f388fb35e09b363842408f84c5ddf137f6aed148996053fe0124c991865f6702fcedf276d8f9eb842a90f7c2f5d1494914bf170384bec1a7699f7e07c5b3e586d9dae058276f3c613d9eba04b7031e9304156c3881569ff870b3c9165d1c24613175edb5fe16b9df9ffa8b5424f506bc9f111d4de82407470ea1e2bfc9a1abc3086c54c392695112ac54d5da6a34e557ed334b1d1cbb182e43a7e06b1aefb6aa2a8e6e7e25fd24f216975732f15b4473b5ce8682673b2bdd81ff68b7920258d956d327c3df1fb0eaab138943812ab89661677f3a41aeaf89670f45e*192307","123456789009876543211234567890098765432112345678900987654321123456789009876543211234567890098765432112345678900987654321"}, @@ -111,3 +116,154 @@ struct fmt_tests dmg_tests[] = { #endif {NULL} }; + +int dmg_valid(char *ciphertext, struct fmt_main *self) +{ + char *ctcopy, *keeptr; + char *p; + int headerver; + int res, extra; + + if (strncmp(ciphertext, FORMAT_TAG, FORMAT_TAG_LEN) != 0) + return 0; + ctcopy = xstrdup(ciphertext); + keeptr = ctcopy; + ctcopy += FORMAT_TAG_LEN; + if ((p = strtokm(ctcopy, "*")) == NULL) + goto err; + headerver = atoi(p); + if (headerver == 3) { + if ((p = strtokm(NULL, "*")) == NULL) /* salt len */ + goto err; + if (!isdec(p)) + goto err; + res = atoi(p); + if (res > 20) + goto err; + if ((p = strtokm(NULL, "*")) == NULL) /* salt */ + goto err; + if (hexlenl(p, &extra) / 2 != res || extra) + goto err; + if ((p = strtokm(NULL, "*")) == NULL) /* blob key bits */ + goto err; + if (!isdec(p) || atoi(p) != 192) + goto err; + if ((p = strtokm(NULL, "*")) == NULL) /* encrypted keyblob size */ + goto err; + if (!isdec(p) || atoi(p) != 64) + goto err; + if ((p = strtokm(NULL, "*")) == NULL) /* encrypted keyblob */ + goto err; + if (hexlenl(p, &extra) != 128 || extra) + goto err; + if ((p = strtokm(NULL, "*")) == NULL) /* iterations */ + goto err; + if (!isdec(p) || !atoi(p)) + goto err; + if (strtokm(NULL, "*") != NULL) + goto err; + } + else if (headerver == 2) { + if ((p = strtokm(NULL, "*")) == NULL) /* salt len */ + goto err; + if (!isdec(p)) + goto err; + res = atoi(p); + if (res > 20) + goto err; + if ((p = strtokm(NULL, "*")) == NULL) /* salt */ + goto err; + if (hexlenl(p, &extra) / 2 != res || extra) + goto err; + if ((p = strtokm(NULL, "*")) == NULL) /* ivlen */ + goto err; + if (!isdec(p)) + goto err; + res = atoi(p); + if (res > 32) + goto err; + if ((p = strtokm(NULL, "*")) == NULL) /* iv */ + goto err; + if (hexlenl(p, &extra) / 2 != res || extra) + goto err; + if ((p = strtokm(NULL, "*")) == NULL) /* encrypted_keyblob_size */ + goto err; + if (!isdec(p)) + goto err; + res = atoi(p); + if (res > 128) + goto err; + if ((p = strtokm(NULL, "*")) == NULL) /* encrypted keyblob */ + goto err; + if (hexlenl(p, &extra) / 2 != res || extra) + goto err; + if ((p = strtokm(NULL, "*")) == NULL) /* chunk number */ + goto err; + if ((p = strtokm(NULL, "*")) == NULL) /* data_size */ + goto err; + if (!isdec(p)) + goto err; + res = atoi(p); + if ((p = strtokm(NULL, "*")) == NULL) /* chunk */ + goto err; + if (hexlenl(p, &extra) / 2 != res || extra) + goto err; + if (res > 8192) + goto err; + if ((p = strtokm(NULL, "*")) == NULL) /* scp */ + goto err; + if (!isdec(p)) + goto err; + res = atoi(p); + if (res == 1) { + if ((p = strtokm(NULL, "*")) == NULL) /* zchunk */ + goto err; + if (strlen(p) != 4096 * 2) + goto err; + } else if (res != 0) + goto err; + } + else if (headerver == 1) { + if ((p = strtokm(NULL, "*")) == NULL) /* salt len */ + goto err; + if (!isdec(p)) + goto err; + res = atoi(p); + if (res > 20) + goto err; + if ((p = strtokm(NULL, "*")) == NULL) /* salt */ + goto err; + if (hexlenl(p, &extra) / 2 != res || extra) + goto err; + if ((p = strtokm(NULL, "*")) == NULL) /* len_wrapped_aes_key */ + goto err; + if (!isdec(p)) + goto err; + res = atoi(p); + if (res > 296) + goto err; + if ((p = strtokm(NULL, "*")) == NULL) /* wrapped_aes_key */ + goto err; + if (hexlenl(p, &extra) / 2 != res || extra) + goto err; + if ((p = strtokm(NULL, "*")) == NULL) /* len_hmac_sha1_key */ + goto err; + if (!isdec(p)) + goto err; + res = atoi(p); + if (res > 300) + goto err; + if ((p = strtokm(NULL, "*")) == NULL) /* hmac_sha1_key */ + goto err; + if (strlen(p) / 2 != res) + goto err; + } + else + goto err; + MEM_FREE(keeptr); + return 1; + +err: + MEM_FREE(keeptr); + return 0; +} diff --git a/src/dmg_fmt_plug.c b/src/dmg_fmt_plug.c index b3377b3c67e..b42a7d48dc4 100644 --- a/src/dmg_fmt_plug.c +++ b/src/dmg_fmt_plug.c @@ -90,8 +90,7 @@ john_register_one(&fmt_dmg); #define FORMAT_LABEL "dmg" #define FORMAT_NAME "Apple DMG" -#define FORMAT_TAG "$dmg$" -#define FORMAT_TAG_LEN (sizeof(FORMAT_TAG)-1) +/* FORMAT_TAG / FORMAT_TAG_LEN are defined in dmg_common.h */ #ifdef SIMD_COEF_32 #define ALGORITHM_NAME "PBKDF2-SHA1 " SHA1_ALGORITHM_NAME " 3DES/AES" #else @@ -134,6 +133,7 @@ static struct custom_salt { unsigned char chunk[8192]; uint32_t encrypted_keyblob_size; uint8_t encrypted_keyblob[128]; + unsigned int blob_enc_keybits; unsigned int len_wrapped_aes_key; unsigned char wrapped_aes_key[296]; unsigned int len_hmac_sha1_key; @@ -164,122 +164,7 @@ static void done(void) static int valid(char *ciphertext, struct fmt_main *self) { - char *ctcopy, *keeptr; - char *p; - int headerver; - int res, extra; - - if (strncmp(ciphertext, FORMAT_TAG, FORMAT_TAG_LEN) != 0) - return 0; - ctcopy = xstrdup(ciphertext); - keeptr = ctcopy; - ctcopy += FORMAT_TAG_LEN; /* skip over "$dmg$" marker */ - if ((p = strtokm(ctcopy, "*")) == NULL) - goto err; - headerver = atoi(p); - if (headerver == 2) { - if ((p = strtokm(NULL, "*")) == NULL) /* salt len */ - goto err; - if (!isdec(p)) - goto err; - res = atoi(p); - if (res > 20) - goto err; - if ((p = strtokm(NULL, "*")) == NULL) /* salt */ - goto err; - if (hexlenl(p, &extra) / 2 != res || extra) - goto err; - if ((p = strtokm(NULL, "*")) == NULL) /* ivlen */ - goto err; - if (!isdec(p)) - goto err; - res = atoi(p); - if (atoi(p) > 32) - goto err; - if ((p = strtokm(NULL, "*")) == NULL) /* iv */ - goto err; - if (hexlenl(p, &extra) / 2 != res || extra) - goto err; - if ((p = strtokm(NULL, "*")) == NULL) /* encrypted_keyblob_size */ - goto err; - if (!isdec(p)) - goto err; - res = atoi(p); - if (res > 128) - goto err; - if ((p = strtokm(NULL, "*")) == NULL) /* encrypted keyblob */ - goto err; - if (hexlenl(p, &extra) / 2 != res || extra) - goto err; - if ((p = strtokm(NULL, "*")) == NULL) /* chunk number */ - goto err; - if ((p = strtokm(NULL, "*")) == NULL) /* data_size */ - goto err; - if (!isdec(p)) - goto err; - res = atoi(p); - if ((p = strtokm(NULL, "*")) == NULL) /* chunk */ - goto err; - if (hexlenl(p, &extra) / 2 != res || extra) - goto err; - if (res > 8192) - goto err; - if ((p = strtokm(NULL, "*")) == NULL) /* scp */ - goto err; - if (!isdec(p)) - goto err; - res = atoi(p); - /* FIXME: which values are allowed here? */ - if (res == 1) { - if ((p = strtokm(NULL, "*")) == NULL) /* zchunk */ - goto err; - if (strlen(p) != 4096 * 2) - goto err; - } - } - else if (headerver == 1) { - if ((p = strtokm(NULL, "*")) == NULL) /* salt len */ - goto err; - if (!isdec(p)) - goto err; - res = atoi(p); - if (res > 20) - goto err; - if ((p = strtokm(NULL, "*")) == NULL) /* salt */ - goto err; - if (hexlenl(p, &extra) / 2 != res || extra) - goto err; - if ((p = strtokm(NULL, "*")) == NULL) /* len_wrapped_aes_key */ - goto err; - if (!isdec(p)) - goto err; - res = atoi(p); - if (res > 296) - goto err; - if ((p = strtokm(NULL, "*")) == NULL) /* wrapped_aes_key */ - goto err; - if (hexlenl(p, &extra) / 2 != res || extra) - goto err; - if ((p = strtokm(NULL, "*")) == NULL) /* len_hmac_sha1_key */ - goto err; - if (!isdec(p)) - goto err; - res = atoi(p); - if (res > 300) - goto err; - if ((p = strtokm(NULL, "*")) == NULL) /* hmac_sha1_key */ - goto err; - if (strlen(p) / 2 != res) - goto err; - } - else - goto err; - MEM_FREE(keeptr); - return 1; - -err: - MEM_FREE(keeptr); - return 0; + return dmg_valid(ciphertext, self); } static void *get_salt(char *ciphertext) @@ -294,7 +179,25 @@ static void *get_salt(char *ciphertext) ctcopy += FORMAT_TAG_LEN; p = strtokm(ctcopy, "*"); cs.headerver = atoi(p); - if (cs.headerver == 2) { + if (cs.headerver == 3) { + p = strtokm(NULL, "*"); + cs.saltlen = atoi(p); + p = strtokm(NULL, "*"); + for (i = 0; i < cs.saltlen; i++) + cs.salt[i] = atoi16[ARCH_INDEX(p[i * 2])] * 16 + + atoi16[ARCH_INDEX(p[i * 2 + 1])]; + p = strtokm(NULL, "*"); + cs.blob_enc_keybits = atoi(p); + p = strtokm(NULL, "*"); + cs.encrypted_keyblob_size = atoi(p); + p = strtokm(NULL, "*"); + for (i = 0; i < cs.encrypted_keyblob_size; i++) + cs.encrypted_keyblob[i] = atoi16[ARCH_INDEX(p[i * 2])] * 16 + + atoi16[ARCH_INDEX(p[i * 2 + 1])]; + p = strtokm(NULL, "*"); + cs.iterations = atoi(p); + } + else if (cs.headerver == 2) { p = strtokm(NULL, "*"); cs.saltlen = atoi(p); p = strtokm(NULL, "*"); @@ -402,7 +305,55 @@ static void hash_plugin_check_hash(int index) unsigned char aes_key_[32]; int j; - if (cur_salt->headerver == 1) { + if (cur_salt->headerver == 3) { +#ifdef SIMD_COEF_32 + unsigned char *derived_key, Derived_key[SSE_GROUP_SZ_SHA1][32]; + int lens[SSE_GROUP_SZ_SHA1], i; + unsigned char *pin[SSE_GROUP_SZ_SHA1]; + union { + uint32_t *pout[SSE_GROUP_SZ_SHA1]; + unsigned char *poutc; + } x; + for (i = 0; i < SSE_GROUP_SZ_SHA1; ++i) { + lens[i] = strlen(saved_key[index+i]); + pin[i] = (unsigned char*)saved_key[index+i]; + x.pout[i] = (uint32_t*)(Derived_key[i]); + } + pbkdf2_sha1_sse((const unsigned char **)pin, lens, cur_salt->salt, + cur_salt->saltlen, + cur_salt->iterations, &(x.poutc), 32, 0); +#else + unsigned char derived_key[32]; + const char *password = saved_key[index]; + pbkdf2_sha1((const unsigned char*)password, strlen(password), + cur_salt->salt, cur_salt->saltlen, cur_salt->iterations, + derived_key, 32, 0); +#endif + j = 0; +#ifdef SIMD_COEF_32 + for (j = 0; j < SSE_GROUP_SZ_SHA1; ++j) { + derived_key = Derived_key[j]; +#endif + AES_KEY aes_decrypt_key; + unsigned char last_block[16]; + int i; + + AES_set_decrypt_key(derived_key, cur_salt->blob_enc_keybits, + &aes_decrypt_key); + AES_decrypt(&cur_salt->encrypted_keyblob[48], last_block, + &aes_decrypt_key); + for (i = 0; i < 16; i++) + last_block[i] ^= cur_salt->encrypted_keyblob[32 + i]; + + /* 9 = 4-byte prefix + "CKIE" (4) + null (1); remaining 7 bytes are PKCS#7 padding */ + if (!memcmp(&last_block[4], "CKIE", 4) && !last_block[8] && + check_pkcs_pad(last_block, 16, 16) == 9) + cracked[index+j] = 1; +#ifdef SIMD_COEF_32 + } +#endif + } + else if (cur_salt->headerver == 1) { #ifdef SIMD_COEF_32 unsigned char *derived_key, Derived_key[SSE_GROUP_SZ_SHA1][32]; int lens[SSE_GROUP_SZ_SHA1], i; diff --git a/src/opencl_dmg_fmt_plug.c b/src/opencl_dmg_fmt_plug.c index 09d549b6534..c2f8a30f8e4 100644 --- a/src/opencl_dmg_fmt_plug.c +++ b/src/opencl_dmg_fmt_plug.c @@ -31,8 +31,7 @@ john_register_one(&fmt_opencl_dmg); #define FORMAT_LABEL "dmg-opencl" #define FORMAT_NAME "Apple DMG" -#define FORMAT_TAG "$dmg$" -#define FORMAT_TAG_LEN (sizeof(FORMAT_TAG)-1) +/* FORMAT_TAG / FORMAT_TAG_LEN are defined in dmg_common.h */ #define ALGORITHM_NAME "PBKDF2-SHA1 3DES/AES OpenCL" #define BENCHMARK_COMMENT "" #define BENCHMARK_LENGTH 0x107 @@ -52,7 +51,8 @@ typedef struct { unsigned int ivlen; unsigned char iv[32]; uint32_t encrypted_keyblob_size; - uint8_t encrypted_keyblob[32]; + uint8_t encrypted_keyblob[64]; + unsigned int blob_enc_keybits; unsigned int len_wrapped_aes_key; unsigned char wrapped_aes_key[296]; unsigned int len_hmac_sha1_key; @@ -78,7 +78,7 @@ static cl_mem mem_in, mem_out, mem_salt, mem_state; static int new_keys; static struct fmt_main *self; -static cl_kernel pbkdf2_init, pbkdf2_loop, pbkdf2_final, dmg_final[3]; +static cl_kernel pbkdf2_init, pbkdf2_loop, pbkdf2_final, dmg_final[4]; /* * HASH_LOOPS is ideally made by factors of (iteration count - 1) and should @@ -108,6 +108,7 @@ static size_t get_task_max_work_group_size() s = MIN(s, autotune_get_task_max_work_group_size(FALSE, 0, pbkdf2_final)); s = MIN(s, autotune_get_task_max_work_group_size(FALSE, 0, dmg_final[1])); s = MIN(s, autotune_get_task_max_work_group_size(FALSE, 0, dmg_final[2])); + s = MIN(s, autotune_get_task_max_work_group_size(FALSE, 0, dmg_final[3])); return s; } @@ -151,6 +152,9 @@ static void create_clobj(size_t gws, struct fmt_main *self) HANDLE_CLERROR(clSetKernelArg(dmg_final[2], 0, sizeof(mem_salt), &mem_salt), "Error while setting mem_salt kernel argument"); HANDLE_CLERROR(clSetKernelArg(dmg_final[2], 1, sizeof(mem_out), &mem_out), "Error while setting mem_out kernel argument"); + + HANDLE_CLERROR(clSetKernelArg(dmg_final[3], 0, sizeof(mem_salt), &mem_salt), "Error while setting mem_salt kernel argument"); + HANDLE_CLERROR(clSetKernelArg(dmg_final[3], 1, sizeof(mem_out), &mem_out), "Error while setting mem_out kernel argument"); } static void release_clobj(void) @@ -177,6 +181,7 @@ static void done(void) HANDLE_CLERROR(clReleaseKernel(pbkdf2_final), "Release kernel"); HANDLE_CLERROR(clReleaseKernel(dmg_final[1]), "Release kernel"); HANDLE_CLERROR(clReleaseKernel(dmg_final[2]), "Release kernel"); + HANDLE_CLERROR(clReleaseKernel(dmg_final[3]), "Release kernel"); HANDLE_CLERROR(clReleaseProgram(program[gpu_id]), "Release Program"); program[gpu_id] = NULL; @@ -223,6 +228,8 @@ static void reset(struct db_main *db) HANDLE_CLERROR(ret_code, "Error creating kernel"); dmg_final[2] = clCreateKernel(program[gpu_id], "dmg_final_v2", &ret_code); HANDLE_CLERROR(ret_code, "Error creating kernel"); + dmg_final[3] = clCreateKernel(program[gpu_id], "dmg_final_v3", &ret_code); + HANDLE_CLERROR(ret_code, "Error creating kernel"); } // FIXME: Share in opencl_autotune.h @@ -240,122 +247,7 @@ static void reset(struct db_main *db) static int valid(char *ciphertext, struct fmt_main *self) { - char *ctcopy, *keeptr; - char *p; - int headerver; - int res, extra; - - if (strncmp(ciphertext, FORMAT_TAG, FORMAT_TAG_LEN) != 0) - return 0; - ctcopy = xstrdup(ciphertext); - keeptr = ctcopy; - ctcopy += FORMAT_TAG_LEN; /* skip over "$dmg$" marker */ - if ((p = strtokm(ctcopy, "*")) == NULL) - goto err; - headerver = atoi(p); - if (headerver == 2) { - if ((p = strtokm(NULL, "*")) == NULL) /* salt len */ - goto err; - if (!isdec(p)) - goto err; - res = atoi(p); - if (res > 20) - goto err; - if ((p = strtokm(NULL, "*")) == NULL) /* salt */ - goto err; - if (hexlenl(p, &extra) != res*2 || extra) - goto err; - if ((p = strtokm(NULL, "*")) == NULL) /* ivlen */ - goto err; - if (!isdec(p)) - goto err; - res = atoi(p); - if (atoi(p) > sizeof(cur_salt->iv)) - goto err; - if ((p = strtokm(NULL, "*")) == NULL) /* iv */ - goto err; - if (hexlenl(p, &extra) != res*2 || extra) - goto err; - if ((p = strtokm(NULL, "*")) == NULL) /* encrypted_keyblob_size */ - goto err; - if (!isdec(p)) - goto err; - res = atoi(p); - if (res > 128) /* This is truncated to 32 anyway, in get_salt */ - goto err; - if ((p = strtokm(NULL, "*")) == NULL) /* encrypted keyblob */ - goto err; - if (hexlenl(p, &extra) != res*2 || extra) - goto err; - if ((p = strtokm(NULL, "*")) == NULL) /* chunk number */ - goto err; - if ((p = strtokm(NULL, "*")) == NULL) /* data_size */ - goto err; - if (!isdec(p)) - goto err; - res = atoi(p); - if ((p = strtokm(NULL, "*")) == NULL) /* chunk */ - goto err; - if (hexlenl(p, &extra) != res*2 || extra) - goto err; - if (res > sizeof(cur_salt->chunk)) - goto err; - if ((p = strtokm(NULL, "*")) == NULL) /* scp */ - goto err; - if (!isdec(p)) - goto err; - res = atoi(p); - if (res == 1) { - if ((p = strtokm(NULL, "*")) == NULL) /* zchunk */ - goto err; - if (strlen(p) != 4096 * 2) - goto err; - } else if (res != 0) - goto err; - } - else if (headerver == 1) { - if ((p = strtokm(NULL, "*")) == NULL) /* salt len */ - goto err; - if (!isdec(p)) - goto err; - res = atoi(p); - if (res > 20) - goto err; - if ((p = strtokm(NULL, "*")) == NULL) /* salt */ - goto err; - if (hexlenl(p, &extra) != res*2 || extra) - goto err; - if ((p = strtokm(NULL, "*")) == NULL) /* len_wrapped_aes_key */ - goto err; - if (!isdec(p)) - goto err; - res = atoi(p); - if (res > sizeof(cur_salt->wrapped_aes_key)) - goto err; - if ((p = strtokm(NULL, "*")) == NULL) /* wrapped_aes_key */ - goto err; - if (hexlenl(p, &extra) != res*2 || extra) - goto err; - if ((p = strtokm(NULL, "*")) == NULL) /* len_hmac_sha1_key */ - goto err; - if (!isdec(p)) - goto err; - res = atoi(p); - if (res > sizeof(cur_salt->wrapped_hmac_sha1_key)) - goto err; - if ((p = strtokm(NULL, "*")) == NULL) /* hmac_sha1_key */ - goto err; - if (strlen(p) / 2 != res) - goto err; - } - else - goto err; - MEM_FREE(keeptr); - return 1; - -err: - MEM_FREE(keeptr); - return 0; + return dmg_valid(ciphertext, self); } static void *get_salt(char *ciphertext) @@ -370,7 +262,25 @@ static void *get_salt(char *ciphertext) ctcopy += FORMAT_TAG_LEN; p = strtokm(ctcopy, "*"); cs.headerver = atoi(p); - if (cs.headerver == 2) { + if (cs.headerver == 3) { + p = strtokm(NULL, "*"); + cs.pbkdf2.length = atoi(p); + p = strtokm(NULL, "*"); + for (i = 0; i < cs.pbkdf2.length; i++) + cs.pbkdf2.salt[i] = atoi16[ARCH_INDEX(p[i * 2])] * 16 + + atoi16[ARCH_INDEX(p[i * 2 + 1])]; + p = strtokm(NULL, "*"); + cs.blob_enc_keybits = atoi(p); + p = strtokm(NULL, "*"); + cs.encrypted_keyblob_size = atoi(p); + p = strtokm(NULL, "*"); + for (i = 0; i < cs.encrypted_keyblob_size; i++) + cs.encrypted_keyblob[i] = atoi16[ARCH_INDEX(p[i * 2])] * 16 + + atoi16[ARCH_INDEX(p[i * 2 + 1])]; + p = strtokm(NULL, "*"); + cs.pbkdf2.iterations = atoi(p); + } + else if (cs.headerver == 2) { p = strtokm(NULL, "*"); cs.pbkdf2.length = atoi(p); p = strtokm(NULL, "*");