feistel_permutation_rs/
feistel.rs

1// SPDX-License-Identifier: Apache-2.0
2use std::hash::{BuildHasher, Hasher};
3
4
5/// A basic Feistel Network cipher.
6/// 
7/// The cipher requires a series of hashes which are built using
8/// a supplied [`std::hash::BuildHasher`] (passed with the parameter
9/// name `bob`, coz it'z a builder, right?)
10pub struct Feistel<B>
11where
12    B: BuildHasher,
13{
14    bob: B,
15    bits: usize,
16    keys: Vec<u64>,
17}
18
19impl<B> Feistel<B>
20where
21    B: BuildHasher,
22{
23    /// Construct a new Feistel cipher.
24    pub fn new(bob: B, bits: usize, keys: &[u64]) -> Feistel<B> {
25        // Insist that there are an even number of bits.
26        assert_eq!(bits & 1, 0);
27        Feistel {
28            bob,
29            bits,
30            keys: Vec::from(keys),
31        }
32    }
33
34    /// Encrypt a value.
35    pub fn encrypt(&self, x: u64) -> u64 {
36        let (mut l, mut r) = self.split(x);
37        for k in self.keys.iter() {
38            l ^= self.hash(*k, r);
39            let t = l;
40            l = r;
41            r = t;
42        }
43        self.combine(r, l)
44    }
45
46    /// Decrypt a value.
47    pub fn decrypt(&self, x: u64) -> u64 {
48        let (mut l, mut r) = self.split(x);
49        for k in self.keys.iter().rev() {
50            l ^= self.hash(*k, r);
51            let t = l;
52            l = r;
53            r = t;
54        }
55        self.combine(r, l)
56    }
57
58    fn split(&self, x: u64) -> (u64, u64) {
59        let n = self.bits >> 1;
60        let m = (1u64 << n) - 1;
61        let hi = x >> n;
62        let lo = x & m;
63        (hi, lo)
64    }
65
66    fn combine(&self, hi: u64, lo: u64) -> u64 {
67        let n = self.bits >> 1;
68        (hi << n) | lo
69    }
70
71    fn hash(&self, k: u64, x: u64) -> u64 {
72        let mut h: <B as BuildHasher>::Hasher = self.bob.build_hasher();
73        h.write_u64(k);
74        h.write_u64(x);
75        let res = h.finish();
76        let n = self.bits >> 1;
77        let m = (1u64 << n) - 1;
78        res & m
79    }
80}
81
82#[cfg(test)]
83mod tests {
84    use crate::DefaultBuildHasher;
85
86    use super::*;
87
88    #[test]
89    fn test_1a() {
90        let bob = DefaultBuildHasher::new();
91        let bits = 32;
92        let keys = [0x1c10u64, 0x8fd6u64, 0x2d5au64, 0x7363u64, 0x5f70u64];
93        let f = Feistel::new(bob, bits, &keys);
94        let x = 17;
95        let y = f.encrypt(x);
96        let z = f.decrypt(y);
97        println!("x=0x{x:0x}, y=0x{y:0x}, z=0x{z:0x}");
98        assert_eq!(x, z);
99    }
100
101    #[test]
102    fn test_1b() {
103        let bob = DefaultBuildHasher::new();
104        let bits = 32;
105        let keys = [0x1c10u64, 0x8fd6u64, 0x2d5au64, 0x7363u64, 0x5f70u64];
106        let f = Feistel::new(bob, bits, &keys);
107        let x = 234;
108        let y = f.encrypt(x);
109        let z = f.decrypt(y);
110        assert_eq!(x, z);
111    }
112
113    #[test]
114    fn test_2() {
115        let bob = DefaultBuildHasher::new();
116        let bits = 56;
117        let keys = [0x1c10u64, 0x8fd6u64, 0x2d5au64, 0x7363u64, 0x5f70u64];
118        let f = Feistel::new(bob, bits, &keys);
119        let x = 17;
120        let y = f.encrypt(x);
121        let z = f.decrypt(y);
122        assert_eq!(x, z);
123    }
124}