visual_cryptography/
matrix.rs

1//! Matrix generation for visual cryptography schemes
2
3use crate::error::{Result, VCError};
4use nalgebra::DMatrix;
5use rand::Rng;
6
7/// Type alias for sharing matrices
8pub type ShareMatrix = DMatrix<u8>;
9
10/// Elementary matrices used in visual cryptography
11#[derive(Debug, Clone)]
12pub struct ElementaryMatrices {
13    pub c0: ShareMatrix,
14    pub c1: ShareMatrix,
15    pub c2: ShareMatrix,
16    pub c3: ShareMatrix,
17}
18
19/// Dispatching matrices for different pixel combinations
20#[derive(Debug, Clone)]
21pub struct DispatchingMatrices {
22    pub m0: ShareMatrix, // (white secret, white cover)
23    pub m1: ShareMatrix, // (white secret, black cover)
24    pub m2: ShareMatrix, // (black secret, white cover)
25    pub m3: ShareMatrix, // (black secret, black cover)
26}
27
28/// XOR-based matrices for better contrast
29#[derive(Debug, Clone)]
30pub struct XorMatrices {
31    pub white_pixel: ShareMatrix,
32    pub black_pixel: ShareMatrix,
33}
34
35/// Color mixing matrices for EVCT(3,3) scheme
36#[derive(Debug, Clone)]
37pub struct ColorMixingMatrices {
38    pub basis_matrices: Vec<ShareMatrix>,
39    pub complement_matrices: Vec<ShareMatrix>,
40}
41
42/// Generate elementary matrices for n participants
43pub fn generate_elementary_matrices(n: usize) -> ElementaryMatrices {
44    // C0: First row all 1s, rest all 0s
45    let mut c0 = DMatrix::zeros(n, n);
46    for j in 0..n {
47        c0[(0, j)] = 1;
48    }
49
50    // C1: Identity matrix
51    let c1 = DMatrix::identity(n, n);
52
53    // C2: Similar to C1 but with grouped columns for progressive schemes
54    let c2 = c1.clone();
55
56    // C3: All 1s
57    let c3 = DMatrix::from_element(n, n, 1);
58
59    ElementaryMatrices { c0, c1, c2, c3 }
60}
61
62/// Generate proper (k,n) threshold sharing matrices based on visual cryptography theory
63pub fn generate_proper_sharing_matrices(k: usize, n: usize) -> Result<(ShareMatrix, ShareMatrix)> {
64    if k > n {
65        return Err(VCError::InvalidConfiguration(
66            "k cannot be greater than n".to_string(),
67        ));
68    }
69
70    // Calculate the number of columns needed for proper contrast
71    let m = binomial_coefficient(n - 1, k - 1);
72
73    // Generate S0 (white pixel matrix)
74    let mut s0 = DMatrix::zeros(n, m);
75    let mut col = 0;
76
77    // Generate all combinations of k-1 participants out of n-1 (excluding first participant)
78    let combinations = generate_combinations(n - 1, k - 1);
79
80    for combo in combinations {
81        // First participant always gets 1
82        s0[(0, col)] = 1;
83
84        // Selected participants get 1
85        for &participant in &combo {
86            s0[(participant + 1, col)] = 1;
87        }
88        col += 1;
89    }
90
91    // Generate S1 (black pixel matrix) - complement of S0
92    let mut s1 = DMatrix::zeros(n, m);
93    for row in 0..n {
94        for col in 0..m {
95            s1[(row, col)] = 1 - s0[(row, col)];
96        }
97    }
98
99    Ok((s0, s1))
100}
101
102/// Generate XOR-based matrices for better contrast
103pub fn generate_xor_matrices(n: usize) -> Result<XorMatrices> {
104    let m = 2_usize.pow((n - 1) as u32); // 2^(n-1) columns
105
106    let mut white_matrix = DMatrix::zeros(n, m);
107    let mut black_matrix = DMatrix::zeros(n, m);
108
109    // Generate all possible bit patterns for n-1 participants
110    for col in 0..m {
111        // First participant gets random bit
112        let first_bit = rand::thread_rng().gen_range(0..2);
113        white_matrix[(0, col)] = first_bit;
114        black_matrix[(0, col)] = first_bit;
115
116        let mut xor_sum = first_bit;
117
118        // Other participants get bits from the column index
119        for row in 1..n {
120            let bit = (col >> (row - 1)) & 1;
121            white_matrix[(row, col)] = bit as u8;
122            black_matrix[(row, col)] = bit as u8;
123            xor_sum ^= bit as u8;
124        }
125
126        // Adjust last participant to ensure proper XOR properties
127        if n > 1 {
128            // For white pixels: XOR should result in 0 (even parity)
129            if xor_sum == 1 {
130                white_matrix[(n - 1, col)] = 1 - white_matrix[(n - 1, col)];
131            }
132
133            // For black pixels: XOR should result in 1 (odd parity)
134            if xor_sum == 0 {
135                black_matrix[(n - 1, col)] = 1 - black_matrix[(n - 1, col)];
136            }
137        }
138    }
139
140    Ok(XorMatrices {
141        white_pixel: white_matrix,
142        black_pixel: black_matrix,
143    })
144}
145
146/// Generate color mixing matrices for EVCT(3,3) Dhiman-Kasana algorithm
147pub fn generate_color_mixing_matrices() -> ColorMixingMatrices {
148    // For EVCT(3,3), we need 8 basis matrices (2^3)
149    let mut basis_matrices = Vec::new();
150    let mut complement_matrices = Vec::new();
151
152    // Generate all 8 possible combinations for 3 participants
153    for i in 0..8 {
154        let mut matrix = DMatrix::zeros(3, 8);
155        let mut complement = DMatrix::zeros(3, 8);
156
157        for col in 0..8 {
158            for row in 0..3 {
159                // Generate pattern based on bit positions
160                let bit = (col >> row) & 1;
161                matrix[(row, col)] = bit as u8;
162                complement[(row, col)] = 1 - (bit as u8);
163            }
164        }
165
166        basis_matrices.push(matrix);
167        complement_matrices.push(complement);
168    }
169
170    ColorMixingMatrices {
171        basis_matrices,
172        complement_matrices,
173    }
174}
175
176/// Generate dispatching matrices for meaningful shares
177pub fn generate_dispatching_matrices(n: usize, i: usize) -> Result<DispatchingMatrices> {
178    if i < 2 || i > n {
179        return Err(VCError::InvalidConfiguration(format!(
180            "i must be between 2 and {}, got {}",
181            n, i
182        )));
183    }
184
185    let elem = generate_elementary_matrices(n);
186    let total_rows = i + n;
187
188    // Generate C2' and C3' for the upper part
189    let c2_prime = generate_c2_prime(i, n);
190    let c3_prime = DMatrix::from_element(i, n, 1);
191
192    // M0: (white, white) = [C2'; C0]
193    let mut m0 = DMatrix::zeros(total_rows, n);
194    for row in 0..i {
195        for col in 0..n {
196            m0[(row, col)] = c2_prime[(row, col)];
197        }
198    }
199    for row in 0..n {
200        for col in 0..n {
201            m0[(i + row, col)] = elem.c0[(row, col)];
202        }
203    }
204
205    // M1: (white, black) = [C3'; C0]
206    let mut m1 = DMatrix::zeros(total_rows, n);
207    for row in 0..i {
208        for col in 0..n {
209            m1[(row, col)] = c3_prime[(row, col)];
210        }
211    }
212    for row in 0..n {
213        for col in 0..n {
214            m1[(i + row, col)] = elem.c0[(row, col)];
215        }
216    }
217
218    // M2: (black, white) = [C2'; C1]
219    let mut m2 = DMatrix::zeros(total_rows, n);
220    for row in 0..i {
221        for col in 0..n {
222            m2[(row, col)] = c2_prime[(row, col)];
223        }
224    }
225    for row in 0..n {
226        for col in 0..n {
227            m2[(i + row, col)] = elem.c1[(row, col)];
228        }
229    }
230
231    // M3: (black, black) = [C3'; C1]
232    let mut m3 = DMatrix::zeros(total_rows, n);
233    for row in 0..i {
234        for col in 0..n {
235            m3[(row, col)] = c3_prime[(row, col)];
236        }
237    }
238    for row in 0..n {
239        for col in 0..n {
240            m3[(i + row, col)] = elem.c1[(row, col)];
241        }
242    }
243
244    Ok(DispatchingMatrices { m0, m1, m2, m3 })
245}
246
247/// Generate C2' matrix with grouped columns
248fn generate_c2_prime(i: usize, n: usize) -> ShareMatrix {
249    let mut matrix = DMatrix::zeros(i, n);
250    let group_size = n / i;
251    let remainder = n % i;
252
253    let mut col = 0;
254    for row in 0..i {
255        let current_group_size = if row < remainder {
256            group_size + 1
257        } else {
258            group_size
259        };
260
261        for _ in 0..current_group_size {
262            if col < n {
263                matrix[(row, col)] = 1;
264                col += 1;
265            }
266        }
267    }
268
269    matrix
270}
271
272/// Generate basic sharing matrices for (k,n) threshold scheme
273pub fn generate_basic_matrices(k: usize, n: usize, block_size: usize) -> Result<Vec<ShareMatrix>> {
274    if k > n {
275        return Err(VCError::InvalidConfiguration(
276            "k cannot be greater than n".to_string(),
277        ));
278    }
279
280    // Use proper sharing matrices if available
281    if block_size == 1 && k <= n {
282        let (s0, s1) = generate_proper_sharing_matrices(k, n)?;
283        return Ok(vec![s0, s1]);
284    }
285
286    let mut matrices = Vec::new();
287    let matrix_size = block_size * block_size;
288
289    // Generate matrices for white pixels
290    let white_matrix = generate_white_pixel_matrix(k, n, matrix_size);
291    matrices.push(white_matrix);
292
293    // Generate matrices for black pixels
294    let black_matrix = generate_black_pixel_matrix(k, n, matrix_size);
295    matrices.push(black_matrix);
296
297    Ok(matrices)
298}
299
300/// Generate matrix for white pixels in basic scheme
301fn generate_white_pixel_matrix(k: usize, n: usize, size: usize) -> ShareMatrix {
302    let mut matrix = DMatrix::zeros(n, size);
303    let mut rng = rand::thread_rng();
304
305    // For white pixels, ensure that any k shares will have some white subpixels
306    for col in 0..size {
307        // Randomly select k-1 shares to have black subpixels
308        let mut indices: Vec<usize> = (0..n).collect();
309        indices.sort_by_key(|_| rng.gen::<u32>());
310
311        let k_minus_1 = k.saturating_sub(1);
312        for i in 0..k_minus_1 {
313            let row_idx = indices[i];
314            matrix[(row_idx, col)] = 1;
315        }
316    }
317
318    matrix
319}
320
321/// Generate matrix for black pixels in basic scheme
322fn generate_black_pixel_matrix(k: usize, n: usize, size: usize) -> ShareMatrix {
323    let mut matrix = DMatrix::zeros(n, size);
324
325    // For black pixels, ensure that any k shares will have all black subpixels
326    for row in 0..n {
327        for col in 0..size {
328            matrix[(row, col)] = 1;
329        }
330    }
331
332    matrix
333}
334
335/// Select a row from a dispatching matrix based on pixel values
336pub fn select_dispatching_row(
337    matrices: &DispatchingMatrices,
338    secret_pixel: bool,
339    cover_pixel: bool,
340) -> Vec<u8> {
341    let matrix = match (secret_pixel, cover_pixel) {
342        (false, false) => &matrices.m0,
343        (false, true) => &matrices.m1,
344        (true, false) => &matrices.m2,
345        (true, true) => &matrices.m3,
346    };
347
348    let mut rng = rand::thread_rng();
349    let row_index = rng.gen_range(0..matrix.nrows());
350
351    matrix.row(row_index).iter().cloned().collect()
352}
353
354/// Calculate binomial coefficient C(n, k)
355fn binomial_coefficient(n: usize, k: usize) -> usize {
356    if k > n {
357        return 0;
358    }
359    if k == 0 || k == n {
360        return 1;
361    }
362
363    let k = if k > n - k { n - k } else { k };
364    let mut result = 1;
365
366    for i in 0..k {
367        result = result * (n - i) / (i + 1);
368    }
369
370    result
371}
372
373/// Generate all combinations of k elements from n elements
374fn generate_combinations(n: usize, k: usize) -> Vec<Vec<usize>> {
375    let mut combinations = Vec::new();
376    let mut current = Vec::new();
377    generate_combinations_recursive(0, n, k, &mut current, &mut combinations);
378    combinations
379}
380
381fn generate_combinations_recursive(
382    start: usize,
383    n: usize,
384    k: usize,
385    current: &mut Vec<usize>,
386    combinations: &mut Vec<Vec<usize>>,
387) {
388    if current.len() == k {
389        combinations.push(current.clone());
390        return;
391    }
392
393    for i in start..n {
394        current.push(i);
395        generate_combinations_recursive(i + 1, n, k, current, combinations);
396        current.pop();
397    }
398}
399
400#[cfg(test)]
401mod tests {
402    use super::*;
403
404    #[test]
405    fn test_elementary_matrices() {
406        let elem = generate_elementary_matrices(4);
407
408        // Test C0: first row should be all 1s
409        let first_row_sum: u8 = elem.c0.row(0).iter().sum();
410        assert_eq!(first_row_sum, 4);
411
412        // Check that other rows are all zeros
413        let mut other_rows_sum = 0u8;
414        for row in 1..4 {
415            other_rows_sum += elem.c0.row(row).iter().sum::<u8>();
416        }
417        assert_eq!(other_rows_sum, 0);
418
419        // Test C1: should be identity
420        assert_eq!(elem.c1, DMatrix::identity(4, 4));
421
422        // Test C3: should be all 1s
423        let c3_sum: u8 = elem.c3.iter().sum();
424        assert_eq!(c3_sum, 16);
425    }
426
427    #[test]
428    fn test_dispatching_matrices() {
429        let matrices = generate_dispatching_matrices(4, 2).unwrap();
430
431        // Check dimensions
432        assert_eq!(matrices.m0.nrows(), 6); // i + n = 2 + 4
433        assert_eq!(matrices.m0.ncols(), 4);
434    }
435
436    #[test]
437    fn test_proper_sharing_matrices() {
438        let (s0, s1) = generate_proper_sharing_matrices(2, 3).unwrap();
439
440        // Check dimensions
441        assert_eq!(s0.nrows(), 3);
442        assert_eq!(s1.nrows(), 3);
443
444        // S1 should be complement of S0
445        for row in 0..s0.nrows() {
446            for col in 0..s0.ncols() {
447                assert_eq!(s0[(row, col)] + s1[(row, col)], 1);
448            }
449        }
450    }
451
452    #[test]
453    fn test_xor_matrices() {
454        let xor_matrices = generate_xor_matrices(3).unwrap();
455
456        // Check dimensions - should be 3x4 (n x 2^(n-1))
457        assert_eq!(xor_matrices.white_pixel.nrows(), 3);
458        assert_eq!(xor_matrices.white_pixel.ncols(), 4);
459    }
460
461    #[test]
462    fn test_binomial_coefficient() {
463        assert_eq!(binomial_coefficient(5, 2), 10);
464        assert_eq!(binomial_coefficient(4, 0), 1);
465        assert_eq!(binomial_coefficient(4, 4), 1);
466        assert_eq!(binomial_coefficient(6, 3), 20);
467    }
468}