1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
use std::hash::{BuildHasher, Hasher};


/// A basic Feistel Network cipher.
/// 
/// The cipher requires a series of hashes which are built using
/// a supplied [`std::hash::BuildHasher`] (passed with the parameter
/// name `bob`, coz it'z a builder, right?)
pub struct Feistel<B>
where
    B: BuildHasher,
{
    bob: B,
    bits: usize,
    keys: Vec<u64>,
}

impl<B> Feistel<B>
where
    B: BuildHasher,
{
    /// Construct a new Feistel cipher.
    pub fn new(bob: B, bits: usize, keys: &[u64]) -> Feistel<B> {
        // Insist that there are an even number of bits.
        assert_eq!(bits & 1, 0);
        Feistel {
            bob,
            bits,
            keys: Vec::from(keys),
        }
    }

    /// Encrypt a value.
    pub fn encrypt(&self, x: u64) -> u64 {
        let (mut l, mut r) = self.split(x);
        for k in self.keys.iter() {
            l ^= self.hash(*k, r);
            let t = l;
            l = r;
            r = t;
        }
        self.combine(r, l)
    }

    /// Decrypt a value.
    pub fn decrypt(&self, x: u64) -> u64 {
        let (mut l, mut r) = self.split(x);
        for k in self.keys.iter().rev() {
            l ^= self.hash(*k, r);
            let t = l;
            l = r;
            r = t;
        }
        self.combine(r, l)
    }

    fn split(&self, x: u64) -> (u64, u64) {
        let n = self.bits >> 1;
        let m = (1u64 << n) - 1;
        let hi = x >> n;
        let lo = x & m;
        (hi, lo)
    }

    fn combine(&self, hi: u64, lo: u64) -> u64 {
        let n = self.bits >> 1;
        (hi << n) | lo
    }

    fn hash(&self, k: u64, x: u64) -> u64 {
        let mut h: <B as BuildHasher>::Hasher = self.bob.build_hasher();
        h.write_u64(k);
        h.write_u64(x);
        let res = h.finish();
        let n = self.bits >> 1;
        let m = (1u64 << n) - 1;
        res & m
    }
}

#[cfg(test)]
mod tests {
    use crate::DefaultBuildHasher;

    use super::*;

    #[test]
    fn test_1a() {
        let bob = DefaultBuildHasher::new();
        let bits = 32;
        let keys = [0x1c10u64, 0x8fd6u64, 0x2d5au64, 0x7363u64, 0x5f70u64];
        let f = Feistel::new(bob, bits, &keys);
        let x = 17;
        let y = f.encrypt(x);
        let z = f.decrypt(y);
        println!("x=0x{x:0x}, y=0x{y:0x}, z=0x{z:0x}");
        assert_eq!(x, z);
    }

    #[test]
    fn test_1b() {
        let bob = DefaultBuildHasher::new();
        let bits = 32;
        let keys = [0x1c10u64, 0x8fd6u64, 0x2d5au64, 0x7363u64, 0x5f70u64];
        let f = Feistel::new(bob, bits, &keys);
        let x = 234;
        let y = f.encrypt(x);
        let z = f.decrypt(y);
        assert_eq!(x, z);
    }

    #[test]
    fn test_2() {
        let bob = DefaultBuildHasher::new();
        let bits = 56;
        let keys = [0x1c10u64, 0x8fd6u64, 0x2d5au64, 0x7363u64, 0x5f70u64];
        let f = Feistel::new(bob, bits, &keys);
        let x = 17;
        let y = f.encrypt(x);
        let z = f.decrypt(y);
        assert_eq!(x, z);
    }
}