Remove allocations from Base38 and QR calc
This commit is contained in:
parent
26fb6b01c5
commit
f7a887c1d2
6 changed files with 236 additions and 245 deletions
|
@ -15,7 +15,7 @@ name = "matter"
|
||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["std", "crypto_mbedtls", "nightly"]
|
default = ["std", "crypto_mbedtls"]
|
||||||
std = []
|
std = []
|
||||||
nightly = []
|
nightly = []
|
||||||
crypto_openssl = ["openssl", "foreign-types", "hmac", "sha2"]
|
crypto_openssl = ["openssl", "foreign-types", "hmac", "sha2"]
|
||||||
|
|
|
@ -17,10 +17,6 @@
|
||||||
|
|
||||||
//! Base38 encoding and decoding functions.
|
//! Base38 encoding and decoding functions.
|
||||||
|
|
||||||
extern crate alloc;
|
|
||||||
|
|
||||||
use alloc::{string::String, vec::Vec};
|
|
||||||
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
|
|
||||||
const BASE38_CHARS: [char; 38] = [
|
const BASE38_CHARS: [char; 38] = [
|
||||||
|
@ -81,60 +77,68 @@ const DECODE_BASE38: [u8; 46] = [
|
||||||
35, // 'Z', =90
|
35, // 'Z', =90
|
||||||
];
|
];
|
||||||
|
|
||||||
const BASE38_CHARACTERS_NEEDED_IN_NBYTES_CHUNK: [u8; 3] = [2, 4, 5];
|
|
||||||
const RADIX: u32 = BASE38_CHARS.len() as u32;
|
const RADIX: u32 = BASE38_CHARS.len() as u32;
|
||||||
|
|
||||||
/// Encode a byte array into a base38 string.
|
/// Encode a byte array into a base38 string.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// * `bytes` - byte array to encode
|
/// * `bytes` - byte array to encode
|
||||||
/// * `length` - optional length of the byte array to encode. If not specified, the entire byte array is encoded.
|
pub fn encode_string<const N: usize>(bytes: &[u8]) -> Result<heapless::String<N>, Error> {
|
||||||
pub fn encode(bytes: &[u8], length: Option<usize>) -> String {
|
let mut string = heapless::String::new();
|
||||||
let mut offset = 0;
|
for c in encode(bytes) {
|
||||||
let mut result = String::new();
|
string.push(c).map_err(|_| Error::NoSpace)?;
|
||||||
|
}
|
||||||
|
|
||||||
// if length is specified, use it, otherwise use the length of the byte array
|
Ok(string)
|
||||||
// if length is specified but is greater than the length of the byte array, use the length of the byte array
|
}
|
||||||
let b_len = bytes.len();
|
|
||||||
let length = length.map(|l| l.min(b_len)).unwrap_or(b_len);
|
|
||||||
|
|
||||||
while offset < length {
|
pub fn encode(bytes: &[u8]) -> impl Iterator<Item = char> + '_ {
|
||||||
let remaining = length - offset;
|
(0..bytes.len() / 3)
|
||||||
match remaining.cmp(&2) {
|
.flat_map(move |index| {
|
||||||
core::cmp::Ordering::Greater => {
|
let offset = index * 3;
|
||||||
result.push_str(&encode_base38(
|
|
||||||
|
encode_base38(
|
||||||
((bytes[offset + 2] as u32) << 16)
|
((bytes[offset + 2] as u32) << 16)
|
||||||
| ((bytes[offset + 1] as u32) << 8)
|
| ((bytes[offset + 1] as u32) << 8)
|
||||||
| (bytes[offset] as u32),
|
| (bytes[offset] as u32),
|
||||||
5,
|
5,
|
||||||
));
|
)
|
||||||
offset += 3;
|
})
|
||||||
}
|
.chain(
|
||||||
core::cmp::Ordering::Equal => {
|
core::iter::once(bytes.len() % 3).flat_map(move |remainder| {
|
||||||
result.push_str(&encode_base38(
|
let offset = bytes.len() / 3 * 3;
|
||||||
|
|
||||||
|
match remainder {
|
||||||
|
2 => encode_base38(
|
||||||
((bytes[offset + 1] as u32) << 8) | (bytes[offset] as u32),
|
((bytes[offset + 1] as u32) << 8) | (bytes[offset] as u32),
|
||||||
4,
|
4,
|
||||||
));
|
),
|
||||||
break;
|
1 => encode_base38(bytes[offset] as u32, 2),
|
||||||
|
_ => encode_base38(0, 0),
|
||||||
}
|
}
|
||||||
core::cmp::Ordering::Less => {
|
}),
|
||||||
result.push_str(&encode_base38(bytes[offset] as u32, 2));
|
)
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encode_base38(mut value: u32, char_count: u8) -> String {
|
fn encode_base38(mut value: u32, repeat: usize) -> impl Iterator<Item = char> {
|
||||||
let mut result = String::new();
|
(0..repeat).map(move |_| {
|
||||||
for _ in 0..char_count {
|
let remainder = value % RADIX;
|
||||||
let remainder = value % 38;
|
let c = BASE38_CHARS[remainder as usize];
|
||||||
result.push(BASE38_CHARS[remainder as usize]);
|
|
||||||
value = (value - remainder) / 38;
|
value = (value - remainder) / RADIX;
|
||||||
|
|
||||||
|
c
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decode_vec<const N: usize>(base38_str: &str) -> Result<heapless::Vec<u8, N>, Error> {
|
||||||
|
let mut vec = heapless::Vec::new();
|
||||||
|
|
||||||
|
for byte in decode(base38_str) {
|
||||||
|
vec.push(byte?).map_err(|_| Error::NoSpace)?;
|
||||||
}
|
}
|
||||||
result
|
|
||||||
|
Ok(vec)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decode a base38-encoded string into a byte slice
|
/// Decode a base38-encoded string into a byte slice
|
||||||
|
@ -142,57 +146,64 @@ fn encode_base38(mut value: u32, char_count: u8) -> String {
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// * `base38_str` - base38-encoded string to decode
|
/// * `base38_str` - base38-encoded string to decode
|
||||||
///
|
///
|
||||||
/// Fails if the string contains invalid characters
|
/// Fails if the string contains invalid characters or if the supplied buffer is too small to fit the decoded data
|
||||||
pub fn decode(base38_str: &str) -> Result<Vec<u8>, Error> {
|
pub fn decode(base38_str: &str) -> impl Iterator<Item = Result<u8, Error>> + '_ {
|
||||||
let mut result = Vec::new();
|
let stru = base38_str.as_bytes();
|
||||||
let mut base38_characters_number: usize = base38_str.len();
|
|
||||||
let mut decoded_base38_characters: usize = 0;
|
|
||||||
|
|
||||||
while base38_characters_number > 0 {
|
(0..stru.len() / 5)
|
||||||
let base38_characters_in_chunk: usize;
|
.flat_map(move |index| {
|
||||||
let bytes_in_decoded_chunk: usize;
|
let offset = index * 5;
|
||||||
|
decode_base38(&stru[offset..offset + 5])
|
||||||
if base38_characters_number >= BASE38_CHARACTERS_NEEDED_IN_NBYTES_CHUNK[2] as usize {
|
})
|
||||||
base38_characters_in_chunk = BASE38_CHARACTERS_NEEDED_IN_NBYTES_CHUNK[2] as usize;
|
.chain({
|
||||||
bytes_in_decoded_chunk = 3;
|
let offset = stru.len() / 5 * 5;
|
||||||
} else if base38_characters_number == BASE38_CHARACTERS_NEEDED_IN_NBYTES_CHUNK[1] as usize {
|
decode_base38(&stru[offset..])
|
||||||
base38_characters_in_chunk = BASE38_CHARACTERS_NEEDED_IN_NBYTES_CHUNK[1] as usize;
|
})
|
||||||
bytes_in_decoded_chunk = 2;
|
.take_while(Result::is_ok)
|
||||||
} else if base38_characters_number == BASE38_CHARACTERS_NEEDED_IN_NBYTES_CHUNK[0] as usize {
|
|
||||||
base38_characters_in_chunk = BASE38_CHARACTERS_NEEDED_IN_NBYTES_CHUNK[0] as usize;
|
|
||||||
bytes_in_decoded_chunk = 1;
|
|
||||||
} else {
|
|
||||||
return Err(Error::InvalidData);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut value = 0u32;
|
|
||||||
|
|
||||||
for i in (1..=base38_characters_in_chunk).rev() {
|
|
||||||
let mut base38_chars = base38_str.chars();
|
|
||||||
let v = decode_char(base38_chars.nth(decoded_base38_characters + i - 1).unwrap())?;
|
|
||||||
|
|
||||||
value = value * RADIX + v as u32;
|
|
||||||
}
|
|
||||||
|
|
||||||
decoded_base38_characters += base38_characters_in_chunk;
|
|
||||||
base38_characters_number -= base38_characters_in_chunk;
|
|
||||||
|
|
||||||
for _i in 0..bytes_in_decoded_chunk {
|
|
||||||
result.push(value as u8);
|
|
||||||
value >>= 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
if value > 0 {
|
|
||||||
// encoded value is too big to represent a correct chunk of size 1, 2 or 3 bytes
|
|
||||||
return Err(Error::InvalidArgument);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(result)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_char(c: char) -> Result<u8, Error> {
|
fn decode_base38(chars: &[u8]) -> impl Iterator<Item = Result<u8, Error>> {
|
||||||
let c = c as u8;
|
let mut value = 0u32;
|
||||||
|
let mut cerr = None;
|
||||||
|
|
||||||
|
let repeat = match chars.len() {
|
||||||
|
5 => 3,
|
||||||
|
4 => 2,
|
||||||
|
2 => 1,
|
||||||
|
0 => 0,
|
||||||
|
_ => -1,
|
||||||
|
};
|
||||||
|
|
||||||
|
if repeat >= 0 {
|
||||||
|
for c in chars.iter().rev() {
|
||||||
|
match decode_char(*c) {
|
||||||
|
Ok(v) => value = value * RADIX + v as u32,
|
||||||
|
Err(err) => {
|
||||||
|
cerr = Some(err);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cerr = Some(Error::InvalidData)
|
||||||
|
}
|
||||||
|
|
||||||
|
(0..repeat)
|
||||||
|
.map(move |_| {
|
||||||
|
if let Some(err) = cerr {
|
||||||
|
Err(err)
|
||||||
|
} else {
|
||||||
|
let byte = (value & 0xff) as u8;
|
||||||
|
|
||||||
|
value >>= 8;
|
||||||
|
|
||||||
|
Ok(byte)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.take_while(Result::is_ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode_char(c: u8) -> Result<u8, Error> {
|
||||||
if !(45..=90).contains(&c) {
|
if !(45..=90).contains(&c) {
|
||||||
return Err(Error::InvalidData);
|
return Err(Error::InvalidData);
|
||||||
}
|
}
|
||||||
|
@ -215,15 +226,17 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn can_base38_encode() {
|
fn can_base38_encode() {
|
||||||
assert_eq!(encode(&DECODED, None), ENCODED);
|
assert_eq!(
|
||||||
assert_eq!(encode(&DECODED, Some(11)), ENCODED);
|
encode_string::<{ ENCODED.len() }>(&DECODED).unwrap(),
|
||||||
|
ENCODED
|
||||||
// length is greater than the length of the byte array
|
);
|
||||||
assert_eq!(encode(&DECODED, Some(12)), ENCODED);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn can_base38_decode() {
|
fn can_base38_decode() {
|
||||||
assert_eq!(decode(ENCODED).expect("can not decode base38"), DECODED);
|
assert_eq!(
|
||||||
|
decode_vec::<{ DECODED.len() }>(ENCODED).expect("Cannot decode base38"),
|
||||||
|
DECODED
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,10 +84,19 @@ impl<'a> Matter<'a> {
|
||||||
self.dev_det
|
self.dev_det
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start(&mut self, dev_comm: CommissioningData) -> Result<(), Error> {
|
pub fn start<const N: usize>(
|
||||||
|
&mut self,
|
||||||
|
dev_comm: CommissioningData,
|
||||||
|
buf: &mut [u8],
|
||||||
|
) -> Result<(), Error> {
|
||||||
let open_comm_window = self.fabric_mgr.borrow().is_empty();
|
let open_comm_window = self.fabric_mgr.borrow().is_empty();
|
||||||
if open_comm_window {
|
if open_comm_window {
|
||||||
print_pairing_code_and_qr(self.dev_det, &dev_comm, DiscoveryCapabilities::default());
|
print_pairing_code_and_qr::<N>(
|
||||||
|
self.dev_det,
|
||||||
|
&dev_comm,
|
||||||
|
DiscoveryCapabilities::default(),
|
||||||
|
buf,
|
||||||
|
);
|
||||||
|
|
||||||
self.pase_mgr.borrow_mut().enable_pase_session(
|
self.pase_mgr.borrow_mut().enable_pase_session(
|
||||||
dev_comm.verifier,
|
dev_comm.verifier,
|
||||||
|
|
|
@ -15,38 +15,8 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use alloc::sync::Arc;
|
|
||||||
use std::sync::{Mutex, Once};
|
|
||||||
|
|
||||||
use crate::{crypto, error::Error};
|
use crate::{crypto, error::Error};
|
||||||
|
|
||||||
extern crate alloc;
|
|
||||||
|
|
||||||
// This is just makeshift implementation for now, not used anywhere
|
|
||||||
pub struct GroupKeys {}
|
|
||||||
|
|
||||||
static mut G_GRP_KEYS: Option<Arc<Mutex<GroupKeys>>> = None;
|
|
||||||
static INIT: Once = Once::new();
|
|
||||||
|
|
||||||
impl GroupKeys {
|
|
||||||
fn new() -> Self {
|
|
||||||
Self {}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get() -> Result<Arc<Mutex<Self>>, Error> {
|
|
||||||
unsafe {
|
|
||||||
INIT.call_once(|| {
|
|
||||||
G_GRP_KEYS = Some(Arc::new(Mutex::new(GroupKeys::new())));
|
|
||||||
});
|
|
||||||
Ok(G_GRP_KEYS.as_ref().ok_or(Error::Invalid)?.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert_key() -> Result<(), Error> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct KeySet {
|
pub struct KeySet {
|
||||||
pub epoch_key: [u8; crypto::SYMM_KEY_LEN_BYTES],
|
pub epoch_key: [u8; crypto::SYMM_KEY_LEN_BYTES],
|
||||||
|
|
|
@ -82,14 +82,16 @@ impl DiscoveryCapabilities {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prepares and prints the pairing code and the QR code for easy pairing.
|
/// Prepares and prints the pairing code and the QR code for easy pairing.
|
||||||
pub fn print_pairing_code_and_qr(
|
pub fn print_pairing_code_and_qr<const N: usize>(
|
||||||
dev_det: &BasicInfoConfig,
|
dev_det: &BasicInfoConfig,
|
||||||
comm_data: &CommissioningData,
|
comm_data: &CommissioningData,
|
||||||
discovery_capabilities: DiscoveryCapabilities,
|
discovery_capabilities: DiscoveryCapabilities,
|
||||||
|
buf: &mut [u8],
|
||||||
) {
|
) {
|
||||||
let pairing_code = compute_pairing_code(comm_data);
|
let pairing_code = compute_pairing_code(comm_data);
|
||||||
let qr_code_data = QrSetupPayload::new(dev_det, comm_data, discovery_capabilities);
|
let qr_code_data = QrSetupPayload::new(dev_det, comm_data, discovery_capabilities);
|
||||||
let data_str = payload_base38_representation(&qr_code_data).expect("Failed to encode");
|
let data_str =
|
||||||
|
payload_base38_representation::<N>(&qr_code_data, buf).expect("Failed to encode");
|
||||||
|
|
||||||
pretty_print_pairing_code(&pairing_code);
|
pretty_print_pairing_code(&pairing_code);
|
||||||
print_qr_code(&data_str);
|
print_qr_code(&data_str);
|
||||||
|
|
|
@ -15,8 +15,6 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use heapless::FnvIndexMap;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
tlv::{TLVWriter, TagType},
|
tlv::{TLVWriter, TagType},
|
||||||
utils::writebuf::WriteBuf,
|
utils::writebuf::WriteBuf,
|
||||||
|
@ -45,6 +43,7 @@ const TOTAL_PAYLOAD_DATA_SIZE_IN_BITS: usize = VERSION_FIELD_LENGTH_IN_BITS
|
||||||
+ PAYLOAD_DISCRIMINATOR_FIELD_LENGTH_IN_BITS
|
+ PAYLOAD_DISCRIMINATOR_FIELD_LENGTH_IN_BITS
|
||||||
+ SETUP_PINCODE_FIELD_LENGTH_IN_BITS
|
+ SETUP_PINCODE_FIELD_LENGTH_IN_BITS
|
||||||
+ PADDING_FIELD_LENGTH_IN_BITS;
|
+ PADDING_FIELD_LENGTH_IN_BITS;
|
||||||
|
|
||||||
const TOTAL_PAYLOAD_DATA_SIZE_IN_BYTES: usize = TOTAL_PAYLOAD_DATA_SIZE_IN_BITS / 8;
|
const TOTAL_PAYLOAD_DATA_SIZE_IN_BYTES: usize = TOTAL_PAYLOAD_DATA_SIZE_IN_BITS / 8;
|
||||||
|
|
||||||
// Spec 5.1.4.2 CHIP-Common Reserved Tags
|
// Spec 5.1.4.2 CHIP-Common Reserved Tags
|
||||||
|
@ -80,8 +79,8 @@ pub struct QrSetupPayload<'data> {
|
||||||
discovery_capabilities: DiscoveryCapabilities,
|
discovery_capabilities: DiscoveryCapabilities,
|
||||||
dev_det: &'data BasicInfoConfig<'data>,
|
dev_det: &'data BasicInfoConfig<'data>,
|
||||||
comm_data: &'data CommissioningData,
|
comm_data: &'data CommissioningData,
|
||||||
// we use a BTreeMap to keep the order of the optional data stable
|
// The vec is ordered by the tag of OptionalQRCodeInfo
|
||||||
optional_data: heapless::FnvIndexMap<u8, OptionalQRCodeInfo, 16>,
|
optional_data: heapless::Vec<OptionalQRCodeInfo, 16>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'data> QrSetupPayload<'data> {
|
impl<'data> QrSetupPayload<'data> {
|
||||||
|
@ -98,7 +97,7 @@ impl<'data> QrSetupPayload<'data> {
|
||||||
discovery_capabilities,
|
discovery_capabilities,
|
||||||
dev_det,
|
dev_det,
|
||||||
comm_data,
|
comm_data,
|
||||||
optional_data: FnvIndexMap::new(),
|
optional_data: heapless::Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
if !dev_det.serial_no.is_empty() {
|
if !dev_det.serial_no.is_empty() {
|
||||||
|
@ -132,15 +131,11 @@ impl<'data> QrSetupPayload<'data> {
|
||||||
/// * `tag` - tag number in the [0x80-0xFF] range
|
/// * `tag` - tag number in the [0x80-0xFF] range
|
||||||
/// * `data` - Data to add
|
/// * `data` - Data to add
|
||||||
pub fn add_optional_vendor_data(&mut self, tag: u8, data: QRCodeInfoType) -> Result<(), Error> {
|
pub fn add_optional_vendor_data(&mut self, tag: u8, data: QRCodeInfoType) -> Result<(), Error> {
|
||||||
if !is_vendor_tag(tag) {
|
if is_vendor_tag(tag) {
|
||||||
return Err(Error::InvalidArgument);
|
self.add_optional_data(tag, data)
|
||||||
|
} else {
|
||||||
|
Err(Error::InvalidArgument)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.optional_data
|
|
||||||
.insert(tag, OptionalQRCodeInfo { tag, data })
|
|
||||||
.map_err(|_| Error::NoSpace)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A function to add an optional QR Code info CHIP object
|
/// A function to add an optional QR Code info CHIP object
|
||||||
|
@ -152,18 +147,26 @@ impl<'data> QrSetupPayload<'data> {
|
||||||
tag: u8,
|
tag: u8,
|
||||||
data: QRCodeInfoType,
|
data: QRCodeInfoType,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
if !is_common_tag(tag) {
|
if is_common_tag(tag) {
|
||||||
return Err(Error::InvalidArgument);
|
self.add_optional_data(tag, data)
|
||||||
|
} else {
|
||||||
|
Err(Error::InvalidArgument)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.optional_data
|
fn add_optional_data(&mut self, tag: u8, data: QRCodeInfoType) -> Result<(), Error> {
|
||||||
.insert(tag, OptionalQRCodeInfo { tag, data })
|
let item = OptionalQRCodeInfo { tag, data };
|
||||||
.map_err(|_| Error::NoSpace)?;
|
let index = self.optional_data.iter().position(|info| tag < info.tag);
|
||||||
|
|
||||||
Ok(())
|
if let Some(index) = index {
|
||||||
|
self.optional_data.insert(index, item)
|
||||||
|
} else {
|
||||||
|
self.optional_data.push(item)
|
||||||
|
}
|
||||||
|
.map_err(|_| Error::NoSpace)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_all_optional_data(&self) -> &FnvIndexMap<u8, OptionalQRCodeInfo, 16> {
|
pub fn get_all_optional_data(&self) -> &[OptionalQRCodeInfo] {
|
||||||
&self.optional_data
|
&self.optional_data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,35 +252,26 @@ pub enum CommissionningFlowType {
|
||||||
Custom = 2,
|
Custom = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TlvData {
|
pub(super) fn payload_base38_representation<const N: usize>(
|
||||||
max_data_length_in_bytes: u32,
|
payload: &QrSetupPayload,
|
||||||
data_length_in_bytes: Option<usize>,
|
buf: &mut [u8],
|
||||||
data: Option<Vec<u8>>,
|
) -> Result<heapless::String<N>, Error> {
|
||||||
}
|
if payload.is_valid() {
|
||||||
|
let (bits_buf, tlv_buf) = if payload.has_tlv() {
|
||||||
|
let (bits_buf, tlv_buf) = buf.split_at_mut(buf.len() / 2);
|
||||||
|
|
||||||
pub(super) fn payload_base38_representation(payload: &QrSetupPayload) -> Result<String, Error> {
|
(bits_buf, Some(tlv_buf))
|
||||||
let (mut bits, tlv_data) = if payload.has_tlv() {
|
|
||||||
let buffer_size = estimate_buffer_size(payload)?;
|
|
||||||
(
|
|
||||||
vec![0; buffer_size],
|
|
||||||
Some(TlvData {
|
|
||||||
max_data_length_in_bytes: buffer_size as u32,
|
|
||||||
data_length_in_bytes: None,
|
|
||||||
data: None,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
(vec![0; TOTAL_PAYLOAD_DATA_SIZE_IN_BYTES], None)
|
(buf, None)
|
||||||
};
|
};
|
||||||
|
|
||||||
if !payload.is_valid() {
|
payload_base38_representation_with_tlv(payload, bits_buf, tlv_buf)
|
||||||
return Err(Error::InvalidArgument);
|
} else {
|
||||||
|
Err(Error::InvalidArgument)
|
||||||
}
|
}
|
||||||
|
|
||||||
payload_base38_representation_with_tlv(payload, &mut bits, tlv_data)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn estimate_buffer_size(payload: &QrSetupPayload) -> Result<usize, Error> {
|
pub fn estimate_buffer_size(payload: &QrSetupPayload) -> Result<usize, Error> {
|
||||||
// Estimate the size of the needed buffer; initialize with the size of the standard payload.
|
// Estimate the size of the needed buffer; initialize with the size of the standard payload.
|
||||||
let mut estimate = TOTAL_PAYLOAD_DATA_SIZE_IN_BYTES;
|
let mut estimate = TOTAL_PAYLOAD_DATA_SIZE_IN_BYTES;
|
||||||
|
|
||||||
|
@ -298,10 +292,9 @@ fn estimate_buffer_size(payload: &QrSetupPayload) -> Result<usize, Error> {
|
||||||
size
|
size
|
||||||
};
|
};
|
||||||
|
|
||||||
let vendor_data = payload.get_all_optional_data();
|
for data in payload.get_all_optional_data() {
|
||||||
vendor_data.values().for_each(|data| {
|
|
||||||
estimate += data_item_size_estimate(&data.data);
|
estimate += data_item_size_estimate(&data.data);
|
||||||
});
|
}
|
||||||
|
|
||||||
estimate = estimate_struct_overhead(estimate);
|
estimate = estimate_struct_overhead(estimate);
|
||||||
|
|
||||||
|
@ -372,70 +365,72 @@ fn populate_bits(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn payload_base38_representation_with_tlv(
|
fn payload_base38_representation_with_tlv<const N: usize>(
|
||||||
payload: &QrSetupPayload,
|
payload: &QrSetupPayload,
|
||||||
bits: &mut [u8],
|
bits_buf: &mut [u8],
|
||||||
mut tlv_data: Option<TlvData>,
|
tlv_buf: Option<&mut [u8]>,
|
||||||
) -> Result<String, Error> {
|
) -> Result<heapless::String<N>, Error> {
|
||||||
if let Some(tlv_data) = tlv_data.as_mut() {
|
let tlv_data = if let Some(tlv_buf) = tlv_buf {
|
||||||
generate_tlv_from_optional_data(payload, tlv_data)?;
|
Some(generate_tlv_from_optional_data(payload, tlv_buf)?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let bits = generate_bit_set(payload, bits_buf, tlv_data)?;
|
||||||
|
|
||||||
|
let mut base38_encoded: heapless::String<N> = "MT:".into();
|
||||||
|
|
||||||
|
for c in base38::encode(bits) {
|
||||||
|
base38_encoded.push(c).map_err(|_| Error::NoSpace)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let bytes_written = generate_bit_set(payload, bits, tlv_data)?;
|
Ok(base38_encoded)
|
||||||
let base38_encoded = base38::encode(&*bits, Some(bytes_written));
|
|
||||||
Ok(format!("MT:{}", base38_encoded))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_tlv_from_optional_data(
|
fn generate_tlv_from_optional_data<'a>(
|
||||||
payload: &QrSetupPayload,
|
payload: &QrSetupPayload,
|
||||||
tlv_data: &mut TlvData,
|
tlv_buf: &'a mut [u8],
|
||||||
) -> Result<(), Error> {
|
) -> Result<&'a [u8], Error> {
|
||||||
let size_needed = tlv_data.max_data_length_in_bytes as usize;
|
let mut wb = WriteBuf::new(tlv_buf);
|
||||||
let mut tlv_buffer = vec![0u8; size_needed];
|
|
||||||
let mut wb = WriteBuf::new(&mut tlv_buffer);
|
|
||||||
let mut tw = TLVWriter::new(&mut wb);
|
let mut tw = TLVWriter::new(&mut wb);
|
||||||
|
|
||||||
tw.start_struct(TagType::Anonymous)?;
|
tw.start_struct(TagType::Anonymous)?;
|
||||||
let data = payload.get_all_optional_data();
|
|
||||||
|
|
||||||
for (tag, value) in data {
|
for info in payload.get_all_optional_data() {
|
||||||
match &value.data {
|
match &info.data {
|
||||||
QRCodeInfoType::String(data) => tw.utf8(TagType::Context(*tag), data.as_bytes())?,
|
QRCodeInfoType::String(data) => tw.utf8(TagType::Context(info.tag), data.as_bytes())?,
|
||||||
QRCodeInfoType::Int32(data) => tw.i32(TagType::Context(*tag), *data)?,
|
QRCodeInfoType::Int32(data) => tw.i32(TagType::Context(info.tag), *data)?,
|
||||||
QRCodeInfoType::Int64(data) => tw.i64(TagType::Context(*tag), *data)?,
|
QRCodeInfoType::Int64(data) => tw.i64(TagType::Context(info.tag), *data)?,
|
||||||
QRCodeInfoType::UInt32(data) => tw.u32(TagType::Context(*tag), *data)?,
|
QRCodeInfoType::UInt32(data) => tw.u32(TagType::Context(info.tag), *data)?,
|
||||||
QRCodeInfoType::UInt64(data) => tw.u64(TagType::Context(*tag), *data)?,
|
QRCodeInfoType::UInt64(data) => tw.u64(TagType::Context(info.tag), *data)?,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tw.end_container()?;
|
tw.end_container()?;
|
||||||
tlv_data.data_length_in_bytes = Some(tw.get_tail());
|
|
||||||
tlv_data.data = Some(tlv_buffer);
|
|
||||||
|
|
||||||
Ok(())
|
let tail = tw.get_tail();
|
||||||
|
|
||||||
|
Ok(&tlv_buf[..tail])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_bit_set(
|
fn generate_bit_set<'a>(
|
||||||
payload: &QrSetupPayload,
|
payload: &QrSetupPayload,
|
||||||
bits: &mut [u8],
|
bits_buf: &'a mut [u8],
|
||||||
tlv_data: Option<TlvData>,
|
tlv_data: Option<&[u8]>,
|
||||||
) -> Result<usize, Error> {
|
) -> Result<&'a [u8], Error> {
|
||||||
let mut offset: usize = 0;
|
let total_payload_size_in_bits =
|
||||||
|
TOTAL_PAYLOAD_DATA_SIZE_IN_BITS + tlv_data.map(|tlv_data| tlv_data.len() * 8).unwrap_or(0);
|
||||||
|
|
||||||
let total_payload_size_in_bits = if let Some(tlv_data) = &tlv_data {
|
if bits_buf.len() * 8 < total_payload_size_in_bits {
|
||||||
TOTAL_PAYLOAD_DATA_SIZE_IN_BITS + (tlv_data.data_length_in_bytes.unwrap_or_default() * 8)
|
|
||||||
} else {
|
|
||||||
TOTAL_PAYLOAD_DATA_SIZE_IN_BITS
|
|
||||||
};
|
|
||||||
|
|
||||||
if bits.len() * 8 < total_payload_size_in_bits {
|
|
||||||
return Err(Error::BufferTooSmall);
|
return Err(Error::BufferTooSmall);
|
||||||
};
|
};
|
||||||
|
|
||||||
let passwd = passwd_from_comm_data(payload.comm_data);
|
let passwd = passwd_from_comm_data(payload.comm_data);
|
||||||
|
|
||||||
|
let mut offset: usize = 0;
|
||||||
|
|
||||||
populate_bits(
|
populate_bits(
|
||||||
bits,
|
bits_buf,
|
||||||
&mut offset,
|
&mut offset,
|
||||||
payload.version as u64,
|
payload.version as u64,
|
||||||
VERSION_FIELD_LENGTH_IN_BITS,
|
VERSION_FIELD_LENGTH_IN_BITS,
|
||||||
|
@ -443,7 +438,7 @@ fn generate_bit_set(
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
populate_bits(
|
populate_bits(
|
||||||
bits,
|
bits_buf,
|
||||||
&mut offset,
|
&mut offset,
|
||||||
payload.dev_det.vid as u64,
|
payload.dev_det.vid as u64,
|
||||||
VENDOR_IDFIELD_LENGTH_IN_BITS,
|
VENDOR_IDFIELD_LENGTH_IN_BITS,
|
||||||
|
@ -451,7 +446,7 @@ fn generate_bit_set(
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
populate_bits(
|
populate_bits(
|
||||||
bits,
|
bits_buf,
|
||||||
&mut offset,
|
&mut offset,
|
||||||
payload.dev_det.pid as u64,
|
payload.dev_det.pid as u64,
|
||||||
PRODUCT_IDFIELD_LENGTH_IN_BITS,
|
PRODUCT_IDFIELD_LENGTH_IN_BITS,
|
||||||
|
@ -459,7 +454,7 @@ fn generate_bit_set(
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
populate_bits(
|
populate_bits(
|
||||||
bits,
|
bits_buf,
|
||||||
&mut offset,
|
&mut offset,
|
||||||
payload.flow_type as u64,
|
payload.flow_type as u64,
|
||||||
COMMISSIONING_FLOW_FIELD_LENGTH_IN_BITS,
|
COMMISSIONING_FLOW_FIELD_LENGTH_IN_BITS,
|
||||||
|
@ -467,7 +462,7 @@ fn generate_bit_set(
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
populate_bits(
|
populate_bits(
|
||||||
bits,
|
bits_buf,
|
||||||
&mut offset,
|
&mut offset,
|
||||||
payload.discovery_capabilities.as_bits() as u64,
|
payload.discovery_capabilities.as_bits() as u64,
|
||||||
RENDEZVOUS_INFO_FIELD_LENGTH_IN_BITS,
|
RENDEZVOUS_INFO_FIELD_LENGTH_IN_BITS,
|
||||||
|
@ -475,7 +470,7 @@ fn generate_bit_set(
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
populate_bits(
|
populate_bits(
|
||||||
bits,
|
bits_buf,
|
||||||
&mut offset,
|
&mut offset,
|
||||||
payload.comm_data.discriminator as u64,
|
payload.comm_data.discriminator as u64,
|
||||||
PAYLOAD_DISCRIMINATOR_FIELD_LENGTH_IN_BITS,
|
PAYLOAD_DISCRIMINATOR_FIELD_LENGTH_IN_BITS,
|
||||||
|
@ -483,7 +478,7 @@ fn generate_bit_set(
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
populate_bits(
|
populate_bits(
|
||||||
bits,
|
bits_buf,
|
||||||
&mut offset,
|
&mut offset,
|
||||||
passwd as u64,
|
passwd as u64,
|
||||||
SETUP_PINCODE_FIELD_LENGTH_IN_BITS,
|
SETUP_PINCODE_FIELD_LENGTH_IN_BITS,
|
||||||
|
@ -491,7 +486,7 @@ fn generate_bit_set(
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
populate_bits(
|
populate_bits(
|
||||||
bits,
|
bits_buf,
|
||||||
&mut offset,
|
&mut offset,
|
||||||
0,
|
0,
|
||||||
PADDING_FIELD_LENGTH_IN_BITS,
|
PADDING_FIELD_LENGTH_IN_BITS,
|
||||||
|
@ -499,26 +494,22 @@ fn generate_bit_set(
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if let Some(tlv_data) = tlv_data {
|
if let Some(tlv_data) = tlv_data {
|
||||||
populate_tlv_bits(bits, &mut offset, tlv_data, total_payload_size_in_bits)?;
|
populate_tlv_bits(bits_buf, &mut offset, tlv_data, total_payload_size_in_bits)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let bytes_written = (offset + 7) / 8;
|
let bytes_written = (offset + 7) / 8;
|
||||||
Ok(bytes_written)
|
|
||||||
|
Ok(&bits_buf[..bytes_written])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn populate_tlv_bits(
|
fn populate_tlv_bits(
|
||||||
bits: &mut [u8],
|
bits_buf: &mut [u8],
|
||||||
offset: &mut usize,
|
offset: &mut usize,
|
||||||
tlv_data: TlvData,
|
tlv_data: &[u8],
|
||||||
total_payload_size_in_bits: usize,
|
total_payload_size_in_bits: usize,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
if let (Some(data), Some(data_length_in_bytes)) = (tlv_data.data, tlv_data.data_length_in_bytes)
|
for b in tlv_data {
|
||||||
{
|
populate_bits(bits_buf, offset, *b as u64, 8, total_payload_size_in_bits)?;
|
||||||
for b in data.iter().take(data_length_in_bytes) {
|
|
||||||
populate_bits(bits, offset, *b as u64, 8, total_payload_size_in_bits)?;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Err(Error::InvalidArgument);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -555,7 +546,9 @@ mod tests {
|
||||||
|
|
||||||
let disc_cap = DiscoveryCapabilities::new(false, true, false);
|
let disc_cap = DiscoveryCapabilities::new(false, true, false);
|
||||||
let qr_code_data = QrSetupPayload::new(&dev_det, &comm_data, disc_cap);
|
let qr_code_data = QrSetupPayload::new(&dev_det, &comm_data, disc_cap);
|
||||||
let data_str = payload_base38_representation(&qr_code_data).expect("Failed to encode");
|
let mut buf = [0; 1024];
|
||||||
|
let data_str = payload_base38_representation::<128>(&qr_code_data, &mut buf)
|
||||||
|
.expect("Failed to encode");
|
||||||
assert_eq!(data_str, QR_CODE)
|
assert_eq!(data_str, QR_CODE)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -576,7 +569,9 @@ mod tests {
|
||||||
|
|
||||||
let disc_cap = DiscoveryCapabilities::new(true, false, false);
|
let disc_cap = DiscoveryCapabilities::new(true, false, false);
|
||||||
let qr_code_data = QrSetupPayload::new(&dev_det, &comm_data, disc_cap);
|
let qr_code_data = QrSetupPayload::new(&dev_det, &comm_data, disc_cap);
|
||||||
let data_str = payload_base38_representation(&qr_code_data).expect("Failed to encode");
|
let mut buf = [0; 1024];
|
||||||
|
let data_str = payload_base38_representation::<128>(&qr_code_data, &mut buf)
|
||||||
|
.expect("Failed to encode");
|
||||||
assert_eq!(data_str, QR_CODE)
|
assert_eq!(data_str, QR_CODE)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -620,7 +615,9 @@ mod tests {
|
||||||
)
|
)
|
||||||
.expect("Failed to add optional data");
|
.expect("Failed to add optional data");
|
||||||
|
|
||||||
let data_str = payload_base38_representation(&qr_code_data).expect("Failed to encode");
|
let mut buf = [0; 1024];
|
||||||
|
let data_str = payload_base38_representation::<{ QR_CODE.len() }>(&qr_code_data, &mut buf)
|
||||||
|
.expect("Failed to encode");
|
||||||
assert_eq!(data_str, QR_CODE)
|
assert_eq!(data_str, QR_CODE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue