1use crate::{
4 error::{Result, VCError},
5 matrix::{
6 generate_basic_matrices, generate_color_mixing_matrices, generate_dispatching_matrices,
7 generate_proper_sharing_matrices, generate_xor_matrices, select_dispatching_row,
8 ColorMixingMatrices, XorMatrices,
9 },
10 share::{stack_shares, Share},
11 utils::{apply_halftone, convert_to_binary, expand_pixel},
12 VCConfig,
13};
14use image::{DynamicImage, ImageBuffer, Luma, Rgb};
15use rand::{seq::SliceRandom, Rng};
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum Algorithm {
20 BasicThreshold,
22 Progressive,
24 ExtendedMeaningful,
26 NaorShamir,
28 TaghaddosLatif,
30 DhimanKasana,
32 XorBased,
34}
35
36pub trait VCScheme {
38 fn encrypt(
40 &self,
41 image: &DynamicImage,
42 config: &VCConfig,
43 cover_images: Option<Vec<DynamicImage>>,
44 ) -> Result<Vec<Share>>;
45
46 fn decrypt(&self, shares: &[Share], config: &VCConfig) -> Result<DynamicImage>;
48}
49
50pub fn encrypt(
52 image: &DynamicImage,
53 config: &VCConfig,
54 cover_images: Option<Vec<DynamicImage>>,
55) -> Result<Vec<Share>> {
56 match config.algorithm {
57 Algorithm::BasicThreshold => basic_threshold_encrypt(image, config),
58 Algorithm::Progressive => progressive_encrypt(image, config, cover_images),
59 Algorithm::ExtendedMeaningful => extended_meaningful_encrypt(image, config, cover_images),
60 Algorithm::NaorShamir => naor_shamir_encrypt(image, config),
61 Algorithm::TaghaddosLatif => taghaddos_latif_encrypt(image, config),
62 Algorithm::DhimanKasana => dhiman_kasana_encrypt(image, config, cover_images),
63 Algorithm::XorBased => xor_based_encrypt(image, config),
64 }
65}
66
67pub fn decrypt(shares: &[Share], config: &VCConfig) -> Result<DynamicImage> {
69 match config.algorithm {
70 Algorithm::BasicThreshold => basic_threshold_decrypt(shares, config),
71 Algorithm::Progressive => progressive_decrypt(shares, config),
72 Algorithm::ExtendedMeaningful => extended_meaningful_decrypt(shares, config),
73 Algorithm::NaorShamir => naor_shamir_decrypt(shares, config),
74 Algorithm::TaghaddosLatif => taghaddos_latif_decrypt(shares, config),
75 Algorithm::DhimanKasana => dhiman_kasana_decrypt(shares, config),
76 Algorithm::XorBased => xor_based_decrypt(shares, config),
77 }
78}
79
80fn xor_based_encrypt(image: &DynamicImage, config: &VCConfig) -> Result<Vec<Share>> {
82 let binary = convert_to_binary(image);
83 let (width, height) = (binary.width(), binary.height());
84
85 let xor_matrices = generate_xor_matrices(config.num_shares)?;
87
88 let mut shares = Vec::new();
89 for i in 0..config.num_shares {
90 shares.push(ImageBuffer::new(width, height));
91 }
92
93 let mut rng = rand::thread_rng();
94
95 for y in 0..height {
97 for x in 0..width {
98 let pixel = binary.get_pixel(x, y)[0];
99 let is_black = pixel == 0;
100
101 let matrix = if is_black {
103 &xor_matrices.black_pixel
104 } else {
105 &xor_matrices.white_pixel
106 };
107
108 let col = rng.gen_range(0..matrix.ncols());
110
111 for share_idx in 0..config.num_shares {
113 let value = matrix[(share_idx, col)];
114 let pixel_value = if value == 1 { 0u8 } else { 255u8 };
115 shares[share_idx].put_pixel(x, y, Luma([pixel_value]));
116 }
117 }
118 }
119
120 let result: Vec<Share> = shares
122 .into_iter()
123 .enumerate()
124 .map(|(i, img)| {
125 Share::new(
126 DynamicImage::ImageLuma8(img),
127 i + 1,
128 config.num_shares,
129 width,
130 height,
131 1,
132 false,
133 )
134 })
135 .collect();
136
137 Ok(result)
138}
139
140fn xor_based_decrypt(shares: &[Share], _config: &VCConfig) -> Result<DynamicImage> {
142 if shares.is_empty() {
143 return Err(VCError::InsufficientShares {
144 required: 1,
145 provided: 0,
146 });
147 }
148
149 let (width, height) = shares[0].dimensions();
150 let mut result = ImageBuffer::new(width, height);
151
152 for y in 0..height {
153 for x in 0..width {
154 let mut xor_value = 0u8;
155
156 for share in shares {
157 if let DynamicImage::ImageLuma8(img) = &share.image {
158 let pixel = img.get_pixel(x, y)[0];
159 let bit = if pixel == 0 { 1 } else { 0 };
160 xor_value ^= bit;
161 }
162 }
163
164 let pixel_value = if xor_value == 1 { 0u8 } else { 255u8 };
166 result.put_pixel(x, y, Luma([pixel_value]));
167 }
168 }
169
170 Ok(DynamicImage::ImageLuma8(result))
171}
172
173fn basic_threshold_encrypt(image: &DynamicImage, config: &VCConfig) -> Result<Vec<Share>> {
175 let binary = convert_to_binary(image);
177 let (width, height) = (binary.width(), binary.height());
178
179 let matrices = generate_basic_matrices(config.threshold, config.num_shares, config.block_size)?;
181
182 let share_width = width * config.block_size as u32;
184 let share_height = height * config.block_size as u32;
185
186 let mut shares = Vec::new();
187 for i in 0..config.num_shares {
188 shares.push(ImageBuffer::new(share_width, share_height));
189 }
190
191 let mut rng = rand::thread_rng();
192
193 for y in 0..height {
195 for x in 0..width {
196 let pixel = binary.get_pixel(x, y)[0];
197 let matrix_idx = if pixel == 0 { 1 } else { 0 }; let matrix = &matrices[matrix_idx];
199
200 let col = rng.gen_range(0..matrix.ncols());
202
203 for share_idx in 0..config.num_shares {
205 let value = matrix[(share_idx, col)];
206 let block_value = if value == 1 { 0u8 } else { 255u8 };
207
208 let base_x = x * config.block_size as u32;
210 let base_y = y * config.block_size as u32;
211
212 for dy in 0..config.block_size as u32 {
213 for dx in 0..config.block_size as u32 {
214 shares[share_idx].put_pixel(base_x + dx, base_y + dy, Luma([block_value]));
215 }
216 }
217 }
218 }
219 }
220
221 let result: Vec<Share> = shares
223 .into_iter()
224 .enumerate()
225 .map(|(i, img)| {
226 Share::new(
227 DynamicImage::ImageLuma8(img),
228 i + 1,
229 config.num_shares,
230 width,
231 height,
232 config.block_size,
233 false,
234 )
235 })
236 .collect();
237
238 Ok(result)
239}
240
241fn basic_threshold_decrypt(shares: &[Share], _config: &VCConfig) -> Result<DynamicImage> {
243 if let Some(stacked) = stack_shares(shares) {
244 Ok(DynamicImage::ImageLuma8(stacked))
245 } else {
246 Err(VCError::DecryptionError(
247 "Failed to stack shares".to_string(),
248 ))
249 }
250}
251
252fn progressive_encrypt(
254 image: &DynamicImage,
255 config: &VCConfig,
256 cover_images: Option<Vec<DynamicImage>>,
257) -> Result<Vec<Share>> {
258 let binary = convert_to_binary(image);
260 let (width, height) = (binary.width(), binary.height());
261
262 if let Some(covers) = cover_images {
264 if covers.len() != config.num_shares {
265 return Err(VCError::CoverImageError(format!(
266 "Number of cover images ({}) must match number of shares ({})",
267 covers.len(),
268 config.num_shares
269 )));
270 }
271
272 let i = config.num_shares; let matrices = generate_dispatching_matrices(config.num_shares, i)?;
275
276 let mut shares = Vec::new();
277
278 for share_idx in 0..config.num_shares {
279 let mut share_img = ImageBuffer::new(width, height);
280 let cover_binary = convert_to_binary(&covers[share_idx]);
281
282 for y in 0..height {
283 for x in 0..width {
284 let secret_pixel = binary.get_pixel(x, y)[0] == 0; let cover_pixel = cover_binary.get_pixel(x, y)[0] == 0; let row = select_dispatching_row(&matrices, secret_pixel, cover_pixel);
288 let value = if row[share_idx] == 1 { 0u8 } else { 255u8 };
289
290 share_img.put_pixel(x, y, Luma([value]));
291 }
292 }
293
294 shares.push(Share::new(
295 DynamicImage::ImageLuma8(share_img),
296 share_idx + 1,
297 config.num_shares,
298 width,
299 height,
300 1, true, ));
303 }
304
305 Ok(shares)
306 } else {
307 progressive_matrix_based_encrypt(image, config)
309 }
310}
311
312fn progressive_matrix_based_encrypt(image: &DynamicImage, config: &VCConfig) -> Result<Vec<Share>> {
314 let binary = convert_to_binary(image);
315 let (width, height) = (binary.width(), binary.height());
316
317 let (white_matrix, black_matrix) = generate_proper_sharing_matrices(2, config.num_shares)?;
319
320 let mut shares = Vec::new();
321 for i in 0..config.num_shares {
322 shares.push(ImageBuffer::new(width, height));
323 }
324
325 let mut rng = rand::thread_rng();
326
327 for y in 0..height {
329 for x in 0..width {
330 let pixel = binary.get_pixel(x, y)[0];
331 let is_black = pixel == 0;
332
333 let matrix = if is_black {
335 &black_matrix
336 } else {
337 &white_matrix
338 };
339
340 let total_cols = matrix.ncols();
342 let mut col_weights = Vec::new();
343
344 for col in 0..total_cols {
345 let black_count = (0..config.num_shares)
347 .map(|row| matrix[(row, col)] as usize)
348 .sum::<usize>();
349
350 let weight = if is_black {
352 (config.num_shares - black_count) + 1
353 } else {
354 black_count + 1
355 };
356
357 col_weights.push(weight);
358 }
359
360 let total_weight: usize = col_weights.iter().sum();
362 let mut random_weight = rng.gen_range(0..total_weight);
363 let mut selected_col = 0;
364
365 for (col, &weight) in col_weights.iter().enumerate() {
366 if random_weight < weight {
367 selected_col = col;
368 break;
369 }
370 random_weight -= weight;
371 }
372
373 for share_idx in 0..config.num_shares {
375 let value = matrix[(share_idx, selected_col)];
376 let pixel_value = if value == 1 { 0u8 } else { 255u8 };
377 shares[share_idx].put_pixel(x, y, Luma([pixel_value]));
378 }
379 }
380 }
381
382 let result: Vec<Share> = shares
384 .into_iter()
385 .enumerate()
386 .map(|(i, img)| {
387 Share::new(
388 DynamicImage::ImageLuma8(img),
389 i + 1,
390 config.num_shares,
391 width,
392 height,
393 1,
394 false,
395 )
396 })
397 .collect();
398
399 Ok(result)
400}
401
402fn progressive_decrypt(shares: &[Share], _config: &VCConfig) -> Result<DynamicImage> {
404 basic_threshold_decrypt(shares, _config)
405}
406
407fn extended_meaningful_encrypt(
409 image: &DynamicImage,
410 config: &VCConfig,
411 cover_images: Option<Vec<DynamicImage>>,
412) -> Result<Vec<Share>> {
413 if cover_images.is_none() {
414 return Err(VCError::CoverImageError(
415 "Extended meaningful scheme requires cover images".to_string(),
416 ));
417 }
418
419 let binary = convert_to_binary(image);
421 let (width, height) = (binary.width(), binary.height());
422 let covers = cover_images.unwrap();
423
424 let matrices = generate_dispatching_matrices(config.num_shares, config.num_shares)?;
426
427 let mut shares = Vec::new();
428
429 for share_idx in 0..config.num_shares {
430 let mut share_img = ImageBuffer::new(width, height);
431 let cover_binary = convert_to_binary(&covers[share_idx]);
432
433 for y in 0..height {
434 for x in 0..width {
435 let secret_pixel = binary.get_pixel(x, y)[0] == 0;
436 let cover_pixel = cover_binary.get_pixel(x, y)[0] == 0;
437
438 let row = select_dispatching_row(&matrices, secret_pixel, cover_pixel);
440 let value = if row[share_idx] == 1 { 0u8 } else { 255u8 };
441
442 share_img.put_pixel(x, y, Luma([value]));
443 }
444 }
445
446 shares.push(Share::new(
447 DynamicImage::ImageLuma8(share_img),
448 share_idx + 1,
449 config.num_shares,
450 width,
451 height,
452 1,
453 true,
454 ));
455 }
456
457 Ok(shares)
458}
459
460fn extended_meaningful_decrypt(shares: &[Share], config: &VCConfig) -> Result<DynamicImage> {
462 basic_threshold_decrypt(shares, config)
463}
464
465fn naor_shamir_encrypt(image: &DynamicImage, config: &VCConfig) -> Result<Vec<Share>> {
467 if config.num_shares != 2 || config.threshold != 2 {
469 return Err(VCError::InvalidConfiguration(
470 "Original Naor-Shamir scheme requires exactly 2 shares with threshold 2".to_string(),
471 ));
472 }
473
474 let binary = convert_to_binary(image);
475 let (width, height) = (binary.width(), binary.height());
476
477 let white_matrix = vec![vec![1, 1, 0, 0], vec![1, 1, 0, 0]];
480 let black_matrix = vec![vec![1, 1, 0, 0], vec![0, 0, 1, 1]];
482
483 let share_width = width * 2;
484 let share_height = height * 2;
485
486 let mut share1 = ImageBuffer::new(share_width, share_height);
487 let mut share2 = ImageBuffer::new(share_width, share_height);
488
489 let mut rng = rand::thread_rng();
490
491 for y in 0..height {
492 for x in 0..width {
493 let pixel = binary.get_pixel(x, y)[0];
494
495 let matrix = if pixel == 0 {
497 &black_matrix
499 } else {
500 &white_matrix
502 };
503
504 let mut columns: Vec<usize> = (0..4).collect();
506 columns.as_mut_slice().shuffle(&mut rng);
507
508 let share1_pattern = vec![
510 matrix[0][columns[0]],
511 matrix[0][columns[1]],
512 matrix[0][columns[2]],
513 matrix[0][columns[3]],
514 ];
515 let share2_pattern = vec![
516 matrix[1][columns[0]],
517 matrix[1][columns[1]],
518 matrix[1][columns[2]],
519 matrix[1][columns[3]],
520 ];
521
522 share1.put_pixel(
525 x * 2,
526 y * 2,
527 Luma([if share1_pattern[0] == 1 { 0 } else { 255 }]),
528 );
529 share1.put_pixel(
530 x * 2 + 1,
531 y * 2,
532 Luma([if share1_pattern[1] == 1 { 0 } else { 255 }]),
533 );
534 share1.put_pixel(
535 x * 2,
536 y * 2 + 1,
537 Luma([if share1_pattern[2] == 1 { 0 } else { 255 }]),
538 );
539 share1.put_pixel(
540 x * 2 + 1,
541 y * 2 + 1,
542 Luma([if share1_pattern[3] == 1 { 0 } else { 255 }]),
543 );
544
545 share2.put_pixel(
547 x * 2,
548 y * 2,
549 Luma([if share2_pattern[0] == 1 { 0 } else { 255 }]),
550 );
551 share2.put_pixel(
552 x * 2 + 1,
553 y * 2,
554 Luma([if share2_pattern[1] == 1 { 0 } else { 255 }]),
555 );
556 share2.put_pixel(
557 x * 2,
558 y * 2 + 1,
559 Luma([if share2_pattern[2] == 1 { 0 } else { 255 }]),
560 );
561 share2.put_pixel(
562 x * 2 + 1,
563 y * 2 + 1,
564 Luma([if share2_pattern[3] == 1 { 0 } else { 255 }]),
565 );
566 }
567 }
568
569 Ok(vec![
570 Share::new(
571 DynamicImage::ImageLuma8(share1),
572 1,
573 2,
574 width,
575 height,
576 2,
577 false,
578 ),
579 Share::new(
580 DynamicImage::ImageLuma8(share2),
581 2,
582 2,
583 width,
584 height,
585 2,
586 false,
587 ),
588 ])
589}
590
591fn naor_shamir_decrypt(shares: &[Share], config: &VCConfig) -> Result<DynamicImage> {
593 basic_threshold_decrypt(shares, config)
594}
595
596fn taghaddos_latif_encrypt(image: &DynamicImage, config: &VCConfig) -> Result<Vec<Share>> {
598 if config.num_shares != 2 {
599 return Err(VCError::InvalidConfiguration(
600 "Taghaddos-Latif scheme requires exactly 2 shares".to_string(),
601 ));
602 }
603
604 let gray = image.to_luma8();
605 let (width, height) = (gray.width(), gray.height());
606
607 let patterns = [
609 [1u8, 1u8, 0u8, 0u8],
610 [1u8, 0u8, 1u8, 0u8],
611 [1u8, 0u8, 0u8, 1u8],
612 [0u8, 1u8, 1u8, 0u8],
613 [0u8, 1u8, 0u8, 1u8],
614 [0u8, 0u8, 1u8, 1u8],
615 ];
616
617 let share_width = width * 2;
619 let share_height = height * 2;
620
621 let mut share_a = ImageBuffer::new(share_width, share_height);
622 let mut share_b = ImageBuffer::new(share_width, share_height);
623
624 let mut rng = rand::thread_rng();
625
626 for y in 0..height {
628 for x in 0..width {
629 let pixel_value = gray.get_pixel(x, y)[0];
630 let mut share_a_colors = [0u8; 4];
631 let mut share_b_colors = [0u8; 4];
632
633 for bit_pos in 0..8 {
635 let bit = (pixel_value >> bit_pos) & 1;
636
637 let pattern = patterns[rng.gen_range(0..6)];
639
640 if bit == 1 {
641 for i in 0..4 {
643 share_a_colors[i] |= (pattern[i] << bit_pos);
644 share_b_colors[i] = share_a_colors[i];
645 }
646 } else {
647 for i in 0..4 {
649 share_a_colors[i] |= (pattern[i] << bit_pos);
650 share_b_colors[i] |= ((1 - pattern[i]) << bit_pos);
651 }
652 }
653 }
654
655 let base_x = x * 2;
657 let base_y = y * 2;
658
659 share_a.put_pixel(base_x, base_y, Luma([share_a_colors[0]]));
661 share_a.put_pixel(base_x + 1, base_y, Luma([share_a_colors[1]]));
662 share_a.put_pixel(base_x, base_y + 1, Luma([share_a_colors[2]]));
663 share_a.put_pixel(base_x + 1, base_y + 1, Luma([share_a_colors[3]]));
664
665 share_b.put_pixel(base_x, base_y, Luma([share_b_colors[0]]));
667 share_b.put_pixel(base_x + 1, base_y, Luma([share_b_colors[1]]));
668 share_b.put_pixel(base_x, base_y + 1, Luma([share_b_colors[2]]));
669 share_b.put_pixel(base_x + 1, base_y + 1, Luma([share_b_colors[3]]));
670 }
671 }
672
673 Ok(vec![
674 Share::new(
675 DynamicImage::ImageLuma8(share_a),
676 1,
677 2,
678 width,
679 height,
680 2, false,
682 ),
683 Share::new(
684 DynamicImage::ImageLuma8(share_b),
685 2,
686 2,
687 width,
688 height,
689 2, false,
691 ),
692 ])
693}
694
695fn taghaddos_latif_decrypt(shares: &[Share], _config: &VCConfig) -> Result<DynamicImage> {
697 if shares.len() < 2 {
698 return Err(VCError::InsufficientShares {
699 required: 2,
700 provided: shares.len(),
701 });
702 }
703
704 let (expanded_width, expanded_height) = shares[0].dimensions();
705
706 let width = expanded_width / 2;
708 let height = expanded_height / 2;
709
710 let mut result = ImageBuffer::new(width, height);
711
712 let share_a = if let DynamicImage::ImageLuma8(img) = &shares[0].image {
714 img
715 } else {
716 return Err(VCError::DecryptionError(
717 "Share A is not grayscale".to_string(),
718 ));
719 };
720
721 let share_b = if let DynamicImage::ImageLuma8(img) = &shares[1].image {
722 img
723 } else {
724 return Err(VCError::DecryptionError(
725 "Share B is not grayscale".to_string(),
726 ));
727 };
728
729 for y in 0..height {
731 for x in 0..width {
732 let base_x = x * 2;
733 let base_y = y * 2;
734
735 let share_a_block = [
737 share_a.get_pixel(base_x, base_y)[0],
738 share_a.get_pixel(base_x + 1, base_y)[0],
739 share_a.get_pixel(base_x, base_y + 1)[0],
740 share_a.get_pixel(base_x + 1, base_y + 1)[0],
741 ];
742
743 let share_b_block = [
744 share_b.get_pixel(base_x, base_y)[0],
745 share_b.get_pixel(base_x + 1, base_y)[0],
746 share_b.get_pixel(base_x, base_y + 1)[0],
747 share_b.get_pixel(base_x + 1, base_y + 1)[0],
748 ];
749
750 let mut reconstructed_value = 0u8;
752
753 for bit_pos in 0..8 {
754 let mut reconstructed_bits = [0u8; 4];
755
756 for i in 0..4 {
758 let bit_a = (share_a_block[i] >> bit_pos) & 1;
759 let bit_b = (share_b_block[i] >> bit_pos) & 1;
760 reconstructed_bits[i] = bit_a & bit_b;
761 }
762
763 let sum = reconstructed_bits.iter().map(|&x| x as u32).sum::<u32>();
766 let average_bit = if sum >= 2 { 1 } else { 0 }; reconstructed_value |= (average_bit as u8) << bit_pos;
769 }
770
771 result.put_pixel(x, y, Luma([reconstructed_value]));
772 }
773 }
774
775 Ok(DynamicImage::ImageLuma8(result))
776}
777
778fn dhiman_kasana_encrypt(
780 image: &DynamicImage,
781 config: &VCConfig,
782 cover_images: Option<Vec<DynamicImage>>,
783) -> Result<Vec<Share>> {
784 if config.num_shares != 3 {
785 return Err(VCError::InvalidConfiguration(
786 "Dhiman-Kasana EVCT(3,3) scheme requires exactly 3 shares".to_string(),
787 ));
788 }
789
790 let rgb = image.to_rgb8();
791 let (width, height) = (rgb.width(), rgb.height());
792
793 let color_matrices = generate_color_mixing_matrices();
795
796 let mut shares = Vec::new();
797 for _ in 0..3 {
798 shares.push(ImageBuffer::new(width, height));
799 }
800
801 let mut rng = rand::thread_rng();
802
803 for y in 0..height {
805 for x in 0..width {
806 let pixel = rgb.get_pixel(x, y);
807 let [r, g, b] = pixel.0;
808
809 let r_binary = convert_channel_to_binary(r);
811 let g_binary = convert_channel_to_binary(g);
812 let b_binary = convert_channel_to_binary(b);
813
814 let mut final_shares = [Rgb([0u8, 0u8, 0u8]); 3];
816
817 for channel in 0..3 {
818 let binary_value = match channel {
819 0 => r_binary,
820 1 => g_binary,
821 2 => b_binary,
822 _ => 0,
823 };
824
825 let matrix_idx = binary_value as usize % color_matrices.basis_matrices.len();
827 let matrix = &color_matrices.basis_matrices[matrix_idx];
828
829 let col = rng.gen_range(0..matrix.ncols());
831
832 for share_idx in 0..3 {
834 let intensity = matrix[(share_idx, col)];
835 let color_value = if intensity == 1 {
836 match channel {
837 0 => r,
838 1 => g,
839 2 => b,
840 _ => 0,
841 }
842 } else {
843 0
844 };
845
846 match channel {
847 0 => {
848 final_shares[share_idx].0[0] =
849 final_shares[share_idx].0[0].saturating_add(color_value)
850 }
851 1 => {
852 final_shares[share_idx].0[1] =
853 final_shares[share_idx].0[1].saturating_add(color_value)
854 }
855 2 => {
856 final_shares[share_idx].0[2] =
857 final_shares[share_idx].0[2].saturating_add(color_value)
858 }
859 _ => {}
860 }
861 }
862 }
863
864 for share_idx in 0..3 {
866 shares[share_idx].put_pixel(x, y, final_shares[share_idx]);
867 }
868 }
869 }
870
871 let result: Vec<Share> = shares
873 .into_iter()
874 .enumerate()
875 .map(|(i, img)| {
876 Share::new(
877 DynamicImage::ImageRgb8(img),
878 i + 1,
879 3,
880 width,
881 height,
882 1,
883 false,
884 )
885 })
886 .collect();
887
888 Ok(result)
889}
890
891fn convert_channel_to_binary(value: u8) -> u8 {
893 if value > 127 {
894 1
895 } else {
896 0
897 }
898}
899
900fn dhiman_kasana_decrypt(shares: &[Share], _config: &VCConfig) -> Result<DynamicImage> {
902 if shares.len() < 3 {
903 return Err(VCError::InsufficientShares {
904 required: 3,
905 provided: shares.len(),
906 });
907 }
908
909 let (width, height) = shares[0].dimensions();
910 let mut result = ImageBuffer::new(width, height);
911
912 for y in 0..height {
913 for x in 0..width {
914 let mut final_pixel = [0u8; 3];
915
916 for channel in 0..3 {
918 let mut channel_sum = 0u16;
919 let mut count = 0;
920
921 for share in shares {
922 if let DynamicImage::ImageRgb8(img) = &share.image {
923 let pixel = img.get_pixel(x, y);
924 channel_sum += pixel.0[channel] as u16;
925 count += 1;
926 }
927 }
928
929 final_pixel[channel] = if count > 0 {
931 (channel_sum / count as u16).min(255) as u8
932 } else {
933 0
934 };
935 }
936
937 result.put_pixel(x, y, Rgb(final_pixel));
938 }
939 }
940
941 Ok(DynamicImage::ImageRgb8(result))
942}