1#![no_std]
74
75extern crate alloc;
76
77pub extern crate base32;
78pub extern crate base64_url;
79
80use alloc::{string::String, vec::Vec};
81use core::fmt::{self, Debug, Formatter};
82
83pub use base64_url::base64;
84use crc_any::{CRCu8, CRCu64};
85
86pub type Cipher = (u8, Vec<u8>);
88
89pub struct ShortCrypt {
91 hashed_key: [u8; 8],
92 key_sum_rev: u64,
93}
94
95impl Debug for ShortCrypt {
96 #[inline]
97 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
98 f.debug_struct("ShortCrypt").finish_non_exhaustive()
99 }
100}
101
102#[inline]
103fn encode_base(base: u8) -> u8 {
104 debug_assert!(base < 32);
105
106 if base < 10 { base + b'0' } else { base - 10 + b'A' }
107}
108
109#[inline]
110fn decode_base(byte: u8) -> Option<u8> {
111 match byte {
112 b'0'..=b'9' => Some(byte - b'0'),
113 b'A'..=b'V' => Some(byte - b'A' + 10),
114 _ => None,
115 }
116}
117
118#[inline]
119fn checksum_base(data: &[u8]) -> u8 {
120 let mut crc8 = CRCu8::crc8cdma2000();
121
122 crc8.update(data);
123 crc8.get_crc() % 32
124}
125
126impl ShortCrypt {
127 pub fn new<S: AsRef<str>>(key: S) -> ShortCrypt {
129 let key_bytes = key.as_ref().as_bytes();
130
131 let hashed_key = {
132 let mut hasher = CRCu64::crc64we();
133
134 hasher.update(key_bytes);
135
136 hasher.get_crc().to_be_bytes()
137 };
138
139 let mut key_sum = 0u64;
140
141 for n in key_bytes.iter().copied() {
142 key_sum = key_sum.wrapping_add(u64::from(n));
143 }
144
145 let key_sum_rev = key_sum.reverse_bits();
146
147 ShortCrypt {
148 hashed_key,
149 key_sum_rev,
150 }
151 }
152
153 pub fn encrypt<T: ?Sized + AsRef<[u8]>>(&self, plaintext: &T) -> Cipher {
155 let data = plaintext.as_ref();
156
157 let len = data.len();
158
159 let base = checksum_base(data);
160
161 let mut encrypted = Vec::with_capacity(len);
162
163 let mut m = base;
164 let mut sum = u64::from(base);
165
166 for (i, d) in data.iter().enumerate() {
167 let offset = self.hashed_key[i % 8] ^ base;
168
169 let v = d ^ offset;
170
171 encrypted.push(v);
172
173 m ^= v;
174 sum = sum.wrapping_add(u64::from(v));
175 }
176
177 let sum: [u8; 8] = sum.to_be_bytes();
178
179 let hashed_array: [u8; 8] = {
180 let mut hasher = CRCu64::crc64we();
181
182 hasher.update(&[m]);
183 hasher.update(&sum);
184
185 hasher.get_crc().to_be_bytes()
186 };
187
188 for i in 0..len {
189 let index = i % 8;
190 let p = (hashed_array[index] ^ self.hashed_key[index]) as usize % len;
191
192 if i == p {
193 continue;
194 }
195
196 encrypted.swap(i, p);
197 }
198
199 (base, encrypted)
200 }
201
202 pub fn decrypt(&self, data: &Cipher) -> Result<Vec<u8>, &'static str> {
204 let base = data.0;
205 let data = &data.1;
206
207 if base > 31 {
208 return Err("The base is not correct.");
209 }
210
211 let mut decrypted = data.to_vec();
212
213 self.decrypt_appended_inner(base, 0, &mut decrypted)
214 .map_err(|_| "The cipher is incorrect.")?;
215
216 Ok(decrypted)
217 }
218
219 fn decrypt_appended_inner(
220 &self,
221 base: u8,
222 original_len: usize,
223 output: &mut Vec<u8>,
224 ) -> Result<(), ()> {
225 let len = output.len() - original_len;
226
227 let mut m = base;
228 let mut sum = u64::from(base);
229
230 for v in output[original_len..].iter().copied() {
231 m ^= v;
232 sum = sum.wrapping_add(u64::from(v));
233 }
234
235 let sum: [u8; 8] = sum.to_be_bytes();
236
237 let hashed_array: [u8; 8] = {
238 let mut hasher = CRCu64::crc64we();
239
240 hasher.update(&[m]);
241 hasher.update(&sum);
242
243 hasher.get_crc().to_be_bytes()
244 };
245
246 for i in (0..len).rev() {
247 let index = i % 8;
248 let p = (hashed_array[index] ^ self.hashed_key[index]) as usize % len;
249
250 if i == p {
251 continue;
252 }
253
254 output.swap(original_len + i, original_len + p);
255 }
256
257 for (i, d) in output[original_len..].iter_mut().enumerate() {
258 let offset = self.hashed_key[i % 8] ^ base;
259
260 *d ^= offset;
261 }
262
263 if checksum_base(&output[original_len..]) != base {
264 output.truncate(original_len);
265
266 return Err(());
267 }
268
269 Ok(())
270 }
271
272 fn extract_base(&self, bytes: &[u8]) -> Option<(u8, usize)> {
273 if bytes.is_empty() {
274 return None;
275 }
276
277 let mut sum = 0u64;
278
279 for n in bytes.iter().copied() {
280 sum = sum.wrapping_add(u64::from(n));
281 }
282
283 let base_index = ((self.key_sum_rev ^ sum) % (bytes.len() as u64)) as usize;
284 let base = decode_base(bytes[base_index])?;
285
286 Some((base, base_index))
287 }
288
289 fn insert_base(&self, base: u8, original_len: usize, mut output: String) -> String {
290 let base = encode_base(base);
291 let mut sum = u64::from(base);
292
293 for n in output.bytes().skip(original_len) {
294 sum = sum.wrapping_add(u64::from(n));
295 }
296
297 let base_index =
298 ((self.key_sum_rev ^ sum) % ((output.len() - original_len + 1) as u64)) as usize;
299
300 output.insert(original_len + base_index, base as char);
301 output
302 }
303
304 #[inline]
306 pub fn encrypt_to_url_component<T: ?Sized + AsRef<[u8]>>(&self, data: &T) -> String {
307 self.encrypt_to_url_component_and_push_to_string(data, String::new())
308 }
309
310 pub fn encrypt_to_url_component_and_push_to_string<T: ?Sized + AsRef<[u8]>, S: Into<String>>(
312 &self,
313 data: &T,
314 output: S,
315 ) -> String {
316 let (base, encrypted) = self.encrypt(data);
317
318 let mut output = output.into();
319
320 let original_len = output.len();
321
322 base64_url::encode_to_string(&encrypted, &mut output);
323
324 self.insert_base(base, original_len, output)
325 }
326
327 #[inline]
329 pub fn decrypt_url_component<S: AsRef<str>>(
330 &self,
331 url_component: S,
332 ) -> Result<Vec<u8>, &'static str> {
333 self.decrypt_url_component_and_push_to_vec(url_component, Vec::new())
334 }
335
336 pub fn decrypt_url_component_and_push_to_vec<S: AsRef<str>>(
338 &self,
339 url_component: S,
340 mut output: Vec<u8>,
341 ) -> Result<Vec<u8>, &'static str> {
342 let bytes = url_component.as_ref().as_bytes();
343 let (base, base_index) =
344 self.extract_base(bytes).ok_or("The URL component is incorrect.")?;
345
346 let encrypted_base64_url = [&bytes[..base_index], &bytes[(base_index + 1)..]].concat();
347 let original_len = output.len();
348
349 base64_url::decode_to_vec(&encrypted_base64_url, &mut output)
350 .map_err(|_| "The URL component is incorrect.")?;
351
352 self.decrypt_appended_inner(base, original_len, &mut output)
353 .map_err(|_| "The URL component is incorrect.")?;
354
355 Ok(output)
356 }
357
358 #[inline]
360 pub fn encrypt_to_qr_code_alphanumeric<T: ?Sized + AsRef<[u8]>>(&self, data: &T) -> String {
361 self.encrypt_to_qr_code_alphanumeric_and_push_to_string(data, String::new())
362 }
363
364 pub fn encrypt_to_qr_code_alphanumeric_and_push_to_string<
366 T: ?Sized + AsRef<[u8]>,
367 S: Into<String>,
368 >(
369 &self,
370 data: &T,
371 output: S,
372 ) -> String {
373 let (base, encrypted) = self.encrypt(data);
374
375 let mut output = output.into();
376
377 let original_len = output.len();
378
379 output.push_str(&base32::encode(
380 base32::Alphabet::Rfc4648 {
381 padding: false
382 },
383 &encrypted,
384 ));
385
386 self.insert_base(base, original_len, output)
387 }
388
389 #[inline]
391 pub fn decrypt_qr_code_alphanumeric<S: AsRef<str>>(
392 &self,
393 qr_code_alphanumeric: S,
394 ) -> Result<Vec<u8>, &'static str> {
395 self.decrypt_qr_code_alphanumeric_and_push_to_vec(qr_code_alphanumeric, Vec::new())
396 }
397
398 pub fn decrypt_qr_code_alphanumeric_and_push_to_vec<S: AsRef<str>>(
400 &self,
401 qr_code_alphanumeric: S,
402 mut output: Vec<u8>,
403 ) -> Result<Vec<u8>, &'static str> {
404 let bytes = qr_code_alphanumeric.as_ref().as_bytes();
405 let (base, base_index) =
406 self.extract_base(bytes).ok_or("The QR code alphanumeric text is incorrect.")?;
407
408 let encrypted_base32 =
409 String::from_utf8([&bytes[..base_index], &bytes[(base_index + 1)..]].concat())
410 .map_err(|_| "The QR code alphanumeric text is incorrect.")?;
411
412 let encrypted = base32::decode(
413 base32::Alphabet::Rfc4648 {
414 padding: false
415 },
416 &encrypted_base32,
417 )
418 .ok_or("The QR code alphanumeric text is incorrect.")?;
419
420 let original_len = output.len();
421
422 if original_len == 0 {
423 output = encrypted;
424 } else {
425 output.extend_from_slice(&encrypted);
426 }
427
428 self.decrypt_appended_inner(base, original_len, &mut output)
429 .map_err(|_| "The QR code alphanumeric text is incorrect.")?;
430
431 Ok(output)
432 }
433}