cuprate_cryptonight/
cnaes.rs

1use crate::util::subarray_copy;
2
3pub(crate) const AES_BLOCK_SIZE: usize = 16;
4
5/// 16 bytes, the same as AES 128 and 256
6const ROUND_KEY_SIZE: usize = 16;
7
8/// AES-128 uses 11 round keys and AES-256 uses 15 round keys. Cryptonight's
9/// version of AES uses one less round key than AES-128 (even though they both
10/// perform 10 rounds), because Cryptonight uses the first (0th) round in the
11/// first round, while AES mixes the first round key into the state.
12const NUM_AES_ROUND_KEYS: usize = 10;
13
14/// Cryptonight's hash uses the key size of AES256, but it only does 10 AES
15/// rounds like AES128.
16pub(crate) const CN_AES_KEY_SIZE: usize = 32;
17
18#[rustfmt::skip]
19const AES_SBOX: [[u8; 16]; 16] = [
20    [0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76],
21    [0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0],
22    [0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15],
23    [0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75],
24    [0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84],
25    [0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf],
26    [0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8],
27    [0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2],
28    [0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73],
29    [0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb],
30    [0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79],
31    [0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08],
32    [0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a],
33    [0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e],
34    [0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf],
35    [0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16]
36];
37
38/// Cryptonight extends the AES S-Box to 4096 bytes (1024 32-bit words) using a complicated
39/// system of nested macros:
40/// <https://github.com/monero-project/monero/blob/v0.18.3.4/src/crypto/aesb.c#L64-L142>
41/// The table below is the fully processed result.
42#[rustfmt::skip]
43const CRYPTONIGHT_SBOX: [u32; 1024] = [
44    0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0x0df2f2ff, 0xbd6b6bd6, 0xb16f6fde, 0x54c5c591,
45    0x50303060, 0x03010102, 0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d, 0x9a7676ec,
46    0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa, 0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb,
47    0xecadad41, 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453, 0x967272e4, 0x5bc0c09b,
48    0xc2b7b775, 0x1cfdfde1, 0xae93933d, 0x6a26264c, 0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83,
49    0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x08f1f1f9, 0x937171e2, 0x73d8d8ab, 0x53313162, 0x3f15152a,
50    0x0c040408, 0x52c7c795, 0x65232346, 0x5ec3c39d, 0x28181830, 0xa1969637, 0x0f05050a, 0xb59a9a2f,
51    0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df, 0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea,
52    0x1b090912, 0x9e83831d, 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, 0xb26e6edc, 0xee5a5ab4, 0xfba0a05b,
53    0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, 0xceb3b37d, 0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413,
54    0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1, 0x60202040, 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6,
55    0xbe6a6ad4, 0x46cbcb8d, 0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0, 0x4acfcf85,
56    0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed, 0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511,
57    0xcf45458a, 0x10f9f9e9, 0x06020204, 0x817f7ffe, 0xf05050a0, 0x443c3c78, 0xba9f9f25, 0xe3a8a84b,
58    0xf35151a2, 0xfea3a35d, 0xc0404080, 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1,
59    0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, 0x30101020, 0x1affffe5, 0x0ef3f3fd, 0x6dd2d2bf,
60    0x4ccdcd81, 0x140c0c18, 0x35131326, 0x2fececc3, 0xe15f5fbe, 0xa2979735, 0xcc444488, 0x3917172e,
61    0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a, 0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6,
62    0xa06060c0, 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54, 0xab90903b, 0x8388880b,
63    0xca46468c, 0x29eeeec7, 0xd3b8b86b, 0x3c141428, 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad,
64    0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, 0xdb494992, 0x0a06060c, 0x6c242448, 0xe45c5cb8,
65    0x5dc2c29f, 0x6ed3d3bd, 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, 0x37e4e4d3, 0x8b7979f2,
66    0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda, 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949,
67    0xb46c6cd8, 0xfa5656ac, 0x07f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4, 0xe9aeae47, 0x18080810,
68    0xd5baba6f, 0x887878f0, 0x6f25254a, 0x722e2e5c, 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697,
69    0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, 0xdd4b4b96, 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f,
70    0x907070e0, 0x423e3e7c, 0xc4b5b571, 0xaa6666cc, 0xd8484890, 0x05030306, 0x01f6f6f7, 0x120e0e1c,
71    0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969, 0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27,
72    0x38e1e1d9, 0x13f8f8eb, 0xb398982b, 0x33111122, 0xbb6969d2, 0x70d9d9a9, 0x898e8e07, 0xa7949433,
73    0xb69b9b2d, 0x221e1e3c, 0x92878715, 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5,
74    0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65, 0x31e6e6d7, 0xc6424284, 0xb86868d0,
75    0xc3414182, 0xb0999929, 0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, 0x3a16162c,
76    0x6363c6a5, 0x7c7cf884, 0x7777ee99, 0x7b7bf68d, 0xf2f2ff0d, 0x6b6bd6bd, 0x6f6fdeb1, 0xc5c59154,
77    0x30306050, 0x01010203, 0x6767cea9, 0x2b2b567d, 0xfefee719, 0xd7d7b562, 0xabab4de6, 0x7676ec9a,
78    0xcaca8f45, 0x82821f9d, 0xc9c98940, 0x7d7dfa87, 0xfafaef15, 0x5959b2eb, 0x47478ec9, 0xf0f0fb0b,
79    0xadad41ec, 0xd4d4b367, 0xa2a25ffd, 0xafaf45ea, 0x9c9c23bf, 0xa4a453f7, 0x7272e496, 0xc0c09b5b,
80    0xb7b775c2, 0xfdfde11c, 0x93933dae, 0x26264c6a, 0x36366c5a, 0x3f3f7e41, 0xf7f7f502, 0xcccc834f,
81    0x3434685c, 0xa5a551f4, 0xe5e5d134, 0xf1f1f908, 0x7171e293, 0xd8d8ab73, 0x31316253, 0x15152a3f,
82    0x0404080c, 0xc7c79552, 0x23234665, 0xc3c39d5e, 0x18183028, 0x969637a1, 0x05050a0f, 0x9a9a2fb5,
83    0x07070e09, 0x12122436, 0x80801b9b, 0xe2e2df3d, 0xebebcd26, 0x27274e69, 0xb2b27fcd, 0x7575ea9f,
84    0x0909121b, 0x83831d9e, 0x2c2c5874, 0x1a1a342e, 0x1b1b362d, 0x6e6edcb2, 0x5a5ab4ee, 0xa0a05bfb,
85    0x5252a4f6, 0x3b3b764d, 0xd6d6b761, 0xb3b37dce, 0x2929527b, 0xe3e3dd3e, 0x2f2f5e71, 0x84841397,
86    0x5353a6f5, 0xd1d1b968, 0x00000000, 0xededc12c, 0x20204060, 0xfcfce31f, 0xb1b179c8, 0x5b5bb6ed,
87    0x6a6ad4be, 0xcbcb8d46, 0xbebe67d9, 0x3939724b, 0x4a4a94de, 0x4c4c98d4, 0x5858b0e8, 0xcfcf854a,
88    0xd0d0bb6b, 0xefefc52a, 0xaaaa4fe5, 0xfbfbed16, 0x434386c5, 0x4d4d9ad7, 0x33336655, 0x85851194,
89    0x45458acf, 0xf9f9e910, 0x02020406, 0x7f7ffe81, 0x5050a0f0, 0x3c3c7844, 0x9f9f25ba, 0xa8a84be3,
90    0x5151a2f3, 0xa3a35dfe, 0x404080c0, 0x8f8f058a, 0x92923fad, 0x9d9d21bc, 0x38387048, 0xf5f5f104,
91    0xbcbc63df, 0xb6b677c1, 0xdadaaf75, 0x21214263, 0x10102030, 0xffffe51a, 0xf3f3fd0e, 0xd2d2bf6d,
92    0xcdcd814c, 0x0c0c1814, 0x13132635, 0xececc32f, 0x5f5fbee1, 0x979735a2, 0x444488cc, 0x17172e39,
93    0xc4c49357, 0xa7a755f2, 0x7e7efc82, 0x3d3d7a47, 0x6464c8ac, 0x5d5dbae7, 0x1919322b, 0x7373e695,
94    0x6060c0a0, 0x81811998, 0x4f4f9ed1, 0xdcdca37f, 0x22224466, 0x2a2a547e, 0x90903bab, 0x88880b83,
95    0x46468cca, 0xeeeec729, 0xb8b86bd3, 0x1414283c, 0xdedea779, 0x5e5ebce2, 0x0b0b161d, 0xdbdbad76,
96    0xe0e0db3b, 0x32326456, 0x3a3a744e, 0x0a0a141e, 0x494992db, 0x06060c0a, 0x2424486c, 0x5c5cb8e4,
97    0xc2c29f5d, 0xd3d3bd6e, 0xacac43ef, 0x6262c4a6, 0x919139a8, 0x959531a4, 0xe4e4d337, 0x7979f28b,
98    0xe7e7d532, 0xc8c88b43, 0x37376e59, 0x6d6ddab7, 0x8d8d018c, 0xd5d5b164, 0x4e4e9cd2, 0xa9a949e0,
99    0x6c6cd8b4, 0x5656acfa, 0xf4f4f307, 0xeaeacf25, 0x6565caaf, 0x7a7af48e, 0xaeae47e9, 0x08081018,
100    0xbaba6fd5, 0x7878f088, 0x25254a6f, 0x2e2e5c72, 0x1c1c3824, 0xa6a657f1, 0xb4b473c7, 0xc6c69751,
101    0xe8e8cb23, 0xdddda17c, 0x7474e89c, 0x1f1f3e21, 0x4b4b96dd, 0xbdbd61dc, 0x8b8b0d86, 0x8a8a0f85,
102    0x7070e090, 0x3e3e7c42, 0xb5b571c4, 0x6666ccaa, 0x484890d8, 0x03030605, 0xf6f6f701, 0x0e0e1c12,
103    0x6161c2a3, 0x35356a5f, 0x5757aef9, 0xb9b969d0, 0x86861791, 0xc1c19958, 0x1d1d3a27, 0x9e9e27b9,
104    0xe1e1d938, 0xf8f8eb13, 0x98982bb3, 0x11112233, 0x6969d2bb, 0xd9d9a970, 0x8e8e0789, 0x949433a7,
105    0x9b9b2db6, 0x1e1e3c22, 0x87871592, 0xe9e9c920, 0xcece8749, 0x5555aaff, 0x28285078, 0xdfdfa57a,
106    0x8c8c038f, 0xa1a159f8, 0x89890980, 0x0d0d1a17, 0xbfbf65da, 0xe6e6d731, 0x424284c6, 0x6868d0b8,
107    0x414182c3, 0x999929b0, 0x2d2d5a77, 0x0f0f1e11, 0xb0b07bcb, 0x5454a8fc, 0xbbbb6dd6, 0x16162c3a,
108    0x63c6a563, 0x7cf8847c, 0x77ee9977, 0x7bf68d7b, 0xf2ff0df2, 0x6bd6bd6b, 0x6fdeb16f, 0xc59154c5,
109    0x30605030, 0x01020301, 0x67cea967, 0x2b567d2b, 0xfee719fe, 0xd7b562d7, 0xab4de6ab, 0x76ec9a76,
110    0xca8f45ca, 0x821f9d82, 0xc98940c9, 0x7dfa877d, 0xfaef15fa, 0x59b2eb59, 0x478ec947, 0xf0fb0bf0,
111    0xad41ecad, 0xd4b367d4, 0xa25ffda2, 0xaf45eaaf, 0x9c23bf9c, 0xa453f7a4, 0x72e49672, 0xc09b5bc0,
112    0xb775c2b7, 0xfde11cfd, 0x933dae93, 0x264c6a26, 0x366c5a36, 0x3f7e413f, 0xf7f502f7, 0xcc834fcc,
113    0x34685c34, 0xa551f4a5, 0xe5d134e5, 0xf1f908f1, 0x71e29371, 0xd8ab73d8, 0x31625331, 0x152a3f15,
114    0x04080c04, 0xc79552c7, 0x23466523, 0xc39d5ec3, 0x18302818, 0x9637a196, 0x050a0f05, 0x9a2fb59a,
115    0x070e0907, 0x12243612, 0x801b9b80, 0xe2df3de2, 0xebcd26eb, 0x274e6927, 0xb27fcdb2, 0x75ea9f75,
116    0x09121b09, 0x831d9e83, 0x2c58742c, 0x1a342e1a, 0x1b362d1b, 0x6edcb26e, 0x5ab4ee5a, 0xa05bfba0,
117    0x52a4f652, 0x3b764d3b, 0xd6b761d6, 0xb37dceb3, 0x29527b29, 0xe3dd3ee3, 0x2f5e712f, 0x84139784,
118    0x53a6f553, 0xd1b968d1, 0x00000000, 0xedc12ced, 0x20406020, 0xfce31ffc, 0xb179c8b1, 0x5bb6ed5b,
119    0x6ad4be6a, 0xcb8d46cb, 0xbe67d9be, 0x39724b39, 0x4a94de4a, 0x4c98d44c, 0x58b0e858, 0xcf854acf,
120    0xd0bb6bd0, 0xefc52aef, 0xaa4fe5aa, 0xfbed16fb, 0x4386c543, 0x4d9ad74d, 0x33665533, 0x85119485,
121    0x458acf45, 0xf9e910f9, 0x02040602, 0x7ffe817f, 0x50a0f050, 0x3c78443c, 0x9f25ba9f, 0xa84be3a8,
122    0x51a2f351, 0xa35dfea3, 0x4080c040, 0x8f058a8f, 0x923fad92, 0x9d21bc9d, 0x38704838, 0xf5f104f5,
123    0xbc63dfbc, 0xb677c1b6, 0xdaaf75da, 0x21426321, 0x10203010, 0xffe51aff, 0xf3fd0ef3, 0xd2bf6dd2,
124    0xcd814ccd, 0x0c18140c, 0x13263513, 0xecc32fec, 0x5fbee15f, 0x9735a297, 0x4488cc44, 0x172e3917,
125    0xc49357c4, 0xa755f2a7, 0x7efc827e, 0x3d7a473d, 0x64c8ac64, 0x5dbae75d, 0x19322b19, 0x73e69573,
126    0x60c0a060, 0x81199881, 0x4f9ed14f, 0xdca37fdc, 0x22446622, 0x2a547e2a, 0x903bab90, 0x880b8388,
127    0x468cca46, 0xeec729ee, 0xb86bd3b8, 0x14283c14, 0xdea779de, 0x5ebce25e, 0x0b161d0b, 0xdbad76db,
128    0xe0db3be0, 0x32645632, 0x3a744e3a, 0x0a141e0a, 0x4992db49, 0x060c0a06, 0x24486c24, 0x5cb8e45c,
129    0xc29f5dc2, 0xd3bd6ed3, 0xac43efac, 0x62c4a662, 0x9139a891, 0x9531a495, 0xe4d337e4, 0x79f28b79,
130    0xe7d532e7, 0xc88b43c8, 0x376e5937, 0x6ddab76d, 0x8d018c8d, 0xd5b164d5, 0x4e9cd24e, 0xa949e0a9,
131    0x6cd8b46c, 0x56acfa56, 0xf4f307f4, 0xeacf25ea, 0x65caaf65, 0x7af48e7a, 0xae47e9ae, 0x08101808,
132    0xba6fd5ba, 0x78f08878, 0x254a6f25, 0x2e5c722e, 0x1c38241c, 0xa657f1a6, 0xb473c7b4, 0xc69751c6,
133    0xe8cb23e8, 0xdda17cdd, 0x74e89c74, 0x1f3e211f, 0x4b96dd4b, 0xbd61dcbd, 0x8b0d868b, 0x8a0f858a,
134    0x70e09070, 0x3e7c423e, 0xb571c4b5, 0x66ccaa66, 0x4890d848, 0x03060503, 0xf6f701f6, 0x0e1c120e,
135    0x61c2a361, 0x356a5f35, 0x57aef957, 0xb969d0b9, 0x86179186, 0xc19958c1, 0x1d3a271d, 0x9e27b99e,
136    0xe1d938e1, 0xf8eb13f8, 0x982bb398, 0x11223311, 0x69d2bb69, 0xd9a970d9, 0x8e07898e, 0x9433a794,
137    0x9b2db69b, 0x1e3c221e, 0x87159287, 0xe9c920e9, 0xce8749ce, 0x55aaff55, 0x28507828, 0xdfa57adf,
138    0x8c038f8c, 0xa159f8a1, 0x89098089, 0x0d1a170d, 0xbf65dabf, 0xe6d731e6, 0x4284c642, 0x68d0b868,
139    0x4182c341, 0x9929b099, 0x2d5a772d, 0x0f1e110f, 0xb07bcbb0, 0x54a8fc54, 0xbb6dd6bb, 0x162c3a16,
140    0xc6a56363, 0xf8847c7c, 0xee997777, 0xf68d7b7b, 0xff0df2f2, 0xd6bd6b6b, 0xdeb16f6f, 0x9154c5c5,
141    0x60503030, 0x02030101, 0xcea96767, 0x567d2b2b, 0xe719fefe, 0xb562d7d7, 0x4de6abab, 0xec9a7676,
142    0x8f45caca, 0x1f9d8282, 0x8940c9c9, 0xfa877d7d, 0xef15fafa, 0xb2eb5959, 0x8ec94747, 0xfb0bf0f0,
143    0x41ecadad, 0xb367d4d4, 0x5ffda2a2, 0x45eaafaf, 0x23bf9c9c, 0x53f7a4a4, 0xe4967272, 0x9b5bc0c0,
144    0x75c2b7b7, 0xe11cfdfd, 0x3dae9393, 0x4c6a2626, 0x6c5a3636, 0x7e413f3f, 0xf502f7f7, 0x834fcccc,
145    0x685c3434, 0x51f4a5a5, 0xd134e5e5, 0xf908f1f1, 0xe2937171, 0xab73d8d8, 0x62533131, 0x2a3f1515,
146    0x080c0404, 0x9552c7c7, 0x46652323, 0x9d5ec3c3, 0x30281818, 0x37a19696, 0x0a0f0505, 0x2fb59a9a,
147    0x0e090707, 0x24361212, 0x1b9b8080, 0xdf3de2e2, 0xcd26ebeb, 0x4e692727, 0x7fcdb2b2, 0xea9f7575,
148    0x121b0909, 0x1d9e8383, 0x58742c2c, 0x342e1a1a, 0x362d1b1b, 0xdcb26e6e, 0xb4ee5a5a, 0x5bfba0a0,
149    0xa4f65252, 0x764d3b3b, 0xb761d6d6, 0x7dceb3b3, 0x527b2929, 0xdd3ee3e3, 0x5e712f2f, 0x13978484,
150    0xa6f55353, 0xb968d1d1, 0x00000000, 0xc12ceded, 0x40602020, 0xe31ffcfc, 0x79c8b1b1, 0xb6ed5b5b,
151    0xd4be6a6a, 0x8d46cbcb, 0x67d9bebe, 0x724b3939, 0x94de4a4a, 0x98d44c4c, 0xb0e85858, 0x854acfcf,
152    0xbb6bd0d0, 0xc52aefef, 0x4fe5aaaa, 0xed16fbfb, 0x86c54343, 0x9ad74d4d, 0x66553333, 0x11948585,
153    0x8acf4545, 0xe910f9f9, 0x04060202, 0xfe817f7f, 0xa0f05050, 0x78443c3c, 0x25ba9f9f, 0x4be3a8a8,
154    0xa2f35151, 0x5dfea3a3, 0x80c04040, 0x058a8f8f, 0x3fad9292, 0x21bc9d9d, 0x70483838, 0xf104f5f5,
155    0x63dfbcbc, 0x77c1b6b6, 0xaf75dada, 0x42632121, 0x20301010, 0xe51affff, 0xfd0ef3f3, 0xbf6dd2d2,
156    0x814ccdcd, 0x18140c0c, 0x26351313, 0xc32fecec, 0xbee15f5f, 0x35a29797, 0x88cc4444, 0x2e391717,
157    0x9357c4c4, 0x55f2a7a7, 0xfc827e7e, 0x7a473d3d, 0xc8ac6464, 0xbae75d5d, 0x322b1919, 0xe6957373,
158    0xc0a06060, 0x19988181, 0x9ed14f4f, 0xa37fdcdc, 0x44662222, 0x547e2a2a, 0x3bab9090, 0x0b838888,
159    0x8cca4646, 0xc729eeee, 0x6bd3b8b8, 0x283c1414, 0xa779dede, 0xbce25e5e, 0x161d0b0b, 0xad76dbdb,
160    0xdb3be0e0, 0x64563232, 0x744e3a3a, 0x141e0a0a, 0x92db4949, 0x0c0a0606, 0x486c2424, 0xb8e45c5c,
161    0x9f5dc2c2, 0xbd6ed3d3, 0x43efacac, 0xc4a66262, 0x39a89191, 0x31a49595, 0xd337e4e4, 0xf28b7979,
162    0xd532e7e7, 0x8b43c8c8, 0x6e593737, 0xdab76d6d, 0x018c8d8d, 0xb164d5d5, 0x9cd24e4e, 0x49e0a9a9,
163    0xd8b46c6c, 0xacfa5656, 0xf307f4f4, 0xcf25eaea, 0xcaaf6565, 0xf48e7a7a, 0x47e9aeae, 0x10180808,
164    0x6fd5baba, 0xf0887878, 0x4a6f2525, 0x5c722e2e, 0x38241c1c, 0x57f1a6a6, 0x73c7b4b4, 0x9751c6c6,
165    0xcb23e8e8, 0xa17cdddd, 0xe89c7474, 0x3e211f1f, 0x96dd4b4b, 0x61dcbdbd, 0x0d868b8b, 0x0f858a8a,
166    0xe0907070, 0x7c423e3e, 0x71c4b5b5, 0xccaa6666, 0x90d84848, 0x06050303, 0xf701f6f6, 0x1c120e0e,
167    0xc2a36161, 0x6a5f3535, 0xaef95757, 0x69d0b9b9, 0x17918686, 0x9958c1c1, 0x3a271d1d, 0x27b99e9e,
168    0xd938e1e1, 0xeb13f8f8, 0x2bb39898, 0x22331111, 0xd2bb6969, 0xa970d9d9, 0x07898e8e, 0x33a79494,
169    0x2db69b9b, 0x3c221e1e, 0x15928787, 0xc920e9e9, 0x8749cece, 0xaaff5555, 0x50782828, 0xa57adfdf,
170    0x038f8c8c, 0x59f8a1a1, 0x09808989, 0x1a170d0d, 0x65dabfbf, 0xd731e6e6, 0x84c64242, 0xd0b86868,
171    0x82c34141, 0x29b09999, 0x5a772d2d, 0x1e110f0f, 0x7bcbb0b0, 0xa8fc5454, 0x6dd6bbbb, 0x2c3a1616,
172];
173
174const fn substitute_word(word: u32) -> u32 {
175    let wb: [u8; 4] = word.to_le_bytes();
176    u32::from_le_bytes([
177        AES_SBOX[(wb[0] >> 4) as usize][(wb[0] & 0x0F) as usize],
178        AES_SBOX[(wb[1] >> 4) as usize][(wb[1] & 0x0F) as usize],
179        AES_SBOX[(wb[2] >> 4) as usize][(wb[2] & 0x0F) as usize],
180        AES_SBOX[(wb[3] >> 4) as usize][(wb[3] & 0x0F) as usize],
181    ])
182}
183
184/// Extends the key in the same way as it is extended for AES256, but for
185/// Cryptonight's hash we only need to extend to 10 round keys instead of 15
186/// like AES256.
187#[expect(clippy::cast_possible_truncation)]
188pub(crate) fn key_extend(key_bytes: &[u8; CN_AES_KEY_SIZE]) -> [u128; NUM_AES_ROUND_KEYS] {
189    // NK comes from the AES specification, it is the number of 32-bit words in
190    // the non-expanded key (For AES-256: 32/4 = 8)
191    const NK: usize = 8;
192    let mut expanded_key = [0_u128; NUM_AES_ROUND_KEYS];
193
194    // The next 2 lines, which set the first 2 round keys without using
195    // the expansion algorithm, are specific to Cryptonight.
196    expanded_key[0] = u128::from_le_bytes(subarray_copy(key_bytes, 0));
197    expanded_key[1] = u128::from_le_bytes(subarray_copy(key_bytes, ROUND_KEY_SIZE));
198
199    /// See FIPS-197, especially figure 11 to better understand how the
200    /// expansion happens: <https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.197.pdf>
201    const ROUND_CONSTS: [u8; 11] = [
202        0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36,
203    ];
204
205    // the word before w0 in the loop below
206    let mut w0_prev = (expanded_key[1] >> 96) as u32;
207
208    // expand to 10, 16-byte round keys (160 total bytes, or 40 4-byte words)
209    for i in 2..NUM_AES_ROUND_KEYS {
210        let word_num = i * 4;
211
212        // if `i` is even, `word_num` for `w0` is divisible by 8 (`NK`), otherwise it
213        // is divisible by 4
214        let mut w0 = if i & 1 == 0 {
215            substitute_word(w0_prev.rotate_right(8)) ^ u32::from(ROUND_CONSTS[word_num / NK])
216        } else {
217            substitute_word(w0_prev)
218        };
219
220        let pprev_key = expanded_key[i - 2];
221
222        w0 ^= pprev_key as u32;
223        let w1 = w0 ^ ((pprev_key >> 32) as u32);
224        let w2 = w1 ^ ((pprev_key >> 64) as u32);
225        let w3 = w2 ^ ((pprev_key >> 96) as u32);
226
227        expanded_key[i] = u128::from(w0)
228            | (u128::from(w1) << 32)
229            | (u128::from(w2) << 64)
230            | (u128::from(w3) << 96);
231
232        w0_prev = w3;
233    }
234
235    expanded_key
236}
237
238#[expect(clippy::cast_possible_truncation)]
239pub(crate) fn round_fwd(state: u128, key: u128) -> u128 {
240    let mut r1 = CRYPTONIGHT_SBOX[usize::from(state as u8)];
241    r1 ^= CRYPTONIGHT_SBOX[256 + usize::from((state >> 40) as u8)];
242    r1 ^= CRYPTONIGHT_SBOX[512 + usize::from((state >> 80) as u8)];
243    r1 ^= CRYPTONIGHT_SBOX[768 + usize::from((state >> 120) as u8)];
244
245    let mut r2 = CRYPTONIGHT_SBOX[usize::from((state >> 32) as u8)];
246    r2 ^= CRYPTONIGHT_SBOX[256 + usize::from((state >> 72) as u8)];
247    r2 ^= CRYPTONIGHT_SBOX[512 + usize::from((state >> 112) as u8)];
248    r2 ^= CRYPTONIGHT_SBOX[768 + usize::from((state >> 24) as u8)];
249
250    let mut r3 = CRYPTONIGHT_SBOX[usize::from((state >> 64) as u8)];
251    r3 ^= CRYPTONIGHT_SBOX[256 + usize::from((state >> 104) as u8)];
252    r3 ^= CRYPTONIGHT_SBOX[512 + usize::from((state >> 16) as u8)];
253    r3 ^= CRYPTONIGHT_SBOX[768 + usize::from((state >> 56) as u8)];
254
255    let mut r4 = CRYPTONIGHT_SBOX[usize::from((state >> 96) as u8)];
256    r4 ^= CRYPTONIGHT_SBOX[256 + usize::from((state >> 8) as u8)];
257    r4 ^= CRYPTONIGHT_SBOX[512 + usize::from((state >> 48) as u8)];
258    r4 ^= CRYPTONIGHT_SBOX[768 + usize::from((state >> 88) as u8)];
259
260    let mut new_state =
261        (u128::from(r4) << 96) | (u128::from(r3) << 64) | (u128::from(r2) << 32) | u128::from(r1);
262    new_state ^= key;
263    new_state
264}
265
266pub(crate) fn aesb_pseudo_round(block: u128, expanded_key: &[u128; NUM_AES_ROUND_KEYS]) -> u128 {
267    let mut block = block;
268    for round_key in expanded_key {
269        block = round_fwd(block, *round_key);
270    }
271
272    block
273}
274
275pub(crate) fn aesb_single_round(block: &mut u128, round_key: u128) {
276    *block = round_fwd(*block, round_key);
277}
278
279#[cfg(test)]
280mod tests {
281    use super::*;
282    use crate::util::hex_to_array;
283
284    #[test]
285    fn test_substitute_word() {
286        assert_eq!(substitute_word(0x12345678), 3373838780);
287        assert_eq!(substitute_word(0x00000000), 1667457891);
288        assert_eq!(substitute_word(0xFFFFFFFF), 370546198);
289        assert_eq!(substitute_word(0xAAAAAAAA), 2896997548);
290        assert_eq!(substitute_word(0x55555555), 4244438268);
291    }
292
293    #[test]
294    fn test_key_schedule() {
295        fn test(key_hex: &str, expected_out: &str) {
296            let key = hex_to_array(key_hex);
297            let expanded_key = key_extend(&key);
298            let expanded_key_hex = expanded_key
299                .iter()
300                .map(|value| hex::encode(value.to_le_bytes()))
301                .collect::<String>();
302            assert_eq!(expected_out[..64], expanded_key_hex[..64]);
303            assert_eq!(expected_out[64..], expanded_key_hex[64..]);
304        }
305
306        test(
307            "ac156e17cdabc0b92e3e724a06ef21e5317eb71fbc7f1587403b30ae6962a21a",
308            "ac156e17cdabc0b92e3e724a06ef21e5317eb71fbc7f1587403b30ae6962a21a072fcceeca840c57e4ba7e1de2555ff8a982785e15fd6dd955c65d773ca4ff6d4c39f00586bdfc526207824f8052ddb76482b9f7717fd42e24b98959181d7634ec01e8a86abc14fa08bb96b588e94b02a09c0a80d1e3deaef55a57f7ed4721c344fcc6fd2e40d20726fb44b2ae120fb044557c6795b6a2c960ecf53e8dabd4fd",
309        );
310        test(
311            "688dcc56a1c9b8c9cd9e378a98a1388f17a2c05a698a37232ecd4a567dccdf79",
312            "688dcc56a1c9b8c9cd9e378a98a1388f17a2c05a698a37232ecd4a567dccdf7922137aa983dac2604e44f5ead6e5cd65e17b7d1788f14a34a63c0062dbf0df1bac8dd5102f5717706113e29ab7f62fff48396801c0c8223566f42257bd04fd4c5ad9fc6a758eeb1a149d0980a36b267f42469fd3828ebde6e47a9fb1597e62fda173a8a1d4fd43bbc0604a3b630b6c44b96dcfc83be3722edf99ed9f86e78f62",
313        );
314        test(
315            "a6116fc295f15ff03d538581a560a9c1fdaa1e7f5745d8e6125d6eb092c71b15",
316            "a6116fc295f15ff03d538581a560a9c1fdaa1e7f5745d8e6125d6eb092c71b1561be368df44f697dc91cecfc6c7c453dadba7058faffa8bee8a2c60e7a65dd1b2e7f9957da30f02a132c1cd67f5059eb7fe9bbb18516130f6db4d50117d1081a144f3ba7ce7fcb8ddd53d75ba2038eb04592a256c084b159ad306458bae16c42e41f17532a60dcdef7330b8555308535b99635c079128499d422e0c16ec38c83",
317        );
318        test(
319            "80784e1c1d3730e6f422aae6b10596ab16b190e41eea452af9aeedc97aee4b74",
320            "80784e1c1d3730e6f422aae6b10596ab16b190e41eea452af9aeedc97aee4b74a9cbdcc6b4fcec2040de46c6f1dbd06db708e0d8a9e2a5f2504c483b2aa2034f91b05823254cb4036592f2c5944922a89533731a3cd1d6e86c9d9ed3463f9d9ce0ee8679c5a2327aa030c0bf3479e2178d85ebeab1543d02ddc9a3d19bf63e4daa5c656d6ffe5717cfce97a8fbb775bf822c76e233784be0eeb1e8317547d67c",
321        );
322        test(
323            "cc08712809fd4c0f0b63dc21657f22b3752fba8f2ed5882e7d75e65906bb3399",
324            "cc08712809fd4c0f0b63dc21657f22b3752fba8f2ed5882e7d75e65906bb339927cb9f472e36d34825550f69402a2dda7cca62d8521feaf62f6a0caf29d13f361bbe9ae2358849aa10dd46c350f76b192fa21d0c7dbdf7fa52d7fb557b06c46370a261c3452a286955f76eaa050005b344c17661397c819b6bab7ace10adbeaded0cf409a826dc60fdd1b2caf8d1b77905ffdfd73c835e4c5728248247859a2f",
325        );
326    }
327
328    #[test]
329    fn test_aesb_pseudo_round() {
330        fn test(key_hex: &str, input_hex: &str, expected_out: &str) {
331            let key: [u8; 32] = hex_to_array(key_hex);
332            let extended_key = key_extend(&key);
333            let mut block = u128::from_le_bytes(hex_to_array(input_hex));
334
335            block = aesb_pseudo_round(block, &extended_key);
336            assert_eq!(expected_out, hex::encode(block.to_le_bytes()));
337        }
338
339        test(
340            "1d0b47a047340e32cbe890ca0d61720a09bcfb39e01b7541d1100d1ef91f955f",
341            "274fe9eeb2d1e4c71f0f0244a80e93a1",
342            "be98612d6b05a6cd72df39326180066a",
343        );
344        test(
345            "0093d86fe74698f7b02774e6d4f67e9e29eb71d1754804a19b77d986b8141434",
346            "110f2e5d81f73a512ec95aa5b8e0d7be",
347            "1f1750d997704943b828df66661f7cbf",
348        );
349        test(
350            "d5044939d15af565447ef76445405cd899f81c6f41f4493a5a1323712f815e53",
351            "6022f491b67e27909f74d0e71becebaa",
352            "9f75d250681954d60e418b4333d247a5",
353        );
354        test(
355            "256670ed9eba1db67e6ddec5dfb78f6bfbf55d0f74e2a46d06f2e3592a208014",
356            "4de6ecad6a885ac88f09f9b2be4145fb",
357            "cb286e70825609cb97b7c7ae72548fa9",
358        );
359        test(
360            "e1077c3566d1e8bfeb2e8e48540ed76fb61e973f4951a821c3e8bb918facc03d",
361            "2a2ff0dd38c79ab13fb6b06751824e93",
362            "82f65ba66f8fc6d8e1f4e1f41976eed8",
363        );
364        test(
365            "dee818b6a894121e5e967e2218bb8772b9486bec2241377fdcfed7db75f3b724",
366            "eebc705f33d00fdf7c8add2481c62767",
367            "bee070b25e969ea87578daa1c7831651",
368        );
369        test(
370            "c9b653644f3d3adc3498c029a1373b63f548e853deadc48e559b1a0a05e5c543",
371            "bef0968fc6adb8ce96bfa99642481624",
372            "859fc5f637ee1ee835b6f9a3f16a41f8",
373        );
374        test(
375            "8e65798ebbae347c969ef9778e04e06649e3765aa58f5cd776b6ee58afde98ff",
376            "629f87e95b67e7bd5a3af528379cbef7",
377            "04a697b4fb82466950e9c0668e8c3eb9",
378        );
379        test(
380            "4c0f6a402316b3a73e2a778f20ca3f8335e7a7bb5aecdaf9db91664604b74d62",
381            "3c9ab665451100d8d21029f96edf85f3",
382            "de5e23b1ba21a16ac01098937b26f3a9",
383        );
384        test(
385            "a0b2cb30088b6145d9651ed019b0d051e4e6bf6cc0c8165dc76e3aa9fa9849f0",
386            "6a007f218c3f8b97c8489fe56433c99a",
387            "1885d448a81b0a048cc241275b9d7dce",
388        );
389    }
390
391    #[test]
392    fn test_aesb_single_round() {
393        let test = |key_hex: &str, input_hex: &str, expected_out: &str| {
394            // TODO: Show that both big and little endian work
395            let round_key = u128::from_ne_bytes(hex_to_array(key_hex));
396            let mut block = u128::from_ne_bytes(hex_to_array(input_hex));
397
398            aesb_single_round(&mut block, round_key);
399            assert_eq!(expected_out, hex::encode(block.to_ne_bytes()));
400        };
401
402        test(
403            "9af7bd044f96bba5251ebd8065f4c757",
404            "8844d7f6f6aa2df5706ef0e7b26a3410",
405            "a03593fc2b9b906069bfc3a86a12e7fe",
406        );
407        test(
408            "9749a59d1ee692c3b70b9c38a0c88369",
409            "f96b5a1984f7c57b92d7b2e82dd0ce46",
410            "6b03d4c6edf7a914265ca765b784c5ee",
411        );
412        test(
413            "20959275e137a08267e35afe66f9adeb",
414            "3fce6f546d8fbc15bd9c6ac7d533eae4",
415            "34692b49471e37df3cbe43a9459ebe97",
416        );
417        test(
418            "5c429524d022dc5dd48f7cd529fdf4f2",
419            "3edae93308c9aab4dfb6bfcd8e4012af",
420            "2e3061ce680d75177bac5b7af3182543",
421        );
422        test(
423            "e76c56ca69a7309866a730e8976da086",
424            "39d8ee732a115b6b21f89ca181bd9ddc",
425            "069ef0b7aaada2b65ea9665827dae9ae",
426        );
427        test(
428            "afd540af324c4fcda6246c657424a3ce",
429            "a5a01d75141522ff1ea717083abc5b5e",
430            "e0320cbc9dd8279c5f7d121ef7e1ae46",
431        );
432        test(
433            "dfe9ba9468ccf1ac20a305730d1bdcb7",
434            "7be56ce9d924bf2fc4b574e225676f3c",
435            "bee2b49ed0b578b2c94b03a8930d990c",
436        );
437        test(
438            "381e788a8d3389f27fe9aff054a0b407",
439            "b8d2600f71b0e9535d17c00ba90246f6",
440            "2a305ae7f7f3f44a43cd0342180b9394",
441        );
442        test(
443            "16a94460158a5512052626f6cb080d6d",
444            "5ea0c238c05b8f3a913c1b36102eabeb",
445            "ab8040d7395cc940ea2a47610989ceb1",
446        );
447        test(
448            "7e584682efb38bf2adfc6f1958fe08ff",
449            "80c78bb6ca2f114cbcb49fbaadaee9d1",
450            "25f7717cdbaa9c614424ef3d4e9543ec",
451        );
452    }
453}