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
38static 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
50static 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}