permutation_iterator/
lib.rs

1// Copyright 2019, Asim Ihsan
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
4// in compliance with the License. You may obtain a copy of the License at
5//
6//     http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software distributed under the License
9// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
10// or implied. See the License for the specific language governing permissions and limitations under
11// the License.
12
13//! Utilities for iterating over random permutations.
14//!
15//! `permutation-iterator` provides utilities for iterating over random permutations in constant
16//! space.
17//!
18//! # Quick Start
19//!
20//! Please check the GitHub repository's `README.md` and `examples` folder for how to get started
21//! with this library.
22
23use blake2_rfc::blake2b::Blake2b;
24use rand::Rng;
25
26/// Permutor gives you back a permutation iterator that returns a random permutation over
27/// [0, max) (0 inclusive to max exclusive).
28///
29/// # Examples
30///
31/// Permutor can be used to iterate over a random permutation of integers [0..max) (0 inclusive to
32/// max exclusive):
33///
34/// ```
35/// use crate::permutation_iterator::Permutor;
36/// use std::collections::HashSet;
37///
38/// let max: u64 = 10;
39/// let permutor = Permutor::new(max);
40/// for value in permutor {
41///     println!("{}", value);
42/// }
43/// ```
44pub struct Permutor {
45    feistel: FeistelNetwork,
46    max: u64,
47    current: u64,
48    values_returned: u64,
49}
50
51impl Permutor {
52    pub fn new_with_u64_key(max: u64, key: u64) -> Permutor {
53        let key = u64_to_32slice(key);
54        Permutor {
55            feistel: FeistelNetwork::new_with_slice_key(max, key),
56            max,
57            current: 0,
58            values_returned: 0,
59        }
60    }
61
62    pub fn new_with_slice_key(max: u64, key: [u8; 32]) -> Permutor {
63        Permutor {
64            feistel: FeistelNetwork::new_with_slice_key(max, key),
65            max,
66            current: 0,
67            values_returned: 0,
68        }
69    }
70
71    pub fn new(max: u64) -> Permutor {
72        Permutor {
73            feistel: FeistelNetwork::new(max),
74            max,
75            current: 0,
76            values_returned: 0,
77        }
78    }
79}
80
81impl Iterator for Permutor {
82    type Item = u64;
83
84    fn next(&mut self) -> Option<Self::Item> {
85        while self.values_returned < self.max {
86            let next = self.feistel.permute(self.current);
87            self.current += 1;
88            if next >= self.max {
89                continue;
90            }
91            self.values_returned += 1;
92            return Some(next);
93        }
94        None
95    }
96}
97
98/// Iterate over a random permutation of a pair of integer sequences.
99///
100/// # Examples
101///
102/// Suppose you have two lists, first with 3. elements and the second with 7 elements,
103/// and you want to iterate over a random permutation of pairs:
104///
105/// ```
106/// use permutation_iterator::RandomPairPermutor;
107///
108/// let pair_permutor = RandomPairPermutor::new(3, 7);
109/// for (i, j) in pair_permutor {
110///     println!("({}, {})", i, j);
111/// }
112/// ```
113///
114pub struct RandomPairPermutor {
115    permutor: Permutor,
116    max2: u32,
117}
118
119impl RandomPairPermutor {
120    pub fn new(max1: u32, max2: u32) -> RandomPairPermutor {
121        let max: u64 = (max1 as u64) * (max2 as u64);
122        RandomPairPermutor {
123            permutor: Permutor::new(max),
124            max2,
125        }
126    }
127}
128
129impl Iterator for RandomPairPermutor {
130    type Item = (u32, u32);
131
132    fn next(&mut self) -> Option<Self::Item> {
133        match self.permutor.next() {
134            Some(value) => {
135                let first = value as u32 / self.max2;
136                let second = value as u32 % self.max2;
137                Some((first, second))
138            }
139            _ => None,
140        }
141    }
142}
143
144/// Implements a Feistel network, which can take a non-invertible pseudo-random function (PRF)
145/// and turn it into an invertible pseudo-random permutation (PRP).
146///
147/// If you use this struct directly note that its intended purpose is to be a PRP and map from
148/// an n-bit input to an n-bit output, where n is an even positive integer. For example, if
149/// constructed with a `max` of `10`, internally it creates a 4-bit Feistel network, and for all
150/// integers in the 4-bit domain `[0, 16)` (`0` inclusive to `16` exclusive) it will map an input
151/// to one and only one output, and vice-versa (a given output maps to one and only one input).
152/// Even though you specified a max value of `10`, the output range may be larger than expected.
153/// Clients like `RandomPermutor` handle this by excluding output values outside of the desired
154/// range.
155///
156/// This is useful in fields like cryptography, where a block cipher is a PRP.
157///
158/// Another great use of a Feistel network is when you want some input to always map to one and only
159/// one output (and vice versa). For example, given a 32-bit IP address, we could use some secret
160/// key and map each IP address to some other 32-bit IP address. We could log this new 32-bit
161/// IP address and people who do not know what the secret key is would find it difficult
162/// to determine what the input IP address was. This is Format Preserving Encryption (FPE).
163pub struct FeistelNetwork {
164    /// TODO visible just for testing, fix
165    pub half_width: u64,
166
167    /// Mask used to keep within the width for the right.
168    /// TODO visible just for testing, fix
169    pub right_mask: u64,
170
171    /// Mask used to keep within the width for the left.
172    /// TODO visible just for testing, fix
173    pub left_mask: u64,
174
175    /// Private key, some random seed. 256 bits as 32 bytes.
176    key: [u8; 32],
177
178    rounds: u8,
179}
180
181impl FeistelNetwork {
182    /// Create a new FeistelNetwork instance that can give you a random permutation of
183    /// integers.
184    ///
185    /// Note that the value of max is rounded up to the nearest even power of 2. If clients are
186    /// trying to get a permutation of [0, max) they need to iterate over the input range and
187    /// discard values from FeistelNetwork >= max.
188    ///
189    /// The key used for the permutation is made up of securely gathered 32 bytes.
190    pub fn new(max: u64) -> FeistelNetwork {
191        let key = rand::thread_rng().gen::<[u8; 32]>();
192        FeistelNetwork::new_with_slice_key(max, key)
193    }
194
195    /// Create a new FeistelNetwork instance that can give you a random permutation of
196    /// integers.
197    ///
198    /// Note that the value of max is rounded up to the nearest even power of 2. If clients are
199    /// trying to get a permutation of [0, max) they need to iterate over the input range and
200    /// discard values from FeistelNetwork >= max.
201    pub fn new_with_slice_key(max_value: u64, key: [u8; 32]) -> FeistelNetwork {
202        let width = (max_value as f64).log2();
203        let mut width = width.ceil() as u64;
204        if width % 2 != 0 {
205            width += 1;
206        }
207        let half_width = width / 2;
208        let mut right_mask = 0;
209        for i in 0..half_width {
210            right_mask |= 1 << i;
211        }
212        let left_mask = right_mask << half_width;
213        FeistelNetwork {
214            half_width,
215            right_mask,
216            left_mask,
217            key,
218            rounds: 12,
219        }
220    }
221
222    pub fn permute(&self, input: u64) -> u64 {
223        let mut left = (input & self.left_mask) >> self.half_width;
224        let mut right = input & self.right_mask;
225
226        for i in 0..self.rounds as u8 {
227            let new_left = right;
228            let f = self.round_function(right, i, &self.key[..], self.right_mask);
229            right = left ^ f;
230            left = new_left;
231        }
232
233        let result = (left << self.half_width) | right;
234        result & (self.left_mask | self.right_mask)
235    }
236
237    fn round_function(&self, right: u64, round: u8, key: &[u8], mask: u64) -> u64 {
238        let right_bytes = u64_to_8slice(right);
239        let round_bytes = u8_to_1slice(round);
240        let mut context: Blake2b = Blake2b::with_key(8, key);
241        context.update(&right_bytes[..]);
242        context.update(&round_bytes[..]);
243        let hash = context.finalize();
244        let hash_bytes: &[u8] = hash.as_bytes();
245        slice_to_u64(hash_bytes) & mask
246    }
247}
248
249fn slice_to_u64(input: &[u8]) -> u64 {
250    (input[7] as u64)
251        | ((input[6] as u64) << 8)
252        | ((input[5] as u64) << 16)
253        | ((input[4] as u64) << 24)
254        | ((input[3] as u64) << 32)
255        | ((input[2] as u64) << 40)
256        | ((input[1] as u64) << 48)
257        | ((input[0] as u64) << 56)
258}
259
260fn u8_to_1slice(input: u8) -> [u8; 1] {
261    let mut result: [u8; 1] = [0; 1];
262    result[0] = input;
263    result
264}
265
266/// Convert an unsigned 64 bit number so a slice of 8 bytes in big-endian format (most significant
267/// bit first).
268///
269/// # Examples
270///
271/// ```
272/// use crate::permutation_iterator::u64_to_8slice;
273/// let output = u64_to_8slice(42);
274/// assert_eq!(output, [0, 0, 0, 0, 0, 0, 0, 0x2A]);
275/// ```
276pub fn u64_to_8slice(input: u64) -> [u8; 8] {
277    let mut result: [u8; 8] = [0; 8];
278    result[7] = (input & 0xFF) as u8;
279    result[6] = ((input & 0xFF00) >> 8) as u8;
280    result[5] = ((input & 0x00FF_0000) >> 16) as u8;
281    result[4] = ((input & 0xFF00_0000) >> 24) as u8;
282    result[3] = ((input & 0x00FF_0000_0000) >> 32) as u8;
283    result[2] = ((input & 0xFF00_0000_0000) >> 40) as u8;
284    result[1] = ((input & 0x00FF_0000_0000_0000) >> 48) as u8;
285    result[0] = ((input & 0xFF00_0000_0000_0000) >> 56) as u8;
286    result
287}
288
289/// Convert an unsigned 64 bit number so a slice of 32 bytes in big-endian format (most significant
290/// bit first).
291///
292/// # Examples
293///
294/// ```
295/// use crate::permutation_iterator::u64_to_32slice;
296/// let output = u64_to_32slice(42);
297/// assert_eq!(output, [0, 0, 0, 0, 0, 0, 0, 0x2A,
298///                     0, 0, 0, 0, 0, 0, 0, 0,
299///                     0, 0, 0, 0, 0, 0, 0, 0,
300///                     0, 0, 0, 0, 0, 0, 0, 0]);
301/// ```
302pub fn u64_to_32slice(input: u64) -> [u8; 32] {
303    let result8 = u64_to_8slice(input);
304    let mut result: [u8; 32] = [0; 32];
305    result[..8].clone_from_slice(&result8[..8]);
306    result
307}