commit 8707e18fa947d4a8f563cb512336c13e25f88ccb Author: Li Date: Mon Jul 24 00:15:53 2023 +1200 Inital Commit: diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..be695f6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +out/* +node_modules/* + diff --git a/aes.js b/aes.js new file mode 100644 index 0000000..13c3950 --- /dev/null +++ b/aes.js @@ -0,0 +1,1451 @@ +const rounds = 14 // 256 bit key +const roundsX4 = rounds * 4 +const roundKeyCount = (rounds + 1) * 4 + +// Round constant words +const rcon = new Uint8Array([ + 0x01, + 0x02, + 0x04, + 0x08, + 0x10, + 0x20, + 0x40, + 0x80, + 0x1b, + 0x36, + 0x6c, + 0xd8, + 0xab, + 0x4d, + 0x9a, + 0x2f, + 0x5e, + 0xbc, + 0x63, + 0xc6, + 0x97, + 0x35, + 0x6a, + 0xd4, + 0xb3, + 0x7d, + 0xfa, + 0xef, + 0xc5, + 0x91 +]) + +// S-box and Inverse S-box (S is for Substitution) +const S = new Uint8Array([ + 0x63, + 0x7c, + 0x77, + 0x7b, + 0xf2, + 0x6b, + 0x6f, + 0xc5, + 0x30, + 0x01, + 0x67, + 0x2b, + 0xfe, + 0xd7, + 0xab, + 0x76, + 0xca, + 0x82, + 0xc9, + 0x7d, + 0xfa, + 0x59, + 0x47, + 0xf0, + 0xad, + 0xd4, + 0xa2, + 0xaf, + 0x9c, + 0xa4, + 0x72, + 0xc0, + 0xb7, + 0xfd, + 0x93, + 0x26, + 0x36, + 0x3f, + 0xf7, + 0xcc, + 0x34, + 0xa5, + 0xe5, + 0xf1, + 0x71, + 0xd8, + 0x31, + 0x15, + 0x04, + 0xc7, + 0x23, + 0xc3, + 0x18, + 0x96, + 0x05, + 0x9a, + 0x07, + 0x12, + 0x80, + 0xe2, + 0xeb, + 0x27, + 0xb2, + 0x75, + 0x09, + 0x83, + 0x2c, + 0x1a, + 0x1b, + 0x6e, + 0x5a, + 0xa0, + 0x52, + 0x3b, + 0xd6, + 0xb3, + 0x29, + 0xe3, + 0x2f, + 0x84, + 0x53, + 0xd1, + 0x00, + 0xed, + 0x20, + 0xfc, + 0xb1, + 0x5b, + 0x6a, + 0xcb, + 0xbe, + 0x39, + 0x4a, + 0x4c, + 0x58, + 0xcf, + 0xd0, + 0xef, + 0xaa, + 0xfb, + 0x43, + 0x4d, + 0x33, + 0x85, + 0x45, + 0xf9, + 0x02, + 0x7f, + 0x50, + 0x3c, + 0x9f, + 0xa8, + 0x51, + 0xa3, + 0x40, + 0x8f, + 0x92, + 0x9d, + 0x38, + 0xf5, + 0xbc, + 0xb6, + 0xda, + 0x21, + 0x10, + 0xff, + 0xf3, + 0xd2, + 0xcd, + 0x0c, + 0x13, + 0xec, + 0x5f, + 0x97, + 0x44, + 0x17, + 0xc4, + 0xa7, + 0x7e, + 0x3d, + 0x64, + 0x5d, + 0x19, + 0x73, + 0x60, + 0x81, + 0x4f, + 0xdc, + 0x22, + 0x2a, + 0x90, + 0x88, + 0x46, + 0xee, + 0xb8, + 0x14, + 0xde, + 0x5e, + 0x0b, + 0xdb, + 0xe0, + 0x32, + 0x3a, + 0x0a, + 0x49, + 0x06, + 0x24, + 0x5c, + 0xc2, + 0xd3, + 0xac, + 0x62, + 0x91, + 0x95, + 0xe4, + 0x79, + 0xe7, + 0xc8, + 0x37, + 0x6d, + 0x8d, + 0xd5, + 0x4e, + 0xa9, + 0x6c, + 0x56, + 0xf4, + 0xea, + 0x65, + 0x7a, + 0xae, + 0x08, + 0xba, + 0x78, + 0x25, + 0x2e, + 0x1c, + 0xa6, + 0xb4, + 0xc6, + 0xe8, + 0xdd, + 0x74, + 0x1f, + 0x4b, + 0xbd, + 0x8b, + 0x8a, + 0x70, + 0x3e, + 0xb5, + 0x66, + 0x48, + 0x03, + 0xf6, + 0x0e, + 0x61, + 0x35, + 0x57, + 0xb9, + 0x86, + 0xc1, + 0x1d, + 0x9e, + 0xe1, + 0xf8, + 0x98, + 0x11, + 0x69, + 0xd9, + 0x8e, + 0x94, + 0x9b, + 0x1e, + 0x87, + 0xe9, + 0xce, + 0x55, + 0x28, + 0xdf, + 0x8c, + 0xa1, + 0x89, + 0x0d, + 0xbf, + 0xe6, + 0x42, + 0x68, + 0x41, + 0x99, + 0x2d, + 0x0f, + 0xb0, + 0x54, + 0xbb, + 0x16 +]) + +// Transformations for encryption +const T1 = new Uint32Array([ + 0xc66363a5, + 0xf87c7c84, + 0xee777799, + 0xf67b7b8d, + 0xfff2f20d, + 0xd66b6bbd, + 0xde6f6fb1, + 0x91c5c554, + 0x60303050, + 0x02010103, + 0xce6767a9, + 0x562b2b7d, + 0xe7fefe19, + 0xb5d7d762, + 0x4dababe6, + 0xec76769a, + 0x8fcaca45, + 0x1f82829d, + 0x89c9c940, + 0xfa7d7d87, + 0xeffafa15, + 0xb25959eb, + 0x8e4747c9, + 0xfbf0f00b, + 0x41adadec, + 0xb3d4d467, + 0x5fa2a2fd, + 0x45afafea, + 0x239c9cbf, + 0x53a4a4f7, + 0xe4727296, + 0x9bc0c05b, + 0x75b7b7c2, + 0xe1fdfd1c, + 0x3d9393ae, + 0x4c26266a, + 0x6c36365a, + 0x7e3f3f41, + 0xf5f7f702, + 0x83cccc4f, + 0x6834345c, + 0x51a5a5f4, + 0xd1e5e534, + 0xf9f1f108, + 0xe2717193, + 0xabd8d873, + 0x62313153, + 0x2a15153f, + 0x0804040c, + 0x95c7c752, + 0x46232365, + 0x9dc3c35e, + 0x30181828, + 0x379696a1, + 0x0a05050f, + 0x2f9a9ab5, + 0x0e070709, + 0x24121236, + 0x1b80809b, + 0xdfe2e23d, + 0xcdebeb26, + 0x4e272769, + 0x7fb2b2cd, + 0xea75759f, + 0x1209091b, + 0x1d83839e, + 0x582c2c74, + 0x341a1a2e, + 0x361b1b2d, + 0xdc6e6eb2, + 0xb45a5aee, + 0x5ba0a0fb, + 0xa45252f6, + 0x763b3b4d, + 0xb7d6d661, + 0x7db3b3ce, + 0x5229297b, + 0xdde3e33e, + 0x5e2f2f71, + 0x13848497, + 0xa65353f5, + 0xb9d1d168, + 0x00000000, + 0xc1eded2c, + 0x40202060, + 0xe3fcfc1f, + 0x79b1b1c8, + 0xb65b5bed, + 0xd46a6abe, + 0x8dcbcb46, + 0x67bebed9, + 0x7239394b, + 0x944a4ade, + 0x984c4cd4, + 0xb05858e8, + 0x85cfcf4a, + 0xbbd0d06b, + 0xc5efef2a, + 0x4faaaae5, + 0xedfbfb16, + 0x864343c5, + 0x9a4d4dd7, + 0x66333355, + 0x11858594, + 0x8a4545cf, + 0xe9f9f910, + 0x04020206, + 0xfe7f7f81, + 0xa05050f0, + 0x783c3c44, + 0x259f9fba, + 0x4ba8a8e3, + 0xa25151f3, + 0x5da3a3fe, + 0x804040c0, + 0x058f8f8a, + 0x3f9292ad, + 0x219d9dbc, + 0x70383848, + 0xf1f5f504, + 0x63bcbcdf, + 0x77b6b6c1, + 0xafdada75, + 0x42212163, + 0x20101030, + 0xe5ffff1a, + 0xfdf3f30e, + 0xbfd2d26d, + 0x81cdcd4c, + 0x180c0c14, + 0x26131335, + 0xc3ecec2f, + 0xbe5f5fe1, + 0x359797a2, + 0x884444cc, + 0x2e171739, + 0x93c4c457, + 0x55a7a7f2, + 0xfc7e7e82, + 0x7a3d3d47, + 0xc86464ac, + 0xba5d5de7, + 0x3219192b, + 0xe6737395, + 0xc06060a0, + 0x19818198, + 0x9e4f4fd1, + 0xa3dcdc7f, + 0x44222266, + 0x542a2a7e, + 0x3b9090ab, + 0x0b888883, + 0x8c4646ca, + 0xc7eeee29, + 0x6bb8b8d3, + 0x2814143c, + 0xa7dede79, + 0xbc5e5ee2, + 0x160b0b1d, + 0xaddbdb76, + 0xdbe0e03b, + 0x64323256, + 0x743a3a4e, + 0x140a0a1e, + 0x924949db, + 0x0c06060a, + 0x4824246c, + 0xb85c5ce4, + 0x9fc2c25d, + 0xbdd3d36e, + 0x43acacef, + 0xc46262a6, + 0x399191a8, + 0x319595a4, + 0xd3e4e437, + 0xf279798b, + 0xd5e7e732, + 0x8bc8c843, + 0x6e373759, + 0xda6d6db7, + 0x018d8d8c, + 0xb1d5d564, + 0x9c4e4ed2, + 0x49a9a9e0, + 0xd86c6cb4, + 0xac5656fa, + 0xf3f4f407, + 0xcfeaea25, + 0xca6565af, + 0xf47a7a8e, + 0x47aeaee9, + 0x10080818, + 0x6fbabad5, + 0xf0787888, + 0x4a25256f, + 0x5c2e2e72, + 0x381c1c24, + 0x57a6a6f1, + 0x73b4b4c7, + 0x97c6c651, + 0xcbe8e823, + 0xa1dddd7c, + 0xe874749c, + 0x3e1f1f21, + 0x964b4bdd, + 0x61bdbddc, + 0x0d8b8b86, + 0x0f8a8a85, + 0xe0707090, + 0x7c3e3e42, + 0x71b5b5c4, + 0xcc6666aa, + 0x904848d8, + 0x06030305, + 0xf7f6f601, + 0x1c0e0e12, + 0xc26161a3, + 0x6a35355f, + 0xae5757f9, + 0x69b9b9d0, + 0x17868691, + 0x99c1c158, + 0x3a1d1d27, + 0x279e9eb9, + 0xd9e1e138, + 0xebf8f813, + 0x2b9898b3, + 0x22111133, + 0xd26969bb, + 0xa9d9d970, + 0x078e8e89, + 0x339494a7, + 0x2d9b9bb6, + 0x3c1e1e22, + 0x15878792, + 0xc9e9e920, + 0x87cece49, + 0xaa5555ff, + 0x50282878, + 0xa5dfdf7a, + 0x038c8c8f, + 0x59a1a1f8, + 0x09898980, + 0x1a0d0d17, + 0x65bfbfda, + 0xd7e6e631, + 0x844242c6, + 0xd06868b8, + 0x824141c3, + 0x299999b0, + 0x5a2d2d77, + 0x1e0f0f11, + 0x7bb0b0cb, + 0xa85454fc, + 0x6dbbbbd6, + 0x2c16163a +]) +const T2 = new Uint32Array([ + 0xa5c66363, + 0x84f87c7c, + 0x99ee7777, + 0x8df67b7b, + 0x0dfff2f2, + 0xbdd66b6b, + 0xb1de6f6f, + 0x5491c5c5, + 0x50603030, + 0x03020101, + 0xa9ce6767, + 0x7d562b2b, + 0x19e7fefe, + 0x62b5d7d7, + 0xe64dabab, + 0x9aec7676, + 0x458fcaca, + 0x9d1f8282, + 0x4089c9c9, + 0x87fa7d7d, + 0x15effafa, + 0xebb25959, + 0xc98e4747, + 0x0bfbf0f0, + 0xec41adad, + 0x67b3d4d4, + 0xfd5fa2a2, + 0xea45afaf, + 0xbf239c9c, + 0xf753a4a4, + 0x96e47272, + 0x5b9bc0c0, + 0xc275b7b7, + 0x1ce1fdfd, + 0xae3d9393, + 0x6a4c2626, + 0x5a6c3636, + 0x417e3f3f, + 0x02f5f7f7, + 0x4f83cccc, + 0x5c683434, + 0xf451a5a5, + 0x34d1e5e5, + 0x08f9f1f1, + 0x93e27171, + 0x73abd8d8, + 0x53623131, + 0x3f2a1515, + 0x0c080404, + 0x5295c7c7, + 0x65462323, + 0x5e9dc3c3, + 0x28301818, + 0xa1379696, + 0x0f0a0505, + 0xb52f9a9a, + 0x090e0707, + 0x36241212, + 0x9b1b8080, + 0x3ddfe2e2, + 0x26cdebeb, + 0x694e2727, + 0xcd7fb2b2, + 0x9fea7575, + 0x1b120909, + 0x9e1d8383, + 0x74582c2c, + 0x2e341a1a, + 0x2d361b1b, + 0xb2dc6e6e, + 0xeeb45a5a, + 0xfb5ba0a0, + 0xf6a45252, + 0x4d763b3b, + 0x61b7d6d6, + 0xce7db3b3, + 0x7b522929, + 0x3edde3e3, + 0x715e2f2f, + 0x97138484, + 0xf5a65353, + 0x68b9d1d1, + 0x00000000, + 0x2cc1eded, + 0x60402020, + 0x1fe3fcfc, + 0xc879b1b1, + 0xedb65b5b, + 0xbed46a6a, + 0x468dcbcb, + 0xd967bebe, + 0x4b723939, + 0xde944a4a, + 0xd4984c4c, + 0xe8b05858, + 0x4a85cfcf, + 0x6bbbd0d0, + 0x2ac5efef, + 0xe54faaaa, + 0x16edfbfb, + 0xc5864343, + 0xd79a4d4d, + 0x55663333, + 0x94118585, + 0xcf8a4545, + 0x10e9f9f9, + 0x06040202, + 0x81fe7f7f, + 0xf0a05050, + 0x44783c3c, + 0xba259f9f, + 0xe34ba8a8, + 0xf3a25151, + 0xfe5da3a3, + 0xc0804040, + 0x8a058f8f, + 0xad3f9292, + 0xbc219d9d, + 0x48703838, + 0x04f1f5f5, + 0xdf63bcbc, + 0xc177b6b6, + 0x75afdada, + 0x63422121, + 0x30201010, + 0x1ae5ffff, + 0x0efdf3f3, + 0x6dbfd2d2, + 0x4c81cdcd, + 0x14180c0c, + 0x35261313, + 0x2fc3ecec, + 0xe1be5f5f, + 0xa2359797, + 0xcc884444, + 0x392e1717, + 0x5793c4c4, + 0xf255a7a7, + 0x82fc7e7e, + 0x477a3d3d, + 0xacc86464, + 0xe7ba5d5d, + 0x2b321919, + 0x95e67373, + 0xa0c06060, + 0x98198181, + 0xd19e4f4f, + 0x7fa3dcdc, + 0x66442222, + 0x7e542a2a, + 0xab3b9090, + 0x830b8888, + 0xca8c4646, + 0x29c7eeee, + 0xd36bb8b8, + 0x3c281414, + 0x79a7dede, + 0xe2bc5e5e, + 0x1d160b0b, + 0x76addbdb, + 0x3bdbe0e0, + 0x56643232, + 0x4e743a3a, + 0x1e140a0a, + 0xdb924949, + 0x0a0c0606, + 0x6c482424, + 0xe4b85c5c, + 0x5d9fc2c2, + 0x6ebdd3d3, + 0xef43acac, + 0xa6c46262, + 0xa8399191, + 0xa4319595, + 0x37d3e4e4, + 0x8bf27979, + 0x32d5e7e7, + 0x438bc8c8, + 0x596e3737, + 0xb7da6d6d, + 0x8c018d8d, + 0x64b1d5d5, + 0xd29c4e4e, + 0xe049a9a9, + 0xb4d86c6c, + 0xfaac5656, + 0x07f3f4f4, + 0x25cfeaea, + 0xafca6565, + 0x8ef47a7a, + 0xe947aeae, + 0x18100808, + 0xd56fbaba, + 0x88f07878, + 0x6f4a2525, + 0x725c2e2e, + 0x24381c1c, + 0xf157a6a6, + 0xc773b4b4, + 0x5197c6c6, + 0x23cbe8e8, + 0x7ca1dddd, + 0x9ce87474, + 0x213e1f1f, + 0xdd964b4b, + 0xdc61bdbd, + 0x860d8b8b, + 0x850f8a8a, + 0x90e07070, + 0x427c3e3e, + 0xc471b5b5, + 0xaacc6666, + 0xd8904848, + 0x05060303, + 0x01f7f6f6, + 0x121c0e0e, + 0xa3c26161, + 0x5f6a3535, + 0xf9ae5757, + 0xd069b9b9, + 0x91178686, + 0x5899c1c1, + 0x273a1d1d, + 0xb9279e9e, + 0x38d9e1e1, + 0x13ebf8f8, + 0xb32b9898, + 0x33221111, + 0xbbd26969, + 0x70a9d9d9, + 0x89078e8e, + 0xa7339494, + 0xb62d9b9b, + 0x223c1e1e, + 0x92158787, + 0x20c9e9e9, + 0x4987cece, + 0xffaa5555, + 0x78502828, + 0x7aa5dfdf, + 0x8f038c8c, + 0xf859a1a1, + 0x80098989, + 0x171a0d0d, + 0xda65bfbf, + 0x31d7e6e6, + 0xc6844242, + 0xb8d06868, + 0xc3824141, + 0xb0299999, + 0x775a2d2d, + 0x111e0f0f, + 0xcb7bb0b0, + 0xfca85454, + 0xd66dbbbb, + 0x3a2c1616 +]) +const T3 = new Uint32Array([ + 0x63a5c663, + 0x7c84f87c, + 0x7799ee77, + 0x7b8df67b, + 0xf20dfff2, + 0x6bbdd66b, + 0x6fb1de6f, + 0xc55491c5, + 0x30506030, + 0x01030201, + 0x67a9ce67, + 0x2b7d562b, + 0xfe19e7fe, + 0xd762b5d7, + 0xabe64dab, + 0x769aec76, + 0xca458fca, + 0x829d1f82, + 0xc94089c9, + 0x7d87fa7d, + 0xfa15effa, + 0x59ebb259, + 0x47c98e47, + 0xf00bfbf0, + 0xadec41ad, + 0xd467b3d4, + 0xa2fd5fa2, + 0xafea45af, + 0x9cbf239c, + 0xa4f753a4, + 0x7296e472, + 0xc05b9bc0, + 0xb7c275b7, + 0xfd1ce1fd, + 0x93ae3d93, + 0x266a4c26, + 0x365a6c36, + 0x3f417e3f, + 0xf702f5f7, + 0xcc4f83cc, + 0x345c6834, + 0xa5f451a5, + 0xe534d1e5, + 0xf108f9f1, + 0x7193e271, + 0xd873abd8, + 0x31536231, + 0x153f2a15, + 0x040c0804, + 0xc75295c7, + 0x23654623, + 0xc35e9dc3, + 0x18283018, + 0x96a13796, + 0x050f0a05, + 0x9ab52f9a, + 0x07090e07, + 0x12362412, + 0x809b1b80, + 0xe23ddfe2, + 0xeb26cdeb, + 0x27694e27, + 0xb2cd7fb2, + 0x759fea75, + 0x091b1209, + 0x839e1d83, + 0x2c74582c, + 0x1a2e341a, + 0x1b2d361b, + 0x6eb2dc6e, + 0x5aeeb45a, + 0xa0fb5ba0, + 0x52f6a452, + 0x3b4d763b, + 0xd661b7d6, + 0xb3ce7db3, + 0x297b5229, + 0xe33edde3, + 0x2f715e2f, + 0x84971384, + 0x53f5a653, + 0xd168b9d1, + 0x00000000, + 0xed2cc1ed, + 0x20604020, + 0xfc1fe3fc, + 0xb1c879b1, + 0x5bedb65b, + 0x6abed46a, + 0xcb468dcb, + 0xbed967be, + 0x394b7239, + 0x4ade944a, + 0x4cd4984c, + 0x58e8b058, + 0xcf4a85cf, + 0xd06bbbd0, + 0xef2ac5ef, + 0xaae54faa, + 0xfb16edfb, + 0x43c58643, + 0x4dd79a4d, + 0x33556633, + 0x85941185, + 0x45cf8a45, + 0xf910e9f9, + 0x02060402, + 0x7f81fe7f, + 0x50f0a050, + 0x3c44783c, + 0x9fba259f, + 0xa8e34ba8, + 0x51f3a251, + 0xa3fe5da3, + 0x40c08040, + 0x8f8a058f, + 0x92ad3f92, + 0x9dbc219d, + 0x38487038, + 0xf504f1f5, + 0xbcdf63bc, + 0xb6c177b6, + 0xda75afda, + 0x21634221, + 0x10302010, + 0xff1ae5ff, + 0xf30efdf3, + 0xd26dbfd2, + 0xcd4c81cd, + 0x0c14180c, + 0x13352613, + 0xec2fc3ec, + 0x5fe1be5f, + 0x97a23597, + 0x44cc8844, + 0x17392e17, + 0xc45793c4, + 0xa7f255a7, + 0x7e82fc7e, + 0x3d477a3d, + 0x64acc864, + 0x5de7ba5d, + 0x192b3219, + 0x7395e673, + 0x60a0c060, + 0x81981981, + 0x4fd19e4f, + 0xdc7fa3dc, + 0x22664422, + 0x2a7e542a, + 0x90ab3b90, + 0x88830b88, + 0x46ca8c46, + 0xee29c7ee, + 0xb8d36bb8, + 0x143c2814, + 0xde79a7de, + 0x5ee2bc5e, + 0x0b1d160b, + 0xdb76addb, + 0xe03bdbe0, + 0x32566432, + 0x3a4e743a, + 0x0a1e140a, + 0x49db9249, + 0x060a0c06, + 0x246c4824, + 0x5ce4b85c, + 0xc25d9fc2, + 0xd36ebdd3, + 0xacef43ac, + 0x62a6c462, + 0x91a83991, + 0x95a43195, + 0xe437d3e4, + 0x798bf279, + 0xe732d5e7, + 0xc8438bc8, + 0x37596e37, + 0x6db7da6d, + 0x8d8c018d, + 0xd564b1d5, + 0x4ed29c4e, + 0xa9e049a9, + 0x6cb4d86c, + 0x56faac56, + 0xf407f3f4, + 0xea25cfea, + 0x65afca65, + 0x7a8ef47a, + 0xaee947ae, + 0x08181008, + 0xbad56fba, + 0x7888f078, + 0x256f4a25, + 0x2e725c2e, + 0x1c24381c, + 0xa6f157a6, + 0xb4c773b4, + 0xc65197c6, + 0xe823cbe8, + 0xdd7ca1dd, + 0x749ce874, + 0x1f213e1f, + 0x4bdd964b, + 0xbddc61bd, + 0x8b860d8b, + 0x8a850f8a, + 0x7090e070, + 0x3e427c3e, + 0xb5c471b5, + 0x66aacc66, + 0x48d89048, + 0x03050603, + 0xf601f7f6, + 0x0e121c0e, + 0x61a3c261, + 0x355f6a35, + 0x57f9ae57, + 0xb9d069b9, + 0x86911786, + 0xc15899c1, + 0x1d273a1d, + 0x9eb9279e, + 0xe138d9e1, + 0xf813ebf8, + 0x98b32b98, + 0x11332211, + 0x69bbd269, + 0xd970a9d9, + 0x8e89078e, + 0x94a73394, + 0x9bb62d9b, + 0x1e223c1e, + 0x87921587, + 0xe920c9e9, + 0xce4987ce, + 0x55ffaa55, + 0x28785028, + 0xdf7aa5df, + 0x8c8f038c, + 0xa1f859a1, + 0x89800989, + 0x0d171a0d, + 0xbfda65bf, + 0xe631d7e6, + 0x42c68442, + 0x68b8d068, + 0x41c38241, + 0x99b02999, + 0x2d775a2d, + 0x0f111e0f, + 0xb0cb7bb0, + 0x54fca854, + 0xbbd66dbb, + 0x163a2c16 +]) +const T4 = new Uint32Array([ + 0x6363a5c6, + 0x7c7c84f8, + 0x777799ee, + 0x7b7b8df6, + 0xf2f20dff, + 0x6b6bbdd6, + 0x6f6fb1de, + 0xc5c55491, + 0x30305060, + 0x01010302, + 0x6767a9ce, + 0x2b2b7d56, + 0xfefe19e7, + 0xd7d762b5, + 0xababe64d, + 0x76769aec, + 0xcaca458f, + 0x82829d1f, + 0xc9c94089, + 0x7d7d87fa, + 0xfafa15ef, + 0x5959ebb2, + 0x4747c98e, + 0xf0f00bfb, + 0xadadec41, + 0xd4d467b3, + 0xa2a2fd5f, + 0xafafea45, + 0x9c9cbf23, + 0xa4a4f753, + 0x727296e4, + 0xc0c05b9b, + 0xb7b7c275, + 0xfdfd1ce1, + 0x9393ae3d, + 0x26266a4c, + 0x36365a6c, + 0x3f3f417e, + 0xf7f702f5, + 0xcccc4f83, + 0x34345c68, + 0xa5a5f451, + 0xe5e534d1, + 0xf1f108f9, + 0x717193e2, + 0xd8d873ab, + 0x31315362, + 0x15153f2a, + 0x04040c08, + 0xc7c75295, + 0x23236546, + 0xc3c35e9d, + 0x18182830, + 0x9696a137, + 0x05050f0a, + 0x9a9ab52f, + 0x0707090e, + 0x12123624, + 0x80809b1b, + 0xe2e23ddf, + 0xebeb26cd, + 0x2727694e, + 0xb2b2cd7f, + 0x75759fea, + 0x09091b12, + 0x83839e1d, + 0x2c2c7458, + 0x1a1a2e34, + 0x1b1b2d36, + 0x6e6eb2dc, + 0x5a5aeeb4, + 0xa0a0fb5b, + 0x5252f6a4, + 0x3b3b4d76, + 0xd6d661b7, + 0xb3b3ce7d, + 0x29297b52, + 0xe3e33edd, + 0x2f2f715e, + 0x84849713, + 0x5353f5a6, + 0xd1d168b9, + 0x00000000, + 0xeded2cc1, + 0x20206040, + 0xfcfc1fe3, + 0xb1b1c879, + 0x5b5bedb6, + 0x6a6abed4, + 0xcbcb468d, + 0xbebed967, + 0x39394b72, + 0x4a4ade94, + 0x4c4cd498, + 0x5858e8b0, + 0xcfcf4a85, + 0xd0d06bbb, + 0xefef2ac5, + 0xaaaae54f, + 0xfbfb16ed, + 0x4343c586, + 0x4d4dd79a, + 0x33335566, + 0x85859411, + 0x4545cf8a, + 0xf9f910e9, + 0x02020604, + 0x7f7f81fe, + 0x5050f0a0, + 0x3c3c4478, + 0x9f9fba25, + 0xa8a8e34b, + 0x5151f3a2, + 0xa3a3fe5d, + 0x4040c080, + 0x8f8f8a05, + 0x9292ad3f, + 0x9d9dbc21, + 0x38384870, + 0xf5f504f1, + 0xbcbcdf63, + 0xb6b6c177, + 0xdada75af, + 0x21216342, + 0x10103020, + 0xffff1ae5, + 0xf3f30efd, + 0xd2d26dbf, + 0xcdcd4c81, + 0x0c0c1418, + 0x13133526, + 0xecec2fc3, + 0x5f5fe1be, + 0x9797a235, + 0x4444cc88, + 0x1717392e, + 0xc4c45793, + 0xa7a7f255, + 0x7e7e82fc, + 0x3d3d477a, + 0x6464acc8, + 0x5d5de7ba, + 0x19192b32, + 0x737395e6, + 0x6060a0c0, + 0x81819819, + 0x4f4fd19e, + 0xdcdc7fa3, + 0x22226644, + 0x2a2a7e54, + 0x9090ab3b, + 0x8888830b, + 0x4646ca8c, + 0xeeee29c7, + 0xb8b8d36b, + 0x14143c28, + 0xdede79a7, + 0x5e5ee2bc, + 0x0b0b1d16, + 0xdbdb76ad, + 0xe0e03bdb, + 0x32325664, + 0x3a3a4e74, + 0x0a0a1e14, + 0x4949db92, + 0x06060a0c, + 0x24246c48, + 0x5c5ce4b8, + 0xc2c25d9f, + 0xd3d36ebd, + 0xacacef43, + 0x6262a6c4, + 0x9191a839, + 0x9595a431, + 0xe4e437d3, + 0x79798bf2, + 0xe7e732d5, + 0xc8c8438b, + 0x3737596e, + 0x6d6db7da, + 0x8d8d8c01, + 0xd5d564b1, + 0x4e4ed29c, + 0xa9a9e049, + 0x6c6cb4d8, + 0x5656faac, + 0xf4f407f3, + 0xeaea25cf, + 0x6565afca, + 0x7a7a8ef4, + 0xaeaee947, + 0x08081810, + 0xbabad56f, + 0x787888f0, + 0x25256f4a, + 0x2e2e725c, + 0x1c1c2438, + 0xa6a6f157, + 0xb4b4c773, + 0xc6c65197, + 0xe8e823cb, + 0xdddd7ca1, + 0x74749ce8, + 0x1f1f213e, + 0x4b4bdd96, + 0xbdbddc61, + 0x8b8b860d, + 0x8a8a850f, + 0x707090e0, + 0x3e3e427c, + 0xb5b5c471, + 0x6666aacc, + 0x4848d890, + 0x03030506, + 0xf6f601f7, + 0x0e0e121c, + 0x6161a3c2, + 0x35355f6a, + 0x5757f9ae, + 0xb9b9d069, + 0x86869117, + 0xc1c15899, + 0x1d1d273a, + 0x9e9eb927, + 0xe1e138d9, + 0xf8f813eb, + 0x9898b32b, + 0x11113322, + 0x6969bbd2, + 0xd9d970a9, + 0x8e8e8907, + 0x9494a733, + 0x9b9bb62d, + 0x1e1e223c, + 0x87879215, + 0xe9e920c9, + 0xcece4987, + 0x5555ffaa, + 0x28287850, + 0xdfdf7aa5, + 0x8c8c8f03, + 0xa1a1f859, + 0x89898009, + 0x0d0d171a, + 0xbfbfda65, + 0xe6e631d7, + 0x4242c684, + 0x6868b8d0, + 0x4141c382, + 0x9999b029, + 0x2d2d775a, + 0x0f0f111e, + 0xb0b0cb7b, + 0x5454fca8, + 0xbbbbd66d, + 0x16163a2c +]) + +function convertToInt32(bytes) { + const result = new Int32Array(bytes.length / 4) + for (let i = 0; i < bytes.length; i += 4) { + result[i / 4] = + (bytes[i] << 24) | + (bytes[i + 1] << 16) | + (bytes[i + 2] << 8) | + bytes[i + 3] + } + return result +} + +function expand_aes(key) { + if (key.length == 0) throw "key too short" + // encryption round keys + const _Ke = new Uint32Array(roundKeyCount) + + const KC = key.length / 4 + // convert the key into ints + const tk = convertToInt32(key) + + // copy values into round key arrays + for (let i = 0; i < KC; i++) _Ke[i] ^= tk[i] + + // key expansion (fips-197 section 5.2) + let rconpointer = 0 + let t = KC, + tt + while (t < roundKeyCount) { + tt = tk[KC - 1] + tk[0] ^= + (S[(tt >> 16) & 0xff] << 24) ^ + (S[(tt >> 8) & 0xff] << 16) ^ + (S[tt & 0xff] << 8) ^ + S[(tt >> 24) & 0xff] ^ + (rcon[rconpointer] << 24) + rconpointer += 1 + + // 256bit Key Expansion + for (let i = 1; i < KC / 2; i++) tk[i] ^= tk[i - 1] + tt = tk[KC / 2 - 1] + + tk[KC / 2] ^= + S[tt & 0xff] ^ + (S[(tt >> 8) & 0xff] << 8) ^ + (S[(tt >> 16) & 0xff] << 16) ^ + (S[(tt >> 24) & 0xff] << 24) + + for (let i = KC / 2 + 1; i < KC; i++) tk[i] ^= tk[i - 1] + + // copy values into round key arrays + for (let i = 0; i < KC && t < roundKeyCount; i++, t++) _Ke[t] ^= tk[i] + } + return _Ke +} + +function encrypt_aes(bytes, _Ke) { + // convert plaintext to (ints ^ key) + let t0 = + ((bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]) ^ _Ke[0] + let t1 = + ((bytes[4] << 24) | (bytes[5] << 16) | (bytes[6] << 8) | bytes[7]) ^ _Ke[1] + let t2 = + ((bytes[8] << 24) | (bytes[9] << 16) | (bytes[10] << 8) | bytes[11]) ^ + _Ke[2] + let t3 = + ((bytes[12] << 24) | (bytes[13] << 16) | (bytes[14] << 8) | bytes[15]) ^ + _Ke[3] + + // apply round transforms + let a0, a1, a2 + let r = 4 + while (r < roundsX4) { + a0 = + T1[(t0 >> 24) & 0xff] ^ + T2[(t1 >> 16) & 0xff] ^ + T3[(t2 >> 8) & 0xff] ^ + T4[t3 & 0xff] ^ + _Ke[r] + a1 = + T1[(t1 >> 24) & 0xff] ^ + T2[(t2 >> 16) & 0xff] ^ + T3[(t3 >> 8) & 0xff] ^ + T4[t0 & 0xff] ^ + _Ke[r + 1] + a2 = + T1[(t2 >> 24) & 0xff] ^ + T2[(t3 >> 16) & 0xff] ^ + T3[(t0 >> 8) & 0xff] ^ + T4[t1 & 0xff] ^ + _Ke[r + 2] + t3 = + T1[(t3 >> 24) & 0xff] ^ + T2[(t0 >> 16) & 0xff] ^ + T3[(t1 >> 8) & 0xff] ^ + T4[t2 & 0xff] ^ + _Ke[r + 3] + t0 = a0 + t1 = a1 + t2 = a2 + r += 4 + } + + // only need the first byte + return (S[(t0 >> 24) & 0xff] ^ (_Ke[roundsX4] >> 24)) & 0xff +} + +module.exports = function aescfb(ciphertext, key) { + const KE = expand_aes(key) + const _iv = Array.from(key.slice(0, 16)) + + for (let i = 0; i < ciphertext.length; i += 1) { + const temp = ciphertext[i] + ciphertext[i] ^= encrypt_aes(_iv, KE) + _iv.shift() + _iv.push(temp) + } + return ciphertext +} diff --git a/ent.js b/ent.js new file mode 100644 index 0000000..c9c5ef2 --- /dev/null +++ b/ent.js @@ -0,0 +1,131 @@ +const fs = require("fs"); +const path = require("path"); +const aescfb = require('./aes'); + +const skinKey = "s5s5ejuDru4uchuF2drUFuthaspAbepE"; + +const localStatePath = path.join(process.env.LocalAppData, "/Packages/Microsoft.MinecraftUWP_8wekyb3d8bbwe/LocalState"); +const mcpePath = path.join(localStatePath, "/games/com.mojang/minecraftpe"); + +const keyDb = {}; + +getEntFile(); +function getTitleAccountId() { + const optionsTxt = path.join(mcpePath, 'options.txt'); + if(fs.existsSync(optionsTxt)) { + const options = fs.readFileSync(optionsTxt).toString(); + const lines = options.split('\n'); + for (let i = 0; i < lines.length; i++) { + const [key, value] = lines[i].split(':'); + if (key === "last_title_account_id") { + return value.replace("\n", "").replace("\r", ""); + } + } + } +} + +function getEntKey() { + const titleAccountId = getTitleAccountId(); + const entXorKey = "X(nG*ejm&E8)m+8c;-SkLTjF)*QdN6_Y"; + const entKey = Buffer.alloc(32); + + for (let i = 0; i < 32; i++) { + entKey[i] = titleAccountId.charCodeAt(i % titleAccountId.length) ^ entXorKey.charCodeAt(i); + } + + + return entKey; +} + +function lookupKey(uuid) { + return keyDb[uuid] || skinKey; +} + + +function getEntFile() { + if(fs.existsSync(localStatePath)) { + const files = fs.readdirSync(localStatePath); + const entFileNames = files.filter(file => file.endsWith(".ent")); + + const entFiles = entFileNames.map(file => fs.readFileSync(path.join(localStatePath, file)).toString().substring("Version2".length)); + + for (let index = 0; index < entFiles.length; index++) { + const entFile = entFiles[index]; + const cipherText = Buffer.from(entFile, 'base64') + const decrypted = decryptBuffer(cipherText, getEntKey()); + try { + const json = JSON.parse(decrypted.toString()); + parseEnt(json) + } catch { + continue; + } + + } + } +} +module.exports = lookupKey; + +function parseEnt(ent) { + const mainReceipt = ent.Receipt; + parseReceipt(mainReceipt) + + for (let index = 0; index < ent.Items.length; index++) { + const item = ent.Items[index]; + const receipt = item.Receipt; + parseReceipt(receipt); + } +} + +function parseReceipt(receipt) { + const receiptContent = receipt.split(".")[1]; + const content = JSON.parse(atob(receiptContent)); + + const entitlements = content.Receipt.Entitlements; + + const deviceId = content.Receipt.ReceiptData?.DeviceId; + const entityId = content.Receipt.EntityId; + if (!deviceId || !entityId) return; + const userKey = deriveUserKey(deviceId, entityId); + + for (let index = 0; index < entitlements.length; index++) { + const element = entitlements[index]; + if (!element.ContentKey) continue; + keyDb[element.FriendlyId] = deobfuscateContentKey(element.ContentKey, userKey); + } +} + +function deriveUserKey(deviceId, entityId) { + const deviceIdBuffer = Buffer.from(deviceId, 'utf16le'); + const entityIdBuffer = Buffer.from(entityId, 'utf16le'); + + let length = deviceIdBuffer.length; + if (entityIdBuffer.length < length) length = entityIdBuffer.length; + const userKey = Buffer.alloc(length); + + for (let index = 0; index < userKey.length; index++) { + userKey[index] = deviceIdBuffer[index] ^ entityIdBuffer[index]; + } + return userKey; +} + +function deobfuscateContentKey(contentKey, userKey) { + const b64DecodedKey = Buffer.from(contentKey, 'base64'); + + let length = b64DecodedKey.length; + if (userKey.length < length) length = userKey.length; + const deobfuscatedKey = Buffer.alloc(length); + + for (let index = 0; index < deobfuscatedKey.length; index++) { + deobfuscatedKey[index] = b64DecodedKey[index] ^ userKey[index]; + } + + return deobfuscatedKey.toString("utf16le"); +} + + +function decryptBuffer(buffer, key) { + const bufferKey = Buffer.from(key, 'binary'); + + + return aescfb(buffer, bufferKey); +} \ No newline at end of file diff --git a/forge.config.js b/forge.config.js new file mode 100644 index 0000000..f2bdd5c --- /dev/null +++ b/forge.config.js @@ -0,0 +1,31 @@ +module.exports = { + packagerConfig: { + asar: true, + icon: 'renderer/decrypt' + }, + rebuildConfig: {}, + makers: [ + { + name: '@electron-forge/maker-squirrel', + config: {}, + }, + { + name: '@electron-forge/maker-zip', + platforms: ['darwin'], + }, + { + name: '@electron-forge/maker-deb', + config: {}, + }, + { + name: '@electron-forge/maker-rpm', + config: {}, + }, + ], + plugins: [ + { + name: '@electron-forge/plugin-auto-unpack-natives', + config: {}, + }, + ], +}; diff --git a/main.js b/main.js new file mode 100644 index 0000000..d9ef796 --- /dev/null +++ b/main.js @@ -0,0 +1,158 @@ +const {app, BrowserWindow, ipcMain, dialog} = require("electron"); +const path = require("path"); +const fs = require("fs"); +const PackDecryptor = require("./packDecrypter"); + +const minecraftFolderPath = path.join(process.env.LocalAppData, "/Packages/Microsoft.MinecraftUWP_8wekyb3d8bbwe"); +const localStatePath = path.join(minecraftFolderPath, "LocalState") +const premiumCachePath = path.join(localStatePath, "premium_cache"); +const worldsPath = path.join(localStatePath, "/games/com.mojang/minecraftWorlds"); + + +function checkWorldEncrypted(worldPath){ + const worldDbFolder = path.join(worldPath, "db"); + const dbFiles = fs.readdirSync(worldDbFolder); + for(let index = 0; index < dbFiles.length; index++) { + if(path.extname(dbFiles[index]).toLowerCase() === ".ldb") { + return PackDecryptor.isContentFileEncrypted(path.join(worldDbFolder, dbFiles[index])) + } + } + return false; +} + +function getWorlds() { + const worlds = []; + if(fs.existsSync(worldsPath)) { + const files = fs.readdirSync(worldsPath); + for (let index = 0; index < files.length; index++) { + const isEncrypted = checkWorldEncrypted(path.join(worldsPath, files[index])); + if (!isEncrypted) continue; + const name = fs.readFileSync(path.join(worldsPath, files[index], 'levelname.txt'), 'utf8'); + const packIcon = getPackIcon(path.join(worldsPath, files[index])); + worlds.push({ + name: replaceName(name), + packPath: path.join(worldsPath, files[index]), + packIcon, + }) + } + } + return worlds; +} + +function getPremiumCache() { + const packTypes = {}; + if(fs.existsSync(premiumCachePath)) { + const files = fs.readdirSync(premiumCachePath); + for (let index = 0; index < files.length; index++) { + const dirname = files[index]; + packTypes[dirname] = []; + const packs = getPacks(path.join(premiumCachePath, dirname)) + if (packs.length === 0) { + delete packTypes[dirname]; + continue; + } + packTypes[dirname] = packs; + } + } + return packTypes +} + +function getPacks(dirPath) { + const packList = fs.readdirSync(dirPath); + return packList.map(packDir => { + const packPath = path.join(dirPath, packDir); + const packName = getPackName(packPath) + return { + name: replaceName(packName), + + packPath: packPath, + packIcon: getPackIcon(packPath), + } + }) +} + +function replaceName(name) { + return name + .replaceAll("#", "") + .replaceAll("?", "") + .replaceAll("*", "") + .replaceAll("<", "") + .replaceAll(">", "") + .replaceAll("|", "") + .replaceAll(":", "") + .replaceAll("\\", "") + .replaceAll("/", "") + .trim() +} + + +function getPackIcon(packPath) { + const packIconNames = ["pack_icon.png", "pack_icon.jpeg" ,"world_icon.jpeg", "world_icon.png"] + for (let index = 0; index < packIconNames.length; index++) { + const packIconName = packIconNames[index]; + const iconPath = path.join(packPath, packIconName); + if (fs.existsSync(iconPath)) { + return fs.readFileSync(iconPath, 'base64') + } + } + return null; +} + +function getPackName(packPath) { + const langFile = fs.readFileSync(path.join(packPath, "texts", "en_US.lang"), 'utf8'); + return langFile.split("\n")[0].split("=").at(-1).replace("\n", "").replace("\r", ""); +} + +app.whenReady().then(() => { + const win = new BrowserWindow({ + width: 800, + height: 600, + webPreferences: { + preload: path.join(__dirname, 'preload.js'), + nodeIntegrationInWorker: true, + } + }); + win.removeMenu() + win.loadFile("./renderer/index.html") + + //win.webContents.openDevTools({mode: 'detach'}) + + ipcMain.handle("get-packs", (event) => { + packs = {worlds: getWorlds(), ...getPremiumCache()}; + if(packs["worlds"].length == 0) + delete packs["worlds"]; + return packs; + }) + + ipcMain.handle("pick-path", async (event, {path, type, name}) => { + const filter = {}; + if (type === "world_templates") { + filter.name = "World Template"; + filter.extensions = ["mctemplate"] + } + if (type === "resource_packs") { + filter.name = "Resource Pack"; + filter.extensions = ["mcpack"] + } + if (type === "skin_packs") { + filter.name = "Skin Pack"; + filter.extensions = ["mcpack"] + } + if (type === "persona") { + filter.name = "Persona Peice"; + filter.extensions = ["mcpersona"] + } + if (type === "worlds") { + filter.name = "World"; + filter.extensions = ["mcworld"] + } + + const dialogReturnValue = await dialog.showSaveDialog({ + defaultPath: name, + filters: [filter] + }) + if (dialogReturnValue.canceled) return; + return dialogReturnValue.filePath; + + }) +}) \ No newline at end of file diff --git a/packDecrypter.js b/packDecrypter.js new file mode 100644 index 0000000..763ac7d --- /dev/null +++ b/packDecrypter.js @@ -0,0 +1,197 @@ +const fs = require("fs"); +const path = require("path"); +const aescfb = require('./aes'); +const JSZip = require("jszip"); +const lookupKey = require("./ent"); +const Progress = require("./progress"); +const { ipcMain } = require("electron"); + + +module.exports = class PackDecryptor extends Progress { + + inputPath = ""; + outputFilePath = ""; + zip = new JSZip(); + zippedContent = []; + contentFiles = [] + + decryptDenylist = ["pack_icon.png", "pack_icon.jpeg", "world_icon.png", "world_icon.jpeg", "manifest.json"] + + + constructor(inputPath, outputFilePath) { + super(); + this.inputPath = inputPath; + this.outputFilePath = outputFilePath; + } + async start() { + return new Promise(async res => { + console.log("start") + const dbPath = path.join(this.inputPath, "db"); + this.contentFiles = recursiveReaddirrSync(this.inputPath); + this._started = true; + + if (fs.existsSync(dbPath)) { + const dbDir = recursiveReaddirrSync(dbPath); + + for (let index = 0; index < dbDir.length; index++) { + const dbFilePath = dbDir[index]; + if (fs.lstatSync(dbFilePath).isDirectory()) { + continue; + } + const decrypted = await this.decryptContentFile(dbFilePath); + this.addFile(dbFilePath, decrypted) + } + } + + for (let index = 0; index < this.contentFiles.length; index++) { + const name = path.basename(this.contentFiles[index]); + if (name.toLowerCase() === "contents.json") { + await this.decryptContent(this.contentFiles[index]); + } + } + + for (let index = 0; index < this.contentFiles.length; index++) { + const filePath = this.contentFiles[index]; + if (!this.zippedContent.includes(filePath)) { + this.addFile(filePath, fs.readFileSync(filePath)); + } + } + + await this.crackSkinPack() + this.crackWorld(); + + + this.zip.generateAsync({type:"arraybuffer"}).then((content) => { + fs.writeFileSync(this.outputFilePath, Buffer.from(content)); + console.log("done") + res(0); + }); + }) + } + crackWorld() { + const levelDatPath = path.join(this.inputPath, "level.dat"); + if (!fs.existsSync(levelDatPath)) return; + const levelDat = fs.readFileSync(levelDatPath); + + + let offset = levelDat.indexOf("prid"); + while (offset !== -1) { + levelDat.writeUInt8("a".charCodeAt(0), offset); + offset = levelDat.indexOf("prid"); + } + + this.addFile(levelDatPath, levelDat) + } + + addFile(filePath, content) { + const relPath = path.relative(this.inputPath, filePath).replaceAll("\\", "/"); + if (!this.zippedContent.includes(filePath)) { + this.zippedContent.push(filePath); + } + this.zip.file(relPath, content, {binary: true}) + + self.postMessage(this.getPercentage()); + + } + + + static isContentFileEncrypted(filePath){ + const contents = fs.readFileSync(filePath); + if (contents.length < 0x100) return false; + const magic = contents.readUint32LE(0x4); + if (magic === 2614082044) { + return true; + } + return false; + } + + async decryptContent(filePath) { + const dirname = path.dirname(filePath); + const isEncrypted = PackDecryptor.isContentFileEncrypted(filePath); + const content = await this.decryptContentFile(filePath); + const parsedContent = JSON.parse(content); + + if(isEncrypted) { + for (let index = 0; index < parsedContent.content.length; index++) { + const key = parsedContent.content[index].key; + const filePath = parsedContent.content[index].path; + const fileName = path.basename(filePath); + + if(this.decryptDenylist.indexOf(fileName.toLowerCase()) !== -1) continue; + if (!key) continue; + + const joinedPath = path.join(dirname, filePath); + const file = await this.decryptFile(joinedPath, key); + this.addFile(joinedPath, file) + } + } + + this.addFile(filePath, content) + } + + async decryptContentFile(filePath) { + const contents = fs.readFileSync(filePath); + if (contents.length < 0x100) return contents; + const magic = contents.readUint32LE(0x4); + if (magic === 2614082044) { + const cipherText = contents.subarray(0x100); + const uuidSize = contents.readUInt8(0x10) + const uuid = contents.subarray(0x11, 0x11 + uuidSize) + const key = lookupKey(uuid); + + const decrypted = decryptAes(key, cipherText) + return decrypted + } else { + return contents; + } + } + + async decryptFile(filePath, key) { + const contents = fs.readFileSync(filePath); + + const decrypted = decryptAes(key, contents) + return decrypted; + } + + async crackSkinPack() { + const skinJsonFilePath = "skins.json"; + + if (!this.zip.files[skinJsonFilePath]) return; + const skinsFile = await this.zip.files[skinJsonFilePath].async("string"); + + try{ + const skins = JSON.parse(skinsFile); + + for (let index = 0; index < skins.skins.length; index++) { + const skin = skins.skins[index]; + skin.type = "free"; + } + + this.addFile(path.join(this.inputPath, skinJsonFilePath), JSON.stringify(skins, null, 2)); + } + catch(Exception) {}; + + + } +} + +function recursiveReaddirrSync(dir) { + let results = []; + let list = fs.readdirSync(dir); + list.forEach(function (file) { + file = path.join(dir, file); + let stat = fs.statSync(file); + if (stat && stat.isDirectory()) { + results = results.concat(recursiveReaddirrSync(file)); + } else { + results.push(file); + } + }); + return results; +} + +function decryptAes(key, buffer) { + const bufferKey = Buffer.from(key, 'binary'); + + return aescfb(buffer, bufferKey); +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..6a1d20b --- /dev/null +++ b/package.json @@ -0,0 +1,27 @@ +{ + "name": "MCPackDecrypt", + "version": "1.0.0", + "description": "Marketplace Pack Decrypter", + "main": "main.js", + "scripts": { + "start": "electron .", + "package": "electron-forge package", + "make": "electron-forge make" + }, + "keywords": [], + "author": "MCPackDecrypt", + "license": "MIT", + "dependencies": { + "electron-squirrel-startup": "^1.0.0", + "jszip": "^3.10.1" + }, + "devDependencies": { + "@electron-forge/cli": "^6.2.1", + "@electron-forge/maker-deb": "^6.2.1", + "@electron-forge/maker-rpm": "^6.2.1", + "@electron-forge/maker-squirrel": "^6.2.1", + "@electron-forge/maker-zip": "^6.2.1", + "@electron-forge/plugin-auto-unpack-natives": "^6.2.1", + "electron": "^22.3.18" + } +} diff --git a/preload.js b/preload.js new file mode 100644 index 0000000..ee5160e --- /dev/null +++ b/preload.js @@ -0,0 +1,6 @@ +const {contextBridge, ipcRenderer} = require('electron'); + +contextBridge.exposeInMainWorld("electron", { + getPacks: () => ipcRenderer.invoke('get-packs'), + pickPath: (inputDir) => ipcRenderer.invoke('pick-path', inputDir), +}) \ No newline at end of file diff --git a/progress.js b/progress.js new file mode 100644 index 0000000..153c4e3 --- /dev/null +++ b/progress.js @@ -0,0 +1,14 @@ +module.exports = class Progress { + _started = false; + constructor() { + } + getPercentage() { + if (!this._started) { + return 0; + } + return Math.round((this.zippedContent.length / this.contentFiles.length ) * 100); + } + isStarted(){ + return this._started; + } +} \ No newline at end of file diff --git a/renderer/Thumbs.db b/renderer/Thumbs.db new file mode 100644 index 0000000..2074b02 Binary files /dev/null and b/renderer/Thumbs.db differ diff --git a/renderer/decrypt.ico b/renderer/decrypt.ico new file mode 100644 index 0000000..b5a8acc Binary files /dev/null and b/renderer/decrypt.ico differ diff --git a/renderer/index.html b/renderer/index.html new file mode 100644 index 0000000..292261a --- /dev/null +++ b/renderer/index.html @@ -0,0 +1,11 @@ + + + + + MC Pack Decrypter + + +
+ + + \ No newline at end of file diff --git a/renderer/pack.png b/renderer/pack.png new file mode 100644 index 0000000..973a7cf Binary files /dev/null and b/renderer/pack.png differ diff --git a/renderer/script.js b/renderer/script.js new file mode 100644 index 0000000..81ba2c5 --- /dev/null +++ b/renderer/script.js @@ -0,0 +1,117 @@ +electron.getPacks().then(ready) + + +const worker = new Worker('worker.js') + +const queue = []; + +function addToQueue(element, postFunc) { + queue.push({ + element: element, + postFunc: postFunc + }) + if (queue.length === 1) { + nextQueue(); + } +} + +function endQueue() { + queue.shift(); +} + +function nextQueue() { + const item = queue[0] + if (!item) return; + + item.element.id = "pack-running"; + item.postFunc(); + +} + +worker.onmessage = (e) => { + if (e.data === "end") { + document.querySelector('#pack-running #pack-progress').style.width = "0%"; + document.querySelector('#pack-running').id = "pack"; + endQueue(); + nextQueue(); + if (queue.length === 0) { + alert("The pack(s) was decrypted.") + } + return; + } + document.querySelector('#pack-running #pack-progress').style.width = e.data + "%" +} + + +const categoriesEl = document.getElementById("categories"); + +function ready(categories) { + const order = ["worlds", "world_templates", "resource_packs", "skin_packs", "persona"]; + const keys = Object.keys(categories).sort((a, b) => { + return order.indexOf(a) - order.indexOf(b) + }); + if(keys.length > 0) { + for (let i = 0; i < keys.length; i++) { + const name = keys[i]; + const categoryEl = createCategoryEl(name, categories[name]) + categoriesEl.appendChild(categoryEl); + } + } + else { + displayError("No encrypted pack(s) were found."); + } +} + + +function displayError(msg) { + const errorEl = document.createElement("div"); + errorEl.classList.add("error-msg") + const errorP = document.createElement("p"); + errorP.textContent = msg; + errorEl.appendChild(errorP); + categoriesEl.appendChild(errorEl); +} + +function createCategoryEl(name, packs) { + const categoryEl = document.createElement("div"); + categoryEl.classList.add("category"); + + categoryEl.innerHTML = `
${name.replace("_", " ")}
` + + const packsEl = document.createElement("div"); + packsEl.classList.add("packs"); + + for (let i = 0; i < packs.length; i++) { + const pack = packs[i]; + const packEl = createPackEl(pack, name); + packsEl.appendChild(packEl); + } + categoryEl.appendChild(packsEl); + return categoryEl; +} + +function createPackEl(pack, type) { + const packEl = document.createElement("div"); + packEl.classList.add("pack"); + + const packClick = async() => { + const outPath = await electron.pickPath({path: pack.packPath, type, name: pack.name}); + if (!outPath) return; + + if(packEl.id === "pack-queued") return; + if(packEl.id === "pack-running") return; + + packEl.id = "pack-queued" + addToQueue(packEl, () => worker.postMessage({outPath, path: pack.packPath, type, name: pack.name})) + + } + + packEl.addEventListener("click", packClick) + + packEl.innerHTML = ` +
+ +
${pack.name}
+ ` + return packEl; +} \ No newline at end of file diff --git a/renderer/styles.css b/renderer/styles.css new file mode 100644 index 0000000..28d3860 --- /dev/null +++ b/renderer/styles.css @@ -0,0 +1,88 @@ +body { + background-color: black; + color: white; + font-family: Arial, Helvetica, sans-serif; +} + +.category { + background: rgba(128, 128, 128, 0.2); + margin: 10px; + padding: 10px; + border-radius: 6px; +} +.category-title { + font-weight: bold; + margin-bottom: 10px; + text-transform: capitalize; +} + +.packs { + display: flex; + gap: 10px; + flex-wrap: wrap; +} +.pack { + position: relative; + overflow: hidden; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 10px; + width: 160px; + flex-shrink: 0; + background: rgba(255,255,255,0.1); + padding: 10px; + border-radius: 6px; + cursor: pointer; +} + +.pack:hover { + background: rgba(255,255,255,0.2); +} + +.pack-name { + text-align: center; +} + +.pack-icon-container { + + width: 64px; + height: 64px; +} + +.error-msg { + text-align: center; + font-size: 200%; + color: #fbff00; + top: 50%; + background-color: #7e509b; + left: 50%; + border-radius: 50px; + padding: 20px; + transform: translate(-50%,-50%); + position: absolute; +} + +.pack-icon { + width: 64px; + height: 64px; +} + +.pack-unknown-icon { + filter: grayscale(100%); +} + +#pack-progress { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 0%; + background-color: rgb(78, 78, 255); + z-index: -1; +} + +#pack-queued { + background-color: rgb(25, 109, 78); +} \ No newline at end of file diff --git a/renderer/worker.js b/renderer/worker.js new file mode 100644 index 0000000..db52676 --- /dev/null +++ b/renderer/worker.js @@ -0,0 +1,8 @@ +const PackDecryptor = require("../packDecrypter"); + +self.addEventListener("message", async function(e) { + const decryptor = new PackDecryptor(e.data.path, e.data.outPath); + + await decryptor.start(); + self.postMessage("end"); +}); \ No newline at end of file