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