1use crate::ct::zeroize_slice;
20use crate::BlockCipher;
21
22const PHI: u32 = 0x9E37_79B9;
24
25const SBOXES: [[u8; 16]; 8] = [
27 [3, 8, 15, 1, 10, 6, 5, 11, 14, 13, 4, 2, 7, 0, 9, 12],
28 [15, 12, 2, 7, 9, 0, 5, 10, 1, 11, 14, 8, 6, 13, 3, 4],
29 [8, 6, 7, 9, 3, 12, 10, 15, 13, 1, 14, 4, 0, 11, 5, 2],
30 [0, 15, 11, 8, 12, 9, 6, 3, 13, 1, 2, 4, 10, 7, 5, 14],
31 [1, 15, 8, 3, 12, 0, 11, 6, 2, 5, 4, 10, 9, 14, 7, 13],
32 [15, 5, 2, 11, 4, 10, 9, 12, 0, 3, 14, 8, 13, 6, 7, 1],
33 [7, 2, 12, 5, 8, 4, 6, 11, 14, 9, 1, 15, 13, 3, 10, 0],
34 [1, 13, 15, 0, 14, 8, 2, 11, 7, 4, 12, 10, 9, 3, 5, 6],
35];
36
37const INV_SBOXES: [[u8; 16]; 8] = [
39 [13, 3, 11, 0, 10, 6, 5, 12, 1, 14, 4, 7, 15, 9, 8, 2],
40 [5, 8, 2, 14, 15, 6, 12, 3, 11, 4, 7, 9, 1, 13, 10, 0],
41 [12, 9, 15, 4, 11, 14, 1, 2, 0, 3, 6, 13, 5, 8, 10, 7],
42 [0, 9, 10, 7, 11, 14, 6, 13, 3, 5, 12, 2, 4, 8, 15, 1],
43 [5, 0, 8, 3, 10, 9, 7, 14, 2, 12, 11, 6, 4, 15, 13, 1],
44 [8, 15, 2, 9, 4, 1, 13, 14, 11, 6, 5, 3, 7, 12, 10, 0],
45 [15, 10, 1, 13, 5, 3, 6, 0, 4, 9, 14, 7, 2, 12, 8, 11],
46 [3, 0, 6, 13, 9, 14, 15, 8, 5, 12, 11, 7, 10, 1, 4, 2],
47];
48
49const fn build_sboxes_anf(sboxes: &[[u8; 16]; 8]) -> [[u16; 4]; 8] {
50 let mut out = [[0u16; 4]; 8];
51 let mut i = 0usize;
52 while i < 8 {
53 out[i] = crate::ct::build_nibble_sbox_anf(&sboxes[i]);
54 i += 1;
55 }
56 out
57}
58
59const SBOXES_ANF: [[u16; 4]; 8] = build_sboxes_anf(&SBOXES);
60const INV_SBOXES_ANF: [[u16; 4]; 8] = build_sboxes_anf(&INV_SBOXES);
61
62#[inline]
63fn sbox_ct_nibble(input: u8, sbox_anf: [u16; 4]) -> u8 {
64 crate::ct::eval_nibble_sbox(sbox_anf, input)
65}
66
67#[inline]
83fn apply_sbox_table(words: [u32; 4], table: &[u8; 16]) -> [u32; 4] {
84 let [x0, x1, x2, x3] = words;
87 let mut out = [0u32; 4];
88 let mut bit = 0u32;
89 while bit < 32 {
90 let nibble = (((x0 >> bit) & 1)
91 | (((x1 >> bit) & 1) << 1)
92 | (((x2 >> bit) & 1) << 2)
93 | (((x3 >> bit) & 1) << 3)) as usize;
94 let s = table[nibble];
95 out[0] |= u32::from(s & 1) << bit;
96 out[1] |= u32::from((s >> 1) & 1) << bit;
97 out[2] |= u32::from((s >> 2) & 1) << bit;
98 out[3] |= u32::from((s >> 3) & 1) << bit;
99 bit += 1;
100 }
101 out
102}
103
104#[inline]
105fn apply_sbox_ct(words: [u32; 4], sbox_anf: [u16; 4]) -> [u32; 4] {
106 let [x0, x1, x2, x3] = words;
109 let mut out = [0u32; 4];
110 let mut bit = 0u32;
111 while bit < 32 {
112 let nibble = u8::try_from(
113 ((x0 >> bit) & 1)
114 | (((x1 >> bit) & 1) << 1)
115 | (((x2 >> bit) & 1) << 2)
116 | (((x3 >> bit) & 1) << 3),
117 )
118 .expect("bit-packed nibble fits in u8");
119 let s = sbox_ct_nibble(nibble, sbox_anf);
120 out[0] |= u32::from(s & 1) << bit;
121 out[1] |= u32::from((s >> 1) & 1) << bit;
122 out[2] |= u32::from((s >> 2) & 1) << bit;
123 out[3] |= u32::from((s >> 3) & 1) << bit;
124 bit += 1;
125 }
126 out
127}
128
129#[inline]
130fn apply_sbox_round(words: [u32; 4], round: usize, use_ct: bool) -> [u32; 4] {
131 if use_ct {
132 apply_sbox_ct(words, SBOXES_ANF[round & 7])
133 } else {
134 apply_sbox_table(words, &SBOXES[round & 7])
135 }
136}
137
138#[inline]
139fn apply_inv_sbox_round(words: [u32; 4], round: usize, use_ct: bool) -> [u32; 4] {
140 if use_ct {
141 apply_sbox_ct(words, INV_SBOXES_ANF[round & 7])
142 } else {
143 apply_sbox_table(words, &INV_SBOXES[round & 7])
144 }
145}
146
147#[inline]
148fn lt(words: [u32; 4]) -> [u32; 4] {
149 let mut x0 = words[0].rotate_left(13);
150 let mut x2 = words[2].rotate_left(3);
151 let mut x1 = words[1] ^ x0 ^ x2;
152 let mut x3 = words[3] ^ x2 ^ (x0 << 3);
153 x1 = x1.rotate_left(1);
154 x3 = x3.rotate_left(7);
155 x0 ^= x1 ^ x3;
156 x2 ^= x3 ^ (x1 << 7);
157 x0 = x0.rotate_left(5);
158 x2 = x2.rotate_left(22);
159 [x0, x1, x2, x3]
160}
161
162#[inline]
163fn inv_lt(words: [u32; 4]) -> [u32; 4] {
164 let mut x0 = words[0].rotate_right(5);
165 let mut x1 = words[1];
166 let mut x2 = words[2].rotate_right(22);
167 let mut x3 = words[3];
168 x2 ^= x3 ^ (x1 << 7);
169 x0 ^= x1 ^ x3;
170 x3 = x3.rotate_right(7);
171 x1 = x1.rotate_right(1);
172 x3 ^= x2 ^ (x0 << 3);
173 x1 ^= x0 ^ x2;
174 x2 = x2.rotate_right(3);
175 x0 = x0.rotate_right(13);
176 [x0, x1, x2, x3]
177}
178
179#[inline]
180fn reverse_bytes<const N: usize>(input: &[u8; N]) -> [u8; N] {
181 let mut out = [0u8; N];
182 let mut i = 0usize;
183 while i < N {
184 out[i] = input[N - 1 - i];
185 i += 1;
186 }
187 out
188}
189
190#[inline]
191fn words_from_block_internal(block: &[u8; 16]) -> [u32; 4] {
192 [
193 u32::from_le_bytes(block[0..4].try_into().unwrap()),
194 u32::from_le_bytes(block[4..8].try_into().unwrap()),
195 u32::from_le_bytes(block[8..12].try_into().unwrap()),
196 u32::from_le_bytes(block[12..16].try_into().unwrap()),
197 ]
198}
199
200#[inline]
201fn block_from_words_internal(words: [u32; 4]) -> [u8; 16] {
202 let mut out = [0u8; 16];
203 out[0..4].copy_from_slice(&words[0].to_le_bytes());
204 out[4..8].copy_from_slice(&words[1].to_le_bytes());
205 out[8..12].copy_from_slice(&words[2].to_le_bytes());
206 out[12..16].copy_from_slice(&words[3].to_le_bytes());
207 out
208}
209
210fn expand_round_keys<const N: usize>(user_key: &[u8; N], use_ct: bool) -> [[u32; 4]; 33] {
211 let key = reverse_bytes(user_key);
212 let mut padded = [0u8; 32];
213 padded[..N].copy_from_slice(&key);
214 if N < 32 {
215 padded[N] = 1;
216 }
217
218 let mut words = [0u32; 140];
219 let mut i = 0usize;
220 while i < 8 {
221 let off = 4 * i;
222 words[i] = u32::from_le_bytes(padded[off..off + 4].try_into().unwrap());
223 i += 1;
224 }
225 while i < 140 {
226 words[i] = (words[i - 8]
227 ^ words[i - 5]
228 ^ words[i - 3]
229 ^ words[i - 1]
230 ^ PHI
231 ^ u32::try_from(i - 8).expect("round-key index fits in u32"))
232 .rotate_left(11);
233 i += 1;
234 }
235
236 let mut out = [[0u32; 4]; 33];
237 let mut round = 0usize;
238 while round < 33 {
239 let sbox_idx = (3usize.wrapping_sub(round)) & 7;
240 let input = [
241 words[8 + 4 * round],
242 words[8 + 4 * round + 1],
243 words[8 + 4 * round + 2],
244 words[8 + 4 * round + 3],
245 ];
246 out[round] = if use_ct {
247 apply_sbox_ct(input, SBOXES_ANF[sbox_idx])
248 } else {
249 apply_sbox_table(input, &SBOXES[sbox_idx])
250 };
251 round += 1;
252 }
253
254 out
255}
256
257fn serpent_encrypt_words(
258 mut state: [u32; 4],
259 round_keys: &[[u32; 4]; 33],
260 use_ct: bool,
261) -> [u32; 4] {
262 let mut round = 0usize;
263 while round < 31 {
264 state[0] ^= round_keys[round][0];
265 state[1] ^= round_keys[round][1];
266 state[2] ^= round_keys[round][2];
267 state[3] ^= round_keys[round][3];
268 state = apply_sbox_round(state, round, use_ct);
269 state = lt(state);
270 round += 1;
271 }
272
273 state[0] ^= round_keys[31][0];
274 state[1] ^= round_keys[31][1];
275 state[2] ^= round_keys[31][2];
276 state[3] ^= round_keys[31][3];
277 state = apply_sbox_round(state, 31, use_ct);
278 state[0] ^= round_keys[32][0];
279 state[1] ^= round_keys[32][1];
280 state[2] ^= round_keys[32][2];
281 state[3] ^= round_keys[32][3];
282 state
283}
284
285fn serpent_decrypt_words(
286 mut state: [u32; 4],
287 round_keys: &[[u32; 4]; 33],
288 use_ct: bool,
289) -> [u32; 4] {
290 state[0] ^= round_keys[32][0];
291 state[1] ^= round_keys[32][1];
292 state[2] ^= round_keys[32][2];
293 state[3] ^= round_keys[32][3];
294 state = apply_inv_sbox_round(state, 31, use_ct);
295 state[0] ^= round_keys[31][0];
296 state[1] ^= round_keys[31][1];
297 state[2] ^= round_keys[31][2];
298 state[3] ^= round_keys[31][3];
299
300 let mut round = 31usize;
301 while round > 0 {
302 round -= 1;
303 state = inv_lt(state);
304 state = apply_inv_sbox_round(state, round, use_ct);
305 state[0] ^= round_keys[round][0];
306 state[1] ^= round_keys[round][1];
307 state[2] ^= round_keys[round][2];
308 state[3] ^= round_keys[round][3];
309 }
310
311 state
312}
313
314fn encrypt_block_words(round_keys: &[[u32; 4]; 33], block: &[u8; 16], use_ct: bool) -> [u8; 16] {
315 let internal = reverse_bytes(block);
316 let state = words_from_block_internal(&internal);
317 let out = serpent_encrypt_words(state, round_keys, use_ct);
318 reverse_bytes(&block_from_words_internal(out))
319}
320
321fn decrypt_block_words(round_keys: &[[u32; 4]; 33], block: &[u8; 16], use_ct: bool) -> [u8; 16] {
322 let internal = reverse_bytes(block);
323 let state = words_from_block_internal(&internal);
324 let out = serpent_decrypt_words(state, round_keys, use_ct);
325 reverse_bytes(&block_from_words_internal(out))
326}
327
328macro_rules! serpent_type {
329 ($name:ident, $name_ct:ident, $key_len:literal, $doc:literal, $doc_ct:literal) => {
330 #[doc = $doc]
331 pub struct $name {
332 round_keys: [[u32; 4]; 33],
333 }
334
335 impl $name {
336 pub fn new(key: &[u8; $key_len]) -> Self {
338 Self {
339 round_keys: expand_round_keys(key, false),
340 }
341 }
342
343 pub fn new_wiping(key: &mut [u8; $key_len]) -> Self {
345 let cipher = Self::new(key);
346 zeroize_slice(key);
347 cipher
348 }
349
350 pub fn encrypt_block(&self, block: &[u8; 16]) -> [u8; 16] {
352 encrypt_block_words(&self.round_keys, block, false)
353 }
354
355 pub fn decrypt_block(&self, block: &[u8; 16]) -> [u8; 16] {
357 decrypt_block_words(&self.round_keys, block, false)
358 }
359 }
360
361 impl BlockCipher for $name {
362 const BLOCK_LEN: usize = 16;
363
364 fn encrypt(&self, block: &mut [u8]) {
365 let arr: &[u8; 16] = (&*block).try_into().expect("wrong block length");
366 let ct = self.encrypt_block(arr);
367 block.copy_from_slice(&ct);
368 }
369
370 fn decrypt(&self, block: &mut [u8]) {
371 let arr: &[u8; 16] = (&*block).try_into().expect("wrong block length");
372 let pt = self.decrypt_block(arr);
373 block.copy_from_slice(&pt);
374 }
375 }
376
377 impl Drop for $name {
378 fn drop(&mut self) {
379 for rk in &mut self.round_keys {
380 zeroize_slice(rk);
381 }
382 }
383 }
384
385 #[doc = $doc_ct]
386 pub struct $name_ct {
387 round_keys: [[u32; 4]; 33],
388 }
389
390 impl $name_ct {
391 pub fn new(key: &[u8; $key_len]) -> Self {
393 Self {
394 round_keys: expand_round_keys(key, true),
395 }
396 }
397
398 pub fn new_wiping(key: &mut [u8; $key_len]) -> Self {
400 let cipher = Self::new(key);
401 zeroize_slice(key);
402 cipher
403 }
404
405 pub fn encrypt_block(&self, block: &[u8; 16]) -> [u8; 16] {
407 encrypt_block_words(&self.round_keys, block, true)
408 }
409
410 pub fn decrypt_block(&self, block: &[u8; 16]) -> [u8; 16] {
412 decrypt_block_words(&self.round_keys, block, true)
413 }
414 }
415
416 impl BlockCipher for $name_ct {
417 const BLOCK_LEN: usize = 16;
418
419 fn encrypt(&self, block: &mut [u8]) {
420 let arr: &[u8; 16] = (&*block).try_into().expect("wrong block length");
421 let ct = self.encrypt_block(arr);
422 block.copy_from_slice(&ct);
423 }
424
425 fn decrypt(&self, block: &mut [u8]) {
426 let arr: &[u8; 16] = (&*block).try_into().expect("wrong block length");
427 let pt = self.decrypt_block(arr);
428 block.copy_from_slice(&pt);
429 }
430 }
431
432 impl Drop for $name_ct {
433 fn drop(&mut self) {
434 for rk in &mut self.round_keys {
435 zeroize_slice(rk);
436 }
437 }
438 }
439 };
440}
441
442serpent_type!(
443 Serpent128,
444 Serpent128Ct,
445 16,
446 "Serpent with a 128-bit key (public byte order matches the original submission vectors).",
447 "Constant-time Serpent with a 128-bit key."
448);
449serpent_type!(
450 Serpent192,
451 Serpent192Ct,
452 24,
453 "Serpent with a 192-bit key (public byte order matches the original submission vectors).",
454 "Constant-time Serpent with a 192-bit key."
455);
456serpent_type!(
457 Serpent256,
458 Serpent256Ct,
459 32,
460 "Serpent with a 256-bit key (public byte order matches the original submission vectors).",
461 "Constant-time Serpent with a 256-bit key."
462);
463
464pub type Serpent = Serpent128;
465pub type SerpentCt = Serpent128Ct;
466
467#[cfg(test)]
468mod tests {
469 use super::*;
470
471 fn decode_hex(s: &str) -> Vec<u8> {
472 assert_eq!(s.len() % 2, 0);
473 let mut out = Vec::with_capacity(s.len() / 2);
474 let bytes = s.as_bytes();
475 let mut i = 0usize;
476 while i < bytes.len() {
477 let hi = (bytes[i] as char).to_digit(16).unwrap();
478 let lo = (bytes[i + 1] as char).to_digit(16).unwrap();
479 out.push(u8::try_from((hi << 4) | lo).expect("decoded hex byte fits in u8"));
480 i += 2;
481 }
482 out
483 }
484
485 #[test]
486 fn ct_sboxes_match_tables() {
487 for sbox in 0..8 {
488 for x in 0u8..16 {
489 assert_eq!(
490 SBOXES[sbox][x as usize],
491 sbox_ct_nibble(x, SBOXES_ANF[sbox])
492 );
493 assert_eq!(
494 INV_SBOXES[sbox][x as usize],
495 sbox_ct_nibble(x, INV_SBOXES_ANF[sbox])
496 );
497 }
498 }
499 }
500
501 #[test]
502 fn serpent128_kat() {
503 let key: [u8; 16] = decode_hex("80000000000000000000000000000000")
504 .try_into()
505 .unwrap();
506 let pt: [u8; 16] = decode_hex("00000000000000000000000000000000")
507 .try_into()
508 .unwrap();
509 let ct: [u8; 16] = decode_hex("49AFBFAD9D5A34052CD8FFA5986BD2DD")
510 .try_into()
511 .unwrap();
512 let cipher = Serpent128::new(&key);
513 assert_eq!(cipher.encrypt_block(&pt), ct);
514 assert_eq!(cipher.decrypt_block(&ct), pt);
515 }
516
517 #[test]
518 fn serpent128_ct_kat() {
519 let key: [u8; 16] = decode_hex("80000000000000000000000000000000")
520 .try_into()
521 .unwrap();
522 let pt: [u8; 16] = decode_hex("00000000000000000000000000000000")
523 .try_into()
524 .unwrap();
525 let ct: [u8; 16] = decode_hex("49AFBFAD9D5A34052CD8FFA5986BD2DD")
526 .try_into()
527 .unwrap();
528 let cipher = Serpent128Ct::new(&key);
529 assert_eq!(cipher.encrypt_block(&pt), ct);
530 assert_eq!(cipher.decrypt_block(&ct), pt);
531 }
532
533 #[test]
534 fn serpent128_standard_plaintext_vector() {
535 let key = [0u8; 16];
536 let pt: [u8; 16] = decode_hex("80000000000000000000000000000000")
537 .try_into()
538 .unwrap();
539 let ct: [u8; 16] = decode_hex("10B5FFB720B8CB9002A1142B0BA2E94A")
540 .try_into()
541 .unwrap();
542 let cipher = Serpent128::new(&key);
543 assert_eq!(cipher.encrypt_block(&pt), ct);
544 assert_eq!(cipher.decrypt_block(&ct), pt);
545 }
546
547 #[test]
548 fn serpent192_kat() {
549 let key: [u8; 24] = decode_hex("800000000000000000000000000000000000000000000000")
550 .try_into()
551 .unwrap();
552 let pt: [u8; 16] = decode_hex("00000000000000000000000000000000")
553 .try_into()
554 .unwrap();
555 let ct: [u8; 16] = decode_hex("E78E5402C7195568AC3678F7A3F60C66")
556 .try_into()
557 .unwrap();
558 let cipher = Serpent192::new(&key);
559 assert_eq!(cipher.encrypt_block(&pt), ct);
560 assert_eq!(cipher.decrypt_block(&ct), pt);
561 }
562
563 #[test]
564 fn serpent192_ct_kat() {
565 let key: [u8; 24] = decode_hex("800000000000000000000000000000000000000000000000")
566 .try_into()
567 .unwrap();
568 let pt: [u8; 16] = decode_hex("00000000000000000000000000000000")
569 .try_into()
570 .unwrap();
571 let ct: [u8; 16] = decode_hex("E78E5402C7195568AC3678F7A3F60C66")
572 .try_into()
573 .unwrap();
574 let cipher = Serpent192Ct::new(&key);
575 assert_eq!(cipher.encrypt_block(&pt), ct);
576 assert_eq!(cipher.decrypt_block(&ct), pt);
577 }
578
579 #[test]
580 fn serpent256_kat() {
581 let key: [u8; 32] =
582 decode_hex("8000000000000000000000000000000000000000000000000000000000000000")
583 .try_into()
584 .unwrap();
585 let pt: [u8; 16] = decode_hex("00000000000000000000000000000000")
586 .try_into()
587 .unwrap();
588 let ct: [u8; 16] = decode_hex("ABED96E766BF28CBC0EBD21A82EF0819")
589 .try_into()
590 .unwrap();
591 let cipher = Serpent256::new(&key);
592 assert_eq!(cipher.encrypt_block(&pt), ct);
593 assert_eq!(cipher.decrypt_block(&ct), pt);
594 }
595
596 #[test]
597 fn serpent256_ct_kat() {
598 let key: [u8; 32] =
599 decode_hex("8000000000000000000000000000000000000000000000000000000000000000")
600 .try_into()
601 .unwrap();
602 let pt: [u8; 16] = decode_hex("00000000000000000000000000000000")
603 .try_into()
604 .unwrap();
605 let ct: [u8; 16] = decode_hex("ABED96E766BF28CBC0EBD21A82EF0819")
606 .try_into()
607 .unwrap();
608 let cipher = Serpent256Ct::new(&key);
609 assert_eq!(cipher.encrypt_block(&pt), ct);
610 assert_eq!(cipher.decrypt_block(&ct), pt);
611 }
612}