Skip to main content

cryptography/ciphers/
present.rs

1//! PRESENT lightweight block cipher — CHES 2007 / ISO/IEC 29192-2.
2//!
3//! 64-bit block cipher with two standard key schedules:
4//!
5//! - `Present80` / `Present80Ct`: 80-bit key
6//! - `Present128` / `Present128Ct`: 128-bit key
7//!
8//! The round structure is a 31-round SP-network:
9//!
10//! ```text
11//! state <- state xor round_key
12//! state <- sbox_layer(state)
13//! state <- p_layer(state)
14//! ```
15//!
16//! followed by a final round-key xor. The fast path keeps the direct 4-bit
17//! S-box table lookup. The Ct path uses a packed 16-term ANF form of the same
18//! 4->4 bijection so substitution avoids secret-indexed table reads while the
19//! rest of the permutation network stays unchanged.
20
21const SBOX: [u8; 16] = [
22    0xC, 0x5, 0x6, 0xB, 0x9, 0x0, 0xA, 0xD, 0x3, 0xE, 0xF, 0x8, 0x4, 0x7, 0x1, 0x2,
23];
24const INV_SBOX: [u8; 16] = [
25    0x5, 0xE, 0xF, 0x8, 0xC, 0x1, 0x2, 0xD, 0xB, 0x4, 0x6, 0x3, 0x0, 0x7, 0x9, 0xA,
26];
27
28const SBOX_ANF: [u16; 4] = crate::ct::build_nibble_sbox_anf(&SBOX);
29const INV_SBOX_ANF: [u16; 4] = crate::ct::build_nibble_sbox_anf(&INV_SBOX);
30
31#[inline]
32fn sbox_ct_nibble(input: u8) -> u8 {
33    crate::ct::eval_nibble_sbox(SBOX_ANF, input)
34}
35
36#[inline]
37fn inv_sbox_ct_nibble(input: u8) -> u8 {
38    crate::ct::eval_nibble_sbox(INV_SBOX_ANF, input)
39}
40
41#[inline]
42fn sbox_layer(state: u64) -> u64 {
43    let mut out = 0u64;
44    let mut i = 0usize;
45    while i < 16 {
46        let nibble = ((state >> (4 * i)) & 0x0f) as usize;
47        out |= u64::from(SBOX[nibble]) << (4 * i);
48        i += 1;
49    }
50    out
51}
52
53#[inline]
54fn inv_sbox_layer(state: u64) -> u64 {
55    let mut out = 0u64;
56    let mut i = 0usize;
57    while i < 16 {
58        let nibble = ((state >> (4 * i)) & 0x0f) as usize;
59        out |= u64::from(INV_SBOX[nibble]) << (4 * i);
60        i += 1;
61    }
62    out
63}
64
65#[inline]
66fn sbox_layer_ct(state: u64) -> u64 {
67    let mut out = 0u64;
68    let mut i = 0usize;
69    while i < 16 {
70        let nibble = ((state >> (4 * i)) & 0x0f) as u8;
71        out |= u64::from(sbox_ct_nibble(nibble)) << (4 * i);
72        i += 1;
73    }
74    out
75}
76
77#[inline]
78fn inv_sbox_layer_ct(state: u64) -> u64 {
79    let mut out = 0u64;
80    let mut i = 0usize;
81    while i < 16 {
82        let nibble = ((state >> (4 * i)) & 0x0f) as u8;
83        out |= u64::from(inv_sbox_ct_nibble(nibble)) << (4 * i);
84        i += 1;
85    }
86    out
87}
88
89/// Apply the PRESENT P-layer bit permutation (ISO/IEC 29192-2 §2.3).
90///
91/// The P-layer sends source bit `i` to destination bit `P(i)` where:
92///   P(i) = 16·i mod 63   for i ∈ 0..62
93///   P(63) = 63            (MSB is fixed)
94///
95/// The formula produces a full permutation on 63 elements because
96/// gcd(16, 63) = 1 (easily verified: 63 = 3·16 + 15; 16 = 1·15 + 1; gcd = 1),
97/// so "stride-16" cycles through all 63 bit positions before repeating.
98///
99/// Implementation: "scatter" mode — for each source bit, compute its
100/// destination and set that bit in the output.
101#[inline]
102fn p_layer(state: u64) -> u64 {
103    // PRESENT bit permutation:
104    // P(i) = 16*i mod 63 for i in [0, 62], and P(63) = 63.
105    // (Bogdanov et al., CHES 2007; ISO/IEC 29192-2.)
106    let mut out = 0u64;
107    let mut bit = 0usize;
108    while bit < 63 {
109        let dst = (16 * bit) % 63;
110        out |= ((state >> bit) & 1) << dst;
111        bit += 1;
112    }
113    out |= ((state >> 63) & 1) << 63;
114    out
115}
116
117/// Inverse P-layer — "gather" mode using the same stride-16 formula.
118///
119/// `inv_p_layer` reads output bit `i` from source position `P(i) = 16·i mod 63`.
120/// This is the inverse of `p_layer`'s scatter because gather-P undoes
121/// scatter-P for any permutation P: gather-P(scatter-P(v))[i] = v[i].
122/// (Proof: gather-P(scatter-P(v))[i] = scatter-P(v)[P(i)] = v[P⁻¹(P(i))] = v[i].)
123#[inline]
124fn inv_p_layer(state: u64) -> u64 {
125    let mut out = 0u64;
126    let mut bit = 0usize;
127    while bit < 63 {
128        let src = (16 * bit) % 63;
129        out |= ((state >> src) & 1) << bit;
130        bit += 1;
131    }
132    out |= ((state >> 63) & 1) << 63;
133    out
134}
135
136fn present_encrypt(state: u64, round_keys: &[u64; 32]) -> u64 {
137    let mut s = state;
138    let mut round = 0usize;
139    while round < 31 {
140        s ^= round_keys[round];
141        s = sbox_layer(s);
142        s = p_layer(s);
143        round += 1;
144    }
145    s ^ round_keys[31]
146}
147
148fn present_encrypt_ct(state: u64, round_keys: &[u64; 32]) -> u64 {
149    let mut s = state;
150    let mut round = 0usize;
151    while round < 31 {
152        s ^= round_keys[round];
153        s = sbox_layer_ct(s);
154        s = p_layer(s);
155        round += 1;
156    }
157    s ^ round_keys[31]
158}
159
160fn present_decrypt(state: u64, round_keys: &[u64; 32]) -> u64 {
161    let mut s = state ^ round_keys[31];
162    let mut round = 31usize;
163    while round > 0 {
164        round -= 1;
165        s = inv_p_layer(s);
166        s = inv_sbox_layer(s);
167        s ^= round_keys[round];
168    }
169    s
170}
171
172fn present_decrypt_ct(state: u64, round_keys: &[u64; 32]) -> u64 {
173    let mut s = state ^ round_keys[31];
174    let mut round = 31usize;
175    while round > 0 {
176        round -= 1;
177        s = inv_p_layer(s);
178        s = inv_sbox_layer_ct(s);
179        s ^= round_keys[round];
180    }
181    s
182}
183
184fn expand_round_keys_80(key: &[u8; 10]) -> [u64; 32] {
185    let mut reg = 0u128;
186    for &b in key {
187        reg = (reg << 8) | u128::from(b);
188    }
189
190    let mask80 = (1u128 << 80) - 1;
191    let mut out = [0u64; 32];
192
193    for round in 1..=32u8 {
194        out[(round - 1) as usize] = ((reg >> 16) & 0xffff_ffff_ffff_ffff) as u64;
195        if round == 32 {
196            break;
197        }
198
199        reg = ((reg << 61) | (reg >> 19)) & mask80;
200        let top = ((reg >> 76) & 0x0f) as usize;
201        reg &= !(0x0fu128 << 76);
202        reg |= u128::from(SBOX[top]) << 76;
203        reg ^= u128::from(round) << 15;
204    }
205
206    out
207}
208
209fn expand_round_keys_128(key: &[u8; 16]) -> [u64; 32] {
210    let mut reg = u128::from_be_bytes(*key);
211    let mut out = [0u64; 32];
212
213    for round in 1..=32u8 {
214        out[(round - 1) as usize] = (reg >> 64) as u64;
215        if round == 32 {
216            break;
217        }
218
219        reg = reg.rotate_left(61);
220
221        let top = ((reg >> 124) & 0x0f) as usize;
222        reg &= !(0x0fu128 << 124);
223        reg |= u128::from(SBOX[top]) << 124;
224
225        let next = ((reg >> 120) & 0x0f) as usize;
226        reg &= !(0x0fu128 << 120);
227        reg |= u128::from(SBOX[next]) << 120;
228
229        reg ^= u128::from(round) << 62;
230    }
231
232    out
233}
234
235/// PRESENT-80 fast software path.
236pub struct Present80 {
237    round_keys: [u64; 32],
238}
239
240impl Present80 {
241    #[must_use]
242    pub fn new(key: &[u8; 10]) -> Self {
243        Self {
244            round_keys: expand_round_keys_80(key),
245        }
246    }
247
248    pub fn new_wiping(key: &mut [u8; 10]) -> Self {
249        let out = Self::new(key);
250        crate::ct::zeroize_slice(key.as_mut_slice());
251        out
252    }
253
254    #[must_use]
255    pub fn encrypt_block(&self, block: &[u8; 8]) -> [u8; 8] {
256        present_encrypt(u64::from_be_bytes(*block), &self.round_keys).to_be_bytes()
257    }
258
259    #[must_use]
260    pub fn decrypt_block(&self, block: &[u8; 8]) -> [u8; 8] {
261        present_decrypt(u64::from_be_bytes(*block), &self.round_keys).to_be_bytes()
262    }
263}
264
265/// PRESENT-80 constant-time software path.
266pub struct Present80Ct {
267    round_keys: [u64; 32],
268}
269
270impl Present80Ct {
271    #[must_use]
272    pub fn new(key: &[u8; 10]) -> Self {
273        Self {
274            round_keys: expand_round_keys_80(key),
275        }
276    }
277
278    pub fn new_wiping(key: &mut [u8; 10]) -> Self {
279        let out = Self::new(key);
280        crate::ct::zeroize_slice(key.as_mut_slice());
281        out
282    }
283
284    #[must_use]
285    pub fn encrypt_block(&self, block: &[u8; 8]) -> [u8; 8] {
286        present_encrypt_ct(u64::from_be_bytes(*block), &self.round_keys).to_be_bytes()
287    }
288
289    #[must_use]
290    pub fn decrypt_block(&self, block: &[u8; 8]) -> [u8; 8] {
291        present_decrypt_ct(u64::from_be_bytes(*block), &self.round_keys).to_be_bytes()
292    }
293}
294
295/// PRESENT-128 fast software path.
296pub struct Present128 {
297    round_keys: [u64; 32],
298}
299
300impl Present128 {
301    #[must_use]
302    pub fn new(key: &[u8; 16]) -> Self {
303        Self {
304            round_keys: expand_round_keys_128(key),
305        }
306    }
307
308    pub fn new_wiping(key: &mut [u8; 16]) -> Self {
309        let out = Self::new(key);
310        crate::ct::zeroize_slice(key.as_mut_slice());
311        out
312    }
313
314    #[must_use]
315    pub fn encrypt_block(&self, block: &[u8; 8]) -> [u8; 8] {
316        present_encrypt(u64::from_be_bytes(*block), &self.round_keys).to_be_bytes()
317    }
318
319    #[must_use]
320    pub fn decrypt_block(&self, block: &[u8; 8]) -> [u8; 8] {
321        present_decrypt(u64::from_be_bytes(*block), &self.round_keys).to_be_bytes()
322    }
323}
324
325/// PRESENT-128 constant-time software path.
326pub struct Present128Ct {
327    round_keys: [u64; 32],
328}
329
330impl Present128Ct {
331    #[must_use]
332    pub fn new(key: &[u8; 16]) -> Self {
333        Self {
334            round_keys: expand_round_keys_128(key),
335        }
336    }
337
338    pub fn new_wiping(key: &mut [u8; 16]) -> Self {
339        let out = Self::new(key);
340        crate::ct::zeroize_slice(key.as_mut_slice());
341        out
342    }
343
344    #[must_use]
345    pub fn encrypt_block(&self, block: &[u8; 8]) -> [u8; 8] {
346        present_encrypt_ct(u64::from_be_bytes(*block), &self.round_keys).to_be_bytes()
347    }
348
349    #[must_use]
350    pub fn decrypt_block(&self, block: &[u8; 8]) -> [u8; 8] {
351        present_decrypt_ct(u64::from_be_bytes(*block), &self.round_keys).to_be_bytes()
352    }
353}
354
355/// The original PRESENT instantiation from the CHES 2007 paper (80-bit key).
356pub type Present = Present80;
357/// Constant-time PRESENT-80 alias.
358pub type PresentCt = Present80Ct;
359
360macro_rules! impl_block_cipher {
361    ($name:ty) => {
362        impl crate::BlockCipher for $name {
363            const BLOCK_LEN: usize = 8;
364            fn encrypt(&self, block: &mut [u8]) {
365                let arr: &[u8; 8] = (&*block).try_into().expect("wrong block length");
366                block.copy_from_slice(&self.encrypt_block(arr));
367            }
368            fn decrypt(&self, block: &mut [u8]) {
369                let arr: &[u8; 8] = (&*block).try_into().expect("wrong block length");
370                block.copy_from_slice(&self.decrypt_block(arr));
371            }
372        }
373    };
374}
375
376impl_block_cipher!(Present80);
377impl_block_cipher!(Present80Ct);
378impl_block_cipher!(Present128);
379impl_block_cipher!(Present128Ct);
380
381macro_rules! impl_drop_zeroize {
382    ($name:ty) => {
383        impl Drop for $name {
384            fn drop(&mut self) {
385                crate::ct::zeroize_slice(self.round_keys.as_mut_slice());
386            }
387        }
388    };
389}
390
391impl_drop_zeroize!(Present80);
392impl_drop_zeroize!(Present80Ct);
393impl_drop_zeroize!(Present128);
394impl_drop_zeroize!(Present128Ct);
395
396#[cfg(test)]
397mod tests {
398    use super::*;
399
400    fn h8(s: &str) -> [u8; 8] {
401        let b: Vec<u8> = (0..s.len())
402            .step_by(2)
403            .map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap())
404            .collect();
405        b.try_into().unwrap()
406    }
407
408    fn h10(s: &str) -> [u8; 10] {
409        let b: Vec<u8> = (0..s.len())
410            .step_by(2)
411            .map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap())
412            .collect();
413        b.try_into().unwrap()
414    }
415
416    fn h16(s: &str) -> [u8; 16] {
417        let b: Vec<u8> = (0..s.len())
418            .step_by(2)
419            .map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap())
420            .collect();
421        b.try_into().unwrap()
422    }
423
424    #[test]
425    fn ct_sbox_matches_tables() {
426        for x in 0u8..16 {
427            assert_eq!(sbox_ct_nibble(x), SBOX[x as usize]);
428            assert_eq!(inv_sbox_ct_nibble(x), INV_SBOX[x as usize]);
429        }
430    }
431
432    #[test]
433    fn present80_kats() {
434        // CHES 2007 Appendix I.
435        let cases = [
436            (
437                h10("00000000000000000000"),
438                h8("0000000000000000"),
439                h8("5579c1387b228445"),
440            ),
441            (
442                h10("ffffffffffffffffffff"),
443                h8("0000000000000000"),
444                h8("e72c46c0f5945049"),
445            ),
446            (
447                h10("00000000000000000000"),
448                h8("ffffffffffffffff"),
449                h8("a112ffc72f68417b"),
450            ),
451            (
452                h10("ffffffffffffffffffff"),
453                h8("ffffffffffffffff"),
454                h8("3333dcd3213210d2"),
455            ),
456        ];
457
458        for (key, pt, ct) in cases {
459            let cipher = Present80::new(&key);
460            assert_eq!(cipher.encrypt_block(&pt), ct);
461            assert_eq!(cipher.decrypt_block(&ct), pt);
462        }
463    }
464
465    #[test]
466    fn present80_ct_kats() {
467        let cases = [
468            (
469                h10("00000000000000000000"),
470                h8("0000000000000000"),
471                h8("5579c1387b228445"),
472            ),
473            (
474                h10("ffffffffffffffffffff"),
475                h8("0000000000000000"),
476                h8("e72c46c0f5945049"),
477            ),
478            (
479                h10("00000000000000000000"),
480                h8("ffffffffffffffff"),
481                h8("a112ffc72f68417b"),
482            ),
483            (
484                h10("ffffffffffffffffffff"),
485                h8("ffffffffffffffff"),
486                h8("3333dcd3213210d2"),
487            ),
488        ];
489
490        for (key, pt, ct) in cases {
491            let cipher = Present80Ct::new(&key);
492            assert_eq!(cipher.encrypt_block(&pt), ct);
493            assert_eq!(cipher.decrypt_block(&ct), pt);
494        }
495    }
496
497    #[test]
498    fn present128_kats() {
499        // Commonly cited four-corner vectors for the 128-bit schedule.
500        let cases = [
501            (
502                h16("00000000000000000000000000000000"),
503                h8("0000000000000000"),
504                h8("96db702a2e6900af"),
505            ),
506            (
507                h16("ffffffffffffffffffffffffffffffff"),
508                h8("0000000000000000"),
509                h8("13238c710272a5d8"),
510            ),
511            (
512                h16("00000000000000000000000000000000"),
513                h8("ffffffffffffffff"),
514                h8("3c6019e5e5edd563"),
515            ),
516            (
517                h16("ffffffffffffffffffffffffffffffff"),
518                h8("ffffffffffffffff"),
519                h8("628d9fbd4218e5b4"),
520            ),
521        ];
522
523        for (key, pt, ct) in cases {
524            let cipher = Present128::new(&key);
525            assert_eq!(cipher.encrypt_block(&pt), ct);
526            assert_eq!(cipher.decrypt_block(&ct), pt);
527        }
528    }
529
530    #[test]
531    fn present128_ct_kats() {
532        let cases = [
533            (
534                h16("00000000000000000000000000000000"),
535                h8("0000000000000000"),
536                h8("96db702a2e6900af"),
537            ),
538            (
539                h16("ffffffffffffffffffffffffffffffff"),
540                h8("0000000000000000"),
541                h8("13238c710272a5d8"),
542            ),
543            (
544                h16("00000000000000000000000000000000"),
545                h8("ffffffffffffffff"),
546                h8("3c6019e5e5edd563"),
547            ),
548            (
549                h16("ffffffffffffffffffffffffffffffff"),
550                h8("ffffffffffffffff"),
551                h8("628d9fbd4218e5b4"),
552            ),
553        ];
554
555        for (key, pt, ct) in cases {
556            let cipher = Present128Ct::new(&key);
557            assert_eq!(cipher.encrypt_block(&pt), ct);
558            assert_eq!(cipher.decrypt_block(&ct), pt);
559        }
560    }
561}