1#![no_std]
70
71#[macro_use]
72extern crate alloc;
73
74pub extern crate base32;
75pub extern crate base64_url;
76
77use alloc::{string::String, vec::Vec};
78use core::fmt::{self, Debug, Formatter};
79
80pub use base64_url::base64;
81use crc_any::{CRCu64, CRCu8};
82
83pub type Cipher = (u8, Vec<u8>);
85
86pub struct ShortCrypt {
87 hashed_key: [u8; 8],
88 key_sum_rev: u64,
89}
90
91impl Debug for ShortCrypt {
92 #[inline]
93 fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
94 debug_helper::impl_debug_for_struct!(ShortCrypt, f, self, let .hashed_key = self.hashed_key.as_ref(), (.key_sum_rev, "{:X}", self.key_sum_rev));
95 }
96}
97
98macro_rules! u8_to_string_64 {
99 ($i:expr) => {
100 if $i < 10 {
101 $i + b'0'
102 } else if (10..36).contains(&$i) {
103 $i - 10 + b'A'
104 } else if (36..62).contains(&$i) {
105 $i - 36 + b'a'
106 } else if $i == 62 {
107 b'-'
108 } else {
109 b'_'
110 }
111 };
112}
113
114macro_rules! string_64_to_u8 {
115 ($c:expr) => {
116 if $c >= b'0' && $c <= b'9' {
117 $c - b'0'
118 } else if $c >= b'A' && $c <= b'Z' {
119 $c + 10 - b'A'
120 } else if $c >= b'a' && $c <= b'z' {
121 $c + 36 - b'a'
122 } else if $c == b'-' {
123 62
124 } else {
125 63
126 }
127 };
128}
129
130macro_rules! u8_to_string_32 {
131 ($i:expr) => {
132 if $i < 10 {
133 $i + b'0'
134 } else {
135 $i - 10 + b'A'
136 }
137 };
138}
139
140macro_rules! string_32_to_u8 {
141 ($c:expr) => {
142 if $c >= b'0' && $c <= b'9' {
143 $c - b'0'
144 } else {
145 $c + 10 - b'A'
146 }
147 };
148}
149
150impl ShortCrypt {
151 pub fn new<S: AsRef<str>>(key: S) -> ShortCrypt {
153 let key_bytes = key.as_ref().as_bytes();
154
155 let hashed_key = {
156 let mut hasher = CRCu64::crc64we();
157
158 hasher.digest(key_bytes);
159
160 hasher.get_crc().to_be_bytes()
161 };
162
163 let mut key_sum = 0u64;
164
165 for n in key_bytes.iter().copied() {
166 key_sum = key_sum.wrapping_add(u64::from(n));
167 }
168
169 let key_sum_rev = key_sum.reverse_bits();
170
171 ShortCrypt {
172 hashed_key,
173 key_sum_rev,
174 }
175 }
176
177 pub fn encrypt<T: ?Sized + AsRef<[u8]>>(&self, plaintext: &T) -> Cipher {
178 let data = plaintext.as_ref();
179
180 let len = data.len();
181
182 let hashed_value = {
183 let mut crc8 = CRCu8::crc8cdma2000();
184
185 crc8.digest(data);
186 crc8.get_crc()
187 };
188
189 let base = hashed_value % 32;
190
191 let mut encrypted = Vec::with_capacity(len);
192
193 let mut m = base;
194 let mut sum = u64::from(base);
195
196 for (i, d) in data.iter().enumerate() {
197 let offset = self.hashed_key[i % 8] ^ base;
198
199 let v = d ^ offset;
200
201 encrypted.push(v);
202
203 m ^= v;
204 sum = sum.wrapping_add(u64::from(v));
205 }
206
207 let sum: [u8; 8] = sum.to_be_bytes();
208
209 let hashed_array: [u8; 8] = {
210 let mut hasher = CRCu64::crc64we();
211
212 hasher.digest(&[m]);
213 hasher.digest(&sum);
214
215 hasher.get_crc().to_be_bytes()
216 };
217
218 let mut path = Vec::with_capacity(len);
219
220 for i in 0..len {
221 let index = i % 8;
222 path.push((hashed_array[index] ^ self.hashed_key[index]) as usize % len);
223 }
224
225 for (i, p) in path.iter().copied().enumerate() {
226 if i == p {
227 continue;
228 }
229
230 encrypted.swap(i, p);
231 }
232
233 (base, encrypted)
234 }
235
236 pub fn decrypt(&self, data: &Cipher) -> Result<Vec<u8>, &'static str> {
237 let base = data.0;
238 let data = &data.1;
239
240 if base > 31 {
241 return Err("The base is not correct.");
242 }
243
244 let len = data.len();
245
246 let mut decrypted = Vec::with_capacity(len);
247
248 self.decrypt_inner(base, data, &mut decrypted);
249
250 Ok(decrypted)
251 }
252
253 fn decrypt_inner(&self, base: u8, data: &[u8], output: &mut Vec<u8>) {
254 let len = data.len();
255
256 let mut m = base;
257 let mut sum = u64::from(base);
258
259 for v in data.iter().copied() {
260 m ^= v;
261 sum = sum.wrapping_add(u64::from(v));
262 }
263
264 let sum: [u8; 8] = sum.to_be_bytes();
265
266 let hashed_array: [u8; 8] = {
267 let mut hasher = CRCu64::crc64we();
268
269 hasher.digest(&[m]);
270 hasher.digest(&sum);
271
272 hasher.get_crc().to_be_bytes()
273 };
274
275 let mut path = Vec::with_capacity(len);
276
277 for i in 0..len {
278 let index = i % 8;
279 path.push((hashed_array[index] ^ self.hashed_key[index]) as usize % len);
280 }
281
282 let mut data = data.to_vec();
283
284 for (i, p) in path.iter().copied().enumerate().rev() {
285 if i == p {
286 continue;
287 }
288
289 data.swap(i, p);
290 }
291
292 for (i, d) in data.iter().enumerate() {
293 let offset = self.hashed_key[i % 8] ^ base;
294
295 output.push(d ^ offset);
296 }
297 }
298
299 pub fn encrypt_to_url_component<T: ?Sized + AsRef<[u8]>>(&self, data: &T) -> String {
300 let (base, encrypted) = self.encrypt(data);
301
302 let base = u8_to_string_64!(base);
303
304 let base_char = base as char;
305
306 let mut result = String::with_capacity(1 + ((encrypted.len() * 4 + 2) / 3));
307
308 base64_url::encode_to_string(&encrypted, &mut result);
309
310 let mut sum = u64::from(base);
311
312 for n in result.bytes() {
313 sum = sum.wrapping_add(u64::from(n));
314 }
315
316 let base_index = ((self.key_sum_rev ^ sum) % ((result.len() + 1) as u64)) as usize;
317
318 result.insert(base_index, base_char);
319
320 result
321 }
322
323 pub fn encrypt_to_url_component_and_push_to_string<T: ?Sized + AsRef<[u8]>, S: Into<String>>(
324 &self,
325 data: &T,
326 output: S,
327 ) -> String {
328 let (base, encrypted) = self.encrypt(data);
329
330 let base = u8_to_string_64!(base);
331
332 let base_char = base as char;
333
334 let mut output = output.into();
335
336 let original_len = output.len();
337
338 base64_url::encode_to_string(&encrypted, &mut output);
339
340 let mut sum = u64::from(base);
341
342 for n in output.bytes().skip(original_len) {
343 sum = sum.wrapping_add(u64::from(n));
344 }
345
346 let base_index =
347 ((self.key_sum_rev ^ sum) % ((output.len() - original_len + 1) as u64)) as usize;
348
349 output.insert(original_len + base_index, base_char);
350
351 output
352 }
353
354 pub fn decrypt_url_component<S: AsRef<str>>(
355 &self,
356 url_component: S,
357 ) -> Result<Vec<u8>, &'static str> {
358 let bytes = url_component.as_ref().as_bytes();
359 let len = bytes.len();
360
361 if len < 1 {
362 return Err("The URL component is incorrect.");
363 }
364
365 let base_index = {
366 let mut sum = 0u64;
367
368 for n in bytes.iter().copied() {
369 sum = sum.wrapping_add(u64::from(n));
370 }
371
372 ((self.key_sum_rev ^ sum) % (len as u64)) as usize
373 };
374
375 let base = string_64_to_u8!(bytes[base_index]);
376
377 if base > 31 {
378 return Err("The URL component is incorrect.");
379 }
380
381 let encrypted_base64_url = [&bytes[..base_index], &bytes[(base_index + 1)..]].concat();
382
383 let encrypted = base64_url::decode(&encrypted_base64_url)
384 .map_err(|_| "The URL component is incorrect.")?;
385
386 self.decrypt(&(base, encrypted))
387 }
388
389 pub fn decrypt_url_component_and_push_to_vec<S: AsRef<str>>(
390 &self,
391 url_component: S,
392 mut output: Vec<u8>,
393 ) -> Result<Vec<u8>, &'static str> {
394 let bytes = url_component.as_ref().as_bytes();
395 let len = bytes.len();
396
397 if len < 1 {
398 return Err("The URL component is incorrect.");
399 }
400
401 let base_index = {
402 let mut sum = 0u64;
403
404 for n in bytes.iter().copied() {
405 sum = sum.wrapping_add(u64::from(n));
406 }
407
408 ((self.key_sum_rev ^ sum) % (len as u64)) as usize
409 };
410
411 let base = string_64_to_u8!(bytes[base_index]);
412
413 if base > 31 {
414 return Err("The URL component is incorrect.");
415 }
416
417 let encrypted_base64_url = [&bytes[..base_index], &bytes[(base_index + 1)..]].concat();
418
419 let encrypted = base64_url::decode(&encrypted_base64_url)
420 .map_err(|_| "The URL component is incorrect.")?;
421
422 let len = encrypted.len();
423
424 output.reserve(len);
425
426 self.decrypt_inner(base, &encrypted, &mut output);
427
428 Ok(output)
429 }
430
431 pub fn encrypt_to_qr_code_alphanumeric<T: ?Sized + AsRef<[u8]>>(&self, data: &T) -> String {
432 let (base, encrypted) = self.encrypt(data);
433
434 let base = u8_to_string_32!(base);
435
436 let base_char = base as char;
437
438 let mut result = String::with_capacity(1 + ((encrypted.len() * 8 + 4) / 5));
439
440 result.push_str(&base32::encode(
441 base32::Alphabet::RFC4648 {
442 padding: false
443 },
444 &encrypted,
445 ));
446
447 let mut sum = u64::from(base);
448
449 for n in result.bytes() {
450 sum = sum.wrapping_add(u64::from(n));
451 }
452
453 let base_index = ((self.key_sum_rev ^ sum) % ((result.len() + 1) as u64)) as usize;
454
455 result.insert(base_index, base_char);
456
457 result
458 }
459
460 pub fn encrypt_to_qr_code_alphanumeric_and_push_to_string<
461 T: ?Sized + AsRef<[u8]>,
462 S: Into<String>,
463 >(
464 &self,
465 data: &T,
466 output: S,
467 ) -> String {
468 let (base, encrypted) = self.encrypt(data);
469
470 let base = u8_to_string_32!(base);
471
472 let base_char = base as char;
473
474 let mut output = output.into();
475
476 let original_len = output.len();
477
478 output.push_str(&base32::encode(
479 base32::Alphabet::RFC4648 {
480 padding: false
481 },
482 &encrypted,
483 ));
484
485 let mut sum = u64::from(base);
486
487 for n in output.bytes().skip(original_len) {
488 sum = sum.wrapping_add(u64::from(n));
489 }
490
491 let base_index =
492 ((self.key_sum_rev ^ sum) % ((output.len() - original_len + 1) as u64)) as usize;
493
494 output.insert(original_len + base_index, base_char);
495
496 output
497 }
498
499 pub fn decrypt_qr_code_alphanumeric<S: AsRef<str>>(
500 &self,
501 qr_code_alphanumeric: S,
502 ) -> Result<Vec<u8>, &'static str> {
503 let bytes = qr_code_alphanumeric.as_ref().as_bytes();
504 let len = bytes.len();
505
506 if len < 1 {
507 return Err("The QR code alphanumeric text is incorrect.");
508 }
509
510 let base_index = {
511 let mut sum = 0u64;
512
513 for n in bytes.iter().copied() {
514 sum = sum.wrapping_add(u64::from(n));
515 }
516
517 ((self.key_sum_rev ^ sum) % (len as u64)) as usize
518 };
519
520 let base = string_32_to_u8!(bytes[base_index]);
521
522 if base > 31 {
523 return Err("The QR code alphanumeric text is incorrect.");
524 }
525
526 let encrypted_base32 =
527 String::from_utf8([&bytes[..base_index], &bytes[(base_index + 1)..]].concat())
528 .map_err(|_| "The QR code alphanumeric text is incorrect.")?;
529
530 let encrypted = match base32::decode(
531 base32::Alphabet::RFC4648 {
532 padding: false
533 },
534 &encrypted_base32,
535 ) {
536 Some(t) => t,
537 None => return Err("The QR code alphanumeric text is incorrect."),
538 };
539
540 self.decrypt(&(base, encrypted))
541 }
542
543 pub fn decrypt_qr_code_alphanumeric_and_push_to_vec<S: AsRef<str>>(
544 &self,
545 qr_code_alphanumeric: S,
546 mut output: Vec<u8>,
547 ) -> Result<Vec<u8>, &'static str> {
548 let bytes = qr_code_alphanumeric.as_ref().as_bytes();
549 let len = bytes.len();
550
551 if len < 1 {
552 return Err("The QR code alphanumeric text is incorrect.");
553 }
554
555 let base_index = {
556 let mut sum = 0u64;
557
558 for n in bytes.iter().copied() {
559 sum = sum.wrapping_add(u64::from(n));
560 }
561
562 ((self.key_sum_rev ^ sum) % (len as u64)) as usize
563 };
564
565 let base = string_32_to_u8!(bytes[base_index]);
566
567 if base > 31 {
568 return Err("The QR code alphanumeric text is incorrect.");
569 }
570
571 let encrypted_base32 =
572 String::from_utf8([&bytes[..base_index], &bytes[(base_index + 1)..]].concat())
573 .map_err(|_| "The QR code alphanumeric text is incorrect.")?;
574
575 let encrypted = match base32::decode(
576 base32::Alphabet::RFC4648 {
577 padding: false
578 },
579 &encrypted_base32,
580 ) {
581 Some(t) => t,
582 None => return Err("The QR code alphanumeric text is incorrect."),
583 };
584
585 let len = encrypted.len();
586
587 output.reserve(len);
588
589 self.decrypt_inner(base, &encrypted, &mut output);
590
591 Ok(output)
592 }
593}