Skip to content

Commit 24fa1be

Browse files
authored
Merge pull request #7 from mibes/mdns-discovery
Simplified discovery
2 parents d2926a4 + 10b734d commit 24fa1be

File tree

18 files changed

+1149
-17
lines changed

18 files changed

+1149
-17
lines changed

examples/onoff_light/src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ fn main() {
3636
hw_ver: 2,
3737
sw_ver: 1,
3838
serial_no: "aabbccdd".to_string(),
39+
device_name: "OnOff Light".to_string(),
3940
};
4041
let dev_att = Box::new(dev_att::HardCodedDevAtt::new());
4142

matter/Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@ safemem = "0.3.3"
4747
chrono = { version = "0.4.19", default-features = false, features = ["clock", "std"] }
4848
async-channel = "1.6"
4949

50+
# to compute the check digit
51+
verhoeff = "1"
52+
53+
# print QR code
54+
qrcode = { version = "0.12", default-features = false }
55+
5056
[target.'cfg(target_os = "macos")'.dependencies]
5157
astro-dnssd = "0.3"
5258

matter/src/codec/base38.rs

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
/*
2+
*
3+
* Copyright (c) 2020-2022 Project CHIP Authors
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
//! Base38 encoding and decoding functions.
19+
20+
use crate::error::Error;
21+
22+
const BASE38_CHARS: [char; 38] = [
23+
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
24+
'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '-', '.',
25+
];
26+
27+
const UNUSED: u8 = 255;
28+
29+
// map of base38 charater to numeric value
30+
// subtract 45 from the character, then index into this array, if possible
31+
const DECODE_BASE38: [u8; 46] = [
32+
36, // '-', =45
33+
37, // '.', =46
34+
UNUSED, // '/', =47
35+
0, // '0', =48
36+
1, // '1', =49
37+
2, // '2', =50
38+
3, // '3', =51
39+
4, // '4', =52
40+
5, // '5', =53
41+
6, // '6', =54
42+
7, // '7', =55
43+
8, // '8', =56
44+
9, // '9', =57
45+
UNUSED, // ':', =58
46+
UNUSED, // ';', =59
47+
UNUSED, // '<', =50
48+
UNUSED, // '=', =61
49+
UNUSED, // '>', =62
50+
UNUSED, // '?', =63
51+
UNUSED, // '@', =64
52+
10, // 'A', =65
53+
11, // 'B', =66
54+
12, // 'C', =67
55+
13, // 'D', =68
56+
14, // 'E', =69
57+
15, // 'F', =70
58+
16, // 'G', =71
59+
17, // 'H', =72
60+
18, // 'I', =73
61+
19, // 'J', =74
62+
20, // 'K', =75
63+
21, // 'L', =76
64+
22, // 'M', =77
65+
23, // 'N', =78
66+
24, // 'O', =79
67+
25, // 'P', =80
68+
26, // 'Q', =81
69+
27, // 'R', =82
70+
28, // 'S', =83
71+
29, // 'T', =84
72+
30, // 'U', =85
73+
31, // 'V', =86
74+
32, // 'W', =87
75+
33, // 'X', =88
76+
34, // 'Y', =89
77+
35, // 'Z', =90
78+
];
79+
80+
const BASE38_CHARACTERS_NEEDED_IN_NBYTES_CHUNK: [u8; 3] = [2, 4, 5];
81+
const RADIX: u32 = BASE38_CHARS.len() as u32;
82+
83+
/// Encode a byte array into a base38 string.
84+
///
85+
/// # Arguments
86+
/// * `bytes` - byte array to encode
87+
/// * `length` - optional length of the byte array to encode. If not specified, the entire byte array is encoded.
88+
pub fn encode(bytes: &[u8], length: Option<usize>) -> String {
89+
let mut offset = 0;
90+
let mut result = String::new();
91+
92+
// if length is specified, use it, otherwise use the length of the byte array
93+
// if length is specified but is greater than the length of the byte array, use the length of the byte array
94+
let b_len = bytes.len();
95+
let length = length.map(|l| l.min(b_len)).unwrap_or(b_len);
96+
97+
while offset < length {
98+
let remaining = length - offset;
99+
match remaining.cmp(&2) {
100+
std::cmp::Ordering::Greater => {
101+
result.push_str(&encode_base38(
102+
((bytes[offset + 2] as u32) << 16)
103+
| ((bytes[offset + 1] as u32) << 8)
104+
| (bytes[offset] as u32),
105+
5,
106+
));
107+
offset += 3;
108+
}
109+
std::cmp::Ordering::Equal => {
110+
result.push_str(&encode_base38(
111+
((bytes[offset + 1] as u32) << 8) | (bytes[offset] as u32),
112+
4,
113+
));
114+
break;
115+
}
116+
std::cmp::Ordering::Less => {
117+
result.push_str(&encode_base38(bytes[offset] as u32, 2));
118+
break;
119+
}
120+
}
121+
}
122+
123+
result
124+
}
125+
126+
fn encode_base38(mut value: u32, char_count: u8) -> String {
127+
let mut result = String::new();
128+
for _ in 0..char_count {
129+
let remainder = value % 38;
130+
result.push(BASE38_CHARS[remainder as usize]);
131+
value = (value - remainder) / 38;
132+
}
133+
result
134+
}
135+
136+
/// Decode a base38-encoded string into a byte slice
137+
///
138+
/// # Arguments
139+
/// * `base38_str` - base38-encoded string to decode
140+
///
141+
/// Fails if the string contains invalid characters
142+
pub fn decode(base38_str: &str) -> Result<Vec<u8>, Error> {
143+
let mut result = Vec::new();
144+
let mut base38_characters_number: usize = base38_str.len();
145+
let mut decoded_base38_characters: usize = 0;
146+
147+
while base38_characters_number > 0 {
148+
let base38_characters_in_chunk: usize;
149+
let bytes_in_decoded_chunk: usize;
150+
151+
if base38_characters_number >= BASE38_CHARACTERS_NEEDED_IN_NBYTES_CHUNK[2] as usize {
152+
base38_characters_in_chunk = BASE38_CHARACTERS_NEEDED_IN_NBYTES_CHUNK[2] as usize;
153+
bytes_in_decoded_chunk = 3;
154+
} else if base38_characters_number == BASE38_CHARACTERS_NEEDED_IN_NBYTES_CHUNK[1] as usize {
155+
base38_characters_in_chunk = BASE38_CHARACTERS_NEEDED_IN_NBYTES_CHUNK[1] as usize;
156+
bytes_in_decoded_chunk = 2;
157+
} else if base38_characters_number == BASE38_CHARACTERS_NEEDED_IN_NBYTES_CHUNK[0] as usize {
158+
base38_characters_in_chunk = BASE38_CHARACTERS_NEEDED_IN_NBYTES_CHUNK[0] as usize;
159+
bytes_in_decoded_chunk = 1;
160+
} else {
161+
return Err(Error::InvalidData);
162+
}
163+
164+
let mut value = 0u32;
165+
166+
for i in (1..=base38_characters_in_chunk).rev() {
167+
let mut base38_chars = base38_str.chars();
168+
let v = decode_char(base38_chars.nth(decoded_base38_characters + i - 1).unwrap())?;
169+
170+
value = value * RADIX + v as u32;
171+
}
172+
173+
decoded_base38_characters += base38_characters_in_chunk;
174+
base38_characters_number -= base38_characters_in_chunk;
175+
176+
for _i in 0..bytes_in_decoded_chunk {
177+
result.push(value as u8);
178+
value >>= 8;
179+
}
180+
181+
if value > 0 {
182+
// encoded value is too big to represent a correct chunk of size 1, 2 or 3 bytes
183+
return Err(Error::InvalidArgument);
184+
}
185+
}
186+
187+
Ok(result)
188+
}
189+
190+
fn decode_char(c: char) -> Result<u8, Error> {
191+
let c = c as u8;
192+
if !(45..=90).contains(&c) {
193+
return Err(Error::InvalidData);
194+
}
195+
196+
let c = DECODE_BASE38[c as usize - 45];
197+
if c == UNUSED {
198+
return Err(Error::InvalidData);
199+
}
200+
201+
Ok(c)
202+
}
203+
204+
#[cfg(test)]
205+
mod tests {
206+
use super::*;
207+
const ENCODED: &str = "-MOA57ZU02IT2L2BJ00";
208+
const DECODED: [u8; 11] = [
209+
0x88, 0xff, 0xa7, 0x91, 0x50, 0x40, 0x00, 0x47, 0x51, 0xdd, 0x02,
210+
];
211+
212+
#[test]
213+
fn can_base38_encode() {
214+
assert_eq!(encode(&DECODED, None), ENCODED);
215+
assert_eq!(encode(&DECODED, Some(11)), ENCODED);
216+
217+
// length is greater than the length of the byte array
218+
assert_eq!(encode(&DECODED, Some(12)), ENCODED);
219+
}
220+
221+
#[test]
222+
fn can_base38_decode() {
223+
assert_eq!(decode(ENCODED).expect("can not decode base38"), DECODED);
224+
}
225+
}

matter/src/codec/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub mod base38;

matter/src/core.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use crate::{
2525
fabric::FabricMgr,
2626
interaction_model::InteractionModel,
2727
mdns::Mdns,
28+
pairing::{print_pairing_code_and_qr, DiscoveryCapabilities},
2829
secure_channel::{core::SecureChannel, pake::PaseMgr, spake2p::VerifierData},
2930
transport,
3031
};
@@ -58,7 +59,9 @@ impl Matter {
5859
dev_comm: CommissioningData,
5960
) -> Result<Box<Matter>, Error> {
6061
let mdns = Mdns::get()?;
61-
mdns.set_values(dev_det.vid, dev_det.pid);
62+
mdns.set_values(dev_det.vid, dev_det.pid, &dev_det.device_name);
63+
64+
print_pairing_code_and_qr(&dev_det, &dev_comm, DiscoveryCapabilities::default());
6265

6366
let fabric_mgr = Arc::new(FabricMgr::new()?);
6467
let acl_mgr = Arc::new(AclMgr::new()?);

matter/src/data_model/cluster_basic_information.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,15 @@ enum Attributes {
3131
SerialNo = 0x0f,
3232
}
3333

34+
#[derive(Default)]
3435
pub struct BasicInfoConfig {
3536
pub vid: u16,
3637
pub pid: u16,
3738
pub hw_ver: u16,
3839
pub sw_ver: u32,
3940
pub serial_no: String,
41+
/// Device name; up to 32 characters
42+
pub device_name: String,
4043
}
4144

4245
fn attr_dm_rev_new() -> Result<Attribute, Error> {

matter/src/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use log::error;
2424
pub enum Error {
2525
AttributeNotFound,
2626
AttributeIsCustom,
27+
BufferTooSmall,
2728
ClusterNotFound,
2829
CommandNotFound,
2930
EndpointNotFound,
@@ -38,6 +39,7 @@ pub enum Error {
3839
NoHandler,
3940
NoNetworkInterface,
4041
NoNodeId,
42+
NoMemory,
4143
NoSession,
4244
NoSpace,
4345
NoSpaceAckTable,
@@ -58,6 +60,7 @@ pub enum Error {
5860
InvalidSignature,
5961
InvalidState,
6062
InvalidTime,
63+
InvalidArgument,
6164
RwLock,
6265
TLVNotFound,
6366
TLVTypeMismatch,

matter/src/fabric.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ impl FabricMgr {
381381
pub fn set_label(&self, index: u8, label: String) -> Result<(), Error> {
382382
let index = index as usize;
383383
let mut mgr = self.inner.write()?;
384-
if label != "" {
384+
if !label.is_empty() {
385385
for i in 1..MAX_SUPPORTED_FABRICS {
386386
if let Some(fabric) = &mgr.fabrics[i] {
387387
if fabric.label == label {

matter/src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@
4141
//! let comm_data = CommissioningData {
4242
//! verifier: VerifierData::new_with_pw(123456),
4343
//! discriminator: 250,
44-
//!
4544
//! };
4645
//!
4746
//! /// The basic information about this device
@@ -51,6 +50,7 @@
5150
//! hw_ver: 2,
5251
//! sw_ver: 1,
5352
//! serial_no: "aabbcc".to_string(),
53+
//! device_name: "OnOff Light".to_string(),
5454
//! };
5555
//!
5656
//! /// Get the Matter Object
@@ -69,6 +69,7 @@
6969
7070
pub mod acl;
7171
pub mod cert;
72+
pub mod codec;
7273
pub mod core;
7374
pub mod crypto;
7475
pub mod data_model;
@@ -77,6 +78,7 @@ pub mod fabric;
7778
pub mod group_keys;
7879
pub mod interaction_model;
7980
pub mod mdns;
81+
pub mod pairing;
8082
pub mod secure_channel;
8183
pub mod sys;
8284
pub mod tlv;

0 commit comments

Comments
 (0)