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