twoken/
core.rs

1use crc;
2use serde::{de, Deserializer, Serializer};
3use std::fmt;
4
5// for yubico keys, listed as CRC-16/IBM-SDLC or X-25 on
6// https://reveng.sourceforge.io/crc-catalogue/all.htm
7const CRC_YUBICO: crc::Crc<u16> = crc::Crc::<u16>::new(&crc::CRC_16_IBM_SDLC);
8
9const MODHEX: [char; 16] = [
10    'c', 'b', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'n', 'r', 't', 'u', 'v',
11];
12
13pub fn get_crc(bytes: &[u8]) -> u16 {
14    let mut digest = CRC_YUBICO.digest();
15    digest.update(bytes);
16    return digest.finalize();
17}
18
19pub fn to_modkex(bytes: &[u8]) -> String {
20    let mut output = String::with_capacity(bytes.len() * 2);
21    for byte in bytes {
22        output.push(MODHEX[(byte >> 4) as usize]);
23        output.push(MODHEX[(byte & 0xf) as usize]);
24    }
25    output
26}
27
28pub fn serialize_hex<S>(array: &[u8], serializer: S) -> Result<S::Ok, S::Error>
29where
30    S: Serializer,
31{
32    serializer.serialize_str(&hex::encode(array))
33}
34
35pub fn deserialize_hex<'de, D, const N: usize>(deserializer: D) -> Result<[u8; N], D::Error>
36where
37    D: Deserializer<'de>,
38{
39    struct StringVisitor<const M: usize>;
40
41    impl<'de, const M: usize> de::Visitor<'de> for StringVisitor<M> {
42        type Value = [u8; M];
43        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
44            formatter.write_str("a hex string")
45        }
46
47        fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
48        where
49            E: de::Error,
50        {
51            let mut output = [0u8; M];
52            hex::decode_to_slice(v, &mut output).map_err(E::custom)?;
53            Ok(output)
54        }
55    }
56    deserializer.deserialize_any(StringVisitor::<N>)
57}
58
59#[cfg(test)]
60mod tests {
61    use super::*;
62
63    #[test]
64    fn crc_check() {
65        assert_eq!(get_crc(b"123456789"), 0x906e)
66    }
67
68    #[test]
69    fn test_modhex() {
70        assert_eq!(to_modkex(&[0xaf, 0x1d, 0xc5]), "lvbtrg")
71    }
72}