1extern crate core;
2
3use std::ffi::OsStr;
4use std::fs::File;
5use std::io::BufReader;
6use std::path::Path;
7use std::{fs, io};
8
9use crate::errors::CoreError;
10use errors::{RecoveryError, SharesLoaderError, SplitError};
11use image::ImageError;
12use rqrr::DeQRError;
13
14use crate::shared_secret::data_block::common::SharedSecretConfig;
15use crate::shared_secret::data_block::shared_secret_data_block::SharedSecretBlock;
16use crate::shared_secret::shared_secret::{PlainText, SharedSecret, SharedSecretEncryption, UserShareDto};
17use errors::RecoveryError::EmptyInput;
18
19pub mod crypto;
20pub mod errors;
21pub mod node;
22pub mod sdk;
23pub mod shared_secret;
24pub mod models;
25
26#[macro_use]
27extern crate serde_derive;
28
29extern crate serde;
30extern crate serde_json;
31
32pub type CoreResult<T> = std::result::Result<T, CoreError>;
33
34#[derive(Debug, thiserror::Error)]
35pub enum RecoveryOperationError {
36 #[error(transparent)]
37 LoaderError(#[from] SharesLoaderError),
38 #[error(transparent)]
39 RecoveryFromSharesError(#[from] RecoveryError),
40}
41
42pub fn recover() -> CoreResult<PlainText> {
43 let users_shares = load_users_shares()?;
44 let recovered = recover_from_shares(users_shares)?;
45 Ok(recovered)
46}
47
48pub fn recover_from_shares(users_shares: Vec<UserShareDto>) -> CoreResult<PlainText> {
49 let mut secret_blocks: Vec<SharedSecretBlock> = vec![];
50
51 if users_shares[0].share_blocks.is_empty() {
52 let err = EmptyInput("Empty shares list. Nothing to recover".to_string());
53 return Err(CoreError::from(err));
54 }
55
56 let blocks_num: usize = users_shares[0].share_blocks.len();
57
58 for block_index in 0..blocks_num {
59 let mut encrypted_data_blocks = vec![];
60
61 for user_share in users_shares.iter() {
62 let encrypted_data_block = user_share.get_encrypted_data_block(block_index)?;
63 encrypted_data_blocks.push(encrypted_data_block);
64 }
65
66 let curr_block = &users_shares[0].share_blocks[block_index];
67 let secret_block = SharedSecretBlock {
68 config: curr_block.config,
69 meta_data: curr_block.meta_data.clone(),
70 shares: encrypted_data_blocks,
71 };
72
73 secret_blocks.push(secret_block);
74 }
75
76 let secret = SharedSecret { secret_blocks };
77
78 let result = secret.recover()?;
79 Ok(result)
80}
81
82fn load_users_shares() -> Result<Vec<UserShareDto>, SharesLoaderError> {
83 let shares = fs::read_dir("secrets")?;
85
86 let mut users_shares_dto: Vec<UserShareDto> = vec![];
87 for secret_share_file in shares {
88 let file_path = secret_share_file?.path();
89
90 let maybe_ext = file_path.extension().and_then(OsStr::to_str);
91
92 if let Some(ext) = maybe_ext {
93 if ext.eq("json") {
94 let file = File::open(file_path)?;
96 let reader = BufReader::new(file);
97
98 let secret_share: UserShareDto = serde_json::from_reader(reader)?;
100 users_shares_dto.push(secret_share);
101 }
102 }
103 }
104
105 Ok(users_shares_dto)
106}
107
108pub fn split(secret: String, config: SharedSecretConfig) -> CoreResult<()> {
109 let plain_text = PlainText::from(secret);
110 let shared_secret = SharedSecretEncryption::new(config, &plain_text)?;
111
112 let dir_op = fs::create_dir_all("secrets");
113
114 if let Err(dir_err) = dir_op {
115 return Err(CoreError::from(SplitError::from(dir_err)));
116 }
117
118 for share_index in 0..config.number_of_shares {
119 let share: UserShareDto = shared_secret.get_share(share_index);
120 let share_json = serde_json::to_string_pretty(&share)?;
121
122 let write_op = fs::write(format!("secrets/shared-secret-{share_index}.json"), share_json.clone());
124
125 if let Err(op_err) = write_op {
126 return Err(CoreError::from(SplitError::from(op_err)));
127 }
128
129 generate_qr_code(
131 share_json.as_str(),
132 format!("secrets/shared-secret-{share_index}.png").as_str(),
133 )
134 }
135
136 Ok(())
137}
138
139pub fn generate_qr_code(data: &str, path: &str) {
140 use qrcode_generator::QrCodeEcc;
141
142 qrcode_generator::to_png_to_file(data, QrCodeEcc::High, data.len(), path).unwrap();
143}
144
145#[derive(Debug, thiserror::Error)]
146pub enum QrToJsonParserError {
147 #[error(
148 "Secrets directory has invalid structure. \
149 Please check that 'secrets' dir exists and \
150 contains json or qr files with password shares"
151 )]
152 SecretsDirectoryError {
153 #[from]
154 source: io::Error,
155 },
156 #[error("Image parsing error")]
157 ImageParsingError {
158 #[from]
159 source: QrCodeParserError,
160 },
161}
162
163pub fn convert_qr_images_to_json_files() -> Result<Vec<String>, QrToJsonParserError> {
164 let shares = fs::read_dir("secrets")?;
165
166 let mut shares_json: Vec<String> = vec![];
167
168 let mut share_index = 0;
169 for secret_share_file in shares {
170 let file_path = secret_share_file?.path();
171
172 let extension = file_path.extension().and_then(OsStr::to_str).unwrap();
173
174 if !extension.eq("png") {
175 continue;
176 }
177
178 let json_str = read_qr_code(file_path.as_path())?;
179 fs::write(format!("secrets/shared-secret-{share_index}.json"), json_str.clone())?;
180
181 shares_json.push(json_str.clone());
182
183 share_index += 1;
184 }
185
186 Ok(shares_json)
187}
188
189#[derive(Debug, thiserror::Error)]
190pub enum QrCodeParserError {
191 #[error("Qr code parsing error")]
192 ImageParsingError {
193 #[from]
194 source: ImageError,
195 },
196 #[error("Error decoding image")]
197 ImageDecodingError {
198 #[from]
199 source: DeQRError,
200 },
201}
202
203pub fn read_qr_code(path: &Path) -> Result<String, QrCodeParserError> {
204 let img = image::open(path)?.to_luma8();
205 let mut img = rqrr::PreparedImage::prepare(img);
207 let grids = img.detect_grids();
209 assert_eq!(grids.len(), 1);
210 let (_, content) = grids[0].decode()?;
212 Ok(content)
213}