1#![allow(clippy::unreadable_literal)]
2
3pub const P: [u32; 18] = [
4 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,
5 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
6 0x9216d5d9, 0x8979fb1b,
7];
8
9pub const S: [[u32; 256]; 4] = [
10 [
11 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96, 0xba7c9045,
12 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, 0x636920d8, 0x71574e69,
13 0xa458fea3, 0xf4933d7e, 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, 0x7b54a41d,
14 0xc25a59b5, 0x9c30d539, 0x2af26013, 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef,
15 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, 0xbd314b27, 0x78af2fda,
16 0x55605c60, 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, 0x55ca396a, 0x2aab10b6,
17 0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, 0x2ba9c55d,
18 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677,
19 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, 0x61d809cc, 0xfb21a991, 0x487cac60,
20 0x5dec8032, 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5,
21 0x0f6d6ff3, 0x83f44239, 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842,
22 0xf6e96c9a, 0x670c9c61, 0xabd388f0, 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,
23 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, 0xa1f1651d, 0x39af0176, 0x66ca593e,
24 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, 0xe06f75d8, 0x85c12073,
25 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, 0x37d0d724,
26 0xd00a1248, 0xdb0fead3, 0x49f1c09b, 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,
27 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2,
28 0x196a2463, 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c,
29 0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, 0xc0cba857,
30 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279,
31 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, 0x3c7516df, 0xfd616b15, 0x2f501ec8,
32 0xad0552ab, 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0,
33 0x1a87562e, 0xdf1769db, 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, 0x695b27b0,
34 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
35 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341,
36 0xcee4c6e8, 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, 0x95dbda4d, 0xae909198,
37 0xeaad8e71, 0x6b93d5a0, 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb,
38 0xf2122b64, 0x8888b812, 0x900df01c, 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad,
39 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6,
40 0xce89e299, 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 0x165fa266, 0x80957705,
41 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, 0xebcdaf0c,
42 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af,
43 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, 0x78c14389, 0xd95a537f, 0x207d5ba2,
44 0x02e5b9c5, 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a,
45 0x1b510052, 0x9a532915, 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5,
46 0x571be91f, 0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,
47 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a,
48 ],
49 [
50 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8,
51 0x8fedb266, 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, 0x193602a5, 0x75094c29,
52 0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07,
53 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e,
54 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286, 0xb79c5305,
55 0xaa500737, 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, 0xb03ada37, 0xf0500c0d,
56 0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, 0xd19113f9,
57 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,
58 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, 0xe238cd99, 0x3bea0e2f, 0x3280bba1,
59 0x183eb331, 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79,
60 0x5679b072, 0xbcaf89af, 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f,
61 0x2e6b7124, 0x501adde6, 0x9f84cd87, 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
62 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 0xef1c1847, 0x3215d908, 0xdd433b37,
63 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, 0x71dff89e, 0x10314e55,
64 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, 0xf28fe6ed,
65 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3,
66 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978,
67 0x9c10b36a, 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d,
68 0x1939260f, 0x19c27960, 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, 0xe3bc4595,
69 0xa67bc883, 0xb17f37d1, 0x018cff28, 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802,
70 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, 0x1521b628, 0x29076170, 0xecdd4775,
71 0x619f1510, 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, 0xb5735c90, 0x4c70a239,
72 0xd59e9e0b, 0xcbaade14, 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, 0x648b1eaf,
73 0x19bdf0ca, 0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
74 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77, 0x11ed935f,
75 0x16681281, 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, 0x57f584a5, 0x1b227263,
76 0x9b83c3ff, 0x1ac24696, 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef,
77 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0,
78 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, 0xc742f442, 0xef6abbb5, 0x654f3b1d,
79 0x41cd2105, 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 0xcf62a1f2, 0x5b8d2646,
80 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, 0x095bbf00,
81 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,
82 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, 0x7cde3759, 0xcbee7460, 0x4085f2a7,
83 0xce77326e, 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2,
84 0x5a04abfc, 0x800bcadc, 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3,
85 0x105588cd, 0x675fda79, 0xe3674340, 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
86 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7,
87 ],
88 [
89 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7, 0xbcf46b2e,
90 0xd4a20068, 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, 0x1e39f62e, 0x97244546,
91 0x14214f74, 0xbf8b8840, 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec,
92 0x03bd9785, 0x7fac6dd0, 0x31cb8504, 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a,
93 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, 0x68dc1462, 0xd7486900, 0x680ec0a4,
94 0x27a18dee, 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, 0xaace1e7c, 0xd3375fec,
95 0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, 0x1dc9faf7,
96 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,
97 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, 0x55533a3a, 0x20838d87, 0xfe6ba9b7,
98 0xd096954b, 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9,
99 0x5ef47e1c, 0x9029317c, 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548,
100 0xe4c66d22, 0x48c1133f, 0xc70f86dc, 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,
101 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, 0x257b7834, 0x602a9c60, 0xdff8e8a3,
102 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, 0x6b2395e0, 0x333e92e1,
103 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, 0xd0127845,
104 0x95b794fd, 0x647d0862, 0xe7ccf5f0, 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e,
105 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c,
106 0xdb6e6b0d, 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3,
107 0xb5390f92, 0x690fed0b, 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, 0xbb132f88,
108 0x515bad24, 0x7b9479bf, 0x763bd6eb, 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d,
109 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, 0x6a124237, 0xb79251e7, 0x06a1bbe6,
110 0x4bfb6350, 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, 0x44421659, 0x0a121386,
111 0xd90cec6e, 0xd5abea2a, 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, 0x9dbc8057,
112 0xf0f7c086, 0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
113 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24, 0x55464299,
114 0xbf582e61, 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, 0x5366f9c3, 0xc8b38e74,
115 0xb475f255, 0x46fcd9b9, 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, 0x466e598e,
116 0x20b45770, 0x8cd55591, 0xc902de4c, 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e,
117 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, 0xe85a1f02, 0x09f0be8c, 0x4a99a025,
118 0x1d6efe10, 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, 0xdcb7da83, 0x573906fe,
119 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, 0x9af88c27,
120 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62,
121 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, 0xbbcbee56, 0x90bcb6de, 0xebfc7da1,
122 0xce591d76, 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9,
123 0x1ac15bb4, 0xd39eb8fc, 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e,
124 0xb161e6f8, 0xa28514d9, 0x6c51133c, 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,
125 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0,
126 ],
127 [
128 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742, 0xd3822740,
129 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, 0xb78c1b6b, 0x21a19045,
130 0xb26eb1be, 0x6a366eb4, 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, 0x530ff8ee,
131 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304,
132 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, 0xc089c2b8, 0x43242ef6, 0xa51e03aa,
133 0x9cf2d0a4, 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, 0x2826a2f9, 0xa73a3ae1,
134 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, 0x80e4a915,
135 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51,
136 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472,
137 0x5a88f54c, 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc,
138 0xf8d56629, 0x79132e28, 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4,
139 0x88f46dba, 0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
140 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, 0x7533d928, 0xb155fdf5, 0x03563482,
141 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, 0x4de81751, 0x3830dc8e,
142 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, 0xa8b6e37e,
143 0xc3293d46, 0x48de5369, 0x6413e680, 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,
144 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f,
145 0x6bb4e3bb, 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb,
146 0x8d6612ae, 0xbf3c6f47, 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, 0x740e0d8d,
147 0xe75b1357, 0xf8721671, 0xaf537d5d, 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84,
148 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, 0x6f3f3b82, 0x3520ab82, 0x011a1d4b,
149 0x277227f8, 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, 0xa08839e1, 0x51ce794b,
150 0x2f32c9b7, 0xa01fbac9, 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 0x1a908749,
151 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
152 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a, 0x0f91fc71,
153 0x9b941525, 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, 0xb6c1075e, 0xe3056a0c,
154 0x10d25065, 0xcb03a442, 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, 0x9f1f9532,
155 0xe0d392df, 0xd3a0342b, 0x8971f21e, 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8,
156 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, 0xe54cda54, 0x1edad891, 0xce6279cf,
157 0xcd3e7e6f, 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, 0xf523f357, 0xa6327623,
158 0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, 0xde966292,
159 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a,
160 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, 0x71126905, 0xb2040222, 0xb6cbcf7c,
161 0xcd769c2b, 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76,
162 0x77afa1c5, 0x20756060, 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c,
163 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
164 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6,
165 ],
166];
167
168
169const BCRYPT_ALPHABET: &[u8] = b"./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
171
172fn char_to_index(c: u8) -> Option<u8> {
173 match c {
174 b'.' => Some(0),
175 b'/' => Some(1),
176 b'A'..=b'Z' => Some(c - b'A' + 2),
177 b'a'..=b'z' => Some(c - b'a' + 28),
178 b'0'..=b'9' => Some(c - b'0' + 54),
179 _ => None,
180 }
181}
182
183pub fn base64_encode(src: &[u8]) -> String {
184 let mut out = String::with_capacity((src.len() * 8 + 5) / 6);
185 let mut i = 0;
186 while i < src.len() {
187 let b0 = src[i] as u32;
188 let b1 = if i + 1 < src.len() { src[i + 1] as u32 } else { 0 };
189 let b2 = if i + 2 < src.len() { src[i + 2] as u32 } else { 0 };
190
191 let chunk = (b0 << 16) | (b1 << 8) | b2;
192
193 out.push(BCRYPT_ALPHABET[((chunk >> 18) & 0x3F) as usize] as char);
194 if i + 1 <= src.len() {
195 out.push(BCRYPT_ALPHABET[((chunk >> 12) & 0x3F) as usize] as char);
196 }
197 if i + 2 <= src.len() {
198 out.push(BCRYPT_ALPHABET[((chunk >> 6) & 0x3F) as usize] as char);
199 }
200 if i + 3 <= src.len() {
201 out.push(BCRYPT_ALPHABET[(chunk & 0x3F) as usize] as char);
202 }
203
204 i += 3;
205 }
206 if src.len() == 16 {
208 out.truncate(22);
209 } else if src.len() == 23 {
210 out.truncate(31);
211 }
212 out
213}
214
215pub fn base64_decode(src: &str, dest: &mut [u8]) -> Result<(), &'static str> {
216 let bytes = src.as_bytes();
217 let mut dest_idx = 0;
218
219 let mut buffer = 0u32;
221 let mut bits = 0;
222
223 for &b in bytes {
224 let idx = char_to_index(b).ok_or("Invalid character in base64")? as u32;
225 buffer = (buffer << 6) | idx;
226 bits += 6;
227 if bits >= 8 {
228 bits -= 8;
229 if dest_idx < dest.len() {
230 dest[dest_idx] = ((buffer >> bits) & 0xFF) as u8;
231 dest_idx += 1;
232 }
233 }
234 }
235
236 if dest_idx < dest.len() {
237 return Err("Insufficient base64 data");
238 }
239
240 Ok(())
241}
242
243
244pub struct Blowfish {
246 s: [[u32; 256]; 4],
247 p: [u32; 18],
248}
249
250fn next_u32_wrap(buf: &[u8], offset: &mut usize) -> u32 {
251 let mut v = 0;
252 for _ in 0..4 {
253 if *offset >= buf.len() {
254 *offset = 0;
255 }
256 v = (v << 8) | buf[*offset] as u32;
257 *offset += 1;
258 }
259 v
260}
261
262impl Blowfish {
263 fn init_state() -> Self {
264 Self {
265 p: P,
266 s: S,
267 }
268 }
269
270 fn expand_key(&mut self, key: &[u8]) {
271 let mut key_pos = 0;
272 for i in 0..18 {
273 self.p[i] ^= next_u32_wrap(key, &mut key_pos);
274 }
275 let mut lr = [0u32; 2];
276 for i in 0..9 {
277 lr = self.encrypt(lr);
278 self.p[2 * i] = lr[0];
279 self.p[2 * i + 1] = lr[1];
280 }
281 for i in 0..4 {
282 for j in 0..128 {
283 lr = self.encrypt(lr);
284 self.s[i][2 * j] = lr[0];
285 self.s[i][2 * j + 1] = lr[1];
286 }
287 }
288 }
289
290 fn round_function(&self, x: u32) -> u32 {
291 let a = self.s[0][(x >> 24) as usize];
292 let b = self.s[1][((x >> 16) & 0xff) as usize];
293 let c = self.s[2][((x >> 8) & 0xff) as usize];
294 let d = self.s[3][(x & 0xff) as usize];
295 (a.wrapping_add(b) ^ c).wrapping_add(d)
296 }
297
298 fn encrypt(&self, [mut l, mut r]: [u32; 2]) -> [u32; 2] {
299 for i in 0..8 {
300 l ^= self.p[2 * i];
301 r ^= self.round_function(l);
302 r ^= self.p[2 * i + 1];
303 l ^= self.round_function(r);
304 }
305 l ^= self.p[16];
306 r ^= self.p[17];
307 [r, l]
308 }
309
310 pub fn salted_expand_key(&mut self, salt: &[u8], key: &[u8]) {
311 let mut key_pos = 0;
312 for i in 0..18 {
313 self.p[i] ^= next_u32_wrap(key, &mut key_pos);
314 }
315 let mut lr = [0u32; 2];
316 let mut salt_pos = 0;
317 for i in 0..9 {
318 lr[0] ^= next_u32_wrap(salt, &mut salt_pos);
319 lr[1] ^= next_u32_wrap(salt, &mut salt_pos);
320 lr = self.encrypt(lr);
321
322 self.p[2 * i] = lr[0];
323 self.p[2 * i + 1] = lr[1];
324 }
325 for i in 0..4 {
326 for j in 0..64 {
327 lr[0] ^= next_u32_wrap(salt, &mut salt_pos);
328 lr[1] ^= next_u32_wrap(salt, &mut salt_pos);
329 lr = self.encrypt(lr);
330
331 self.s[i][4 * j] = lr[0];
332 self.s[i][4 * j + 1] = lr[1];
333
334 lr[0] ^= next_u32_wrap(salt, &mut salt_pos);
335 lr[1] ^= next_u32_wrap(salt, &mut salt_pos);
336 lr = self.encrypt(lr);
337
338 self.s[i][4 * j + 2] = lr[0];
339 self.s[i][4 * j + 3] = lr[1];
340 }
341 }
342 }
343
344 pub fn bc_init_state() -> Self {
345 Self::init_state()
346 }
347
348 pub fn bc_encrypt(&self, lr: [u32; 2]) -> [u32; 2] {
349 self.encrypt(lr)
350 }
351
352 pub fn bc_expand_key(&mut self, key: &[u8]) {
353 self.expand_key(key)
354 }
355}
356
357
358fn setup(cost: u32, salt: &[u8], key: &[u8]) -> Blowfish {
360 assert!(cost < 32);
361 let mut state = Blowfish::bc_init_state();
362
363 state.salted_expand_key(salt, key);
364 for _ in 0..1u32 << cost {
365 state.bc_expand_key(key);
366 state.bc_expand_key(salt);
367 }
368
369 state
370}
371
372pub fn bcrypt(cost: u32, salt: [u8; 16], password: &[u8]) -> [u8; 24] {
373 assert!(!password.is_empty() && password.len() <= 72);
374
375 let mut output = [0; 24];
376
377 let state = setup(cost, &salt, password);
378 let mut ctext = [
380 0x4f727068, 0x65616e42, 0x65686f6c, 0x64657253, 0x63727944, 0x6f756274,
381 ];
382 for i in 0..3 {
383 let i: usize = i * 2;
384 for _ in 0..64 {
385 let [l, r] = state.bc_encrypt([ctext[i], ctext[i + 1]]);
386 ctext[i] = l;
387 ctext[i + 1] = r;
388 }
389
390 let buf = ctext[i].to_be_bytes();
391 output[i * 4..][..4].copy_from_slice(&buf);
392 let buf = ctext[i + 1].to_be_bytes();
393 output[(i + 1) * 4..][..4].copy_from_slice(&buf);
394 }
395
396 output
397}
398
399
400use std::fmt;
402
403pub type BcryptResult<T> = Result<T, BcryptError>;
404
405#[derive(Debug)]
406pub enum BcryptError {
407 CostNotAllowed(u32),
408 InvalidHash(&'static str),
409 Truncation(usize),
410}
411
412impl fmt::Display for BcryptError {
413 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
414 match *self {
415 BcryptError::CostNotAllowed(c) => write!(f, "Cost needs to be between 4 and 31, got {}", c),
416 BcryptError::InvalidHash(r) => write!(f, "Invalid hash: {}", r),
417 BcryptError::Truncation(l) => write!(f, "Expected 72 bytes or fewer; found {} bytes", l),
418 }
419 }
420}
421
422impl std::error::Error for BcryptError {}
423
424pub const DEFAULT_COST: u32 = 12;
425
426fn constant_time_eq(a: &[u8], b: &[u8]) -> bool {
427 if a.len() != b.len() {
428 return false;
429 }
430 let mut result = 0;
431 for (x, y) in a.iter().zip(b.iter()) {
432 result |= x ^ y;
433 }
434 result == 0
436}
437
438fn hash_password_internal(
439 password: &[u8],
440 cost: u32,
441 salt: [u8; 16],
442 err_on_truncation: bool,
443) -> BcryptResult<String> {
444 if !(4..=31).contains(&cost) {
445 return Err(BcryptError::CostNotAllowed(cost));
446 }
447
448 let password_len = password.len();
449 if err_on_truncation && password_len >= 72 {
450 return Err(BcryptError::Truncation(password_len + 1));
451 }
452
453 let copy_len = password_len.min(72);
454 let mut pass = [0u8; 72];
455 pass[..copy_len].copy_from_slice(&password[..copy_len]);
456 let used = (copy_len + 1).min(72);
457 let truncated = &pass[..used];
458
459 let output = bcrypt(cost, salt, truncated);
460
461 let salt_b64 = base64_encode(&salt);
462 let hash_b64 = base64_encode(&output[..23]);
463
464 Ok(format!("$2b${:02}${}{}", cost, salt_b64, hash_b64))
465}
466
467pub fn hash<P: AsRef<[u8]>>(password: P, cost: u32) -> BcryptResult<String> {
468 let mut salt = [0u8; 16];
469 crate::rand::fill_bytes(&mut salt);
470 hash_password_internal(password.as_ref(), cost, salt, false)
471}
472
473pub fn verify<P: AsRef<[u8]>>(password: P, hash_str: &str) -> BcryptResult<bool> {
474 if hash_str.len() != 60 {
475 return Err(BcryptError::InvalidHash("expected 60 bytes"));
476 }
477 let bytes = hash_str.as_bytes();
478 if bytes[0] != b'$' || bytes[3] != b'$' || bytes[6] != b'$' {
479 return Err(BcryptError::InvalidHash("malformed hash"));
480 }
481 let version = &hash_str[1..3];
482 if version != "2y" && version != "2b" && version != "2a" && version != "2x" {
483 return Err(BcryptError::InvalidHash("invalid bcrypt prefix"));
484 }
485 let cost = hash_str[4..6]
486 .parse::<u32>()
487 .map_err(|_| BcryptError::InvalidHash("invalid cost"))?;
488
489 let salt_b64 = &hash_str[7..29];
490 let hash_b64 = &hash_str[29..60];
491
492 let mut salt = [0u8; 16];
493 base64_decode(salt_b64, &mut salt).map_err(|_| BcryptError::InvalidHash("invalid salt base64"))?;
494
495 let mut expected_hash = [0u8; 23];
496 base64_decode(hash_b64, &mut expected_hash).map_err(|_| BcryptError::InvalidHash("invalid hash base64"))?;
497
498 let password_len = password.as_ref().len();
500 let copy_len = password_len.min(72);
501 let mut pass = [0u8; 72];
502 pass[..copy_len].copy_from_slice(&password.as_ref()[..copy_len]);
503 let used = (copy_len + 1).min(72);
504 let truncated = &pass[..used];
505
506 let generated = bcrypt(cost, salt, truncated);
507
508 Ok(constant_time_eq(&generated[..23], &expected_hash))
509}