1#[cfg(target_arch = "aarch64")]
4mod aarch64;
5#[cfg(target_arch = "x86_64")]
6mod x86_64;
7
8use aes::Aes128;
9use aes::cipher::array::Array;
10use aes::cipher::{BlockCipherDecrypt, BlockCipherEncrypt, KeyInit};
11use subspace_core_primitives::pot::{PotCheckpoints, PotKey, PotOutput, PotSeed};
12
13#[inline(always)]
15pub(crate) fn create(seed: PotSeed, key: PotKey, checkpoint_iterations: u32) -> PotCheckpoints {
16 #[cfg(target_arch = "x86_64")]
17 {
18 cpufeatures::new!(has_aes, "aes");
19 if has_aes::get() {
20 return unsafe { x86_64::create(seed.as_ref(), key.as_ref(), checkpoint_iterations) };
22 }
23 }
24 #[cfg(target_arch = "aarch64")]
25 {
26 cpufeatures::new!(has_aes, "aes");
27 if has_aes::get() {
28 return unsafe { aarch64::create(seed.as_ref(), key.as_ref(), checkpoint_iterations) };
30 }
31 }
32
33 create_generic(seed, key, checkpoint_iterations)
34}
35
36fn create_generic(seed: PotSeed, key: PotKey, checkpoint_iterations: u32) -> PotCheckpoints {
37 let key = Array::from(*key);
38 let cipher = Aes128::new(&key);
39 let mut cur_block = Array::from(*seed);
40
41 let mut checkpoints = PotCheckpoints::default();
42 for checkpoint in checkpoints.iter_mut() {
43 for _ in 0..checkpoint_iterations {
44 cipher.encrypt_block(&mut cur_block);
46 }
47 checkpoint.copy_from_slice(&cur_block);
48 }
49
50 checkpoints
51}
52
53#[inline(always)]
57pub(crate) fn verify_sequential(
58 seed: PotSeed,
59 key: PotKey,
60 checkpoints: &PotCheckpoints,
61 checkpoint_iterations: u32,
62) -> bool {
63 assert_eq!(checkpoint_iterations % 2, 0);
64
65 #[cfg(target_arch = "x86_64")]
66 {
67 cpufeatures::new!(has_avx512f_vaes, "avx512f", "vaes");
68 if has_avx512f_vaes::get() {
69 return unsafe {
71 x86_64::verify_sequential_avx512f_vaes(
72 &seed,
73 &key,
74 checkpoints,
75 checkpoint_iterations,
76 )
77 };
78 }
79
80 cpufeatures::new!(has_avx2_vaes, "avx2", "vaes");
81 if has_avx2_vaes::get() {
82 return unsafe {
84 x86_64::verify_sequential_avx2_vaes(&seed, &key, checkpoints, checkpoint_iterations)
85 };
86 }
87
88 cpufeatures::new!(has_aes_sse41, "aes", "sse4.1");
89 if has_aes_sse41::get() {
90 return unsafe {
92 x86_64::verify_sequential_aes_sse41(&seed, &key, checkpoints, checkpoint_iterations)
93 };
94 }
95 }
96 #[cfg(target_arch = "aarch64")]
97 {
98 cpufeatures::new!(has_aes, "aes");
99 if has_aes::get() {
100 return unsafe {
102 aarch64::verify_sequential_aes(&seed, &key, checkpoints, checkpoint_iterations)
103 };
104 }
105 }
106
107 verify_sequential_generic(seed, key, checkpoints, checkpoint_iterations)
108}
109
110fn verify_sequential_generic(
111 seed: PotSeed,
112 key: PotKey,
113 checkpoints: &PotCheckpoints,
114 checkpoint_iterations: u32,
115) -> bool {
116 let key = Array::from(*key);
117 let cipher = Aes128::new(&key);
118
119 let mut inputs = [[0u8; 16]; PotCheckpoints::NUM_CHECKPOINTS.get() as usize];
120 inputs[0] = *seed;
121 inputs[1..].copy_from_slice(PotOutput::repr_from_slice(
122 &checkpoints[..PotCheckpoints::NUM_CHECKPOINTS.get() as usize - 1],
123 ));
124
125 let mut outputs = [[0u8; 16]; PotCheckpoints::NUM_CHECKPOINTS.get() as usize];
126 outputs.copy_from_slice(PotOutput::repr_from_slice(checkpoints.as_slice()));
127
128 for _ in 0..checkpoint_iterations / 2 {
129 cipher.encrypt_blocks(Array::cast_slice_from_core_mut(&mut inputs));
130 cipher.decrypt_blocks(Array::cast_slice_from_core_mut(&mut outputs));
131 }
132
133 inputs == outputs
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139 use subspace_core_primitives::pot::PotOutput;
140
141 const SEED: [u8; 16] = [
142 0xd6, 0x66, 0xcc, 0xd8, 0xd5, 0x93, 0xc2, 0x3d, 0xa8, 0xdb, 0x6b, 0x5b, 0x14, 0x13, 0xb1,
143 0x3a,
144 ];
145 const SEED_1: [u8; 16] = [
146 0xd7, 0xd6, 0xdc, 0xd8, 0xd5, 0x93, 0xc2, 0x3d, 0xa8, 0xdb, 0x6b, 0x5b, 0x14, 0x13, 0xb1,
147 0x3a,
148 ];
149 const KEY: [u8; 16] = [
150 0x9a, 0x84, 0x94, 0x0f, 0xfe, 0xf5, 0xb0, 0xd7, 0x01, 0x99, 0xfc, 0x67, 0xf4, 0x6e, 0xa2,
151 0x7a,
152 ];
153 const KEY_1: [u8; 16] = [
154 0x9b, 0x8b, 0x9b, 0x0f, 0xfe, 0xf5, 0xb0, 0xd7, 0x01, 0x99, 0xfc, 0x67, 0xf4, 0x6e, 0xa2,
155 0x7a,
156 ];
157 const BAD_CIPHER: [u8; 16] = [22; 16];
158
159 fn verify_test(
160 seed: PotSeed,
161 key: PotKey,
162 checkpoints: &PotCheckpoints,
163 checkpoint_iterations: u32,
164 ) -> bool {
165 let sequential = verify_sequential(seed, key, checkpoints, checkpoint_iterations);
166 let generic = verify_sequential_generic(seed, key, checkpoints, checkpoint_iterations);
167 assert_eq!(sequential, generic);
168
169 #[cfg(target_arch = "x86_64")]
170 {
171 cpufeatures::new!(has_avx512f_vaes, "avx512f", "vaes");
172 if has_avx512f_vaes::get() {
173 let avx512f_vaes = unsafe {
175 x86_64::verify_sequential_avx512f_vaes(
176 &seed,
177 &key,
178 checkpoints,
179 checkpoint_iterations,
180 )
181 };
182 assert_eq!(sequential, avx512f_vaes);
183 }
184
185 cpufeatures::new!(has_avx2_vaes, "avx2", "vaes");
186 if has_avx2_vaes::get() {
187 let avx2_vaes = unsafe {
189 x86_64::verify_sequential_avx2_vaes(
190 &seed,
191 &key,
192 checkpoints,
193 checkpoint_iterations,
194 )
195 };
196 assert_eq!(sequential, avx2_vaes);
197 }
198
199 cpufeatures::new!(has_aes_sse41, "aes", "sse4.1");
200 if has_aes_sse41::get() {
201 let aes_sse41 = unsafe {
203 x86_64::verify_sequential_aes_sse41(
204 &seed,
205 &key,
206 checkpoints,
207 checkpoint_iterations,
208 )
209 };
210 assert_eq!(sequential, aes_sse41);
211 }
212 }
213 #[cfg(target_arch = "aarch64")]
214 {
215 cpufeatures::new!(has_aes, "aes");
216 if has_aes::get() {
217 let aes = unsafe {
219 aarch64::verify_sequential_aes(&seed, &key, checkpoints, checkpoint_iterations)
220 };
221 assert_eq!(sequential, aes);
222 }
223 }
224
225 sequential
226 }
227
228 #[test]
229 fn test_create_verify() {
230 let seed = PotSeed::from(SEED);
231 let key = PotKey::from(KEY);
232 let checkpoint_iterations = 20;
233
234 let checkpoints = create(seed, key, checkpoint_iterations);
236 {
237 let generic_checkpoints = create_generic(seed, key, checkpoint_iterations);
238 assert_eq!(checkpoints, generic_checkpoints);
239 }
240
241 assert!(verify_test(seed, key, &checkpoints, checkpoint_iterations,));
242
243 let mut checkpoints_1 = checkpoints;
245 checkpoints_1[0] = PotOutput::from(BAD_CIPHER);
246 assert!(!verify_test(
247 seed,
248 key,
249 &checkpoints_1,
250 checkpoint_iterations,
251 ));
252
253 assert!(!verify_test(
255 seed,
256 key,
257 &checkpoints,
258 checkpoint_iterations + 2,
259 ));
260 assert!(!verify_test(
261 seed,
262 key,
263 &checkpoints,
264 checkpoint_iterations - 2,
265 ));
266
267 assert!(!verify_test(
269 PotSeed::from(SEED_1),
270 key,
271 &checkpoints,
272 checkpoint_iterations,
273 ));
274
275 assert!(!verify_test(
277 seed,
278 PotKey::from(KEY_1),
279 &checkpoints,
280 checkpoint_iterations,
281 ));
282 }
283}