matc/
onboarding.rs

1use anyhow::Result;
2
3#[derive(Debug)]
4pub struct OnboardingInfo {
5    pub discriminator: u16,
6    pub passcode: u32,
7}
8
9pub fn decode_manual_pairing_code(code: &str) -> Result<OnboardingInfo> {
10    let norm = code.replace("-", "");
11    let first_grp = &norm[0..1];
12    let second_grp = &norm[1..6];
13    let third_grp = &norm[6..10];
14    let first = first_grp.parse::<u32>()?;
15    let second = second_grp.parse::<u32>()?;
16    let third = third_grp.parse::<u32>()?;
17    let passcode = second & 0x3fff | (third << 14);
18    let discriminator = (((first & 3) << 10) | (second >> 6) & 0x300) as u16;
19    Ok(OnboardingInfo {
20        discriminator,
21        passcode,
22    })
23}
24
25static D: [[u8; 10]; 10] = [
26    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
27    [1, 2, 3, 4, 0, 6, 7, 8, 9, 5],
28    [2, 3, 4, 0, 1, 7, 8, 9, 5, 6],
29    [3, 4, 0, 1, 2, 8, 9, 5, 6, 7],
30    [4, 0, 1, 2, 3, 9, 5, 6, 7, 8],
31    [5, 9, 8, 7, 6, 0, 4, 3, 2, 1],
32    [6, 5, 9, 8, 7, 1, 0, 4, 3, 2],
33    [7, 6, 5, 9, 8, 2, 1, 0, 4, 3],
34    [8, 7, 6, 5, 9, 3, 2, 1, 0, 4],
35    [9, 8, 7, 6, 5, 4, 3, 2, 1, 0],
36];
37
38/// The permutation table.
39static P: [[u8; 10]; 8] = [
40    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
41    [1, 5, 7, 6, 2, 8, 3, 0, 9, 4],
42    [5, 8, 0, 3, 7, 9, 6, 1, 4, 2],
43    [8, 9, 1, 6, 0, 4, 3, 5, 2, 7],
44    [9, 4, 5, 3, 1, 2, 6, 8, 7, 0],
45    [4, 2, 8, 6, 5, 7, 3, 9, 0, 1],
46    [2, 7, 9, 3, 8, 0, 6, 4, 1, 5],
47    [7, 0, 4, 6, 9, 1, 3, 2, 5, 8],
48];
49
50/// Inverse table for Verhoeff's dihedral group D5.
51static INV: [u8; 10] = [0, 4, 3, 2, 1, 5, 6, 7, 8, 9];
52
53fn verhoeff_checksum(num: &str) -> u8 {
54    let mut c: usize = 0;
55    for (i, ch) in num.chars().rev().enumerate() {
56        let digit = ch.to_digit(10).unwrap() as usize;
57        c = D[c][P[(i + 1) % 8][digit] as usize] as usize;
58    }
59    INV[c]
60}
61
62pub fn encode_manual_pairing_code(info: &OnboardingInfo) -> String {
63    let first = (info.discriminator as u32 >> 10) as u8;
64    let second = ((info.discriminator & 0x300) << 6) as u32 | (info.passcode & 0x3fff);
65    let third = info.passcode >> 14;
66    let digits = format!("{:01}{:05}{:04}", first, second, third);
67    let check = verhoeff_checksum(&digits);
68    format!("{}-{:05}-{:04}-{}", first, second, third, check)
69}
70
71#[cfg(test)]
72mod tests {
73    use crate::onboarding::OnboardingInfo;
74
75    use super::decode_manual_pairing_code;
76    use super::encode_manual_pairing_code;
77
78    #[test]
79    pub fn test_1() {
80        let res = decode_manual_pairing_code("2585-103-3238").unwrap();
81        assert_eq!(res.discriminator, 2816);
82        assert_eq!(res.passcode, 54453390);
83        let encoded = encode_manual_pairing_code(&res);
84        assert_eq!(encoded.replace("-", ""), "25851033238");
85    }
86
87    #[test]
88    pub fn test_2() {
89        let res = decode_manual_pairing_code("34970112332").unwrap();
90        assert_eq!(res.discriminator, 3840);
91        assert_eq!(res.passcode, 20202021);
92        let encoded = encode_manual_pairing_code(&res);
93        assert_eq!(encoded.replace("-", ""), "34970112332");
94    }
95    #[test]
96    pub fn test_3() {
97        let oi = OnboardingInfo {
98            discriminator: 3840,
99            passcode: 123456,
100        };
101        let encoded = encode_manual_pairing_code(&oi);
102        println!("Encoded: {}", encoded);
103    }
104}